diff --git a/.gitignore b/.gitignore index abe47e3..501a249 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ __debug* # work /.work/ +.vscode/ \ No newline at end of file diff --git a/operator/minioutil/admin.go b/operator/minioutil/admin.go index e614a86..d832343 100644 --- a/operator/minioutil/admin.go +++ b/operator/minioutil/admin.go @@ -14,19 +14,29 @@ import ( // It can be used to assign a policy to a usser. func NewMinioAdmin(ctx context.Context, c client.Client, config *providerv1.ProviderConfig) (*madmin.AdminClient, error) { + secret, tls, parsed, err := ExtractDataFromProviderConfig(ctx, c, config) + if err != nil { + return nil, err + } + + return madmin.New(parsed.Host, string(secret.Data[MinioIDKey]), string(secret.Data[MinioSecretKey]), tls) +} + +// this is the helper function that is used in the NewMinioClient function +func ExtractDataFromProviderConfig(ctx context.Context, c client.Client, config *providerv1.ProviderConfig) (*corev1.Secret, bool, *url.URL, error) { secret := &corev1.Secret{} key := client.ObjectKey{Name: config.Spec.Credentials.APISecretRef.Name, Namespace: config.Spec.Credentials.APISecretRef.Namespace} err := c.Get(ctx, key, secret) if err != nil { - return nil, err + return nil, false, nil, err } parsed, err := url.Parse(config.Spec.MinioURL) if err != nil { - return nil, err + return nil, false, nil, err } tls := isTLSEnabled(parsed) - return madmin.New(parsed.Host, string(secret.Data[MinioIDKey]), string(secret.Data[MinioSecretKey]), tls) + return secret, tls, parsed, nil } diff --git a/operator/user/connector.go b/operator/user/connector.go index 98ea26a..6063947 100644 --- a/operator/user/connector.go +++ b/operator/user/connector.go @@ -3,6 +3,7 @@ package user import ( "context" "fmt" + "net/url" "github.com/crossplane/crossplane-runtime/pkg/event" "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" @@ -26,8 +27,11 @@ type connector struct { } type userClient struct { - ma *madmin.AdminClient - recorder event.Recorder + ma *madmin.AdminClient + kube client.Client + recorder event.Recorder + url *url.URL + tlsSettings bool } func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { @@ -54,9 +58,17 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E return nil, err } + _, tls, parsed, err := minioutil.ExtractDataFromProviderConfig(ctx, c.kube, config) + if err != nil { + return nil, err + } + uc := &userClient{ - ma: ma, - recorder: c.recorder, + ma: ma, + kube: c.kube, + recorder: c.recorder, + url: parsed, + tlsSettings: tls, } return uc, nil diff --git a/operator/user/observe.go b/operator/user/observe.go index 14202ca..d867f0c 100644 --- a/operator/user/observe.go +++ b/operator/user/observe.go @@ -9,7 +9,12 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/minio/madmin-go/v3" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" miniov1 "github.com/vshn/provider-minio/apis/minio/v1" + k8svi "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" ) const ( @@ -18,6 +23,7 @@ const ( ) func (u *userClient) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + log := ctrl.LoggerFrom(ctx) user, ok := mg.(*miniov1.User) if !ok { @@ -59,6 +65,35 @@ func (u *userClient) Observe(ctx context.Context, mg resource.Managed) (managed. user.SetConditions(miniov1.Disabled()) } + if mg.GetDeletionTimestamp() == nil { + + secret := k8svi.Secret{} + + err = u.kube.Get(ctx, types.NamespacedName{ + Namespace: mg.GetWriteConnectionSecretToReference().Namespace, + Name: mg.GetWriteConnectionSecretToReference().Name, + }, &secret) + if err != nil { + return managed.ExternalObservation{}, err + } + + mclient, err := minio.New(u.url.Host, &minio.Options{ + Creds: credentials.NewStaticV4(string(secret.Data[AccessKeyName]), string(secret.Data[SecretKeyName]), ""), + Secure: u.tlsSettings, + }) + if err != nil { + return managed.ExternalObservation{ResourceUpToDate: false, ResourceExists: true}, nil + } + + _, err = mclient.ListBuckets(context.Background()) + // AccessDenied is ok in this context, because we just want to check if the user has working credentials + if err != nil && err.Error() != "Access Denied." { + return managed.ExternalObservation{ResourceUpToDate: false, ResourceExists: true}, nil + } + + log.Info("user client created, everything went fine " + string(secret.Data[AccessKeyName]) + " " + string(secret.Data[SecretKeyName])) + } + return managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil } diff --git a/operator/user/update.go b/operator/user/update.go index 6e44d2d..acae81b 100644 --- a/operator/user/update.go +++ b/operator/user/update.go @@ -8,7 +8,10 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/minio/madmin-go/v3" + miniov1 "github.com/vshn/provider-minio/apis/minio/v1" + k8svi "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" controllerruntime "sigs.k8s.io/controller-runtime" ) @@ -48,6 +51,24 @@ func (u *userClient) Update(ctx context.Context, mg resource.Managed) (managed.E return managed.ExternalUpdate{}, err } + if mg.GetDeletionTimestamp() == nil { + + secret := k8svi.Secret{} + + err = u.kube.Get(ctx, types.NamespacedName{ + Namespace: mg.GetWriteConnectionSecretToReference().Namespace, + Name: mg.GetWriteConnectionSecretToReference().Name, + }, &secret) + if err != nil { + return managed.ExternalUpdate{}, err + } + + err = u.ma.SetUser(ctx, string(secret.Data[AccessKeyName]), string(secret.Data[SecretKeyName]), madmin.AccountEnabled) + if err != nil { + return managed.ExternalUpdate{}, err + } + } + u.emitUpdateEvent(user) return managed.ExternalUpdate{}, nil } diff --git a/test/e2e/upload-object.sh b/test/e2e/upload-object.sh index 77c9858..291733e 100755 --- a/test/e2e/upload-object.sh +++ b/test/e2e/upload-object.sh @@ -12,4 +12,10 @@ access_key=$(kubectl -n "${secret_namespace}" get secret "${secret_name}" -o jso secret_key=$(kubectl -n "${secret_namespace}" get secret "${secret_name}" -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d) export MC_HOST_minio=http://${access_key}:${secret_key}@${endpoint} -"${GOBIN}/mc" cp --quiet "${file_path}" "minio/${bucket_name}" +echo "Uploading object to bucket: ${bucket_name}" +echo "File path: ${file_path}" +echo "Endpoint: ${endpoint}" +echo "Access key: ${access_key}" +echo "Secret key: ${secret_key}" + +"${GOBIN}/mc" cp --quiet --debug "${file_path}" "minio/${bucket_name}"