Skip to content

Commit

Permalink
Declarative plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Orive Oneca <[email protected]>
  • Loading branch information
Adirio committed Mar 2, 2021
1 parent bc9576c commit 85c445e
Show file tree
Hide file tree
Showing 10 changed files with 566 additions and 13 deletions.
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sigs.k8s.io/kubebuilder/v3/pkg/cli"
cfgv2 "sigs.k8s.io/kubebuilder/v3/pkg/config/v2"
cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
declarativev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/declarative/v1"
pluginv2 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v2"
pluginv3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
)
Expand All @@ -34,6 +35,7 @@ func main() {
cli.WithPlugins(
&pluginv2.Plugin{},
&pluginv3.Plugin{},
&declarativev1.Plugin{},
),
cli.WithDefaultPlugins(cfgv2.Version, &pluginv2.Plugin{}),
cli.WithDefaultPlugins(cfgv3.Version, &pluginv3.Plugin{}),
Expand Down
107 changes: 107 additions & 0 deletions pkg/plugins/declarative/v1/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
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 v1

import (
"fmt"
"path/filepath"
"strings"

"github.com/spf13/afero"

"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/model"
"sigs.k8s.io/kubebuilder/v3/pkg/model/resource"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugins/declarative/v1/internal/templates"
goPluginV3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
"sigs.k8s.io/kubebuilder/v3/pkg/plugins/internal/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/plugins/internal/util"
)

const (
// kbDeclarativePattern is the sigs.k8s.io/kubebuilder-declarative-pattern version
kbDeclarativePatternForV2 = "v0.0.0-20200522144838-848d48e5b073"
kbDeclarativePatternForV3 = "v0.0.0-20210113160450-b84d99da0217"

exampleManifestVersion = "0.0.1"
)

var _ plugin.CreateAPISubcommand = &createAPISubcommand{}

type createAPISubcommand struct {
config config.Config

resource *resource.Resource
}

func (p *createAPISubcommand) InjectConfig(c config.Config) error {
p.config = c

return nil
}

func (p *createAPISubcommand) InjectResource(res *resource.Resource) error {
p.resource = res

if !p.resource.HasAPI() || !p.resource.HasController() {
return plugin.ExitError{
Plugin: pluginName,
Reason: "declarative pattern is only supported when API and controller are scaffolded",
}
}

return nil
}

func (p *createAPISubcommand) Scaffold(fs afero.Fs) error {
fmt.Println("updating scaffold with declarative pattern...")

// Load the boilerplate
bp, err := afero.ReadFile(fs, filepath.Join("hack", "boilerplate.go.txt"))
if err != nil {
return fmt.Errorf("error updating scaffold: unable to load boilerplate: %w", err)
}
boilerplate := string(bp)

if err := machinery.NewScaffold(fs).Execute(
model.NewUniverse(
model.WithConfig(p.config),
model.WithBoilerplate(boilerplate),
model.WithResource(p.resource),
),
&templates.Types{},
&templates.Controller{},
&templates.Channel{ManifestVersion: exampleManifestVersion},
&templates.Manifest{ManifestVersion: exampleManifestVersion},
); err != nil {
return fmt.Errorf("error updating scaffold: %w", err)
}

// Ensure that we are pinning sigs.k8s.io/kubebuilder-declarative-pattern version
kbDeclarativePattern := kbDeclarativePatternForV2
if strings.Split(p.config.GetLayout(), ",")[0] == plugin.KeyFor(goPluginV3.Plugin{}) {
kbDeclarativePattern = kbDeclarativePatternForV3
}
err = util.RunCmd("Get declarative pattern", "go", "get",
"sigs.k8s.io/kubebuilder-declarative-pattern@"+kbDeclarativePattern)
if err != nil {
return err
}

return nil
}
52 changes: 52 additions & 0 deletions pkg/plugins/declarative/v1/internal/templates/channel.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 templates

import (
"fmt"
"path/filepath"

"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
)

var _ file.Template = &Channel{}

// Channel scaffolds the file for the channel
type Channel struct {
file.TemplateMixin

ManifestVersion string
}

// SetTemplateDefaults implements file.Template
func (f *Channel) SetTemplateDefaults() error {
if f.Path == "" {
f.Path = filepath.Join("channels", "stable")
}
fmt.Println(f.Path)

f.TemplateBody = channelTemplate

f.IfExistsAction = file.Skip

return nil
}

const channelTemplate = `# Versions for the stable channel
manifests:
- version: {{ .ManifestVersion }}
`
130 changes: 130 additions & 0 deletions pkg/plugins/declarative/v1/internal/templates/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
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 templates

import (
"path/filepath"

"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
)

var _ file.Template = &Controller{}

// Controller scaffolds the file that defines the controller for a CRD or a builtin resource
// nolint:maligned
type Controller struct {
file.TemplateMixin
file.MultiGroupMixin
file.BoilerplateMixin
file.ResourceMixin
}

// SetTemplateDefaults implements file.Template
func (f *Controller) SetTemplateDefaults() error {
if f.Path == "" {
if f.MultiGroup {
f.Path = filepath.Join("controllers", "%[group]", "%[kind]_controller.go")
} else {
f.Path = filepath.Join("controllers", "%[kind]_controller.go")
}
}
f.Path = f.Resource.Replacer().Replace(f.Path)

f.TemplateBody = controllerTemplate

f.IfExistsAction = file.Overwrite

return nil
}

//nolint:lll
const controllerTemplate = `{{ .Boilerplate }}
package controllers
import (
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon"
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/status"
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative"
{{ .Resource.ImportAlias }} "{{ .Resource.Path }}"
)
var _ reconcile.Reconciler = &{{ .Resource.Kind }}Reconciler{}
// {{ .Resource.Kind }}Reconciler reconciles a {{ .Resource.Kind }} object
type {{ .Resource.Kind }}Reconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
declarative.Reconciler
}
//+kubebuilder:rbac:groups={{ .Resource.QualifiedGroup }},resources={{ .Resource.Plural }},verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups={{ .Resource.QualifiedGroup }},resources={{ .Resource.Plural }}/status,verbs=get;update;patch
// SetupWithManager sets up the controller with the Manager.
func (r *{{ .Resource.Kind }}Reconciler) SetupWithManager(mgr ctrl.Manager) error {
addon.Init()
labels := map[string]string{
"k8s-app": "{{ lower .Resource.Kind }}",
}
watchLabels := declarative.SourceLabel(mgr.GetScheme())
if err := r.Reconciler.Init(mgr, &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{},
declarative.WithObjectTransform(declarative.AddLabels(labels)),
declarative.WithOwner(declarative.SourceAsOwner),
declarative.WithLabels(watchLabels),
declarative.WithStatus(status.NewBasic(mgr.GetClient())),
// TODO: add an application to your manifest: declarative.WithObjectTransform(addon.TransformApplicationFromStatus),
// TODO: add an application to your manifest: declarative.WithManagedApplication(watchLabels),
declarative.WithObjectTransform(addon.ApplyPatches),
); err != nil {
return err
}
c, err := controller.New("{{ lower .Resource.Kind }}-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to {{ .Resource.Kind }}
err = c.Watch(&source.Kind{Type: &{{ .Resource.ImportAlias }}.{{ .Resource.Kind }}{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
// Watch for changes to deployed objects
_, err = declarative.WatchAll(mgr.GetConfig(), c, r, watchLabels)
if err != nil {
return err
}
return nil
}
`
52 changes: 52 additions & 0 deletions pkg/plugins/declarative/v1/internal/templates/manifest.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 templates

import (
"fmt"
"path/filepath"

"sigs.k8s.io/kubebuilder/v3/pkg/model/file"
)

var _ file.Template = &Manifest{}

// Manifest scaffolds the file that acts as a placeholder for the manifest
type Manifest struct {
file.TemplateMixin
file.ResourceMixin

ManifestVersion string
}

// SetTemplateDefaults implements file.Template
func (f *Manifest) SetTemplateDefaults() error {
if f.Path == "" {
f.Path = filepath.Join("channels", "packages", "%[kind]", f.ManifestVersion, "manifest.yaml")
}
f.Path = f.Resource.Replacer().Replace(f.Path)
fmt.Println(f.Path)

f.TemplateBody = manifestTemplate

f.IfExistsAction = file.Skip

return nil
}

const manifestTemplate = `# Placeholder manifest - replace with the manifest for your addon
`
Loading

0 comments on commit 85c445e

Please sign in to comment.