Skip to content

Commit

Permalink
Skaffold Kustomize function for generating configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
pwittrock committed Nov 21, 2020
1 parent cd3cb2a commit cb01d2c
Show file tree
Hide file tree
Showing 39 changed files with 4,135 additions and 13 deletions.
80 changes: 80 additions & 0 deletions config-gen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Config-gen

This is a prototype of using a kustomize function to generate configuration.

To test it:

```sh
go build -o ~/go/bin/config-gen ./config-gen
```

## Usage Options

### With PROJECT file

Add the config gen fields to your project file

```yaml
# PROEJCT
metadata:
name: project-name # name used to generated resource names and namespaces
spec:
image: pwittrock/simple # controller-manager image to run
...
```

```sh
# from a kubebuilder project
config-gen
```

### With config file

Create a config.yaml

```yaml
# config.yaml
apiVersion: kubebuilder.sigs.k8s.io
kind: APIConfiguration
metadata:
name: project-name
spec:
image: example/simple:latest
```
```sh
# from a kubebuilder project
config-gen config.yaml
```

### With patch overrides

```yaml
# config.yaml
apiVersion: kubebuilder.sigs.k8s.io
kind: APIConfiguration
metadata:
name: project-name
spec:
image: example/simple:latest
```
```yaml
# patch1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: project-name-system
spec:
...
```

```sh
# from a kubebuilder project
config-gen config.yaml patch1.yaml
```

### From kustomize

config-gen may be run as a Kustomize plugin using kustomize
94 changes: 94 additions & 0 deletions config-gen/apis/v1alpha1/controllergen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"bytes"
"fmt"
"io"
"os"

"sigs.k8s.io/controller-tools/pkg/crd"
"sigs.k8s.io/controller-tools/pkg/genall"
"sigs.k8s.io/controller-tools/pkg/loader"
"sigs.k8s.io/controller-tools/pkg/rbac"
"sigs.k8s.io/controller-tools/pkg/webhook"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

var _ kio.Filter = &ControllerGenFilter{}

// ControllerGenFilter generates resources using controller-gen
type ControllerGenFilter struct {
*APIConfiguration
}

// Filter implements kio.Filter
func (cgr ControllerGenFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
desclen := 40
crdGen := genall.Generator(crd.Generator{
TrivialVersions: true,
MaxDescLen: &desclen,
})
gens := genall.Generators{&crdGen}
if !cgr.Spec.DisableCreateRBAC {
rbacGen := genall.Generator(rbac.Generator{
RoleName: cgr.Spec.Namespace + "-manager-role",
})
gens = append(gens, &rbacGen)
}
if cgr.Spec.EnableWebhooks {
webhookGen := genall.Generator(webhook.Generator{})
gens = append(gens, &webhookGen)
}

b := BufferedGenerator{}
paths := "./..."
if cgr.Spec.Directory != "" {
paths = cgr.Spec.Directory
}
rt, _ := gens.ForRoots(paths)
rt.OutputRules = genall.OutputRules{Default: &b}

if ok := rt.Run(); ok {
fmt.Fprintln(os.Stderr, "error running controller-gen")
}

// Parse the output
n, err := (&kio.ByteReader{Reader: &b.Buffer}).Read()
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to parse controller-gen output")
}
return append(n, input...), nil
}

// BufferedGenerator implements a genall.Generator store the output in a bytes.Buffer
type BufferedGenerator struct {
bytes.Buffer
}

// Open implements genall.Generator
func (o *BufferedGenerator) Open(_ *loader.Package, _ string) (io.WriteCloser, error) {
return o, nil
}

// Close implements genall.Generator
func (BufferedGenerator) Close() error {
return nil
}
131 changes: 131 additions & 0 deletions config-gen/apis/v1alpha1/patchetemplates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"text/template"

"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

var (
controllerManagerSelector = &framework.Selector{
Kinds: []string{"Deployment"},
Namespaces: []string{"{{ .Spec.Namespace }}"},
Names: []string{"controller-manager"},
Labels: map[string]string{"control-plane": "controller-manager"},
TemplatizeValues: true,
}
)

// getPatchTemplates returns a set of conditional patches
func getPatchTemplates(c *APIConfiguration) []framework.PatchTemplate {
return []framework.PatchTemplate{
// Cert manager patche
{
Selector: &framework.Selector{
Kinds: []string{
"CustomResourceDefinition",
"ValidatingWebhookConfiguration",
"MutatingWebhookConfiguration",
},
},
Template: template.Must(template.New("conversion-webhook").Parse(`{{ if .Spec.EnableCertManager}}
metadata:
annotations:
cert-manager.io/inject-ca-from: {{ .Spec.Namespace }}/{{ .Spec.Name }}-serving-cert
{{ end }}`)),
},

// Conversion webhook patche -- enable conversion webhooks for allowed CRDs
{
Selector: &framework.Selector{
Kinds: []string{"CustomResourceDefinition"},
Filter: func(r *yaml.RNode) bool {
m, _ := r.GetMeta()
return c.Spec.ConversionWebhooks[m.Name]
},
},
Template: template.Must(template.New("conversion-webhook").Parse(`{{ if .Spec.EnableWebhooks}}
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
{{- if .Spec.Development.GenerateCert }}
caBundle: {{ .Spec.Development.CA }}
{{- end }}
service:
namespace: {{ .Spec.Namespace }}
name: webhook-service
path: /convert
{{ end }}`)),
},

// Auth proxy patch for the controller-manager
{
Selector: controllerManagerSelector,
Template: template.Must(template.New("controller-manager-auth-proxy-patch").Parse(`{{ if not .Spec.DisableAuthProxy}}
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=10"
ports:
- containerPort: 8443
name: https
- name: manager
args:
- "--metrics-addr=127.0.0.1:8080"
- "--enable-leader-election"
{{ end }}`)),
},

// Webhooks patch for the controller-manager
{
Selector: controllerManagerSelector,
Template: template.Must(template.New("controller-manager-webhooks").Parse(`{{ if .Spec.EnableWebhooks }}
spec:
template:
spec:
containers:
- name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
{{ end }}`)),
},
}
}
Loading

0 comments on commit cb01d2c

Please sign in to comment.