chore: add license check for prebuilds (#20947)

Related: https://github.com/coder/coder/pull/20864
This commit is contained in:
Marcin Tojek
2025-11-26 15:00:07 +01:00
committed by GitHub
parent b7d8918d60
commit 9c7135a61d
3 changed files with 127 additions and 3 deletions
+1 -1
View File
@@ -460,7 +460,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
})
r.Route("/templates/{template}/prebuilds", func(r chi.Router) {
r.Use(
api.templateRBACEnabledMW,
api.RequireFeatureMW(codersdk.FeatureWorkspacePrebuilds),
apiKeyMiddleware,
httpmw.ExtractTemplateParam(api.Database),
)
+6 -1
View File
@@ -378,7 +378,12 @@ func (api *API) postInvalidateTemplatePresets(rw http.ResponseWriter, r *http.Re
slog.F("preset_count", len(invalidatedPresets)),
)
invalidated := db2sdk.InvalidatedPresets(invalidatedPresets)
if invalidated == nil {
invalidated = []codersdk.InvalidatedPreset{} // need to avoid nil value
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.InvalidatePresetsResponse{
Invalidated: db2sdk.InvalidatedPresets(invalidatedPresets),
Invalidated: invalidated,
})
}
+120 -1
View File
@@ -3,6 +3,7 @@ package coderd_test
import (
"bytes"
"context"
"errors"
"net/http"
"slices"
"testing"
@@ -2154,7 +2155,7 @@ func TestInvalidateTemplatePrebuilds(t *testing.T) {
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureTemplateRBAC: 1,
codersdk.FeatureWorkspacePrebuilds: 1,
},
},
})
@@ -2208,3 +2209,121 @@ func TestInvalidateTemplatePrebuilds(t *testing.T) {
require.Equal(t, codersdk.InvalidatedPreset{TemplateName: template.Name, TemplateVersionName: version2.Name, PresetName: presetWithParameters2.Name}, invalidated.Invalidated[0])
require.Equal(t, codersdk.InvalidatedPreset{TemplateName: template.Name, TemplateVersionName: version2.Name, PresetName: presetWithParameters3.Name}, invalidated.Invalidated[1])
}
func TestInvalidateTemplatePrebuilds_RegularUser(t *testing.T) {
t.Parallel()
// Given the following parameters and presets...
templateVersionParameters := []*proto.RichParameter{
{Name: "param1", Type: "string", Required: false, DefaultValue: "default1"},
}
presetWithParameters1 := &proto.Preset{
Name: "Preset With Parameters 1",
Parameters: []*proto.PresetParameter{
{Name: "param1", Value: "value1"},
},
}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureWorkspacePrebuilds: 1,
},
},
})
regularUserClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
// Given
version1 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionPlan: []*proto.Response{
{
Type: &proto.Response_Plan{
Plan: &proto.PlanComplete{
Presets: []*proto.Preset{presetWithParameters1},
Parameters: templateVersionParameters,
},
},
},
},
ProvisionApply: echo.ApplyComplete,
})
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version1.ID)
template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, version1.ID)
// When
ctx := testutil.Context(t, testutil.WaitShort)
_, err := regularUserClient.InvalidateTemplatePresets(ctx, template.ID)
// Then
require.Error(t, err, "regular user cannot invalidate presets")
var sdkError *codersdk.Error
require.True(t, errors.As(err, &sdkError))
require.ErrorAs(t, err, &sdkError)
require.Equal(t, http.StatusNotFound, sdkError.StatusCode())
}
func TestInvalidateTemplatePrebuilds_NoPresets(t *testing.T) {
t.Parallel()
// Given the template versions and template...
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{
codersdk.FeatureWorkspacePrebuilds: 1,
},
},
})
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
version1 := coderdtest.CreateTemplateVersion(t, templateAdminClient, owner.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyComplete,
})
coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdminClient, version1.ID)
template := coderdtest.CreateTemplate(t, templateAdminClient, owner.OrganizationID, version1.ID)
// When
ctx := testutil.Context(t, testutil.WaitLong)
invalidated, err := templateAdminClient.InvalidateTemplatePresets(ctx, template.ID)
require.NoError(t, err)
// Then
require.NotNil(t, invalidated.Invalidated)
require.Len(t, invalidated.Invalidated, 0)
}
func TestInvalidateTemplatePrebuilds_LicenseFeatureDisabled(t *testing.T) {
t.Parallel()
// Given the template versions and template...
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
Options: &coderdtest.Options{
IncludeProvisionerDaemon: true,
},
LicenseOptions: &coderdenttest.LicenseOptions{},
})
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin())
version1 := coderdtest.CreateTemplateVersion(t, templateAdminClient, owner.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyComplete,
})
coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdminClient, version1.ID)
template := coderdtest.CreateTemplate(t, templateAdminClient, owner.OrganizationID, version1.ID)
// When
ctx := testutil.Context(t, testutil.WaitLong)
_, err := templateAdminClient.InvalidateTemplatePresets(ctx, template.ID)
// Then
require.Error(t, err, "license feature prebuilds is required")
var sdkError *codersdk.Error
require.True(t, errors.As(err, &sdkError))
require.ErrorAs(t, err, &sdkError)
require.Equal(t, http.StatusForbidden, sdkError.StatusCode())
}