Skip to content

Commit

Permalink
feat: Remove magical parameters from being injected (#371)
Browse files Browse the repository at this point in the history
* 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.

* Add "coder_workspace" data source

* feat: Remove magical parameters from being injected

This is a much cleaner abstraction. Explicitly declaring the user
parameters for each provisioner makes for significantly simpler
testing.
  • Loading branch information
kylecarbs committed Feb 28, 2022
1 parent bd0293a commit e5c9555
Show file tree
Hide file tree
Showing 19 changed files with 571 additions and 323 deletions.
4 changes: 0 additions & 4 deletions cli/projectcreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"golang.org/x/xerrors"

"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/parameter"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/database"
"github.com/coder/coder/provisionerd"
Expand Down Expand Up @@ -187,9 +186,6 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o
if ok {
continue
}
if parameterSchema.Name == parameter.CoderWorkspaceTransition {
continue
}
value, err := prompt(cmd, &promptui.Prompt{
Label: fmt.Sprintf("Enter value for %s:", color.HiCyanString(parameterSchema.Name)),
})
Expand Down
2 changes: 1 addition & 1 deletion cli/projectcreate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func TestProjectCreate(t *testing.T) {
matches := []string{
"organization?", "y",
"name?", "test-project",
"somevar:", "value",
"somevar", "value",
"project?", "y",
"created!", "n",
}
Expand Down
16 changes: 9 additions & 7 deletions coderd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ func Root() *cobra.Command {
Use: "coderd",
RunE: func(cmd *cobra.Command, args []string) error {
logger := slog.Make(sloghuman.Sink(os.Stderr))
accessURL := &url.URL{
Scheme: "http",
Host: address,
}
handler, closeCoderd := coderd.New(&coderd.Options{
Logger: logger,
Database: databasefake.New(),
Pubsub: database.NewPubsubInMemory(),
AccessURL: accessURL,
Logger: logger,
Database: databasefake.New(),
Pubsub: database.NewPubsubInMemory(),
})

listener, err := net.Listen("tcp", address)
Expand All @@ -45,10 +50,7 @@ func Root() *cobra.Command {
}
defer listener.Close()

client := codersdk.New(&url.URL{
Scheme: "http",
Host: address,
})
client := codersdk.New(accessURL)
daemonClose, err := newProvisionerDaemon(cmd.Context(), client, logger)
if err != nil {
return xerrors.Errorf("create provisioner daemon: %w", err)
Expand Down
8 changes: 5 additions & 3 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package coderd

import (
"net/http"
"net/url"
"sync"

"github.com/go-chi/chi/v5"
Expand All @@ -16,9 +17,10 @@ import (

// Options are requires parameters for Coder to start.
type Options struct {
Logger slog.Logger
Database database.Store
Pubsub database.Pubsub
AccessURL *url.URL
Logger slog.Logger
Database database.Store
Pubsub database.Pubsub

GoogleTokenValidator *idtoken.Validator
}
Expand Down
19 changes: 11 additions & 8 deletions coderd/coderdtest/coderdtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,7 @@ func New(t *testing.T, options *Options) *codersdk.Client {
})
}

handler, closeWait := coderd.New(&coderd.Options{
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
Database: db,
Pubsub: pubsub,

GoogleTokenValidator: options.GoogleTokenValidator,
})
srv := httptest.NewUnstartedServer(handler)
srv := httptest.NewUnstartedServer(nil)
srv.Config.BaseContext = func(_ net.Listener) context.Context {
ctx, cancelFunc := context.WithCancel(context.Background())
t.Cleanup(cancelFunc)
Expand All @@ -96,6 +89,16 @@ func New(t *testing.T, options *Options) *codersdk.Client {
srv.Start()
serverURL, err := url.Parse(srv.URL)
require.NoError(t, err)
var closeWait func()
// We set the handler after server creation for the access URL.
srv.Config.Handler, closeWait = coderd.New(&coderd.Options{
AccessURL: serverURL,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
Database: db,
Pubsub: pubsub,

GoogleTokenValidator: options.GoogleTokenValidator,
})
t.Cleanup(func() {
srv.Close()
closeWait()
Expand Down
5 changes: 0 additions & 5 deletions coderd/parameter/compute.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import (
"github.com/coder/coder/database"
)

const (
CoderUsername = "coder_username"
CoderWorkspaceTransition = "coder_workspace_transition"
)

// ComputeScope targets identifiers to pull parameters from.
type ComputeScope struct {
ProjectImportJobID uuid.UUID
Expand Down
33 changes: 27 additions & 6 deletions coderd/provisionerdaemons.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"reflect"
"time"

Expand Down Expand Up @@ -90,6 +91,7 @@ func (api *api) provisionerDaemonsServe(rw http.ResponseWriter, r *http.Request)
}
mux := drpcmux.New()
err = proto.DRPCRegisterProvisionerDaemon(mux, &provisionerdServer{
AccessURL: api.AccessURL,
ID: daemon.ID,
Database: api.Database,
Pubsub: api.Pubsub,
Expand Down Expand Up @@ -117,6 +119,7 @@ type workspaceProvisionJob struct {

// Implementation of the provisioner daemon protobuf server.
type provisionerdServer struct {
AccessURL *url.URL
ID uuid.UUID
Logger slog.Logger
Provisioners []database.ProvisionerType
Expand Down Expand Up @@ -228,23 +231,30 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
}
protoParameters = append(protoParameters, converted)
}
protoParameters = append(protoParameters, &sdkproto.ParameterValue{
DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE,
Name: parameter.CoderWorkspaceTransition,
Value: string(workspaceHistory.Transition),
})
transition, err := convertWorkspaceTransition(workspaceHistory.Transition)
if err != nil {
return nil, failJob(fmt.Sprint("convert workspace transition: %w", err))
}

protoJob.Type = &proto.AcquiredJob_WorkspaceProvision_{
WorkspaceProvision: &proto.AcquiredJob_WorkspaceProvision{
WorkspaceHistoryId: workspaceHistory.ID.String(),
WorkspaceName: workspace.Name,
State: workspaceHistory.ProvisionerState,
ParameterValues: protoParameters,
Metadata: &sdkproto.Provision_Metadata{
CoderUrl: server.AccessURL.String(),
WorkspaceTransition: transition,
},
},
}
case database.ProvisionerJobTypeProjectVersionImport:
protoJob.Type = &proto.AcquiredJob_ProjectImport_{
ProjectImport: &proto.AcquiredJob_ProjectImport{},
ProjectImport: &proto.AcquiredJob_ProjectImport{
Metadata: &sdkproto.Provision_Metadata{
CoderUrl: server.AccessURL.String(),
},
},
}
}
switch job.StorageMethod {
Expand Down Expand Up @@ -660,3 +670,14 @@ func convertComputedParameterValue(param parameter.ComputedValue) (*sdkproto.Par
Value: param.SourceValue,
}, nil
}

func convertWorkspaceTransition(transition database.WorkspaceTransition) (sdkproto.WorkspaceTransition, error) {
switch transition {
case database.WorkspaceTransitionStart:
return sdkproto.WorkspaceTransition_START, nil
case database.WorkspaceTransitionStop:
return sdkproto.WorkspaceTransition_STOP, nil
default:
return 0, xerrors.Errorf("unrecognized transition: %q", transition)
}
}
11 changes: 1 addition & 10 deletions coderd/provisionerjobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/coderdtest"
"github.com/coder/coder/coderd/parameter"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/database"
"github.com/coder/coder/provisioner/echo"
Expand Down Expand Up @@ -180,15 +179,7 @@ func TestProvisionerJobResourcesByID(t *testing.T) {
user := coderdtest.CreateInitialUser(t, client)
_ = coderdtest.NewProvisionerDaemon(t, client)
job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{
Parse: []*proto.Parse_Response{{
Type: &proto.Parse_Response_Complete{
Complete: &proto.Parse_Complete{
ParameterSchemas: []*proto.ParameterSchema{{
Name: parameter.CoderWorkspaceTransition,
}},
},
},
}},
Parse: echo.ParseComplete,
Provision: []*proto.Provision_Response{{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Expand Down
7 changes: 7 additions & 0 deletions codersdk/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package codersdk
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"

"github.com/coder/coder/coderd"
)
Expand All @@ -26,3 +28,8 @@ 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
func (c *Client) DownloadURL(asset string) (*url.URL, error) {
return c.URL.Parse(fmt.Sprintf("/api/v2/downloads/%s", asset))
}
17 changes: 17 additions & 0 deletions provisioner/terraform/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

"github.com/coder/coder/database"
"github.com/coder/coder/provisionersdk"
)

Expand Down Expand Up @@ -54,6 +55,22 @@ func New() *schema.Provider {
}, nil
},
DataSourcesMap: map[string]*schema.Resource{
"coder_workspace": {
Description: "TODO",
ReadContext: func(c context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics {
rd.SetId(uuid.NewString())
return nil
},
Schema: map[string]*schema.Schema{
"transition": {
Type: schema.TypeString,
Optional: true,
Description: "TODO",
DefaultFunc: schema.EnvDefaultFunc("CODER_WORKSPACE_TRANSITION", ""),
ValidateFunc: validation.StringInSlice([]string{string(database.WorkspaceTransitionStart), string(database.WorkspaceTransitionStop)}, false),
},
},
},
"coder_agent_script": {
Description: "TODO",
ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
Expand Down
29 changes: 29 additions & 0 deletions provisioner/terraform/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ func TestProvider(t *testing.T) {
require.NoError(t, err)
}

func TestWorkspace(t *testing.T) {
t.Parallel()
resource.Test(t, resource.TestCase{
Providers: map[string]*schema.Provider{
"coder": provider.New(),
},
IsUnitTest: true,
Steps: []resource.TestStep{{
Config: `
provider "coder" {
url = "https://example.com"
}
data "coder_workspace" "me" {
transition = "start"
}`,
Check: func(state *terraform.State) error {
require.Len(t, state.Modules, 1)
require.Len(t, state.Modules[0].Resources, 1)
resource := state.Modules[0].Resources["data.coder_workspace.me"]
require.NotNil(t, resource)
value := resource.Primary.Attributes["transition"]
require.NotNil(t, value)
t.Log(value)
return nil
},
}},
})
}

func TestAgentScript(t *testing.T) {
t.Parallel()
resource.Test(t, resource.TestCase{
Expand Down
10 changes: 8 additions & 2 deletions provisioner/terraform/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ func (t *terraform) Provision(request *proto.Provision_Request, stream proto.DRP
}

func (t *terraform) runTerraformPlan(ctx context.Context, terraform *tfexec.Terraform, request *proto.Provision_Request, stream proto.DRPCProvisioner_ProvisionStream) error {
env := map[string]string{}
env := map[string]string{
"CODER_URL": request.Metadata.CoderUrl,
"CODER_WORKSPACE_TRANSITION": strings.ToLower(request.Metadata.WorkspaceTransition.String()),
}
planfilePath := filepath.Join(request.Directory, "terraform.tfplan")
options := []tfexec.PlanOption{tfexec.JSON(true), tfexec.Out(planfilePath)}
for _, param := range request.ParameterValues {
Expand Down Expand Up @@ -250,7 +253,10 @@ func (t *terraform) runTerraformPlan(ctx context.Context, terraform *tfexec.Terr
}

func (t *terraform) runTerraformApply(ctx context.Context, terraform *tfexec.Terraform, request *proto.Provision_Request, stream proto.DRPCProvisioner_ProvisionStream, statefilePath string) error {
env := map[string]string{}
env := map[string]string{
"CODER_URL": request.Metadata.CoderUrl,
"CODER_WORKSPACE_TRANSITION": strings.ToLower(request.Metadata.WorkspaceTransition.String()),
}
options := []tfexec.ApplyOption{tfexec.JSON(true)}
for _, param := range request.ParameterValues {
switch param.DestinationScheme {
Expand Down
21 changes: 20 additions & 1 deletion provisioner/terraform/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ terraform {
}
provider "coder" {
url = "https://example.com"
}
`
t.Log(provider)
Expand Down Expand Up @@ -174,6 +173,11 @@ provider "coder" {
]
}`,
},
Request: &proto.Provision_Request{
Metadata: &proto.Provision_Metadata{
CoderUrl: "https://example.com",
},
},
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Expand Down Expand Up @@ -204,6 +208,11 @@ provider "coder" {
}
resource "null_resource" "A" {}`,
},
Request: &proto.Provision_Request{
Metadata: &proto.Provision_Metadata{
CoderUrl: "https://example.com",
},
},
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Complete: &proto.Provision_Complete{
Expand Down Expand Up @@ -235,6 +244,9 @@ provider "coder" {
},
Request: &proto.Provision_Request{
DryRun: true,
Metadata: &proto.Provision_Metadata{
CoderUrl: "https://example.com",
},
},
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Expand Down Expand Up @@ -266,6 +278,9 @@ provider "coder" {
},
Request: &proto.Provision_Request{
DryRun: true,
Metadata: &proto.Provision_Metadata{
CoderUrl: "https://example.com",
},
},
Response: &proto.Provision_Response{
Type: &proto.Provision_Response_Complete{
Expand Down Expand Up @@ -302,6 +317,10 @@ provider "coder" {
request.ParameterValues = testCase.Request.ParameterValues
request.State = testCase.Request.State
request.DryRun = testCase.Request.DryRun
request.Metadata = testCase.Request.Metadata
}
if request.Metadata == nil {
request.Metadata = &proto.Provision_Metadata{}
}
response, err := api.Provision(ctx, request)
require.NoError(t, err)
Expand Down

0 comments on commit e5c9555

Please sign in to comment.