Files
coder/coderd/projectimport.go
Kyle Carberry bd0293aff9 fix: Convert all jobs to use a common resource and agent type (#369)
* ci: Update DataDog GitHub branch to fallback to GITHUB_REF

This was detecting branches, but not our "main" branch before.
Hopefully this fixes it!

* Add basic Terraform Provider

* Rename post files to upload

* Add tests for resources

* Skip instance identity test

* Add tests for ensuring agent get's passed through properly

* Fix linting errors

* Add echo path

* Fix agent authentication

* fix: Convert all jobs to use a common resource and agent type

This enables a consistent API for project import and provisioned resources.
2022-02-28 18:00:52 +00:00

183 lines
5.7 KiB
Go

package coderd
import (
"database/sql"
"errors"
"fmt"
"net/http"
"github.com/go-chi/render"
"github.com/google/uuid"
"github.com/coder/coder/coderd/parameter"
"github.com/coder/coder/database"
"github.com/coder/coder/httpapi"
"github.com/coder/coder/httpmw"
)
// ParameterSchema represents a parameter parsed from project version source.
type ParameterSchema database.ParameterSchema
// ComputedParameterValue represents a computed parameter value.
type ComputedParameterValue parameter.ComputedValue
// CreateProjectImportJobRequest provides options to create a project import job.
type CreateProjectImportJobRequest struct {
StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"`
StorageSource string `json:"storage_source" validate:"required"`
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
// ParameterValues allows for additional parameters to be provided
// during the dry-run provision stage.
ParameterValues []CreateParameterValueRequest `json:"parameter_values"`
}
// Create a new project import job!
func (api *api) postProjectImportByOrganization(rw http.ResponseWriter, r *http.Request) {
apiKey := httpmw.APIKey(r)
organization := httpmw.OrganizationParam(r)
var req CreateProjectImportJobRequest
if !httpapi.Read(rw, r, &req) {
return
}
file, err := api.Database.GetFileByHash(r.Context(), req.StorageSource)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "file not found",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get file: %s", err),
})
return
}
jobID := uuid.New()
for _, parameterValue := range req.ParameterValues {
_, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{
ID: uuid.New(),
Name: parameterValue.Name,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Scope: database.ParameterScopeImportJob,
ScopeID: jobID.String(),
SourceScheme: parameterValue.SourceScheme,
SourceValue: parameterValue.SourceValue,
DestinationScheme: parameterValue.DestinationScheme,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert parameter value: %s", err),
})
return
}
}
job, err := api.Database.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
ID: jobID,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
OrganizationID: organization.ID,
InitiatorID: apiKey.UserID,
Provisioner: req.Provisioner,
StorageMethod: database.ProvisionerStorageMethodFile,
StorageSource: file.Hash,
Type: database.ProvisionerJobTypeProjectVersionImport,
Input: []byte{'{', '}'},
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("insert provisioner job: %s", err),
})
return
}
render.Status(r, http.StatusCreated)
render.JSON(rw, r, convertProvisionerJob(job))
}
// Returns imported parameter schemas from a completed job!
func (api *api) projectImportJobSchemasByID(rw http.ResponseWriter, r *http.Request) {
job := httpmw.ProvisionerJobParam(r)
if !convertProvisionerJob(job).Status.Completed() {
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
Message: "Job hasn't completed!",
})
return
}
schemas, err := api.Database.GetParameterSchemasByJobID(r.Context(), job.ID)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("list parameter schemas: %s", err),
})
return
}
if schemas == nil {
schemas = []database.ParameterSchema{}
}
render.Status(r, http.StatusOK)
render.JSON(rw, r, schemas)
}
// Returns computed parameters for an import job by ID.
func (api *api) projectImportJobParametersByID(rw http.ResponseWriter, r *http.Request) {
apiKey := httpmw.APIKey(r)
job := httpmw.ProvisionerJobParam(r)
if !convertProvisionerJob(job).Status.Completed() {
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
Message: "Job hasn't completed!",
})
return
}
values, err := parameter.Compute(r.Context(), api.Database, parameter.ComputeScope{
ProjectImportJobID: job.ID,
OrganizationID: job.OrganizationID,
UserID: apiKey.UserID,
}, &parameter.ComputeOptions{
// We *never* want to send the client secret parameter values.
HideRedisplayValues: true,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("compute values: %s", err),
})
return
}
if values == nil {
values = []parameter.ComputedValue{}
}
render.Status(r, http.StatusOK)
render.JSON(rw, r, values)
}
// Returns resources for an import job by ID.
func (api *api) projectImportJobResourcesByID(rw http.ResponseWriter, r *http.Request) {
job := httpmw.ProvisionerJobParam(r)
if !convertProvisionerJob(job).Status.Completed() {
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
Message: "Job hasn't completed!",
})
return
}
resources, err := api.Database.GetProvisionerJobResourcesByJobID(r.Context(), job.ID)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get project import job resources: %s", err),
})
return
}
if resources == nil {
resources = []database.ProvisionerJobResource{}
}
render.Status(r, http.StatusOK)
render.JSON(rw, r, resources)
}