Skip to content

Commit

Permalink
feat: add AWS IAM instance profile patch (#216)
Browse files Browse the repository at this point in the history
New AWS patch for control-plane and worker machines

Tested creating a cluster:
```
- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
  kind: AWSMachineTemplate
  metadata:
    name: aws-quick-start-control-plane-pgg9l
    namespace: default
  spec:
    template:
      spec:
        iamInstanceProfile: custom-control-plane.cluster-api-provider-aws.sigs.k8s.io
        instanceType: m5.xlarge
- apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
  kind: AWSMachineTemplate
    name: aws-quick-start-md-0-infra-jkwdz
    namespace: default
  spec:
    template:
      spec:
        iamInstanceProfile: custom-nodes.cluster-api-provider-aws.sigs.k8s.io
        instanceType: m5.2xlarge
```

---------

Signed-off-by: Dimitri Koshkin <dimitri.koshkin@gmail.com>
Co-authored-by: Shalin Patel <spatel@d2iq.com>
  • Loading branch information
dkoshkin and Shalin Patel committed Oct 9, 2023
1 parent fa8e744 commit 572735b
Show file tree
Hide file tree
Showing 17 changed files with 593 additions and 71 deletions.
20 changes: 18 additions & 2 deletions api/v1alpha1/aws_node_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,30 @@ package v1alpha1

import clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"

type AWSNodeSpec struct{}
type AWSNodeSpec struct {
// +optional
IAMInstanceProfile *IAMInstanceProfile `json:"iamInstanceProfile,omitempty"`
}

func (AWSNodeSpec) VariableSchema() clusterv1.VariableSchema {
return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Description: "AWS Node configuration",
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{},
Properties: map[string]clusterv1.JSONSchemaProps{
"iamInstanceProfile": IAMInstanceProfile("").VariableSchema().OpenAPIV3Schema,
},
},
}
}

type IAMInstanceProfile string

func (IAMInstanceProfile) VariableSchema() clusterv1.VariableSchema {
return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Type: "string",
Description: "The IAM instance profile to use for the cluster Machines",
},
}
}
7 changes: 6 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions common/pkg/testutils/capitest/request/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import (
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"

capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
capdv1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api/test/infrastructure/docker/api/v1beta1"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest/serializer"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

const (
Expand Down Expand Up @@ -140,3 +142,95 @@ func NewAWSClusterTemplateRequestItem(
uid,
)
}

func NewCPDockerMachineTemplateRequestItem(
uid types.UID,
) runtimehooksv1.GeneratePatchesRequestItem {
return NewRequestItem(
&capdv1.DockerMachineTemplate{
TypeMeta: metav1.TypeMeta{
APIVersion: capdv1.GroupVersion.String(),
Kind: "DockerMachineTemplate",
},
ObjectMeta: metav1.ObjectMeta{
Name: "docker-machine-template",
Namespace: "docker-cluster",
},
},
&runtimehooksv1.HolderReference{
APIVersion: controlplanev1.GroupVersion.String(),
Kind: "KubeadmControlPlane",
FieldPath: "spec.machineTemplate.infrastructureRef",
},
uid,
)
}

func NewWorkerDockerMachineTemplateRequestItem(
uid types.UID,
) runtimehooksv1.GeneratePatchesRequestItem {
return NewRequestItem(
&capdv1.DockerMachineTemplate{
TypeMeta: metav1.TypeMeta{
APIVersion: capdv1.GroupVersion.String(),
Kind: "DockerMachineTemplate",
},
ObjectMeta: metav1.ObjectMeta{
Name: "docker-machine-template",
Namespace: "docker-cluster",
},
},
&runtimehooksv1.HolderReference{
APIVersion: clusterv1.GroupVersion.String(),
Kind: "MachineDeployment",
FieldPath: "spec.template.spec.infrastructureRef",
},
uid,
)
}

func NewCPAWSMachineTemplateRequestItem(
uid types.UID,
) runtimehooksv1.GeneratePatchesRequestItem {
return NewRequestItem(
&capav1.AWSMachineTemplate{
TypeMeta: metav1.TypeMeta{
APIVersion: capav1.GroupVersion.String(),
Kind: "AWSMachineTemplate",
},
ObjectMeta: metav1.ObjectMeta{
Name: "aws-machine-template",
Namespace: "aws-cluster",
},
},
&runtimehooksv1.HolderReference{
APIVersion: controlplanev1.GroupVersion.String(),
Kind: "KubeadmControlPlane",
FieldPath: "spec.machineTemplate.infrastructureRef",
},
uid,
)
}

func NewWorkerAWSMachineTemplateRequestItem(
uid types.UID,
) runtimehooksv1.GeneratePatchesRequestItem {
return NewRequestItem(
&capav1.AWSMachineTemplate{
TypeMeta: metav1.TypeMeta{
APIVersion: capav1.GroupVersion.String(),
Kind: "AWSMachineTemplate",
},
ObjectMeta: metav1.ObjectMeta{
Name: "aws-machine-template",
Namespace: "aws-cluster",
},
},
&runtimehooksv1.HolderReference{
APIVersion: clusterv1.GroupVersion.String(),
Kind: "MachineDeployment",
FieldPath: "spec.template.spec.infrastructureRef",
},
uid,
)
}
52 changes: 52 additions & 0 deletions docs/content/customization/aws/iam-instance-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
+++
title = "IAM Instance Profile"
+++

The IAM instance profile customization allows the user to specify the profile to use for control-plane
and worker Machines.

This customization will be available when the
[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`.

## Example

To specify the IAM instance profile, use the following configuration:

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
controlPlane:
aws:
iamInstanceProfile: custom-control-plane.cluster-api-provider-aws.sigs.k8s.io
- name: workerConfig
value:
aws:
iamInstanceProfile: custom-nodes.cluster-api-provider-aws.sigs.k8s.io
```

Applying this configuration will result in the following value being set:

- control-plane `AWSMachineTemplate`:

- ```yaml
spec:
template:
spec:
iamInstanceProfile: custom-control-plane.cluster-api-provider-aws.sigs.k8s.io
```

- worker `AWSMachineTemplate`:

- ```yaml
spec:
template:
spec:
iamInstanceProfile: custom-nodes.cluster-api-provider-aws.sigs.k8s.io
```
14 changes: 14 additions & 0 deletions pkg/handlers/aws/clusterconfig/variables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,19 @@ func TestVariableValidation(t *testing.T) {
},
},
},
capitest.VariableTestDef{
Name: "specified IAM instance profile",
Vals: v1alpha1.ClusterConfigSpec{
ControlPlane: &v1alpha1.NodeConfigSpec{
AWS: &v1alpha1.AWSNodeSpec{
IAMInstanceProfile: ptr.To(
v1alpha1.IAMInstanceProfile(
"control-plane.cluster-api-provider-aws.sigs.k8s.io",
),
),
},
},
},
},
)
}
129 changes: 129 additions & 0 deletions pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2023 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package iaminstanceprofile

import (
"context"
_ "embed"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/apis"
commonhandlers "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables"
capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers"
awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig"
)

const (
// ControlPlaneHandlerNamePatch is the name of the inject handler.
ControlPlaneHandlerNamePatch = "AWSIAMInstanceProfileControlPlanePatch"
)

type awsIAMInstanceProfileControlPlanePatchHandler struct {
variableName string
variableFieldPath []string
}

var (
_ commonhandlers.Named = &awsIAMInstanceProfileControlPlanePatchHandler{}
_ mutation.GeneratePatches = &awsIAMInstanceProfileControlPlanePatchHandler{}
_ mutation.MetaMutator = &awsIAMInstanceProfileControlPlanePatchHandler{}
)

func NewControlPlaneMetaPatch() *awsIAMInstanceProfileControlPlanePatchHandler {
return newAWSIAMInstanceProfileControlPlanePatchHandler(
clusterconfig.MetaVariableName,
clusterconfig.MetaControlPlaneConfigName,
awsclusterconfig.AWSVariableName,
VariableName,
)
}

func newAWSIAMInstanceProfileControlPlanePatchHandler(
variableName string,
variableFieldPath ...string,
) *awsIAMInstanceProfileControlPlanePatchHandler {
return &awsIAMInstanceProfileControlPlanePatchHandler{
variableName: variableName,
variableFieldPath: variableFieldPath,
}
}

func (h *awsIAMInstanceProfileControlPlanePatchHandler) Name() string {
return ControlPlaneHandlerNamePatch
}

func (h *awsIAMInstanceProfileControlPlanePatchHandler) Mutate(
ctx context.Context,
obj runtime.Object,
vars map[string]apiextensionsv1.JSON,
holderRef runtimehooksv1.HolderReference,
_ client.ObjectKey,
) error {
log := ctrl.LoggerFrom(ctx).WithValues(
"holderRef", holderRef,
)

iamInstanceProfileVar, found, err := variables.Get[v1alpha1.IAMInstanceProfile](
vars,
h.variableName,
h.variableFieldPath...,
)
if err != nil {
return err
}
if !found {
log.V(5).Info("AWS IAM instance profile variable for control-plane not defined")
return nil
}

log = log.WithValues(
"variableName",
h.variableName,
"variableFieldPath",
h.variableFieldPath,
"variableValue",
iamInstanceProfileVar,
)

return patches.Generate(
obj,
vars,
&holderRef,
selectors.InfrastructureControlPlaneMachines(
"v1beta2",
"AWSMachineTemplate",
),
log,
func(obj *capav1.AWSMachineTemplate) error {
log.WithValues(
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
"patchedObjectName", client.ObjectKeyFromObject(obj),
).Info("setting IAM instance profile in control plane AWSMachineTemplate spec")

obj.Spec.Template.Spec.IAMInstanceProfile = string(iamInstanceProfileVar)

return nil
},
)
}

func (h *awsIAMInstanceProfileControlPlanePatchHandler) GeneratePatches(
ctx context.Context,
req *runtimehooksv1.GeneratePatchesRequest,
resp *runtimehooksv1.GeneratePatchesResponse,
) {
handlers.GeneratePatches(ctx, req, resp, apis.CAPADecoder(), h.Mutate)
}
Loading

0 comments on commit 572735b

Please sign in to comment.