Files
kustomize/pkg/commands/build/build.go
Jerome Brette 579995dc8a Address simultaneous transformation of name and namespace
Namereference handler needs to address simulatenous change of
name and namespace in ClusterRoleBinding for instance.
2019-07-16 18:17:33 -05:00

239 lines
5.9 KiB
Go

// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"io"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/plugins"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/target"
"sigs.k8s.io/kustomize/v3/plugin/builtin"
"sigs.k8s.io/yaml"
)
// Options contain the options for running a build
type Options struct {
kustomizationPath string
outputPath string
loadRestrictor loader.LoadRestrictorFunc
outOrder reorderOutput
}
// NewOptions creates a Options object
func NewOptions(p, o string) *Options {
return &Options{
kustomizationPath: p,
outputPath: o,
loadRestrictor: loader.RestrictionRootOnly,
}
}
var examples = `
To generate the resources specified in 'someDir/kustomization.yaml', run
kustomize build someDir
The default argument to 'build' is '.' (the current working directory).
The argument can be a URL resolving to a directory
with a kustomization.yaml file, e.g.
kustomize build \
github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6
The URL should be formulated as described at
https://github.com/hashicorp/go-getter#url-format
`
// NewCmdBuild creates a new build command.
func NewCmdBuild(
out io.Writer, fSys fs.FileSystem,
v ifc.Validator, rf *resmap.Factory,
ptf resmap.PatchFactory) *cobra.Command {
var o Options
pluginConfig := plugins.DefaultPluginConfig()
pl := plugins.NewLoader(pluginConfig, rf)
cmd := &cobra.Command{
Use: "build {path}",
Short: "Print configuration per contents of " + pgmconfig.KustomizationFileNames[0],
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuild(out, v, fSys, rf, ptf, pl)
},
}
cmd.Flags().StringVarP(
&o.outputPath,
"output", "o", "",
"If specified, write the build output to this path.")
loader.AddFlagLoadRestrictor(cmd.Flags())
plugins.AddFlagEnablePlugins(
cmd.Flags(), &pluginConfig.Enabled)
addFlagReorderOutput(cmd.Flags())
cmd.AddCommand(NewCmdBuildPrune(out, v, fSys, rf, ptf, pl))
return cmd
}
// Validate validates build command.
func (o *Options) Validate(args []string) (err error) {
if len(args) > 1 {
return errors.New(
"specify one path to " + pgmconfig.KustomizationFileNames[0])
}
if len(args) == 0 {
o.kustomizationPath = loader.CWD
} else {
o.kustomizationPath = args[0]
}
o.loadRestrictor, err = loader.ValidateFlagLoadRestrictor()
if err != nil {
return err
}
o.outOrder, err = validateFlagReorderOutput()
return
}
// RunBuild runs build command.
func (o *Options) RunBuild(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
m, err := kt.MakeCustomizedResMap()
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) RunBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
m, err := kt.MakePruneConfigMap()
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) emitResources(
out io.Writer, fSys fs.FileSystem, m resmap.ResMap) error {
if o.outputPath != "" && fSys.IsDir(o.outputPath) {
return writeIndividualFiles(fSys, o.outputPath, m)
}
if o.outOrder == legacy {
// Done this way just to show how overall sorting
// can be performed by a plugin. This particular
// plugin doesn't require configuration; just make
// it and call transform.
builtin.NewLegacyOrderTransformerPlugin().Transform(m)
}
res, err := m.AsYaml()
if err != nil {
return err
}
if o.outputPath != "" {
return fSys.WriteFile(o.outputPath, res)
}
_, err = out.Write(res)
return err
}
func NewCmdBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) *cobra.Command {
var o Options
cmd := &cobra.Command{
Use: "alpha-inventory [path]",
Short: "Print the inventory object which contains a list of all other objects",
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuildPrune(out, v, fSys, rf, ptf, pl)
},
}
return cmd
}
func writeIndividualFiles(
fSys fs.FileSystem, folderPath string, m resmap.ResMap) error {
byNamespace := m.GroupedByCurrentNamespace()
for namespace, resList := range byNamespace {
for _, res := range resList {
fName := fileName(res)
if len(byNamespace) > 1 {
fName = strings.ToLower(namespace) + "_" + fName
}
err := writeFile(fSys, folderPath, fName, res)
if err != nil {
return err
}
}
}
for _, res := range m.NonNamespaceable() {
err := writeFile(fSys, folderPath, fileName(res), res)
if err != nil {
return err
}
}
return nil
}
func fileName(res *resource.Resource) string {
return strings.ToLower(res.GetGvk().String()) +
"_" + strings.ToLower(res.GetName()) + ".yaml"
}
func writeFile(
fSys fs.FileSystem, path, fName string, res *resource.Resource) error {
out, err := yaml.Marshal(res.Map())
if err != nil {
return err
}
return fSys.WriteFile(filepath.Join(path, fName), out)
}