Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apis/quay/v1/quayregistry_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ const (
ConditionReasonComponentOverrideInvalid ConditionReason = "ComponentOverrideInvalid"
ConditionReasonPVCPending ConditionReason = "PVCPending"
ConditionReasonPVCProvisioningFailed ConditionReason = "PVCProvisioningFailed"
ConditionReasonCredentialRequestNotProvisioned ConditionReason = "CredentialRequestNotProvisioned"
ConditionReasonConflictingCredentials ConditionReason = "ConflictingCredentials"
)

// Condition is a single condition of a QuayRegistry.
Expand Down
40 changes: 39 additions & 1 deletion bundle/manifests/quay-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ metadata:
features.operators.openshift.io/fips-compliant: "true"
features.operators.openshift.io/proxy-aware: "true"
features.operators.openshift.io/tls-profiles: "false"
features.operators.openshift.io/token-auth-aws: "false"
features.operators.openshift.io/token-auth-aws: "true"
features.operators.openshift.io/token-auth-azure: "false"
features.operators.openshift.io/token-auth-gcp: "false"
name: quay-operator.v3.99.0-dev
Expand Down Expand Up @@ -168,6 +168,17 @@ spec:
value: quay.io/sclorg/postgresql-13-c9s:latest
- name: RELATED_IMAGE_COMPONENT_REDIS
value: quay.io/sclorg/redis-7-c9s:latest
volumeMounts:
- name: bound-sa-token
mountPath: /var/run/secrets/openshift/serviceaccount
readOnly: true
volumes:
- name: bound-sa-token
projected:
sources:
- serviceAccountToken:
path: token
audience: openshift
serviceAccountName: quay-operator
permissions:
- rules:
Expand Down Expand Up @@ -252,6 +263,33 @@ spec:
verbs:
- get
serviceAccountName: quay-operator
clusterPermissions:
- rules:
- apiGroups:
- config.openshift.io
resources:
- infrastructures
verbs:
- get
- apiGroups:
- operator.openshift.io
resources:
- cloudcredentials
verbs:
- get
- apiGroups:
- cloudcredential.openshift.io
resources:
- credentialsrequests
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
serviceAccountName: quay-operator
strategy: deployment
installModes:
- supported: true
Expand Down
19 changes: 19 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,23 @@ rules:
- patch
- update
- watch
- apiGroups:
- cloudcredential.openshift.io
resources:
- credentialsrequests
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- config.openshift.io
resources:
- apiservers
- infrastructures
verbs:
- get
- apiGroups:
Expand Down Expand Up @@ -99,6 +112,12 @@ rules:
- patch
- update
- watch
- apiGroups:
- operator.openshift.io
resources:
- cloudcredentials
verbs:
- get
- apiGroups:
- quay.redhat.com
resources:
Expand Down
76 changes: 76 additions & 0 deletions controllers/quay/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
Expand Down Expand Up @@ -192,6 +193,7 @@ func (r *QuayRegistryReconciler) checkManagedTLS(
}

var errRouteProbeInProgress = fmt.Errorf("route probe in progress, awaiting ingress status")
var errCredentialRequestPending = fmt.Errorf("CredentialRequest not yet provisioned by CCO")

// checkExternalTLSSecret validates and reads TLS cert/key from an external Secret referenced
// by the TLS component's secretRef. Populates TLSCert, TLSKey, and TLSSecretHash on the context.
Expand Down Expand Up @@ -669,3 +671,77 @@ func getCertificatesPEM(address string) ([]byte, error) {

return b.Bytes(), nil
}

func (r *QuayRegistryReconciler) checkSTSCapability(
ctx context.Context, qctx *quaycontext.QuayRegistryContext, quay *v1.QuayRegistry,
) error {
if r.STSRoleARN == "" {
return nil
}

if v1.ComponentIsManaged(quay.Spec.Components, v1.ComponentObjectStorage) {
r.Log.Info("ROLEARN is set but ObjectStorage is managed, skipping STS path")
return nil
}

var infra unstructured.Unstructured
infra.SetGroupVersionKind(schema.GroupVersionKind{
Group: "config.openshift.io",
Version: "v1",
Kind: "Infrastructure",
})
if err := r.Get(ctx, types.NamespacedName{Name: "cluster"}, &infra); err != nil {
if errors.IsNotFound(err) || meta.IsNoMatchError(err) {
r.Log.Info("Infrastructure API not available, skipping STS path")
return nil
}
return fmt.Errorf("unable to get Infrastructure: %w", err)
}

platformType, _, _ := unstructured.NestedString(infra.Object, "status", "platformStatus", "type")
if platformType != "AWS" {
r.Log.Info("cluster platform is not AWS, skipping STS path", "platform", platformType)
return nil
}

var cco unstructured.Unstructured
cco.SetGroupVersionKind(schema.GroupVersionKind{
Group: "operator.openshift.io",
Version: "v1",
Kind: "CloudCredential",
})
if err := r.Get(ctx, types.NamespacedName{Name: "cluster"}, &cco); err != nil {
if errors.IsNotFound(err) || meta.IsNoMatchError(err) {
r.Log.Info("CloudCredential API not available, skipping STS path")
return nil
}
return fmt.Errorf("unable to get CloudCredential: %w", err)
}

credentialsMode, _, _ := unstructured.NestedString(cco.Object, "spec", "credentialsMode")
if credentialsMode == "Mint" || credentialsMode == "Passthrough" {
r.Log.Info("CCO credentials mode is not STS-compatible, skipping STS path", "mode", credentialsMode)
return nil
}

var crList unstructured.UnstructuredList
crList.SetGroupVersionKind(schema.GroupVersionKind{
Group: "cloudcredential.openshift.io",
Version: "v1",
Kind: "CredentialsRequestList",
})
if err := r.List(ctx, &crList, client.InNamespace(quay.GetNamespace())); err != nil {
r.Log.Info("CredentialsRequest CRD not available, skipping STS path")
return nil
}

qctx.STSEnabled = true
qctx.STSRoleARN = r.STSRoleARN
qctx.STSCredentialRequestName = fmt.Sprintf("%s-quay-app", quay.GetName())
qctx.STSCredentialSecretName = fmt.Sprintf("%s-quay-app-aws", quay.GetName())
r.Log.Info("STS capability detected",
"roleARN", r.STSRoleARN,
"credentialRequestName", qctx.STSCredentialRequestName,
)
return nil
}
Loading
Loading