diff --git a/go.mod b/go.mod index 3dbb918d4..f8ba2d17b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,9 @@ module opendev.org/airship/airshipctl go 1.13 require ( + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible github.com/Microsoft/go-winio v0.4.12 // indirect github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0 @@ -16,6 +19,8 @@ require ( github.com/go-git/go-git/v5 v5.0.0 github.com/gorilla/mux v1.7.4 // indirect github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect + github.com/huandu/xstrings v1.3.1 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/spf13/cobra v0.0.6 diff --git a/go.sum b/go.sum index 2ed5dd569..3a7cafc36 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -327,6 +333,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= @@ -402,6 +410,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= @@ -409,6 +419,8 @@ github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/pkg/document/plugin/run.go b/pkg/document/plugin/run.go index 6ffe9f422..1a38bee0e 100644 --- a/pkg/document/plugin/run.go +++ b/pkg/document/plugin/run.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "opendev.org/airship/airshipctl/pkg/document/plugin/replacement" + "opendev.org/airship/airshipctl/pkg/document/plugin/templater" "opendev.org/airship/airshipctl/pkg/document/plugin/types" "opendev.org/airship/airshipctl/pkg/environment" ) @@ -32,6 +33,7 @@ var Registry = make(map[schema.GroupVersionKind]types.Factory) func init() { replacement.RegisterPlugin(Registry) + templater.RegisterPlugin(Registry) } // ConfigureAndRun executes particular plugin based on group, version, kind diff --git a/pkg/document/plugin/templater/init.go b/pkg/document/plugin/templater/init.go new file mode 100644 index 000000000..734e34404 --- /dev/null +++ b/pkg/document/plugin/templater/init.go @@ -0,0 +1,27 @@ +/* + 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 templater + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + + tmplv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/templater/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 +} diff --git a/pkg/document/plugin/templater/v1alpha1/templater.go b/pkg/document/plugin/templater/v1alpha1/templater.go new file mode 100644 index 000000000..3976a0ecb --- /dev/null +++ b/pkg/document/plugin/templater/v1alpha1/templater.go @@ -0,0 +1,55 @@ +/* + 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 ( + "io" + "text/template" + + "github.com/Masterminds/sprig" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/yaml" + + plugtypes "opendev.org/airship/airshipctl/pkg/document/plugin/types" + "opendev.org/airship/airshipctl/pkg/environment" +) + +// 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", + } +} + +// New creates new instance of the plugin +func New(_ *environment.AirshipCTLSettings, cfg []byte) (plugtypes.Plugin, error) { + t := &Templater{} + if err := yaml.Unmarshal(cfg, t); err != nil { + return nil, err + } + return t, nil +} + +// Run templater plugin +func (t *Templater) Run(_ io.Reader, out io.Writer) error { + tmpl, err := template.New("tmpl").Funcs(sprig.TxtFuncMap()).Parse(t.Template) + if err != nil { + return err + } + return tmpl.Execute(out, t.Values) +} diff --git a/pkg/document/plugin/templater/v1alpha1/templater_test.go b/pkg/document/plugin/templater/v1alpha1/templater_test.go new file mode 100644 index 000000000..7d2ee80ba --- /dev/null +++ b/pkg/document/plugin/templater/v1alpha1/templater_test.go @@ -0,0 +1,98 @@ +/* + 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_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + tmplv1alpha1 "opendev.org/airship/airshipctl/pkg/document/plugin/templater/v1alpha1" +) + +func TestMalformedConfig(t *testing.T) { + _, err := tmplv1alpha1.New(nil, []byte("--")) + assert.Error(t, err) +} + +func TestTemplater(t *testing.T) { + testCases := []struct { + cfg string + expectedOut string + expectedErr string + }{ + { + cfg: ` +apiVersion: airshipit.org/v1alpha1 +kind: Templater +metadata: + name: notImportantHere +values: + hosts: + - macAddress: 00:aa:bb:cc:dd + name: node-1 + - macAddress: 00:aa:bb:cc:ee + name: node-2 +template: | + {{ range .hosts -}} + --- + apiVersion: metal3.io/v1alpha1 + kind: BareMetalHost + metadata: + name: {{ .name }} + spec: + bootMACAddress: {{ .macAddress }} + {{ end -}}`, + expectedOut: `--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + name: node-1 +spec: + bootMACAddress: 00:aa:bb:cc:dd +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + name: node-2 +spec: + bootMACAddress: 00:aa:bb:cc:ee +`, + }, + { + cfg: ` +apiVersion: airshipit.org/v1alpha1 +kind: Templater +metadata: + name: notImportantHere +template: | + {{ end }`, + expectedErr: "template: tmpl:1: unexpected \"}\" in end", + }, + } + + for _, tc := range testCases { + plugin, err := tmplv1alpha1.New(nil, []byte(tc.cfg)) + require.NoError(t, err) + buf := &bytes.Buffer{} + err = plugin.Run(nil, buf) + if tc.expectedErr != "" { + assert.EqualError(t, err, tc.expectedErr) + } + assert.Equal(t, tc.expectedOut, buf.String()) + } +} diff --git a/pkg/document/plugin/templater/v1alpha1/types.go b/pkg/document/plugin/templater/v1alpha1/types.go new file mode 100644 index 000000000..f2ebf72c5 --- /dev/null +++ b/pkg/document/plugin/templater/v1alpha1/types.go @@ -0,0 +1,31 @@ +/* + 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" +) + +// Templater plugin for airship document model +type Templater struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Values contains map with object parameters to render + Values map[string]interface{} `json:"values,omitempty"` + // Template field is used to specify actual go-template which is going + // to be used to render the object defined in Spec field + Template string `json:"template,omitempty"` +}