diff --git a/.gitignore b/.gitignore index e5a80c26..4d5d5ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ website/vendor !command/test-fixtures/**/.terraform/ # Keep windows files with windows line endings -*.winfile eol=crlf \ No newline at end of file +*.winfile eol=crlf + +# Binary +terraform-provider-coder diff --git a/integration/integration_test.go b/integration/integration_test.go index 1c2046be..cf58b99e 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -164,7 +164,9 @@ func setup(ctx context.Context, t *testing.T, name string) string { "CODER_ACCESS_URL=" + localURL, // Set explicitly to avoid creating try.coder.app URLs. "CODER_IN_MEMORY=true", // We don't necessarily care about real persistence here. "CODER_TELEMETRY_ENABLE=false", // Avoid creating noise. + "CODER_VERBOSE=TRUE", // Debug logging. "TF_CLI_CONFIG_FILE=/tmp/integration.tfrc", // Our custom tfrc from above. + "TF_LOG=DEBUG", // Debug logging in Terraform provider }, Labels: map[string]string{}, }, &container.HostConfig{ diff --git a/provider/agent.go b/provider/agent.go index 7b197870..0ff5ca21 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -3,7 +3,6 @@ package provider import ( "context" "fmt" - "os" "reflect" "strings" @@ -12,10 +11,14 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "golang.org/x/xerrors" + + "github.com/coder/terraform-provider-coder/provider/helpers" ) func agentResource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this resource to associate an agent.", CreateContext: func(_ context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { // This should be a real authentication token! @@ -363,7 +366,7 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia if err != nil { return diag.Errorf("parse access url: %s", err) } - script := os.Getenv(fmt.Sprintf("CODER_AGENT_SCRIPT_%s_%s", operatingSystem, arch)) + script := helpers.OptionalEnv(fmt.Sprintf("CODER_AGENT_SCRIPT_%s_%s", operatingSystem, arch)) if script != "" { script = strings.ReplaceAll(script, "${ACCESS_URL}", accessURL.String()) script = strings.ReplaceAll(script, "${AUTH_TYPE}", auth) diff --git a/provider/app.go b/provider/app.go index ed0ac53e..c2690311 100644 --- a/provider/app.go +++ b/provider/app.go @@ -25,6 +25,8 @@ var ( func appResource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this resource to define shortcuts to access applications in a workspace.", CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { resourceData.SetId(uuid.NewString()) diff --git a/provider/env.go b/provider/env.go index 66d03a22..8f55ff8c 100644 --- a/provider/env.go +++ b/provider/env.go @@ -12,6 +12,8 @@ import ( func envResource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: `Use this resource to set an environment variable in a workspace. Note that this resource cannot be used to overwrite existing environment variables set on the "coder_agent" resource.`, CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) diff --git a/provider/externalauth.go b/provider/externalauth.go index 31dadd66..13c85fab 100644 --- a/provider/externalauth.go +++ b/provider/externalauth.go @@ -3,15 +3,18 @@ package provider import ( "context" "fmt" - "os" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/coder/terraform-provider-coder/provider/helpers" ) // externalAuthDataSource returns a schema for an external authentication data source. func externalAuthDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this data source to require users to authenticate with an external service prior to workspace creation. This can be used to pre-authenticate external services in a workspace. (e.g. gcloud, gh, docker, etc)", ReadContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { id, ok := rd.Get("id").(string) @@ -20,7 +23,7 @@ func externalAuthDataSource() *schema.Resource { } rd.SetId(id) - accessToken := os.Getenv(ExternalAuthAccessTokenEnvironmentVariable(id)) + accessToken := helpers.OptionalEnv(ExternalAuthAccessTokenEnvironmentVariable(id)) rd.Set("access_token", accessToken) return nil }, diff --git a/provider/gitauth.go b/provider/gitauth.go index aa36d493..72c05bcd 100644 --- a/provider/gitauth.go +++ b/provider/gitauth.go @@ -3,15 +3,18 @@ package provider import ( "context" "fmt" - "os" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/coder/terraform-provider-coder/provider/helpers" ) // gitAuthDataSource returns a schema for a Git authentication data source. func gitAuthDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + DeprecationMessage: "Use the `coder_external_auth` data source instead.", Description: "Use this data source to require users to authenticate with a Git provider prior to workspace creation. This can be used to perform an authenticated `git clone` in startup scripts.", ReadContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { @@ -25,7 +28,7 @@ func gitAuthDataSource() *schema.Resource { } rd.SetId(id) - accessToken := os.Getenv(GitAuthAccessTokenEnvironmentVariable(id)) + accessToken := helpers.OptionalEnv(GitAuthAccessTokenEnvironmentVariable(id)) rd.Set("access_token", accessToken) return nil diff --git a/provider/helpers/env.go b/provider/helpers/env.go new file mode 100644 index 00000000..b1007820 --- /dev/null +++ b/provider/helpers/env.go @@ -0,0 +1,31 @@ +package helpers + +import ( + "fmt" + "os" +) + +// RequireEnv requires environment variable to be present. +func RequireEnv(name string) (string, error) { + val := os.Getenv(name) + if val == "" { + return "", fmt.Errorf("%s is required", name) + } + return val, nil +} + +// OptionalEnv returns the value for environment variable if it exists, +// otherwise returns an empty string. +func OptionalEnv(name string) string { + return OptionalEnvOrDefault(name, "") +} + +// OptionalEnvOrDefault returns the value for environment variable if it exists, +// otherwise returns the default value. +func OptionalEnvOrDefault(name, defaultValue string) string { + val := os.Getenv(name) + if val == "" { + return defaultValue + } + return val +} diff --git a/provider/metadata.go b/provider/metadata.go index 3bd5e6f5..00b488e4 100644 --- a/provider/metadata.go +++ b/provider/metadata.go @@ -11,6 +11,8 @@ import ( func metadataResource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this resource to attach metadata to a resource. They will be " + "displayed in the Coder dashboard.", CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { diff --git a/provider/parameter.go b/provider/parameter.go index 12dbc019..d0f71dab 100644 --- a/provider/parameter.go +++ b/provider/parameter.go @@ -63,6 +63,8 @@ type Parameter struct { func parameterDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this data source to configure editable options for workspaces.", ReadContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) diff --git a/provider/provisioner.go b/provider/provisioner.go index 314e89cd..9d356798 100644 --- a/provider/provisioner.go +++ b/provider/provisioner.go @@ -11,6 +11,8 @@ import ( func provisionerDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this data source to get information about the Coder provisioner.", ReadContext: func(c context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) diff --git a/provider/script.go b/provider/script.go index 4a05440e..1474dbd2 100644 --- a/provider/script.go +++ b/provider/script.go @@ -15,6 +15,8 @@ var ScriptCRONParser = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cr func scriptResource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this resource to run a script from an agent.", CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) diff --git a/provider/workspace.go b/provider/workspace.go index 098d64cc..f06e9e1e 100644 --- a/provider/workspace.go +++ b/provider/workspace.go @@ -3,44 +3,38 @@ package provider import ( "context" "encoding/json" - "os" "reflect" "strconv" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/coder/terraform-provider-coder/provider/helpers" ) func workspaceDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this data source to get information for the active workspace build.", ReadContext: func(c context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { - transition := os.Getenv("CODER_WORKSPACE_TRANSITION") - if transition == "" { - // Default to start! - transition = "start" - } + transition := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_TRANSITION", "start") // Default to start! _ = rd.Set("transition", transition) + count := 0 if transition == "start" { count = 1 } _ = rd.Set("start_count", count) - owner := os.Getenv("CODER_WORKSPACE_OWNER") - if owner == "" { - owner = "default" - } + owner := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_OWNER", "default") _ = rd.Set("owner", owner) - ownerEmail := os.Getenv("CODER_WORKSPACE_OWNER_EMAIL") - if ownerEmail == "" { - ownerEmail = "default@example.com" - } + ownerEmail := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_OWNER_EMAIL", "default@example.com") _ = rd.Set("owner_email", ownerEmail) - ownerGroupsText := os.Getenv("CODER_WORKSPACE_OWNER_GROUPS") + ownerGroupsText := helpers.OptionalEnv("CODER_WORKSPACE_OWNER_GROUPS") var ownerGroups []string if ownerGroupsText != "" { err := json.Unmarshal([]byte(ownerGroupsText), &ownerGroups) @@ -50,43 +44,31 @@ func workspaceDataSource() *schema.Resource { } _ = rd.Set("owner_groups", ownerGroups) - ownerName := os.Getenv("CODER_WORKSPACE_OWNER_NAME") - if ownerName == "" { - ownerName = "default" - } + ownerName := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_OWNER_NAME", "default") _ = rd.Set("owner_name", ownerName) - ownerID := os.Getenv("CODER_WORKSPACE_OWNER_ID") - if ownerID == "" { - ownerID = uuid.Nil.String() - } + ownerID := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_OWNER_ID", uuid.Nil.String()) _ = rd.Set("owner_id", ownerID) - ownerOIDCAccessToken := os.Getenv("CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN") + ownerOIDCAccessToken := helpers.OptionalEnv("CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN") _ = rd.Set("owner_oidc_access_token", ownerOIDCAccessToken) - name := os.Getenv("CODER_WORKSPACE_NAME") - if name == "" { - name = "default" - } + name := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_NAME", "default") rd.Set("name", name) - sessionToken := os.Getenv("CODER_WORKSPACE_OWNER_SESSION_TOKEN") + sessionToken := helpers.OptionalEnv("CODER_WORKSPACE_OWNER_SESSION_TOKEN") _ = rd.Set("owner_session_token", sessionToken) - id := os.Getenv("CODER_WORKSPACE_ID") - if id == "" { - id = uuid.NewString() - } + id := helpers.OptionalEnvOrDefault("CODER_WORKSPACE_ID", uuid.NewString()) rd.SetId(id) - templateID := os.Getenv("CODER_WORKSPACE_TEMPLATE_ID") + templateID := helpers.OptionalEnv("CODER_WORKSPACE_TEMPLATE_ID") // FIXME switch to `helpers.RequireEnv(...)` _ = rd.Set("template_id", templateID) - templateName := os.Getenv("CODER_WORKSPACE_TEMPLATE_NAME") + templateName := helpers.OptionalEnv("CODER_WORKSPACE_TEMPLATE_NAME") // FIXME switch to `helpers.RequireEnv(...)` _ = rd.Set("template_name", templateName) - templateVersion := os.Getenv("CODER_WORKSPACE_TEMPLATE_VERSION") + templateVersion := helpers.OptionalEnv("CODER_WORKSPACE_TEMPLATE_VERSION") // FIXME switch to `helpers.RequireEnv(...)` _ = rd.Set("template_version", templateVersion) config, valid := i.(config) diff --git a/provider/workspace_tags.go b/provider/workspace_tags.go index 088ff546..09736d02 100644 --- a/provider/workspace_tags.go +++ b/provider/workspace_tags.go @@ -14,6 +14,8 @@ type WorkspaceTags struct { func workspaceTagDataSource() *schema.Resource { return &schema.Resource{ + SchemaVersion: 1, + Description: "Use this data source to configure workspace tags to select provisioners.", ReadContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { rd.SetId(uuid.NewString())