Skip to content

Commit a4da4ec

Browse files
committed
Add cluster registry config
Signed-off-by: melserngawy <[email protected]>
1 parent 2fe1c84 commit a4da4ec

File tree

6 files changed

+469
-71
lines changed

6 files changed

+469
-71
lines changed

config/crd/bases/controlplane.cluster.x-k8s.io_rosacontrolplanes.yaml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,73 @@ spec:
8282
rule: self == oldSelf
8383
- message: billingAccount must be a valid AWS account ID
8484
rule: self.matches('^[0-9]{12}$')
85+
clusterRegistryConfig:
86+
description: ClusterRegistryConfig represents registry config used
87+
with the cluster.
88+
properties:
89+
additionalTrustedCa:
90+
additionalProperties:
91+
type: string
92+
description: |-
93+
AdditionalTrustedCa containing the registry hostname as the key, and the PEM-encoded certificate as the value,
94+
for each additional registry CA to trust.
95+
type: object
96+
allowedRegistriesForImport:
97+
description: |-
98+
AllowedRegistriesForImport limits the container image registries that normal users may import
99+
images from. Set this list to the registries that you trust to contain valid Docker
100+
images and that you want applications to be able to import from.
101+
items:
102+
description: RegistryLocation contains a location of the registry
103+
specified by the registry domain name.
104+
properties:
105+
domainName:
106+
description: |-
107+
domainName specifies a domain name for the registry. The domain name might include wildcards, like '*' or '??'.
108+
In case the registry use non-standard (80 or 443) port, the port should be included in the domain name as well.
109+
type: string
110+
insecure:
111+
default: false
112+
description: insecure indicates whether the registry is
113+
secure (https) or insecure (http), default is secured.
114+
type: boolean
115+
type: object
116+
type: array
117+
registrySources:
118+
description: |-
119+
RegistrySources contains configuration that determines how the container runtime
120+
should treat individual registries when accessing images. It does not contain configuration
121+
for the internal cluster registry. AllowedRegistries, BlockedRegistries are mutually exclusive.
122+
properties:
123+
allowedRegistries:
124+
description: |-
125+
AllowedRegistries: registries for which image pull and push actions are allowed.
126+
To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
127+
For example, *.example.com. You can specify an individual repository within a registry.
128+
For example: reg1.io/myrepo/myapp:latest. All other registries are blocked.
129+
items:
130+
type: string
131+
type: array
132+
blockedRegistries:
133+
description: |-
134+
BlockedRegistries: registries for which image pull and push actions are denied.
135+
To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
136+
For example, *.example.com. You can specify an individual repository within a registry.
137+
For example: reg1.io/myrepo/myapp:latest. All other registries are allowed.
138+
items:
139+
type: string
140+
type: array
141+
insecureRegistries:
142+
description: |-
143+
InsecureRegistries are registries which do not have a valid TLS certificate or only support HTTP connections.
144+
To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
145+
For example, *.example.com. You can specify an individual repository within a registry.
146+
For example: reg1.io/myrepo/myapp:latest.
147+
items:
148+
type: string
149+
type: array
150+
type: object
151+
type: object
85152
controlPlaneEndpoint:
86153
description: ControlPlaneEndpoint represents the endpoint used to
87154
communicate with the control plane.

controlplane/rosa/api/v1beta2/rosacontrolplane_types.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,67 @@ type RosaControlPlaneSpec struct { //nolint: maligned
180180
// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
181181
// +optional
182182
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint"`
183+
184+
// ClusterRegistryConfig represents registry config used with the cluster.
185+
// +optional
186+
ClusterRegistryConfig *RegistryConfig `json:"clusterRegistryConfig,omitempty"`
187+
}
188+
189+
// RegistryConfig for ROSA-HCP cluster
190+
type RegistryConfig struct {
191+
// AdditionalTrustedCa containing the registry hostname as the key, and the PEM-encoded certificate as the value,
192+
// for each additional registry CA to trust.
193+
// +optional
194+
AdditionalTrustedCa map[string]string `json:"additionalTrustedCa,omitempty"`
195+
196+
// AllowedRegistriesForImport limits the container image registries that normal users may import
197+
// images from. Set this list to the registries that you trust to contain valid Docker
198+
// images and that you want applications to be able to import from.
199+
// +optional
200+
AllowedRegistriesForImport []RegistryLocation `json:"allowedRegistriesForImport,omitempty"`
201+
202+
// RegistrySources contains configuration that determines how the container runtime
203+
// should treat individual registries when accessing images. It does not contain configuration
204+
// for the internal cluster registry. AllowedRegistries, BlockedRegistries are mutually exclusive.
205+
// +optional
206+
RegistrySources *RegistrySources `json:"registrySources,omitempty"`
207+
}
208+
209+
// RegistryLocation contains a location of the registry specified by the registry domain name.
210+
type RegistryLocation struct {
211+
// domainName specifies a domain name for the registry. The domain name might include wildcards, like '*' or '??'.
212+
// In case the registry use non-standard (80 or 443) port, the port should be included in the domain name as well.
213+
// +optional
214+
DomainName string `json:"domainName,omitempty"`
215+
216+
// insecure indicates whether the registry is secure (https) or insecure (http), default is secured.
217+
// +kubebuilder:default=false
218+
// +optional
219+
Insecure bool `json:"insecure,omitempty"`
220+
}
221+
222+
// RegistrySources contains registries configuration.
223+
type RegistrySources struct {
224+
// AllowedRegistries: registries for which image pull and push actions are allowed.
225+
// To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
226+
// For example, *.example.com. You can specify an individual repository within a registry.
227+
// For example: reg1.io/myrepo/myapp:latest. All other registries are blocked.
228+
// +optional
229+
AllowedRegistries []string `json:"allowedRegistries,omitempty"`
230+
231+
// BlockedRegistries: registries for which image pull and push actions are denied.
232+
// To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
233+
// For example, *.example.com. You can specify an individual repository within a registry.
234+
// For example: reg1.io/myrepo/myapp:latest. All other registries are allowed.
235+
// +optional
236+
BlockedRegistries []string `json:"blockedRegistries,omitempty"`
237+
238+
// InsecureRegistries are registries which do not have a valid TLS certificate or only support HTTP connections.
239+
// To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name.
240+
// For example, *.example.com. You can specify an individual repository within a registry.
241+
// For example: reg1.io/myrepo/myapp:latest.
242+
// +optional
243+
InsecureRegistries []string `json:"insecureRegistries,omitempty"`
183244
}
184245

185246
// NetworkSpec for ROSA-HCP.

controlplane/rosa/api/v1beta2/zz_generated.deepcopy.go

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/rosa/controllers/rosacontrolplane_controller.go

Lines changed: 100 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ import (
2424
"fmt"
2525
"net"
2626
"net/url"
27+
"reflect"
2728
"strconv"
2829
"strings"
2930
"time"
3031

32+
stsv2 "github.com/aws/aws-sdk-go-v2/service/sts"
33+
sts "github.com/aws/aws-sdk-go/service/sts"
3134
"github.com/google/go-cmp/cmp"
3235
idputils "github.com/openshift-online/ocm-common/pkg/idp/utils"
3336
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
@@ -206,7 +209,7 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
206209
return ctrl.Result{}, fmt.Errorf("failed to create OCM client: %w", err)
207210
}
208211

209-
creator, err := rosaaws.CreatorForCallerIdentity(rosaScope.Identity)
212+
creator, err := rosaaws.CreatorForCallerIdentity(convertStsV2(rosaScope.Identity))
210213
if err != nil {
211214
return ctrl.Result{}, fmt.Errorf("failed to transform caller identity to creator: %w", err)
212215
}
@@ -334,7 +337,7 @@ func (r *ROSAControlPlaneReconciler) reconcileDelete(ctx context.Context, rosaSc
334337
return ctrl.Result{}, fmt.Errorf("failed to create OCM client: %w", err)
335338
}
336339

337-
creator, err := rosaaws.CreatorForCallerIdentity(rosaScope.Identity)
340+
creator, err := rosaaws.CreatorForCallerIdentity(convertStsV2(rosaScope.Identity))
338341
if err != nil {
339342
return ctrl.Result{}, fmt.Errorf("failed to transform caller identity to creator: %w", err)
340343
}
@@ -439,23 +442,75 @@ func (r *ROSAControlPlaneReconciler) reconcileClusterVersion(rosaScope *scope.RO
439442
}
440443

441444
func (r *ROSAControlPlaneReconciler) updateOCMCluster(rosaScope *scope.ROSAControlPlaneScope, ocmClient *ocm.Client, cluster *cmv1.Cluster, creator *rosaaws.Creator) error {
445+
ocmClusterSpec := ocm.Spec{}
446+
updated := false
442447
currentAuditLogRole := cluster.AWS().AuditLog().RoleArn()
443-
if currentAuditLogRole == rosaScope.ControlPlane.Spec.AuditLogRoleARN {
444-
return nil
448+
449+
if currentAuditLogRole != rosaScope.ControlPlane.Spec.AuditLogRoleARN {
450+
ocmClusterSpec.AuditLogRoleARN = ptr.To(rosaScope.ControlPlane.Spec.AuditLogRoleARN)
451+
updated = true
445452
}
446453

447-
ocmClusterSpec := ocm.Spec{
448-
AuditLogRoleARN: ptr.To(rosaScope.ControlPlane.Spec.AuditLogRoleARN),
454+
// Check for registry config changes
455+
regConfig := &rosacontrolplanev1.RegistryConfig{
456+
RegistrySources: &rosacontrolplanev1.RegistrySources{},
449457
}
458+
if rosaScope.ControlPlane.Spec.ClusterRegistryConfig != nil {
459+
regConfig.AdditionalTrustedCa = rosaScope.ControlPlane.Spec.ClusterRegistryConfig.AdditionalTrustedCa
460+
regConfig.AllowedRegistriesForImport = rosaScope.ControlPlane.Spec.ClusterRegistryConfig.AllowedRegistriesForImport
450461

451-
// if this fails, the provided role is likely invalid or it doesn't have the required permissions.
452-
if err := ocmClient.UpdateCluster(cluster.ID(), creator, ocmClusterSpec); err != nil {
453-
conditions.MarkFalse(rosaScope.ControlPlane,
454-
rosacontrolplanev1.ROSAControlPlaneValidCondition,
455-
rosacontrolplanev1.ROSAControlPlaneInvalidConfigurationReason,
456-
clusterv1.ConditionSeverityError,
457-
err.Error())
458-
return err
462+
if rosaScope.ControlPlane.Spec.ClusterRegistryConfig.RegistrySources != nil {
463+
regConfig.RegistrySources.AllowedRegistries = rosaScope.ControlPlane.Spec.ClusterRegistryConfig.RegistrySources.AllowedRegistries
464+
regConfig.RegistrySources.BlockedRegistries = rosaScope.ControlPlane.Spec.ClusterRegistryConfig.RegistrySources.BlockedRegistries
465+
regConfig.RegistrySources.InsecureRegistries = rosaScope.ControlPlane.Spec.ClusterRegistryConfig.RegistrySources.InsecureRegistries
466+
}
467+
}
468+
if !reflect.DeepEqual(regConfig.AdditionalTrustedCa, cluster.RegistryConfig().AdditionalTrustedCa()) {
469+
ocmClusterSpec.AdditionalTrustedCa = regConfig.AdditionalTrustedCa
470+
updated = true
471+
}
472+
if !reflect.DeepEqual(regConfig.RegistrySources.AllowedRegistries, cluster.RegistryConfig().RegistrySources().AllowedRegistries()) {
473+
ocmClusterSpec.AllowedRegistries = regConfig.RegistrySources.AllowedRegistries
474+
updated = true
475+
}
476+
if !reflect.DeepEqual(regConfig.RegistrySources.BlockedRegistries, cluster.RegistryConfig().RegistrySources().BlockedRegistries()) {
477+
ocmClusterSpec.BlockedRegistries = regConfig.RegistrySources.BlockedRegistries
478+
updated = true
479+
}
480+
if !reflect.DeepEqual(regConfig.RegistrySources.InsecureRegistries, cluster.RegistryConfig().RegistrySources().InsecureRegistries()) {
481+
ocmClusterSpec.InsecureRegistries = regConfig.RegistrySources.InsecureRegistries
482+
updated = true
483+
}
484+
485+
var newAllowedRegisters, oldAllowedRegisters []string
486+
if len(regConfig.AllowedRegistriesForImport) > 0 {
487+
for id := range regConfig.AllowedRegistriesForImport {
488+
newAllowedRegisters = append(newAllowedRegisters, regConfig.AllowedRegistriesForImport[id].DomainName+":"+
489+
strconv.FormatBool(regConfig.AllowedRegistriesForImport[id].Insecure))
490+
}
491+
}
492+
if len(cluster.RegistryConfig().AllowedRegistriesForImport()) > 0 {
493+
for id := range cluster.RegistryConfig().AllowedRegistriesForImport() {
494+
oldAllowedRegisters = append(oldAllowedRegisters, cluster.RegistryConfig().AllowedRegistriesForImport()[id].DomainName()+":"+
495+
strconv.FormatBool(cluster.RegistryConfig().AllowedRegistriesForImport()[id].Insecure()))
496+
}
497+
}
498+
if !reflect.DeepEqual(newAllowedRegisters, oldAllowedRegisters) {
499+
ocmClusterSpec.AllowedRegistriesForImport = strings.Join(newAllowedRegisters, ",")
500+
updated = true
501+
}
502+
503+
if updated {
504+
// Update the cluster.
505+
rosaScope.Info("Updating cluster")
506+
if err := ocmClient.UpdateCluster(cluster.ID(), creator, ocmClusterSpec); err != nil {
507+
conditions.MarkFalse(rosaScope.ControlPlane,
508+
rosacontrolplanev1.ROSAControlPlaneValidCondition,
509+
rosacontrolplanev1.ROSAControlPlaneInvalidConfigurationReason,
510+
clusterv1.ConditionSeverityError,
511+
err.Error())
512+
return err
513+
}
459514
}
460515

461516
return nil
@@ -888,6 +943,28 @@ func buildOCMClusterSpec(controlPlaneSpec rosacontrolplanev1.RosaControlPlaneSpe
888943
}
889944
}
890945

946+
// Set the cluster registry config.
947+
if controlPlaneSpec.ClusterRegistryConfig != nil {
948+
if len(controlPlaneSpec.ClusterRegistryConfig.AdditionalTrustedCa) > 0 {
949+
ocmClusterSpec.AdditionalTrustedCa = controlPlaneSpec.ClusterRegistryConfig.AdditionalTrustedCa
950+
}
951+
952+
if len(controlPlaneSpec.ClusterRegistryConfig.AllowedRegistriesForImport) > 0 {
953+
registers := make([]string, 0)
954+
for id := range controlPlaneSpec.ClusterRegistryConfig.AllowedRegistriesForImport {
955+
registers = append(registers, controlPlaneSpec.ClusterRegistryConfig.AllowedRegistriesForImport[id].DomainName+":"+
956+
strconv.FormatBool(controlPlaneSpec.ClusterRegistryConfig.AllowedRegistriesForImport[id].Insecure))
957+
}
958+
ocmClusterSpec.AllowedRegistriesForImport = strings.Join(registers, ",")
959+
}
960+
961+
if controlPlaneSpec.ClusterRegistryConfig.RegistrySources != nil {
962+
ocmClusterSpec.BlockedRegistries = controlPlaneSpec.ClusterRegistryConfig.RegistrySources.BlockedRegistries
963+
ocmClusterSpec.AllowedRegistries = controlPlaneSpec.ClusterRegistryConfig.RegistrySources.AllowedRegistries
964+
ocmClusterSpec.InsecureRegistries = controlPlaneSpec.ClusterRegistryConfig.RegistrySources.InsecureRegistries
965+
}
966+
}
967+
891968
return ocmClusterSpec, nil
892969
}
893970

@@ -996,3 +1073,12 @@ func buildAPIEndpoint(cluster *cmv1.Cluster) (*clusterv1.APIEndpoint, error) {
9961073
Port: int32(port), // #nosec G109
9971074
}, nil
9981075
}
1076+
1077+
// TODO: Remove this and update the aws-sdk lib to v2.
1078+
func convertStsV2(identity *sts.GetCallerIdentityOutput) *stsv2.GetCallerIdentityOutput {
1079+
return &stsv2.GetCallerIdentityOutput{
1080+
Account: identity.Account,
1081+
Arn: identity.Arn,
1082+
UserId: identity.UserId,
1083+
}
1084+
}

0 commit comments

Comments
 (0)