diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 7e7a4dc59..8d236a096 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -51,6 +51,7 @@ jobs: uses: engineerd/setup-kind@v0.5.0 with: version: v0.11.1 + config: ./config/testdata/kind/kind-config.yaml image: kindest/node:v1.21.1@sha256:fae9a58f17f18f06aeac9772ca8b5ac680ebbed985e266f711d936e91d113bad - name: Setup Kustomize uses: fluxcd/pkg/actions/kustomize@main @@ -122,6 +123,14 @@ jobs: kubectl -n tf-system apply -f ./config/testdata/source kubectl -n tf-system wait gitrepository/helloworld --for=condition=ready --timeout=4m kubectl -n tf-system wait ocirepository/helloworld-oci --for=condition=ready --timeout=4m + - name: Run planConfig.storage.claimName tests + run: | + kubectl -n tf-system apply -f ./config/testdata/plan-config-claim + kubectl -n tf-system wait terraform/helloworld-plan-config-claim --for=condition=ready --timeout=4m + + # delete after tests + kubectl -n tf-system delete -f ./config/testdata/plan-config-claim/test.yaml &&\ + kubectl -n tf-system delete -f ./config/testdata/plan-config-claim/pvc.yaml - name: Run approvePlan tests run: | kubectl -n tf-system apply -f ./config/testdata/approve-plan diff --git a/api/v1alpha1/terraform_types.go b/api/v1alpha1/terraform_types.go index abbd059b1..4b2173226 100644 --- a/api/v1alpha1/terraform_types.go +++ b/api/v1alpha1/terraform_types.go @@ -36,11 +36,12 @@ const ( CACertSecretName = "tf-controller.tls" // RunnerTLSSecretName is the name of the secret containing a TLS cert that will be written to // the namespace in which a terraform runner is created - RunnerTLSSecretName = "terraform-runner.tls" - RunnerLabel = "infra.contrib.fluxcd.io/terraform" - GitRepositoryIndexKey = ".metadata.gitRepository" - BucketIndexKey = ".metadata.bucket" - OCIRepositoryIndexKey = ".metadata.ociRepository" + RunnerTLSSecretName = "terraform-runner.tls" + RunnerLabel = "infra.contrib.fluxcd.io/terraform" + GitRepositoryIndexKey = ".metadata.gitRepository" + BucketIndexKey = ".metadata.bucket" + OCIRepositoryIndexKey = ".metadata.ociRepository" + PlanStorageMountSubDirBase = "terraform_controller" ) type ReadInputsFromSecretSpec struct { @@ -87,6 +88,9 @@ type TerraformSpec struct { // +optional Destroy bool `json:"destroy,omitempty"` + // +optional + PlanConfig *PlanConfigSpec `json:"planConfig,omitempty"` + // +optional BackendConfig *BackendConfigSpec `json:"backendConfig,omitempty"` @@ -352,6 +356,17 @@ type TerraformList struct { Items []Terraform `json:"items"` } +// PlanConfigSpec is for specifying configuration for Terraform plan related options +type PlanConfigSpec struct { + Storage *PlanStorage `json:"storage,omitempty"` +} + +// PlanStorage is for specifying Terraform Plan storage configurations +type PlanStorage struct { + // ClaimName hold a name of a Kubernetes PVC that will hold the tf-runner plan file. + ClaimName string `json:"claimName,omitempty"` +} + // BackendConfigSpec is for specifying configuration for Terraform's Kubernetes backend type BackendConfigSpec struct { @@ -773,6 +788,18 @@ func (in Terraform) GetRetryInterval() time.Duration { return in.Spec.Interval.Duration } +func (in Terraform) GetClaimName() string { + if in.Spec.PlanConfig != nil && in.Spec.PlanConfig.Storage != nil { + return in.Spec.PlanConfig.Storage.ClaimName + } + + return "" +} + +func (in Terraform) GetPlanStorageMountSubDir() string { + return strings.Join([]string{PlanStorageMountSubDirBase, in.Namespace, in.Name}, "/") +} + // GetStatusConditions returns a pointer to the Status.Conditions slice. func (in *Terraform) GetStatusConditions() *[]metav1.Condition { return &in.Status.Conditions diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9a3d7e0d9..0d55cae0e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -137,6 +137,26 @@ func (in *LockStatus) DeepCopy() *LockStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlanConfigSpec) DeepCopyInto(out *PlanConfigSpec) { + *out = *in + if in.Storage != nil { + in, out := &in.Storage, &out.Storage + *out = new(PlanStorage) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanConfigSpec. +func (in *PlanConfigSpec) DeepCopy() *PlanConfigSpec { + if in == nil { + return nil + } + out := new(PlanConfigSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PlanStatus) DeepCopyInto(out *PlanStatus) { *out = *in @@ -152,6 +172,21 @@ func (in *PlanStatus) DeepCopy() *PlanStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlanStorage) DeepCopyInto(out *PlanStorage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlanStorage. +func (in *PlanStorage) DeepCopy() *PlanStorage { + if in == nil { + return nil + } + out := new(PlanStorage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ReadInputsFromSecretSpec) DeepCopyInto(out *ReadInputsFromSecretSpec) { *out = *in @@ -394,6 +429,11 @@ func (in *TerraformList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TerraformSpec) DeepCopyInto(out *TerraformSpec) { *out = *in + if in.PlanConfig != nil { + in, out := &in.PlanConfig, &out.PlanConfig + *out = new(PlanConfigSpec) + (*in).DeepCopyInto(*out) + } if in.BackendConfig != nil { in, out := &in.BackendConfig, &out.BackendConfig *out = new(BackendConfigSpec) diff --git a/config/crd/bases/infra.contrib.fluxcd.io_terraforms.yaml b/config/crd/bases/infra.contrib.fluxcd.io_terraforms.yaml index e0f69072d..805d31a8e 100644 --- a/config/crd/bases/infra.contrib.fluxcd.io_terraforms.yaml +++ b/config/crd/bases/infra.contrib.fluxcd.io_terraforms.yaml @@ -245,6 +245,20 @@ spec: description: Path to the directory containing Terraform (.tf) files. Defaults to 'None', which translates to the root path of the SourceRef. type: string + planConfig: + description: PlanConfigSpec is for specifying configuration for Terraform + plan related options + properties: + storage: + description: PlanStorage is for specifying Terraform Plan storage + configurations + properties: + claimName: + description: ClaimName hold a name of a Kubernetes PVC that + will hold the tf-runner plan file. + type: string + type: object + type: object readInputsFromSecrets: items: properties: diff --git a/config/crd/bases/ocirepositories.yaml b/config/crd/bases/ocirepositories.yaml index 2d236ec99..d40c11861 100644 --- a/config/crd/bases/ocirepositories.yaml +++ b/config/crd/bases/ocirepositories.yaml @@ -301,12 +301,14 @@ spec: type: object type: array contentConfigChecksum: - description: 'ContentConfigChecksum is a checksum of all the configurations + description: "ContentConfigChecksum is a checksum of all the configurations related to the content of the source artifact: - .spec.ignore - .spec.layerSelector observed in .status.observedGeneration version of the object. This can be used to determine if the content configuration has changed and the artifact needs to be rebuilt. It has the format - of `:`, for example: `sha256:`.' + of `:`, for example: `sha256:`. \n Deprecated: + Replaced with explicit fields for observed artifact content config + in the status." type: string lastHandledReconcileAt: description: LastHandledReconcileAt holds the value of the most recent @@ -317,6 +319,29 @@ spec: description: ObservedGeneration is the last observed generation. format: int64 type: integer + observedIgnore: + description: ObservedIgnore is the observed exclusion patterns used + for constructing the source artifact. + type: string + observedLayerSelector: + description: ObservedLayerSelector is the observed layer selector + used for constructing the source artifact. + properties: + mediaType: + description: MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The first layer + matching this type is selected. + type: string + operation: + description: Operation specifies how the selected layer should + be processed. By default, the layer compressed content is extracted + to storage. When the operation is set to 'copy', the layer compressed + content is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object url: description: URL is the download link for the artifact output of the last OCI Repository sync. diff --git a/config/testdata/kind/kind-config.yaml b/config/testdata/kind/kind-config.yaml new file mode 100644 index 000000000..0f7fc7389 --- /dev/null +++ b/config/testdata/kind/kind-config.yaml @@ -0,0 +1,7 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + extraMounts: # For plan_config_claim + - hostPath: /tmp/plan-config-claim + containerPath: /tmp/plan-config-claim \ No newline at end of file diff --git a/config/testdata/plan-config-claim/pvc.yaml b/config/testdata/plan-config-claim/pvc.yaml new file mode 100644 index 000000000..2193283b1 --- /dev/null +++ b/config/testdata/plan-config-claim/pvc.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: plan-config-claim-pvc +spec: + storageClassName: standard + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/config/testdata/plan-config-claim/test.yaml b/config/testdata/plan-config-claim/test.yaml new file mode 100644 index 000000000..f0070a93a --- /dev/null +++ b/config/testdata/plan-config-claim/test.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: infra.contrib.fluxcd.io/v1alpha1 +kind: Terraform +metadata: + name: helloworld-plan-config-claim +spec: + interval: 10s + approvePlan: "auto" + storeReadablePlan: "human" + path: ./ + planConfig: + storage: + claimName: "plan-config-claim-pvc" + sourceRef: + kind: GitRepository + name: helloworld \ No newline at end of file diff --git a/controllers/tf_controller_finalizer.go b/controllers/tf_controller_finalizer.go index a14db0017..bca449cfb 100644 --- a/controllers/tf_controller_finalizer.go +++ b/controllers/tf_controller_finalizer.go @@ -110,8 +110,8 @@ func (r *TerraformReconciler) finalize(ctx context.Context, terraform infrav1.Te outputSecretName = terraform.Spec.WriteOutputsToSecret.Name } - traceLog.Info("Finalize the secrets") - finalizeSecretsReply, err := runnerClient.FinalizeSecrets(ctx, &runner.FinalizeSecretsRequest{ + traceLog.Info("Finalize storage: secrets, pvc") + finalizeStorageReply, err := runnerClient.FinalizeStorage(ctx, &runner.FinalizeStorageRequest{ Namespace: terraform.Namespace, Name: terraform.Name, Workspace: terraform.WorkspaceName(), @@ -136,7 +136,7 @@ func (r *TerraformReconciler) finalize(ctx context.Context, terraform infrav1.Te traceLog.Info("Check for an error") if err == nil { - log.Info(fmt.Sprintf("finalizing secrets: %s", finalizeSecretsReply.Message)) + log.Info(fmt.Sprintf("finalizing secrets: %s", finalizeStorageReply.Message)) } // Record deleted status diff --git a/controllers/tf_controller_plan.go b/controllers/tf_controller_plan.go index 20e988f85..c345698a2 100644 --- a/controllers/tf_controller_plan.go +++ b/controllers/tf_controller_plan.go @@ -113,7 +113,7 @@ func (r *TerraformReconciler) plan(ctx context.Context, terraform infrav1.Terraf Revision: revision, }) if err != nil { - err = fmt.Errorf("error saving plan secret: %s", err) + err = fmt.Errorf("error saving plan: %s", err) return infrav1.TerraformNotReady( terraform, revision, diff --git a/controllers/tf_controller_runner.go b/controllers/tf_controller_runner.go index ef706d974..6594ea531 100644 --- a/controllers/tf_controller_runner.go +++ b/controllers/tf_controller_runner.go @@ -227,6 +227,25 @@ func (r *TerraformReconciler) runnerPodSpec(terraform v1alpha1.Terraform, tlsSec podVolumeMounts = append(podVolumeMounts, terraform.Spec.RunnerPodTemplate.Spec.VolumeMounts...) } + if terraform.GetClaimName() != "" { + podVolumes = append(podVolumes, + v1.Volume{ + Name: terraform.Spec.PlanConfig.Storage.ClaimName, + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: terraform.Spec.PlanConfig.Storage.ClaimName, + ReadOnly: false, + }, + }, + }) + podVolumeMounts = append(podVolumeMounts, v1.VolumeMount{ + Name: terraform.GetClaimName(), + ReadOnly: false, + MountPath: runner.PlanStoragePath, + SubPath: terraform.GetPlanStorageMountSubDir(), + }) + } + return v1.PodSpec{ TerminationGracePeriodSeconds: gracefulTermPeriod, InitContainers: terraform.Spec.RunnerPodTemplate.Spec.InitContainers, diff --git a/docs/References/terraform.md b/docs/References/terraform.md index ad7b47084..534349597 100644 --- a/docs/References/terraform.md +++ b/docs/References/terraform.md @@ -428,6 +428,39 @@ string +

PlanConfigSpec +

+

+(Appears on: +TerraformSpec) +

+

PlanConfigSpec is for specifying configuration for Terraform plan related options

+
+
+ + + + + + + + + + + + + +
FieldDescription
+storage
+ + +PlanStorage + + +
+
+
+

PlanStatus

@@ -492,6 +525,38 @@ bool +

PlanStorage +

+

+(Appears on: +PlanConfigSpec) +

+

PlanStorage is for specifying Terraform Plan storage configurations

+
+
+ + + + + + + + + + + + + +
FieldDescription
+claimName
+ +string + +
+

ClaimName hold a name of a Kubernetes PVC that will hold the tf-runner plan file.

+
+
+

ReadInputsFromSecretSpec

@@ -1119,6 +1184,19 @@ bool +planConfig
+ + +PlanConfigSpec + + + + +(Optional) + + + + backendConfig
@@ -1577,6 +1655,19 @@ bool +planConfig
+ +
+PlanConfigSpec + + + + +(Optional) + + + + backendConfig
diff --git a/runner/runner.pb.go b/runner/runner.pb.go index d6b83d46b..063ff1b63 100644 --- a/runner/runner.pb.go +++ b/runner/runner.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.28.1 // protoc v3.19.4 // source: runner/runner.proto @@ -2817,7 +2817,7 @@ func (x *UploadReply) GetMessage() string { return "" } -type FinalizeSecretsRequest struct { +type FinalizeStorageRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -2829,8 +2829,8 @@ type FinalizeSecretsRequest struct { OutputSecretName string `protobuf:"bytes,5,opt,name=outputSecretName,proto3" json:"outputSecretName,omitempty"` } -func (x *FinalizeSecretsRequest) Reset() { - *x = FinalizeSecretsRequest{} +func (x *FinalizeStorageRequest) Reset() { + *x = FinalizeStorageRequest{} if protoimpl.UnsafeEnabled { mi := &file_runner_runner_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2838,13 +2838,13 @@ func (x *FinalizeSecretsRequest) Reset() { } } -func (x *FinalizeSecretsRequest) String() string { +func (x *FinalizeStorageRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*FinalizeSecretsRequest) ProtoMessage() {} +func (*FinalizeStorageRequest) ProtoMessage() {} -func (x *FinalizeSecretsRequest) ProtoReflect() protoreflect.Message { +func (x *FinalizeStorageRequest) ProtoReflect() protoreflect.Message { mi := &file_runner_runner_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2856,47 +2856,47 @@ func (x *FinalizeSecretsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use FinalizeSecretsRequest.ProtoReflect.Descriptor instead. -func (*FinalizeSecretsRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use FinalizeStorageRequest.ProtoReflect.Descriptor instead. +func (*FinalizeStorageRequest) Descriptor() ([]byte, []int) { return file_runner_runner_proto_rawDescGZIP(), []int{51} } -func (x *FinalizeSecretsRequest) GetNamespace() string { +func (x *FinalizeStorageRequest) GetNamespace() string { if x != nil { return x.Namespace } return "" } -func (x *FinalizeSecretsRequest) GetName() string { +func (x *FinalizeStorageRequest) GetName() string { if x != nil { return x.Name } return "" } -func (x *FinalizeSecretsRequest) GetWorkspace() string { +func (x *FinalizeStorageRequest) GetWorkspace() string { if x != nil { return x.Workspace } return "" } -func (x *FinalizeSecretsRequest) GetHasSpecifiedOutputSecret() bool { +func (x *FinalizeStorageRequest) GetHasSpecifiedOutputSecret() bool { if x != nil { return x.HasSpecifiedOutputSecret } return false } -func (x *FinalizeSecretsRequest) GetOutputSecretName() string { +func (x *FinalizeStorageRequest) GetOutputSecretName() string { if x != nil { return x.OutputSecretName } return "" } -type FinalizeSecretsReply struct { +type FinalizeStorageReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -2905,8 +2905,8 @@ type FinalizeSecretsReply struct { NotFound bool `protobuf:"varint,2,opt,name=notFound,proto3" json:"notFound,omitempty"` } -func (x *FinalizeSecretsReply) Reset() { - *x = FinalizeSecretsReply{} +func (x *FinalizeStorageReply) Reset() { + *x = FinalizeStorageReply{} if protoimpl.UnsafeEnabled { mi := &file_runner_runner_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2914,13 +2914,13 @@ func (x *FinalizeSecretsReply) Reset() { } } -func (x *FinalizeSecretsReply) String() string { +func (x *FinalizeStorageReply) String() string { return protoimpl.X.MessageStringOf(x) } -func (*FinalizeSecretsReply) ProtoMessage() {} +func (*FinalizeStorageReply) ProtoMessage() {} -func (x *FinalizeSecretsReply) ProtoReflect() protoreflect.Message { +func (x *FinalizeStorageReply) ProtoReflect() protoreflect.Message { mi := &file_runner_runner_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2932,19 +2932,19 @@ func (x *FinalizeSecretsReply) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use FinalizeSecretsReply.ProtoReflect.Descriptor instead. -func (*FinalizeSecretsReply) Descriptor() ([]byte, []int) { +// Deprecated: Use FinalizeStorageReply.ProtoReflect.Descriptor instead. +func (*FinalizeStorageReply) Descriptor() ([]byte, []int) { return file_runner_runner_proto_rawDescGZIP(), []int{52} } -func (x *FinalizeSecretsReply) GetMessage() string { +func (x *FinalizeStorageReply) GetMessage() string { if x != nil { return x.Message } return "" } -func (x *FinalizeSecretsReply) GetNotFound() bool { +func (x *FinalizeStorageReply) GetNotFound() bool { if x != nil { return x.NotFound } @@ -3333,8 +3333,8 @@ var file_runner_runner_proto_rawDesc = []byte{ 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x22, 0x27, 0x0a, 0x0b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd0, - 0x01, 0x0a, 0x16, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x01, 0x0a, 0x16, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x77, @@ -3346,8 +3346,8 @@ var file_runner_runner_proto_rawDesc = []byte{ 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x22, 0x4c, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x65, 0x22, 0x4c, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, @@ -3466,11 +3466,11 @@ var file_runner_runner_proto_rawDesc = []byte{ 0x64, 0x12, 0x15, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, - 0x51, 0x0a, 0x0f, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x51, 0x0a, 0x0f, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x12, 0x1e, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6e, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0b, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1a, 0x2e, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, @@ -3544,8 +3544,8 @@ var file_runner_runner_proto_goTypes = []interface{}{ (*WorkspaceReply)(nil), // 48: runner.WorkspaceReply (*UploadRequest)(nil), // 49: runner.UploadRequest (*UploadReply)(nil), // 50: runner.UploadReply - (*FinalizeSecretsRequest)(nil), // 51: runner.FinalizeSecretsRequest - (*FinalizeSecretsReply)(nil), // 52: runner.FinalizeSecretsReply + (*FinalizeStorageRequest)(nil), // 51: runner.FinalizeStorageRequest + (*FinalizeStorageReply)(nil), // 52: runner.FinalizeStorageReply (*ForceUnlockRequest)(nil), // 53: runner.ForceUnlockRequest (*ForceUnlockReply)(nil), // 54: runner.ForceUnlockReply nil, // 55: runner.SetEnvRequest.EnvsEntry @@ -3585,7 +3585,7 @@ var file_runner_runner_proto_depIdxs = []int32{ 45, // 28: runner.Runner.Init:input_type -> runner.InitRequest 47, // 29: runner.Runner.SelectWorkspace:input_type -> runner.WorkspaceRequest 49, // 30: runner.Runner.Upload:input_type -> runner.UploadRequest - 51, // 31: runner.Runner.FinalizeSecrets:input_type -> runner.FinalizeSecretsRequest + 51, // 31: runner.Runner.FinalizeStorage:input_type -> runner.FinalizeStorageRequest 53, // 32: runner.Runner.ForceUnlock:input_type -> runner.ForceUnlockRequest 1, // 33: runner.Runner.LookPath:output_type -> runner.LookPathReply 3, // 34: runner.Runner.NewTerraform:output_type -> runner.NewTerraformReply @@ -3611,7 +3611,7 @@ var file_runner_runner_proto_depIdxs = []int32{ 46, // 54: runner.Runner.Init:output_type -> runner.InitReply 48, // 55: runner.Runner.SelectWorkspace:output_type -> runner.WorkspaceReply 50, // 56: runner.Runner.Upload:output_type -> runner.UploadReply - 52, // 57: runner.Runner.FinalizeSecrets:output_type -> runner.FinalizeSecretsReply + 52, // 57: runner.Runner.FinalizeStorage:output_type -> runner.FinalizeStorageReply 54, // 58: runner.Runner.ForceUnlock:output_type -> runner.ForceUnlockReply 33, // [33:59] is the sub-list for method output_type 7, // [7:33] is the sub-list for method input_type @@ -4239,7 +4239,7 @@ func file_runner_runner_proto_init() { } } file_runner_runner_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FinalizeSecretsRequest); i { + switch v := v.(*FinalizeStorageRequest); i { case 0: return &v.state case 1: @@ -4251,7 +4251,7 @@ func file_runner_runner_proto_init() { } } file_runner_runner_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FinalizeSecretsReply); i { + switch v := v.(*FinalizeStorageReply); i { case 0: return &v.state case 1: diff --git a/runner/runner.proto b/runner/runner.proto index c22a5dde2..b514b3add 100644 --- a/runner/runner.proto +++ b/runner/runner.proto @@ -35,7 +35,7 @@ service Runner { rpc SelectWorkspace(WorkspaceRequest) returns (WorkspaceReply) {} rpc Upload(UploadRequest) returns (UploadReply) {} - rpc FinalizeSecrets(FinalizeSecretsRequest) returns (FinalizeSecretsReply) {} + rpc FinalizeStorage(FinalizeStorageRequest) returns (FinalizeStorageReply) {} rpc ForceUnlock(ForceUnlockRequest) returns (ForceUnlockReply) {} } @@ -293,7 +293,7 @@ message UploadReply { string message = 1; } -message FinalizeSecretsRequest { +message FinalizeStorageRequest { string namespace = 1; string name = 2; string workspace = 3; @@ -301,7 +301,7 @@ message FinalizeSecretsRequest { string outputSecretName = 5; } -message FinalizeSecretsReply { +message FinalizeStorageReply { string message = 1; bool notFound = 2; } diff --git a/runner/runner_grpc.pb.go b/runner/runner_grpc.pb.go index 12f282b30..d0b92f508 100644 --- a/runner/runner_grpc.pb.go +++ b/runner/runner_grpc.pb.go @@ -46,7 +46,7 @@ type RunnerClient interface { Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*InitReply, error) SelectWorkspace(ctx context.Context, in *WorkspaceRequest, opts ...grpc.CallOption) (*WorkspaceReply, error) Upload(ctx context.Context, in *UploadRequest, opts ...grpc.CallOption) (*UploadReply, error) - FinalizeSecrets(ctx context.Context, in *FinalizeSecretsRequest, opts ...grpc.CallOption) (*FinalizeSecretsReply, error) + FinalizeStorage(ctx context.Context, in *FinalizeStorageRequest, opts ...grpc.CallOption) (*FinalizeStorageReply, error) ForceUnlock(ctx context.Context, in *ForceUnlockRequest, opts ...grpc.CallOption) (*ForceUnlockReply, error) } @@ -274,9 +274,9 @@ func (c *runnerClient) Upload(ctx context.Context, in *UploadRequest, opts ...gr return out, nil } -func (c *runnerClient) FinalizeSecrets(ctx context.Context, in *FinalizeSecretsRequest, opts ...grpc.CallOption) (*FinalizeSecretsReply, error) { - out := new(FinalizeSecretsReply) - err := c.cc.Invoke(ctx, "/runner.Runner/FinalizeSecrets", in, out, opts...) +func (c *runnerClient) FinalizeStorage(ctx context.Context, in *FinalizeStorageRequest, opts ...grpc.CallOption) (*FinalizeStorageReply, error) { + out := new(FinalizeStorageReply) + err := c.cc.Invoke(ctx, "/runner.Runner/FinalizeStorage", in, out, opts...) if err != nil { return nil, err } @@ -320,7 +320,7 @@ type RunnerServer interface { Init(context.Context, *InitRequest) (*InitReply, error) SelectWorkspace(context.Context, *WorkspaceRequest) (*WorkspaceReply, error) Upload(context.Context, *UploadRequest) (*UploadReply, error) - FinalizeSecrets(context.Context, *FinalizeSecretsRequest) (*FinalizeSecretsReply, error) + FinalizeStorage(context.Context, *FinalizeStorageRequest) (*FinalizeStorageReply, error) ForceUnlock(context.Context, *ForceUnlockRequest) (*ForceUnlockReply, error) mustEmbedUnimplementedRunnerServer() } @@ -401,8 +401,8 @@ func (UnimplementedRunnerServer) SelectWorkspace(context.Context, *WorkspaceRequ func (UnimplementedRunnerServer) Upload(context.Context, *UploadRequest) (*UploadReply, error) { return nil, status.Errorf(codes.Unimplemented, "method Upload not implemented") } -func (UnimplementedRunnerServer) FinalizeSecrets(context.Context, *FinalizeSecretsRequest) (*FinalizeSecretsReply, error) { - return nil, status.Errorf(codes.Unimplemented, "method FinalizeSecrets not implemented") +func (UnimplementedRunnerServer) FinalizeStorage(context.Context, *FinalizeStorageRequest) (*FinalizeStorageReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalizeStorage not implemented") } func (UnimplementedRunnerServer) ForceUnlock(context.Context, *ForceUnlockRequest) (*ForceUnlockReply, error) { return nil, status.Errorf(codes.Unimplemented, "method ForceUnlock not implemented") @@ -852,20 +852,20 @@ func _Runner_Upload_Handler(srv interface{}, ctx context.Context, dec func(inter return interceptor(ctx, in, info, handler) } -func _Runner_FinalizeSecrets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FinalizeSecretsRequest) +func _Runner_FinalizeStorage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FinalizeStorageRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(RunnerServer).FinalizeSecrets(ctx, in) + return srv.(RunnerServer).FinalizeStorage(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/runner.Runner/FinalizeSecrets", + FullMethod: "/runner.Runner/FinalizeStorage", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RunnerServer).FinalizeSecrets(ctx, req.(*FinalizeSecretsRequest)) + return srv.(RunnerServer).FinalizeStorage(ctx, req.(*FinalizeStorageRequest)) } return interceptor(ctx, in, info, handler) } @@ -992,8 +992,8 @@ var Runner_ServiceDesc = grpc.ServiceDesc{ Handler: _Runner_Upload_Handler, }, { - MethodName: "FinalizeSecrets", - Handler: _Runner_FinalizeSecrets_Handler, + MethodName: "FinalizeStorage", + Handler: _Runner_FinalizeStorage_Handler, }, { MethodName: "ForceUnlock", diff --git a/runner/server.go b/runner/server.go index de90a8f7f..2bf1b0339 100644 --- a/runner/server.go +++ b/runner/server.go @@ -42,6 +42,7 @@ const ( runnerFileMappingDirectoryPermissions = 0700 runnerFileMappingFilePermissions = 0600 HomePath = "/home/runner" + PlanStoragePath = "/mnt/plan" ) type LocalPrintfer struct { @@ -730,7 +731,7 @@ func (r *TerraformRunnerServer) SaveTFPlan(ctx context.Context, req *SaveTFPlanR planRev := strings.Replace(req.Revision, "/", "-", 1) planName := "plan-" + planRev - if err := r.writePlanAsSecret(ctx, req.Name, req.Namespace, log, planName, tfplan, "", req.Uuid); err != nil { + if err := r.writePlan(ctx, req.Name, req.Namespace, log, planName, tfplan, "", req.Uuid); err != nil { return nil, err } @@ -746,17 +747,11 @@ func (r *TerraformRunnerServer) SaveTFPlan(ctx context.Context, req *SaveTFPlanR return nil, err } - if err := r.writePlanAsSecret(ctx, req.Name, req.Namespace, log, planName, jsonBytes, ".json", req.Uuid); err != nil { + if err := r.writePlan(ctx, req.Name, req.Namespace, log, planName, jsonBytes, ".json", req.Uuid); err != nil { return nil, err } } else if r.terraform.Spec.StoreReadablePlan == "human" { - rawOutput, err := r.tf.ShowPlanFileRaw(ctx, TFPlanName) - if err != nil { - log.Error(err, "unable to get the plan output for human") - return nil, err - } - - if err := r.writePlanAsConfigMap(ctx, req.Name, req.Namespace, log, planName, rawOutput, "", req.Uuid); err != nil { + if err := r.writePlanHuman(ctx, req.Name, req.Namespace, log, planName, tfplan, "", req.Uuid); err != nil { return nil, err } } @@ -764,8 +759,53 @@ func (r *TerraformRunnerServer) SaveTFPlan(ctx context.Context, req *SaveTFPlanR return &SaveTFPlanReply{Message: "ok"}, nil } +func (r *TerraformRunnerServer) GetPlanStorageName(name string, suffix string) string { + return "tfplan-" + r.terraform.WorkspaceName() + "-" + name + suffix +} + +func (r *TerraformRunnerServer) writePlan(ctx context.Context, name string, namespace string, log logr.Logger, planName string, tfplan []byte, suffix string, uuid string) error { + tfplan, err := utils.GzipEncode(tfplan) + if err != nil { + log.Error(err, "unable to encode the plan revision", "planName", planName) + return err + } + if r.terraform.GetClaimName() != "" { + return r.writePlanInPVC(name, log, planName, tfplan, suffix) + } + return r.writePlanAsSecret(ctx, name, namespace, log, planName, tfplan, suffix, uuid) +} + +func (r *TerraformRunnerServer) writePlanHuman(ctx context.Context, name string, namespace string, log logr.Logger, planName string, tfplan []byte, suffix string, uuid string) error { + rawOutput, err := r.tf.ShowPlanFileRaw(ctx, TFPlanName) + if err != nil { + log.Error(err, "unable to get the plan output for human") + return err + } + + if r.terraform.GetClaimName() != "" { + return r.writePlanInPVC(name, log, planName, []byte(rawOutput), ".human") + } + return r.writePlanAsConfigMap(ctx, name, namespace, log, planName, rawOutput, suffix, uuid) +} + +func (r *TerraformRunnerServer) writePlanInPVC(name string, log logr.Logger, planName string, tfplan []byte, suffix string) error { + planFileName := r.GetPlanStorageName(name, suffix) + planPVCFilePath, err := securejoin.SecureJoin(PlanStoragePath, planFileName) + if err != nil { + log.Error(err, "unable to create PVC file path for plan") + return err + } + + if err = os.WriteFile(planPVCFilePath, tfplan, os.ModePerm); err != nil { + log.Error(err, "write plan to pvc", "planName", planName) + return err + } + + return nil +} + func (r *TerraformRunnerServer) writePlanAsSecret(ctx context.Context, name string, namespace string, log logr.Logger, planName string, tfplan []byte, suffix string, uuid string) error { - secretName := "tfplan-" + r.terraform.WorkspaceName() + "-" + name + suffix + secretName := r.GetPlanStorageName(name, suffix) tfplanObjectKey := types.NamespacedName{Name: secretName, Namespace: namespace} var tfplanSecret corev1.Secret tfplanSecretExists := true @@ -788,12 +828,6 @@ func (r *TerraformRunnerServer) writePlanAsSecret(ctx context.Context, name stri } } - tfplan, err := utils.GzipEncode(tfplan) - if err != nil { - log.Error(err, "unable to encode the plan revision", "planName", planName) - return err - } - tfplanData := map[string][]byte{TFPlanName: tfplan} tfplanSecret = corev1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -830,7 +864,7 @@ func (r *TerraformRunnerServer) writePlanAsSecret(ctx context.Context, name stri } func (r *TerraformRunnerServer) writePlanAsConfigMap(ctx context.Context, name string, namespace string, log logr.Logger, planName string, tfplan string, suffix string, uuid string) error { - configMapName := "tfplan-" + r.terraform.WorkspaceName() + "-" + name + suffix + configMapName := r.GetPlanStorageName(name, suffix) tfplanObjectKey := types.NamespacedName{Name: configMapName, Namespace: namespace} var tfplanCM corev1.ConfigMap tfplanCMExists := true @@ -888,20 +922,67 @@ func (r *TerraformRunnerServer) writePlanAsConfigMap(ctx context.Context, name s func (r *TerraformRunnerServer) LoadTFPlan(ctx context.Context, req *LoadTFPlanRequest) (*LoadTFPlanReply, error) { log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) - log.Info("loading plan from secret") if req.TfInstance != r.InstanceID { err := fmt.Errorf("no TF instance found") log.Error(err, "no terraform") return nil, err } - tfplanSecretKey := types.NamespacedName{Namespace: req.Namespace, Name: "tfplan-" + r.terraform.WorkspaceName() + "-" + req.Name} + if req.BackendCompletelyDisable { + return &LoadTFPlanReply{Message: "ok"}, nil + } + + if r.terraform.GetClaimName() != "" { + if err := r.LoadTFPlanFromPVC(ctx, req); err != nil { + return nil, err + } + } else { + if err := r.LoadTFPlanFromSecret(ctx, req); err != nil { + return nil, err + } + } + + return &LoadTFPlanReply{Message: "ok"}, nil +} + +func (r *TerraformRunnerServer) LoadTFPlanFromPVC(ctx context.Context, req *LoadTFPlanRequest) error { + log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) + log.Info("loading plan from PVC storage") + + planFileName := r.GetPlanStorageName(req.Name, "") + planPVCFilePath, err := securejoin.SecureJoin(PlanStoragePath, planFileName) + if err != nil { + log.Error(err, "unable to create PVC file path for plan") + return err + } + tfplan, err := os.ReadFile(planPVCFilePath) + tfplan, err = utils.GzipDecode(tfplan) + if err != nil { + log.Error(err, "unable to decode the plan") + return err + } + + err = os.WriteFile(filepath.Join(r.tf.WorkingDir(), TFPlanName), tfplan, 0644) + if err != nil { + err = fmt.Errorf("error saving plan file to disk: %s", err) + log.Error(err, "unable to write the plan to disk") + return err + } + + return nil +} + +func (r *TerraformRunnerServer) LoadTFPlanFromSecret(ctx context.Context, req *LoadTFPlanRequest) error { + log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) + log.Info("loading plan from secret") + + tfplanSecretKey := types.NamespacedName{Namespace: req.Namespace, Name: r.GetPlanStorageName(req.Name, "")} tfplanSecret := corev1.Secret{} err := r.Get(ctx, tfplanSecretKey, &tfplanSecret) if err != nil { err = fmt.Errorf("error getting plan secret: %s", err) log.Error(err, "unable to get secret") - return nil, err + return err } if tfplanSecret.Annotations[SavedPlanSecretAnnotation] != req.PendingPlan { @@ -909,27 +990,23 @@ func (r *TerraformRunnerServer) LoadTFPlan(ctx context.Context, req *LoadTFPlanR req.PendingPlan, tfplanSecret.Annotations[SavedPlanSecretAnnotation]) log.Error(err, "plan name mismatch") - return nil, err + return err } - if req.BackendCompletelyDisable { - // do nothing - } else { - tfplan := tfplanSecret.Data[TFPlanName] - tfplan, err = utils.GzipDecode(tfplan) - if err != nil { - log.Error(err, "unable to decode the plan") - return nil, err - } - err = ioutil.WriteFile(filepath.Join(r.tf.WorkingDir(), TFPlanName), tfplan, 0644) - if err != nil { - err = fmt.Errorf("error saving plan file to disk: %s", err) - log.Error(err, "unable to write the plan to disk") - return nil, err - } + tfplan := tfplanSecret.Data[TFPlanName] + tfplan, err = utils.GzipDecode(tfplan) + if err != nil { + log.Error(err, "unable to decode the plan") + return err + } + err = ioutil.WriteFile(filepath.Join(r.tf.WorkingDir(), TFPlanName), tfplan, 0644) + if err != nil { + err = fmt.Errorf("error saving plan file to disk: %s", err) + log.Error(err, "unable to write the plan to disk") + return err } - return &LoadTFPlanReply{Message: "ok"}, nil + return nil } func (r *TerraformRunnerServer) Destroy(ctx context.Context, req *DestroyRequest) (*DestroyReply, error) { @@ -1190,25 +1267,63 @@ func (r *TerraformRunnerServer) GetOutputs(ctx context.Context, req *GetOutputsR return &GetOutputsReply{Outputs: outputs}, nil } -func (r *TerraformRunnerServer) FinalizeSecrets(ctx context.Context, req *FinalizeSecretsRequest) (*FinalizeSecretsReply, error) { +func (r *TerraformRunnerServer) FinalizeStorage(ctx context.Context, req *FinalizeStorageRequest) (*FinalizeStorageReply, error) { + log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) + log.Info("finalize the output storage") + + if err := r.FinalizeSecrets(ctx, req); err != nil { + log.Info("Failed to finalize secrets") + return nil, err + } + + if err := r.FinalizePVC(ctx, req); err != nil { + log.Info("Failed to finalize PVC storage") + return nil, err + } + + return &FinalizeStorageReply{Message: "ok"}, nil +} + +func (r *TerraformRunnerServer) FinalizePVC(ctx context.Context, req *FinalizeStorageRequest) error { + log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) + if r.terraform.GetClaimName() != "" { + planRegex := r.GetPlanStorageName(req.Name, "*") + planFiles, err := filepath.Glob(strings.Join([]string{PlanStoragePath, planRegex}, "/")) + if err != nil { + log.Info("failed to find files") + return err + } + for _, f := range planFiles { + if err := os.RemoveAll(f); err != nil { + log.Info("failed removing file: " + f) + return err + } + } + } + return nil +} + +func (r *TerraformRunnerServer) FinalizeSecrets(ctx context.Context, req *FinalizeStorageRequest) error { log := ctrl.LoggerFrom(ctx, "instance-id", r.InstanceID).WithName(loggerName) log.Info("finalize the output secrets") // nil dereference bug here - planObjectKey := types.NamespacedName{Namespace: req.Namespace, Name: "tfplan-" + req.Workspace + "-" + req.Name} + planObjectKey := types.NamespacedName{Namespace: req.Namespace, Name: r.GetPlanStorageName(req.Name, "")} var planSecret corev1.Secret if err := r.Client.Get(ctx, planObjectKey, &planSecret); err == nil { if err := r.Client.Delete(ctx, &planSecret); err != nil { // transient failure log.Error(err, "unable to delete the plan secret") - return nil, status.Error(codes.Internal, err.Error()) + return status.Error(codes.Internal, err.Error()) } + } else if apierrors.IsNotFound(err) && r.terraform.GetClaimName() != "" { + log.Error(err, "using PVC as storage. secret not found.") } else if apierrors.IsNotFound(err) { log.Error(err, "plan secret not found") - return nil, status.Error(codes.NotFound, err.Error()) + return status.Error(codes.NotFound, err.Error()) } else { // transient failure log.Error(err, "unable to get the plan secret") - return nil, status.Error(codes.Internal, err.Error()) + return status.Error(codes.Internal, err.Error()) } if req.HasSpecifiedOutputSecret { @@ -1218,19 +1333,19 @@ func (r *TerraformRunnerServer) FinalizeSecrets(ctx context.Context, req *Finali if err := r.Client.Delete(ctx, &outputsSecret); err != nil { // transient failure log.Error(err, "unable to delete the output secret") - return nil, status.Error(codes.Internal, err.Error()) + return status.Error(codes.Internal, err.Error()) } } else if apierrors.IsNotFound(err) { log.Error(err, "output secret not found") - return nil, status.Error(codes.NotFound, err.Error()) + return status.Error(codes.NotFound, err.Error()) } else { // transient failure log.Error(err, "unable to get the output secret") - return nil, status.Error(codes.Internal, err.Error()) + return status.Error(codes.Internal, err.Error()) } } - return &FinalizeSecretsReply{Message: "ok"}, nil + return nil } func (r *TerraformRunnerServer) ForceUnlock(ctx context.Context, req *ForceUnlockRequest) (*ForceUnlockReply, error) {