Move plugins configurations to API module
Data structure representing plugin configurations should be a part of airshipctl API module. Plugin implementations will reside in document package. Change-Id: Id2e359b747a16a5573052cfb05c1148d346db508 Relates-To: #322
This commit is contained in:
parent
1660efc231
commit
034efc3682
@ -49,6 +49,8 @@ func init() {
|
||||
&ImageConfiguration{},
|
||||
&RemoteDirectConfiguration{},
|
||||
&ClusterMap{},
|
||||
&ReplacementTransformer{},
|
||||
&Templater{},
|
||||
)
|
||||
_ = AddToScheme(Scheme) //nolint:errcheck
|
||||
}
|
||||
|
57
pkg/api/v1alpha1/replacement_plugin_types.go
Normal file
57
pkg/api/v1alpha1/replacement_plugin_types.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ReplacementTransformer plugin configuration for airship document model
|
||||
type ReplacementTransformer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Replacements list of source and target field to do a replacement
|
||||
Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
|
||||
Tst []string
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ReplacementTransformer) DeepCopyInto(out *ReplacementTransformer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Replacements != nil {
|
||||
out.Replacements = make([]types.Replacement, len(in.Replacements))
|
||||
for i, repl := range in.Replacements {
|
||||
out.Replacements[i] = types.Replacement{
|
||||
Source: &types.ReplSource{
|
||||
ObjRef: &types.Target{},
|
||||
FieldRef: repl.Source.FieldRef,
|
||||
Value: repl.Source.Value,
|
||||
},
|
||||
Target: &types.ReplTarget{
|
||||
ObjRef: &types.Selector{},
|
||||
FieldRefs: repl.Target.FieldRefs,
|
||||
},
|
||||
}
|
||||
*(out.Replacements[i].Source.ObjRef) = *(in.Replacements[i].Source.ObjRef)
|
||||
*(out.Replacements[i].Target.ObjRef) = *(in.Replacements[i].Target.ObjRef)
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,12 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Templater plugin for airship document model
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// Templater plugin configuration for airship document model
|
||||
type Templater struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
@ -29,3 +32,13 @@ type Templater struct {
|
||||
// to be used to render the object defined in Spec field
|
||||
Template string `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// NOTE map[string]interface is not supported by controller gen
|
||||
|
||||
// DeepCopyInto is copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Templater) DeepCopyInto(out *Templater) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Values = runtime.DeepCopyJSON(in.Values)
|
||||
}
|
@ -490,3 +490,39 @@ func (in *RemoteDirectConfiguration) DeepCopyObject() runtime.Object {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacementTransformer.
|
||||
func (in *ReplacementTransformer) DeepCopy() *ReplacementTransformer {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ReplacementTransformer)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ReplacementTransformer) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Templater.
|
||||
func (in *Templater) DeepCopy() *Templater {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Templater)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Templater) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
package replacement
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -17,11 +17,17 @@ package replacement
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
replv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/replacement/v1alpha1"
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
// RegisterPlugin registers BareMetalHost generator plugin
|
||||
func RegisterPlugin(registry map[schema.GroupVersionKind]types.Factory) {
|
||||
registry[replv1alpha1.GetGVK()] = replv1alpha1.New
|
||||
func RegisterPlugin(registry map[schema.GroupVersionKind]types.Factory) error {
|
||||
obj := &airshipv1.ReplacementTransformer{}
|
||||
gvks, _, err := airshipv1.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry[gvks[0]] = New
|
||||
return nil
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
package replacement
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -11,13 +11,13 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
plugtypes "opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
@ -31,22 +31,29 @@ const (
|
||||
dotReplacer = "$$$$"
|
||||
)
|
||||
|
||||
// GetGVK returns group, version, kind object used to register version
|
||||
// of the plugin
|
||||
func GetGVK() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{
|
||||
Group: "airshipit.org",
|
||||
Version: "v1alpha1",
|
||||
Kind: "ReplacementTransformer",
|
||||
}
|
||||
type plugin struct {
|
||||
*airshipv1.ReplacementTransformer
|
||||
}
|
||||
|
||||
// New creates new instance of the plugin
|
||||
func New(cfg []byte) (plugtypes.Plugin, error) {
|
||||
p := &plugin{}
|
||||
if err := p.Config(nil, cfg); err != nil {
|
||||
func New(obj map[string]interface{}) (plugtypes.Plugin, error) {
|
||||
cfg := &airshipv1.ReplacementTransformer{}
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &plugin{ReplacementTransformer: cfg}
|
||||
for _, r := range p.Replacements {
|
||||
if r.Source == nil {
|
||||
return nil, ErrBadConfiguration{Msg: "`from` must be specified in one replacement"}
|
||||
}
|
||||
if r.Target == nil {
|
||||
return nil, ErrBadConfiguration{Msg: "`to` must be specified in one replacement"}
|
||||
}
|
||||
if r.Source.ObjRef != nil && r.Source.Value != "" {
|
||||
return nil, ErrBadConfiguration{Msg: "only one of fieldref and value is allowed in one replacement"}
|
||||
}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -82,28 +89,6 @@ func (p *plugin) Run(in io.Reader, out io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config function reads replacements configuration
|
||||
func (p *plugin) Config(
|
||||
_ *resmap.PluginHelpers, c []byte) error {
|
||||
p.Replacements = []types.Replacement{}
|
||||
err := yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range p.Replacements {
|
||||
if r.Source == nil {
|
||||
return ErrBadConfiguration{Msg: "`from` must be specified in one replacement"}
|
||||
}
|
||||
if r.Target == nil {
|
||||
return ErrBadConfiguration{Msg: "`to` must be specified in one replacement"}
|
||||
}
|
||||
if r.Source.ObjRef != nil && r.Source.Value != "" {
|
||||
return ErrBadConfiguration{Msg: "only one of fieldref and value is allowed in one replacement"}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform resources using configured replacements
|
||||
func (p *plugin) Transform(m resmap.ResMap) error {
|
||||
var err error
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1_test
|
||||
package replacement_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -11,12 +11,15 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
replv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/replacement/v1alpha1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/document/plugin/replacement"
|
||||
plugtypes "opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
func samplePlugin(t *testing.T) plugtypes.Plugin {
|
||||
plugin, err := replv1alpha1.New([]byte(`
|
||||
cfg := make(map[string]interface{})
|
||||
conf := `
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ReplacementTransformer
|
||||
metadata:
|
||||
@ -28,17 +31,15 @@ replacements:
|
||||
objref:
|
||||
kind: Deployment
|
||||
fieldrefs:
|
||||
- spec.template.spec.containers[name=nginx-latest].image`))
|
||||
- spec.template.spec.containers[name=nginx-latest].image`
|
||||
|
||||
err := yaml.Unmarshal([]byte(conf), &cfg)
|
||||
require.NoError(t, err)
|
||||
plugin, err := replacement.New(cfg)
|
||||
require.NoError(t, err)
|
||||
return plugin
|
||||
}
|
||||
|
||||
func TestMalformedConfig(t *testing.T) {
|
||||
_, err := replv1alpha1.New([]byte("--"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMalformedInput(t *testing.T) {
|
||||
plugin := samplePlugin(t)
|
||||
err := plugin.Run(strings.NewReader("--"), &bytes.Buffer{})
|
||||
@ -978,7 +979,7 @@ metadata:
|
||||
name: notImportantHere
|
||||
replacements:
|
||||
- source:
|
||||
value: 12345678
|
||||
value: "12345678"
|
||||
target:
|
||||
objref:
|
||||
kind: KubeadmControlPlane
|
||||
@ -1011,7 +1012,10 @@ spec:
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
plugin, err := replv1alpha1.New([]byte(tc.cfg))
|
||||
cfg := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(tc.cfg), &cfg)
|
||||
require.NoError(t, err)
|
||||
plugin, err := replacement.New(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := &bytes.Buffer{}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
https://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 (
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
// Find matching image declarations and replace
|
||||
// the name, tag and/or digest.
|
||||
type plugin struct {
|
||||
Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
|
||||
}
|
@ -27,28 +27,38 @@ import (
|
||||
"opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
// Registry contains factory functions for the available plugins
|
||||
var Registry = make(map[schema.GroupVersionKind]types.Factory)
|
||||
|
||||
func init() {
|
||||
replacement.RegisterPlugin(Registry)
|
||||
templater.RegisterPlugin(Registry)
|
||||
// DefaultPlugins returns map with plugin factories
|
||||
func DefaultPlugins() (map[schema.GroupVersionKind]types.Factory, error) {
|
||||
registry := make(map[schema.GroupVersionKind]types.Factory)
|
||||
if err := replacement.RegisterPlugin(registry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := templater.RegisterPlugin(registry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// ConfigureAndRun executes particular plugin based on group, version, kind
|
||||
// which have been specified in configuration file. Config file should be
|
||||
// supplied as a first element of args slice
|
||||
func ConfigureAndRun(pluginCfg []byte, in io.Reader, out io.Writer) error {
|
||||
var cfg unstructured.Unstructured
|
||||
if err := yaml.Unmarshal(pluginCfg, &cfg); err != nil {
|
||||
rawCfg := make(map[string]interface{})
|
||||
if err := yaml.Unmarshal(pluginCfg, &rawCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
pluginFactory, ok := Registry[cfg.GroupVersionKind()]
|
||||
uCfg := &unstructured.Unstructured{}
|
||||
uCfg.SetUnstructuredContent(rawCfg)
|
||||
registry, err := DefaultPlugins()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pluginFactory, ok := registry[uCfg.GroupVersionKind()]
|
||||
if !ok {
|
||||
return ErrPluginNotFound{PluginID: cfg.GroupVersionKind()}
|
||||
return ErrPluginNotFound{PluginID: uCfg.GroupVersionKind()}
|
||||
}
|
||||
|
||||
plugin, err := pluginFactory(pluginCfg)
|
||||
plugin, err := pluginFactory(rawCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -17,11 +17,17 @@ package templater
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
tmplv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/templater/v1alpha1"
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
// RegisterPlugin registers BareMetalHost generator plugin
|
||||
func RegisterPlugin(registry map[schema.GroupVersionKind]types.Factory) {
|
||||
registry[tmplv1alpha1.GetGVK()] = tmplv1alpha1.New
|
||||
func RegisterPlugin(registry map[schema.GroupVersionKind]types.Factory) error {
|
||||
obj := &airshipv1.Templater{}
|
||||
gvks, _, err := airshipv1.Scheme.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry[gvks[0]] = New
|
||||
return nil
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
package templater
|
||||
|
||||
import (
|
||||
"io"
|
||||
@ -20,33 +20,31 @@ import (
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
plugtypes "opendev.org/airship/airshipctl/pkg/document/plugin/types"
|
||||
)
|
||||
|
||||
// GetGVK returns group, version, kind object used to register version
|
||||
// of the plugin
|
||||
func GetGVK() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{
|
||||
Group: "airshipit.org",
|
||||
Version: "v1alpha1",
|
||||
Kind: "Templater",
|
||||
}
|
||||
type plugin struct {
|
||||
*airshipv1.Templater
|
||||
}
|
||||
|
||||
// New creates new instance of the plugin
|
||||
func New(cfg []byte) (plugtypes.Plugin, error) {
|
||||
t := &Templater{}
|
||||
if err := yaml.Unmarshal(cfg, t); err != nil {
|
||||
func New(obj map[string]interface{}) (plugtypes.Plugin, error) {
|
||||
cfg := &airshipv1.Templater{}
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
return &plugin{
|
||||
Templater: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Run templater plugin
|
||||
func (t *Templater) Run(_ io.Reader, out io.Writer) error {
|
||||
func (t *plugin) Run(_ io.Reader, out io.Writer) error {
|
||||
funcMap := sprig.TxtFuncMap()
|
||||
funcMap["toYaml"] = toYaml
|
||||
tmpl, err := template.New("tmpl").Funcs(funcMap).Parse(t.Template)
|
@ -12,7 +12,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1_test
|
||||
package templater_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -20,15 +20,11 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
tmplv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/templater/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document/plugin/templater"
|
||||
)
|
||||
|
||||
func TestMalformedConfig(t *testing.T) {
|
||||
_, err := tmplv1alpha1.New([]byte("--"))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestTemplater(t *testing.T) {
|
||||
testCases := []struct {
|
||||
cfg string
|
||||
@ -119,7 +115,10 @@ template: |
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
plugin, err := tmplv1alpha1.New([]byte(tc.cfg))
|
||||
cfg := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(tc.cfg), &cfg)
|
||||
require.NoError(t, err)
|
||||
plugin, err := templater.New(cfg)
|
||||
require.NoError(t, err)
|
||||
buf := &bytes.Buffer{}
|
||||
err = plugin.Run(nil, buf)
|
@ -25,4 +25,4 @@ type Plugin interface {
|
||||
|
||||
// Factory function for plugins. Functions of such type are used in the plugin
|
||||
// registry to instantiate a plugin object
|
||||
type Factory func([]byte) (Plugin, error)
|
||||
type Factory func(map[string]interface{}) (Plugin, error)
|
||||
|
Loading…
Reference in New Issue
Block a user