Skip to content

Commit

Permalink
Merge pull request #99 from st3v/main
Browse files Browse the repository at this point in the history
Allow Object to define a set of connectionDetails
  • Loading branch information
turkenh committed Mar 8, 2023
2 parents 56b5974 + b759622 commit 725baee
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 41 deletions.
17 changes: 13 additions & 4 deletions apis/object/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ limitations under the License.
package v1alpha1

import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"

xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
)

// ObjectAction defines actions applicable to Object
Expand Down Expand Up @@ -116,10 +118,17 @@ type ObjectObservation struct {
// A ObjectSpec defines the desired state of a Object.
type ObjectSpec struct {
xpv1.ResourceSpec `json:",inline"`
ConnectionDetails []ConnectionDetail `json:"connectionDetails,omitempty"`
ForProvider ObjectParameters `json:"forProvider"`
// +kubebuilder:default=Default
ManagementPolicy `json:"managementPolicy,omitempty"`
References []Reference `json:"references,omitempty"`
ForProvider ObjectParameters `json:"forProvider"`
References []Reference `json:"references,omitempty"`
}

// ConnectionDetail represents an entry in the connection secret for an Object
type ConnectionDetail struct {
v1.ObjectReference `json:",inline"`
ToConnectionSecretKey string `json:"toConnectionSecretKey,omitempty"`
}

// A ObjectStatus represents the observed state of a Object.
Expand Down
23 changes: 22 additions & 1 deletion apis/object/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 42 additions & 9 deletions examples/in-composition/composition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,19 +161,28 @@ spec:
namespace: argocd
fieldPath: status.loadBalancer.ingress[0].ip
toConnectionSecretKey: argocd-ip
# unfortunately following is not possible today due to this limitation in provider-helm:
# https://github.com/crossplane-contrib/provider-helm/blob/1d3a1c8c13a90e36c26191c70599c2e112fa10c6/pkg/controller/release/observe.go#L136
# - apiVersion: v1
# kind: Secret
# name: argocd-initial-admin-secret
# namespace: argocd
# fieldPath: data.password
# toConnectionSecretKey: argocd-password
- apiVersion: v1
kind: Secret
name: argocd-initial-admin-secret
namespace: argocd
fieldPath: data.password
toConnectionSecretKey: argocd-password
skipPartOfReleaseCheck: true
writeConnectionSecretToRef:
namespace: crossplane-system
patches:
- fromFieldPath: metadata.name
toFieldPath: spec.providerConfigRef.name
connectionDetails:
- fromConnectionSecretKey: argocd-ip
- fromConnectionSecretKey: argocd-password
patches:
- fromFieldPath: "metadata.uid"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-argocd"
- base:
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: ProviderConfig
Expand Down Expand Up @@ -214,10 +223,34 @@ spec:
destination:
namespace: argocd
server: https://kubernetes.default.svc
connectionDetails:
- apiVersion: v1
kind: Service
name: guestbook-ui
namespace: argocd
fieldPath: spec.clusterIP
toConnectionSecretKey: host
- apiVersion: v1
kind: Service
name: guestbook-ui
namespace: argocd
fieldPath: spec.ports[0].port
toConnectionSecretKey: port
writeConnectionSecretToRef:
namespace: crossplane-system
connectionDetails:
- fromConnectionSecretKey: host
- fromConnectionSecretKey: port
patches:
- fromFieldPath: metadata.name
toFieldPath: spec.providerConfigRef.name
- fromFieldPath: "spec.repoURL"
toFieldPath: "spec.forProvider.manifest.spec.source.repoURL"
- fromFieldPath: "spec.path"
toFieldPath: "spec.forProvider.manifest.spec.source.path"
toFieldPath: "spec.forProvider.manifest.spec.source.path"
- fromFieldPath: "metadata.uid"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-guestbook"
63 changes: 59 additions & 4 deletions internal/controller/object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ package object

import (
"context"
"encoding/base64"
"fmt"
"strings"

"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
Expand All @@ -33,6 +36,7 @@ import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/controller"
"github.com/crossplane/crossplane-runtime/pkg/event"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/ratelimiter"
Expand Down Expand Up @@ -74,6 +78,10 @@ const (
errRemoveReferenceFinalizer = "cannot remove finalizer from referenced resource"
objFinalizerName = "finalizer.managedresource.crossplane.io"
refFinalizerNamePrefix = "kubernetes.crossplane.io/referred-by-object-"

errGetConnectionDetails = "cannot get connection details"
errGetValueAtFieldPath = "cannot get value at fieldPath"
errDecodeSecretData = "cannot decode secret data"
)

// Setup adds a controller that reconciles Object managed resources.
Expand Down Expand Up @@ -233,7 +241,7 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
if last, err = getLastApplied(cr, observed); err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, errGetLastApplied)
}
return c.handleLastApplied(cr, last, desired)
return c.handleLastApplied(ctx, cr, last, desired)
}

func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) {
Expand Down Expand Up @@ -430,7 +438,7 @@ func (c *external) isNotFound(obj *v1alpha1.Object, err error) bool {
return isNotFound
}

func (c *external) handleLastApplied(obj *v1alpha1.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) {
func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha1.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) {
isUpToDate := false

if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionUpdate) {
Expand All @@ -447,9 +455,15 @@ func (c *external) handleLastApplied(obj *v1alpha1.Object, last, desired *unstru

obj.Status.SetConditions(xpv1.Available())

cd, err := connectionDetails(ctx, c.client, obj.Spec.ConnectionDetails)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, errGetConnectionDetails)
}

return managed.ExternalObservation{
ResourceExists: true,
ResourceUpToDate: true,
ResourceExists: true,
ResourceUpToDate: true,
ConnectionDetails: cd,
}, nil
}

Expand Down Expand Up @@ -560,3 +574,44 @@ func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object)
err = f.client.Update(ctx, obj)
return errors.Wrap(err, errRemoveFinalizer)
}

func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1alpha1.ConnectionDetail) (managed.ConnectionDetails, error) {
mcd := managed.ConnectionDetails{}

for _, cd := range connDetails {
ro := unstructuredFromObjectRef(cd.ObjectReference)
if err := kube.Get(ctx, types.NamespacedName{Name: ro.GetName(), Namespace: ro.GetNamespace()}, &ro); err != nil {
return mcd, errors.Wrap(err, errGetObject)
}

paved := fieldpath.Pave(ro.Object)
v, err := paved.GetValue(cd.FieldPath)
if err != nil {
return mcd, errors.Wrap(err, errGetValueAtFieldPath)
}

s := fmt.Sprintf("%v", v)
fv := []byte(s)
// prevent secret data being encoded twice
if cd.Kind == "Secret" && cd.APIVersion == "v1" && strings.HasPrefix(cd.FieldPath, "data") {
fv, err = base64.StdEncoding.DecodeString(s)
if err != nil {
return mcd, errors.Wrap(err, errDecodeSecretData)
}
}

mcd[cd.ToConnectionSecretKey] = fv
}

return mcd, nil
}

func unstructuredFromObjectRef(r v1.ObjectReference) unstructured.Unstructured {
u := unstructured.Unstructured{}
u.SetAPIVersion(r.APIVersion)
u.SetKind(r.Kind)
u.SetName(r.Name)
u.SetNamespace(r.Namespace)

return u
}
Loading

0 comments on commit 725baee

Please sign in to comment.