github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/pkg/kptpkg/init.go (about)

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kptpkg
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"html/template"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"github.com/GoogleContainerTools/kpt/internal/builtins"
    25  	"github.com/GoogleContainerTools/kpt/internal/pkg"
    26  	"github.com/GoogleContainerTools/kpt/internal/printer"
    27  	"github.com/GoogleContainerTools/kpt/internal/util/man"
    28  	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
    29  	"sigs.k8s.io/kustomize/kyaml/errors"
    30  	"sigs.k8s.io/kustomize/kyaml/filesys"
    31  	"sigs.k8s.io/kustomize/kyaml/kio/filters"
    32  	"sigs.k8s.io/kustomize/kyaml/yaml"
    33  )
    34  
    35  // Initializer defines capability to initialize a kpt package.
    36  type Initializer interface {
    37  	Initialize(ctx context.Context, pkg filesys.FileSystem, opts InitOptions) error
    38  }
    39  
    40  // InitOptions contains customization options for package initialization.
    41  type InitOptions struct {
    42  	PkgName string
    43  	PkgPath string
    44  	// RelPath is used purely for printing info relative to current working dir of user.
    45  	// It may or may not be same as PkgPath.
    46  	RelPath  string
    47  	Desc     string
    48  	Keywords []string
    49  	Site     string
    50  }
    51  
    52  // DefaultInitilizer implements Initializer interface.
    53  type DefaultInitializer struct{}
    54  
    55  func (i *DefaultInitializer) Initialize(
    56  	ctx context.Context,
    57  	fsys filesys.FileSystem,
    58  	opts InitOptions,
    59  ) error {
    60  	p, err := pkg.New(fsys, opts.PkgPath)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	var pkgName string
    66  	if opts.PkgName != "" {
    67  		pkgName = opts.PkgName
    68  	} else {
    69  		pkgName = string(p.DisplayPath)
    70  	}
    71  
    72  	up := string(p.UniquePath)
    73  	if !fsys.Exists(string(p.UniquePath)) {
    74  		return errors.Errorf("%s does not exist", p.UniquePath)
    75  	}
    76  
    77  	pr := printer.FromContextOrDie(ctx)
    78  
    79  	if !fsys.Exists(filepath.Join(up, kptfilev1.KptFileName)) {
    80  		pr.Printf("writing %s\n", filepath.Join(opts.RelPath, "Kptfile"))
    81  		k := kptfilev1.KptFile{
    82  			ResourceMeta: yaml.ResourceMeta{
    83  				ObjectMeta: yaml.ObjectMeta{
    84  					NameMeta: yaml.NameMeta{
    85  						Name: pkgName,
    86  					},
    87  					// mark Kptfile as local-config
    88  					Annotations: map[string]string{
    89  						filters.LocalConfigAnnotation: "true",
    90  					},
    91  				},
    92  			},
    93  			Info: &kptfilev1.PackageInfo{
    94  				Description: opts.Desc,
    95  				Site:        opts.Site,
    96  				Keywords:    opts.Keywords,
    97  			},
    98  		}
    99  
   100  		// serialize the gvk when writing the Kptfile
   101  		k.Kind = kptfilev1.TypeMeta.Kind
   102  		k.APIVersion = kptfilev1.TypeMeta.APIVersion
   103  
   104  		err = func() error {
   105  			f, err := fsys.Create(filepath.Join(up, kptfilev1.KptFileName))
   106  			if err != nil {
   107  				return err
   108  			}
   109  			defer f.Close()
   110  			e := yaml.NewEncoder(f)
   111  
   112  			defer e.Close()
   113  			return e.Encode(k)
   114  		}()
   115  		if err != nil {
   116  			return err
   117  		}
   118  	}
   119  
   120  	if !fsys.Exists(filepath.Join(up, man.ManFilename)) {
   121  		pr.Printf("writing %s\n", filepath.Join(opts.RelPath, man.ManFilename))
   122  		buff := &bytes.Buffer{}
   123  		t, err := template.New("man").Parse(manTemplate)
   124  		if err != nil {
   125  			return err
   126  		}
   127  		templateData := map[string]string{
   128  			"Name":        pkgName,
   129  			"Description": opts.Desc,
   130  		}
   131  
   132  		err = t.Execute(buff, templateData)
   133  		if err != nil {
   134  			return err
   135  		}
   136  
   137  		// Replace single quotes with backticks.
   138  		content := strings.ReplaceAll(buff.String(), "'", "`")
   139  
   140  		err = fsys.WriteFile(filepath.Join(up, man.ManFilename), []byte(content))
   141  		if err != nil {
   142  			return err
   143  		}
   144  	}
   145  
   146  	pkgContextPath := filepath.Join(up, builtins.PkgContextFile)
   147  	if !fsys.Exists(pkgContextPath) {
   148  		pr.Printf("writing %s\n", filepath.Join(opts.RelPath, builtins.PkgContextFile))
   149  		if err := fsys.WriteFile(pkgContextPath, []byte(builtins.AbstractPkgContext())); err != nil {
   150  			return err
   151  		}
   152  	}
   153  	return nil
   154  }
   155  
   156  // manTemplate is the content for the automatically generated README.md file.
   157  // It uses ' instead of ` since golang doesn't allow using ` in a raw string
   158  // literal. We do a replace on the content before printing.
   159  var manTemplate = `# {{.Name}}
   160  
   161  ## Description
   162  {{.Description}}
   163  
   164  ## Usage
   165  
   166  ### Fetch the package
   167  'kpt pkg get REPO_URI[.git]/PKG_PATH[@VERSION] {{.Name}}'
   168  Details: https://kpt.dev/reference/cli/pkg/get/
   169  
   170  ### View package content
   171  'kpt pkg tree {{.Name}}'
   172  Details: https://kpt.dev/reference/cli/pkg/tree/
   173  
   174  ### Apply the package
   175  '''
   176  kpt live init {{.Name}}
   177  kpt live apply {{.Name}} --reconcile-timeout=2m --output=table
   178  '''
   179  Details: https://kpt.dev/reference/cli/live/
   180  `