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

✨ Add kubebuilder alpha config-gen command to explore generating configuration with kustomize functions #1831

Merged
merged 1 commit into from
Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
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.
*/

//go:generate go run github.com/markbates/pkger/cmd/pkger

// Package kubebuilder contains pkged files compiled into the
// go binaries.
package kubebuilder
Comment on lines +17 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is pkger being used for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To include the templates statically in the binary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be achieved with embed in 1.16?
(I'm not suggesting to change it, we are not supporting 1.16 yet)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe so. :). (Yay)

11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ module sigs.k8s.io/kubebuilder/v3
go 1.15

require (
github.com/cloudflare/cfssl v1.5.0 // for `kubebuilder alpha config-gen`
github.com/gobuffalo/flect v0.2.2
// TODO: remove this in favor of embed once using 1.16
github.com/markbates/pkger v0.17.1 // for `kubebuilder alpha config-gen`
github.com/onsi/ginkgo v1.15.0
github.com/onsi/gomega v1.10.5
github.com/spf13/afero v1.2.2
github.com/spf13/cobra v0.0.7
github.com/spf13/cobra v1.1.1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to update cobra for this PR?

I definetely think that bumping cobra is somethign we need to do, but if it is not needed forn this PR we should probably do it in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is just what happened with I ran go test ; go mod tidy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking if it works with v0.0.7 and if go mod tidy doesn't bump it afterwards would confirm if this is needed or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running either go build ./pkg/... or go mod tidy bumps this to v1.0.0 or higher.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok.

github.com/spf13/pflag v1.0.5
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e
// for `kubebuilder alpha config-gen`
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
camilamacedo86 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which import requires this? Just curious if we could somehow prevent using two different YAML parsers as we are using sigs.k8s.io/yaml v1.2.0 internally.

Copy link
Contributor Author

@pwittrock pwittrock Mar 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the kustomize fn framework uses this -- it is quite different as it parses the yaml into a tree and keeps comments, formatting, etc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the output changes considerable or breaking? Or can we just update the test and remove this indirect require?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They aren't breaking, but go doesn't seem to pick a consistent version unless this is specified and the flapping between versions creates a lot of confusion and churn.

k8s.io/apimachinery v0.20.2 // for `kubebuilder alpha config-gen`
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 // indirect
sigs.k8s.io/controller-tools v0.3.0 // for `kubebuilder alpha config-gen`
sigs.k8s.io/kustomize/kyaml v0.10.10 // for `kubebuilder alpha config-gen`
sigs.k8s.io/yaml v1.2.0
)
561 changes: 552 additions & 9 deletions go.sum

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions pkg/cli/alpha.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright 2021 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 cli

import (
"strings"

"github.com/spf13/cobra"
configgen "sigs.k8s.io/kubebuilder/v3/pkg/cli/alpha/config-gen"
)

var alphaCommands = []*cobra.Command{
configgen.NewCommand(),
}

func (c *CLI) newAlphaCmd() *cobra.Command {
alpha := &cobra.Command{
Use: "alpha",
SuggestFor: []string{"experimental"},
Short: "Alpha kubebuilder subcommands",
Long: strings.TrimSpace(`
Alpha kubebuilder commands are for unstable features.

- Alpha commands are exploratory and may be removed without warning.
- No backwards compatibility is provided for any alpha commands.
`),
}
for i := range alphaCommands {
alpha.AddCommand(alphaCommands[i])
}
return alpha
}

func (c *CLI) addAlphaCmd() {
if len(alphaCommands) > 0 {
c.cmd.AddCommand(c.newAlphaCmd())
}
}
41 changes: 41 additions & 0 deletions pkg/cli/alpha/config-gen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Config-gen

`kubebuilder alpha config-gen` is a subcommand that generates configuration for kubebuilder projects as a configuration function.

Supports:

- Generating CRDs and RBAC from code
- Generating webhook certificates for development
- Selectively enabling / disabling components such as prometheus and webhooks
- See [types.go](apis/v1alpha1/types.go) for a list of components

## Usage

`config-gen` may be run as a standalone command or from kustomize as a transformer plugin.

### Standalone command

config-gen may be run as a standalone program on the commandline.

See [examples/standalone](examples/standalone/README.md)

### From kustomize

config-gen may be run as a Kustomize plugin using kustomize.

See [examples/kustomize](examples/kustomize/README.md)

### Extending `config-gen`

config-gen may be extended by composing additional functions on top of it.

See examples of layering additional functions on:

- [examples/basicextension](examples/basicextension/README.md)
- [examples/advancedextension](examples/advancedextension/README.md)

## `KubebuilderConfigGen`

See [types.go](apis/v1alpha1/types.go) for KubebuilderConfigGen schema.

See [testdata](apis/v1alpha1/testdata) for examples of configuration options.
164 changes: 164 additions & 0 deletions pkg/cli/alpha/config-gen/cert-generation-filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
Copyright 2021 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 configgen

import (
"encoding/base64"
"fmt"

"github.com/cloudflare/cfssl/cli/genkey"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/selfsign"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

var _ kio.Filter = &CertFilter{}

// CertFilter generates and injects certificates into webhook
type CertFilter struct {
*KubebuilderConfigGen
}

// Filter implements kio.Filter
func (c CertFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {

if c.Spec.Webhooks.CertificateSource.Type != "dev" {
return input, nil
}
if err := c.generateCert(); err != nil {
return nil, err
}

s := &framework.Selector{
Kinds: []string{
"ValidatingWebhookConfiguration",
"MutatingWebhookConfiguration",
},
}
matches, err := s.GetMatches(&framework.ResourceList{Items: input})
if err != nil {
return nil, err
}
for i := range matches {
wh := matches[i].Field("webhooks")
if wh.IsNilOrEmpty() {
continue
}
err := wh.Value.VisitElements(func(node *yaml.RNode) error {
err := node.PipeE(yaml.LookupCreate(yaml.ScalarNode, "clientConfig", "caBundle"),
yaml.FieldSetter{StringValue: c.Status.CertCA})
if err != nil {
return err
}
err = node.PipeE(yaml.LookupCreate(yaml.ScalarNode, "clientConfig", "service", "namespace"),
yaml.FieldSetter{StringValue: c.Namespace})
if err != nil {
return err
}

return nil
})
if err != nil {
return nil, err
}
}

s = &framework.Selector{
Filter: func(n *yaml.RNode) bool {
// Allow-list conversion webhooks
m, _ := n.GetMeta()
if m.Kind != "CustomResourceDefinition" {
return true
}
return c.Spec.Webhooks.Conversions[m.Name]
},
}
matches, err = s.GetMatches(&framework.ResourceList{Items: input})
if err != nil {
return nil, err
}
for i := range matches {
err := matches[i].PipeE(yaml.LookupCreate(yaml.ScalarNode, "spec", "conversion", "strategy"),
yaml.FieldSetter{StringValue: "Webhook"})
if err != nil {
return nil, err
}
err = matches[i].PipeE(yaml.LookupCreate(
yaml.ScalarNode, "spec", "conversion", "webhookClientConfig", "caBundle"),
yaml.FieldSetter{StringValue: c.Status.CertCA})
if err != nil {
return nil, err
}
err = matches[i].PipeE(yaml.LookupCreate(
yaml.ScalarNode, "spec", "conversion", "webhookClientConfig", "service", "name"),
yaml.FieldSetter{StringValue: "webhook-service"})
if err != nil {
return nil, err
}
err = matches[i].PipeE(yaml.LookupCreate(
yaml.ScalarNode, "spec", "conversion", "webhookClientConfig", "service", "namespace"),
yaml.FieldSetter{StringValue: c.Namespace})
if err != nil {
return nil, err
}

err = matches[i].PipeE(yaml.LookupCreate(
yaml.ScalarNode, "spec", "conversion", "webhookClientConfig", "service", "path"),
yaml.FieldSetter{StringValue: "/convert"})
if err != nil {
return nil, err
}
}

return input, nil
}

func (c CertFilter) generateCert() error {
var err error
var req = csr.New()
req.Hosts = []string{
fmt.Sprintf("webhook-service.%s.svc", c.Namespace),
fmt.Sprintf("webhook-service.%s.svc.cluster.local", c.Namespace),
}
req.CN = "kb-dev-controller-manager"

var key, csrPEM []byte
g := &csr.Generator{Validator: genkey.Validator}
csrPEM, key, err = g.ProcessRequest(req)
if err != nil {
return err
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
return err
}

profile := config.DefaultConfig()
profile.Expiry = c.Spec.Webhooks.CertificateSource.DevCertificate.CertDuration
cert, err := selfsign.Sign(priv, csrPEM, profile)
if err != nil {
return err
}

c.Status.CertCA = base64.StdEncoding.EncodeToString(cert)
c.Status.CertKey = base64.StdEncoding.EncodeToString(key)
return nil
}
39 changes: 39 additions & 0 deletions pkg/cli/alpha/config-gen/cert-manager-patches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
Copyright 2021 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 configgen

import (
"github.com/markbates/pkger"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
)

// CertManagerPatchTemplate returns the PatchTemplate for cert-manager
func CertManagerPatchTemplate(_ *KubebuilderConfigGen) framework.PT {
return framework.PT{
// keep casting -- required by pkger to find the directory
Dir: pkger.Dir("/pkg/cli/alpha/config-gen/templates/patches/cert-manager"),
Selector: func() *framework.Selector {
return &framework.Selector{
Kinds: []string{
"CustomResourceDefinition",
"ValidatingWebhookConfiguration",
"MutatingWebhookConfiguration",
},
}
},
}
}
Loading