mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: fix null pointer on external provisioner daemons with daily_cost (#9401)
* fix: fix null pointer on external provisioner daemons with daily_cost Signed-off-by: Spike Curtis <spike@coder.com> * Add logging for debounce and job acquire Signed-off-by: Spike Curtis <spike@coder.com> * Return error instead of panic Signed-off-by: Spike Curtis <spike@coder.com> * remove debounce on external provisioners to fix test flakes Signed-off-by: Spike Curtis <spike@coder.com> --------- Signed-off-by: Spike Curtis <spike@coder.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/yamux"
|
||||
@@ -243,23 +244,33 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
mux := drpcmux.New()
|
||||
err = proto.DRPCRegisterProvisionerDaemon(mux, &provisionerdserver.Server{
|
||||
AccessURL: api.AccessURL,
|
||||
GitAuthConfigs: api.GitAuthConfigs,
|
||||
OIDCConfig: api.OIDCConfig,
|
||||
ID: daemon.ID,
|
||||
Database: api.Database,
|
||||
Pubsub: api.Pubsub,
|
||||
Provisioners: daemon.Provisioners,
|
||||
Telemetry: api.Telemetry,
|
||||
Auditor: &api.AGPL.Auditor,
|
||||
TemplateScheduleStore: api.AGPL.TemplateScheduleStore,
|
||||
UserQuietHoursScheduleStore: api.AGPL.UserQuietHoursScheduleStore,
|
||||
Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)),
|
||||
Tags: rawTags,
|
||||
Tracer: trace.NewNoopTracerProvider().Tracer("noop"),
|
||||
DeploymentValues: api.DeploymentValues,
|
||||
})
|
||||
srv, err := provisionerdserver.NewServer(
|
||||
api.AccessURL,
|
||||
daemon.ID,
|
||||
api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)),
|
||||
daemon.Provisioners,
|
||||
rawTags,
|
||||
api.Database,
|
||||
api.Pubsub,
|
||||
api.Telemetry,
|
||||
trace.NewNoopTracerProvider().Tracer("noop"),
|
||||
&api.AGPL.QuotaCommitter,
|
||||
&api.AGPL.Auditor,
|
||||
api.AGPL.TemplateScheduleStore,
|
||||
api.AGPL.UserQuietHoursScheduleStore,
|
||||
api.DeploymentValues,
|
||||
// TODO(spikecurtis) - fix debounce to not cause flaky tests.
|
||||
time.Duration(0),
|
||||
provisionerdserver.Options{
|
||||
GitAuthConfigs: api.GitAuthConfigs,
|
||||
OIDCConfig: api.OIDCConfig,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
_ = conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("create provisioner daemon server: %s", err))
|
||||
return
|
||||
}
|
||||
err = proto.DRPCRegisterProvisionerDaemon(mux, srv)
|
||||
if err != nil {
|
||||
_ = conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("drpc register provisioner daemon: %s", err))
|
||||
return
|
||||
|
||||
@@ -9,13 +9,20 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/provisionerdserver"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/license"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/provisionerd"
|
||||
provisionerdproto "github.com/coder/coder/v2/provisionerd/proto"
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
@@ -212,6 +219,107 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
||||
require.Len(t, daemons, 1)
|
||||
})
|
||||
|
||||
t.Run("PSK_daily_cost", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
UserWorkspaceQuota: 10,
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureExternalProvisionerDaemons: 1,
|
||||
codersdk.FeatureTemplateRBAC: 1,
|
||||
},
|
||||
},
|
||||
ProvisionerDaemonPSK: "provisionersftw",
|
||||
})
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
terraformClient, terraformServer := provisionersdk.MemTransportPipe()
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = terraformClient.Close()
|
||||
_ = terraformServer.Close()
|
||||
}()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: terraformServer,
|
||||
Logger: logger.Named("echo"),
|
||||
WorkDirectory: tempDir,
|
||||
})
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
provisioners := provisionerd.Provisioners{
|
||||
string(database.ProvisionerTypeEcho): proto.NewDRPCProvisionerClient(terraformClient),
|
||||
}
|
||||
another := codersdk.New(client.URL)
|
||||
pd := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
||||
return another.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
|
||||
Organization: user.OrganizationID,
|
||||
Provisioners: []codersdk.ProvisionerType{
|
||||
codersdk.ProvisionerTypeEcho,
|
||||
},
|
||||
Tags: map[string]string{
|
||||
provisionerdserver.TagScope: provisionerdserver.ScopeOrganization,
|
||||
},
|
||||
PreSharedKey: "provisionersftw",
|
||||
})
|
||||
}, &provisionerd.Options{
|
||||
Logger: logger.Named("provisionerd"),
|
||||
Provisioners: provisioners,
|
||||
})
|
||||
defer pd.Close()
|
||||
|
||||
// Patch the 'Everyone' group to give the user quota to build their workspace.
|
||||
_, err := client.PatchGroup(ctx, user.OrganizationID, codersdk.PatchGroupRequest{
|
||||
QuotaAllowance: ptr.Ref(1),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
DailyCost: 1,
|
||||
Agents: []*proto.Agent{{
|
||||
Id: uuid.NewString(),
|
||||
Name: "example",
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
build := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status)
|
||||
|
||||
err = pd.Shutdown(ctx)
|
||||
require.NoError(t, err)
|
||||
err = terraformServer.Close()
|
||||
require.NoError(t, err)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Error("timeout waiting for server to shut down")
|
||||
case err := <-errCh:
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BadPSK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, user := coderdenttest.New(t, &coderdenttest.Options{
|
||||
|
||||
Reference in New Issue
Block a user