diff --git a/.changelog/2510.txt b/.changelog/2510.txt new file mode 100644 index 0000000000..2d81d5cd2b --- /dev/null +++ b/.changelog/2510.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +Add support for Terraform's experimental deferred actions +``` \ No newline at end of file diff --git a/.github/workflows/acceptance_test_dfa.yaml b/.github/workflows/acceptance_test_dfa.yaml new file mode 100644 index 0000000000..e0c7bb5321 --- /dev/null +++ b/.github/workflows/acceptance_test_dfa.yaml @@ -0,0 +1,32 @@ +name: Deferred Actions + +on: + pull_request: + branches: + - main + paths: + - "manifest/**/*.go" + - 'kubernetes/**/*.go' + - "go.mod" + workflow_dispatch: + inputs: + terraformVersion: + description: Terraform version + default: 1.9.0-alpha20240516 + +jobs: + acceptance_tests: + runs-on: custom-linux-medium + steps: + - name: Checkout repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - name: Set up Go + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + with: + go-version-file: 'go.mod' + - name: Run Tests + env: + TF_ACC: 1 + TF_ACC_TERRAFORM_VERSION: ${{ github.event.inputs.terraformVersion || vars.TERRAFORM_VERSION_EXP }} + run: | + go test -v -run '^TestAccKubernetesDeferredActions' ./kubernetes/test-dfa diff --git a/_examples/deferred-actions/cluster.tf b/_examples/deferred-actions/cluster.tf new file mode 100644 index 0000000000..63833fe610 --- /dev/null +++ b/_examples/deferred-actions/cluster.tf @@ -0,0 +1,22 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_providers { + kind = { + source = "tehcyx/kind" + } + kubernetes = {} + } +} + +resource "kind_cluster" "demo" { + name = "demo-cluster" +} + +provider "kubernetes" { + host = kind_cluster.demo.endpoint + cluster_ca_certificate = kind_cluster.demo.cluster_ca_certificate + client_certificate = kind_cluster.demo.client_certificate + client_key = kind_cluster.demo.client_key +} diff --git a/_examples/deferred-actions/crd.tf b/_examples/deferred-actions/crd.tf new file mode 100644 index 0000000000..f2756a43bc --- /dev/null +++ b/_examples/deferred-actions/crd.tf @@ -0,0 +1,1032 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "kubernetes_manifest" "crd_workspaces" { + manifest = { + "apiVersion" = "apiextensions.k8s.io/v1" + "kind" = "CustomResourceDefinition" + "metadata" = { + "annotations" = { + "controller-gen.kubebuilder.io/version" = "v0.14.0" + } + "name" = "workspaces.app.terraform.io" + } + "spec" = { + "group" = "app.terraform.io" + "names" = { + "kind" = "Workspace" + "listKind" = "WorkspaceList" + "plural" = "workspaces" + "singular" = "workspace" + } + "scope" = "Namespaced" + "versions" = [ + { + "additionalPrinterColumns" = [ + { + "jsonPath" = ".status.workspaceID" + "name" = "Workspace ID" + "type" = "string" + }, + ] + "name" = "v1alpha2" + "schema" = { + "openAPIV3Schema" = { + "description" = "Workspace is the Schema for the workspaces API" + "properties" = { + "apiVersion" = { + "description" = <<-EOT + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + EOT + "type" = "string" + } + "kind" = { + "description" = <<-EOT + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + EOT + "type" = "string" + } + "metadata" = { + "type" = "object" + } + "spec" = { + "description" = "WorkspaceSpec defines the desired state of Workspace." + "properties" = { + "agentPool" = { + "description" = <<-EOT + HCP Terraform Agents allow HCP Terraform to communicate with isolated, private, or on-premises infrastructure. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/agents + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Agent Pool ID. + Must match pattern: `^apool-[a-zA-Z0-9]+$` + EOT + "pattern" = "^apool-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Agent Pool name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "allowDestroyPlan" = { + "default" = true + "description" = <<-EOT + Allows a destroy plan to be created and applied. + Default: `true`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#destruction-and-deletion + EOT + "type" = "boolean" + } + "applyMethod" = { + "default" = "manual" + "description" = <<-EOT + Define either change will be applied automatically(auto) or require an operator to confirm(manual). + Must be one of the following values: `auto`, `manual`. + Default: `manual`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#auto-apply-and-manual-apply + EOT + "pattern" = "^(auto|manual)$" + "type" = "string" + } + "description" = { + "description" = "Workspace description." + "minLength" = 1 + "type" = "string" + } + "environmentVariables" = { + "description" = <<-EOT + Terraform Environment variables for all plans and applies in this workspace. + Variables defined within a workspace always overwrite variables from variable sets that have the same type and the same key. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables#environment-variables + EOT + "items" = { + "description" = <<-EOT + Variables let you customize configurations, modify Terraform's behavior, and store information like provider credentials. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + EOT + "properties" = { + "description" = { + "description" = "Description of the variable." + "minLength" = 1 + "type" = "string" + } + "hcl" = { + "default" = false + "description" = <<-EOT + Parse this field as HashiCorp Configuration Language (HCL). This allows you to interpolate values at runtime. + Default: `false`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Name of the variable." + "minLength" = 1 + "type" = "string" + } + "sensitive" = { + "default" = false + "description" = <<-EOT + Sensitive variables are never shown in the UI or API. + They may appear in Terraform logs if your configuration is designed to output them. + Default: `false`. + EOT + "type" = "boolean" + } + "value" = { + "description" = "Value of the variable." + "minLength" = 1 + "type" = "string" + } + "valueFrom" = { + "description" = "Source for the variable's value. Cannot be used if value is not empty." + "properties" = { + "configMapKeyRef" = { + "description" = "Selects a key of a ConfigMap." + "properties" = { + "key" = { + "description" = "The key to select." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the ConfigMap or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + "secretKeyRef" = { + "description" = "Selects a key of a Secret." + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "type" = "object" + } + } + "required" = [ + "name", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "executionMode" = { + "default" = "remote" + "description" = <<-EOT + Define where the Terraform code will be executed. + Must be one of the following values: `agent`, `local`, `remote`. + Default: `remote`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#execution-mode + EOT + "pattern" = "^(agent|local|remote)$" + "type" = "string" + } + "name" = { + "description" = "Workspace name." + "minLength" = 1 + "type" = "string" + } + "notifications" = { + "description" = <<-EOT + Notifications allow you to send messages to other applications based on run and workspace events. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/notifications + EOT + "items" = { + "description" = <<-EOT + Notifications allow you to send messages to other applications based on run and workspace events. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/notifications + EOT + "properties" = { + "emailAddresses" = { + "description" = <<-EOT + The list of email addresses that will receive notification emails. + It is only available for Terraform Enterprise users. It is not available in HCP Terraform. + EOT + "items" = { + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "emailUsers" = { + "description" = "The list of users belonging to the organization that will receive notification emails." + "items" = { + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "enabled" = { + "default" = true + "description" = <<-EOT + Whether the notification configuration should be enabled or not. + Default: `true`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Notification name." + "minLength" = 1 + "type" = "string" + } + "token" = { + "description" = "The token of the notification." + "minLength" = 1 + "type" = "string" + } + "triggers" = { + "description" = <<-EOT + The list of run events that will trigger notifications. + Trigger represents the different TFC notifications that can be sent as a run's progress transitions between different states. + There are two categories of triggers: + - Health Events: `assessment:check_failure`, `assessment:drifted`, `assessment:failed`. + - Run Events: `run:applying`, `run:completed`, `run:created`, `run:errored`, `run:needs_attention`, `run:planning`. + EOT + "items" = { + "description" = <<-EOT + NotificationTrigger represents the different TFC notifications that can be sent as a run's progress transitions between different states. + This must be aligned with go-tfe type `NotificationTriggerType`. + Must be one of the following values: `run:applying`, `assessment:check_failure`, `run:completed`, `run:created`, `assessment:drifted`, `run:errored`, `assessment:failed`, `run:needs_attention`, `run:planning`. + EOT + "enum" = [ + "run:applying", + "assessment:check_failure", + "run:completed", + "run:created", + "assessment:drifted", + "run:errored", + "assessment:failed", + "run:needs_attention", + "run:planning", + ] + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "type" = { + "description" = <<-EOT + The type of the notification. + Must be one of the following values: `email`, `generic`, `microsoft-teams`, `slack`. + EOT + "enum" = [ + "email", + "generic", + "microsoft-teams", + "slack", + ] + "type" = "string" + } + "url" = { + "description" = <<-EOT + The URL of the notification. + Must match pattern: `^https?://.*` + EOT + "pattern" = "^https?://.*" + "type" = "string" + } + } + "required" = [ + "name", + "type", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "organization" = { + "description" = <<-EOT + Organization name where the Workspace will be created. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/organizations + EOT + "minLength" = 1 + "type" = "string" + } + "project" = { + "description" = <<-EOT + Projects let you organize your workspaces into groups. + Default: default organization project. + More information: + - https://developer.hashicorp.com/terraform/tutorials/cloud/projects + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Project ID. + Must match pattern: `^prj-[a-zA-Z0-9]+$` + EOT + "pattern" = "^prj-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Project name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "remoteStateSharing" = { + "description" = <<-EOT + Remote state access between workspaces. + By default, new workspaces in HCP Terraform do not allow other workspaces to access their state. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/state#accessing-state-from-other-workspaces + EOT + "properties" = { + "allWorkspaces" = { + "default" = false + "description" = <<-EOT + Allow access to the state for all workspaces within the same organization. + Default: `false`. + EOT + "type" = "boolean" + } + "workspaces" = { + "description" = "Allow access to the state for specific workspaces within the same organization." + "items" = { + "description" = <<-EOT + ConsumerWorkspace allows access to the state for specific workspaces within the same organization. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/state#remote-state-access-controls + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Consumer Workspace ID. + Must match pattern: `^ws-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ws-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Consumer Workspace name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + } + "type" = "object" + } + "runTasks" = { + "description" = <<-EOT + Run tasks allow HCP Terraform to interact with external systems at specific points in the HCP Terraform run lifecycle. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-tasks + EOT + "items" = { + "description" = <<-EOT + Run tasks allow HCP Terraform to interact with external systems at specific points in the HCP Terraform run lifecycle. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-tasks + EOT + "properties" = { + "enforcementLevel" = { + "default" = "advisory" + "description" = <<-EOT + Run Task Enforcement Level. Can be one of `advisory` or `mandatory`. Default: `advisory`. + Must be one of the following values: `advisory`, `mandatory` + Default: `advisory`. + EOT + "pattern" = "^(advisory|mandatory)$" + "type" = "string" + } + "id" = { + "description" = <<-EOT + Run Task ID. + Must match pattern: `^task-[a-zA-Z0-9]+$` + EOT + "pattern" = "^task-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Run Task Name." + "minLength" = 1 + "type" = "string" + } + "stage" = { + "default" = "post_plan" + "description" = <<-EOT + Run Task Stage. + Must be one of the following values: `pre_apply`, `pre_plan`, `post_plan`. + Default: `post_plan`. + EOT + "pattern" = "^(pre_apply|pre_plan|post_plan)$" + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "runTriggers" = { + "description" = <<-EOT + Run triggers allow you to connect this workspace to one or more source workspaces. + These connections allow runs to queue automatically in this workspace on successful apply of runs in any of the source workspaces. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-triggers + EOT + "items" = { + "description" = <<-EOT + RunTrigger allows you to connect this workspace to one or more source workspaces. + These connections allow runs to queue automatically in this workspace on successful apply of runs in any of the source workspaces. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-triggers + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Source Workspace ID. + Must match pattern: `^ws-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ws-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Source Workspace Name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "sshKey" = { + "description" = <<-EOT + SSH key used to clone Terraform modules. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/ssh-keys + EOT + "properties" = { + "id" = { + "description" = <<-EOT + SSH key ID. + Must match pattern: `^sshkey-[a-zA-Z0-9]+$` + EOT + "pattern" = "^sshkey-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "SSH key name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "tags" = { + "description" = <<-EOT + Workspace tags are used to help identify and group together workspaces. + Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number. + EOT + "items" = { + "description" = <<-EOT + Tags allows you to correlate, organize, and even filter workspaces based on the assigned tags. + Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number. + Must match pattern: `^[A-Za-z0-9][A-Za-z0-9:_-]*$` + EOT + "pattern" = "^[A-Za-z0-9][A-Za-z0-9:_-]*$" + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "teamAccess" = { + "description" = <<-EOT + HCP Terraform workspaces can only be accessed by users with the correct permissions. + You can manage permissions for a workspace on a per-team basis. + When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it, + with full admin permissions. These teams' access can't be removed from a workspace. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/access + EOT + "items" = { + "description" = <<-EOT + HCP Terraform workspaces can only be accessed by users with the correct permissions. + You can manage permissions for a workspace on a per-team basis. + When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it, + with full admin permissions. These teams' access can't be removed from a workspace. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/access + EOT + "properties" = { + "access" = { + "description" = <<-EOT + There are two ways to choose which permissions a given team has on a workspace: fixed permission sets, and custom permissions. + Must be one of the following values: `admin`, `custom`, `plan`, `read`, `write`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/permissions#workspace-permissions + EOT + "pattern" = "^(admin|custom|plan|read|write)$" + "type" = "string" + } + "custom" = { + "description" = <<-EOT + Custom permissions let you assign specific, finer-grained permissions to a team than the broader fixed permission sets provide. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/permissions#custom-workspace-permissions + EOT + "properties" = { + "runTasks" = { + "description" = <<-EOT + Manage Workspace Run Tasks. + Default: `false`. + EOT + "type" = "boolean" + } + "runs" = { + "default" = "read" + "description" = <<-EOT + Run access. + Must be one of the following values: `apply`, `plan`, `read`. + Default: `read`. + EOT + "pattern" = "^(apply|plan|read)$" + "type" = "string" + } + "sentinel" = { + "default" = "none" + "description" = <<-EOT + Download Sentinel mocks. + Must be one of the following values: `none`, `read`. + Default: `none`. + EOT + "pattern" = "^(none|read)$" + "type" = "string" + } + "stateVersions" = { + "default" = "none" + "description" = <<-EOT + State access. + Must be one of the following values: `none`, `read`, `read-outputs`, `write`. + Default: `none`. + EOT + "pattern" = "^(none|read|read-outputs|write)$" + "type" = "string" + } + "variables" = { + "default" = "none" + "description" = <<-EOT + Variable access. + Must be one of the following values: `none`, `read`, `write`. + Default: `none`. + EOT + "pattern" = "^(none|read|write)$" + "type" = "string" + } + "workspaceLocking" = { + "default" = false + "description" = <<-EOT + Lock/unlock workspace. + Default: `false`. + EOT + "type" = "boolean" + } + } + "type" = "object" + } + "team" = { + "description" = <<-EOT + Team to grant access. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/teams + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Team ID. + Must match pattern: `^team-[a-zA-Z0-9]+$` + EOT + "pattern" = "^team-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Team name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + } + "required" = [ + "access", + "team", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "terraformVariables" = { + "description" = <<-EOT + Terraform variables for all plans and applies in this workspace. + Variables defined within a workspace always overwrite variables from variable sets that have the same type and the same key. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables#terraform-variables + EOT + "items" = { + "description" = <<-EOT + Variables let you customize configurations, modify Terraform's behavior, and store information like provider credentials. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + EOT + "properties" = { + "description" = { + "description" = "Description of the variable." + "minLength" = 1 + "type" = "string" + } + "hcl" = { + "default" = false + "description" = <<-EOT + Parse this field as HashiCorp Configuration Language (HCL). This allows you to interpolate values at runtime. + Default: `false`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Name of the variable." + "minLength" = 1 + "type" = "string" + } + "sensitive" = { + "default" = false + "description" = <<-EOT + Sensitive variables are never shown in the UI or API. + They may appear in Terraform logs if your configuration is designed to output them. + Default: `false`. + EOT + "type" = "boolean" + } + "value" = { + "description" = "Value of the variable." + "minLength" = 1 + "type" = "string" + } + "valueFrom" = { + "description" = "Source for the variable's value. Cannot be used if value is not empty." + "properties" = { + "configMapKeyRef" = { + "description" = "Selects a key of a ConfigMap." + "properties" = { + "key" = { + "description" = "The key to select." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the ConfigMap or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + "secretKeyRef" = { + "description" = "Selects a key of a Secret." + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "type" = "object" + } + } + "required" = [ + "name", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "terraformVersion" = { + "description" = <<-EOT + The version of Terraform to use for this workspace. + If not specified, the latest available version will be used. + Must match pattern: `^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$` + More information: + - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-version + EOT + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + "token" = { + "description" = "API Token to be used for API calls." + "properties" = { + "secretKeyRef" = { + "description" = "Selects a key of a secret in the workspace's namespace" + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "required" = [ + "secretKeyRef", + ] + "type" = "object" + } + "versionControl" = { + "description" = <<-EOT + Settings for the workspace's VCS repository, enabling the UI/VCS-driven run workflow. + Omit this argument to utilize the CLI-driven and API-driven workflows, where runs are not driven by webhooks on your VCS provider. + More information: + - https://www.terraform.io/cloud-docs/run/ui + - https://www.terraform.io/cloud-docs/vcs + EOT + "properties" = { + "branch" = { + "description" = "The repository branch that Run will execute from. This defaults to the repository's default branch (e.g. main)." + "minLength" = 1 + "type" = "string" + } + "oAuthTokenID" = { + "description" = <<-EOT + The VCS Connection (OAuth Connection + Token) to use. + Must match pattern: `^ot-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ot-[a-zA-Z0-9]+$" + "type" = "string" + } + "repository" = { + "description" = "A reference to your VCS repository in the format `/` where `` and `` refer to the organization and repository in your VCS provider." + "minLength" = 1 + "type" = "string" + } + "speculativePlans" = { + "default" = true + "description" = <<-EOT + Whether this workspace allows automatic speculative plans on PR. + Default: `true`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/run/ui#speculative-plans-on-pull-requests + - https://developer.hashicorp.com/terraform/cloud-docs/run/remote-operations#speculative-plans + EOT + "type" = "boolean" + } + } + "type" = "object" + } + "workingDirectory" = { + "description" = <<-EOT + The directory where Terraform will execute, specified as a relative path from the root of the configuration directory. + More information: + - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-working-directory + EOT + "minLength" = 1 + "type" = "string" + } + } + "required" = [ + "name", + "organization", + "token", + ] + "type" = "object" + } + "status" = { + "description" = "WorkspaceStatus defines the observed state of Workspace." + "properties" = { + "observedGeneration" = { + "description" = "Real world state generation." + "format" = "int64" + "type" = "integer" + } + "plan" = { + "description" = "Run status of plan-only/speculative plan that was triggered manually." + "properties" = { + "id" = { + "description" = "Latest plan-only/speculative plan HCP Terraform run ID." + "type" = "string" + } + "status" = { + "description" = "Latest plan-only/speculative plan HCP Terraform run status." + "type" = "string" + } + "terraformVersion" = { + "description" = "The version of Terraform to use for this run." + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + } + "type" = "object" + } + "runStatus" = { + "description" = "Workspace Runs status." + "properties" = { + "configurationVersion" = { + "description" = "The configuration version of this run." + "type" = "string" + } + "id" = { + "description" = "Current(both active and finished) HCP Terraform run ID." + "type" = "string" + } + "outputRunID" = { + "description" = "Run ID of the latest run that could update the outputs." + "type" = "string" + } + "status" = { + "description" = "Current(both active and finished) HCP Terraform run status." + "type" = "string" + } + } + "type" = "object" + } + "terraformVersion" = { + "description" = "Workspace Terraform version." + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + "updateAt" = { + "description" = "Workspace last update timestamp." + "format" = "int64" + "type" = "integer" + } + "variables" = { + "description" = "Workspace variables." + "items" = { + "properties" = { + "category" = { + "description" = "Category of the variable." + "type" = "string" + } + "id" = { + "description" = "ID of the variable." + "type" = "string" + } + "name" = { + "description" = "Name of the variable." + "type" = "string" + } + "valueID" = { + "description" = "ValueID is a hash of the variable on the CRD end." + "type" = "string" + } + "versionID" = { + "description" = "VersionID is a hash of the variable on the TFC end." + "type" = "string" + } + } + "required" = [ + "category", + "id", + "name", + "valueID", + "versionID", + ] + "type" = "object" + } + "type" = "array" + } + "workspaceID" = { + "description" = "Workspace ID that is managed by the controller." + "type" = "string" + } + } + "required" = [ + "workspaceID", + ] + "type" = "object" + } + } + "required" = [ + "spec", + ] + "type" = "object" + } + } + "served" = true + "storage" = true + "subresources" = { + "status" = {} + } + }, + ] + } + } +} diff --git a/_examples/deferred-actions/workspace.tf b/_examples/deferred-actions/workspace.tf new file mode 100644 index 0000000000..c6f2d6b356 --- /dev/null +++ b/_examples/deferred-actions/workspace.tf @@ -0,0 +1,29 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "kubernetes_namespace_v1" "demo_ns" { + metadata { + name = "demo-ns" + } +} + +resource "kubernetes_manifest" "demo_workspace" { + manifest = { + apiVersion = "app.terraform.io/v1alpha2" + kind = kubernetes_manifest.crd_workspaces.object.spec.names.kind + metadata = { + name = "deferred-demo" + namespace = kubernetes_namespace_v1.demo_ns.id + } + spec = { + name = "demo-ws" + organization = "demo-org" + token = { + secretKeyRef = { + name = "demo-token" + key = "token" + } + } + } + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index f675b3a578..159d9b1f13 100644 --- a/go.mod +++ b/go.mod @@ -6,26 +6,26 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/getkin/kin-openapi v0.111.0 github.com/google/go-cmp v0.6.0 - github.com/hashicorp/go-hclog v1.6.2 + github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.6.0 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/hc-install v0.6.3 - github.com/hashicorp/hcl/v2 v2.20.0 - github.com/hashicorp/terraform-exec v0.20.0 - github.com/hashicorp/terraform-json v0.21.0 + github.com/hashicorp/hc-install v0.6.4 + github.com/hashicorp/hcl/v2 v2.20.1 + github.com/hashicorp/terraform-exec v0.21.0 + github.com/hashicorp/terraform-json v0.22.1 github.com/hashicorp/terraform-plugin-docs v0.16.0 github.com/hashicorp/terraform-plugin-framework v1.7.0 - github.com/hashicorp/terraform-plugin-go v0.22.1 + github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-mux v0.15.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 - github.com/hashicorp/terraform-plugin-testing v1.7.0 + github.com/hashicorp/terraform-plugin-mux v0.16.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 + github.com/hashicorp/terraform-plugin-testing v1.8.0 github.com/jinzhu/copier v0.3.5 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure v1.1.0 github.com/robfig/cron v1.2.0 github.com/stretchr/testify v1.8.2 - golang.org/x/mod v0.15.0 + golang.org/x/mod v0.16.0 k8s.io/api v0.28.6 k8s.io/apiextensions-apiserver v0.28.6 k8s.io/apimachinery v0.28.6 @@ -40,7 +40,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect - github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect + github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect @@ -61,7 +61,7 @@ require ( golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/tools v0.16.1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect ) require ( @@ -80,7 +80,7 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -123,18 +123,18 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/xlab/treeprint v1.2.0 // indirect - github.com/zclconf/go-cty v1.14.3 + github.com/zclconf/go-cty v1.14.4 go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/grpc v1.62.1 - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/grpc v1.63.2 + google.golang.org/protobuf v1.34.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e049ba9520..4c1e0e14ef 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= -github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= @@ -78,8 +78,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -112,8 +112,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= @@ -153,8 +153,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= -github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= @@ -165,30 +165,30 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.6.3 h1:yE/r1yJvWbtrJ0STwScgEnCanb0U9v7zp0Gbkmcoxqs= -github.com/hashicorp/hc-install v0.6.3/go.mod h1:KamGdbodYzlufbWh4r9NRo8y6GLHWZP2GBtdnms1Ln0= -github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4= -github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk= +github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0= +github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA= +github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc= +github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= -github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= -github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= -github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= +github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= +github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec= +github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI= github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw= github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI= -github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w= -github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI= +github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= +github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= -github.com/hashicorp/terraform-plugin-mux v0.15.0/go.mod h1:9ezplb1Dyq394zQ+ldB0nvy/qbNAz3mMoHHseMTMaKo= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= -github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= -github.com/hashicorp/terraform-plugin-testing v1.7.0/go.mod h1:sbAreCleJNOCz+y5vVHV8EJkIWZKi/t4ndKiUjM9vao= +github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= +github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg= +github.com/hashicorp/terraform-plugin-testing v1.8.0 h1:wdYIgwDk4iO933gC4S8KbKdnMQShu6BXuZQPScmHvpk= +github.com/hashicorp/terraform-plugin-testing v1.8.0/go.mod h1:o2kOgf18ADUaZGhtOl0YCkfIxg01MAiMATT2EtIHlZk= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -229,8 +229,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -307,13 +305,13 @@ github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3V github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -351,8 +349,10 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.14.3 h1:1JXy1XroaGrzZuG6X9dt7HL6s9AwbY+l4UNL8o5B6ho= -github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= +github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -362,8 +362,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= @@ -373,8 +373,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -389,8 +389,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -417,22 +417,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -457,13 +457,13 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -474,8 +474,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/internal/framework/provider/provider.go b/internal/framework/provider/provider.go index 977760449a..f953b38875 100644 --- a/internal/framework/provider/provider.go +++ b/internal/framework/provider/provider.go @@ -173,8 +173,9 @@ func (p *KubernetesProvider) Schema(ctx context.Context, req provider.SchemaRequ NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "manifest_resource": schema.BoolAttribute{ - Description: "Enable the `kubernetes_manifest` resource.", - Optional: true, + Description: "Enable the `kubernetes_manifest` resource.", + Optional: true, + DeprecationMessage: "The kubernetes_manifest resource is now permanently enabled and no longer considered an experiment. This flag has no effect and will be removed in the near future.", }, }, }, diff --git a/internal/mux/mux.go b/internal/mux/mux.go new file mode 100644 index 0000000000..c4c279e1ff --- /dev/null +++ b/internal/mux/mux.go @@ -0,0 +1,25 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package mux + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" + framework "github.com/hashicorp/terraform-provider-kubernetes/internal/framework/provider" + "github.com/hashicorp/terraform-provider-kubernetes/kubernetes" + manifest "github.com/hashicorp/terraform-provider-kubernetes/manifest/provider" +) + +func MuxServer(ctx context.Context, v string) (tfprotov5.ProviderServer, error) { + providers := []func() tfprotov5.ProviderServer{ + kubernetes.Provider().GRPCProvider, + manifest.Provider(), + providerserver.NewProtocol5(framework.New(v)), + } + + return tf5muxserver.NewMuxServer(ctx, providers...) +} diff --git a/kubernetes/provider.go b/kubernetes/provider.go index c81bd92966..430bcf7d61 100644 --- a/kubernetes/provider.go +++ b/kubernetes/provider.go @@ -190,6 +190,7 @@ func Provider() *schema.Provider { return true, nil }, Description: "Enable the `kubernetes_manifest` resource.", + Deprecated: "The kubernetes_manifest resource is now permanently enabled and no longer considered an experiment. This flag has no effect and will be removed in the near future.", }, }, }, @@ -360,8 +361,13 @@ func Provider() *schema.Provider { }, } - p.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - return providerConfigure(ctx, d, p.TerraformVersion) + p.ConfigureProvider = func(ctx context.Context, req schema.ConfigureProviderRequest, res *schema.ConfigureProviderResponse) { + if req.DeferralAllowed && !req.ResourceData.GetRawConfig().IsWhollyKnown() { + res.Deferred = &schema.Deferred{ + Reason: schema.DeferredReasonProviderConfigUnknown, + } + } + res.Meta, res.Diagnostics = providerConfigure(ctx, req.ResourceData, p.TerraformVersion) } return p @@ -374,7 +380,7 @@ type KubeClientsets interface { DiscoveryClient() (discovery.DiscoveryInterface, error) } -type kubeClientsets struct { +type providerMetadata struct { // TODO: this struct has become overloaded we should // rename this or break it into smaller structs config *restclient.Config @@ -387,7 +393,7 @@ type kubeClientsets struct { IgnoreLabels []string } -func (k kubeClientsets) MainClientset() (*kubernetes.Clientset, error) { +func (k providerMetadata) MainClientset() (*kubernetes.Clientset, error) { if k.mainClientset != nil { return k.mainClientset, nil } @@ -402,7 +408,7 @@ func (k kubeClientsets) MainClientset() (*kubernetes.Clientset, error) { return k.mainClientset, nil } -func (k kubeClientsets) AggregatorClientset() (*aggregator.Clientset, error) { +func (k providerMetadata) AggregatorClientset() (*aggregator.Clientset, error) { if k.aggregatorClientset != nil { return k.aggregatorClientset, nil } @@ -416,7 +422,7 @@ func (k kubeClientsets) AggregatorClientset() (*aggregator.Clientset, error) { return k.aggregatorClientset, nil } -func (k kubeClientsets) DynamicClient() (dynamic.Interface, error) { +func (k providerMetadata) DynamicClient() (dynamic.Interface, error) { if k.dynamicClient != nil { return k.dynamicClient, nil } @@ -431,7 +437,7 @@ func (k kubeClientsets) DynamicClient() (dynamic.Interface, error) { return k.dynamicClient, nil } -func (k kubeClientsets) DiscoveryClient() (discovery.DiscoveryInterface, error) { +func (k providerMetadata) DiscoveryClient() (discovery.DiscoveryInterface, error) { if k.discoveryClient != nil { return k.discoveryClient, nil } @@ -448,9 +454,9 @@ func (k kubeClientsets) DiscoveryClient() (discovery.DiscoveryInterface, error) func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVersion string) (interface{}, diag.Diagnostics) { // Config initialization - cfg, err := initializeConfiguration(d) - if err != nil { - return nil, diag.FromErr(err) + cfg, diags := initializeConfiguration(d) + if diags.HasError() { + return nil, diags } if cfg == nil { // This is a TEMPORARY measure to work around https://github.com/hashicorp/terraform/issues/24055 @@ -479,7 +485,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVer ignoreLabels = expandStringSlice(v) } - m := kubeClientsets{ + m := providerMetadata{ config: cfg, mainClientset: nil, aggregatorClientset: nil, @@ -489,7 +495,8 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVer return m, diag.Diagnostics{} } -func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error) { +func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, diag.Diagnostics) { + diags := make(diag.Diagnostics, 0) overrides := &clientcmd.ConfigOverrides{} loader := &clientcmd.ClientConfigLoadingRules{} @@ -512,7 +519,7 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error) for _, p := range configPaths { path, err := homedir.Expand(p) if err != nil { - return nil, err + return nil, append(diags, diag.FromErr(err)...) } log.Printf("[DEBUG] Using kubeconfig: %s", path) @@ -571,12 +578,17 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error) // see https://github.com/kubernetes/client-go/blob/v12.0.0/rest/url_utils.go#L85-L87 hasCA := len(overrides.ClusterInfo.CertificateAuthorityData) != 0 hasCert := len(overrides.AuthInfo.ClientCertificateData) != 0 - defaultTLS := hasCA || hasCert || overrides.ClusterInfo.InsecureSkipTLSVerify + defaultTLS := (hasCA || hasCert) && !overrides.ClusterInfo.InsecureSkipTLSVerify host, _, err := restclient.DefaultServerURL(v.(string), "", apimachineryschema.GroupVersion{}, defaultTLS) if err != nil { - return nil, fmt.Errorf("Failed to parse host: %s", err) + nd := diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("Failed to parse value for host: %s", v.(string)), + Detail: err.Error(), + AttributePath: cty.Path{}.IndexString("host"), + } + return nil, append(diags, nd) } - overrides.ClusterInfo.Server = host.String() } if v, ok := d.GetOk("username"); ok { @@ -603,7 +615,12 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error) exec.Env = append(exec.Env, clientcmdapi.ExecEnvVar{Name: kk, Value: vv.(string)}) } } else { - return nil, fmt.Errorf("Failed to parse exec") + nd := diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to parse 'exec' provider configuration", + AttributePath: cty.Path{}.IndexString("exec"), + } + return nil, append(diags, nd) } overrides.AuthInfo.Exec = exec } @@ -615,11 +632,16 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error) cc := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loader, overrides) cfg, err := cc.ClientConfig() if err != nil { - log.Printf("[WARN] Invalid provider configuration was supplied. Provider operations likely to fail: %v", err) - return nil, nil + nd := diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Provider was supplied an invalid configuration. Further operations likely to fail.", + Detail: err.Error(), + } + log.Printf("[WARN] Provider was supplied an invalid configuration. Further operations likely to fail: %v", err) + return nil, append(diags, nd) } - return cfg, nil + return cfg, diags } var useadmissionregistrationv1beta1 *bool diff --git a/kubernetes/resource_kubernetes_env_test.go b/kubernetes/resource_kubernetes_env_test.go index b134b2063b..162ee5c64b 100644 --- a/kubernetes/resource_kubernetes_env_test.go +++ b/kubernetes/resource_kubernetes_env_test.go @@ -253,7 +253,7 @@ func TestAccKubernetesEnv_CronJob_initContainer(t *testing.T) { } func createInitContainerEnv(t *testing.T, name, namespace string) error { - conn, err := testAccProvider.Meta().(kubeClientsets).MainClientset() + conn, err := testAccProvider.Meta().(providerMetadata).MainClientset() if err != nil { return err } @@ -315,7 +315,7 @@ func createInitContainerEnv(t *testing.T, name, namespace string) error { } func createEnv(t *testing.T, name, namespace string) error { - conn, err := testAccProvider.Meta().(kubeClientsets).MainClientset() + conn, err := testAccProvider.Meta().(providerMetadata).MainClientset() if err != nil { return err } @@ -365,7 +365,7 @@ func createEnv(t *testing.T, name, namespace string) error { } func createCronJobEnv(t *testing.T, name, namespace string) error { - conn, err := testAccProvider.Meta().(kubeClientsets).MainClientset() + conn, err := testAccProvider.Meta().(providerMetadata).MainClientset() if err != nil { return err } @@ -424,7 +424,7 @@ func createCronJobEnv(t *testing.T, name, namespace string) error { } func createCronJobInitContainerEnv(t *testing.T, name, namespace string) error { - conn, err := testAccProvider.Meta().(kubeClientsets).MainClientset() + conn, err := testAccProvider.Meta().(providerMetadata).MainClientset() if err != nil { return err } diff --git a/kubernetes/structures.go b/kubernetes/structures.go index 02c9c3983b..d0696740a1 100644 --- a/kubernetes/structures.go +++ b/kubernetes/structures.go @@ -134,15 +134,15 @@ func flattenMetadataFields(meta metav1.ObjectMeta) []interface{} { return []interface{}{m} } -func flattenMetadata(meta metav1.ObjectMeta, d *schema.ResourceData, providerMetadata interface{}) []interface{} { +func flattenMetadata(meta metav1.ObjectMeta, d *schema.ResourceData, providerMeta interface{}) []interface{} { metadataAnnotations := d.Get("metadata.0.annotations").(map[string]interface{}) metadataLabels := d.Get("metadata.0.labels").(map[string]interface{}) - ignoreAnnotations := providerMetadata.(kubeClientsets).IgnoreAnnotations + ignoreAnnotations := providerMeta.(providerMetadata).IgnoreAnnotations removeInternalKeys(meta.Annotations, metadataAnnotations) removeKeys(meta.Annotations, metadataAnnotations, ignoreAnnotations) - ignoreLabels := providerMetadata.(kubeClientsets).IgnoreLabels + ignoreLabels := providerMeta.(providerMetadata).IgnoreLabels removeInternalKeys(meta.Labels, metadataLabels) removeKeys(meta.Labels, metadataLabels, ignoreLabels) diff --git a/kubernetes/structures_test.go b/kubernetes/structures_test.go index d0a97511e1..5e3db73a97 100644 --- a/kubernetes/structures_test.go +++ b/kubernetes/structures_test.go @@ -159,7 +159,7 @@ func TestFlattenMetadata(t *testing.T) { uid := "7e9439cb-2584-4b50-81bc-441127e11b26" cases := map[string]struct { meta metav1.ObjectMeta - providerMeta kubeClientsets + providerMeta providerMetadata expected []interface{} }{ "IgnoreAnnotations": { @@ -180,7 +180,7 @@ func TestFlattenMetadata(t *testing.T) { ResourceVersion: "1", UID: types.UID(uid), }, - kubeClientsets{ + providerMetadata{ IgnoreAnnotations: []string{"foo.example.com"}, IgnoreLabels: []string{}, }, @@ -216,7 +216,7 @@ func TestFlattenMetadata(t *testing.T) { ResourceVersion: "1", UID: types.UID(uid), }, - kubeClientsets{ + providerMetadata{ IgnoreAnnotations: []string{}, IgnoreLabels: []string{"foo"}, }, @@ -252,7 +252,7 @@ func TestFlattenMetadata(t *testing.T) { ResourceVersion: "1", UID: types.UID(uid), }, - kubeClientsets{ + providerMetadata{ IgnoreAnnotations: []string{"foo.example.com"}, IgnoreLabels: []string{"foo"}, }, diff --git a/kubernetes/test-dfa/config-basic/cluster.tf b/kubernetes/test-dfa/config-basic/cluster.tf new file mode 100644 index 0000000000..337e0ba4bb --- /dev/null +++ b/kubernetes/test-dfa/config-basic/cluster.tf @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_providers { + kind = { + source = "tehcyx/kind" + } + } +} + +resource "kind_cluster" "demo" { + name = "tfacc" +} + +provider "kubernetes" { + host = kind_cluster.demo.endpoint + cluster_ca_certificate = kind_cluster.demo.cluster_ca_certificate + client_certificate = kind_cluster.demo.client_certificate + client_key = kind_cluster.demo.client_key +} diff --git a/kubernetes/test-dfa/config-basic/crd.tf b/kubernetes/test-dfa/config-basic/crd.tf new file mode 100644 index 0000000000..f2756a43bc --- /dev/null +++ b/kubernetes/test-dfa/config-basic/crd.tf @@ -0,0 +1,1032 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "kubernetes_manifest" "crd_workspaces" { + manifest = { + "apiVersion" = "apiextensions.k8s.io/v1" + "kind" = "CustomResourceDefinition" + "metadata" = { + "annotations" = { + "controller-gen.kubebuilder.io/version" = "v0.14.0" + } + "name" = "workspaces.app.terraform.io" + } + "spec" = { + "group" = "app.terraform.io" + "names" = { + "kind" = "Workspace" + "listKind" = "WorkspaceList" + "plural" = "workspaces" + "singular" = "workspace" + } + "scope" = "Namespaced" + "versions" = [ + { + "additionalPrinterColumns" = [ + { + "jsonPath" = ".status.workspaceID" + "name" = "Workspace ID" + "type" = "string" + }, + ] + "name" = "v1alpha2" + "schema" = { + "openAPIV3Schema" = { + "description" = "Workspace is the Schema for the workspaces API" + "properties" = { + "apiVersion" = { + "description" = <<-EOT + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + EOT + "type" = "string" + } + "kind" = { + "description" = <<-EOT + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + EOT + "type" = "string" + } + "metadata" = { + "type" = "object" + } + "spec" = { + "description" = "WorkspaceSpec defines the desired state of Workspace." + "properties" = { + "agentPool" = { + "description" = <<-EOT + HCP Terraform Agents allow HCP Terraform to communicate with isolated, private, or on-premises infrastructure. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/agents + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Agent Pool ID. + Must match pattern: `^apool-[a-zA-Z0-9]+$` + EOT + "pattern" = "^apool-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Agent Pool name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "allowDestroyPlan" = { + "default" = true + "description" = <<-EOT + Allows a destroy plan to be created and applied. + Default: `true`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#destruction-and-deletion + EOT + "type" = "boolean" + } + "applyMethod" = { + "default" = "manual" + "description" = <<-EOT + Define either change will be applied automatically(auto) or require an operator to confirm(manual). + Must be one of the following values: `auto`, `manual`. + Default: `manual`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#auto-apply-and-manual-apply + EOT + "pattern" = "^(auto|manual)$" + "type" = "string" + } + "description" = { + "description" = "Workspace description." + "minLength" = 1 + "type" = "string" + } + "environmentVariables" = { + "description" = <<-EOT + Terraform Environment variables for all plans and applies in this workspace. + Variables defined within a workspace always overwrite variables from variable sets that have the same type and the same key. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables#environment-variables + EOT + "items" = { + "description" = <<-EOT + Variables let you customize configurations, modify Terraform's behavior, and store information like provider credentials. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + EOT + "properties" = { + "description" = { + "description" = "Description of the variable." + "minLength" = 1 + "type" = "string" + } + "hcl" = { + "default" = false + "description" = <<-EOT + Parse this field as HashiCorp Configuration Language (HCL). This allows you to interpolate values at runtime. + Default: `false`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Name of the variable." + "minLength" = 1 + "type" = "string" + } + "sensitive" = { + "default" = false + "description" = <<-EOT + Sensitive variables are never shown in the UI or API. + They may appear in Terraform logs if your configuration is designed to output them. + Default: `false`. + EOT + "type" = "boolean" + } + "value" = { + "description" = "Value of the variable." + "minLength" = 1 + "type" = "string" + } + "valueFrom" = { + "description" = "Source for the variable's value. Cannot be used if value is not empty." + "properties" = { + "configMapKeyRef" = { + "description" = "Selects a key of a ConfigMap." + "properties" = { + "key" = { + "description" = "The key to select." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the ConfigMap or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + "secretKeyRef" = { + "description" = "Selects a key of a Secret." + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "type" = "object" + } + } + "required" = [ + "name", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "executionMode" = { + "default" = "remote" + "description" = <<-EOT + Define where the Terraform code will be executed. + Must be one of the following values: `agent`, `local`, `remote`. + Default: `remote`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#execution-mode + EOT + "pattern" = "^(agent|local|remote)$" + "type" = "string" + } + "name" = { + "description" = "Workspace name." + "minLength" = 1 + "type" = "string" + } + "notifications" = { + "description" = <<-EOT + Notifications allow you to send messages to other applications based on run and workspace events. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/notifications + EOT + "items" = { + "description" = <<-EOT + Notifications allow you to send messages to other applications based on run and workspace events. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/notifications + EOT + "properties" = { + "emailAddresses" = { + "description" = <<-EOT + The list of email addresses that will receive notification emails. + It is only available for Terraform Enterprise users. It is not available in HCP Terraform. + EOT + "items" = { + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "emailUsers" = { + "description" = "The list of users belonging to the organization that will receive notification emails." + "items" = { + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "enabled" = { + "default" = true + "description" = <<-EOT + Whether the notification configuration should be enabled or not. + Default: `true`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Notification name." + "minLength" = 1 + "type" = "string" + } + "token" = { + "description" = "The token of the notification." + "minLength" = 1 + "type" = "string" + } + "triggers" = { + "description" = <<-EOT + The list of run events that will trigger notifications. + Trigger represents the different TFC notifications that can be sent as a run's progress transitions between different states. + There are two categories of triggers: + - Health Events: `assessment:check_failure`, `assessment:drifted`, `assessment:failed`. + - Run Events: `run:applying`, `run:completed`, `run:created`, `run:errored`, `run:needs_attention`, `run:planning`. + EOT + "items" = { + "description" = <<-EOT + NotificationTrigger represents the different TFC notifications that can be sent as a run's progress transitions between different states. + This must be aligned with go-tfe type `NotificationTriggerType`. + Must be one of the following values: `run:applying`, `assessment:check_failure`, `run:completed`, `run:created`, `assessment:drifted`, `run:errored`, `assessment:failed`, `run:needs_attention`, `run:planning`. + EOT + "enum" = [ + "run:applying", + "assessment:check_failure", + "run:completed", + "run:created", + "assessment:drifted", + "run:errored", + "assessment:failed", + "run:needs_attention", + "run:planning", + ] + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "type" = { + "description" = <<-EOT + The type of the notification. + Must be one of the following values: `email`, `generic`, `microsoft-teams`, `slack`. + EOT + "enum" = [ + "email", + "generic", + "microsoft-teams", + "slack", + ] + "type" = "string" + } + "url" = { + "description" = <<-EOT + The URL of the notification. + Must match pattern: `^https?://.*` + EOT + "pattern" = "^https?://.*" + "type" = "string" + } + } + "required" = [ + "name", + "type", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "organization" = { + "description" = <<-EOT + Organization name where the Workspace will be created. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/organizations + EOT + "minLength" = 1 + "type" = "string" + } + "project" = { + "description" = <<-EOT + Projects let you organize your workspaces into groups. + Default: default organization project. + More information: + - https://developer.hashicorp.com/terraform/tutorials/cloud/projects + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Project ID. + Must match pattern: `^prj-[a-zA-Z0-9]+$` + EOT + "pattern" = "^prj-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Project name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "remoteStateSharing" = { + "description" = <<-EOT + Remote state access between workspaces. + By default, new workspaces in HCP Terraform do not allow other workspaces to access their state. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/state#accessing-state-from-other-workspaces + EOT + "properties" = { + "allWorkspaces" = { + "default" = false + "description" = <<-EOT + Allow access to the state for all workspaces within the same organization. + Default: `false`. + EOT + "type" = "boolean" + } + "workspaces" = { + "description" = "Allow access to the state for specific workspaces within the same organization." + "items" = { + "description" = <<-EOT + ConsumerWorkspace allows access to the state for specific workspaces within the same organization. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/state#remote-state-access-controls + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Consumer Workspace ID. + Must match pattern: `^ws-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ws-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Consumer Workspace name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + } + "type" = "object" + } + "runTasks" = { + "description" = <<-EOT + Run tasks allow HCP Terraform to interact with external systems at specific points in the HCP Terraform run lifecycle. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-tasks + EOT + "items" = { + "description" = <<-EOT + Run tasks allow HCP Terraform to interact with external systems at specific points in the HCP Terraform run lifecycle. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-tasks + EOT + "properties" = { + "enforcementLevel" = { + "default" = "advisory" + "description" = <<-EOT + Run Task Enforcement Level. Can be one of `advisory` or `mandatory`. Default: `advisory`. + Must be one of the following values: `advisory`, `mandatory` + Default: `advisory`. + EOT + "pattern" = "^(advisory|mandatory)$" + "type" = "string" + } + "id" = { + "description" = <<-EOT + Run Task ID. + Must match pattern: `^task-[a-zA-Z0-9]+$` + EOT + "pattern" = "^task-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Run Task Name." + "minLength" = 1 + "type" = "string" + } + "stage" = { + "default" = "post_plan" + "description" = <<-EOT + Run Task Stage. + Must be one of the following values: `pre_apply`, `pre_plan`, `post_plan`. + Default: `post_plan`. + EOT + "pattern" = "^(pre_apply|pre_plan|post_plan)$" + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "runTriggers" = { + "description" = <<-EOT + Run triggers allow you to connect this workspace to one or more source workspaces. + These connections allow runs to queue automatically in this workspace on successful apply of runs in any of the source workspaces. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-triggers + EOT + "items" = { + "description" = <<-EOT + RunTrigger allows you to connect this workspace to one or more source workspaces. + These connections allow runs to queue automatically in this workspace on successful apply of runs in any of the source workspaces. + Only one of the fields `ID` or `Name` is allowed. + At least one of the fields `ID` or `Name` is mandatory. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/run-triggers + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Source Workspace ID. + Must match pattern: `^ws-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ws-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Source Workspace Name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "sshKey" = { + "description" = <<-EOT + SSH key used to clone Terraform modules. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/ssh-keys + EOT + "properties" = { + "id" = { + "description" = <<-EOT + SSH key ID. + Must match pattern: `^sshkey-[a-zA-Z0-9]+$` + EOT + "pattern" = "^sshkey-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "SSH key name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + "tags" = { + "description" = <<-EOT + Workspace tags are used to help identify and group together workspaces. + Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number. + EOT + "items" = { + "description" = <<-EOT + Tags allows you to correlate, organize, and even filter workspaces based on the assigned tags. + Tags must be one or more characters; can include letters, numbers, colons, hyphens, and underscores; and must begin and end with a letter or number. + Must match pattern: `^[A-Za-z0-9][A-Za-z0-9:_-]*$` + EOT + "pattern" = "^[A-Za-z0-9][A-Za-z0-9:_-]*$" + "type" = "string" + } + "minItems" = 1 + "type" = "array" + } + "teamAccess" = { + "description" = <<-EOT + HCP Terraform workspaces can only be accessed by users with the correct permissions. + You can manage permissions for a workspace on a per-team basis. + When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it, + with full admin permissions. These teams' access can't be removed from a workspace. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/access + EOT + "items" = { + "description" = <<-EOT + HCP Terraform workspaces can only be accessed by users with the correct permissions. + You can manage permissions for a workspace on a per-team basis. + When a workspace is created, only the owners team and teams with the "manage workspaces" permission can access it, + with full admin permissions. These teams' access can't be removed from a workspace. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings/access + EOT + "properties" = { + "access" = { + "description" = <<-EOT + There are two ways to choose which permissions a given team has on a workspace: fixed permission sets, and custom permissions. + Must be one of the following values: `admin`, `custom`, `plan`, `read`, `write`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/permissions#workspace-permissions + EOT + "pattern" = "^(admin|custom|plan|read|write)$" + "type" = "string" + } + "custom" = { + "description" = <<-EOT + Custom permissions let you assign specific, finer-grained permissions to a team than the broader fixed permission sets provide. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/permissions#custom-workspace-permissions + EOT + "properties" = { + "runTasks" = { + "description" = <<-EOT + Manage Workspace Run Tasks. + Default: `false`. + EOT + "type" = "boolean" + } + "runs" = { + "default" = "read" + "description" = <<-EOT + Run access. + Must be one of the following values: `apply`, `plan`, `read`. + Default: `read`. + EOT + "pattern" = "^(apply|plan|read)$" + "type" = "string" + } + "sentinel" = { + "default" = "none" + "description" = <<-EOT + Download Sentinel mocks. + Must be one of the following values: `none`, `read`. + Default: `none`. + EOT + "pattern" = "^(none|read)$" + "type" = "string" + } + "stateVersions" = { + "default" = "none" + "description" = <<-EOT + State access. + Must be one of the following values: `none`, `read`, `read-outputs`, `write`. + Default: `none`. + EOT + "pattern" = "^(none|read|read-outputs|write)$" + "type" = "string" + } + "variables" = { + "default" = "none" + "description" = <<-EOT + Variable access. + Must be one of the following values: `none`, `read`, `write`. + Default: `none`. + EOT + "pattern" = "^(none|read|write)$" + "type" = "string" + } + "workspaceLocking" = { + "default" = false + "description" = <<-EOT + Lock/unlock workspace. + Default: `false`. + EOT + "type" = "boolean" + } + } + "type" = "object" + } + "team" = { + "description" = <<-EOT + Team to grant access. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/teams + EOT + "properties" = { + "id" = { + "description" = <<-EOT + Team ID. + Must match pattern: `^team-[a-zA-Z0-9]+$` + EOT + "pattern" = "^team-[a-zA-Z0-9]+$" + "type" = "string" + } + "name" = { + "description" = "Team name." + "minLength" = 1 + "type" = "string" + } + } + "type" = "object" + } + } + "required" = [ + "access", + "team", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "terraformVariables" = { + "description" = <<-EOT + Terraform variables for all plans and applies in this workspace. + Variables defined within a workspace always overwrite variables from variable sets that have the same type and the same key. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables#terraform-variables + EOT + "items" = { + "description" = <<-EOT + Variables let you customize configurations, modify Terraform's behavior, and store information like provider credentials. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/workspaces/variables + EOT + "properties" = { + "description" = { + "description" = "Description of the variable." + "minLength" = 1 + "type" = "string" + } + "hcl" = { + "default" = false + "description" = <<-EOT + Parse this field as HashiCorp Configuration Language (HCL). This allows you to interpolate values at runtime. + Default: `false`. + EOT + "type" = "boolean" + } + "name" = { + "description" = "Name of the variable." + "minLength" = 1 + "type" = "string" + } + "sensitive" = { + "default" = false + "description" = <<-EOT + Sensitive variables are never shown in the UI or API. + They may appear in Terraform logs if your configuration is designed to output them. + Default: `false`. + EOT + "type" = "boolean" + } + "value" = { + "description" = "Value of the variable." + "minLength" = 1 + "type" = "string" + } + "valueFrom" = { + "description" = "Source for the variable's value. Cannot be used if value is not empty." + "properties" = { + "configMapKeyRef" = { + "description" = "Selects a key of a ConfigMap." + "properties" = { + "key" = { + "description" = "The key to select." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the ConfigMap or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + "secretKeyRef" = { + "description" = "Selects a key of a Secret." + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "type" = "object" + } + } + "required" = [ + "name", + ] + "type" = "object" + } + "minItems" = 1 + "type" = "array" + } + "terraformVersion" = { + "description" = <<-EOT + The version of Terraform to use for this workspace. + If not specified, the latest available version will be used. + Must match pattern: `^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$` + More information: + - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-version + EOT + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + "token" = { + "description" = "API Token to be used for API calls." + "properties" = { + "secretKeyRef" = { + "description" = "Selects a key of a secret in the workspace's namespace" + "properties" = { + "key" = { + "description" = "The key of the secret to select from. Must be a valid secret key." + "type" = "string" + } + "name" = { + "description" = <<-EOT + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + EOT + "type" = "string" + } + "optional" = { + "description" = "Specify whether the Secret or its key must be defined" + "type" = "boolean" + } + } + "required" = [ + "key", + ] + "type" = "object" + "x-kubernetes-map-type" = "atomic" + } + } + "required" = [ + "secretKeyRef", + ] + "type" = "object" + } + "versionControl" = { + "description" = <<-EOT + Settings for the workspace's VCS repository, enabling the UI/VCS-driven run workflow. + Omit this argument to utilize the CLI-driven and API-driven workflows, where runs are not driven by webhooks on your VCS provider. + More information: + - https://www.terraform.io/cloud-docs/run/ui + - https://www.terraform.io/cloud-docs/vcs + EOT + "properties" = { + "branch" = { + "description" = "The repository branch that Run will execute from. This defaults to the repository's default branch (e.g. main)." + "minLength" = 1 + "type" = "string" + } + "oAuthTokenID" = { + "description" = <<-EOT + The VCS Connection (OAuth Connection + Token) to use. + Must match pattern: `^ot-[a-zA-Z0-9]+$` + EOT + "pattern" = "^ot-[a-zA-Z0-9]+$" + "type" = "string" + } + "repository" = { + "description" = "A reference to your VCS repository in the format `/` where `` and `` refer to the organization and repository in your VCS provider." + "minLength" = 1 + "type" = "string" + } + "speculativePlans" = { + "default" = true + "description" = <<-EOT + Whether this workspace allows automatic speculative plans on PR. + Default: `true`. + More information: + - https://developer.hashicorp.com/terraform/cloud-docs/run/ui#speculative-plans-on-pull-requests + - https://developer.hashicorp.com/terraform/cloud-docs/run/remote-operations#speculative-plans + EOT + "type" = "boolean" + } + } + "type" = "object" + } + "workingDirectory" = { + "description" = <<-EOT + The directory where Terraform will execute, specified as a relative path from the root of the configuration directory. + More information: + - https://www.terraform.io/cloud-docs/workspaces/settings#terraform-working-directory + EOT + "minLength" = 1 + "type" = "string" + } + } + "required" = [ + "name", + "organization", + "token", + ] + "type" = "object" + } + "status" = { + "description" = "WorkspaceStatus defines the observed state of Workspace." + "properties" = { + "observedGeneration" = { + "description" = "Real world state generation." + "format" = "int64" + "type" = "integer" + } + "plan" = { + "description" = "Run status of plan-only/speculative plan that was triggered manually." + "properties" = { + "id" = { + "description" = "Latest plan-only/speculative plan HCP Terraform run ID." + "type" = "string" + } + "status" = { + "description" = "Latest plan-only/speculative plan HCP Terraform run status." + "type" = "string" + } + "terraformVersion" = { + "description" = "The version of Terraform to use for this run." + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + } + "type" = "object" + } + "runStatus" = { + "description" = "Workspace Runs status." + "properties" = { + "configurationVersion" = { + "description" = "The configuration version of this run." + "type" = "string" + } + "id" = { + "description" = "Current(both active and finished) HCP Terraform run ID." + "type" = "string" + } + "outputRunID" = { + "description" = "Run ID of the latest run that could update the outputs." + "type" = "string" + } + "status" = { + "description" = "Current(both active and finished) HCP Terraform run status." + "type" = "string" + } + } + "type" = "object" + } + "terraformVersion" = { + "description" = "Workspace Terraform version." + "pattern" = "^\\d{1}\\.\\d{1,2}\\.\\d{1,2}$" + "type" = "string" + } + "updateAt" = { + "description" = "Workspace last update timestamp." + "format" = "int64" + "type" = "integer" + } + "variables" = { + "description" = "Workspace variables." + "items" = { + "properties" = { + "category" = { + "description" = "Category of the variable." + "type" = "string" + } + "id" = { + "description" = "ID of the variable." + "type" = "string" + } + "name" = { + "description" = "Name of the variable." + "type" = "string" + } + "valueID" = { + "description" = "ValueID is a hash of the variable on the CRD end." + "type" = "string" + } + "versionID" = { + "description" = "VersionID is a hash of the variable on the TFC end." + "type" = "string" + } + } + "required" = [ + "category", + "id", + "name", + "valueID", + "versionID", + ] + "type" = "object" + } + "type" = "array" + } + "workspaceID" = { + "description" = "Workspace ID that is managed by the controller." + "type" = "string" + } + } + "required" = [ + "workspaceID", + ] + "type" = "object" + } + } + "required" = [ + "spec", + ] + "type" = "object" + } + } + "served" = true + "storage" = true + "subresources" = { + "status" = {} + } + }, + ] + } + } +} diff --git a/kubernetes/test-dfa/config-basic/workspace.tf b/kubernetes/test-dfa/config-basic/workspace.tf new file mode 100644 index 0000000000..c6f2d6b356 --- /dev/null +++ b/kubernetes/test-dfa/config-basic/workspace.tf @@ -0,0 +1,29 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "kubernetes_namespace_v1" "demo_ns" { + metadata { + name = "demo-ns" + } +} + +resource "kubernetes_manifest" "demo_workspace" { + manifest = { + apiVersion = "app.terraform.io/v1alpha2" + kind = kubernetes_manifest.crd_workspaces.object.spec.names.kind + metadata = { + name = "deferred-demo" + namespace = kubernetes_namespace_v1.demo_ns.id + } + spec = { + name = "demo-ws" + organization = "demo-org" + token = { + secretKeyRef = { + name = "demo-token" + key = "token" + } + } + } + } +} \ No newline at end of file diff --git a/kubernetes/test-dfa/deferred_actions_test.go b/kubernetes/test-dfa/deferred_actions_test.go new file mode 100644 index 0000000000..4d038bd078 --- /dev/null +++ b/kubernetes/test-dfa/deferred_actions_test.go @@ -0,0 +1,116 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testdfa + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/hashicorp/terraform-provider-kubernetes/internal/mux" +) + +var muxFactory = map[string]func() (tfprotov5.ProviderServer, error){ + "kubernetes": func() (tfprotov5.ProviderServer, error) { + return mux.MuxServer(context.Background(), "Test") + }, +} + +func TestAccKubernetesDeferredActions_2_step(t *testing.T) { + resource.Test(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_9_0), + }, + AdditionalCLIOptions: &resource.AdditionalCLIOptions{ + Plan: resource.PlanOptions{AllowDeferral: true}, + Apply: resource.ApplyOptions{AllowDeferral: true}, + }, + Steps: []resource.TestStep{ + { + ProtoV5ProviderFactories: muxFactory, + ConfigDirectory: func(tscr config.TestStepConfigRequest) string { + return "./config-basic" + }, + ExpectNonEmptyPlan: true, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("kind_cluster.demo", plancheck.ResourceActionCreate), + plancheck.ExpectDeferredChange("kubernetes_namespace_v1.demo_ns", plancheck.DeferredReasonProviderConfigUnknown), + plancheck.ExpectDeferredChange("kubernetes_manifest.crd_workspaces", plancheck.DeferredReasonProviderConfigUnknown), + plancheck.ExpectDeferredChange("kubernetes_manifest.demo_workspace", plancheck.DeferredReasonProviderConfigUnknown), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("kubernetes_namespace_v1.demo_ns", plancheck.ResourceActionCreate), + plancheck.ExpectResourceAction("kubernetes_manifest.crd_workspaces", plancheck.ResourceActionCreate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("endpoint"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("cluster_ca_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_key"), knownvalue.NotNull()), + }, + }, + { + ProtoV5ProviderFactories: muxFactory, + ConfigDirectory: func(tscr config.TestStepConfigRequest) string { + return "./config-basic" + }, + ExpectNonEmptyPlan: true, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("kubernetes_namespace_v1.demo_ns", plancheck.ResourceActionCreate), + plancheck.ExpectDeferredChange("kubernetes_manifest.demo_workspace", plancheck.DeferredReasonResourceConfigUnknown), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("kubernetes_manifest.demo_workspace", plancheck.ResourceActionCreate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("endpoint"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("cluster_ca_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_key"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_namespace_v1.demo_ns", tfjsonpath.New("metadata").AtSliceIndex(0).AtMapKey("name"), knownvalue.StringExact("demo-ns")), + statecheck.ExpectKnownValue("kubernetes_manifest.crd_workspaces", tfjsonpath.New("manifest"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_manifest.crd_workspaces", tfjsonpath.New("object"), knownvalue.NotNull()), + }, + }, + { + ProtoV5ProviderFactories: muxFactory, + ConfigDirectory: func(tscr config.TestStepConfigRequest) string { + return "./config-basic" + }, + ExpectNonEmptyPlan: false, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectNoDeferredChanges(), + plancheck.ExpectResourceAction("kubernetes_manifest.demo_workspace", plancheck.ResourceActionCreate), + }, + PostApplyPostRefresh: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("endpoint"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("cluster_ca_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_certificate"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kind_cluster.demo", tfjsonpath.New("client_key"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_namespace_v1.demo_ns", tfjsonpath.New("metadata").AtSliceIndex(0).AtMapKey("name"), knownvalue.StringExact("demo-ns")), + statecheck.ExpectKnownValue("kubernetes_manifest.crd_workspaces", tfjsonpath.New("manifest"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_manifest.crd_workspaces", tfjsonpath.New("object"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_manifest.demo_workspace", tfjsonpath.New("manifest"), knownvalue.NotNull()), + statecheck.ExpectKnownValue("kubernetes_manifest.demo_workspace", tfjsonpath.New("object"), knownvalue.NotNull()), + }, + }, + }, + }) +} diff --git a/main.go b/main.go index 4de13c683e..9dbb3cfea8 100644 --- a/main.go +++ b/main.go @@ -14,14 +14,9 @@ import ( "github.com/hashicorp/go-plugin" "github.com/hashicorp/terraform-exec/tfexec" - "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov5" tf5server "github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server" - tf5muxserver "github.com/hashicorp/terraform-plugin-mux/tf5muxserver" - - framework "github.com/hashicorp/terraform-provider-kubernetes/internal/framework/provider" - "github.com/hashicorp/terraform-provider-kubernetes/kubernetes" - manifest "github.com/hashicorp/terraform-provider-kubernetes/manifest/provider" + "github.com/hashicorp/terraform-provider-kubernetes/internal/mux" ) const ( @@ -37,14 +32,9 @@ func main() { debugFlag := flag.Bool("debug", false, "Start provider in stand-alone debug mode.") flag.Parse() - providers := []func() tfprotov5.ProviderServer{ - kubernetes.Provider().GRPCProvider, - manifest.Provider(), - providerserver.NewProtocol5(framework.New(Version)), - } - ctx := context.Background() - muxer, err := tf5muxserver.NewMuxServer(ctx, providers...) + + muxer, err := mux.MuxServer(ctx, Version) if err != nil { log.Println(err.Error()) os.Exit(1) @@ -64,15 +54,16 @@ func main() { opts = append(opts, tf5server.WithDebug(ctx, reattachConfigCh, nil)) } - tf5server.Serve(providerName, muxer.ProviderServer, opts...) + tf5server.Serve(providerName, func() tfprotov5.ProviderServer { return muxer }, opts...) } // convertReattachConfig converts plugin.ReattachConfig to tfexec.ReattachConfig func convertReattachConfig(reattachConfig *plugin.ReattachConfig) tfexec.ReattachConfig { return tfexec.ReattachConfig{ - Protocol: string(reattachConfig.Protocol), - Pid: reattachConfig.Pid, - Test: true, + Protocol: string(reattachConfig.Protocol), + ProtocolVersion: reattachConfig.ProtocolVersion, + Pid: reattachConfig.Pid, + Test: true, Addr: tfexec.ReattachConfigAddr{ Network: reattachConfig.Addr.Network(), String: reattachConfig.Addr.String(), diff --git a/manifest/provider/configure.go b/manifest/provider/configure.go index 9210e2fab9..cd57f8a209 100644 --- a/manifest/provider/configure.go +++ b/manifest/provider/configure.go @@ -49,6 +49,13 @@ func (s *RawProviderServer) ConfigureProvider(ctx context.Context, req *tfprotov }) return response, nil } + + clcp := req.ClientCapabilities + if !cfgVal.IsFullyKnown() && clcp != nil && clcp.DeferralAllowed { + // need to deferr actions + s.clientConfigUnknown = true + } + err = cfgVal.As(&providerConfig) if err != nil { // invalid configuration schema - this shouldn't happen, bail out now @@ -60,66 +67,6 @@ func (s *RawProviderServer) ConfigureProvider(ctx context.Context, req *tfprotov return response, nil } - providerEnabled := true - if !providerConfig["experiments"].IsNull() && providerConfig["experiments"].IsKnown() { - var experimentsBlock []tftypes.Value - err = providerConfig["experiments"].As(&experimentsBlock) - if err != nil { - // invalid configuration schema - this shouldn't happen, bail out now - response.Diagnostics = append(response.Diagnostics, &tfprotov5.Diagnostic{ - Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Provider configuration: failed to extract 'experiments' value", - Detail: err.Error(), - }) - return response, nil - } - if len(experimentsBlock) > 0 { - var experimentsObj map[string]tftypes.Value - err := experimentsBlock[0].As(&experimentsObj) - if err != nil { - // invalid configuration schema - this shouldn't happen, bail out now - response.Diagnostics = append(response.Diagnostics, &tfprotov5.Diagnostic{ - Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Provider configuration: failed to extract 'experiments' value", - Detail: err.Error(), - }) - return response, nil - } - if !experimentsObj["manifest_resource"].IsNull() && experimentsObj["manifest_resource"].IsKnown() { - err = experimentsObj["manifest_resource"].As(&providerEnabled) - if err != nil { - // invalid attribute type - this shouldn't happen, bail out for now - response.Diagnostics = append(response.Diagnostics, &tfprotov5.Diagnostic{ - Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Provider configuration: failed to extract 'manifest_resource' value", - Detail: err.Error(), - }) - return response, nil - } - } - } - } - if v := os.Getenv("TF_X_KUBERNETES_MANIFEST_RESOURCE"); v != "" { - providerEnabled, err = strconv.ParseBool(v) - if err != nil { - if err != nil { - // invalid attribute type - this shouldn't happen, bail out for now - response.Diagnostics = append(response.Diagnostics, &tfprotov5.Diagnostic{ - Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Provider configuration: failed to parse boolean from `TF_X_KUBERNETES_MANIFEST_RESOURCE` env var", - Detail: err.Error(), - }) - return response, nil - } - } - } - s.providerEnabled = providerEnabled - - if !providerEnabled { - // Configure should be a noop when not enabled in the provider block - return response, nil - } - overrides := &clientcmd.ConfigOverrides{} loader := &clientcmd.ClientConfigLoadingRules{} @@ -673,13 +620,6 @@ func (s *RawProviderServer) ConfigureProvider(ctx context.Context, req *tfprotov } func (s *RawProviderServer) canExecute() (resp []*tfprotov5.Diagnostic) { - if !s.providerEnabled { - resp = append(resp, &tfprotov5.Diagnostic{ - Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Experimental feature not enabled.", - Detail: "The `kubernetes_manifest` resource is an experimental feature and must be explicitly enabled in the provider configuration block.", - }) - } if semver.IsValid(s.hostTFVersion) && semver.Compare(s.hostTFVersion, minTFVersion) < 0 { resp = append(resp, &tfprotov5.Diagnostic{ Severity: tfprotov5.DiagnosticSeverityError, diff --git a/manifest/provider/import.go b/manifest/provider/import.go index c9397faede..cf6417c01f 100644 --- a/manifest/provider/import.go +++ b/manifest/provider/import.go @@ -25,6 +25,15 @@ func (s *RawProviderServer) ImportResourceState(ctx context.Context, req *tfprot // Presumably the Kubernetes API machinery already has a standard for expressing such a group. We should look there first. resp := &tfprotov5.ImportResourceStateResponse{} + cp := req.ClientCapabilities + if cp != nil && cp.DeferralAllowed && s.clientConfigUnknown { + // if client support it, request deferral when client configuration not fully known + resp.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonProviderConfigUnknown, + } + return resp, nil + } + execDiag := s.canExecute() if len(execDiag) > 0 { resp.Diagnostics = append(resp.Diagnostics, execDiag...) diff --git a/manifest/provider/plan.go b/manifest/provider/plan.go index 8bd0a25cf9..0d8cb82fe7 100644 --- a/manifest/provider/plan.go +++ b/manifest/provider/plan.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-provider-kubernetes/manifest" "github.com/hashicorp/terraform-provider-kubernetes/manifest/morph" "github.com/hashicorp/terraform-provider-kubernetes/manifest/payload" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" @@ -129,6 +130,16 @@ func isImportedFlagFromPrivate(p []byte) (f bool, d []*tfprotov5.Diagnostic) { func (s *RawProviderServer) PlanResourceChange(ctx context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { resp := &tfprotov5.PlanResourceChangeResponse{} + canDeferr := req.ClientCapabilities != nil && req.ClientCapabilities.DeferralAllowed + + if canDeferr && s.clientConfigUnknown { + // if client supports it, request deferral when client configuration not fully known + resp.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonProviderConfigUnknown, + } + return resp, nil + } + isImported, d := isImportedFlagFromPrivate(req.PriorPrivate) resp.Diagnostics = append(resp.Diagnostics, d...) if !isImported { @@ -278,11 +289,19 @@ func (s *RawProviderServer) PlanResourceChange(ctx context.Context, req *tfproto } gvk, err := GVKFromTftypesObject(&ppMan, rm) if err != nil { - resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{ + rd := &tfprotov5.Diagnostic{ Severity: tfprotov5.DiagnosticSeverityError, - Summary: "Failed to determine GroupVersionResource for manifest", + Summary: "API did not recognize GroupVersionKind from manifest (CRD may not be installed)", Detail: err.Error(), - }) + } + resp.Diagnostics = append(resp.Diagnostics, rd) + if canDeferr && meta.IsNoMatchError(err) { + // request deferral when client configuration not fully known + resp.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonResourceConfigUnknown, + } + rd.Severity = tfprotov5.DiagnosticSeverityWarning + } return resp, nil } diff --git a/manifest/provider/plugin.go b/manifest/provider/plugin.go index afeedd4f49..5d0b54afd3 100644 --- a/manifest/provider/plugin.go +++ b/manifest/provider/plugin.go @@ -71,9 +71,10 @@ func ServeTest(ctx context.Context, logger hclog.Logger, t *testing.T) (tfexec.R // convertReattachConfig converts plugin.ReattachConfig to tfexec.ReattachConfig func convertReattachConfig(reattachConfig *plugin.ReattachConfig) tfexec.ReattachConfig { return tfexec.ReattachConfig{ - Protocol: string(reattachConfig.Protocol), - Pid: reattachConfig.Pid, - Test: true, + Protocol: string(reattachConfig.Protocol), + ProtocolVersion: reattachConfig.ProtocolVersion, + Pid: reattachConfig.Pid, + Test: true, Addr: tfexec.ReattachConfigAddr{ Network: reattachConfig.Addr.Network(), String: reattachConfig.Addr.String(), diff --git a/manifest/provider/provider_config.go b/manifest/provider/provider_config.go index 44a7072c8f..2adfb1cc63 100644 --- a/manifest/provider/provider_config.go +++ b/manifest/provider/provider_config.go @@ -269,7 +269,7 @@ func GetProviderConfigSchema() *tfprotov5.Schema { Sensitive: false, Description: "Enable the `kubernetes_manifest` resource.", DescriptionKind: 0, - Deprecated: false, + Deprecated: true, }, }, }, diff --git a/manifest/provider/read.go b/manifest/provider/read.go index 7778e69cb9..41143726fc 100644 --- a/manifest/provider/read.go +++ b/manifest/provider/read.go @@ -20,6 +20,15 @@ import ( func (s *RawProviderServer) ReadResource(ctx context.Context, req *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) { resp := &tfprotov5.ReadResourceResponse{} + cp := req.ClientCapabilities + if cp != nil && cp.DeferralAllowed && s.clientConfigUnknown { + // if client support it, request deferral when client configuration not fully known + resp.Deferred = &tfprotov5.Deferred{ + Reason: tfprotov5.DeferredReasonProviderConfigUnknown, + } + return resp, nil + } + // loop private state back in - ATM it's not needed here resp.Private = req.Private diff --git a/manifest/provider/server.go b/manifest/provider/server.go index cc7e8e9cb9..fb678a6f2f 100644 --- a/manifest/provider/server.go +++ b/manifest/provider/server.go @@ -28,16 +28,16 @@ type RawProviderServer struct { // Since the provider is essentially a gRPC server, the execution flow is dictated by the order of the client (Terraform) request calls. // Thus it needs a way to persist state between the gRPC calls. These attributes store values that need to be persisted between gRPC calls, // such as instances of the Kubernetes clients, configuration options needed at runtime. - logger hclog.Logger - clientConfig *rest.Config - dynamicClient dynamic.Interface - discoveryClient discovery.DiscoveryInterface - restMapper meta.RESTMapper - restClient rest.Interface - OAPIFoundry openapi.Foundry - - providerEnabled bool - hostTFVersion string + logger hclog.Logger + clientConfig *rest.Config + clientConfigUnknown bool + dynamicClient dynamic.Interface + discoveryClient discovery.DiscoveryInterface + restMapper meta.RESTMapper + restClient rest.Interface + OAPIFoundry openapi.Foundry + + hostTFVersion string } func dump(v interface{}) hclog.Format { @@ -87,3 +87,24 @@ func (s *RawProviderServer) StopProvider(ctx context.Context, req *tfprotov5.Sto return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") } + +// CallFunction function +func (s *RawProviderServer) CallFunction(ctx context.Context, req *tfprotov5.CallFunctionRequest) (*tfprotov5.CallFunctionResponse, error) { + s.logger.Trace("[CallFunction][Request]\n%s\n", dump(*req)) + resp := &tfprotov5.CallFunctionResponse{} + return resp, nil +} + +// GetFunctions function +func (s *RawProviderServer) GetFunctions(ctx context.Context, req *tfprotov5.GetFunctionsRequest) (*tfprotov5.GetFunctionsResponse, error) { + s.logger.Trace("[GetFunctions][Request]\n%s\n", dump(*req)) + resp := &tfprotov5.GetFunctionsResponse{} + return resp, nil +} + +// MoveResourceState function +func (s *RawProviderServer) MoveResourceState(ctx context.Context, req *tfprotov5.MoveResourceStateRequest) (*tfprotov5.MoveResourceStateResponse, error) { + s.logger.Trace("[MoveResourceState][Request]\n%s\n", dump(*req)) + resp := &tfprotov5.MoveResourceStateResponse{} + return resp, nil +}