-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create connection secret with desired fields #73
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,5 +31,28 @@ spec: | |
# name: svals | ||
# namespace: wordpress | ||
# optional: false | ||
# connectionDetails: | ||
# - apiVersion: v1 | ||
# kind: Service | ||
# name: wordpress-example | ||
# namespace: wordpress | ||
# fieldPath: spec.clusterIP | ||
# #fieldPath: status.loadBalancer.ingress[0].ip | ||
# toConnectionSecretKey: ip | ||
# - apiVersion: v1 | ||
# kind: Service | ||
# name: wordpress-example | ||
# namespace: wordpress | ||
# fieldPath: spec.ports[0].port | ||
# toConnectionSecretKey: port | ||
# - apiVersion: v1 | ||
# kind: Secret | ||
# name: wordpress-example | ||
# namespace: wordpress | ||
# fieldPath: data.wordpress-password | ||
# toConnectionSecretKey: password | ||
Comment on lines
+48
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a So I made a list entry like this on my
And then I'm patching the name like so:
Now the issue is that on the
Which is of course true, because the connection secret is not part of that release, it's just a result of it.
How can I propagate the secret of that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we discussed offline, |
||
# writeConnectionSecretToRef: | ||
# name: wordpress-credentials | ||
# namespace: crossplane-system | ||
providerConfigRef: | ||
name: helm-provider |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,15 @@ package release | |
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath" | ||
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/types" | ||
|
||
"github.com/pkg/errors" | ||
"helm.sh/helm/v3/pkg/release" | ||
|
@@ -31,6 +40,7 @@ const ( | |
errReleaseInfoNilInObservedRelease = "release info is nil in observed helm release" | ||
errChartNilInObservedRelease = "chart field is nil in observed helm release" | ||
errChartMetaNilInObservedRelease = "chart metadata field is nil in observed helm release" | ||
errObjectNotPartOfRelease = "object is not part of release: %v" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really happy with where this landed for security reasons and minimizing unexpected behavior. I could imagine someone might install a few helm charts and create a final observation, but this puts some rails that no one mis-configures and fetches something from a different helm chart. The downside of course as discussed is that maybe an operator is installed and simply creates an object that isn't part of the release explicitly. If this ends up being a use case, they could provide an empty secret in the chart, or we could consider in the future an escape config to let them fetch arbitrary resources, but this seems like a great first step. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm interested in the scenario where I want to observe and provision connection details from a Secret created by an operator. In this case I want to retrieve the kubeconfig from a vcluster Release. I'm not confident that the controller could handle an existing secret without a patch either. Do I have a way to prevent |
||
) | ||
|
||
// generateObservation generates release observation for the input release object | ||
|
@@ -112,3 +122,53 @@ func isUpToDate(ctx context.Context, kube client.Client, in *v1beta1.ReleasePara | |
func isPending(s release.Status) bool { | ||
return s == release.StatusPendingInstall || s == release.StatusPendingUpgrade || s == release.StatusPendingRollback | ||
} | ||
|
||
func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1beta1.ConnectionDetail, relName, relNamespace string) (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, "cannot get object") | ||
} | ||
|
||
// TODO(hasan): consider making this check configurable, i.e. possible to skip via a field in spec | ||
if !partOfRelease(ro, relName, relNamespace) { | ||
return mcd, errors.Errorf(errObjectNotPartOfRelease, cd.ObjectReference) | ||
} | ||
|
||
paved := fieldpath.Pave(ro.Object) | ||
v, err := paved.GetValue(cd.FieldPath) | ||
if err != nil { | ||
return mcd, errors.Wrapf(err, "failed to get value at fieldPath: %s", cd.FieldPath) | ||
} | ||
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, "failed to decode secret data") | ||
} | ||
} | ||
|
||
mcd[cd.ToConnectionSecretKey] = fv | ||
} | ||
|
||
return mcd, nil | ||
} | ||
|
||
func unstructuredFromObjectRef(r corev1.ObjectReference) unstructured.Unstructured { | ||
u := unstructured.Unstructured{} | ||
u.SetAPIVersion(r.APIVersion) | ||
u.SetKind(r.Kind) | ||
u.SetName(r.Name) | ||
u.SetNamespace(r.Namespace) | ||
|
||
return u | ||
} | ||
|
||
func partOfRelease(u unstructured.Unstructured, relName, relNamespace string) bool { | ||
a := u.GetAnnotations() | ||
return a[helmReleaseNameAnnotation] == relName && a[helmReleaseNamespaceAnnotation] == relNamespace | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel connectionDetails isn't the perfect name as it's really general purpose data sync from a Release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean and actually gave it a try to change it in that direction, however ended up with current solution because of following reasons:
writeConnectionSecretToRef
is part of crossplane managed resource spec, there are existing machinery coming with crossplane runtime hence this field is common across all providers. I preferred to reuse this pattern/machinery here.connectionDetails
is also something crossplane users should be familiar with, it exists in composite resource api (example) with similar purposes, where you select what you want to be included in the connection secret that composite resource creates.