diff --git a/go.mod b/go.mod index 159d9b1f13..b2b0c64fb5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/hashicorp/terraform-provider-kubernetes -go 1.21 +go 1.21.5 + +toolchain go1.22.5 require ( github.com/Masterminds/semver v1.5.0 @@ -13,8 +15,11 @@ require ( 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-codegen-kubernetes v0.1.0 github.com/hashicorp/terraform-plugin-docs v0.16.0 - github.com/hashicorp/terraform-plugin-framework v1.7.0 + github.com/hashicorp/terraform-plugin-framework v1.10.0 + github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 + github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 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.16.0 @@ -24,12 +29,12 @@ require ( 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 + github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.16.0 - k8s.io/api v0.28.6 + k8s.io/api v0.28.8 k8s.io/apiextensions-apiserver v0.28.6 - k8s.io/apimachinery v0.28.6 - k8s.io/client-go v0.28.6 + k8s.io/apimachinery v0.28.8 + k8s.io/client-go v0.28.8 k8s.io/kube-aggregator v0.28.6 k8s.io/kubectl v0.28.6 k8s.io/kubernetes v1.28.6 diff --git a/go.sum b/go.sum index 4c1e0e14ef..8c169fb5eb 100644 --- a/go.sum +++ b/go.sum @@ -175,10 +175,16 @@ github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVW 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-codegen-kubernetes v0.1.0 h1:cPPZitB7w5qYPbSad0IkhDaXZc7LpDhsgm4g8Kj2cOY= +github.com/hashicorp/terraform-plugin-codegen-kubernetes v0.1.0/go.mod h1:AOwjDhbkWel/4J4fnLB59l52cGmV13FjkqKxaNVJjok= 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-framework v1.10.0 h1:xXhICE2Fns1RYZxEQebwkB2+kXouLC932Li9qelozrc= +github.com/hashicorp/terraform-plugin-framework v1.10.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY= +github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E= +github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo= 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= @@ -321,8 +327,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -333,8 +340,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -495,16 +502,16 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.28.6 h1:yy6u9CuIhmg55YvF/BavPBBXB+5QicB64njJXxVnzLo= -k8s.io/api v0.28.6/go.mod h1:AM6Ys6g9MY3dl/XNaNfg/GePI0FT7WBGu8efU/lirAo= +k8s.io/api v0.28.8 h1:G0/G7yX1puRAcon/+XPLsKXZ9A5L7Ds6oKbDIe027xw= +k8s.io/api v0.28.8/go.mod h1:rU8f1t9CNUAXlk/1j/wMJ7XnaxkR1g1AlZGQAOOL+sw= k8s.io/apiextensions-apiserver v0.28.6 h1:myB3iG/3v3jqCg28JDbOefu4sH2/erNEXgytRzJKBOo= k8s.io/apiextensions-apiserver v0.28.6/go.mod h1:qlp6xRKBgyRhe5AYc81TQpLx4kLNK8/sGQUOwMkVjRk= -k8s.io/apimachinery v0.28.6 h1:RsTeR4z6S07srPg6XYrwXpTJVMXsjPXn0ODakMytSW0= -k8s.io/apimachinery v0.28.6/go.mod h1:QFNX/kCl/EMT2WTSz8k4WLCv2XnkOLMaL8GAVRMdpsA= +k8s.io/apimachinery v0.28.8 h1:hi/nrxHwk4QLV+W/SHve1bypTE59HCDorLY1stBIxKQ= +k8s.io/apimachinery v0.28.8/go.mod h1:cBnwIM3fXoRo28SqbV/Ihxf/iviw85KyXOrzxvZQ83U= k8s.io/cli-runtime v0.28.6 h1:bDH2+ZbHBK3NORGmIygj/zWOkVd/hGWg9RqAa5c/Ev0= k8s.io/cli-runtime v0.28.6/go.mod h1:KFk67rlb7Pxh15uLbYGBUlW7ZUcpl7IM1GnHtskrcWA= -k8s.io/client-go v0.28.6 h1:Gge6ziyIdafRchfoBKcpaARuz7jfrK1R1azuwORIsQI= -k8s.io/client-go v0.28.6/go.mod h1:+nu0Yp21Oeo/cBCsprNVXB2BfJTV51lFfe5tXl2rUL8= +k8s.io/client-go v0.28.8 h1:TE59Tjd87WKvS2FPBTfIKLFX0nQJ4SSHsnDo5IHjgOw= +k8s.io/client-go v0.28.8/go.mod h1:uDVQ/rPzWpWIy40c6lZ4mUwaEvRWGnpoqSO4FM65P3o= k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo= k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= diff --git a/internal/framework/provider/appsv1/validating_admission_policy.go b/internal/framework/provider/appsv1/validating_admission_policy.go new file mode 100644 index 0000000000..49196695ce --- /dev/null +++ b/internal/framework/provider/appsv1/validating_admission_policy.go @@ -0,0 +1,49 @@ +package appsv1 + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-codegen-kubernetes/autocrud" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &ValidatingAdmissionPolicy{} +var _ resource.ResourceWithImportState = &ValidatingAdmissionPolicy{} + +func NewValidatingAdmissionPolicy() resource.Resource { + return &ValidatingAdmissionPolicy{ + Kind: "ValidatingAdmissionPolicy", + APIVersion: "apps/v1", + } +} + +type ValidatingAdmissionPolicy struct { + APIVersion string + Kind string + + clientGetter autocrud.KubernetesClientGetter +} + +func (r *ValidatingAdmissionPolicy) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "kubernetes_validating_admission_policy_v1_gen" +} + +func (r *ValidatingAdmissionPolicy) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + clientGetter, ok := req.ProviderData.(autocrud.KubernetesClientGetter) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected KubernetesClientGetter, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.clientGetter = clientGetter +} diff --git a/internal/framework/provider/appsv1/validating_admission_policy_crud.go b/internal/framework/provider/appsv1/validating_admission_policy_crud.go new file mode 100644 index 0000000000..84b9093ba7 --- /dev/null +++ b/internal/framework/provider/appsv1/validating_admission_policy_crud.go @@ -0,0 +1,151 @@ +package appsv1 + +import ( + "context" + "time" + + "github.com/hashicorp/terraform-plugin-codegen-kubernetes/autocrud" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *ValidatingAdmissionPolicy) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var validatingAdmissionPolicyModel ValidatingAdmissionPolicyModel + + diag := req.Config.Get(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + + defaultTimeout, err := time.ParseDuration("20m") + if err != nil { + resp.Diagnostics.AddError("Error parsing timeout", err.Error()) + return + } + timeout, diag := validatingAdmissionPolicyModel.Timeouts.Create(ctx, defaultTimeout) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + err = autocrud.Create(ctx, r.clientGetter, r.APIVersion, r.Kind, &validatingAdmissionPolicyModel) + if err != nil { + resp.Diagnostics.AddError("Error creating resource", err.Error()) + return + } + + diags := resp.State.Set(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ValidatingAdmissionPolicy) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var validatingAdmissionPolicyModel ValidatingAdmissionPolicyModel + + diag := req.State.Get(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + + defaultTimeout, err := time.ParseDuration("20m") + if err != nil { + resp.Diagnostics.AddError("Error parsing timeout", err.Error()) + return + } + timeout, diag := validatingAdmissionPolicyModel.Timeouts.Read(ctx, defaultTimeout) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + err = autocrud.Read(ctx, r.clientGetter, r.Kind, r.APIVersion, req, &validatingAdmissionPolicyModel) + if err != nil { + resp.Diagnostics.AddError("Error reading resource", err.Error()) + return + } + + diags := resp.State.Set(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ValidatingAdmissionPolicy) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var validatingAdmissionPolicyModel ValidatingAdmissionPolicyModel + + diag := req.Config.Get(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + + defaultTimeout, err := time.ParseDuration("20m") + if err != nil { + resp.Diagnostics.AddError("Error parsing timeout", err.Error()) + return + } + timeout, diag := validatingAdmissionPolicyModel.Timeouts.Update(ctx, defaultTimeout) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + err = autocrud.Update(ctx, r.clientGetter, r.Kind, r.APIVersion, &validatingAdmissionPolicyModel) + if err != nil { + resp.Diagnostics.AddError("Error updating resource", err.Error()) + return + } + + diags := resp.State.Set(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ValidatingAdmissionPolicy) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + waitForDeletion := false + + var validatingAdmissionPolicyModel ValidatingAdmissionPolicyModel + + diag := req.State.Get(ctx, &validatingAdmissionPolicyModel) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + + defaultTimeout, err := time.ParseDuration("20m") + if err != nil { + resp.Diagnostics.AddError("Error parsing timeout", err.Error()) + return + } + timeout, diag := validatingAdmissionPolicyModel.Timeouts.Delete(ctx, defaultTimeout) + resp.Diagnostics.Append(diag...) + if diag.HasError() { + return + } + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + err = autocrud.Delete(ctx, r.clientGetter, r.Kind, r.APIVersion, req, waitForDeletion) + if err != nil { + resp.Diagnostics.AddError("Error deleting resource", err.Error()) + return + } + +} + +func (r *ValidatingAdmissionPolicy) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/framework/provider/appsv1/validating_admission_policy_model.go b/internal/framework/provider/appsv1/validating_admission_policy_model.go new file mode 100644 index 0000000000..c048c490a7 --- /dev/null +++ b/internal/framework/provider/appsv1/validating_admission_policy_model.go @@ -0,0 +1,103 @@ +package appsv1 + +import ( + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type ValidatingAdmissionPolicyModel struct { + Timeouts timeouts.Value `tfsdk:"timeouts"` + + ID types.String `tfsdk:"id" manifest:""` + + Metadata struct { + Annotations map[string]types.String `tfsdk:"annotations" manifest:"annotations"` + GenerateName types.String `tfsdk:"generate_name" manifest:"generateName"` + Generation types.Int64 `tfsdk:"generation" manifest:"generation"` + Labels map[string]types.String `tfsdk:"labels" manifest:"labels"` + Name types.String `tfsdk:"name" manifest:"name"` + Namespace types.String `tfsdk:"namespace" manifest:"namespace"` + ResourceVersion types.String `tfsdk:"resource_version" manifest:"resourceVersion"` + UID types.String `tfsdk:"uid" manifest:"uid"` + } `tfsdk:"metadata" manifest:"metadata"` + + Spec struct { + AuditAnnotations []struct { + Key types.String `tfsdk:"key" manifest:"key"` + ValueExpressions types.String `tfsdk:"value_expressions" manifest:"valueExpressions"` + } `tfsdk:"audit_annotations" manifest:"auditAnnotations"` + FailurePolicy types.String `tfsdk:"failure_policy" manifest:"failurePolicy"` + MatchConditions []struct { + Expression types.String `tfsdk:"expression" manifest:"expression"` + Name types.String `tfsdk:"Name" manifest:"name"` + } `tfsdk:"match_conditions" manifest:"matchConditions"` + MatchConstraints struct { + ExcludeResourceRules []struct { + APIGroups []types.String `tfsdk:"api_groups" manifest:"apiGroups"` + APIVersions []types.String `tfsdk:"api_versions" manifest:"apiVersions"` + Operations []types.String `tfsdk:"operations" manifest:"operations"` + ResourceNames []types.String `tfsdk:"resource_names" manifest:"resourceNames"` + Resources []types.String `tfsdk:"resources" manifest:"resources"` + Scope types.String `tfsdk:"scope" manifest:"scope"` + } `tfsdk:"exclude_resource_rules" manifest:"excludeResourceRules"` + MatchPolicy types.String `tfsdk:"match_policy" manifest:"matchPolicy"` + NamespaceSelector struct { + APIGroups []types.String `tfsdk:"api_groups" manifest:"apiGroups"` + APIVersions []types.String `tfsdk:"api_versions" manifest:"apiVersions"` + Operations []types.String `tfsdk:"operations" manifest:"operations"` + ResourceNames []types.String `tfsdk:"resource_names" manifest:"resourceNames"` + Resources []types.String `tfsdk:"resources" manifest:"resources"` + Scope types.String `tfsdk:"scope" manifest:"scope"` + } `tfsdk:"namespace_selector" manifest:"NamespaceSelector"` + ObjectSelector struct { + LabelSelector struct { + MatchExpressions []struct { + Key types.String `tfsdk:"key" manifest:"key"` + Operator types.String `tfsdk:"operator" manifest:"operator"` + Values []types.String `tfsdk:"values" manifest:"values"` + } `tfsdk:"match_expressions" manifest:"matchExpressions"` + MatchLabels map[string]types.String `tfsdk:"match_labels" manifest:"matchLabels"` + } `tfsdk:"label_selector" manifest:"labelSelector"` + } `tfsdk:"object_selector" manifest:"objectSelector"` + ResourceRules []struct { + APIGroups []types.String `tfsdk:"api_groups" manifest:"apiGroups"` + APIVersions []types.String `tfsdk:"api_versions" manifest:"apiVersions"` + Operations []types.String `tfsdk:"operations" manifest:"operations"` + ResourceNames []types.String `tfsdk:"resource_names" manifest:"resourceNames"` + Resources []types.String `tfsdk:"resources" manifest:"resources"` + Scope types.String `tfsdk:"scope" manifest:"scope"` + } `tfsdk:"resource_rules" manifest:"resourceRules"` + } `tfsdk:"match_constraints" manifest:"matchConstraints"` + ParamKind struct { + APIVersion types.String `tfsdk:"api_version" manifest:"apiVersion"` + Kind types.String `tfsdk:"kind" manifest:"kind"` + } `tfsdk:"param_kind" manifest:"paramKind"` + Validations []struct { + Expression types.String `tfsdk:"expression" manifest:"expression"` + Message types.String `tfsdk:"message" manifest:"message"` + MessageExpression types.String `tfsdk:"message_expression" manifest:"messageExpression"` + Reason types.String `tfsdk:"reason" manifest:"reason"` + } + Variable []struct { + Expression types.String `tfsdk:"expression" manifest:"expression"` + Name types.String `tfsdk:"name" manifest:"name"` + } + Status struct { + Conditions []struct { + LastTransitionTime types.String `tfsdk:"last_transition_time" manifest:"lastTransitionTime"` + Message types.String `tfsdk:"message" manifest:"message"` + ObservedGeneration types.Int64 `tfsdk:"observed_generation" manifest:"observedGeneration"` + Reason types.String `tfsdk:"reason" manifest:"reason"` + Status types.String `tfsdk:"status" manifest:"status"` + Type types.String `tfsdk:"type" manifest:"type"` + } `tfsdk:"conditions" manifest:"conditions"` + ObservedGeneration types.Int64 `tfsdk:"observed_generation" manifest:"observedGeneration"` + TypeChecking struct { + ExpressionWarning []struct { + FieldRef types.String `tfsdk:"field_ref" manifest:"fieldRef"` + Warning types.String `tfsdk:"warning" manifest:"warning"` + } `tfsdk:"expression_warning" manifest:"expressionWarning"` + } `tfsdk:"type_checking" manifest:"typeChecking"` + } + } +} diff --git a/internal/framework/provider/appsv1/validating_admission_policy_schema.go b/internal/framework/provider/appsv1/validating_admission_policy_schema.go new file mode 100644 index 0000000000..a5a17a137e --- /dev/null +++ b/internal/framework/provider/appsv1/validating_admission_policy_schema.go @@ -0,0 +1,378 @@ +package appsv1 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func (r *ValidatingAdmissionPolicy) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: `validatingadmissionpolicy`, + Blocks: map[string]schema.Block{ + "timeouts": timeouts.BlockAll(ctx), + }, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: `The unique ID for this terraform resource`, + Optional: true, + Computed: true, + }, + "metadata": schema.SingleNestedAttribute{ + MarkdownDescription: `Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata`, + Optional: true, + Attributes: map[string]schema.Attribute{ + "annotations": schema.MapAttribute{ + MarkdownDescription: `Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations`, + ElementType: types.StringType, + Optional: true, + }, + "generate_name": schema.StringAttribute{ + MarkdownDescription: `GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server. + +If this field is specified and the generated name exists, the server will return a 409. + +Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency`, + Optional: true, + }, + "generation": schema.Int64Attribute{ + MarkdownDescription: `A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.`, + Optional: true, + Computed: true, + }, + "labels": schema.MapAttribute{ + MarkdownDescription: `Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels`, + ElementType: types.StringType, + Optional: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: `Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names`, + Optional: true, + Computed: true, + }, + "namespace": schema.StringAttribute{ + MarkdownDescription: `Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty. + +Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces`, + Optional: true, + }, + "resource_version": schema.StringAttribute{ + MarkdownDescription: `An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources. + +Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency`, + Optional: true, + Computed: true, + }, + "uid": schema.StringAttribute{ + MarkdownDescription: `UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations. + +Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids`, + Optional: true, + Computed: true, + }, + }, + }, + "spec": schema.SingleNestedAttribute{ + MarkdownDescription: "Rule defining a set of permissions for the role", + Required: true, + Attributes: map[string]schema.Attribute{ + "audit_annotations": schema.ListNestedAttribute{ + MarkdownDescription: "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request.", + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: auditAnnotationsFields(), + }, + }, + "failure_policy": schema.StringAttribute{ + MarkdownDescription: "failurePolicy defines how to handle failures for the admission policy.", + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf("Fail", "Ignore"), + }, + }, + "match_conditions": schema.ListNestedAttribute{ + MarkdownDescription: "MatchConditions is a list of conditions that must be met for a request to be validated.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: matchConditionsFields(), + }, + }, + "match_constraints": schema.SingleNestedAttribute{ + MarkdownDescription: "MatchConstraints specifies what resources this policy is designed to validate.", + Required: true, + Attributes: matchConstraintsFields(), + }, + "param_kind": schema.SingleNestedAttribute{ + MarkdownDescription: "ParamKind specifies the kind of resources used to parameterize this policy", + Optional: true, + Attributes: paramKindFields(), + }, + "validations": schema.ListNestedAttribute{ + MarkdownDescription: "Validations contain CEL expressions which is used to apply the validation.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: validationFields(), + }, + }, + "variable": schema.ListNestedAttribute{ + MarkdownDescription: "Variables contain definitions of variables that can be used in composition of other expressions.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: variableFields(), + }, + }, + }, + }, + "status": schema.SingleNestedAttribute{ + MarkdownDescription: `The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy behaves in the expected way.`, + Optional: true, + Attributes: map[string]schema.Attribute{ + "conditions": schema.ListNestedAttribute{ + MarkdownDescription: `The conditions represent the latest available observations of a policy's current state.`, + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "last_transition_time": schema.StringAttribute{ + MarkdownDescription: `Last time the condition transitioned from one status to another.`, + Optional: true, + }, + "message": schema.StringAttribute{ + MarkdownDescription: `A human readable message indicating details about the transition.`, + Optional: true, + }, + "observed_generation": schema.Int64Attribute{ + MarkdownDescription: `The generation observed by the deployment controller.`, + Optional: true, + }, + "reason": schema.StringAttribute{ + MarkdownDescription: `The reason for the condition's last transition.`, + Optional: true, + }, + "status": schema.StringAttribute{ + MarkdownDescription: `Status of the condition, one of True, False, Unknown.`, + Optional: true, + }, + "type": schema.StringAttribute{ + MarkdownDescription: `Type of deployment condition.`, + Optional: true, + }, + }, + }, + }, + "observed_generation": schema.Int64Attribute{ + MarkdownDescription: `The generation observed by the deployment controller.`, + Optional: true, + }, + "type_checking": schema.SingleNestedAttribute{ + MarkdownDescription: `The results of type checking for each expression. Presence of this field indicates the completion of the type checking.`, + Optional: true, + Attributes: map[string]schema.Attribute{ + "expression_warning": schema.ListNestedAttribute{ + MarkdownDescription: `The type checking warnings for each expression.`, + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "field_ref": schema.StringAttribute{ + Description: "The path to the field that refers the expression. For example, the reference to the expression of the first item of validations is \"spec.validations[0].expression\"", + Optional: true, + }, + "warning": schema.StringAttribute{ + MarkdownDescription: `The content of type checking information in a human-readable form. Each line of the warning contains the type that the expression is checked against, followed by the type check error from the compiler.`, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func auditAnnotationsFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "key": schema.StringAttribute{ + Description: "key specifies the audit annotation key.", + Required: true, + }, + "value_expressions": schema.StringAttribute{ + Description: "valueExpression represents the expression which is evaluated by CEL to produce an audit annotation value.", + Required: true, + }, + } +} + +func matchConditionsFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "expression": schema.StringAttribute{ + Description: "Expression represents the expression which will be evaluated by CEL.", + Required: true, + }, + "name": schema.StringAttribute{ + Description: "Name is an identifier for this match condition, used for strategic merging of MatchConditions, as well as providing an identifier for logging purposes.", + Required: true, + }, + } +} + +func matchConstraintsFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "exclude_resource_rules": schema.ListNestedAttribute{ + Description: "ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: namedRuleWithOperationsFields(), + }, + }, + "matchPolicy": schema.StringAttribute{ + Optional: true, + Description: "matchPolicy defines how the MatchResources list is used to match incoming requests. Allowed values are Exact or Equivalent.", + Validators: []validator.String{ + stringvalidator.OneOf("Exact", "Equivalent"), + }, + }, + "namespace_selector": schema.SingleNestedAttribute{ + Description: "NamespaceSelector decides whether to run the admission control policy on an object based on whether the namespace for that object matches the selector.", + Optional: true, + Attributes: namedRuleWithOperationsFields(), + }, + "object_selector": schema.SingleNestedAttribute{ + Description: "ObjectSelector decides whether to run the validation based on if the object has matching labels.", + Optional: true, + Attributes: labelSelectorFields(), + }, + "resource_rules": schema.ListNestedAttribute{ + Description: "ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: namedRuleWithOperationsFields(), + }, + }, + } +} + +func paramKindFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "api_version": schema.StringAttribute{ + Description: "APIVersion is the API group version the resources belong to. In format of \"group/version\"", + Required: true, + }, + "kind": schema.StringAttribute{ + Description: "Kind is the API kind the resources belong to.", + Required: true, + }, + } +} + +func validationFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "expression": schema.StringAttribute{ + Description: "Expression represents the expression which will be evaluated by CEL.", + Required: true, + }, + "message": schema.StringAttribute{ + Description: "Message represents the message displayed when validation fails.", + Required: true, + }, + "message_expression": schema.StringAttribute{ + Description: "Message Expression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails.", + Optional: true, + }, + "reason": schema.StringAttribute{ + Description: "Reason represents a machine-readable description of why this validation failed.", + Optional: true, + }, + } +} + +func variableFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "expression": schema.StringAttribute{ + Description: "Expression is the expression that will be evaluated as the value of the variable.", + Optional: true, + }, + "name": schema.StringAttribute{ + Description: "Name is the name of the variable.", + Optional: true, + }, + } +} + +func namedRuleWithOperationsFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "api_groups": schema.ListAttribute{ + Description: "APIGroups is the API groups the resources belong to. '\\*' is all groups. If '\\*' is present, the length of the slice must be one.", + Required: true, + ElementType: types.StringType, + }, + "api_versions": schema.ListAttribute{ + Description: "APIVersions is the API versions the resources belong to. '\\*' is all versions. If '\\*' is present, the length of the slice must be one. Required.", + Required: true, + ElementType: types.StringType, + }, + "operations": schema.ListAttribute{ + Description: "Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added.", + Required: true, + ElementType: types.StringType, + }, + "resource_names": schema.ListAttribute{ + Description: "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", + Optional: true, + ElementType: types.StringType, + }, + "resources": schema.ListAttribute{ + Description: "Resources is a list of resources this rule applies to.", + Required: true, + ElementType: types.StringType, + }, + "scope": schema.StringAttribute{ + Description: "scope specifies the scope of this rule.", + Optional: true, + }, + } +} + +func labelSelectorFields() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "label_selector": schema.SingleNestedAttribute{ + MarkdownDescription: `A label query over a set of resources, in this case pods.`, + Optional: true, + Attributes: map[string]schema.Attribute{ + "match_expressions": schema.ListNestedAttribute{ + MarkdownDescription: `matchExpressions is a list of label selector requirements. The requirements are ANDed.`, + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "key": schema.StringAttribute{ + MarkdownDescription: `key is the label key that the selector applies to.`, + Optional: true, + }, + "operator": schema.StringAttribute{ + MarkdownDescription: `operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.`, + Optional: true, + }, + "values": schema.ListAttribute{ + MarkdownDescription: `values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.`, + ElementType: types.StringType, + Optional: true, + }, + }, + }, + }, + "match_labels": schema.MapAttribute{ + MarkdownDescription: `matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.`, + ElementType: types.StringType, + Optional: true, + }, + }, + }, + } + +}