Skip to content

Commit 3451e3f

Browse files
committed
multi tenancy support for azure managed control plane
1 parent 6145379 commit 3451e3f

9 files changed

+132
-16
lines changed

azure/scope/identity.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/Azure/go-autorest/autorest/adal"
2626
"github.com/pkg/errors"
2727
infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha4"
28+
infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha4"
2829
"sigs.k8s.io/cluster-api-provider-azure/util/identity"
2930
"sigs.k8s.io/cluster-api-provider-azure/util/system"
3031
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha4"
@@ -54,7 +55,14 @@ type AzureClusterCredentialsProvider struct {
5455
AzureCluster *infrav1.AzureCluster
5556
}
5657

58+
// ManagedControlPlaneCredentialsProvider wraps AzureCredentialsProvider with AzureManagedControlPlane.
59+
type ManagedControlPlaneCredentialsProvider struct {
60+
AzureCredentialsProvider
61+
AzureManagedControlPlane *infrav1exp.AzureManagedControlPlane
62+
}
63+
5764
var _ CredentialsProvider = (*AzureClusterCredentialsProvider)(nil)
65+
var _ CredentialsProvider = (*ManagedControlPlaneCredentialsProvider)(nil)
5866

5967
// NewAzureClusterCredentialsProvider creates a new AzureClusterCredentialsProvider from the supplied inputs.
6068
func NewAzureClusterCredentialsProvider(ctx context.Context, kubeClient client.Client, azureCluster *infrav1.AzureCluster) (*AzureClusterCredentialsProvider, error) {
@@ -92,6 +100,42 @@ func (p *AzureClusterCredentialsProvider) GetAuthorizer(ctx context.Context, res
92100
return p.AzureCredentialsProvider.GetAuthorizer(ctx, resourceManagerEndpoint, p.AzureCluster.ObjectMeta)
93101
}
94102

103+
// NewManagedControlPlaneCredentialsProvider creates a new ManagedControlPlaneCredentialsProvider from the supplied inputs.
104+
func NewManagedControlPlaneCredentialsProvider(ctx context.Context, kubeClient client.Client, managedControlPlane *infrav1exp.AzureManagedControlPlane) (*ManagedControlPlaneCredentialsProvider, error) {
105+
if managedControlPlane.Spec.IdentityRef == nil {
106+
return nil, errors.New("failed to generate new ManagedControlPlaneCredentialsProvider from empty identityName")
107+
}
108+
109+
ref := managedControlPlane.Spec.IdentityRef
110+
// if the namespace isn't specified then assume it's in the same namespace as the AzureManagedControlPlane
111+
namespace := ref.Namespace
112+
if namespace == "" {
113+
namespace = managedControlPlane.Namespace
114+
}
115+
identity := &infrav1.AzureClusterIdentity{}
116+
key := client.ObjectKey{Name: ref.Name, Namespace: namespace}
117+
if err := kubeClient.Get(ctx, key, identity); err != nil {
118+
return nil, errors.Errorf("failed to retrieve AzureClusterIdentity external object %q/%q: %v", key.Namespace, key.Name, err)
119+
}
120+
121+
if identity.Spec.Type != infrav1.ServicePrincipal {
122+
return nil, errors.New("AzureClusterIdentity is not of type Service Principal")
123+
}
124+
125+
return &ManagedControlPlaneCredentialsProvider{
126+
AzureCredentialsProvider{
127+
Client: kubeClient,
128+
Identity: identity,
129+
},
130+
managedControlPlane,
131+
}, nil
132+
}
133+
134+
// GetAuthorizer returns an Azure authorizer based on the provided azure identity. It delegates to AzureCredentialsProvider with AzureManagedControlPlane metadata.
135+
func (p *ManagedControlPlaneCredentialsProvider) GetAuthorizer(ctx context.Context, resourceManagerEndpoint string) (autorest.Authorizer, error) {
136+
return p.AzureCredentialsProvider.GetAuthorizer(ctx, resourceManagerEndpoint, p.AzureManagedControlPlane.ObjectMeta)
137+
}
138+
95139
// GetAuthorizer returns an Azure authorizer based on the provided azure identity and cluster metadata.
96140
func (p *AzureCredentialsProvider) GetAuthorizer(ctx context.Context, resourceManagerEndpoint string, clusterMeta metav1.ObjectMeta) (autorest.Authorizer, error) {
97141
azureIdentityType, err := getAzureIdentityType(p.Identity)

azure/scope/managedcontrolplane.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type ManagedControlPlaneScopeParams struct {
4848

4949
// NewManagedControlPlaneScope creates a new Scope from the supplied parameters.
5050
// This is meant to be called for each reconcile iteration.
51-
func NewManagedControlPlaneScope(params ManagedControlPlaneScopeParams) (*ManagedControlPlaneScope, error) {
51+
func NewManagedControlPlaneScope(ctx context.Context, params ManagedControlPlaneScopeParams) (*ManagedControlPlaneScope, error) {
5252
if params.Cluster == nil {
5353
return nil, errors.New("failed to generate new scope from nil Cluster")
5454
}
@@ -61,8 +61,19 @@ func NewManagedControlPlaneScope(params ManagedControlPlaneScopeParams) (*Manage
6161
params.Logger = klogr.New()
6262
}
6363

64-
if err := params.AzureClients.setCredentials(params.ControlPlane.Spec.SubscriptionID, ""); err != nil {
65-
return nil, errors.Wrap(err, "failed to create Azure session")
64+
if params.ControlPlane.Spec.IdentityRef == nil {
65+
if err := params.AzureClients.setCredentials(params.ControlPlane.Spec.SubscriptionID, ""); err != nil {
66+
return nil, errors.Wrap(err, "failed to create Azure session")
67+
}
68+
} else {
69+
credentialsProvider, err := NewManagedControlPlaneCredentialsProvider(ctx, params.Client, params.ControlPlane)
70+
if err != nil {
71+
return nil, errors.Wrap(err, "failed to init credentials provider")
72+
}
73+
74+
if err := params.AzureClients.setCredentialsWithProvider(ctx, params.ControlPlane.Spec.SubscriptionID, "", credentialsProvider); err != nil {
75+
return nil, errors.Wrap(err, "failed to configure azure settings and credentials for Identity")
76+
}
6677
}
6778

6879
helper, err := patch.NewHelper(params.PatchTarget, params.Client)

config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedcontrolplanes.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,31 @@ spec:
192192
dnsServiceIP:
193193
description: DNSServiceIP is an IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr.
194194
type: string
195+
identityRef:
196+
description: IdentityRef is a reference to a AzureClusterIdentity to be used when reconciling this cluster
197+
properties:
198+
apiVersion:
199+
description: API version of the referent.
200+
type: string
201+
fieldPath:
202+
description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.'
203+
type: string
204+
kind:
205+
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
206+
type: string
207+
name:
208+
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
209+
type: string
210+
namespace:
211+
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
212+
type: string
213+
resourceVersion:
214+
description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
215+
type: string
216+
uid:
217+
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
218+
type: string
219+
type: object
195220
loadBalancerSKU:
196221
description: LoadBalancerSKU is the SKU of the loadBalancer to be provisioned.
197222
enum:

exp/api/v1alpha3/azuremanagedcontrolplane_conversion.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1alpha3
1818

1919
import (
20+
apiconversion "k8s.io/apimachinery/pkg/conversion"
2021
expv1alpha4 "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha4"
2122
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
2223
"sigs.k8s.io/controller-runtime/pkg/conversion"
@@ -36,6 +37,8 @@ func (src *AzureManagedControlPlane) ConvertTo(dstRaw conversion.Hub) error { //
3637
return err
3738
}
3839

40+
dst.Spec.IdentityRef = restored.Spec.IdentityRef
41+
3942
return nil
4043
}
4144

@@ -54,3 +57,8 @@ func (dst *AzureManagedControlPlane) ConvertFrom(srcRaw conversion.Hub) error {
5457

5558
return nil
5659
}
60+
61+
// Convert_v1alpha4_AzureManagedControlPlaneSpec_To_v1alpha3_AzureManagedControlPlaneSpec is an autogenerated conversion function.
62+
func Convert_v1alpha4_AzureManagedControlPlaneSpec_To_v1alpha3_AzureManagedControlPlaneSpec(in *expv1alpha4.AzureManagedControlPlaneSpec, out *AzureManagedControlPlaneSpec, s apiconversion.Scope) error {
63+
return autoConvert_v1alpha4_AzureManagedControlPlaneSpec_To_v1alpha3_AzureManagedControlPlaneSpec(in, out, s)
64+
}

exp/api/v1alpha3/zz_generated.conversion.go

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

exp/api/v1alpha4/azuremanagedcontrolplane_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ type AzureManagedControlPlaneSpec struct {
8282
// +kubebuilder:validation:Enum=Basic;Standard
8383
// +optional
8484
LoadBalancerSKU *string `json:"loadBalancerSKU,omitempty"`
85+
86+
// IdentityRef is a reference to a AzureClusterIdentity to be used when reconciling this cluster
87+
// +optional
88+
IdentityRef *corev1.ObjectReference `json:"identityRef,omitempty"`
8589
}
8690

8791
// ManagedControlPlaneVirtualNetwork describes a virtual network required to provision AKS clusters.

exp/api/v1alpha4/zz_generated.deepcopy.go

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

exp/controllers/azuremanagedcontrolplane_controller.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"context"
2121
"time"
2222

23+
"sigs.k8s.io/cluster-api/util/patch"
24+
2325
"github.com/go-logr/logr"
2426
"github.com/pkg/errors"
2527
"go.opentelemetry.io/otel/attribute"
@@ -150,7 +152,7 @@ func (r *AzureManagedControlPlaneReconciler) Reconcile(ctx context.Context, req
150152
// needs to happen before trying to fetch the default pool to avoid circular deletion dependencies
151153
if !azureControlPlane.DeletionTimestamp.IsZero() {
152154
// Create the scope.
153-
mcpScope, err := scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
155+
mcpScope, err := scope.NewManagedControlPlaneScope(ctx, scope.ManagedControlPlaneScopeParams{
154156
Client: r.Client,
155157
Logger: log,
156158
Cluster: cluster,
@@ -192,8 +194,29 @@ func (r *AzureManagedControlPlaneReconciler) Reconcile(ctx context.Context, req
192194

193195
log = log.WithValues("machinePool", ownerPool.Name)
194196

197+
// check if the control plane's namespace is allowed for this identity and update owner references for the identity.
198+
if azureControlPlane.Spec.IdentityRef != nil {
199+
identity, err := infracontroller.GetClusterIdentityFromRef(ctx, r.Client, azureControlPlane.Namespace, azureControlPlane.Spec.IdentityRef)
200+
if err != nil {
201+
return reconcile.Result{}, err
202+
}
203+
if !scope.IsClusterNamespaceAllowed(ctx, r.Client, identity.Spec.AllowedNamespaces, azureControlPlane.Namespace) {
204+
return reconcile.Result{}, errors.New("AzureClusterIdentity list of allowed namespaces doesn't include current azure managed control plane namespace")
205+
}
206+
if identity.Namespace == azureControlPlane.Namespace {
207+
patchHelper, err := patch.NewHelper(identity, r.Client)
208+
if err != nil {
209+
return reconcile.Result{}, errors.Wrap(err, "failed to init patch helper")
210+
}
211+
identity.ObjectMeta.OwnerReferences = azureControlPlane.GetOwnerReferences()
212+
if err := patchHelper.Patch(ctx, identity); err != nil {
213+
return reconcile.Result{}, err
214+
}
215+
}
216+
}
217+
195218
// Create the scope.
196-
mcpScope, err := scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
219+
mcpScope, err := scope.NewManagedControlPlaneScope(ctx, scope.ManagedControlPlaneScopeParams{
197220
Client: r.Client,
198221
Logger: log,
199222
Cluster: cluster,

exp/controllers/azuremanagedmachinepool_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func (r *AzureManagedMachinePoolReconciler) Reconcile(ctx context.Context, req c
191191
}
192192

193193
// Create the scope.
194-
mcpScope, err := scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
194+
mcpScope, err := scope.NewManagedControlPlaneScope(ctx, scope.ManagedControlPlaneScopeParams{
195195
Client: r.Client,
196196
Logger: log,
197197
ControlPlane: controlPlane,

0 commit comments

Comments
 (0)