From 90af2ac2b351c8033a317629757d608e813e1065 Mon Sep 17 00:00:00 2001
From: Alexei Mikhailov <amikhailov83@gmail.com>
Date: Mon, 17 Jun 2024 20:56:30 +0300
Subject: [PATCH] Allow generating `connectionSecretKeys` for XRDs

See https://docs.crossplane.io/latest/concepts/connection-details/

To expose connection details from a Composition, users must specify
`spec.connectionSecretKeys` in XRD. This commit adds support for this.

I also fixed an error in `examples/xrd-gen/apis/generate.go` -
controller-gen would fail, complaining that `xrd` generator is specified
twice.
---
 examples/xrd-gen/apis/generate.go             |   2 +-
 .../xrd-gen/apis/v1alpha1/example_types.go    |   1 +
 .../apis/v1alpha1/zz_generated.deepcopy.go    |   1 -
 .../xrds/test.example.com_xexamples.yaml      | 198 +++++++++++++++++-
 pkg/generate/xrd/markers/markers.go           |   9 +
 5 files changed, 199 insertions(+), 12 deletions(-)

diff --git a/examples/xrd-gen/apis/generate.go b/examples/xrd-gen/apis/generate.go
index 5345d17..ffba92d 100644
--- a/examples/xrd-gen/apis/generate.go
+++ b/examples/xrd-gen/apis/generate.go
@@ -2,4 +2,4 @@ package apis
 
 //go:generate rm -rf ../package/crds
 
-//go:generate go run -tags generate ../../../cmd/xrd-gen xrd paths=./... xrd:allowDangerousTypes=true,crdVersions=v1 object:headerFile=../../../hack/boilerplate.go.txt,year=2022 output:artifacts:config=../package/xrds
+//go:generate go run -tags generate ../../../cmd/xrd-gen paths=./... xrd:allowDangerousTypes=true,crdVersions=v1 object:headerFile=../../../hack/boilerplate.go.txt,year=2022 output:artifacts:config=../package/xrds
diff --git a/examples/xrd-gen/apis/v1alpha1/example_types.go b/examples/xrd-gen/apis/v1alpha1/example_types.go
index b368215..0927988 100644
--- a/examples/xrd-gen/apis/v1alpha1/example_types.go
+++ b/examples/xrd-gen/apis/v1alpha1/example_types.go
@@ -29,6 +29,7 @@ type XExampleStatus struct {
 // +crossbuilder:generate:xrd:claimNames:kind=Example,plural=examples
 // +crossbuilder:generate:xrd:defaultCompositionRef:name=example-composition
 // +crossbuilder:generate:xrd:enforcedCompositionRef:name=example-composition-2
+// +crossbuilder:generate:xrd:connectionSecretKeys={username,password}
 type XExample struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/examples/xrd-gen/apis/v1alpha1/zz_generated.deepcopy.go b/examples/xrd-gen/apis/v1alpha1/zz_generated.deepcopy.go
index 9d1c30a..324a067 100644
--- a/examples/xrd-gen/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/examples/xrd-gen/apis/v1alpha1/zz_generated.deepcopy.go
@@ -1,5 +1,4 @@
 //go:build !ignore_autogenerated
-// +build !ignore_autogenerated
 
 /*
 Copyright 2022 The Crossbuilder Authors.
diff --git a/examples/xrd-gen/package/xrds/test.example.com_xexamples.yaml b/examples/xrd-gen/package/xrds/test.example.com_xexamples.yaml
index e6aa4a8..323bab4 100644
--- a/examples/xrd-gen/package/xrds/test.example.com_xexamples.yaml
+++ b/examples/xrd-gen/package/xrds/test.example.com_xexamples.yaml
@@ -1,4 +1,6 @@
 ---
+apiVersion: apiextensions.crossplane.io/v1
+kind: CompositeResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: (devel)
@@ -8,6 +10,9 @@ spec:
   claimNames:
     kind: Example
     plural: examples
+  connectionSecretKeys:
+  - username
+  - password
   defaultCompositionRef:
     name: example-composition
   enforcedCompositionRef:
@@ -21,9 +26,8 @@ spec:
     plural: xexamples
     singular: xexample
   versions:
-  - deprecated: false
-    name: v1alpha1
-    referenceable: false
+  - name: v1alpha1
+    referenceable: true
     schema:
       openAPIV3Schema:
         properties:
@@ -31,13 +35,42 @@ spec:
             properties:
               deletionPolicy:
                 default: Delete
-                description: DeletionPolicy specifies what will happen to the underlying
+                description: 'DeletionPolicy specifies what will happen to the underlying
                   external when this managed resource is deleted - either "Delete"
-                  or "Orphan" the external resource.
+                  or "Orphan" the external resource. This field is planned to be deprecated
+                  in favor of the ManagementPolicies field in a future release. Currently,
+                  both could be set independently and non-default values would be
+                  honored if the feature flag is enabled. See the design doc for more
+                  information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223'
                 enum:
                 - Orphan
                 - Delete
                 type: string
+              managementPolicies:
+                default:
+                - '*'
+                description: 'THIS IS A BETA FIELD. It is on by default but can be
+                  opted out through a Crossplane feature flag. ManagementPolicies
+                  specify the array of actions Crossplane is allowed to take on the
+                  managed and external resources. This field is planned to replace
+                  the DeletionPolicy field in a future release. Currently, both could
+                  be set independently and non-default values would be honored if
+                  the feature flag is enabled. If both are custom, the DeletionPolicy
+                  field will be ignored. See the design doc for more information:
+                  https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223
+                  and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md'
+                items:
+                  description: A ManagementAction represents an action that the Crossplane
+                    controllers can take on an external resource.
+                  enum:
+                  - Observe
+                  - Create
+                  - Update
+                  - Delete
+                  - LateInitialize
+                  - '*'
+                  type: string
+                type: array
               parameters:
                 properties:
                   exampleField:
@@ -55,24 +88,169 @@ spec:
                   name:
                     description: Name of the referenced object.
                     type: string
+                  policy:
+                    description: Policies for referencing.
+                    properties:
+                      resolution:
+                        default: Required
+                        description: Resolution specifies whether resolution of this
+                          reference is required. The default is 'Required', which
+                          means the reconcile will fail if the reference cannot be
+                          resolved. 'Optional' means this reference will be a no-op
+                          if it cannot be resolved.
+                        enum:
+                        - Required
+                        - Optional
+                        type: string
+                      resolve:
+                        description: Resolve specifies when this reference should
+                          be resolved. The default is 'IfNotPresent', which will attempt
+                          to resolve the reference only when the corresponding field
+                          is not present. Use 'Always' to resolve the reference on
+                          every reconcile.
+                        enum:
+                        - Always
+                        - IfNotPresent
+                        type: string
+                    type: object
                 required:
                 - name
                 type: object
-              providerRef:
-                description: 'ProviderReference specifies the provider that will be
-                  used to create, observe, update, and delete this managed resource.
-                  Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`'
+              publishConnectionDetailsTo:
+                description: PublishConnectionDetailsTo specifies the connection secret
+                  config which contains a name, metadata and a reference to secret
+                  store config to which any connection details for this managed resource
+                  should be written. Connection details frequently include the endpoint,
+                  username, and password required to connect to the managed resource.
                 properties:
+                  configRef:
+                    default:
+                      name: default
+                    description: SecretStoreConfigRef specifies which secret store
+                      config should be used for this ConnectionSecret.
+                    properties:
+                      name:
+                        description: Name of the referenced object.
+                        type: string
+                      policy:
+                        description: Policies for referencing.
+                        properties:
+                          resolution:
+                            default: Required
+                            description: Resolution specifies whether resolution of
+                              this reference is required. The default is 'Required',
+                              which means the reconcile will fail if the reference
+                              cannot be resolved. 'Optional' means this reference
+                              will be a no-op if it cannot be resolved.
+                            enum:
+                            - Required
+                            - Optional
+                            type: string
+                          resolve:
+                            description: Resolve specifies when this reference should
+                              be resolved. The default is 'IfNotPresent', which will
+                              attempt to resolve the reference only when the corresponding
+                              field is not present. Use 'Always' to resolve the reference
+                              on every reconcile.
+                            enum:
+                            - Always
+                            - IfNotPresent
+                            type: string
+                        type: object
+                    required:
+                    - name
+                    type: object
+                  metadata:
+                    description: Metadata is the metadata for connection secret.
+                    properties:
+                      annotations:
+                        additionalProperties:
+                          type: string
+                        description: Annotations are the annotations to be added to
+                          connection secret. - For Kubernetes secrets, this will be
+                          used as "metadata.annotations". - It is up to Secret Store
+                          implementation for others store types.
+                        type: object
+                      labels:
+                        additionalProperties:
+                          type: string
+                        description: Labels are the labels/tags to be added to connection
+                          secret. - For Kubernetes secrets, this will be used as "metadata.labels".
+                          - It is up to Secret Store implementation for others store
+                          types.
+                        type: object
+                      type:
+                        description: Type is the SecretType for the connection secret.
+                          - Only valid for Kubernetes Secret Stores.
+                        type: string
+                    type: object
                   name:
-                    description: Name of the referenced object.
+                    description: Name is the name of the connection secret.
+                    type: string
+                required:
+                - name
+                type: object
+              writeConnectionSecretToRef:
+                description: WriteConnectionSecretToReference specifies the namespace
+                  and name of a Secret to which any connection details for this managed
+                  resource should be written. Connection details frequently include
+                  the endpoint, username, and password required to connect to the
+                  managed resource. This field is planned to be replaced in a future
+                  release in favor of PublishConnectionDetailsTo. Currently, both
+                  could be set independently and connection details would be published
+                  to both without affecting each other.
+                properties:
+                  name:
+                    description: Name of the secret.
+                    type: string
+                  namespace:
+                    description: Namespace of the secret.
                     type: string
                 required:
                 - name
+                - namespace
                 type: object
             required:
             - parameters
             type: object
           status:
+            properties:
+              conditions:
+                description: Conditions of the resource.
+                items:
+                  description: A Condition that may apply to a resource.
+                  properties:
+                    lastTransitionTime:
+                      description: LastTransitionTime is the last time this condition
+                        transitioned from one status to another.
+                      format: date-time
+                      type: string
+                    message:
+                      description: A Message containing details about this condition's
+                        last transition from one status to another, if any.
+                      type: string
+                    reason:
+                      description: A Reason for this condition's last transition from
+                        one status to another.
+                      type: string
+                    status:
+                      description: Status of this condition; is it currently True,
+                        False, or Unknown?
+                      type: string
+                    type:
+                      description: Type of this condition. At most one of each condition
+                        type may apply to a resource at any point in time.
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+                x-kubernetes-list-map-keys:
+                - type
+                x-kubernetes-list-type: map
             type: object
         required:
         - spec
diff --git a/pkg/generate/xrd/markers/markers.go b/pkg/generate/xrd/markers/markers.go
index 8521270..ff0b0e6 100644
--- a/pkg/generate/xrd/markers/markers.go
+++ b/pkg/generate/xrd/markers/markers.go
@@ -14,6 +14,7 @@ var XRDMarkers = []*definitionWithHelp{
 	must(markers.MakeDefinition("crossbuilder:generate:xrd:defaultCompositionRef", markers.DescribesType, DefaultCompositionRef{})),
 	must(markers.MakeDefinition("crossbuilder:generate:xrd:enforcedCompositionRef", markers.DescribesType, EnforcedCompositionRef{})),
 	must(markers.MakeDefinition("crossbuilder:generate:xrd:defaultCompositeDeletePolicy", markers.DescribesType, DefaultCompositeDeletePolicy{})),
+	must(markers.MakeDefinition("crossbuilder:generate:xrd:connectionSecretKeys", markers.DescribesType, ConnectionSecretKeys(nil))),
 }
 
 func init() {
@@ -94,3 +95,11 @@ func (c DefaultCompositeDeletePolicy) ApplyToXRD(xrd *xapiext.CompositeResourceD
 	// test(c)
 	return nil
 }
+
+// ConnectionSecretKeys is a marker to specify connection secret keys of an XRD
+type ConnectionSecretKeys []string
+
+func (c ConnectionSecretKeys) ApplyToXRD(xrd *xapiext.CompositeResourceDefinition, version string) error {
+	xrd.Spec.ConnectionSecretKeys = c
+	return nil
+}