internal/provider/sdk/resource_gitlab_runner.go (212 lines of code) (raw):

package sdk import ( "context" "fmt" "strconv" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/terraform-provider-gitlab/internal/provider/utils" ) var _ = registerResource("gitlab_runner", func() *schema.Resource { return &schema.Resource{ Description: `The ` + "`gitlab_runner`" + ` resource allows to manage the lifecycle of a runner. A runner can either be registered at an instance level or group level. The runner will be registered at a group level if the token used is from a group, or at an instance level if the token used is for the instance. ~ > Using this resource will register a runner using the deprecated ` + "`registration_token`" + ` flow. To use the new ` + "`authentication_token`" + ` flow instead, use the ` + "`gitlab_user_runner`" + ` resource! **Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/api/runners/#register-a-new-runner)`, DeprecationMessage: `This resource has been deprecated in favor of the ` + "`gitlab_user_runner`" + ` resource. Please use that resource, and the new registration flow, instead.`, CreateContext: resourceGitLabRunnerCreate, UpdateContext: resourceGitLabRunnerUpdate, ReadContext: resourceGitLabRunnerRead, DeleteContext: resourceGitLabRunnerDelete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ "registration_token": { Description: `The registration token used to register the runner.`, Type: schema.TypeString, ForceNew: true, Required: true, Sensitive: true, }, "description": { Description: `The runner's description.`, Type: schema.TypeString, Optional: true, }, "paused": { Description: `Whether the runner should ignore new jobs.`, Type: schema.TypeBool, Optional: true, Computed: true, }, "locked": { Description: `Whether the runner should be locked for current project.`, Type: schema.TypeBool, Optional: true, Computed: true, }, "run_untagged": { Description: `Whether the runner should handle untagged jobs.`, Type: schema.TypeBool, Optional: true, Computed: true, }, "tag_list": { Description: `List of runner’s tags.`, Type: schema.TypeSet, Set: schema.HashString, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, "access_level": { Description: fmt.Sprintf(`The access_level of the runner. Valid values are: %s.`, utils.RenderValueListForDocs(runnerAccessLevelAllowedValues)), Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice(runnerAccessLevelAllowedValues, true), Computed: true, }, "maximum_timeout": { Description: `Maximum timeout set when this runner handles the job.`, Type: schema.TypeInt, Optional: true, }, // While Maintenance Note is available during "create", it is not available during "update", so // excluding it here for now. // Even though the output variable is just called 'token', we need to differentiate between the registration // and authentication token in terraform. "authentication_token": { Description: `The authentication token used for building a config.toml file. This value is not present when imported.`, Type: schema.TypeString, Computed: true, Sensitive: true, }, "status": { Description: `The status of runners to show, one of: online and offline. active and paused are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0.`, Type: schema.TypeString, Computed: true, }, "maintenance_note": { Description: `Free-form maintenance notes for the runner (1024 characters).`, Type: schema.TypeString, Optional: true, }, }, } }) func resourceGitLabRunnerCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) options := &gitlab.RegisterNewRunnerOptions{ Token: gitlab.Ptr(d.Get("registration_token").(string)), } if v, ok := d.GetOk("description"); ok { options.Description = gitlab.Ptr(v.(string)) } // GetOK skips the block if the value is "false", so need to use GetOkExists even though it's deprecated. // nolint:staticcheck // SA1019 ignore deprecated GetOkExists // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("paused"); ok { options.Paused = gitlab.Ptr(v.(bool)) } // nolint:staticcheck // SA1019 ignore deprecated GetOkExist // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("locked"); ok { options.Locked = gitlab.Ptr(v.(bool)) } // nolint:staticcheck // SA1019 ignore deprecated GetOkExists // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("run_untagged"); ok { options.RunUntagged = gitlab.Ptr(v.(bool)) } if v, ok := d.GetOk("tag_list"); ok { options.TagList = stringSetToStringSlice(v.(*schema.Set)) } if v, ok := d.GetOk("access_level"); ok { options.AccessLevel = gitlab.Ptr(v.(string)) } if v, ok := d.GetOk("maximum_timeout"); ok { options.MaximumTimeout = gitlab.Ptr(v.(int)) } if v, ok := d.GetOk("maintenance_note"); ok { options.MaintenanceNote = gitlab.Ptr(v.(string)) } // Explicitly not printing the registration token here, even though it may make debugging a bit trickier, since it's a secret tflog.Debug(ctx, "[DEBUG] Update GitLab Runner using registration token in configuration") runner, _, err := client.Runners.RegisterNewRunner(options, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } d.SetId(strconv.Itoa(runner.ID)) // The authentication_token will ONLY exist during creation, and will not return during "read", so we need to set it here. d.Set("authentication_token", runner.Token) return resourceGitLabRunnerRead(ctx, d, meta) } func resourceGitLabRunnerRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) runnerID, err := strconv.Atoi(d.Id()) if err != nil { return diag.FromErr(err) } runner, _, err := client.Runners.GetRunnerDetails(runnerID, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } d.SetId(strconv.Itoa(runner.ID)) d.Set("description", runner.Description) d.Set("paused", runner.Paused) d.Set("locked", runner.Locked) d.Set("run_untagged", runner.RunUntagged) d.Set("access_level", runner.AccessLevel) d.Set("maximum_timeout", runner.MaximumTimeout) d.Set("status", runner.Status) d.Set("maintenance_note", runner.MaintenanceNote) if err := d.Set("tag_list", runner.TagList); err != nil { return diag.FromErr(fmt.Errorf("[DEBUG] error setting tag list for runner: %s", err)) } return nil } func resourceGitLabRunnerUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) runnerID := d.Id() options := &gitlab.UpdateRunnerDetailsOptions{} if v, ok := d.GetOk("description"); ok { options.Description = gitlab.Ptr(v.(string)) } // GetOK skips the block if the value is "false", so need to use GetOkExists even though it's deprecated. // nolint:staticcheck // SA1019 ignore deprecated GetOkExists // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("paused"); ok { options.Paused = gitlab.Ptr(v.(bool)) } // nolint:staticcheck // SA1019 ignore deprecated GetOkExists // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("locked"); ok { options.Locked = gitlab.Ptr(v.(bool)) } // nolint:staticcheck // SA1019 ignore deprecated GetOkExists // lintignore: XR001 // TODO: replace with alternative for GetOkExists if v, ok := d.GetOkExists("run_untagged"); ok { options.RunUntagged = gitlab.Ptr(v.(bool)) } if v, ok := d.GetOk("tag_list"); ok { options.TagList = stringSetToStringSlice(v.(*schema.Set)) } if v, ok := d.GetOk("access_level"); ok { options.AccessLevel = gitlab.Ptr(v.(string)) } if v, ok := d.GetOk("maximum_timeout"); ok { options.MaximumTimeout = gitlab.Ptr(v.(int)) } if v, ok := d.GetOk("maintenance_note"); ok { options.MaintenanceNote = gitlab.Ptr(v.(string)) } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Update GitLab Runner %s", d.Id())) _, _, err := client.Runners.UpdateRunnerDetails(runnerID, options, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } return resourceGitLabRunnerRead(ctx, d, meta) } func resourceGitLabRunnerDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*gitlab.Client) runnerID, err := strconv.Atoi(d.Id()) if err != nil { return diag.FromErr(err) } tflog.Debug(ctx, fmt.Sprintf("[DEBUG] Delete GitLab Runner %s", d.Id())) _, err = client.Runners.DeleteRegisteredRunnerByID(runnerID, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } return nil } var runnerAccessLevelAllowedValues = []string{ "not_protected", "ref_protected", }