| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- Copyright © The ESO 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
- 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 main
- import (
- "context"
- "fmt"
- "os"
- "path/filepath"
- "github.com/spf13/cobra"
- corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime"
- "sigs.k8s.io/yaml"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
- "github.com/external-secrets/external-secrets/pkg/controllers/templating"
- "github.com/external-secrets/external-secrets/runtime/template"
- )
- // version is filled during build time.
- var version string
- var (
- templateFile string
- secretDataFile string
- outputFile string
- templateFromConfigMapFile string
- templateFromSecretFile string
- showVersion bool
- )
- func init() {
- rootCmd.AddCommand(templateCmd)
- templateCmd.Flags().StringVar(&templateFile, "source-templated-object", "", "Link to a file containing the object that contains the template")
- templateCmd.Flags().StringVar(&secretDataFile, "source-secret-data-file", "", "Link to a file containing secret data in form of map[string][]byte")
- templateCmd.Flags().StringVar(&templateFromConfigMapFile, "template-from-config-map", "", "Link to a file containing config map data for TemplateFrom.ConfigMap")
- templateCmd.Flags().StringVar(&templateFromSecretFile, "template-from-secret", "", "Link to a file containing config map data for TemplateFrom.Secret")
- templateCmd.Flags().StringVar(&outputFile, "output", "", "If set, the output will be written to this file")
- templateCmd.Flags().BoolVar(&showVersion, "version", false, "If set, only print the version and exit")
- }
- var templateCmd = &cobra.Command{
- Use: "template",
- Short: "given an input and a template provides an output",
- Long: `Given an input that mimics a secret's data section and a template it produces an output of the render template.`,
- RunE: templateRun,
- }
- func templateRun(_ *cobra.Command, _ []string) error {
- if version == "" {
- version = "0.0.0-dev"
- }
- if showVersion {
- fmt.Printf("version: %s\n", version)
- os.Exit(0)
- }
- ctx := context.Background()
- obj := &unstructured.Unstructured{}
- content, err := os.ReadFile(filepath.Clean(templateFile))
- if err != nil {
- return fmt.Errorf("could not read template file: %w", err)
- }
- if err := yaml.Unmarshal(content, obj); err != nil {
- return fmt.Errorf("could not unmarshal template: %w", err)
- }
- tmpl, err := fetchTemplateFromSourceObject(obj)
- if err != nil {
- return err
- }
- data := map[string][]byte{}
- sourceDataContent, err := os.ReadFile(filepath.Clean(secretDataFile))
- if err != nil {
- return fmt.Errorf("could not read source secret file: %w", err)
- }
- if err := yaml.Unmarshal(sourceDataContent, &data); err != nil {
- return fmt.Errorf("could not unmarshal secret: %w", err)
- }
- execute, err := template.EngineForVersion(tmpl.EngineVersion)
- if err != nil {
- return err
- }
- targetSecret := &corev1.Secret{}
- p := &templating.Parser{
- TargetSecret: targetSecret,
- DataMap: data,
- Exec: execute,
- }
- if err := setupFromConfigAndFromSecret(p); err != nil {
- return fmt.Errorf("could not setup from secret: %w", err)
- }
- if err := executeTemplate(ctx, p, tmpl); err != nil {
- return fmt.Errorf("could not render template: %w", err)
- }
- out := os.Stdout
- if outputFile != "" {
- f, err := os.Create(filepath.Clean(outputFile))
- if err != nil {
- return fmt.Errorf("could not create output file: %w", err)
- }
- defer func() {
- _ = f.Close()
- }()
- out = f
- }
- // display the resulting secret
- content, err = yaml.Marshal(targetSecret)
- if err != nil {
- return fmt.Errorf("could not marshal secret: %w", err)
- }
- _, err = fmt.Fprintln(out, string(content))
- return err
- }
- func fetchTemplateFromSourceObject(obj *unstructured.Unstructured) (*esv1.ExternalSecretTemplate, error) {
- var tmpl *esv1.ExternalSecretTemplate
- switch obj.GetKind() {
- case "ExternalSecret":
- es := &esv1.ExternalSecret{}
- if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, es); err != nil {
- return nil, err
- }
- tmpl = es.Spec.Target.Template
- case "PushSecret":
- ps := &v1alpha1.PushSecret{}
- if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, ps); err != nil {
- return nil, err
- }
- tmpl = ps.Spec.Template
- default:
- return nil, fmt.Errorf("unsupported template kind %s", obj.GetKind())
- }
- return tmpl, nil
- }
- func executeTemplate(ctx context.Context, p *templating.Parser, tmpl *esv1.ExternalSecretTemplate) error {
- // apply templates defined in template.templateFrom
- err := p.MergeTemplateFrom(ctx, "default", tmpl)
- if err != nil {
- return fmt.Errorf("could not merge template: %w", err)
- }
- // apply data templates
- // NOTE: explicitly defined template.data templates take precedence over templateFrom
- err = p.MergeMap(tmpl.Data, esv1.TemplateTargetData)
- if err != nil {
- return fmt.Errorf("could not merge data: %w", err)
- }
- // apply templates for labels
- // NOTE: this only works for v2 templates
- err = p.MergeMap(tmpl.Metadata.Labels, esv1.TemplateTargetLabels)
- if err != nil {
- return fmt.Errorf("could not merge labels: %w", err)
- }
- // apply template for annotations
- // NOTE: this only works for v2 templates
- err = p.MergeMap(tmpl.Metadata.Annotations, esv1.TemplateTargetAnnotations)
- if err != nil {
- return fmt.Errorf("could not merge annotations: %w", err)
- }
- return err
- }
- func setupFromConfigAndFromSecret(p *templating.Parser) error {
- if templateFromConfigMapFile != "" {
- var configMap corev1.ConfigMap
- configMapContent, err := os.ReadFile(filepath.Clean(templateFromConfigMapFile))
- if err != nil {
- return err
- }
- if err := yaml.Unmarshal(configMapContent, &configMap); err != nil {
- return fmt.Errorf("could not unmarshal configmap: %w", err)
- }
- p.TemplateFromConfigMap = &configMap
- }
- if templateFromSecretFile != "" {
- var secret corev1.Secret
- secretContent, err := os.ReadFile(filepath.Clean(templateFromSecretFile))
- if err != nil {
- return err
- }
- if err := yaml.Unmarshal(secretContent, &secret); err != nil {
- return fmt.Errorf("could not unmarshal secret: %w", err)
- }
- p.TemplateFromSecret = &secret
- }
- return nil
- }
|