Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use controllerruntime to discover CABundle #636

Merged
merged 34 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5760600
checkpoint: tests passin'
Aug 24, 2021
93f824d
minor cleanups
Aug 24, 2021
c834adc
add another test for aws required fields
Aug 25, 2021
6adb8c1
validate caBundle
Aug 25, 2021
67346ff
fix PR comment
Aug 25, 2021
0e30543
checkpoint
Aug 26, 2021
ea56f6d
checkpoint - add test for defaulting in CABundle
Aug 26, 2021
95894f6
remove commented-out code
Aug 26, 2021
0d7a97e
fix broken os.FS unit test mock
Aug 29, 2021
4510a61
clean up stray debug lines
Aug 29, 2021
f397185
remove unnecessary inject
Aug 29, 2021
58e6459
remove unnecessary filesys.Inject
Aug 29, 2021
78e0433
minor formatting
Aug 29, 2021
b91394e
remove unnecessary import
Aug 29, 2021
5f0c102
fix stray debugging change
Aug 29, 2021
459a905
add helpful comment that explains why test actually works
Aug 29, 2021
ec5e164
address PR comments
Aug 30, 2021
b9228a7
address pr fback
Aug 30, 2021
982ebd1
switch to afero everywhere because io/fs just doesn't like
Aug 30, 2021
139931e
minor cleanup
Aug 30, 2021
08e4884
Address PR feedback from rustrial@
Aug 30, 2021
dacdbf5
address etarn pr fback
Aug 30, 2021
2e4500c
update docs
Aug 30, 2021
541a2df
remove no longer valid test
Aug 31, 2021
b610d1d
remove stray line
Aug 31, 2021
b6825c8
pr fback
Aug 31, 2021
36a393d
revert changes
Aug 31, 2021
2fc7224
undo my changes hrere
Aug 31, 2021
2f7cc91
start to migrate clusterCA default code to cloudprovider logic
Aug 31, 2021
c16ae01
move CABundle dynamic loading to launch template generation time
Sep 1, 2021
4b9f612
switch to using client-go to get CABundle
Sep 1, 2021
1acd986
revert
Sep 1, 2021
9e7ef29
remove stray line
Sep 1, 2021
85a721d
use GetConfigOrDie() from controllerruntime ...
Sep 2, 2021
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
260 changes: 260 additions & 0 deletions go.sum

Large diffs are not rendered by default.

60 changes: 9 additions & 51 deletions pkg/apis/provisioning/v1alpha3/provisioner_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,65 +17,23 @@ package v1alpha3
import (
"context"
"encoding/base64"
"errors"
"io/ioutil"
"os"
)

const (
InClusterCABundlePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
controllerruntime "sigs.k8s.io/controller-runtime"
)

// SetDefaults for the provisioner
func (p *Provisioner) SetDefaults(ctx context.Context) {}

// SetDefaults for the provisioner, cascading to all subspecs
func (s *ProvisionerSpec) SetDefaults(ctx context.Context) {}

// WithDefaults returns a copy of this Provisioner with some empty/missing
// properties replaced by (potentially dynamic) cloud provider agnostic default values.
// The returned copy might be complemented by dynamic default values which
// must not be hoisted (saved) into the original Provisioner CRD as those
// default values might change over time (e.g. rolling upgrade of CABundle, ...).
func (p *Provisioner) WithDynamicDefaults(ctx context.Context) (_ Provisioner, err error) {
provisioner := *p.DeepCopy()
provisioner.Spec, err = provisioner.Spec.withDynamicDefaults()
return provisioner, err
}

// WithDefaults returns a copy of this ProvisionerSpec with some empty
// properties replaced by default values.
func (s *ProvisionerSpec) withDynamicDefaults() (_ ProvisionerSpec, err error) {
spec := *s.DeepCopy()
spec.Cluster, err = spec.Cluster.withDynamicDefaults()
return spec, err
}

// WithDefaults returns a copy of this Cluster with some empty
// properties replaced by default values. Notably, it will try
// to load the CABundle from the in-cluster configuraiton if it
// is not explicitly set.
func (c *Cluster) withDynamicDefaults() (_ Cluster, err error) {
cluster := *c.DeepCopy()
cluster.CABundle, err = cluster.getCABundle()
return cluster, err
}

func (c *Cluster) getCABundle() (*string, error) {
func (c *Cluster) GetCABundle(ctx context.Context) (*string, error) {
if c.CABundle != nil {
// If CABundle is explicitly provided use that one. An empty string is
// a valid value here if the intention is to disable the in-cluster CABundle
// and using the HTTP client's default trust-store (CABundle) instead.
// If CABundle is explicitly provided, use that one. An empty
// string is a valid value here if the intention is to disable
// the in-cluster CABundle, and using the HTTP client's
// default trust-store (CABundle) instead.
return c.CABundle, nil
}
// Otherwise, fallback to the in-cluster configuration.
binary, err := ioutil.ReadFile(InClusterCABundlePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
return nil, err
}
encoded := base64.StdEncoding.EncodeToString(binary)

config := controllerruntime.GetConfigOrDie()
encoded := base64.StdEncoding.EncodeToString(config.CAData)
return &encoded, nil
}
3 changes: 2 additions & 1 deletion pkg/cloudprovider/aws/cloudprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
v1 "k8s.io/api/core/v1"
"knative.dev/pkg/apis"
"knative.dev/pkg/logging"
"knative.dev/pkg/ptr"
)

const (
Expand Down Expand Up @@ -174,7 +175,7 @@ func (c *CloudProvider) ValidateConstraints(ctx context.Context, constraints *v1

// Validate cloud provider specific components of the cluster spec.
func (c *CloudProvider) ValidateSpec(ctx context.Context, spec *v1alpha3.ProvisionerSpec) (errs *apis.FieldError) {
if spec.Cluster.Name == nil || len(*spec.Cluster.Name) == 0 {
if ptr.StringValue(spec.Cluster.Name) == "" {
errs = errs.Also(apis.ErrMissingField("name")).ViaField("cluster")
}
return errs
Expand Down
51 changes: 29 additions & 22 deletions pkg/cloudprovider/aws/launchtemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"context"
"encoding/base64"
"fmt"
"text/template"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
Expand All @@ -35,18 +34,6 @@ import (

const (
launchTemplateNameFormat = "Karpenter-%s-%s"
bottlerocketUserData = `
[settings.kubernetes]
api-server = "{{.Cluster.Endpoint}}"
{{if .Cluster.CABundle}}{{if len .Cluster.CABundle}}cluster-certificate = "{{.Cluster.CABundle}}"{{end}}{{end}}
cluster-name = "{{if .Cluster.Name}}{{.Cluster.Name}}{{end}}"
{{if .Constraints.Labels }}[settings.kubernetes.node-labels]{{ end }}
{{ range $Key, $Value := .Constraints.Labels }}"{{ $Key }}" = "{{ $Value }}"
{{ end }}
{{if .Constraints.Taints }}[settings.kubernetes.node-taints]{{ end }}
{{ range $Taint := .Constraints.Taints }}"{{ $Taint.Key }}" = "{{ $Taint.Value}}:{{ $Taint.Effect }}"
{{ end }}
`
)

type LaunchTemplateProvider struct {
Expand Down Expand Up @@ -103,10 +90,16 @@ func (p *LaunchTemplateProvider) Get(ctx context.Context, provisioner *v1alpha3.
return nil, err
}

// 3. Get userData for Node
userData, err := p.getUserData(ctx, provisioner, constraints)
if err != nil {
return nil, err
}

// 4. Ensure the launch template exists, or create it
launchTemplate, err := p.ensureLaunchTemplate(ctx, &launchTemplateOptions{
Cluster: provisioner.Spec.Cluster,
UserData: p.getUserData(provisioner, constraints),
UserData: userData,
AMIID: amiID,
SecurityGroups: securityGroups,
})
Expand Down Expand Up @@ -197,14 +190,28 @@ func (p *LaunchTemplateProvider) getSecurityGroupIds(ctx context.Context, provis
return securityGroupIds, nil
}

func (p *LaunchTemplateProvider) getUserData(provisioner *v1alpha3.Provisioner, constraints *Constraints) string {
t := template.Must(template.New("userData").Parse(bottlerocketUserData))
func (p *LaunchTemplateProvider) getUserData(ctx context.Context, provisioner *v1alpha3.Provisioner, constraints *Constraints) (string, error) {
var userData bytes.Buffer
if err := t.Execute(&userData, struct {
Constraints *Constraints
Cluster v1alpha3.Cluster
}{constraints, provisioner.Spec.Cluster}); err != nil {
panic(fmt.Sprintf("Parsing user data from %v, %v, %s", provisioner, constraints, err.Error()))
userData.WriteString(fmt.Sprintf("[settings.kubernetes]\napi-server = \"%s\"\n", provisioner.Spec.Cluster.Endpoint))
userData.WriteString(fmt.Sprintf("cluster-name = \"%s\"\n", *provisioner.Spec.Cluster.Name))
caBundle, err := provisioner.Spec.Cluster.GetCABundle(ctx)
if err != nil {
return "", fmt.Errorf("getting user data, %w", err)
}
if caBundle != nil {
userData.WriteString(fmt.Sprintf("cluster-certificate = \"%s\"\n", *caBundle))
}
if len(constraints.Labels) > 0 {
userData.WriteString("[settings.kubernetes.node-labels]\n")
for k, v := range constraints.Labels {
userData.WriteString(fmt.Sprintf("\"%s\" = \"%v\"\n", k, v))
}
}
if len(constraints.Taints) > 0 {
userData.WriteString("[settings.kubernetes.node-taints]\n")
for _, taint := range constraints.Taints {
userData.WriteString(fmt.Sprintf("\"%s\" = \"%s:%s\"\n", taint.Key, taint.Value, taint.Effect))
}
}
return base64.StdEncoding.EncodeToString(userData.Bytes())
return base64.StdEncoding.EncodeToString(userData.Bytes()), nil
}
6 changes: 3 additions & 3 deletions pkg/cloudprovider/aws/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ var _ = Describe("Allocation", func() {
launchTemplate := input.LaunchTemplateConfigs[0].LaunchTemplateSpecification
Expect(*launchTemplate.LaunchTemplateName).To(Equal(pods[0].Spec.NodeSelector[LaunchTemplateNameLabel]))
})
It("should allow a pod to override the launch template id and use the default launch template version", func() {
It("should allow a pod to override the launch template name and use the default launch template version", func() {
// Setup
provisioner.Spec.Labels = map[string]string{LaunchTemplateNameLabel: randomdata.SillyName()}
ExpectCreated(env.Client, provisioner)
Expand All @@ -352,7 +352,7 @@ var _ = Describe("Allocation", func() {
Expect(*launchTemplate.LaunchTemplateName).To(Equal(pods[0].Spec.NodeSelector[LaunchTemplateNameLabel]))
Expect(*launchTemplate.Version).To(Equal(DefaultLaunchTemplateVersion))
})
It("should allow a pod to override the launch template id and use the provisioner's launch template version", func() {
It("should allow a pod to override the launch template name and use the provisioner's launch template version", func() {
// Setup
provisioner.Spec.Labels = map[string]string{
LaunchTemplateNameLabel: randomdata.SillyName(),
Expand All @@ -372,7 +372,7 @@ var _ = Describe("Allocation", func() {
})
})
Context("Subnets", func() {
It("should default to the clusters subnets", func() {
It("should default to the cluster's subnets", func() {
// Setup
provisioner.Spec.InstanceTypes = []string{"m5.large"} // limit instance type to simplify ConsistOf checks
ExpectCreated(env.Client, provisioner)
Expand Down
10 changes: 1 addition & 9 deletions pkg/controllers/allocation/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,7 @@ func (c *Controller) provisionerFor(ctx context.Context, name types.NamespacedNa
if err := c.KubeClient.Get(ctx, name, provisioner); err != nil {
return nil, err
}

// Hydrate provisioner with (dynamic) default values, which must not
// be persisted into the original CRD as they might change with each reconciliation
// loop iteration.
defaulted, err := provisioner.WithDynamicDefaults(ctx)
if err != nil {
return &defaulted, fmt.Errorf("setting dynamic default values, %w", err)
}
return &defaulted, nil
return provisioner, nil
}

// podToProvisioner is a function handler to transform pod objs to provisioner reconcile requests
Expand Down
1 change: 0 additions & 1 deletion pkg/controllers/allocation/scheduling/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ func (s *Scheduler) getDaemons(ctx context.Context, node *v1.Node) ([]*v1.Pod, e
return pods, nil
}


// IsSchedulable returns true if the pod can schedule to the node
func IsSchedulable(pod *v1.Pod, node *v1.Node) bool {
// Tolerate Taints
Expand Down
8 changes: 4 additions & 4 deletions website/content/en/docs/provisioner-crd.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ spec:
# Provisioned nodes will have these labels
labels:
##### AWS Specific #####
# Constrain node launch template, default="bottlerocket"
node.k8s.aws/launch-template-id: "bottlerocket-qwertyuiop"
# Constrain node launch template, default="$LATEST"
node.k8s.aws/launch-template-version: "my-special-version"
# Constrain node launch template ('$Default' version always used).
# If not specified, Karpenter will generate a Bottlerocket-
# based launch template dynamically.
node.k8s.aws/launch-template-name: "my-launch-template-name"
# Constrain node capacity type, default="on-demand"
node.k8s.aws/capacity-type: "spot"
```