diff --git a/.vscode/settings.json b/.vscode/settings.json index 8c642faa64..32e7a2ddf4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,7 @@ "ntqry", "oneof", "parameterscopeid", + "pqtype", "promptui", "protobuf", "provisionerd", diff --git a/cli/projects.go b/cli/projects.go index 763b1fcc42..0f04dc05cd 100644 --- a/cli/projects.go +++ b/cli/projects.go @@ -40,7 +40,7 @@ func projects() *cobra.Command { return cmd } -func displayProjectImportInfo(cmd *cobra.Command, parameterSchemas []coderd.ParameterSchema, parameterValues []coderd.ComputedParameterValue, resources []coderd.ProjectImportJobResource) error { +func displayProjectImportInfo(cmd *cobra.Command, parameterSchemas []coderd.ParameterSchema, parameterValues []coderd.ComputedParameterValue, resources []coderd.ProvisionerJobResource) error { schemaByID := map[string]coderd.ParameterSchema{} for _, schema := range parameterSchemas { schemaByID[schema.ID.String()] = schema diff --git a/coderd/projectimport.go b/coderd/projectimport.go index d3032f9b1e..5ece718c99 100644 --- a/coderd/projectimport.go +++ b/coderd/projectimport.go @@ -21,9 +21,6 @@ type ParameterSchema database.ParameterSchema // ComputedParameterValue represents a computed parameter value. type ComputedParameterValue parameter.ComputedValue -// ProjectImportJobResource is a resource created by a project import job. -type ProjectImportJobResource database.ProjectImportJobResource - // CreateProjectImportJobRequest provides options to create a project import job. type CreateProjectImportJobRequest struct { StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"` @@ -167,7 +164,7 @@ func (api *api) projectImportJobResourcesByID(rw http.ResponseWriter, r *http.Re }) return } - resources, err := api.Database.GetProjectImportJobResourcesByJobID(r.Context(), job.ID) + resources, err := api.Database.GetProvisionerJobResourcesByJobID(r.Context(), job.ID) if errors.Is(err, sql.ErrNoRows) { err = nil } @@ -178,7 +175,7 @@ func (api *api) projectImportJobResourcesByID(rw http.ResponseWriter, r *http.Re return } if resources == nil { - resources = []database.ProjectImportJobResource{} + resources = []database.ProvisionerJobResource{} } render.Status(r, http.StatusOK) render.JSON(rw, r, resources) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 95d212df7a..1a50f808dd 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -15,6 +15,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/yamux" "github.com/moby/moby/pkg/namesgenerator" + "github.com/tabbed/pqtype" "golang.org/x/xerrors" "nhooyr.io/websocket" "storj.io/drpc/drpcmux" @@ -453,14 +454,8 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr slog.F("resource_name", resource.Name), slog.F("resource_type", resource.Type), slog.F("transition", transition)) - _, err = server.Database.InsertProjectImportJobResource(ctx, database.InsertProjectImportJobResourceParams{ - ID: uuid.New(), - CreatedAt: database.Now(), - JobID: jobID, - Transition: transition, - Type: resource.Type, - Name: resource.Name, - }) + + err = insertProvisionerJobResource(ctx, server.Database, jobID, transition, resource) if err != nil { return nil, xerrors.Errorf("insert resource: %w", err) } @@ -516,26 +511,9 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr } // This could be a bulk insert to improve performance. for _, protoResource := range jobType.WorkspaceProvision.Resources { - var instanceID sql.NullString - if protoResource.Agent != nil && protoResource.Agent.GetGoogleInstanceIdentity() != nil { - instanceID = sql.NullString{ - String: protoResource.Agent.GetGoogleInstanceIdentity().InstanceId, - Valid: true, - } - } - _, err = db.InsertWorkspaceResource(ctx, database.InsertWorkspaceResourceParams{ - ID: uuid.New(), - CreatedAt: database.Now(), - WorkspaceHistoryID: input.WorkspaceHistoryID, - Type: protoResource.Type, - Name: protoResource.Name, - InstanceID: instanceID, - // TODO: Generate this at the variable validation phase. - // Set the value in `default_source`, and disallow overwrite. - WorkspaceAgentToken: uuid.NewString(), - }) + err = insertProvisionerJobResource(ctx, db, job.ID, workspaceHistory.Transition, protoResource) if err != nil { - return xerrors.Errorf("insert workspace resource %q: %w", protoResource.Name, err) + return xerrors.Errorf("insert provisioner job: %w", err) } } return nil @@ -551,6 +529,61 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr return &proto.Empty{}, nil } +func insertProvisionerJobResource(ctx context.Context, db database.Store, jobID uuid.UUID, transition database.WorkspaceTransition, protoResource *sdkproto.Resource) error { + resource, err := db.InsertProvisionerJobResource(ctx, database.InsertProvisionerJobResourceParams{ + ID: uuid.New(), + CreatedAt: database.Now(), + JobID: jobID, + Transition: transition, + Type: protoResource.Type, + Name: protoResource.Name, + AgentID: uuid.NullUUID{ + UUID: uuid.New(), + Valid: protoResource.Agent != nil, + }, + }) + if err != nil { + return xerrors.Errorf("insert provisioner job resource %q: %w", protoResource.Name, err) + } + if resource.AgentID.Valid { + var instanceID sql.NullString + if protoResource.Agent.GetGoogleInstanceIdentity() != nil { + instanceID = sql.NullString{ + String: protoResource.Agent.GetGoogleInstanceIdentity().InstanceId, + Valid: true, + } + } + var env pqtype.NullRawMessage + if protoResource.Agent.Env != nil { + data, err := json.Marshal(protoResource.Agent.Env) + if err != nil { + return xerrors.Errorf("marshal env: %w", err) + } + env = pqtype.NullRawMessage{ + RawMessage: data, + Valid: true, + } + } + + _, err := db.InsertProvisionerJobAgent(ctx, database.InsertProvisionerJobAgentParams{ + ID: resource.AgentID.UUID, + CreatedAt: database.Now(), + ResourceID: resource.ID, + AuthToken: uuid.New(), + AuthInstanceID: instanceID, + EnvironmentVariables: env, + StartupScript: sql.NullString{ + String: protoResource.Agent.StartupScript, + Valid: protoResource.Agent.StartupScript != "", + }, + }) + if err != nil { + return xerrors.Errorf("insert agent: %w", err) + } + } + return nil +} + func convertValidationTypeSystem(typeSystem sdkproto.ParameterSchema_TypeSystem) (database.ParameterTypeSystem, error) { switch typeSystem { case sdkproto.ParameterSchema_None: diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index a99ca905c8..aab66b55e4 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -50,13 +50,32 @@ type ProvisionerJob struct { // ProvisionerJobLog represents a single log from a provisioner job. type ProvisionerJobLog struct { - ID uuid.UUID + ID uuid.UUID `json:"id"` CreatedAt time.Time `json:"created_at"` Source database.LogSource `json:"log_source"` Level database.LogLevel `json:"log_level"` Output string `json:"output"` } +type ProvisionerJobResource struct { + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + JobID uuid.UUID `json:"job_id"` + Transition database.WorkspaceTransition `json:"workspace_transition"` + Type string `json:"type"` + Name string `json:"name"` +} + +type ProvisionerJobAgent struct { + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ResourceID uuid.UUID `json:"resource_id"` + InstanceID string `json:"instance_id,omitempty"` + EnvironmentVariables map[string]string `json:"environment_variables"` + StartupScript string `json:"startup_script,omitempty"` +} + func (*api) provisionerJobByID(rw http.ResponseWriter, r *http.Request) { job := httpmw.ProvisionerJobParam(r) render.Status(r, http.StatusOK) diff --git a/coderd/workspaceagent.go b/coderd/workspaceagent.go index 4f4c88badd..2e45046fa3 100644 --- a/coderd/workspaceagent.go +++ b/coderd/workspaceagent.go @@ -2,12 +2,14 @@ package coderd import ( "database/sql" + "encoding/json" "errors" "fmt" "net/http" "github.com/go-chi/render" + "github.com/coder/coder/database" "github.com/coder/coder/httpapi" "github.com/mitchellh/mapstructure" @@ -54,14 +56,48 @@ func (api *api) postAuthenticateWorkspaceAgentUsingGoogleInstanceIdentity(rw htt }) return } - resource, err := api.Database.GetWorkspaceResourceByInstanceID(r.Context(), claims.Google.ComputeEngine.InstanceID) + agent, err := api.Database.GetProvisionerJobAgentByInstanceID(r.Context(), claims.Google.ComputeEngine.InstanceID) if errors.Is(err, sql.ErrNoRows) { httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ Message: fmt.Sprintf("instance with id %q not found", claims.Google.ComputeEngine.InstanceID), }) return } - resourceHistory, err := api.Database.GetWorkspaceHistoryByID(r.Context(), resource.WorkspaceHistoryID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get provisioner job agent: %s", err), + }) + return + } + resource, err := api.Database.GetProvisionerJobResourceByID(r.Context(), agent.ResourceID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get provisioner job resource: %s", err), + }) + return + } + job, err := api.Database.GetProvisionerJobByID(r.Context(), resource.JobID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get provisioner job: %s", err), + }) + return + } + if job.Type != database.ProvisionerJobTypeWorkspaceProvision { + httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + Message: fmt.Sprintf("%q jobs cannot be authenticated", job.Type), + }) + return + } + var jobData workspaceProvisionJob + err = json.Unmarshal(job.Input, &jobData) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("extract job data: %s", err), + }) + return + } + resourceHistory, err := api.Database.GetWorkspaceHistoryByID(r.Context(), jobData.WorkspaceHistoryID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: fmt.Sprintf("get workspace history: %s", err), @@ -86,6 +122,6 @@ func (api *api) postAuthenticateWorkspaceAgentUsingGoogleInstanceIdentity(rw htt } render.Status(r, http.StatusOK) render.JSON(rw, r, WorkspaceAgentAuthenticateResponse{ - SessionToken: resource.WorkspaceAgentToken, + SessionToken: agent.AuthToken.String(), }) } diff --git a/codersdk/files.go b/codersdk/files.go index 226391740e..732cbb2c42 100644 --- a/codersdk/files.go +++ b/codersdk/files.go @@ -3,9 +3,7 @@ package codersdk import ( "context" "encoding/json" - "fmt" "net/http" - "net/url" "github.com/coder/coder/coderd" ) @@ -28,8 +26,3 @@ func (c *Client) UploadFile(ctx context.Context, contentType string, content []b var resp coderd.UploadFileResponse return resp, json.NewDecoder(res.Body).Decode(&resp) } - -// DownloadURL returns the download URL for the specified asset -func (c *Client) DownloadURL(asset string) (*url.URL, error) { - return c.URL.Parse(fmt.Sprintf("/api/v2/downloads/%s", asset)) -} diff --git a/codersdk/projectimport.go b/codersdk/projectimport.go index 95f7cb6812..7001d63153 100644 --- a/codersdk/projectimport.go +++ b/codersdk/projectimport.go @@ -81,7 +81,7 @@ func (c *Client) ProjectImportJobParameters(ctx context.Context, organization st } // ProjectImportJobResources returns resources for a project import job. -func (c *Client) ProjectImportJobResources(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ProjectImportJobResource, error) { +func (c *Client) ProjectImportJobResources(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ProvisionerJobResource, error) { res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projectimport/%s/%s/resources", organization, job), nil) if err != nil { return nil, err @@ -90,6 +90,6 @@ func (c *Client) ProjectImportJobResources(ctx context.Context, organization str if res.StatusCode != http.StatusOK { return nil, readBodyAsError(res) } - var resources []coderd.ProjectImportJobResource + var resources []coderd.ProvisionerJobResource return resources, json.NewDecoder(res.Body).Decode(&resources) } diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index f8d1ed2c5f..1e09a2a74f 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -19,19 +19,18 @@ func New() database.Store { organizationMembers: make([]database.OrganizationMember, 0), users: make([]database.User, 0), - files: make([]database.File, 0), - parameterValue: make([]database.ParameterValue, 0), - parameterSchema: make([]database.ParameterSchema, 0), - project: make([]database.Project, 0), - projectVersion: make([]database.ProjectVersion, 0), - projectImportJobResource: make([]database.ProjectImportJobResource, 0), - provisionerDaemons: make([]database.ProvisionerDaemon, 0), - provisionerJobs: make([]database.ProvisionerJob, 0), - provisionerJobLog: make([]database.ProvisionerJobLog, 0), - workspace: make([]database.Workspace, 0), - workspaceResource: make([]database.WorkspaceResource, 0), - workspaceHistory: make([]database.WorkspaceHistory, 0), - workspaceAgent: make([]database.WorkspaceAgent, 0), + files: make([]database.File, 0), + parameterValue: make([]database.ParameterValue, 0), + parameterSchema: make([]database.ParameterSchema, 0), + project: make([]database.Project, 0), + projectVersion: make([]database.ProjectVersion, 0), + provisionerDaemons: make([]database.ProvisionerDaemon, 0), + provisionerJobs: make([]database.ProvisionerJob, 0), + provisionerJobLog: make([]database.ProvisionerJobLog, 0), + workspace: make([]database.Workspace, 0), + provisionerJobResource: make([]database.ProvisionerJobResource, 0), + workspaceHistory: make([]database.WorkspaceHistory, 0), + provisionerJobAgent: make([]database.ProvisionerJobAgent, 0), } } @@ -46,19 +45,18 @@ type fakeQuerier struct { users []database.User // New tables - files []database.File - parameterValue []database.ParameterValue - parameterSchema []database.ParameterSchema - project []database.Project - projectVersion []database.ProjectVersion - projectImportJobResource []database.ProjectImportJobResource - provisionerDaemons []database.ProvisionerDaemon - provisionerJobs []database.ProvisionerJob - provisionerJobLog []database.ProvisionerJobLog - workspace []database.Workspace - workspaceAgent []database.WorkspaceAgent - workspaceHistory []database.WorkspaceHistory - workspaceResource []database.WorkspaceResource + files []database.File + parameterValue []database.ParameterValue + parameterSchema []database.ParameterSchema + project []database.Project + projectVersion []database.ProjectVersion + provisionerDaemons []database.ProvisionerDaemon + provisionerJobs []database.ProvisionerJob + provisionerJobAgent []database.ProvisionerJobAgent + provisionerJobResource []database.ProvisionerJobResource + provisionerJobLog []database.ProvisionerJobLog + workspace []database.Workspace + workspaceHistory []database.WorkspaceHistory } // InTx doesn't rollback data properly for in-memory yet. @@ -149,24 +147,6 @@ func (q *fakeQuerier) GetUserCount(_ context.Context) (int64, error) { return int64(len(q.users)), nil } -func (q *fakeQuerier) GetWorkspaceAgentsByResourceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAgent, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - agents := make([]database.WorkspaceAgent, 0) - for _, workspaceAgent := range q.workspaceAgent { - for _, id := range ids { - if workspaceAgent.WorkspaceResourceID.String() == id.String() { - agents = append(agents, workspaceAgent) - } - } - } - if len(agents) == 0 { - return nil, sql.ErrNoRows - } - return agents, nil -} - func (q *fakeQuerier) GetWorkspaceByID(_ context.Context, id uuid.UUID) (database.Workspace, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -195,18 +175,6 @@ func (q *fakeQuerier) GetWorkspaceByUserIDAndName(_ context.Context, arg databas return database.Workspace{}, sql.ErrNoRows } -func (q *fakeQuerier) GetWorkspaceResourceByInstanceID(_ context.Context, instanceID string) (database.WorkspaceResource, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - for _, workspaceResource := range q.workspaceResource { - if workspaceResource.InstanceID.String == instanceID { - return workspaceResource, nil - } - } - return database.WorkspaceResource{}, sql.ErrNoRows -} - func (q *fakeQuerier) GetWorkspaceOwnerCountsByProjectIDs(_ context.Context, projectIDs []uuid.UUID) ([]database.GetWorkspaceOwnerCountsByProjectIDsRow, error) { counts := map[string]map[string]struct{}{} for _, projectID := range projectIDs { @@ -242,22 +210,6 @@ func (q *fakeQuerier) GetWorkspaceOwnerCountsByProjectIDs(_ context.Context, pro return res, nil } -func (q *fakeQuerier) GetWorkspaceResourcesByHistoryID(_ context.Context, workspaceHistoryID uuid.UUID) ([]database.WorkspaceResource, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - resources := make([]database.WorkspaceResource, 0) - for _, workspaceResource := range q.workspaceResource { - if workspaceResource.WorkspaceHistoryID.String() == workspaceHistoryID.String() { - resources = append(resources, workspaceResource) - } - } - if len(resources) == 0 { - return nil, sql.ErrNoRows - } - return resources, nil -} - func (q *fakeQuerier) GetWorkspaceHistoryByID(_ context.Context, id uuid.UUID) (database.WorkspaceHistory, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -448,23 +400,6 @@ func (q *fakeQuerier) GetProjectByOrganizationAndName(_ context.Context, arg dat return database.Project{}, sql.ErrNoRows } -func (q *fakeQuerier) GetProjectImportJobResourcesByJobID(_ context.Context, jobID uuid.UUID) ([]database.ProjectImportJobResource, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - resources := make([]database.ProjectImportJobResource, 0) - for _, resource := range q.projectImportJobResource { - if resource.JobID.String() != jobID.String() { - continue - } - resources = append(resources, resource) - } - if len(resources) == 0 { - return nil, sql.ErrNoRows - } - return resources, nil -} - func (q *fakeQuerier) GetProjectVersionsByProjectID(_ context.Context, projectID uuid.UUID) ([]database.ProjectVersion, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -573,6 +508,38 @@ func (q *fakeQuerier) GetProvisionerDaemons(_ context.Context) ([]database.Provi return q.provisionerDaemons, nil } +func (q *fakeQuerier) GetProvisionerJobAgentByInstanceID(_ context.Context, instanceID string) (database.ProvisionerJobAgent, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + // The schema sorts this by created at, so we iterate the array backwards. + for i := len(q.provisionerJobAgent) - 1; i >= 0; i-- { + agent := q.provisionerJobAgent[i] + if agent.AuthInstanceID.Valid && agent.AuthInstanceID.String == instanceID { + return agent, nil + } + } + return database.ProvisionerJobAgent{}, sql.ErrNoRows +} + +func (q *fakeQuerier) GetProvisionerJobAgentsByResourceIDs(_ context.Context, ids []uuid.UUID) ([]database.ProvisionerJobAgent, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + agents := make([]database.ProvisionerJobAgent, 0) + for _, agent := range q.provisionerJobAgent { + for _, id := range ids { + if agent.ResourceID.String() == id.String() { + agents = append(agents, agent) + } + } + } + if len(agents) == 0 { + return nil, sql.ErrNoRows + } + return agents, nil +} + func (q *fakeQuerier) GetProvisionerDaemonByID(_ context.Context, id uuid.UUID) (database.ProvisionerDaemon, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -599,6 +566,35 @@ func (q *fakeQuerier) GetProvisionerJobByID(_ context.Context, id uuid.UUID) (da return database.ProvisionerJob{}, sql.ErrNoRows } +func (q *fakeQuerier) GetProvisionerJobResourceByID(_ context.Context, id uuid.UUID) (database.ProvisionerJobResource, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, resource := range q.provisionerJobResource { + if resource.ID.String() == id.String() { + return resource, nil + } + } + return database.ProvisionerJobResource{}, sql.ErrNoRows +} + +func (q *fakeQuerier) GetProvisionerJobResourcesByJobID(_ context.Context, jobID uuid.UUID) ([]database.ProvisionerJobResource, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + resources := make([]database.ProvisionerJobResource, 0) + for _, resource := range q.provisionerJobResource { + if resource.JobID.String() != jobID.String() { + continue + } + resources = append(resources, resource) + } + if len(resources) == 0 { + return nil, sql.ErrNoRows + } + return resources, nil +} + func (q *fakeQuerier) GetProvisionerLogsByIDBetween(_ context.Context, arg database.GetProvisionerLogsByIDBetweenParams) ([]database.ProvisionerJobLog, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -732,23 +728,6 @@ func (q *fakeQuerier) InsertProject(_ context.Context, arg database.InsertProjec return project, nil } -func (q *fakeQuerier) InsertProjectImportJobResource(_ context.Context, arg database.InsertProjectImportJobResourceParams) (database.ProjectImportJobResource, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - //nolint:gosimple - projectResource := database.ProjectImportJobResource{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - JobID: arg.JobID, - Transition: arg.Transition, - Type: arg.Type, - Name: arg.Name, - } - q.projectImportJobResource = append(q.projectImportJobResource, projectResource) - return projectResource, nil -} - func (q *fakeQuerier) InsertProjectVersion(_ context.Context, arg database.InsertProjectVersionParams) (database.ProjectVersion, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -847,6 +826,45 @@ func (q *fakeQuerier) InsertProvisionerJob(_ context.Context, arg database.Inser return job, nil } +func (q *fakeQuerier) InsertProvisionerJobAgent(_ context.Context, arg database.InsertProvisionerJobAgentParams) (database.ProvisionerJobAgent, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + //nolint:gosimple + agent := database.ProvisionerJobAgent{ + ID: arg.ID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + ResourceID: arg.ResourceID, + AuthToken: arg.AuthToken, + AuthInstanceID: arg.AuthInstanceID, + EnvironmentVariables: arg.EnvironmentVariables, + StartupScript: arg.StartupScript, + InstanceMetadata: arg.InstanceMetadata, + ResourceMetadata: arg.ResourceMetadata, + } + q.provisionerJobAgent = append(q.provisionerJobAgent, agent) + return agent, nil +} + +func (q *fakeQuerier) InsertProvisionerJobResource(_ context.Context, arg database.InsertProvisionerJobResourceParams) (database.ProvisionerJobResource, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + //nolint:gosimple + resource := database.ProvisionerJobResource{ + ID: arg.ID, + CreatedAt: arg.CreatedAt, + JobID: arg.JobID, + Transition: arg.Transition, + Type: arg.Type, + Name: arg.Name, + AgentID: arg.AgentID, + } + q.provisionerJobResource = append(q.provisionerJobResource, resource) + return resource, nil +} + func (q *fakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParams) (database.User, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -882,23 +900,6 @@ func (q *fakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWork return workspace, nil } -func (q *fakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - //nolint:gosimple - workspaceAgent := database.WorkspaceAgent{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - WorkspaceResourceID: arg.WorkspaceResourceID, - InstanceMetadata: arg.InstanceMetadata, - ResourceMetadata: arg.ResourceMetadata, - } - q.workspaceAgent = append(q.workspaceAgent, workspaceAgent) - return workspaceAgent, nil -} - func (q *fakeQuerier) InsertWorkspaceHistory(_ context.Context, arg database.InsertWorkspaceHistoryParams) (database.WorkspaceHistory, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -920,23 +921,6 @@ func (q *fakeQuerier) InsertWorkspaceHistory(_ context.Context, arg database.Ins return workspaceHistory, nil } -func (q *fakeQuerier) InsertWorkspaceResource(_ context.Context, arg database.InsertWorkspaceResourceParams) (database.WorkspaceResource, error) { - q.mutex.Lock() - defer q.mutex.Unlock() - - workspaceResource := database.WorkspaceResource{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - WorkspaceHistoryID: arg.WorkspaceHistoryID, - InstanceID: arg.InstanceID, - Type: arg.Type, - Name: arg.Name, - WorkspaceAgentToken: arg.WorkspaceAgentToken, - } - q.workspaceResource = append(q.workspaceResource, workspaceResource) - return workspaceResource, nil -} - func (q *fakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPIKeyByIDParams) error { q.mutex.Lock() defer q.mutex.Unlock() diff --git a/database/dump.sql b/database/dump.sql index 371ad221e3..23095aad18 100644 --- a/database/dump.sql +++ b/database/dump.sql @@ -163,15 +163,6 @@ CREATE TABLE project ( active_version_id uuid NOT NULL ); -CREATE TABLE project_import_job_resource ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - job_id uuid NOT NULL, - transition workspace_transition NOT NULL, - type character varying(256) NOT NULL, - name character varying(64) NOT NULL -); - CREATE TABLE project_version ( id uuid NOT NULL, project_id uuid NOT NULL, @@ -208,6 +199,19 @@ CREATE TABLE provisioner_job ( worker_id uuid ); +CREATE TABLE provisioner_job_agent ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone, + resource_id uuid NOT NULL, + auth_token uuid NOT NULL, + auth_instance_id character varying(64), + environment_variables jsonb, + startup_script character varying(65534), + instance_metadata jsonb, + resource_metadata jsonb +); + CREATE TABLE provisioner_job_log ( id uuid NOT NULL, job_id uuid NOT NULL, @@ -217,6 +221,16 @@ CREATE TABLE provisioner_job_log ( output character varying(1024) NOT NULL ); +CREATE TABLE provisioner_job_resource ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + job_id uuid NOT NULL, + transition workspace_transition NOT NULL, + type character varying(256) NOT NULL, + name character varying(64) NOT NULL, + agent_id uuid +); + CREATE TABLE users ( id text NOT NULL, email text NOT NULL, @@ -248,15 +262,6 @@ CREATE TABLE workspace ( name character varying(64) NOT NULL ); -CREATE TABLE workspace_agent ( - id uuid NOT NULL, - workspace_resource_id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - instance_metadata jsonb NOT NULL, - resource_metadata jsonb NOT NULL -); - CREATE TABLE workspace_history ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, @@ -272,17 +277,6 @@ CREATE TABLE workspace_history ( provision_job_id uuid NOT NULL ); -CREATE TABLE workspace_resource ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - workspace_history_id uuid NOT NULL, - instance_id character varying(64), - type character varying(256) NOT NULL, - name character varying(64) NOT NULL, - workspace_agent_token character varying(128) NOT NULL, - workspace_agent_id uuid -); - ALTER TABLE ONLY file ADD CONSTRAINT file_hash_key UNIQUE (hash); @@ -301,9 +295,6 @@ ALTER TABLE ONLY parameter_value ALTER TABLE ONLY project ADD CONSTRAINT project_id_key UNIQUE (id); -ALTER TABLE ONLY project_import_job_resource - ADD CONSTRAINT project_import_job_resource_id_key UNIQUE (id); - ALTER TABLE ONLY project ADD CONSTRAINT project_organization_id_name_key UNIQUE (organization_id, name); @@ -319,14 +310,20 @@ ALTER TABLE ONLY provisioner_daemon ALTER TABLE ONLY provisioner_daemon ADD CONSTRAINT provisioner_daemon_name_key UNIQUE (name); +ALTER TABLE ONLY provisioner_job_agent + ADD CONSTRAINT provisioner_job_agent_auth_token_key UNIQUE (auth_token); + +ALTER TABLE ONLY provisioner_job_agent + ADD CONSTRAINT provisioner_job_agent_id_key UNIQUE (id); + ALTER TABLE ONLY provisioner_job ADD CONSTRAINT provisioner_job_id_key UNIQUE (id); ALTER TABLE ONLY provisioner_job_log ADD CONSTRAINT provisioner_job_log_id_key UNIQUE (id); -ALTER TABLE ONLY workspace_agent - ADD CONSTRAINT workspace_agent_id_key UNIQUE (id); +ALTER TABLE ONLY provisioner_job_resource + ADD CONSTRAINT provisioner_job_resource_id_key UNIQUE (id); ALTER TABLE ONLY workspace_history ADD CONSTRAINT workspace_history_id_key UNIQUE (id); @@ -340,29 +337,20 @@ ALTER TABLE ONLY workspace ALTER TABLE ONLY workspace ADD CONSTRAINT workspace_owner_id_name_key UNIQUE (owner_id, name); -ALTER TABLE ONLY workspace_resource - ADD CONSTRAINT workspace_resource_id_key UNIQUE (id); - -ALTER TABLE ONLY workspace_resource - ADD CONSTRAINT workspace_resource_workspace_agent_token_key UNIQUE (workspace_agent_token); - -ALTER TABLE ONLY workspace_resource - ADD CONSTRAINT workspace_resource_workspace_history_id_type_name_key UNIQUE (workspace_history_id, type, name); - ALTER TABLE ONLY parameter_schema ADD CONSTRAINT parameter_schema_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_job(id) ON DELETE CASCADE; -ALTER TABLE ONLY project_import_job_resource - ADD CONSTRAINT project_import_job_resource_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_job(id) ON DELETE CASCADE; - ALTER TABLE ONLY project_version ADD CONSTRAINT project_version_project_id_fkey FOREIGN KEY (project_id) REFERENCES project(id); +ALTER TABLE ONLY provisioner_job_agent + ADD CONSTRAINT provisioner_job_agent_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES provisioner_job_resource(id) ON DELETE CASCADE; + ALTER TABLE ONLY provisioner_job_log ADD CONSTRAINT provisioner_job_log_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_job(id) ON DELETE CASCADE; -ALTER TABLE ONLY workspace_agent - ADD CONSTRAINT workspace_agent_workspace_resource_id_fkey FOREIGN KEY (workspace_resource_id) REFERENCES workspace_resource(id) ON DELETE CASCADE; +ALTER TABLE ONLY provisioner_job_resource + ADD CONSTRAINT provisioner_job_resource_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_job(id) ON DELETE CASCADE; ALTER TABLE ONLY workspace_history ADD CONSTRAINT workspace_history_project_version_id_fkey FOREIGN KEY (project_version_id) REFERENCES project_version(id) ON DELETE CASCADE; @@ -373,6 +361,3 @@ ALTER TABLE ONLY workspace_history ALTER TABLE ONLY workspace ADD CONSTRAINT workspace_project_id_fkey FOREIGN KEY (project_id) REFERENCES project(id); -ALTER TABLE ONLY workspace_resource - ADD CONSTRAINT workspace_resource_workspace_history_id_fkey FOREIGN KEY (workspace_history_id) REFERENCES workspace_history(id) ON DELETE CASCADE; - diff --git a/database/migrations/000003_workspaces.up.sql b/database/migrations/000003_workspaces.up.sql index 81e894caa3..97e3083d31 100644 --- a/database/migrations/000003_workspaces.up.sql +++ b/database/migrations/000003_workspaces.up.sql @@ -32,36 +32,3 @@ CREATE TABLE workspace_history ( provision_job_id uuid NOT NULL, UNIQUE(workspace_id, name) ); - --- Cloud resources produced by a provision job. -CREATE TABLE workspace_resource ( - id uuid NOT NULL UNIQUE, - created_at timestamptz NOT NULL, - workspace_history_id uuid NOT NULL REFERENCES workspace_history (id) ON DELETE CASCADE, - -- A unique identifier for the resource. This can be used - -- to exchange for an agent token with various providers. - instance_id varchar(64), - -- Resource type produced by a provisioner. - -- eg. "google_compute_instance" - type varchar(256) NOT NULL, - -- Name of the resource. - -- eg. "kyle-dev-instance" - name varchar(64) NOT NULL, - -- Token for an agent to connect. - workspace_agent_token varchar(128) NOT NULL UNIQUE, - -- If an agent has been conencted for this resource, - -- the agent table is not null. - workspace_agent_id uuid, - UNIQUE(workspace_history_id, type, name) -); - -CREATE TABLE workspace_agent ( - id uuid NOT NULL UNIQUE, - workspace_resource_id uuid NOT NULL REFERENCES workspace_resource (id) ON DELETE CASCADE, - created_at timestamptz NOT NULL, - updated_at timestamptz NOT NULL, - -- Identifies instance architecture, cloud, etc. - instance_metadata jsonb NOT NULL, - -- Identifies resources. - resource_metadata jsonb NOT NULL -); diff --git a/database/migrations/000004_jobs.up.sql b/database/migrations/000004_jobs.up.sql index 4d100e7f6f..f8e1ca8db9 100644 --- a/database/migrations/000004_jobs.up.sql +++ b/database/migrations/000004_jobs.up.sql @@ -55,6 +55,31 @@ CREATE TABLE IF NOT EXISTS provisioner_job_log ( output varchar(1024) NOT NULL ); +-- Resources from multiple workspace states are stored here post project-import job. +CREATE TABLE provisioner_job_resource ( + id uuid NOT NULL UNIQUE, + created_at timestamptz NOT NULL, + job_id uuid NOT NULL REFERENCES provisioner_job(id) ON DELETE CASCADE, + transition workspace_transition NOT NULL, + type varchar(256) NOT NULL, + name varchar(64) NOT NULL, + agent_id uuid +); + +-- Agents that associate with a specific resource. +CREATE TABLE provisioner_job_agent ( + id uuid NOT NULL UNIQUE, + created_at timestamptz NOT NULL, + updated_at timestamptz, + resource_id uuid NOT NULL REFERENCES provisioner_job_resource (id) ON DELETE CASCADE, + auth_token uuid NOT NULL UNIQUE, + auth_instance_id varchar(64), + environment_variables jsonb, + startup_script varchar(65534), + instance_metadata jsonb, + resource_metadata jsonb +); + CREATE TYPE parameter_scope AS ENUM ( 'organization', 'project', @@ -119,13 +144,3 @@ CREATE TABLE parameter_value ( -- Prevents duplicates for parameters in the same scope. UNIQUE(name, scope, scope_id) ); - --- Resources from multiple workspace states are stored here post project-import job. -CREATE TABLE project_import_job_resource ( - id uuid NOT NULL UNIQUE, - created_at timestamptz NOT NULL, - job_id uuid NOT NULL REFERENCES provisioner_job(id) ON DELETE CASCADE, - transition workspace_transition NOT NULL, - type varchar(256) NOT NULL, - name varchar(64) NOT NULL -); diff --git a/database/models.go b/database/models.go index 82154d2c2f..bf4ffbbe30 100644 --- a/database/models.go +++ b/database/models.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "github.com/tabbed/pqtype" ) type LogLevel string @@ -342,15 +343,6 @@ type Project struct { ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` } -type ProjectImportJobResource struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - Transition WorkspaceTransition `db:"transition" json:"transition"` - Type string `db:"type" json:"type"` - Name string `db:"name" json:"name"` -} - type ProjectVersion struct { ID uuid.UUID `db:"id" json:"id"` ProjectID uuid.UUID `db:"project_id" json:"project_id"` @@ -387,6 +379,19 @@ type ProvisionerJob struct { WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"` } +type ProvisionerJobAgent struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` + AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` + AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` + EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` + StartupScript sql.NullString `db:"startup_script" json:"startup_script"` + InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` + ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` +} + type ProvisionerJobLog struct { ID uuid.UUID `db:"id" json:"id"` JobID uuid.UUID `db:"job_id" json:"job_id"` @@ -396,6 +401,16 @@ type ProvisionerJobLog struct { Output string `db:"output" json:"output"` } +type ProvisionerJobResource struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` + AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"` +} + type User struct { ID string `db:"id" json:"id"` Email string `db:"email" json:"email"` @@ -427,15 +442,6 @@ type Workspace struct { Name string `db:"name" json:"name"` } -type WorkspaceAgent struct { - ID uuid.UUID `db:"id" json:"id"` - WorkspaceResourceID uuid.UUID `db:"workspace_resource_id" json:"workspace_resource_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - InstanceMetadata json.RawMessage `db:"instance_metadata" json:"instance_metadata"` - ResourceMetadata json.RawMessage `db:"resource_metadata" json:"resource_metadata"` -} - type WorkspaceHistory struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` @@ -450,14 +456,3 @@ type WorkspaceHistory struct { ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` ProvisionJobID uuid.UUID `db:"provision_job_id" json:"provision_job_id"` } - -type WorkspaceResource struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - WorkspaceHistoryID uuid.UUID `db:"workspace_history_id" json:"workspace_history_id"` - InstanceID sql.NullString `db:"instance_id" json:"instance_id"` - Type string `db:"type" json:"type"` - Name string `db:"name" json:"name"` - WorkspaceAgentToken string `db:"workspace_agent_token" json:"workspace_agent_token"` - WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"` -} diff --git a/database/querier.go b/database/querier.go index 0c3445d31d..faa24ac3a4 100644 --- a/database/querier.go +++ b/database/querier.go @@ -20,19 +20,21 @@ type querier interface { GetParameterValuesByScope(ctx context.Context, arg GetParameterValuesByScopeParams) ([]ParameterValue, error) GetProjectByID(ctx context.Context, id uuid.UUID) (Project, error) GetProjectByOrganizationAndName(ctx context.Context, arg GetProjectByOrganizationAndNameParams) (Project, error) - GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProjectImportJobResource, error) GetProjectVersionByID(ctx context.Context, id uuid.UUID) (ProjectVersion, error) GetProjectVersionByProjectIDAndName(ctx context.Context, arg GetProjectVersionByProjectIDAndNameParams) (ProjectVersion, error) GetProjectVersionsByProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectVersion, error) GetProjectsByOrganizationIDs(ctx context.Context, ids []string) ([]Project, error) GetProvisionerDaemonByID(ctx context.Context, id uuid.UUID) (ProvisionerDaemon, error) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDaemon, error) + GetProvisionerJobAgentByInstanceID(ctx context.Context, authInstanceID string) (ProvisionerJobAgent, error) + GetProvisionerJobAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]ProvisionerJobAgent, error) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (ProvisionerJob, error) + GetProvisionerJobResourceByID(ctx context.Context, id uuid.UUID) (ProvisionerJobResource, error) + GetProvisionerJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProvisionerJobResource, error) GetProvisionerLogsByIDBetween(ctx context.Context, arg GetProvisionerLogsByIDBetweenParams) ([]ProvisionerJobLog, error) GetUserByEmailOrUsername(ctx context.Context, arg GetUserByEmailOrUsernameParams) (User, error) GetUserByID(ctx context.Context, id string) (User, error) GetUserCount(ctx context.Context) (int64, error) - GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Workspace, error) GetWorkspaceByUserIDAndName(ctx context.Context, arg GetWorkspaceByUserIDAndNameParams) (Workspace, error) GetWorkspaceHistoryByID(ctx context.Context, id uuid.UUID) (WorkspaceHistory, error) @@ -40,8 +42,6 @@ type querier interface { GetWorkspaceHistoryByWorkspaceIDAndName(ctx context.Context, arg GetWorkspaceHistoryByWorkspaceIDAndNameParams) (WorkspaceHistory, error) GetWorkspaceHistoryByWorkspaceIDWithoutAfter(ctx context.Context, workspaceID uuid.UUID) (WorkspaceHistory, error) GetWorkspaceOwnerCountsByProjectIDs(ctx context.Context, ids []uuid.UUID) ([]GetWorkspaceOwnerCountsByProjectIDsRow, error) - GetWorkspaceResourceByInstanceID(ctx context.Context, instanceID string) (WorkspaceResource, error) - GetWorkspaceResourcesByHistoryID(ctx context.Context, workspaceHistoryID uuid.UUID) ([]WorkspaceResource, error) GetWorkspacesByProjectAndUserID(ctx context.Context, arg GetWorkspacesByProjectAndUserIDParams) ([]Workspace, error) GetWorkspacesByUserID(ctx context.Context, ownerID string) ([]Workspace, error) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) @@ -51,16 +51,15 @@ type querier interface { InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) - InsertProjectImportJobResource(ctx context.Context, arg InsertProjectImportJobResourceParams) (ProjectImportJobResource, error) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) InsertProvisionerDaemon(ctx context.Context, arg InsertProvisionerDaemonParams) (ProvisionerDaemon, error) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) + InsertProvisionerJobAgent(ctx context.Context, arg InsertProvisionerJobAgentParams) (ProvisionerJobAgent, error) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) + InsertProvisionerJobResource(ctx context.Context, arg InsertProvisionerJobResourceParams) (ProvisionerJobResource, error) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) - InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) InsertWorkspaceHistory(ctx context.Context, arg InsertWorkspaceHistoryParams) (WorkspaceHistory, error) - InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error UpdateProvisionerDaemonByID(ctx context.Context, arg UpdateProvisionerDaemonByIDParams) error UpdateProvisionerJobByID(ctx context.Context, arg UpdateProvisionerJobByIDParams) error diff --git a/database/query.sql b/database/query.sql index 5f950404b1..eabea0c43d 100644 --- a/database/query.sql +++ b/database/query.sql @@ -173,14 +173,6 @@ FROM WHERE job_id = $1; --- name: GetProjectImportJobResourcesByJobID :many -SELECT - * -FROM - project_import_job_resource -WHERE - job_id = $1; - -- name: GetProjectVersionsByProjectID :many SELECT * @@ -220,12 +212,6 @@ WHERE ORDER BY created_at; --- name: GetProvisionerDaemons :many -SELECT - * -FROM - provisioner_daemon; - -- name: GetProvisionerDaemonByID :one SELECT * @@ -234,6 +220,22 @@ FROM WHERE id = $1; +-- name: GetProvisionerDaemons :many +SELECT + * +FROM + provisioner_daemon; + +-- name: GetProvisionerJobAgentByInstanceID :one +SELECT + * +FROM + provisioner_job_agent +WHERE + auth_instance_id = @auth_instance_id :: text +ORDER BY + created_at DESC; + -- name: GetProvisionerJobByID :one SELECT * @@ -328,31 +330,29 @@ WHERE LIMIT 1; --- name: GetWorkspaceResourceByInstanceID :one +-- name: GetProvisionerJobResourceByID :one SELECT * FROM - workspace_resource + provisioner_job_resource WHERE - instance_id = @instance_id :: text -ORDER BY - created_at; + id = $1; --- name: GetWorkspaceResourcesByHistoryID :many +-- name: GetProvisionerJobResourcesByJobID :many SELECT * FROM - workspace_resource + provisioner_job_resource WHERE - workspace_history_id = $1; + job_id = $1; --- name: GetWorkspaceAgentsByResourceIDs :many +-- name: GetProvisionerJobAgentsByResourceIDs :many SELECT * FROM - workspace_agent + provisioner_job_agent WHERE - workspace_resource_id = ANY(@ids :: uuid [ ]); + resource_id = ANY(@ids :: uuid [ ]); -- name: InsertAPIKey :one INSERT INTO @@ -457,11 +457,19 @@ INSERT INTO VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *; --- name: InsertProjectImportJobResource :one +-- name: InsertProvisionerJobResource :one INSERT INTO - project_import_job_resource (id, created_at, job_id, transition, type, name) + provisioner_job_resource ( + id, + created_at, + job_id, + transition, + type, + name, + agent_id + ) VALUES - ($1, $2, $3, $4, $5, $6) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7) RETURNING *; -- name: InsertProjectVersion :one INSERT INTO @@ -569,18 +577,22 @@ INSERT INTO VALUES ($1, $2, $3, $4, $5, $6) RETURNING *; --- name: InsertWorkspaceAgent :one +-- name: InsertProvisionerJobAgent :one INSERT INTO - workspace_agent ( + provisioner_job_agent ( id, - workspace_resource_id, created_at, updated_at, + resource_id, + auth_token, + auth_instance_id, + environment_variables, + startup_script, instance_metadata, resource_metadata ) VALUES - ($1, $2, $3, $4, $5, $6) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; -- name: InsertWorkspaceHistory :one INSERT INTO @@ -600,20 +612,6 @@ INSERT INTO VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *; --- name: InsertWorkspaceResource :one -INSERT INTO - workspace_resource ( - id, - created_at, - workspace_history_id, - instance_id, - type, - name, - workspace_agent_token - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING *; - -- name: UpdateAPIKeyByID :exec UPDATE api_keys diff --git a/database/query.sql.go b/database/query.sql.go index a8f4c4b5f4..c74ecbcbf9 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -11,6 +11,7 @@ import ( "github.com/google/uuid" "github.com/lib/pq" + "github.com/tabbed/pqtype" ) const acquireProvisionerJob = `-- name: AcquireProvisionerJob :one @@ -424,45 +425,6 @@ func (q *sqlQuerier) GetProjectByOrganizationAndName(ctx context.Context, arg Ge return i, err } -const getProjectImportJobResourcesByJobID = `-- name: GetProjectImportJobResourcesByJobID :many -SELECT - id, created_at, job_id, transition, type, name -FROM - project_import_job_resource -WHERE - job_id = $1 -` - -func (q *sqlQuerier) GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProjectImportJobResource, error) { - rows, err := q.db.QueryContext(ctx, getProjectImportJobResourcesByJobID, jobID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ProjectImportJobResource - for rows.Next() { - var i ProjectImportJobResource - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.JobID, - &i.Transition, - &i.Type, - &i.Name, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getProjectVersionByID = `-- name: GetProjectVersionByID :one SELECT id, project_id, created_at, updated_at, name, description, import_job_id @@ -655,6 +617,78 @@ func (q *sqlQuerier) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDa return items, nil } +const getProvisionerJobAgentByInstanceID = `-- name: GetProvisionerJobAgentByInstanceID :one +SELECT + id, created_at, updated_at, resource_id, auth_token, auth_instance_id, environment_variables, startup_script, instance_metadata, resource_metadata +FROM + provisioner_job_agent +WHERE + auth_instance_id = $1 :: text +ORDER BY + created_at DESC +` + +func (q *sqlQuerier) GetProvisionerJobAgentByInstanceID(ctx context.Context, authInstanceID string) (ProvisionerJobAgent, error) { + row := q.db.QueryRowContext(ctx, getProvisionerJobAgentByInstanceID, authInstanceID) + var i ProvisionerJobAgent + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.ResourceID, + &i.AuthToken, + &i.AuthInstanceID, + &i.EnvironmentVariables, + &i.StartupScript, + &i.InstanceMetadata, + &i.ResourceMetadata, + ) + return i, err +} + +const getProvisionerJobAgentsByResourceIDs = `-- name: GetProvisionerJobAgentsByResourceIDs :many +SELECT + id, created_at, updated_at, resource_id, auth_token, auth_instance_id, environment_variables, startup_script, instance_metadata, resource_metadata +FROM + provisioner_job_agent +WHERE + resource_id = ANY($1 :: uuid [ ]) +` + +func (q *sqlQuerier) GetProvisionerJobAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]ProvisionerJobAgent, error) { + rows, err := q.db.QueryContext(ctx, getProvisionerJobAgentsByResourceIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ProvisionerJobAgent + for rows.Next() { + var i ProvisionerJobAgent + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.ResourceID, + &i.AuthToken, + &i.AuthInstanceID, + &i.EnvironmentVariables, + &i.StartupScript, + &i.InstanceMetadata, + &i.ResourceMetadata, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getProvisionerJobByID = `-- name: GetProvisionerJobByID :one SELECT id, created_at, updated_at, started_at, cancelled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id @@ -687,6 +721,70 @@ func (q *sqlQuerier) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (P return i, err } +const getProvisionerJobResourceByID = `-- name: GetProvisionerJobResourceByID :one +SELECT + id, created_at, job_id, transition, type, name, agent_id +FROM + provisioner_job_resource +WHERE + id = $1 +` + +func (q *sqlQuerier) GetProvisionerJobResourceByID(ctx context.Context, id uuid.UUID) (ProvisionerJobResource, error) { + row := q.db.QueryRowContext(ctx, getProvisionerJobResourceByID, id) + var i ProvisionerJobResource + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Type, + &i.Name, + &i.AgentID, + ) + return i, err +} + +const getProvisionerJobResourcesByJobID = `-- name: GetProvisionerJobResourcesByJobID :many +SELECT + id, created_at, job_id, transition, type, name, agent_id +FROM + provisioner_job_resource +WHERE + job_id = $1 +` + +func (q *sqlQuerier) GetProvisionerJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProvisionerJobResource, error) { + rows, err := q.db.QueryContext(ctx, getProvisionerJobResourcesByJobID, jobID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ProvisionerJobResource + for rows.Next() { + var i ProvisionerJobResource + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Type, + &i.Name, + &i.AgentID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getProvisionerLogsByIDBetween = `-- name: GetProvisionerLogsByIDBetween :many SELECT id, job_id, created_at, source, level, output @@ -834,45 +932,6 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context) (int64, error) { return count, err } -const getWorkspaceAgentsByResourceIDs = `-- name: GetWorkspaceAgentsByResourceIDs :many -SELECT - id, workspace_resource_id, created_at, updated_at, instance_metadata, resource_metadata -FROM - workspace_agent -WHERE - workspace_resource_id = ANY($1 :: uuid [ ]) -` - -func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error) { - rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsByResourceIDs, pq.Array(ids)) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WorkspaceAgent - for rows.Next() { - var i WorkspaceAgent - if err := rows.Scan( - &i.ID, - &i.WorkspaceResourceID, - &i.CreatedAt, - &i.UpdatedAt, - &i.InstanceMetadata, - &i.ResourceMetadata, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getWorkspaceByID = `-- name: GetWorkspaceByID :one SELECT id, created_at, updated_at, owner_id, project_id, name @@ -1111,74 +1170,6 @@ func (q *sqlQuerier) GetWorkspaceOwnerCountsByProjectIDs(ctx context.Context, id return items, nil } -const getWorkspaceResourceByInstanceID = `-- name: GetWorkspaceResourceByInstanceID :one -SELECT - id, created_at, workspace_history_id, instance_id, type, name, workspace_agent_token, workspace_agent_id -FROM - workspace_resource -WHERE - instance_id = $1 :: text -ORDER BY - created_at -` - -func (q *sqlQuerier) GetWorkspaceResourceByInstanceID(ctx context.Context, instanceID string) (WorkspaceResource, error) { - row := q.db.QueryRowContext(ctx, getWorkspaceResourceByInstanceID, instanceID) - var i WorkspaceResource - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.WorkspaceHistoryID, - &i.InstanceID, - &i.Type, - &i.Name, - &i.WorkspaceAgentToken, - &i.WorkspaceAgentID, - ) - return i, err -} - -const getWorkspaceResourcesByHistoryID = `-- name: GetWorkspaceResourcesByHistoryID :many -SELECT - id, created_at, workspace_history_id, instance_id, type, name, workspace_agent_token, workspace_agent_id -FROM - workspace_resource -WHERE - workspace_history_id = $1 -` - -func (q *sqlQuerier) GetWorkspaceResourcesByHistoryID(ctx context.Context, workspaceHistoryID uuid.UUID) ([]WorkspaceResource, error) { - rows, err := q.db.QueryContext(ctx, getWorkspaceResourcesByHistoryID, workspaceHistoryID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WorkspaceResource - for rows.Next() { - var i WorkspaceResource - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.WorkspaceHistoryID, - &i.InstanceID, - &i.Type, - &i.Name, - &i.WorkspaceAgentToken, - &i.WorkspaceAgentID, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getWorkspacesByProjectAndUserID = `-- name: GetWorkspacesByProjectAndUserID :many SELECT id, created_at, updated_at, owner_id, project_id, name @@ -1677,43 +1668,6 @@ func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) return i, err } -const insertProjectImportJobResource = `-- name: InsertProjectImportJobResource :one -INSERT INTO - project_import_job_resource (id, created_at, job_id, transition, type, name) -VALUES - ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, job_id, transition, type, name -` - -type InsertProjectImportJobResourceParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - Transition WorkspaceTransition `db:"transition" json:"transition"` - Type string `db:"type" json:"type"` - Name string `db:"name" json:"name"` -} - -func (q *sqlQuerier) InsertProjectImportJobResource(ctx context.Context, arg InsertProjectImportJobResourceParams) (ProjectImportJobResource, error) { - row := q.db.QueryRowContext(ctx, insertProjectImportJobResource, - arg.ID, - arg.CreatedAt, - arg.JobID, - arg.Transition, - arg.Type, - arg.Name, - ) - var i ProjectImportJobResource - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.JobID, - &i.Transition, - &i.Type, - &i.Name, - ) - return i, err -} - const insertProjectVersion = `-- name: InsertProjectVersion :one INSERT INTO project_version ( @@ -1859,6 +1813,66 @@ func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisi return i, err } +const insertProvisionerJobAgent = `-- name: InsertProvisionerJobAgent :one +INSERT INTO + provisioner_job_agent ( + id, + created_at, + updated_at, + resource_id, + auth_token, + auth_instance_id, + environment_variables, + startup_script, + instance_metadata, + resource_metadata + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, resource_id, auth_token, auth_instance_id, environment_variables, startup_script, instance_metadata, resource_metadata +` + +type InsertProvisionerJobAgentParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` + AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` + AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` + EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` + StartupScript sql.NullString `db:"startup_script" json:"startup_script"` + InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` + ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` +} + +func (q *sqlQuerier) InsertProvisionerJobAgent(ctx context.Context, arg InsertProvisionerJobAgentParams) (ProvisionerJobAgent, error) { + row := q.db.QueryRowContext(ctx, insertProvisionerJobAgent, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.ResourceID, + arg.AuthToken, + arg.AuthInstanceID, + arg.EnvironmentVariables, + arg.StartupScript, + arg.InstanceMetadata, + arg.ResourceMetadata, + ) + var i ProvisionerJobAgent + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.ResourceID, + &i.AuthToken, + &i.AuthInstanceID, + &i.EnvironmentVariables, + &i.StartupScript, + &i.InstanceMetadata, + &i.ResourceMetadata, + ) + return i, err +} + const insertProvisionerJobLogs = `-- name: InsertProvisionerJobLogs :many INSERT INTO provisioner_job_log @@ -1917,6 +1931,54 @@ func (q *sqlQuerier) InsertProvisionerJobLogs(ctx context.Context, arg InsertPro return items, nil } +const insertProvisionerJobResource = `-- name: InsertProvisionerJobResource :one +INSERT INTO + provisioner_job_resource ( + id, + created_at, + job_id, + transition, + type, + name, + agent_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, job_id, transition, type, name, agent_id +` + +type InsertProvisionerJobResourceParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` + AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"` +} + +func (q *sqlQuerier) InsertProvisionerJobResource(ctx context.Context, arg InsertProvisionerJobResourceParams) (ProvisionerJobResource, error) { + row := q.db.QueryRowContext(ctx, insertProvisionerJobResource, + arg.ID, + arg.CreatedAt, + arg.JobID, + arg.Transition, + arg.Type, + arg.Name, + arg.AgentID, + ) + var i ProvisionerJobResource + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Type, + &i.Name, + &i.AgentID, + ) + return i, err +} + const insertUser = `-- name: InsertUser :one INSERT INTO users ( @@ -2025,50 +2087,6 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar return i, err } -const insertWorkspaceAgent = `-- name: InsertWorkspaceAgent :one -INSERT INTO - workspace_agent ( - id, - workspace_resource_id, - created_at, - updated_at, - instance_metadata, - resource_metadata - ) -VALUES - ($1, $2, $3, $4, $5, $6) RETURNING id, workspace_resource_id, created_at, updated_at, instance_metadata, resource_metadata -` - -type InsertWorkspaceAgentParams struct { - ID uuid.UUID `db:"id" json:"id"` - WorkspaceResourceID uuid.UUID `db:"workspace_resource_id" json:"workspace_resource_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - InstanceMetadata json.RawMessage `db:"instance_metadata" json:"instance_metadata"` - ResourceMetadata json.RawMessage `db:"resource_metadata" json:"resource_metadata"` -} - -func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) { - row := q.db.QueryRowContext(ctx, insertWorkspaceAgent, - arg.ID, - arg.WorkspaceResourceID, - arg.CreatedAt, - arg.UpdatedAt, - arg.InstanceMetadata, - arg.ResourceMetadata, - ) - var i WorkspaceAgent - err := row.Scan( - &i.ID, - &i.WorkspaceResourceID, - &i.CreatedAt, - &i.UpdatedAt, - &i.InstanceMetadata, - &i.ResourceMetadata, - ) - return i, err -} - const insertWorkspaceHistory = `-- name: InsertWorkspaceHistory :one INSERT INTO workspace_history ( @@ -2134,55 +2152,6 @@ func (q *sqlQuerier) InsertWorkspaceHistory(ctx context.Context, arg InsertWorks return i, err } -const insertWorkspaceResource = `-- name: InsertWorkspaceResource :one -INSERT INTO - workspace_resource ( - id, - created_at, - workspace_history_id, - instance_id, - type, - name, - workspace_agent_token - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, workspace_history_id, instance_id, type, name, workspace_agent_token, workspace_agent_id -` - -type InsertWorkspaceResourceParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - WorkspaceHistoryID uuid.UUID `db:"workspace_history_id" json:"workspace_history_id"` - InstanceID sql.NullString `db:"instance_id" json:"instance_id"` - Type string `db:"type" json:"type"` - Name string `db:"name" json:"name"` - WorkspaceAgentToken string `db:"workspace_agent_token" json:"workspace_agent_token"` -} - -func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) { - row := q.db.QueryRowContext(ctx, insertWorkspaceResource, - arg.ID, - arg.CreatedAt, - arg.WorkspaceHistoryID, - arg.InstanceID, - arg.Type, - arg.Name, - arg.WorkspaceAgentToken, - ) - var i WorkspaceResource - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.WorkspaceHistoryID, - &i.InstanceID, - &i.Type, - &i.Name, - &i.WorkspaceAgentToken, - &i.WorkspaceAgentID, - ) - return i, err -} - const updateAPIKeyByID = `-- name: UpdateAPIKeyByID :exec UPDATE api_keys diff --git a/go.mod b/go.mod index 35569e6f1e..1180eba9f0 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( github.com/quasilyte/go-ruleguard/dsl v0.3.17 github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 + github.com/tabbed/pqtype v0.1.1 github.com/unrolled/secure v1.10.0 github.com/xlab/treeprint v1.1.0 go.opencensus.io v0.23.0 diff --git a/go.sum b/go.sum index 6c5de14b22..68dd21a43d 100644 --- a/go.sum +++ b/go.sum @@ -1251,6 +1251,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tabbed/pqtype v0.1.1 h1:PhEcb9JZ8jr7SUjJDFjRPxny0M8fkXZrxn/a9yQfoZg= +github.com/tabbed/pqtype v0.1.1/go.mod h1:HLt2kLJPcUhODQkYn3mJkMHXVsuv3Z2n5NZEeKXL0Uk= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=