Skip to content

Commit 05879a3

Browse files
authored
Add capability to create LT with customized parameters, fix make verify for substrate (#286)
* Split spec & status to support external calls * Expose CreateAndAuthorizeSecurityGroupIngress * Revert changes to split substrate spec & status, re-use operator asg & lt * clean up * Add ClusterCA into operator dataplane spec * fix security group id in operator launch template * review comments
1 parent 2136dc1 commit 05879a3

File tree

11 files changed

+314
-44
lines changed

11 files changed

+314
-44
lines changed

operator/charts/kit-operator/crds/control-plane-crd.yaml

Lines changed: 152 additions & 7 deletions
Large diffs are not rendered by default.

operator/charts/kit-operator/crds/data-plane-crd.yaml

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
33
kind: CustomResourceDefinition
44
metadata:
55
annotations:
6-
controller-gen.kubebuilder.io/version: v0.8.0
6+
controller-gen.kubebuilder.io/version: v0.9.2
77
creationTimestamp: null
88
name: dataplanes.kit.k8s.sh
99
spec:
@@ -40,10 +40,29 @@ spec:
4040
description: AllocationStrategy helps user define the strategy to
4141
provision worker nodes in EC2, defaults to "lowest-price"
4242
type: string
43+
amiID:
44+
description: AmiID helps user create the launch template for work
45+
nodes If not provided, it's obtained by getting the recommended
46+
image id for the current k8s version
47+
type: string
48+
apiServerEndpoint:
49+
description: APIServerEndpoint helps user create the launch template
50+
for work nodes If not provided, it's obtained from master instance
51+
type: string
52+
clusterCA:
53+
description: ClusterCA helps user create the launch template for work
54+
nodes If not provided, get it from the current k8s cluster
55+
format: byte
56+
type: string
4357
clusterName:
4458
description: ClusterName is used to connect the worker nodes to a
4559
control plane clusterName.
4660
type: string
61+
instanceProfileName:
62+
description: InstanceProfileName if provided is assigned to the kube
63+
nodes created If not provided, use the name for the current kit
64+
cluster
65+
type: string
4766
instanceTypes:
4867
description: InstanceTypes is an optional field thats lets user specify
4968
the instance types for worker nodes, defaults to instance types
@@ -55,6 +74,16 @@ spec:
5574
description: NodeCount is the desired number of worker nodes for this
5675
dataplane.
5776
type: integer
77+
securityGroupSelector:
78+
additionalProperties:
79+
type: string
80+
description: SecurityGroupSelector lets user define label key and
81+
values for kit to select the security group for worker nodes. It
82+
can contain key:value to select security group with particular label,
83+
or a specific key:"*" to select all security group with a specific
84+
key. If no selector is provided, security group is discovered with
85+
control plane nodes
86+
type: object
5887
subnetSelector:
5988
additionalProperties:
6089
type: string
@@ -110,9 +139,3 @@ spec:
110139
storage: true
111140
subresources:
112141
status: {}
113-
status:
114-
acceptedNames:
115-
kind: ""
116-
plural: ""
117-
conditions: []
118-
storedVersions: []

operator/pkg/apis/dataplane/v1alpha1/dataplane.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,27 @@ type DataPlaneSpec struct {
5858
// defaults to "lowest-price"
5959
// +optional
6060
AllocationStrategy string `json:"allocationStrategy,omitempty"`
61+
62+
// SecurityGroupSelector lets user define label key and values for kit to select the security group
63+
// for worker nodes. It can contain key:value to select security group with particular label,
64+
// or a specific key:"*" to select all security group with a specific key.
65+
// If no selector is provided, security group is discovered with control plane nodes
66+
// +optional
67+
SecurityGroupSelector map[string]string `json:"securityGroupSelector,omitempty"`
68+
// APIServerEndpoint helps user create the launch template for work nodes
69+
// If not provided, it's obtained from master instance
70+
// +optional
71+
APIServerEndpoint string `json:"apiServerEndpoint,omitempty"`
72+
// AmiID helps user create the launch template for work nodes
73+
// If not provided, it's obtained by getting the recommended image id for the current k8s version
74+
// +optional
75+
AmiID string `json:"amiID,omitempty"`
76+
// InstanceProfileName if provided is assigned to the kube nodes created
77+
// If not provided, use the name for the current kit cluster
78+
// +optional
79+
InstanceProfileName string `json:"instanceProfileName,omitempty"`
80+
// ClusterCA helps user create the launch template for work nodes
81+
// If not provided, get it from the current k8s cluster
82+
// +optional
83+
ClusterCA []byte `json:"clusterCA,omitempty"`
6184
}

operator/pkg/apis/dataplane/v1alpha1/zz_generated.deepcopy.go

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

operator/pkg/awsprovider/launchtemplate/reconciler.go

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,49 @@ func (c *Controller) deleteLaunchTemplate(ctx context.Context, templateName stri
8989
}
9090

9191
func (c *Controller) createLaunchTemplate(ctx context.Context, dataplane *v1alpha1.DataPlane) error {
92-
// Currently, we get the same security group assigned to control plane instances
93-
// At some point, we will be creating dataplane specific security groups
94-
securityGroupID, err := securitygroup.New(c.ec2api, c.kubeclient).For(ctx, dataplane.Spec.ClusterName)
95-
if err != nil {
96-
return fmt.Errorf("getting security group for control plane nodes, %w", err)
92+
var (
93+
securityGroupID string
94+
err error
95+
)
96+
if len(dataplane.Spec.SecurityGroupSelector) != 0 {
97+
securityGroupID, err = c.securityGroupForSelector(ctx, dataplane.Spec.SecurityGroupSelector)
98+
if err != nil {
99+
return fmt.Errorf("getting security groups by selector, %w", err)
100+
}
101+
} else {
102+
// Currently, we get the same security group assigned to control plane instances
103+
// At some point, we will be creating dataplane specific security groups
104+
securityGroupID, err = securitygroup.New(c.ec2api, c.kubeclient).For(ctx, dataplane.Spec.ClusterName)
105+
if err != nil {
106+
return fmt.Errorf("getting security group for control plane nodes, %w", err)
107+
}
97108
}
98-
clusterEndpoint, err := master.GetClusterEndpoint(ctx, c.kubeclient, types.NamespacedName{Namespace: dataplane.Namespace, Name: dataplane.Spec.ClusterName})
99-
if err != nil {
100-
return fmt.Errorf("getting cluster endpoint, %w", err)
109+
apiServerEndpoint := dataplane.Spec.APIServerEndpoint
110+
if apiServerEndpoint == "" {
111+
apiServerEndpoint, err = master.GetClusterEndpoint(ctx, c.kubeclient, types.NamespacedName{Namespace: dataplane.Namespace, Name: dataplane.Spec.ClusterName})
112+
if err != nil {
113+
return fmt.Errorf("getting cluster endpoint, %w", err)
114+
}
101115
}
102-
caSecret, err := keypairs.Reconciler(c.kubeclient).GetSecretFromServer(ctx,
103-
object.NamespacedName(master.RootCASecretNameFor(dataplane.Spec.ClusterName), dataplane.Namespace))
104-
if err != nil {
105-
return fmt.Errorf("getting control plane ca certificate, %w", err)
116+
amiID := dataplane.Spec.AmiID
117+
if amiID == "" {
118+
amiID, err = c.amiID(ctx, dataplane)
119+
if err != nil {
120+
return fmt.Errorf("getting ami id for worker nodes, %w", err)
121+
}
106122
}
107-
_, clusterCA := secrets.Parse(caSecret)
108-
amiID, err := c.amiID(ctx, dataplane)
109-
if err != nil {
110-
return fmt.Errorf("getting ami id for worker nodes, %w", err)
123+
instanceProfile := dataplane.Spec.InstanceProfileName
124+
if len(instanceProfile) == 0 {
125+
instanceProfile = iam.KitNodeInstanceProfileNameFor(dataplane.Spec.ClusterName)
126+
}
127+
clusterCA := dataplane.Spec.ClusterCA
128+
if len(clusterCA) == 0 {
129+
caSecret, err := keypairs.Reconciler(c.kubeclient).GetSecretFromServer(ctx,
130+
object.NamespacedName(master.RootCASecretNameFor(dataplane.Spec.ClusterName), dataplane.Namespace))
131+
if err != nil {
132+
return fmt.Errorf("getting control plane ca certificate, %w", err)
133+
}
134+
_, clusterCA = secrets.Parse(caSecret)
111135
}
112136
input := &ec2.CreateLaunchTemplateInput{
113137
LaunchTemplateData: &ec2.RequestLaunchTemplateData{
@@ -123,22 +147,48 @@ func (c *Controller) createLaunchTemplate(ctx context.Context, dataplane *v1alph
123147
InstanceType: ptr.String("t2.xlarge"), // TODO get this from dataplane spec
124148
ImageId: ptr.String(amiID),
125149
IamInstanceProfile: &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{
126-
Name: aws.String(iam.KitNodeInstanceProfileNameFor(dataplane.Spec.ClusterName)),
150+
Name: aws.String(instanceProfile),
127151
},
128152
Monitoring: &ec2.LaunchTemplatesMonitoringRequest{Enabled: ptr.Bool(true)},
129153
SecurityGroupIds: []*string{ptr.String(securityGroupID)},
130154
UserData: ptr.String(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(userData,
131-
dataplane.Spec.ClusterName, dnsClusterIP, base64.StdEncoding.EncodeToString(clusterCA), clusterEndpoint)))),
155+
dataplane.Spec.ClusterName, dnsClusterIP, base64.StdEncoding.EncodeToString(clusterCA), apiServerEndpoint)))),
132156
},
133157
LaunchTemplateName: ptr.String(TemplateName(dataplane.Spec.ClusterName)),
134158
TagSpecifications: generateEC2Tags("launch-template", dataplane.Spec.ClusterName),
135159
}
136-
if _, err := c.ec2api.CreateLaunchTemplate(input); err != nil {
160+
if _, err := c.ec2api.CreateLaunchTemplateWithContext(ctx, input); err != nil {
137161
return fmt.Errorf("creating launch template, %w", err)
138162
}
139163
return nil
140164
}
141165

166+
func (c *Controller) securityGroupForSelector(ctx context.Context, selector map[string]string) (string, error) {
167+
filters := []*ec2.Filter{}
168+
// Filter by selector
169+
for key, value := range selector {
170+
if value == "*" {
171+
filters = append(filters, &ec2.Filter{
172+
Name: aws.String("tag-key"),
173+
Values: []*string{aws.String(key)},
174+
})
175+
} else {
176+
filters = append(filters, &ec2.Filter{
177+
Name: aws.String(fmt.Sprintf("tag:%s", key)),
178+
Values: []*string{aws.String(value)},
179+
})
180+
}
181+
}
182+
output, err := c.ec2api.DescribeSecurityGroupsWithContext(ctx, &ec2.DescribeSecurityGroupsInput{Filters: filters})
183+
if err != nil {
184+
return "", fmt.Errorf("describing security groups %+v, %w", filters, err)
185+
}
186+
if len(output.SecurityGroups) != 1 {
187+
return "", fmt.Errorf("wrong number of security groups by selector, %d", len(output.SecurityGroups))
188+
}
189+
return aws.StringValue(output.SecurityGroups[0].GroupId), nil
190+
}
191+
142192
func (c *Controller) amiID(ctx context.Context, dataplane *v1alpha1.DataPlane) (string, error) {
143193
kubeVersion, err := c.desiredKubernetesVersion(ctx, dataplane)
144194
if err != nil {

substrate/cmd/kitctl/bootstrap.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package main
1717
import (
1818
"os"
1919
"time"
20+
2021
"github.com/aws/aws-sdk-go/aws"
2122
"github.com/awslabs/kubernetes-iteration-toolkit/substrate/pkg/apis/v1alpha1"
2223
"github.com/awslabs/kubernetes-iteration-toolkit/substrate/pkg/controller/substrate"

substrate/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/awslabs/kubernetes-iteration-toolkit/substrate
33
go 1.17
44

55
require (
6-
github.com/aws/aws-sdk-go v1.43.8
6+
github.com/aws/aws-sdk-go v1.43.16
77
github.com/awslabs/kubernetes-iteration-toolkit/operator v0.0.0-20220818192233-b51328aa42d4
88
github.com/imdario/mergo v0.3.12
99
github.com/mitchellh/hashstructure/v2 v2.0.2

substrate/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,6 @@ github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
162162
github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
163163
github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
164164
github.com/aws/aws-sdk-go v1.38.69/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
165-
github.com/aws/aws-sdk-go v1.43.8 h1:8a/M9C4l5CxFNM6IuNx4F1p+ITJEX12VxWxUQo61cbc=
166-
github.com/aws/aws-sdk-go v1.43.8/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
167165
github.com/aws/aws-sdk-go v1.43.16 h1:Y7wBby44f+tINqJjw5fLH3vA+gFq4uMITIKqditwM14=
168166
github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
169167
github.com/awslabs/kubernetes-iteration-toolkit/operator v0.0.0-20220818192233-b51328aa42d4 h1:FRTo4S/JXaf7X1jhFQ4CUPYTvdC18XoQIRa/cORCaCM=

substrate/pkg/apis/v1alpha1/zz_generated.deepcopy.go

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

substrate/pkg/controller/substrate/cluster/config.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,14 @@ func (c *Config) Create(ctx context.Context, substrate *v1alpha1.Substrate) (rec
106106
return reconcile.Result{}, fmt.Errorf("generating authenticator config, %w", err)
107107
}
108108
// upload to s3 bucket
109-
if err := c.S3Uploader.UploadWithIterator(ctx, NewDirectoryIterator(
110-
aws.StringValue(discovery.Name(substrate)), path.Join(c.clusterConfigPath, aws.StringValue(discovery.Name(substrate))))); err != nil {
109+
directoyIterator, err := NewDirectoryIterator(
110+
aws.StringValue(discovery.Name(substrate)),
111+
path.Join(c.clusterConfigPath, aws.StringValue(discovery.Name(substrate))),
112+
)
113+
if err != nil {
114+
return reconcile.Result{}, fmt.Errorf("building directory iterator %w", err)
115+
}
116+
if err := c.S3Uploader.UploadWithIterator(ctx, directoyIterator); err != nil {
111117
return reconcile.Result{}, fmt.Errorf("uploading to S3 %w", err)
112118
}
113119
logging.FromContext(ctx).Debugf("Uploaded cluster configuration to s3://%s", aws.StringValue(discovery.Name(substrate)))
@@ -369,22 +375,25 @@ type DirectoryIterator struct {
369375
}
370376

371377
// NewDirectoryIterator builds a new DirectoryIterator
372-
func NewDirectoryIterator(bucket, dir string) s3manager.BatchUploadIterator {
378+
func NewDirectoryIterator(bucket, dir string) (s3manager.BatchUploadIterator, error) {
373379
var paths []string
374-
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
380+
walkFunc := func(path string, info os.FileInfo, err error) error {
375381
if err != nil {
376382
return err
377383
}
378384
if !info.IsDir() {
379385
paths = append(paths, path)
380386
}
381387
return nil
382-
})
388+
}
389+
if err := filepath.Walk(dir, walkFunc); err != nil {
390+
return nil, err
391+
}
383392
return &DirectoryIterator{
384393
filePaths: paths,
385394
bucket: bucket,
386395
localDir: dir,
387-
}
396+
}, nil
388397
}
389398

390399
// Next returns whether next file exists or not

0 commit comments

Comments
 (0)