refactor: workspace builds (#7541)

* refactor workspace builds

Signed-off-by: Spike Curtis <spike@coder.com>

* make gen

Signed-off-by: Spike Curtis <spike@coder.com>

* Remove ParameterResolver from typescript

Signed-off-by: Spike Curtis <spike@coder.com>

* rename conversion -> database/db2sdk

Signed-off-by: Spike Curtis <spike@coder.com>

* tests for db2sdk

Signed-off-by: Spike Curtis <spike@coder.com>

* Tests for ParameterResolver

Signed-off-by: Spike Curtis <spike@coder.com>

* wsbuilder tests

Signed-off-by: Spike Curtis <spike@coder.com>

* Move parameter validation tests to richparameters_test.go

Signed-off-by: Spike Curtis <spike@coder.com>

* Fix CI generation; rename mock->dbmock

Signed-off-by: Spike Curtis <spike@coder.com>

* Fix test imports

Signed-off-by: Spike Curtis <spike@coder.com>

---------

Signed-off-by: Spike Curtis <spike@coder.com>
This commit is contained in:
Spike Curtis
2023-05-23 12:06:33 +04:00
committed by GitHub
parent 622456faf8
commit cd416c86dd
31 changed files with 5681 additions and 1238 deletions
+113
View File
@@ -0,0 +1,113 @@
// Package db2sdk provides common conversion routines from database types to codersdk types
package db2sdk
import (
"encoding/json"
"time"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/parameter"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisionersdk/proto"
)
func WorkspaceBuildParameters(params []database.WorkspaceBuildParameter) []codersdk.WorkspaceBuildParameter {
out := make([]codersdk.WorkspaceBuildParameter, len(params))
for i, p := range params {
out[i] = WorkspaceBuildParameter(p)
}
return out
}
func WorkspaceBuildParameter(p database.WorkspaceBuildParameter) codersdk.WorkspaceBuildParameter {
return codersdk.WorkspaceBuildParameter{
Name: p.Name,
Value: p.Value,
}
}
func TemplateVersionParameter(param database.TemplateVersionParameter) (codersdk.TemplateVersionParameter, error) {
var protoOptions []*proto.RichParameterOption
err := json.Unmarshal(param.Options, &protoOptions)
if err != nil {
return codersdk.TemplateVersionParameter{}, err
}
options := make([]codersdk.TemplateVersionParameterOption, 0)
for _, option := range protoOptions {
options = append(options, codersdk.TemplateVersionParameterOption{
Name: option.Name,
Description: option.Description,
Value: option.Value,
Icon: option.Icon,
})
}
descriptionPlaintext, err := parameter.Plaintext(param.Description)
if err != nil {
return codersdk.TemplateVersionParameter{}, err
}
return codersdk.TemplateVersionParameter{
Name: param.Name,
DisplayName: param.DisplayName,
Description: param.Description,
DescriptionPlaintext: descriptionPlaintext,
Type: param.Type,
Mutable: param.Mutable,
DefaultValue: param.DefaultValue,
Icon: param.Icon,
Options: options,
ValidationRegex: param.ValidationRegex,
ValidationMin: param.ValidationMin,
ValidationMax: param.ValidationMax,
ValidationError: param.ValidationError,
ValidationMonotonic: codersdk.ValidationMonotonicOrder(param.ValidationMonotonic),
Required: param.Required,
LegacyVariableName: param.LegacyVariableName,
}, nil
}
func Parameters(params []database.ParameterValue) []codersdk.Parameter {
out := make([]codersdk.Parameter, len(params))
for i, p := range params {
out[i] = Parameter(p)
}
return out
}
func Parameter(parameterValue database.ParameterValue) codersdk.Parameter {
return codersdk.Parameter{
ID: parameterValue.ID,
CreatedAt: parameterValue.CreatedAt,
UpdatedAt: parameterValue.UpdatedAt,
Scope: codersdk.ParameterScope(parameterValue.Scope),
ScopeID: parameterValue.ScopeID,
Name: parameterValue.Name,
SourceScheme: codersdk.ParameterSourceScheme(parameterValue.SourceScheme),
DestinationScheme: codersdk.ParameterDestinationScheme(parameterValue.DestinationScheme),
SourceValue: parameterValue.SourceValue,
}
}
func ProvisionerJobStatus(provisionerJob database.ProvisionerJob) codersdk.ProvisionerJobStatus {
switch {
case provisionerJob.CanceledAt.Valid:
if !provisionerJob.CompletedAt.Valid {
return codersdk.ProvisionerJobCanceling
}
if provisionerJob.Error.String == "" {
return codersdk.ProvisionerJobCanceled
}
return codersdk.ProvisionerJobFailed
case !provisionerJob.StartedAt.Valid:
return codersdk.ProvisionerJobPending
case provisionerJob.CompletedAt.Valid:
if provisionerJob.Error.String == "" {
return codersdk.ProvisionerJobSucceeded
}
return codersdk.ProvisionerJobFailed
case database.Now().Sub(provisionerJob.UpdatedAt) > 30*time.Second:
return codersdk.ProvisionerJobFailed
default:
return codersdk.ProvisionerJobRunning
}
}
+191
View File
@@ -0,0 +1,191 @@
package db2sdk_test
import (
"crypto/rand"
"database/sql"
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/db2sdk"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisionersdk/proto"
)
func TestProvisionerJobStatus(t *testing.T) {
t.Parallel()
cases := []struct {
name string
job database.ProvisionerJob
status codersdk.ProvisionerJobStatus
}{
{
name: "canceling",
job: database.ProvisionerJob{
CanceledAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
},
status: codersdk.ProvisionerJobCanceling,
},
{
name: "canceled",
job: database.ProvisionerJob{
CanceledAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
CompletedAt: sql.NullTime{
Time: database.Now().Add(-30 * time.Second),
Valid: true,
},
},
status: codersdk.ProvisionerJobCanceled,
},
{
name: "canceled_failed",
job: database.ProvisionerJob{
CanceledAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
CompletedAt: sql.NullTime{
Time: database.Now().Add(-30 * time.Second),
Valid: true,
},
Error: sql.NullString{String: "badness", Valid: true},
},
status: codersdk.ProvisionerJobFailed,
},
{
name: "pending",
job: database.ProvisionerJob{},
status: codersdk.ProvisionerJobPending,
},
{
name: "succeeded",
job: database.ProvisionerJob{
StartedAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
CompletedAt: sql.NullTime{
Time: database.Now().Add(-30 * time.Second),
Valid: true,
},
},
status: codersdk.ProvisionerJobSucceeded,
},
{
name: "completed_failed",
job: database.ProvisionerJob{
StartedAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
CompletedAt: sql.NullTime{
Time: database.Now().Add(-30 * time.Second),
Valid: true,
},
Error: sql.NullString{String: "badness", Valid: true},
},
status: codersdk.ProvisionerJobFailed,
},
{
name: "not_updated",
job: database.ProvisionerJob{
StartedAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
UpdatedAt: database.Now().Add(-31 * time.Second),
},
status: codersdk.ProvisionerJobFailed,
},
{
name: "updated",
job: database.ProvisionerJob{
StartedAt: sql.NullTime{
Time: database.Now().Add(-time.Minute),
Valid: true,
},
UpdatedAt: database.Now(),
},
status: codersdk.ProvisionerJobRunning,
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
actual := db2sdk.ProvisionerJobStatus(tc.job)
require.Equal(t, tc.status, actual)
})
}
}
func TestTemplateVersionParameter_OK(t *testing.T) {
t.Parallel()
req := require.New(t)
// In this test we're just going to cover the fields that have to get parsed.
options := []*proto.RichParameterOption{
{
Name: "foo",
Description: "bar",
Value: "baz",
Icon: "David Bowie",
},
}
ob, err := json.Marshal(&options)
req.NoError(err)
db := database.TemplateVersionParameter{
Options: json.RawMessage(ob),
Description: "_The Rise and Fall of **Ziggy Stardust** and the Spiders from Mars_",
}
sdk, err := db2sdk.TemplateVersionParameter(db)
req.NoError(err)
req.Len(sdk.Options, 1)
req.Equal("foo", sdk.Options[0].Name)
req.Equal("bar", sdk.Options[0].Description)
req.Equal("baz", sdk.Options[0].Value)
req.Equal("David Bowie", sdk.Options[0].Icon)
req.Equal("The Rise and Fall of Ziggy Stardust and the Spiders from Mars", sdk.DescriptionPlaintext)
}
func TestTemplateVersionParameter_BadOptions(t *testing.T) {
t.Parallel()
req := require.New(t)
db := database.TemplateVersionParameter{
Options: json.RawMessage("not really JSON!"),
Description: "_The Rise and Fall of **Ziggy Stardust** and the Spiders from Mars_",
}
_, err := db2sdk.TemplateVersionParameter(db)
req.Error(err)
}
func TestTemplateVersionParameter_BadDescription(t *testing.T) {
t.Parallel()
req := require.New(t)
desc := make([]byte, 300)
_, err := rand.Read(desc)
req.NoError(err)
db := database.TemplateVersionParameter{
Options: json.RawMessage("[]"),
Description: string(desc),
}
sdk, err := db2sdk.TemplateVersionParameter(db)
// Although the markdown parser can return an error, the way we use it should not, even
// if we feed it garbage data.
req.NoError(err)
req.NotEmpty(sdk.DescriptionPlaintext, "broke the markdown parser with %v", desc)
}
+4
View File
@@ -0,0 +1,4 @@
// package dbmock contains a mocked implementation of the database.Store interface for use in tests
package dbmock
//go:generate mockgen -destination ./store.go -package dbmock github.com/coder/coder/coderd/database Store
File diff suppressed because it is too large Load Diff