Compare commits

..

2 Commits

Author SHA1 Message Date
Jeffrey Regan
f8412aa3d3 Have kustomize CLI depend on kustomize Go API v3.3.1 2019-10-08 16:26:20 -07:00
Jeffrey Regan
3c9d828f04 Have kustomize CLI depend on kustomize Go API v3.3.0 2019-10-07 16:09:15 -07:00
4171 changed files with 8477 additions and 788932 deletions

3
.gitignore vendored
View File

@@ -5,9 +5,6 @@
*.so
*.dylib
.idea
*.iml
# Test binary, build with `go test -c`
*.test

View File

@@ -4,38 +4,25 @@ run:
linters:
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dupl
- goconst
- gocyclo
- gofmt
- goimports
- golint
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- staticcheck
- structcheck
# stylecheck demands that acronyms not be treated as words
# in camelCase, so JsonOp become JSONOp, etc. Yuck.
# - stylecheck
- typecheck
- unconvert
- unused
- unparam
- varcheck
linters-settings:
dupl:
threshold: 400
lll:
threshold: 400
lll:
line-length: 170
gocyclo:
min-complexity: 15

View File

@@ -1,10 +1,7 @@
os:
- linux
- osx
# TODO: Speed up the slowness of the osx travis runs
# Maybe cache brew installs?
#
# TODO: Uncomment when some gets the tests running on Windows.
# TODO: Uncomment when tests running on Windows.
# - windows
addons:
@@ -23,7 +20,7 @@ git:
language: go
go:
- "1.13"
- "1.12"
go_import_path: sigs.k8s.io/kustomize
@@ -34,9 +31,7 @@ before_install:
install: true
script:
- ./travis/verify-deps.sh
- make verify-kustomize
- ./travis/kyaml-pre-commit.sh
- ./travis/pre-commit.sh
# TBD. Suppressing for now.
notifications:

View File

@@ -6,15 +6,11 @@ _As contributors and maintainers of this project, and in the interest of fosteri
## Getting Started
Dev guides:
- [Mac](docs/macDevGuide.md)
We have full documentation on how to get started contributing here:
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers
## Mentorship

251
Makefile
View File

@@ -1,242 +1,37 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
#
# Makefile for kustomize CLI and API.
BIN_NAME=kustomize
MYGOBIN := $(shell go env GOPATH)/bin
SHELL := /bin/bash
export PATH := $(MYGOBIN):$(PATH)
COVER_FILE=coverage.out
.PHONY: all
all: verify-kustomize
export GO111MODULE=on
.PHONY: verify-kustomize
verify-kustomize: \
lint-kustomize \
test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-latest
all: test build
# Other builds in this repo might want a different linter version.
# Without one Makefile to rule them all, the different makes
# cannot assume that golanci-lint is at the version they want
# since everything uses the same implicit GOPATH.
# This installs in a temp dir to avoid overwriting someone else's
# linter, then installs in MYGOBIN with a new name.
# Version pinned by api/go.mod
$(MYGOBIN)/golangci-lint-kustomize:
( \
set -e; \
export GOBIN=$$(mktemp -d) \
cd api; \
GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint; \
mv $$GOBIN/golangci-lint $(MYGOBIN)/golangci-lint-kustomize \
)
test: generate-code test-lint test-go
# Version pinned by api/go.mod
$(MYGOBIN)/mdrip:
cd api; \
go install github.com/monopole/mdrip
test-go:
go test -v ./...
# Version pinned by api/go.mod
$(MYGOBIN)/stringer:
cd api; \
go install golang.org/x/tools/cmd/stringer
test-lint:
golangci-lint run ./...
# Version pinned by api/go.mod
$(MYGOBIN)/goimports:
cd api; \
go install golang.org/x/tools/cmd/goimports
generate-code:
./plugin/generateBuiltins.sh $(GOPATH)
# To pin pluginator, use this recipe instead:
# cd api;
# go install sigs.k8s.io/kustomize/pluginator/v2
$(MYGOBIN)/pluginator:
cd pluginator; \
go install .
build:
go build -o $(BIN_NAME) cmd/kustomize/main.go
# Install kustomize from whatever is checked out.
$(MYGOBIN)/kustomize:
cd kustomize; \
go install .
install:
go install $(PWD)/cmd/kustomize
.PHONY: install-tools
install-tools: \
$(MYGOBIN)/goimports \
$(MYGOBIN)/golangci-lint-kustomize \
$(MYGOBIN)/mdrip \
$(MYGOBIN)/pluginator \
$(MYGOBIN)/stringer
cover:
# The plugin directory eludes coverage, and is therefore omitted
go test ./pkg/... ./k8sdeps/... ./internal/... -coverprofile=$(COVER_FILE) && \
go tool cover -html=$(COVER_FILE)
### Begin kustomize plugin rules.
#
# The rules to deal with builtin plugins are a bit
# complicated because
#
# - Every builtin plugin is a Go plugin -
# meaning it gets its own module directory
# (outside of the api module) with Go
# code in a 'main' package per Go plugin rules.
# - kustomize locates plugins using the
# 'apiVersion' and 'kind' fields from the
# plugin config file.
# - k8s wants CamelCase in 'kind' fields.
# - The module name (the last name in the path)
# must be the lowercased 'kind' of the
# plugin because Go and related tools
# demand lowercase in import paths, but
# allow CamelCase in file names.
# - the generated code must live in the api
# module (it's linked into the api).
# Where all generated builtin plugin code should go.
pGen=api/builtins
# Where the builtin Go plugin modules live.
pSrc=plugin/builtin
_builtinplugins = \
AnnotationsTransformer.go \
ConfigMapGenerator.go \
HashTransformer.go \
ImageTagTransformer.go \
InventoryTransformer.go \
LabelTransformer.go \
LegacyOrderTransformer.go \
NamespaceTransformer.go \
PatchJson6902Transformer.go \
PatchStrategicMergeTransformer.go \
PatchTransformer.go \
PrefixSuffixTransformer.go \
ReplicaCountTransformer.go \
SecretGenerator.go
# Maintaining this explicit list of generated files, and
# adding it as a dependency to a few targets, to assure
# they get recreated if deleted. The rules below on how
# to make them don't, by themselves, assure they will be
# recreated if deleted.
builtinplugins = $(patsubst %,$(pGen)/%,$(_builtinplugins))
# These rules are verbose, but assure that if a source file
# is modified, the corresponding generated file, and only
# that file, will be recreated.
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
$(pGen)/InventoryTransformer.go: $(pSrc)/inventorytransformer/InventoryTransformer.go
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
$(pGen)/PatchTransformer.go: $(pSrc)/patchtransformer/PatchTransformer.go
$(pGen)/PrefixSuffixTransformer.go: $(pSrc)/prefixsuffixtransformer/PrefixSuffixTransformer.go
$(pGen)/ReplicaCountTransformer.go: $(pSrc)/replicacounttransformer/ReplicaCountTransformer.go
$(pGen)/SecretGenerator.go: $(pSrc)/secretgenerator/SecretGenerator.go
# The (verbose but portable) Makefile way to convert to lowercase.
toLowerCase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
$(pGen)/%.go: $(MYGOBIN)/pluginator
@echo "generating $*"
( \
set -e; \
cd $(pSrc)/$(call toLowerCase,$*); \
go generate .; \
cd ../../../$(pGen); \
$(MYGOBIN)/goimports -w $*.go \
)
# Target is for debugging.
.PHONY: generate-kustomize-builtin-plugins
generate-kustomize-builtin-plugins: $(builtinplugins)
### End kustomize plugin rules.
.PHONY: lint-kustomize
lint-kustomize: install-tools $(builtinplugins)
cd api; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
cd kustomize; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
cd pluginator; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
.PHONY: test-unit-kustomize-api
test-unit-kustomize-api: $(builtinplugins)
cd api; go test ./...
.PHONY: test-unit-kustomize-plugins
test-unit-kustomize-plugins:
./hack/testUnitKustomizePlugins.sh
.PHONY: test-unit-kustomize-cli
test-unit-kustomize-cli:
cd kustomize; go test ./...
.PHONY: test-unit-kustomize-all
test-unit-kustomize-all: \
test-unit-kustomize-api \
test-unit-kustomize-cli \
test-unit-kustomize-plugins
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-kustomize-against-latest: $(MYGOBIN)/mdrip
( \
set -e; \
/bin/rm -f $(MYGOBIN)/kustomize; \
echo "Installing kustomize from latest."; \
GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v3; \
./hack/testExamplesAgainstKustomize.sh latest; \
echo "Reinstalling kustomize from HEAD."; \
cd kustomize; go install .; \
)
# linux only.
# This is for testing an example plugin that
# uses kubeval for validation.
# Don't want to add a hard dependence in go.mod file
# to github.com/instrumenta/kubeval.
# Instead, download the binary.
$(MYGOBIN)/kubeval:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
tar xf kubeval-linux-amd64.tar.gz; \
mv kubeval $(MYGOBIN); \
rm -rf $$d; \
)
# linux only.
# This is for testing an example plugin that
# uses helm to inflate a chart for subsequent kustomization.
# Don't want to add a hard dependence in go.mod file
# to helm.
# Instead, download the binary.
$(MYGOBIN)/helm:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz; \
tar -xvzf helm-v2.13.1-linux-amd64.tar.gz; \
mv linux-amd64/helm $(MYGOBIN); \
rm -rf $$d \
)
.PHONY: clean
clean:
go clean --cache
rm -f $(builtinplugins)
rm -f $(MYGOBIN)/pluginator
rm -f $(MYGOBIN)/kustomize
rm -f $(MYGOBIN)/golangci-lint-kustomize
go clean
rm -f $(BIN_NAME)
rm -f $(COVER_FILE)
.PHONY: nuke
nuke: clean
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io
.PHONY: test build install clean generate-code test-go test-lint cover

View File

@@ -28,9 +28,9 @@ Since [v1.14][kubectl announcement] the kustomize build system has been included
| kubectl version | kustomize version |
|---------|--------|
| v1.16.0 | [v2.0.3](/../../tree/v2.0.3) |
| v1.15.x | [v2.0.3](/../../tree/v2.0.3) |
| v1.14.x | [v2.0.3](/../../tree/v2.0.3) |
| v1.16.0 | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
| v1.15.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
| v1.14.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
For examples and guides for using the kubectl integration please see the [kubectl book] or the [kubernetes documentation].
@@ -167,7 +167,7 @@ is governed by the [Kubernetes Code of Conduct].
[eschewed feature list]: docs/eschewedFeatures.md
[imageBase]: docs/images/base.jpg
[imageOverlay]: docs/images/overlay.jpg
[kind/feature]: /../../labels/kind%2Ffeature
[kind/feature]: https://github.com/kubernetes-sigs/kustomize/labels/kind%2Ffeature
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl book]: https://kubectl.docs.kubernetes.io/pages/app_customization/introduction.html
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
@@ -175,12 +175,12 @@ is governed by the [Kubernetes Code of Conduct].
[kustomization]: docs/glossary.md#kustomization
[overlay]: docs/glossary.md#overlay
[overlays]: docs/glossary.md#overlay
[release page]: /../../releases
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
[resource]: docs/glossary.md#resource
[resources]: docs/glossary.md#resource
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
[variant]: docs/glossary.md#variant
[variants]: docs/glossary.md#variant
[v2.0.3]: /../../releases/tag/v2.0.3
[v2.1.0]: /../../releases/tag/v2.1.0
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[v2.1.0]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.1.0
[workflows]: docs/workflows.md

View File

@@ -1,8 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package builtins holds code generated from the builtin plugins.
// The "builtin" plugins are written as normal plugins and can
// be used as such, but they are also used to generate the code
// in this package so they can be statically linked to client code.
package builtins

View File

@@ -1,15 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"io"
"os"
)
// File groups the basic os.File methods.
type File interface {
io.ReadWriteCloser
Stat() (os.FileInfo, error)
}

View File

@@ -1,34 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"os"
"time"
)
var _ os.FileInfo = fileInfo{}
// fileInfo implements os.FileInfo for a fileInMemory instance.
type fileInfo struct {
node *fsNode
}
// Name returns the name of the file
func (fi fileInfo) Name() string { return fi.node.Name() }
// Size returns the size of the file
func (fi fileInfo) Size() int64 { return fi.node.Size() }
// Mode returns the file mode
func (fi fileInfo) Mode() os.FileMode { return 0777 }
// ModTime returns a bogus time
func (fi fileInfo) ModTime() time.Time { return time.Time{} }
// IsDir returns true if it is a directory
func (fi fileInfo) IsDir() bool { return fi.node.isNodeADir() }
// Sys should return underlying data source, but it now returns nil
func (fi fileInfo) Sys() interface{} { return nil }

View File

@@ -1,557 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/pkg/errors"
)
var _ File = &fsNode{}
var _ FileSystem = &fsNode{}
// fsNode is either a file or a directory.
type fsNode struct {
// What node owns me?
parent *fsNode
// Value to return as the Name() when the
// parent is nil.
nilParentName string
// A directory mapping names to nodes.
// If dir is nil, then self node is a file.
// If dir is non-nil, then self node is a directory,
// albeit possibly an empty directory.
dir map[string]*fsNode
// if this node is a file, this is the content.
content []byte
// if this node is a file, this tracks whether or
// not it is "open".
open bool
}
// MakeEmptyDirInMemory returns an empty directory.
// The paths of nodes in this object will never
// report a leading Separator, meaning they
// aren't "absolute" in the sense defined by
// https://golang.org/pkg/path/filepath/#IsAbs.
func MakeEmptyDirInMemory() *fsNode {
return &fsNode{
dir: make(map[string]*fsNode),
}
}
// MakeFsInMemory returns an empty 'file system'.
// The paths of nodes in this object will always
// report a leading Separator, meaning they
// are "absolute" in the sense defined by
// https://golang.org/pkg/path/filepath/#IsAbs.
// This is a relevant difference when using Walk,
// Glob, Match, etc.
func MakeFsInMemory() FileSystem {
return &fsNode{
nilParentName: Separator,
dir: make(map[string]*fsNode),
}
}
// Name returns the name of the node.
func (n *fsNode) Name() string {
if n.parent == nil {
// Unable to lookup name in parent.
return n.nilParentName
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir")
}
for key, value := range n.parent.dir {
if value == n {
return key
}
}
log.Fatal("unable to find fsNode name")
return ""
}
// Path returns the full path to the node.
func (n *fsNode) Path() string {
if n.parent == nil {
return n.nilParentName
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir, structural error")
}
return filepath.Join(n.parent.Path(), n.Name())
}
// mySplit trims trailing separators from the directory
// result of filepath.Split.
func mySplit(s string) (string, string) {
dName, fName := filepath.Split(s)
return StripTrailingSeps(dName), fName
}
func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
parent := n
dName, fileName := mySplit(name)
if dName != "" {
parent, err = parent.addDir(dName)
if err != nil {
return nil, err
}
}
if !isLegalFileNameForCreation(fileName) {
return nil, fmt.Errorf(
"illegal name '%s' in file creation", fileName)
}
result, ok := parent.dir[fileName]
if ok {
// File already exists; overwrite it.
result.content = c
return result, nil
}
result = &fsNode{
content: c,
parent: parent,
}
parent.dir[fileName] = result
return result, nil
}
// Create implements FileSystem.
// Create makes an empty file.
func (n *fsNode) Create(path string) (result File, err error) {
return n.AddFile(path, []byte{})
}
// WriteFile implements FileSystem.
func (n *fsNode) WriteFile(path string, d []byte) error {
_, err := n.AddFile(path, d)
return err
}
// AddFile adds a file and any necessary containing
// directories to the node.
func (n *fsNode) AddFile(
name string, c []byte) (result *fsNode, err error) {
if n.dir == nil {
return nil, fmt.Errorf(
"cannot add a file to a non-directory '%s'", n.Name())
}
return n.addFile(cleanQueryPath(name), c)
}
func (n *fsNode) addDir(path string) (result *fsNode, err error) {
parent := n
dName, subDirName := mySplit(path)
if dName != "" {
parent, err = n.addDir(dName)
if err != nil {
return nil, err
}
}
switch subDirName {
case "", SelfDir:
return n, nil
case ParentDir:
if n.parent == nil {
return nil, fmt.Errorf(
"cannot add a directory above '%s'", n.Path())
}
return n.parent, nil
default:
if !isLegalFileNameForCreation(subDirName) {
return nil, fmt.Errorf(
"illegal name '%s' in directory creation", subDirName)
}
result, ok := parent.dir[subDirName]
if ok {
if result.isNodeADir() {
// it's already there.
return result, nil
}
return nil, fmt.Errorf(
"cannot make dir '%s'; a file of that name already exists in '%s'",
subDirName, parent.Name())
}
result = &fsNode{
dir: make(map[string]*fsNode),
parent: parent,
}
parent.dir[subDirName] = result
return result, nil
}
}
// Mkdir implements FileSystem.
// Mkdir creates a directory.
func (n *fsNode) Mkdir(path string) error {
_, err := n.AddDir(path)
return err
}
// MkdirAll implements FileSystem.
// MkdirAll creates a directory.
func (n *fsNode) MkdirAll(path string) error {
_, err := n.AddDir(path)
return err
}
// AddDir adds a directory to the node, not complaining
// if it is already there.
func (n *fsNode) AddDir(path string) (result *fsNode, err error) {
if n.dir == nil {
return nil, fmt.Errorf(
"cannot add a directory to file node '%s'", n.Name())
}
return n.addDir(cleanQueryPath(path))
}
// CleanedAbs implements FileSystem.
func (n *fsNode) CleanedAbs(path string) (ConfirmedDir, string, error) {
node, err := n.Find(path)
if err != nil {
return "", "", errors.Wrap(err, "unable to clean")
}
if node == nil {
return "", "", fmt.Errorf("'%s' doesn't exist", path)
}
if node.isNodeADir() {
return ConfirmedDir(node.Path()), "", nil
}
return ConfirmedDir(node.parent.Path()), node.Name(), nil
}
// Exists implements FileSystem.
// Exists returns true if the path exists.
func (n *fsNode) Exists(path string) bool {
if !n.isNodeADir() {
return n.Name() == path
}
result, err := n.Find(path)
if err != nil {
return false
}
return result != nil
}
func cleanQueryPath(path string) string {
// Always ignore leading separator?
// Remember that filepath.Clean returns "." if
// given an empty string argument.
return filepath.Clean(StripLeadingSeps(path))
}
// Find finds the given node, else nil if not found.
// Return error on structural/argument errors.
func (n *fsNode) Find(path string) (*fsNode, error) {
if !n.isNodeADir() {
return nil, fmt.Errorf("can only find inside a dir")
}
if path == "" {
// Special case; check *before* cleaning and *before*
// comparison to nilParentName.
return nil, nil
}
if (n.parent == nil && path == n.nilParentName) || path == SelfDir {
// Special case
return n, nil
}
return n.findIt(cleanQueryPath(path))
}
func (n *fsNode) findIt(path string) (result *fsNode, err error) {
parent := n
dName, item := mySplit(path)
if dName != "" {
parent, err = n.findIt(dName)
if err != nil {
return nil, err
}
if parent == nil {
// all done, target doesn't exist.
return nil, nil
}
}
if !parent.isNodeADir() {
return nil, fmt.Errorf("'%s' is not a directory", parent.Path())
}
return parent.dir[item], nil
}
// RemoveAll implements FileSystem.
// RemoveAll removes an item and everything it contains.
func (n *fsNode) RemoveAll(path string) error {
result, err := n.Find(path)
if err != nil {
return err
}
if result == nil {
return fmt.Errorf("cannot find '%s' to remove it", path)
}
return result.Remove()
}
// Remove drop the node, and everything it contains, from its parent.
func (n *fsNode) Remove() error {
if n.parent == nil {
return fmt.Errorf("cannot remove a root node")
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir")
}
for key, value := range n.parent.dir {
if value == n {
delete(n.parent.dir, key)
return nil
}
}
log.Fatal("unable to find self in parent")
return nil
}
// isNodeADir returns true if the node is a directory.
// Cannot collide with the poorly named "IsDir".
func (n *fsNode) isNodeADir() bool {
return n.dir != nil
}
// IsDir implements FileSystem.
// IsDir returns true if the argument resolves
// to a directory rooted at the node.
func (n *fsNode) IsDir(path string) bool {
result, err := n.Find(path)
if err != nil || result == nil {
return false
}
return result.isNodeADir()
}
// Size returns the size of the node.
func (n *fsNode) Size() int64 {
if n.isNodeADir() {
return int64(len(n.dir))
}
return int64(len(n.content))
}
// Open implements FileSystem.
// Open opens the node for reading (just marks it).
func (n *fsNode) Open(path string) (File, error) {
result, err := n.Find(path)
if err != nil {
return nil, err
}
if result == nil {
return nil, fmt.Errorf("cannot find '%s' to open it", path)
}
result.open = true
return result, nil
}
// Close marks the node closed.
func (n *fsNode) Close() error {
n.open = false
return nil
}
// ReadFile implements FileSystem.
func (n *fsNode) ReadFile(path string) (c []byte, err error) {
result, err := n.Find(path)
if err != nil {
return nil, err
}
if result == nil {
return nil, fmt.Errorf("cannot find '%s' to read it", path)
}
c = make([]byte, len(result.content))
_, err = result.Read(c)
return c, err
}
// Read returns the content of the file node.
func (n *fsNode) Read(d []byte) (c int, err error) {
if n.isNodeADir() {
return 0, fmt.Errorf(
"cannot read content from non-file '%s'", n.Path())
}
return copy(d, n.content), nil
}
// Write saves the contents of the argument to the file node.
func (n *fsNode) Write(p []byte) (c int, err error) {
if n.isNodeADir() {
return 0, fmt.Errorf(
"cannot write content to non-file '%s'", n.Path())
}
n.content = make([]byte, len(p))
return copy(n.content, p), nil
}
// ContentMatches returns true if v matches fake file's content.
func (n *fsNode) ContentMatches(v []byte) bool {
return bytes.Equal(v, n.content)
}
// GetContent the content of a fake file.
func (n *fsNode) GetContent() []byte {
return n.content
}
// Stat returns an instance of FileInfo.
func (n *fsNode) Stat() (os.FileInfo, error) {
return fileInfo{node: n}, nil
}
// Walk implements FileSystem.
func (n *fsNode) Walk(path string, walkFn filepath.WalkFunc) error {
result, err := n.Find(path)
if err != nil {
return err
}
if result == nil {
return fmt.Errorf("cannot find '%s' to walk it", path)
}
return result.WalkMe(walkFn)
}
// Walk runs the given walkFn on each node.
func (n *fsNode) WalkMe(walkFn filepath.WalkFunc) error {
fi, err := n.Stat()
// always visit self first
err = walkFn(n.Path(), fi, err)
if !n.isNodeADir() {
// it's a file, so nothing more to do
return err
}
// process self as a directory
if err == filepath.SkipDir {
return nil
}
// Walk is supposed to visit in lexical order.
for _, k := range n.sortedDirEntries() {
if err := n.dir[k].WalkMe(walkFn); err != nil {
if err == filepath.SkipDir {
// stop processing this directory
break
}
// bail out completely
return err
}
}
return nil
}
func (n *fsNode) sortedDirEntries() []string {
keys := make([]string, len(n.dir))
i := 0
for k := range n.dir {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// FileCount returns a count of files.
// Directories, empty or otherwise, not counted.
func (n *fsNode) FileCount() int {
count := 0
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
count++
}
return nil
})
return count
}
func (n *fsNode) DebugPrint() {
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if info.IsDir() {
if info.Size() == 0 {
fmt.Println("empty dir: " + path)
}
} else {
fmt.Println(" file: " + path)
}
return nil
})
}
var legalFileNamePattern = regexp.MustCompile("^[a-zA-Z0-9-_.]+$")
// This rules enforced here should be simpler and tighter
// than what's allowed on a real OS.
// Should be fine for testing or in-memory purposes.
func isLegalFileNameForCreation(n string) bool {
if n == "" || n == SelfDir || !legalFileNamePattern.MatchString(n) {
return false
}
return !strings.Contains(n, ParentDir)
}
// RegExpGlob returns a list of file paths matching the regexp.
// Excludes directories.
func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
var result []string
var expression = regexp.MustCompile(pattern)
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
if expression.MatchString(path) {
result = append(result, path)
}
}
return nil
})
sort.Strings(result)
return result, nil
}
// Glob implements FileSystem.
// Glob returns the list of file paths matching
// per filepath.Match semantics, i.e. unlike RegExpGlob,
// Match("foo/a*") will not match sub-sub directories of foo.
// This is how /bin/ls behaves.
func (n *fsNode) Glob(pattern string) ([]string, error) {
var result []string
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
match, err := filepath.Match(pattern, path)
if err != nil {
return err
}
if match {
result = append(result, path)
}
}
return nil
})
sort.Strings(result)
return result, nil
}

View File

@@ -1,788 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"testing"
)
const content = `
Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
`
const shortContent = "hi"
var topCases = []pathCase{
{
what: "dotdot",
arg: ParentDir,
errStr: "illegal name '..' in file creation",
},
{
what: "empty",
arg: "",
name: "",
errStr: "illegal name '.' in file creation",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer yet",
arg: filepath.Join("longer", "foo", "bar", "beans", "bob"),
name: "bob",
path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
},
{
what: "tricky",
arg: filepath.Join("bob", ParentDir, "sally"),
name: "sally",
path: "sally",
},
{
what: "trickier",
arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
name: "jean",
path: "jean",
},
}
func TestMakeEmptyDirInMemory(t *testing.T) {
n := MakeEmptyDirInMemory()
if !n.isNodeADir() {
t.Fatalf("not a directory")
}
if n.Size() != 0 {
t.Fatalf("unexpected size %d", n.Size())
}
if n.Name() != "" {
t.Fatalf("unexpected name '%s'", n.Name())
}
if n.Path() != "" {
t.Fatalf("unexpected path '%s'", n.Path())
}
runBasicOperations(
t, "MakeEmptyDirInMemory", false, topCases, n)
}
func TestMakeFsInMemory(t *testing.T) {
runBasicOperations(
t, "MakeFsInMemory", true, topCases, MakeFsInMemory())
}
//nolint:gocyclo
func runBasicOperations(
t *testing.T, tName string, isFSysRooted bool,
cases []pathCase, fSys FileSystem) {
buff := make([]byte, 500)
for _, c := range cases {
err := fSys.WriteFile(c.arg, []byte(content))
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error writing to '%s'!", c.what, c.arg)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if !fSys.Exists(c.path) {
t.Fatalf("%s; expect existence of '%s'", c.what, c.path)
}
stuff, err := fSys.ReadFile(c.path)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(stuff) != content {
t.Fatalf("%s; unexpected content '%s'", c.what, stuff)
}
f, err := fSys.Open(c.arg)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
fi, err := f.Stat()
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if fi.Name() != c.name {
t.Fatalf("%s; expected name '%s', got '%s'", c.what, c.name, fi.Name())
}
count, err := f.Read(buff)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(buff[:count]) != content {
t.Fatalf("%s; unexpected buff '%s'", c.what, buff)
}
count, err = f.Write([]byte(shortContent))
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if count != len(shortContent) {
t.Fatalf("%s; unexpected count: %d", c.what, len(shortContent))
}
stuff, err = fSys.ReadFile(c.path)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(stuff) != shortContent {
t.Fatalf("%s; unexpected content '%s'", c.what, stuff)
}
}
var actualPaths []string
var err error
prefix := ""
{
root := SelfDir
if isFSysRooted {
root = Separator
prefix = Separator
}
err = fSys.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if !info.IsDir() {
actualPaths = append(actualPaths, path)
}
return nil
})
}
if err != nil {
t.Fatalf("unexpected error %v", err)
}
var expectedPaths []string
for _, c := range cases {
if c.errStr == "" {
expectedPaths = append(expectedPaths, prefix+c.path)
}
}
sort.Strings(expectedPaths)
assertEqualStringSlices(t, expectedPaths, actualPaths, tName)
}
type pathCase struct {
what string
arg string
name string
path string
errStr string
}
func TestAddDir(t *testing.T) {
cases := []pathCase{
{
what: "dotdot",
arg: ParentDir,
errStr: "cannot add a directory above ''",
},
{
what: "empty",
arg: "",
name: "",
path: "",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer yet",
arg: filepath.Join("longer", "foo", "bar", "beans", "bob"),
name: "bob",
path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
},
{
what: "tricky",
arg: filepath.Join("bob", ParentDir, "sally"),
name: "sally",
path: "sally",
},
{
what: "trickier",
arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
name: "jean",
path: "jean",
},
}
for _, c := range cases {
n := MakeEmptyDirInMemory()
f, err := n.AddDir(c.arg)
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error!", c.what)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf(
"%s; expected error with '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
checkNode(t, c.what, f, c.name, 0, true, c.path)
checkOsStat(t, c.what, f, f.Name(), 0, true)
}
}
var bagOfCases = []pathCase{
{
what: "empty",
arg: "",
errStr: "illegal name '.' in file creation",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer",
arg: filepath.Join("longer", "sally"),
name: "sally",
path: filepath.Join("longer", "sally"),
},
{
what: "even longer",
arg: filepath.Join("longer", "than", "the", "other", "bob"),
name: "bob",
path: filepath.Join("longer", "than", "the", "other", "bob"),
},
{
what: "even longer",
arg: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
name: "bob",
path: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
},
}
func TestAddFile(t *testing.T) {
n := MakeEmptyDirInMemory()
if n.FileCount() != 0 {
t.Fatalf("expected no files, got %d", n.FileCount())
}
expectedFileCount := 0
for _, c := range bagOfCases {
f, err := n.AddFile(c.arg, []byte(content))
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error!", c.what)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error %v", c.what, err)
}
checkNode(t, c.what, f, c.name, len(content), false, c.path)
checkOsStat(t, c.what, f, f.Name(), len(content), false)
result, err := n.Find(c.arg)
if err != nil {
t.Fatalf("%s; unexpected find error %v", c.what, err)
}
if result != f {
t.Fatalf("%s; unexpected find result %v", c.what, result)
}
result, err = n.Find(filepath.Join("longer", "bogus"))
if err != nil {
t.Fatalf("%s; unexpected find error %v", c.what, err)
}
if result != nil {
t.Fatalf("%s; unexpected find result %v", c.what, result)
}
expectedFileCount++
fc := n.FileCount()
if fc != expectedFileCount {
t.Fatalf("expected file count %d, got %d",
expectedFileCount, fc)
}
}
}
func checkNode(
t *testing.T, what string, f *fsNode, name string,
size int, isDir bool, path string) {
if f.isNodeADir() != isDir {
t.Fatalf("%s; unexpected isNodeADir = %v", what, f.isNodeADir())
}
if f.Size() != int64(size) {
t.Fatalf("%s; unexpected size %d", what, f.Size())
}
if name != f.Name() {
t.Fatalf("%s; expected name '%s', got '%s'", what, name, f.Name())
}
if path != f.Path() {
t.Fatalf("%s; expected path '%s', got '%s'", what, path, f.Path())
}
}
func checkOsStat(
t *testing.T, what string, f File, name string,
size int, isDir bool) {
info, err := f.Stat()
if err != nil {
t.Fatalf("%s; unexpected stat error %v", what, err)
}
if info.IsDir() != isDir {
t.Fatalf("%s; unexpected info.isNodeADir = %v", what, info.IsDir())
}
if info.Size() != int64(size) {
t.Fatalf("%s; unexpected info.size %d", what, info.Size())
}
if info.Name() != name {
t.Fatalf("%s; expected name '%s', got info.Name '%s'", what, name, info.Name())
}
}
var bunchOfFiles = []struct {
path string
addAsDir bool
}{
{
path: filepath.Join("b", "e", "a", "c", "g"),
},
{
path: filepath.Join("z", "r", "a", "b", "g"),
},
{
path: filepath.Join("b", "q", "a", "c", "g"),
},
{
path: filepath.Join("b", "a", "a", "m", "g"),
addAsDir: true,
},
{
path: filepath.Join("b", "w"),
},
{
path: filepath.Join("b", "d", "a", "c", "m"),
},
{
path: filepath.Join("b", "d", "z"),
},
{
path: filepath.Join("b", "d", "y"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "n"),
},
{
path: filepath.Join("b", "d", "x"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "o"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "m"),
},
{
path: filepath.Join("b", "d", "a", "c", "i"),
addAsDir: true,
},
{
path: filepath.Join("x"),
},
{
path: filepath.Join("y"),
},
{
path: filepath.Join("b", "d", "a", "c", "i", "beans"),
},
{
path: filepath.Join("b", "d", "a", "c", "r", "w"),
addAsDir: true,
},
{
path: filepath.Join("b", "d", "a", "c", "u"),
},
}
func makeLoadedFileTree(t *testing.T) *fsNode {
n := MakeEmptyDirInMemory()
var err error
expectedFileCount := 0
for _, item := range bunchOfFiles {
if item.addAsDir {
_, err = n.AddDir(item.path)
} else {
_, err = n.AddFile(item.path, []byte(content))
expectedFileCount++
}
if err != nil {
t.Fatalf("unexpected error %v", err)
}
}
fc := n.FileCount()
if fc != expectedFileCount {
t.Fatalf("expected file count %d, got %d",
expectedFileCount, fc)
}
return n
}
func TestWalkMe(t *testing.T) {
n := makeLoadedFileTree(t)
var actualPaths []string
err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if info.IsDir() {
if info.Name() == "ignore" {
return filepath.SkipDir
}
} else {
actualPaths = append(actualPaths, path)
}
return nil
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
var expectedPaths []string
for _, c := range bunchOfFiles {
if !c.addAsDir && !strings.Contains(c.path, "ignore") {
expectedPaths = append(expectedPaths, c.path)
}
}
sort.Strings(expectedPaths)
assertEqualStringSlices(t, expectedPaths, actualPaths, "testWalkMe")
}
func TestRemove(t *testing.T) {
n := makeLoadedFileTree(t)
orgCount := n.FileCount()
// Remove the "ignore" directory and everything below it.
path := filepath.Join("b", "d", "ignore")
result, err := n.Find(path)
if err != nil {
t.Fatalf("%s; unexpected error %v", path, err)
}
if result == nil {
t.Fatalf("%s; expected to find '%s'", path, path)
}
if !result.isNodeADir() {
t.Fatalf("%s; expected to find a directory", path)
}
err = result.Remove()
if err != nil {
t.Fatalf("%s; unable to remove: %v", path, err)
}
result, err = n.Find(path)
if err != nil {
// Just because it's gone doesn't mean error.
t.Fatalf("%s; unexpected error %v", path, err)
}
if result != nil {
t.Fatalf("%s; should not have been able to find '%s'", path, path)
}
// There were three files below "ignore".
orgCount -= 3
// Now drop one more for a total of four dropped.
result, _ = n.Find(filepath.Join("y"))
err = result.Remove()
if err != nil {
t.Fatalf("%s; unable to remove: %v", path, err)
}
orgCount -= 1
fc := n.FileCount()
if fc != orgCount {
t.Fatalf("expected file count %d, got %d",
orgCount, fc)
}
}
func TestExists(t *testing.T) {
n := makeLoadedFileTree(t)
path := filepath.Join("b", "d", "a")
if !n.Exists(path) {
t.Fatalf("expected existence at %s", path)
}
if !n.IsDir(path) {
t.Fatalf("expected directory at %s", path)
}
}
func TestRegExpGlob(t *testing.T) {
n := makeLoadedFileTree(t)
expected := []string{
filepath.Join("b", "d", "a", "c", "i", "beans"),
filepath.Join("b", "d", "a", "c", "m"),
filepath.Join("b", "d", "a", "c", "u"),
filepath.Join("b", "d", "ignore", "c", "m"),
filepath.Join("b", "d", "ignore", "c", "n"),
filepath.Join("b", "d", "ignore", "c", "o"),
filepath.Join("b", "d", "x"),
filepath.Join("b", "d", "y"),
filepath.Join("b", "d", "z"),
}
paths, err := n.RegExpGlob("b/d/*")
if err != nil {
t.Fatalf("glob error: %v", err)
}
assertEqualStringSlices(t, expected, paths, "glob test")
}
func TestGlob(t *testing.T) {
n := makeLoadedFileTree(t)
expected := []string{
filepath.Join("b", "d", "x"),
filepath.Join("b", "d", "y"),
filepath.Join("b", "d", "z"),
}
paths, err := n.Glob("b/d/*")
if err != nil {
t.Fatalf("glob error: %v", err)
}
assertEqualStringSlices(t, expected, paths, "glob test")
}
func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) {
if len(expected) != len(actual) {
t.Fatalf(
"%s; unequal sizes; len(expected)=%d, len(actual)=%d\n%+v\n%+v\n",
message, len(expected), len(actual), expected, actual)
}
for i := range expected {
if expected[i] != actual[i] {
t.Fatalf(
"%s; unequal entries; expected=%s, actual=%s",
message, expected[i], actual[i])
}
}
}
func TestFind(t *testing.T) {
cases := []struct {
what string
arg string
expectDir bool
expectFile bool
errStr string
}{
{
what: "garbage",
arg: "///1(*&SA",
},
{
what: "simple",
arg: "bob",
},
{
what: "no directory",
arg: filepath.Join("b", "rrrrrr"),
},
{
what: "is a directory",
arg: filepath.Join("b", "d", "ignore"),
expectDir: true,
},
{
what: "longer, ending in file",
arg: filepath.Join("b", "d", "x"),
expectFile: true,
},
{
what: "moar longer, ending in file",
arg: filepath.Join("b", "d", "a", "c", "u"),
expectFile: true,
},
{
what: "directory",
arg: filepath.Join("b"),
expectDir: true,
},
{
// Querying for the empty string could
// 1) be an error,
// 2) return no result (and no error) as with
// any illegal and therefore non-existent
// file name,
// 3) return the node itself, like running
// 'ls' with no argument.
// Going with option 2 (no result, no error),
// since at this low level it makes more sense
// if the results for the empty string query
// differ from the results for the "." query.
what: "empty name",
arg: "",
},
{
what: "self dir",
arg: SelfDir,
expectDir: true,
},
{
what: "parent dir - doesn't exist",
arg: ParentDir,
},
{
what: "many parents - doesn't exist",
arg: filepath.Join(ParentDir, ParentDir, ParentDir),
},
}
n := makeLoadedFileTree(t)
for _, item := range cases {
result, err := n.Find(item.arg)
if item.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error", item.what)
}
if !strings.Contains(err.Error(), item.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
item.what, item.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", item.what, err)
}
if result == nil {
if item.expectDir {
t.Fatalf(
"%s; expected to find directory '%s'", item.what, item.arg)
}
if item.expectFile {
t.Fatalf(
"%s; expected to find file '%s'", item.what, item.arg)
}
continue
}
if item.expectDir {
if !result.isNodeADir() {
t.Fatalf(
"%s; expected '%s' to be a directory", item.what, item.arg)
}
continue
}
if item.expectFile {
if result.isNodeADir() {
t.Fatalf("%s; expected '%s' to be a file", item.what, item.arg)
}
continue
}
t.Fatalf(
"%s; expected nothing for '%s', but got '%s'",
item.what, item.arg, result.Path())
}
}
func TestCleanedAbs(t *testing.T) {
cases := []struct {
what string
full string
cDir string
name string
errStr string
}{
{
what: "empty",
full: "",
errStr: "doesn't exist",
},
{
what: "simple",
full: "bob",
errStr: "'bob' doesn't exist",
},
{
what: "no directory",
full: filepath.Join("b", "rrrrrr"),
errStr: "'b/rrrrrr' doesn't exist",
},
{
what: "longer, ending in file",
full: filepath.Join("b", "d", "x"),
cDir: filepath.Join("b", "d"),
name: "x",
},
{
what: "moar longer, ending in file",
full: filepath.Join("b", "d", "a", "c", "u"),
cDir: filepath.Join("b", "d", "a", "c"),
name: "u",
},
{
what: "directory",
full: filepath.Join("b", "d"),
cDir: filepath.Join("b", "d"),
name: "",
},
}
n := makeLoadedFileTree(t)
for _, item := range cases {
cDir, name, err := n.CleanedAbs(item.full)
if item.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error", item.what)
}
if !strings.Contains(err.Error(), item.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
item.what, item.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", item.what, err)
}
if cDir != ConfirmedDir(item.cDir) {
t.Fatalf("%s; expected cDir=%s, got '%s'", item.what, item.cDir, cDir)
}
if name != item.name {
t.Fatalf("%s; expected name=%s, got '%s'", item.what, item.name, name)
}
}
}

View File

@@ -1,30 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import "path/filepath"
// RootedPath returns a rooted path, e.g. "/foo/bar" as
// opposed to "foo/bar".
func RootedPath(elem ...string) string {
return Separator + filepath.Join(elem...)
}
// StripTrailingSeps trims trailing filepath separators from input.
func StripTrailingSeps(s string) string {
k := len(s)
for k > 0 && s[k-1] == filepath.Separator {
k--
}
return s[:k]
}
// StripLeadingSeps trims leading filepath separators from input.
func StripLeadingSeps(s string) string {
k := 0
for k < len(s) && s[k] == filepath.Separator {
k++
}
return s[k:]
}

View File

@@ -1,205 +0,0 @@
package filesys_test
import (
"path/filepath"
"testing"
. "sigs.k8s.io/kustomize/api/filesys"
)
// Confirm behavior of filepath.Match
func TestFilePathMatch(t *testing.T) {
cases := []struct {
pattern string
path string
expected bool
}{
{
pattern: "*e*",
path: "hey",
expected: true,
},
{
pattern: "*e*",
path: "hay",
expected: false,
},
{
pattern: "*e*",
path: filepath.Join("h", "e", "y"),
expected: false,
},
{
pattern: "*/e/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "h/e/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/e/y",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/*/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/*/*",
path: filepath.Join("h", "e", "y", "there"),
expected: false,
},
{
pattern: "*/*/*/t*e",
path: filepath.Join("h", "e", "y", "there"),
expected: true,
},
}
for _, item := range cases {
match, err := filepath.Match(item.pattern, item.path)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if match != item.expected {
t.Fatalf("'%s' '%s' %v\n", item.pattern, item.path, match)
}
}
}
// Confirm behavior of filepath.Split
func TestFilePathSplit(t *testing.T) {
cases := []struct {
full string
dir string
file string
}{
{
full: "",
dir: "",
file: "",
},
{
full: SelfDir,
dir: "",
file: SelfDir,
},
{
full: "rabbit.jpg",
dir: "",
file: "rabbit.jpg",
},
{
full: "/beans",
dir: "/",
file: "beans",
},
{
full: "/home/foo/bar",
dir: "/home/foo/",
file: "bar",
},
{
full: "/usr/local/",
dir: "/usr/local/",
file: "",
},
{
full: "/usr//local//go",
dir: "/usr//local//",
file: "go",
},
}
for _, p := range cases {
dir, file := filepath.Split(p.full)
if dir != p.dir || file != p.file {
t.Fatalf(
"in '%s',\ngot dir='%s' (expected '%s'),\n got file='%s' (expected %s).",
p.full, dir, p.dir, file, p.file)
}
}
}
func TestStripTrailingSeps(t *testing.T) {
cases := []struct {
full string
rem string
}{
{
full: "foo",
rem: "foo",
},
{
full: "",
rem: "",
},
{
full: "foo/",
rem: "foo",
},
{
full: "foo///bar///",
rem: "foo///bar",
},
{
full: "/////",
rem: "",
},
{
full: "/",
rem: "",
},
}
for _, p := range cases {
dir := StripTrailingSeps(p.full)
if dir != p.rem {
t.Fatalf(
"in '%s', got dir='%s' (expected '%s')",
p.full, dir, p.rem)
}
}
}
func TestStripLeadingSeps(t *testing.T) {
cases := []struct {
full string
rem string
}{
{
full: "foo",
rem: "foo",
},
{
full: "",
rem: "",
},
{
full: "/foo",
rem: "foo",
},
{
full: "///foo///bar///",
rem: "foo///bar///",
},
{
full: "/////",
rem: "",
},
{
full: "/",
rem: "",
},
}
for _, p := range cases {
dir := StripLeadingSeps(p.full)
if dir != p.rem {
t.Fatalf(
"in '%s', got dir='%s' (expected '%s')",
p.full, dir, p.rem)
}
}
}

View File

@@ -1,17 +0,0 @@
module sigs.k8s.io/kustomize/api
go 1.13
require (
github.com/evanphx/json-patch v4.5.0+incompatible
github.com/go-openapi/spec v0.19.4
github.com/golangci/golangci-lint v1.21.0
github.com/monopole/mdrip v1.0.0
github.com/pkg/errors v0.8.1
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff
gopkg.in/yaml.v2 v2.2.4
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
sigs.k8s.io/kustomize/pluginator/v2 v2.0.0
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0
sigs.k8s.io/yaml v1.1.0
)

View File

@@ -1,15 +0,0 @@
FROM golang:1.11 AS build
ARG GO111MODULE=on
WORKDIR /go/src/sigs.k8s.io/kustomize/api/internal/crawl
COPY . /go/src/sigs.k8s.io/kustomize//api/internal/crawl
RUN go mod download
RUN CGO_ENABLED=0 go install -v ./cmd/crawler/crawler.go
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /go/bin/crawler /
ENTRYPOINT ["/crawler"]
CMD []

View File

@@ -1,93 +0,0 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
"sigs.k8s.io/kustomize/api/internal/crawl/crawler"
"sigs.k8s.io/kustomize/api/internal/crawl/crawler/github"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
"sigs.k8s.io/kustomize/api/internal/crawl/httpclient"
"sigs.k8s.io/kustomize/api/internal/crawl/index"
"github.com/gomodule/redigo/redis"
)
const (
githubAccessTokenVar = "GITHUB_ACCESS_TOKEN"
redisCacheURL = "REDIS_CACHE_URL"
redisKeyURL = "REDIS_KEY_URL"
retryCount = 3
)
func main() {
githubToken := os.Getenv(githubAccessTokenVar)
if githubToken == "" {
fmt.Printf("Must set the variable '%s' to make github requests.\n",
githubAccessTokenVar)
return
}
ctx := context.Background()
idx, err := index.NewKustomizeIndex(ctx)
if err != nil {
fmt.Printf("Could not create an index: %v\n", err)
return
}
cacheURL := os.Getenv(redisCacheURL)
query := []byte(`{ "query":{ "match_all":{} } }`)
it := idx.IterateQuery(query, 10000, 60*time.Second)
docs := make(crawler.CrawlSeed, 0)
for it.Next() {
for _, hit := range it.Value().Hits.Hits {
docs = append(docs, hit.Document.Copy())
}
}
if err := it.Err(); err != nil {
fmt.Printf("Error iterating: %v\n", err)
}
cache, err := redis.DialURL(cacheURL)
clientCache := &http.Client{}
if err != nil {
fmt.Printf("Error: redis could not make a connection: %v\n", err)
} else {
clientCache = httpclient.NewClient(cache)
}
ghCrawler := github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml")),
)
crawler.CrawlFromSeed(ctx, docs, []crawler.Crawler{ghCrawler},
// Converter takes in a plain document and processes it for the
// index.
func(d *doc.Document) (crawler.CrawledDocument, error) {
kdoc := doc.KustomizationDocument{
Document: *d,
}
err := kdoc.ParseYAML()
return &kdoc, err
},
// IndexFunc updates the value in the index.
func(cdoc crawler.CrawledDocument, crwlr crawler.Crawler) error {
switch d := cdoc.(type) {
case *doc.KustomizationDocument:
fmt.Println("Inserting: ", d.ID(), d)
_, err := idx.Put(d.ID(), d)
return err
default:
return fmt.Errorf("type %T not supported", d)
}
},
)
}

View File

@@ -1,2 +0,0 @@
<ADD YOUR GITHUB PERSONAL ACCESS TOKEN HERE WITHOUT A TRAILING NEWLINE>
Run: printf "<your-token>" > github_api_secret.txt

View File

@@ -1,43 +0,0 @@
apiVersion: elasticsearch.cloud.google.com/v1alpha1
kind: ESCluster
metadata:
name: esbasic
spec:
plugin:
pluginList:
- repository-gcs
- ingest-user-agent
- ingest-geoip
config:
env:
example: test
nodegroups:
- name: di
replicas: 2
data: true
ingest: true
config:
jvm:
- Djava.net.preferIPv4Stack=true
- Xms2g
- Xmx2g
es:
path.repo: '["/tmp/es_backup_basic"]'
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
es/nodegroup: di
resources:
requests:
memory: 3Gi
limits:
memory: 3Gi
- name: m
replicas: 2
master: true
config:
es:
path.repo: '["/tmp/es_backup_basic"]'

View File

@@ -1,393 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elastic/go-elasticsearch/v6 v6.8.2 h1:rp5DGrd63V5c6nHLjF6QEXUpZSvs0+QM3ld7m9VhV2g=
github.com/elastic/go-elasticsearch/v6 v6.8.2/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.19.1/go.mod h1:2CEc4Fxx3vxDv7g8DyXkHCBF73AOzAymcJAprs2vCps=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
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/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matoous/godox v0.0.0-20190910121045-032ad8106c86/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
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=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monopole/mdrip v1.0.0/go.mod h1:N1/ppRG9CaPeUKAUHZ3dUlfOT81lTpKZLkyhCvTETwM=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/securego/gosec v0.0.0-20190912120752-140048b2a218/go.mod h1:q6oYAujd2qyeU4cJqIri4LBIgdHXGvxWHZ1E29HNFRE=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.3/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5laocGx/5bkDSSLrFUsa5s=
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911230505-6bfd74cf029c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/api v0.2.0 h1:e++6JpysnnlUbHmFrv6jvfF5rFlgQ103bS1DO7r5bWA=
sigs.k8s.io/kustomize/api v0.2.0/go.mod h1:zVtMg179jW1gr74jo9fc2Ac9dLYLTZZThc3DDb9lDW4=
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 h1:otg4dLFc03c3gzl+2CV8GPGcd1kk8wjXwD+UhhcCn5I=
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -1,10 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package builtinconfig provides legacy methods for
// configuring builtin plugins from a common config file.
// As a user, its best to configure plugins individually
// with plugin config files specified in the `transformers:`
// or `generators:` field, than to use this legacy
// configuration technique.
package builtinconfig

View File

@@ -1,42 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinconfig
import (
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/yaml"
)
// loadDefaultConfig returns a TranformerConfig
// object from a list of files.
func loadDefaultConfig(
ldr ifc.Loader, paths []string) (*TransformerConfig, error) {
result := &TransformerConfig{}
for _, path := range paths {
data, err := ldr.Load(path)
if err != nil {
return nil, err
}
t, err := makeTransformerConfigFromBytes(data)
if err != nil {
return nil, err
}
result, err = result.Merge(t)
if err != nil {
return nil, err
}
}
return result, nil
}
// makeTransformerConfigFromBytes returns a TransformerConfig object from bytes
func makeTransformerConfigFromBytes(data []byte) (*TransformerConfig, error) {
var t TransformerConfig
err := yaml.Unmarshal(data, &t)
if err != nil {
return nil, err
}
t.sortFields()
return &t, nil
}

View File

@@ -1,46 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinconfig
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
)
func TestLoadDefaultConfigsFromFiles(t *testing.T) {
fSys := filesys.MakeFsInMemory()
err := fSys.WriteFile("config.yaml", []byte(`
namePrefix:
- path: nameprefix/path
kind: SomeKind
`))
if err != nil {
t.Fatal(err)
}
ldr, err := loader.NewLoader(
loader.RestrictionRootOnly, filesys.Separator, fSys)
if err != nil {
t.Fatal(err)
}
tCfg, err := loadDefaultConfig(ldr, []string{"config.yaml"})
if err != nil {
t.Fatal(err)
}
expected := &TransformerConfig{
NamePrefix: []types.FieldSpec{
{
Gvk: resid.Gvk{Kind: "SomeKind"},
Path: "nameprefix/path",
},
},
}
if !reflect.DeepEqual(tCfg, expected) {
t.Fatalf("expected %v\n but go6t %v\n", expected, tCfg)
}
}

View File

@@ -1,148 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinconfig
import (
"log"
"sort"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
"sigs.k8s.io/kustomize/api/types"
)
// TransformerConfig holds the data needed to perform transformations.
type TransformerConfig struct {
NamePrefix types.FsSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
NameSuffix types.FsSlice `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
NameSpace types.FsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
CommonLabels types.FsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
CommonAnnotations types.FsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"`
VarReference types.FsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"`
Images types.FsSlice `json:"images,omitempty" yaml:"images,omitempty"`
Replicas types.FsSlice `json:"replicas,omitempty" yaml:"replicas,omitempty"`
}
// MakeEmptyConfig returns an empty TransformerConfig object
func MakeEmptyConfig() *TransformerConfig {
return &TransformerConfig{}
}
// MakeDefaultConfig returns a default TransformerConfig.
func MakeDefaultConfig() *TransformerConfig {
c, err := makeTransformerConfigFromBytes(
builtinpluginconsts.GetDefaultFieldSpecs())
if err != nil {
log.Fatalf("Unable to make default transformconfig: %v", err)
}
return c
}
// MakeTransformerConfig returns a merger of custom config,
// if any, with default config.
func MakeTransformerConfig(
ldr ifc.Loader, paths []string) (*TransformerConfig, error) {
t1 := MakeDefaultConfig()
if len(paths) == 0 {
return t1, nil
}
t2, err := loadDefaultConfig(ldr, paths)
if err != nil {
return nil, err
}
return t1.Merge(t2)
}
// sortFields provides determinism in logging, tests, etc.
func (t *TransformerConfig) sortFields() {
sort.Sort(t.NamePrefix)
sort.Sort(t.NameSpace)
sort.Sort(t.CommonLabels)
sort.Sort(t.CommonAnnotations)
sort.Sort(t.NameReference)
sort.Sort(t.VarReference)
sort.Sort(t.Images)
sort.Sort(t.Replicas)
}
// AddPrefixFieldSpec adds a FieldSpec to NamePrefix
func (t *TransformerConfig) AddPrefixFieldSpec(fs types.FieldSpec) (err error) {
t.NamePrefix, err = t.NamePrefix.MergeOne(fs)
return err
}
// AddSuffixFieldSpec adds a FieldSpec to NameSuffix
func (t *TransformerConfig) AddSuffixFieldSpec(fs types.FieldSpec) (err error) {
t.NameSuffix, err = t.NameSuffix.MergeOne(fs)
return err
}
// AddLabelFieldSpec adds a FieldSpec to CommonLabels
func (t *TransformerConfig) AddLabelFieldSpec(fs types.FieldSpec) (err error) {
t.CommonLabels, err = t.CommonLabels.MergeOne(fs)
return err
}
// AddAnnotationFieldSpec adds a FieldSpec to CommonAnnotations
func (t *TransformerConfig) AddAnnotationFieldSpec(fs types.FieldSpec) (err error) {
t.CommonAnnotations, err = t.CommonAnnotations.MergeOne(fs)
return err
}
// AddNamereferenceFieldSpec adds a NameBackReferences to NameReference
func (t *TransformerConfig) AddNamereferenceFieldSpec(
nbrs NameBackReferences) (err error) {
t.NameReference, err = t.NameReference.mergeOne(nbrs)
return err
}
// Merge merges two TransformerConfigs objects into
// a new TransformerConfig object
func (t *TransformerConfig) Merge(input *TransformerConfig) (
merged *TransformerConfig, err error) {
if input == nil {
return t, nil
}
merged = &TransformerConfig{}
merged.NamePrefix, err = t.NamePrefix.MergeAll(input.NamePrefix)
if err != nil {
return nil, err
}
merged.NameSuffix, err = t.NameSuffix.MergeAll(input.NameSuffix)
if err != nil {
return nil, err
}
merged.NameSpace, err = t.NameSpace.MergeAll(input.NameSpace)
if err != nil {
return nil, err
}
merged.CommonAnnotations, err = t.CommonAnnotations.MergeAll(
input.CommonAnnotations)
if err != nil {
return nil, err
}
merged.CommonLabels, err = t.CommonLabels.MergeAll(input.CommonLabels)
if err != nil {
return nil, err
}
merged.VarReference, err = t.VarReference.MergeAll(input.VarReference)
if err != nil {
return nil, err
}
merged.NameReference, err = t.NameReference.mergeAll(input.NameReference)
if err != nil {
return nil, err
}
merged.Images, err = t.Images.MergeAll(input.Images)
if err != nil {
return nil, err
}
merged.Replicas, err = t.Replicas.MergeAll(input.Replicas)
if err != nil {
return nil, err
}
merged.sortFields()
return merged, nil
}

View File

@@ -1,37 +0,0 @@
// Code generated by "stringer -type=BuiltinPluginType"; DO NOT EDIT.
package builtinhelpers
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unknown-0]
_ = x[AnnotationsTransformer-1]
_ = x[ConfigMapGenerator-2]
_ = x[HashTransformer-3]
_ = x[ImageTagTransformer-4]
_ = x[InventoryTransformer-5]
_ = x[LabelTransformer-6]
_ = x[LegacyOrderTransformer-7]
_ = x[NamespaceTransformer-8]
_ = x[PatchJson6902Transformer-9]
_ = x[PatchStrategicMergeTransformer-10]
_ = x[PatchTransformer-11]
_ = x[PrefixSuffixTransformer-12]
_ = x[ReplicaCountTransformer-13]
_ = x[SecretGenerator-14]
}
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerInventoryTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 101, 117, 139, 159, 183, 213, 229, 252, 275, 290}
func (i BuiltinPluginType) String() string {
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {
return "BuiltinPluginType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _BuiltinPluginType_name[_BuiltinPluginType_index[i]:_BuiltinPluginType_index[i+1]]
}

View File

@@ -1,79 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package loader_test
import (
"testing"
"sigs.k8s.io/kustomize/api/filesys"
. "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
)
const (
//nolint:gosec
secretGenerator = `
apiVersion: builtin
kind: SecretGenerator
metadata:
name: secretGenerator
name: mySecret
behavior: merge
envFiles:
- a.env
- b.env
valueFiles:
- longsecret.txt
literals:
- FRUIT=apple
- VEGETABLE=carrot
`
someServiceGenerator = `
apiVersion: someteam.example.com/v1
kind: SomeServiceGenerator
metadata:
name: myServiceGenerator
service: my-service
port: "12345"
`
)
func TestLoader(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
BuildGoPlugin("builtin", "", "SecretGenerator").
BuildGoPlugin("someteam.example.com", "v1", "SomeServiceGenerator")
defer th.Reset()
rmF := resmap.NewFactory(resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()), nil)
fLdr, err := loader.NewLoader(
loader.RestrictionRootOnly,
filesys.Separator, filesys.MakeFsInMemory())
if err != nil {
t.Fatal(err)
}
c, err := konfig.EnabledPluginConfig()
if err != nil {
t.Fatal(err)
}
pLdr := NewLoader(c, rmF)
if pLdr == nil {
t.Fatal("expect non-nil loader")
}
m, err := rmF.NewResMapFromBytes([]byte(
someServiceGenerator + "---\n" + secretGenerator))
if err != nil {
t.Fatal(err)
}
_, err = pLdr.LoadGenerators(
fLdr, valtest_test.MakeFakeValidator(), m)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,48 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target
import (
"fmt"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/konfig"
)
type errMissingKustomization struct {
path string
}
func (e *errMissingKustomization) Error() string {
return fmt.Sprintf(
"unable to find one of %v in directory '%s'",
commaOr(quoted(konfig.RecognizedKustomizationFileNames())),
e.path)
}
func IsMissingKustomizationFileError(err error) bool {
_, ok := err.(*errMissingKustomization)
if ok {
return true
}
_, ok = errors.Cause(err).(*errMissingKustomization)
return ok
}
func NewErrMissingKustomization(p string) *errMissingKustomization {
return &errMissingKustomization{path: p}
}
func quoted(l []string) []string {
r := make([]string, len(l))
for i, v := range l {
r[i] = "'" + v + "'"
}
return r
}
func commaOr(q []string) string {
return strings.Join(q[:len(q)-1], ", ") + " or " + q[len(q)-1]
}

View File

@@ -1,179 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target_test
import (
"encoding/base64"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// KustTarget is primarily tested in the krusty package with
// high level tests.
func TestMakeCustomizedResMap(t *testing.T) {
fSys := filesys.MakeFsInMemory()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
th.WriteK("/whatever", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
nameSuffix: -bar
namespace: ns1
commonLabels:
app: nginx
commonAnnotations:
note: This is a test annotation
resources:
- deployment.yaml
- namespace.yaml
generatorOptions:
disableNameSuffixHash: false
configMapGenerator:
- name: literalConfigMap
literals:
- DB_USERNAME=admin
- DB_PASSWORD=somepw
secretGenerator:
- name: secret
literals:
- DB_USERNAME=admin
- DB_PASSWORD=somepw
type: Opaque
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: dply1
path: jsonpatch.json
`)
th.WriteF("/whatever/deployment.yaml", `
apiVersion: apps/v1
metadata:
name: dply1
kind: Deployment
`)
th.WriteF("/whatever/namespace.yaml", `
apiVersion: v1
kind: Namespace
metadata:
name: ns1
`)
th.WriteF("/whatever/jsonpatch.json", `[
{"op": "add", "path": "/spec/replica", "value": "3"}
]`)
resFactory := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
resources := []*resource.Resource{
resFactory.FromMapWithName("dply1", map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "foo-dply1-bar",
"namespace": "ns1",
"labels": map[string]interface{}{
"app": "nginx",
},
"annotations": map[string]interface{}{
"note": "This is a test annotation",
},
},
"spec": map[string]interface{}{
"replica": "3",
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"app": "nginx",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"note": "This is a test annotation",
},
"labels": map[string]interface{}{
"app": "nginx",
},
},
},
},
}),
resFactory.FromMapWithName("ns1", map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]interface{}{
"name": "foo-ns1-bar",
"labels": map[string]interface{}{
"app": "nginx",
},
"annotations": map[string]interface{}{
"note": "This is a test annotation",
},
},
}),
resFactory.FromMapWithName("literalConfigMap",
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "foo-literalConfigMap-bar-8d2dkb8k24",
"namespace": "ns1",
"labels": map[string]interface{}{
"app": "nginx",
},
"annotations": map[string]interface{}{
"note": "This is a test annotation",
},
},
"data": map[string]interface{}{
"DB_USERNAME": "admin",
"DB_PASSWORD": "somepw",
},
}),
resFactory.FromMapWithName("secret",
map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "foo-secret-bar-9btc7bt4kb",
"namespace": "ns1",
"labels": map[string]interface{}{
"app": "nginx",
},
"annotations": map[string]interface{}{
"note": "This is a test annotation",
},
},
"type": ifc.SecretTypeOpaque,
"data": map[string]interface{}{
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
},
}),
}
expected := resmap.New()
for _, r := range resources {
if err := expected.Append(r); err != nil {
t.Fatalf("unexpected error %v", err)
}
}
actual, err := makeKustTargetWithRf(
t, fSys, "/whatever", resFactory).MakeCustomizedResMap()
if err != nil {
t.Fatalf("unexpected Resources error %v", err)
}
if err = expected.ErrorIfNotEqualLists(actual); err != nil {
t.Fatalf("unexpected inequality: %v", err)
}
}

View File

@@ -1,51 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target_test
import (
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig"
fLdr "sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
)
func makeKustTarget(
t *testing.T,
fSys filesys.FileSystem,
root string) *target.KustTarget {
return makeKustTargetWithRf(
t, fSys, root,
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()))
}
func makeKustTargetWithRf(
t *testing.T,
fSys filesys.FileSystem,
root string,
resFact *resource.Factory) *target.KustTarget {
rf := resmap.NewFactory(resFact, transformer.NewFactoryImpl())
pc := konfig.DisabledPluginConfig()
ldr, err := fLdr.NewLoader(fLdr.RestrictionRootOnly, root, fSys)
if err != nil {
t.Fatal(err)
}
kt := target.NewKustTarget(
ldr,
valtest_test.MakeFakeValidator(),
rf,
transformer.NewFactoryImpl(),
pLdr.NewLoader(pc, rf))
if err = kt.Load(); err != nil {
t.Fatalf("Unexpected construction error %v", err)
}
return kt
}

View File

@@ -1,188 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target_test
import (
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/resid"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/api/types"
)
// To simplify tests, these vars specified in alphabetical order.
var someVars = []types.Var{
{
Name: "AWARD",
ObjRef: types.Target{
APIVersion: "v7",
Gvk: resid.Gvk{Kind: "Service"},
Name: "nobelPrize"},
FieldRef: types.FieldSelector{FieldPath: "some.arbitrary.path"},
},
{
Name: "BIRD",
ObjRef: types.Target{
APIVersion: "v300",
Gvk: resid.Gvk{Kind: "Service"},
Name: "heron"},
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
},
{
Name: "FRUIT",
ObjRef: types.Target{
Gvk: resid.Gvk{Kind: "Service"},
Name: "apple"},
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
},
{
Name: "VEGETABLE",
ObjRef: types.Target{
Gvk: resid.Gvk{Kind: "Leafy"},
Name: "kale"},
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
},
}
func TestGetAllVarsSimple(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
vars:
- name: AWARD
objref:
kind: Service
name: nobelPrize
apiVersion: v7
fieldref:
fieldpath: some.arbitrary.path
- name: BIRD
objref:
kind: Service
name: heron
apiVersion: v300
`)
ra, err := makeKustTarget(
t, th.GetFSys(), "/app").AccumulateTarget()
if err != nil {
t.Fatalf("Err: %v", err)
}
vars := ra.Vars()
if len(vars) != 2 {
t.Fatalf("unexpected size %d", len(vars))
}
for i := range vars[:2] {
// By using Var.DeepEqual, we are protecting the code
// from a potential invocation of vars[i].ObjRef.GVK()
// during accumulateTarget
if !vars[i].DeepEqual(someVars[i]) {
t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i])
}
}
}
func TestGetAllVarsNested(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
vars:
- name: AWARD
objref:
kind: Service
name: nobelPrize
apiVersion: v7
fieldref:
fieldpath: some.arbitrary.path
- name: BIRD
objref:
kind: Service
name: heron
apiVersion: v300
`)
th.WriteK("/app/overlays/o1", `
vars:
- name: FRUIT
objref:
kind: Service
name: apple
resources:
- ../../base
`)
th.WriteK("/app/overlays/o2", `
vars:
- name: VEGETABLE
objref:
kind: Leafy
name: kale
resources:
- ../o1
`)
ra, err := makeKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
if err != nil {
t.Fatalf("Err: %v", err)
}
vars := ra.Vars()
if len(vars) != 4 {
for i, v := range vars {
fmt.Printf("%v: %v\n", i, v)
}
t.Fatalf("expected 4 vars, got %d", len(vars))
}
for i := range vars {
// By using Var.DeepEqual, we are protecting the code
// from a potential invocation of vars[i].ObjRef.GVK()
// during accumulateTarget
if !vars[i].DeepEqual(someVars[i]) {
t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i])
}
}
}
func TestVarCollisionsForbidden(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
vars:
- name: AWARD
objref:
kind: Service
name: nobelPrize
apiVersion: v7
fieldref:
fieldpath: some.arbitrary.path
- name: BIRD
objref:
kind: Service
name: heron
apiVersion: v300
`)
th.WriteK("/app/overlays/o1", `
vars:
- name: AWARD
objref:
kind: Service
name: academy
resources:
- ../../base
`)
th.WriteK("/app/overlays/o2", `
vars:
- name: VEGETABLE
objref:
kind: Leafy
name: kale
resources:
- ../o1
`)
_, err := makeKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
if err == nil {
t.Fatalf("expected var collision")
}
if !strings.Contains(err.Error(),
"var 'AWARD' already encountered") {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@@ -1,19 +0,0 @@
// +build tools
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// This file exists to trigger installs of the given tools.
package tools
import (
// for code generation
_ "golang.org/x/tools/cmd/stringer"
// for lint checks
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
// for integration tests driven by the examples
_ "github.com/monopole/mdrip"
// for generating code for builtin plugins
_ "sigs.k8s.io/kustomize/pluginator/v2"
)

View File

@@ -1,8 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package builtinpluginconsts provides builtin plugin
// configuration data. Builtin plugins can also be
// configured individually with plugin config files,
// in which case the constants in this package are ignored.
package builtinpluginconsts

View File

@@ -1,10 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinpluginconsts
const (
// imageFieldSpecs is left empty since `containers` and `initContainers`
// of *ANY* kind in *ANY* path are builtin supported in code
imagesFieldSpecs = ``
)

View File

@@ -1,11 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinpluginconsts
const (
namePrefixFieldSpecs = `
namePrefix:
- path: metadata/name
`
)

View File

@@ -1,16 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinpluginconsts
const (
namespaceFieldSpecs = `
namespace:
- path: metadata/namespace
create: true
- path: subjects
kind: RoleBinding
- path: subjects
kind: ClusterRoleBinding
`
)

View File

@@ -1,6 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package konfig provides configuration methods and constants
// for the kustomize API.
package konfig

View File

@@ -1,150 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package konfig
import (
"os"
"path/filepath"
"runtime"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/types"
)
const (
// Symbol that must be used inside Go plugins.
PluginSymbol = "KustomizePlugin"
// Name of environment variable used to set AbsPluginHome.
// See that variable for an explanation.
KustomizePluginHomeEnv = "KUSTOMIZE_PLUGIN_HOME"
// Relative path below XDG_CONFIG_HOME/kustomize to find plugins.
// e.g. AbsPluginHome = XDG_CONFIG_HOME/kustomize/plugin
RelPluginHome = "plugin"
// Location of builtin plugins below AbsPluginHome.
BuiltinPluginPackage = "builtin"
// The value of kubernetes ApiVersion to use in configuration
// files for builtin plugins.
// The value for non-builtins can be anything.
BuiltinPluginApiVersion = BuiltinPluginPackage
// Domain from which kustomize code is imported, for locating
// plugin source code under $GOPATH when GOPATH is defined.
DomainName = "sigs.k8s.io"
)
func EnabledPluginConfig() (*types.PluginConfig, error) {
dir, err := DefaultAbsPluginHome(filesys.MakeFsOnDisk())
if err != nil {
return nil, err
}
return MakePluginConfig(types.PluginRestrictionsNone, dir), nil
}
func DisabledPluginConfig() *types.PluginConfig {
return MakePluginConfig(
types.PluginRestrictionsBuiltinsOnly, NoPluginHomeSentinal)
}
func MakePluginConfig(
pr types.PluginRestrictions, home string) *types.PluginConfig {
return &types.PluginConfig{
PluginRestrictions: pr,
AbsPluginHome: home,
}
}
// Use an obviously erroneous path, in case it's accidentally used.
const NoPluginHomeSentinal = "/no/non-builtin/plugins!"
type NotedFunc struct {
Note string
F func() string
}
func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) {
return FirstDirThatExistsElseError(
"plugin home directory", fSys, []NotedFunc{
{
Note: "homed in $" + KustomizePluginHomeEnv,
F: func() string {
return os.Getenv(KustomizePluginHomeEnv)
},
},
{
Note: "homed in $" + XdgConfigHomeEnv,
F: func() string {
return filepath.Join(
os.Getenv(XdgConfigHomeEnv),
ProgramName, RelPluginHome)
},
},
{
Note: "homed in default value of $" + XdgConfigHomeEnv,
F: func() string {
return filepath.Join(
HomeDir(), XdgConfigHomeEnvDefault,
ProgramName, RelPluginHome)
},
},
{
Note: "homed in home directory",
F: func() string {
return filepath.Join(
HomeDir(), ProgramName, RelPluginHome)
},
},
})
}
// FirstDirThatExistsElseError tests different path functions for
// existence, returning the first that works, else error if all fail.
func FirstDirThatExistsElseError(
what string,
fSys filesys.FileSystem,
pathFuncs []NotedFunc) (string, error) {
var nope []types.Pair
for _, dt := range pathFuncs {
dir := dt.F()
if fSys.Exists(dir) {
return dir, nil
}
nope = append(nope, types.Pair{Key: dt.Note, Value: dir})
}
return "", types.NewErrUnableToFind(what, nope)
}
func HomeDir() string {
home := os.Getenv(homeEnv())
if len(home) > 0 {
return home
}
return "~"
}
func homeEnv() string {
if runtime.GOOS == "windows" {
return "USERPROFILE"
}
return "HOME"
}
func CurrentWorkingDir() string {
// Try for full path first to be explicit.
pwd := os.Getenv(pwdEnv())
if len(pwd) > 0 {
return pwd
}
return filesys.SelfDir
}
func pwdEnv() string {
if runtime.GOOS == "windows" {
return "CD"
}
return "PWD"
}

View File

@@ -1,126 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package konfig
import (
"os"
"path/filepath"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/types"
)
func TestDefaultAbsPluginHome_NoKustomizePluginHomeEnv(t *testing.T) {
fSys := filesys.MakeFsInMemory()
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
if isSet {
_ = os.Unsetenv(KustomizePluginHomeEnv)
}
_, err := DefaultAbsPluginHome(fSys)
if isSet {
os.Setenv(KustomizePluginHomeEnv, keep)
}
if err == nil {
t.Fatalf("expected err")
}
if !types.IsErrUnableToFind(err) {
t.Fatalf("unexpected err: %v", err)
}
}
func TestDefaultAbsPluginHome_WithKustomizePluginHomeEnv(t *testing.T) {
fSys := filesys.MakeFsInMemory()
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
if !isSet {
keep = "whatever"
os.Setenv(KustomizePluginHomeEnv, keep)
}
fSys.Mkdir(keep)
h, err := DefaultAbsPluginHome(fSys)
if !isSet {
_ = os.Unsetenv(KustomizePluginHomeEnv)
}
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if h != keep {
t.Fatalf("unexpected config dir: %s", h)
}
}
func TestDefaultAbsPluginHomeWithXdg(t *testing.T) {
fSys := filesys.MakeFsInMemory()
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
if !isSet {
keep = "whatever"
os.Setenv(XdgConfigHomeEnv, keep)
}
configDir := filepath.Join(keep, ProgramName, RelPluginHome)
fSys.Mkdir(configDir)
h, err := DefaultAbsPluginHome(fSys)
if !isSet {
_ = os.Unsetenv(XdgConfigHomeEnv)
}
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if h != configDir {
t.Fatalf("unexpected config dir: %s", h)
}
}
func TestDefaultAbsPluginHomeNoConfig(t *testing.T) {
fSys := filesys.MakeFsInMemory()
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
if isSet {
_ = os.Unsetenv(XdgConfigHomeEnv)
}
_, err := DefaultAbsPluginHome(fSys)
if isSet {
os.Setenv(XdgConfigHomeEnv, keep)
}
if err == nil {
t.Fatalf("expected err")
}
if !types.IsErrUnableToFind(err) {
t.Fatalf("unexpected err: %v", err)
}
}
func TestDefaultAbsPluginHomeNoXdgWithDotConfig(t *testing.T) {
fSys := filesys.MakeFsInMemory()
configDir := filepath.Join(
HomeDir(), XdgConfigHomeEnvDefault, ProgramName, RelPluginHome)
fSys.Mkdir(configDir)
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
if isSet {
_ = os.Unsetenv(XdgConfigHomeEnv)
}
s, _ := DefaultAbsPluginHome(fSys)
if isSet {
os.Setenv(XdgConfigHomeEnv, keep)
}
if s != configDir {
t.Fatalf("unexpected config dir: %s", s)
}
}
func TestDefaultAbsPluginHomeNoXdgJustHomeDir(t *testing.T) {
fSys := filesys.MakeFsInMemory()
configDir := filepath.Join(
HomeDir(), ProgramName, RelPluginHome)
fSys.Mkdir(configDir)
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
if isSet {
_ = os.Unsetenv(XdgConfigHomeEnv)
}
s, _ := DefaultAbsPluginHome(fSys)
if isSet {
os.Setenv(XdgConfigHomeEnv, keep)
}
if s != configDir {
t.Fatalf("unexpected config dir: %s", s)
}
}

View File

@@ -1,95 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"path/filepath"
"strings"
"testing"
. "sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/konfig"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestTargetMustHaveKustomizationFile(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("/app/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: aService
`)
th.WriteF("/app/deeper/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: anotherService
`)
err := th.RunWithErr("/app", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("expected an error")
}
if !IsMissingKustomizationFileError(err) {
t.Fatalf("unexpected error: %q", err)
}
}
func TestTargetMustHaveOnlyOneKustomizationFile(t *testing.T) {
th := kusttest_test.MakeHarness(t)
for _, n := range konfig.RecognizedKustomizationFileNames() {
th.WriteF(filepath.Join("/app", n), `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
}
err := th.RunWithErr("/app", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("expected an error")
}
if !strings.Contains(err.Error(), "Found multiple kustomization files under: /app") {
t.Fatalf("unexpected error: %q", err)
}
}
func TestBaseMustHaveKustomizationFile(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
resources:
- base
`)
th.WriteF("/app/base/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
selector:
backend: bungie
ports:
- port: 7002
`)
err := th.RunWithErr("/app", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("expected an error")
}
if !IsMissingKustomizationFileError(err) {
t.Fatalf("unexpected error: %q", err)
}
}
func TestResourceNotFound(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
resources:
- deployment.yaml
`)
err := th.RunWithErr("/app", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("expected an error")
}
if !strings.Contains(err.Error(), "'/app/deployment.yaml' doesn't exist") {
t.Fatalf("unexpected error: %q", err)
}
}

View File

@@ -1,555 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"strings"
"testing"
. "sigs.k8s.io/kustomize/api/krusty"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/api/types"
)
const httpsService = `
apiVersion: v1
kind: Service
metadata:
name: my-https-svc
spec:
ports:
- port: 443
protocol: TCP
name: https
selector:
app: my-app
`
func writeStatefulSetBase(th kusttest_test.Harness) {
th.WriteK("/app/base", `
resources:
- statefulset.yaml
`)
th.WriteF("/app/base/statefulset.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
serviceName: my-svc
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-image
volumeClaimTemplates:
- spec:
storageClassName: default
`)
}
func writeHTTPSOverlay(th kusttest_test.Harness) {
th.WriteK("/app/https", `
resources:
- ../base
- https-svc.yaml
patchesStrategicMerge:
- sts-patch.yaml
`)
th.WriteF("/app/https/https-svc.yaml", httpsService)
th.WriteF("/app/https/sts-patch.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
serviceName: my-https-svc
`)
}
func writeHTTPSTransformerRaw(th kusttest_test.Harness) {
th.WriteF("/app/https/service/https-svc.yaml", httpsService)
th.WriteF("/app/https/transformer/transformer.yaml", `
apiVersion: builtin
kind: PatchTransformer
metadata:
name: svcNameTran
target:
group: apps
version: v1
kind: StatefulSet
name: my-sts
patch: |-
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
serviceName: my-https-svc
`)
}
func writeHTTPSTransformerBase(th kusttest_test.Harness) {
th.WriteK("/app/https/service", `
resources:
- https-svc.yaml
`)
th.WriteK("/app/https/transformer", `
resources:
- transformer.yaml
`)
writeHTTPSTransformerRaw(th)
}
func writeConfigFromEnvOverlay(th kusttest_test.Harness) {
th.WriteK("/app/config", `
resources:
- ../base
configMapGenerator:
- name: my-config
literals:
- MY_ENV=foo
generatorOptions:
disableNameSuffixHash: true
patchesStrategicMerge:
- sts-patch.yaml
`)
th.WriteF("/app/config/sts-patch.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
template:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: my-config
`)
}
func writeConfigFromEnvTransformerRaw(th kusttest_test.Harness) {
th.WriteF("/app/config/map/generator.yaml", `
apiVersion: builtin
kind: ConfigMapGenerator
metadata:
name: my-config
disableNameSuffixHash: true
literals:
- MY_ENV=foo
`)
th.WriteF("/app/config/transformer/transformer.yaml", `
apiVersion: builtin
kind: PatchTransformer
metadata:
name: envFromConfigTrans
target:
group: apps
version: v1
kind: StatefulSet
name: my-sts
patch: |-
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
template:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: my-config
`)
}
func writeConfigFromEnvTransformerBase(th kusttest_test.Harness) {
th.WriteK("/app/config/map", `
resources:
- generator.yaml
`)
th.WriteK("/app/config/transformer", `
resources:
- transformer.yaml
`)
writeConfigFromEnvTransformerRaw(th)
}
func writeTolerationsOverlay(th kusttest_test.Harness) {
th.WriteK("/app/tolerations", `
resources:
- ../base
patchesStrategicMerge:
- sts-patch.yaml
`)
th.WriteF("/app/tolerations/sts-patch.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
template:
spec:
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
tolerationSeconds: 30
`)
}
func writeTolerationsTransformerRaw(th kusttest_test.Harness) {
th.WriteF("/app/tolerations/transformer.yaml", `
apiVersion: builtin
kind: PatchTransformer
metadata:
name: tolTrans
target:
group: apps
version: v1
kind: StatefulSet
name: my-sts
patch: |-
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
template:
spec:
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
tolerationSeconds: 30
`)
}
func writeTolerationsTransformerBase(th kusttest_test.Harness) {
th.WriteK("/app/tolerations", `
resources:
- transformer.yaml
`)
writeTolerationsTransformerRaw(th)
}
func writeStorageOverlay(th kusttest_test.Harness) {
th.WriteK("/app/storage", `
resources:
- ../base
patchesJson6902:
- target:
group: apps
version: v1
kind: StatefulSet
name: my-sts
path: sts-patch.json
`)
th.WriteF("/app/storage/sts-patch.json", `
[{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
`)
}
func writeStorageTransformerRaw(th kusttest_test.Harness) {
th.WriteF("/app/storage/transformer.yaml", `
apiVersion: builtin
kind: PatchTransformer
metadata:
name: storageTrans
target:
group: apps
version: v1
kind: StatefulSet
name: my-sts
patch: |-
[{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
`)
}
func writeStorageTransformerBase(th kusttest_test.Harness) {
th.WriteK("/app/storage", `
resources:
- transformer.yaml
`)
writeStorageTransformerRaw(th)
}
func writePatchingOverlays(th kusttest_test.Harness) {
writeStorageOverlay(th)
writeConfigFromEnvOverlay(th)
writeTolerationsOverlay(th)
writeHTTPSOverlay(th)
}
func writePatchingTransformersRaw(th kusttest_test.Harness) {
writeStorageTransformerRaw(th)
writeConfigFromEnvTransformerRaw(th)
writeTolerationsTransformerRaw(th)
writeHTTPSTransformerRaw(th)
}
// Similar to writePatchingTransformersRaw, except here the
// transformers and related artifacts are addressable as _bases_.
// They are listed in a kustomization file, and consumers of
// the plugin refer to the kustomization instead of to the local
// file in the "transformers:" field.
//
// Using bases makes the set of files relocatable with
// respect to the overlays, and avoids the need to relax load
// restrictions on file paths reaching outside the `dev` and
// `prod` kustomization roots. I.e. with bases tests can use
// NewKustTestHarness instead of NewKustTestHarnessNoLoadRestrictor.
//
// Using transformer plugins from _bases_ means the plugin config
// must be self-contained, i.e. the config may not have fields that
// refer to local files, since those files won't be present when
// the plugin is instantiated and used.
func writePatchingTransformerBases(th kusttest_test.Harness) {
writeStorageTransformerBase(th)
writeConfigFromEnvTransformerBase(th)
writeTolerationsTransformerBase(th)
writeHTTPSTransformerBase(th)
}
// Here's a complex kustomization scenario that combines multiple overlays
// on a common base:
//
// dev prod
// | |
// | |
// + ------- + + ------------ + ------------- +
// | | | | |
// | | | | |
// v | v v v
// storage + -----> config tolerations https
// | | | |
// | | | |
// | + --- + + --- + |
// | | | |
// | v v |
// + -----------------------> base <------------------ +
//
// The base resource is a statefulset. Each intermediate overlay manages or
// generates new resources and patches different aspects of the same base
// resource, without using any of the `namePrefix`, `nameSuffix` or `namespace`
// kustomization keywords.
//
// Intermediate overlays:
// - storage: Changes the storage class of the stateful set with a JSON patch.
// - config: Generates a config map and adds a field as an environment
// variable.
// - tolerations: Adds a new tolerations field in the spec.
// - https: Adds a new service resource and changes the service name in the
// stateful set.
//
// Top overlays:
// - dev: Combines the storage and config intermediate overlays.
// - prod: Combines the config, tolerations and https intermediate overlays.
func TestComplexComposition_Dev_Failure(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingOverlays(th)
th.WriteK("/app/dev", `
resources:
- ../storage
- ../config
`)
err := th.RunWithErr("/app/dev", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("Expected resource accumulation error")
}
if !strings.Contains(
err.Error(), "already registered id: apps_v1_StatefulSet|~X|my-sts") {
t.Fatalf("Unexpected err: %v", err)
}
}
const devDesiredResult = `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
selector:
matchLabels:
app: my-app
serviceName: my-svc
template:
metadata:
labels:
app: my-app
spec:
containers:
- envFrom:
- configMapRef:
name: my-config
image: my-image
name: app
volumeClaimTemplates:
- spec:
storageClassName: my-sc
---
apiVersion: v1
data:
MY_ENV: foo
kind: ConfigMap
metadata:
name: my-config
`
func TestComplexComposition_Dev_SuccessWithRawTransformers(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingTransformersRaw(th)
th.WriteK("/app/dev", `
resources:
- ../base
generators:
- ../config/map/generator.yaml
transformers:
- ../config/transformer/transformer.yaml
- ../storage/transformer.yaml
`)
m := th.Run("/app/dev", func() Options {
o := th.MakeDefaultOptions()
o.LoadRestrictions = types.LoadRestrictionsNone
return o
}())
th.AssertActualEqualsExpected(m, devDesiredResult)
}
func TestComplexComposition_Dev_SuccessWithBaseTransformers(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingTransformerBases(th)
th.WriteK("/app/dev", `
resources:
- ../base
generators:
- ../config/map
transformers:
- ../config/transformer
- ../storage
`)
m := th.Run("/app/dev", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, devDesiredResult)
}
func TestComplexComposition_Prod_Failure(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingOverlays(th)
th.WriteK("/app/prod", `
resources:
- ../config
- ../tolerations
- ../https
`)
err := th.RunWithErr("/app/prod", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("Expected resource accumulation error")
}
if !strings.Contains(
err.Error(), "already registered id: apps_v1_StatefulSet|~X|my-sts") {
t.Fatalf("Unexpected err: %v", err)
}
}
const prodDesiredResult = `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-sts
spec:
selector:
matchLabels:
app: my-app
serviceName: my-https-svc
template:
metadata:
labels:
app: my-app
spec:
containers:
- envFrom:
- configMapRef:
name: my-config
image: my-image
name: app
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
tolerationSeconds: 30
volumeClaimTemplates:
- spec:
storageClassName: default
---
apiVersion: v1
kind: Service
metadata:
name: my-https-svc
spec:
ports:
- name: https
port: 443
protocol: TCP
selector:
app: my-app
---
apiVersion: v1
data:
MY_ENV: foo
kind: ConfigMap
metadata:
name: my-config
`
func TestComplexComposition_Prod_SuccessWithRawTransformers(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingTransformersRaw(th)
th.WriteK("/app/prod", `
resources:
- ../base
- ../https/service/https-svc.yaml
generators:
- ../config/map/generator.yaml
transformers:
- ../config/transformer/transformer.yaml
- ../https/transformer/transformer.yaml
- ../tolerations/transformer.yaml
`)
m := th.Run("/app/prod", func() Options {
o := th.MakeDefaultOptions()
o.LoadRestrictions = types.LoadRestrictionsNone
return o
}())
th.AssertActualEqualsExpected(m, prodDesiredResult)
}
func TestComplexComposition_Prod_SuccessWithBaseTransformers(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeStatefulSetBase(th)
writePatchingTransformerBases(th)
th.WriteK("/app/prod", `
resources:
- ../base
- ../https/service
generators:
- ../config/map
transformers:
- ../config/transformer
- ../https/transformer
- ../tolerations
`)
m := th.Run("/app/prod", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, prodDesiredResult)
}

View File

@@ -1,94 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// Demo custom configuration of a builtin transformation.
// This is a NamePrefixer that touches Deployments
// and Services exclusively.
func TestCustomNamePrefixer(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepBuiltin("PrefixSuffixTransformer")
defer th.Reset()
th.WriteK("/app", `
resources:
- deployment.yaml
- role.yaml
- service.yaml
transformers:
- prefixer.yaml
`)
th.WriteF("/app/prefixer.yaml", `
apiVersion: builtin
kind: PrefixSuffixTransformer
metadata:
name: customPrefixer
prefix: zzz-
fieldSpecs:
- kind: Deployment
path: metadata/name
- kind: Service
path: metadata/name
`)
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- name: whatever
image: whatever
`)
th.WriteF("/app/role.yaml", `
apiVersion: v1
kind: Role
metadata:
name: myRole
`)
th.WriteF("/app/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: zzz-myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- image: whatever
name: whatever
---
apiVersion: v1
kind: Role
metadata:
name: myRole
---
apiVersion: v1
kind: Service
metadata:
name: zzz-myService
`)
}

View File

@@ -1,177 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// Demo custom configuration as a base.
// This test shows usage of three custom configurations sitting
// in a base, allowing them to be reused in any number of
// kustomizations.
func TestReusableCustomTransformers(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepBuiltin("PrefixSuffixTransformer").
PrepBuiltin("AnnotationsTransformer").
PrepBuiltin("LabelTransformer")
defer th.Reset()
// First write three custom configurations for builtin plugins.
// A custom name prefixer that only touches Deployments and Services.
th.WriteF("/app/mytransformers/deploymentServicePrefixer.yaml", `
apiVersion: builtin
kind: PrefixSuffixTransformer
metadata:
name: myPrefixer
prefix: bob-
fieldSpecs:
- kind: Deployment
path: metadata/name
- kind: Service
path: metadata/name
`)
// A custom annotator exclusively annotating Roles.
th.WriteF("/app/mytransformers/roleAnnotator.yaml", `
apiVersion: builtin
kind: AnnotationsTransformer
metadata:
name: myAnnotator
annotations:
# Imagine that SRE has not approved this role yet.
status: probationary
fieldSpecs:
- path: metadata/annotations
create: true
kind: Role
`)
// A custom labeller that only labels Deployments,
// and only labels them at their top metadata level
// exclusively. It does not modify selectors or
// add labels to pods in the template.
th.WriteF("/app/mytransformers/deploymentLabeller.yaml", `
apiVersion: builtin
kind: LabelTransformer
metadata:
name: myLabeller
labels:
pager: 867-5301
fieldSpecs:
- path: metadata/labels
create: true
kind: Deployment
`)
// Combine these custom transformers in one kustomization file.
// This kustomization file contains only resources that
// all happen to be plugin configurations. This makes
// these plugins re-usable as a group in any number of other
// kustomizations.
th.WriteK("/app/mytransformers", `
resources:
- deploymentServicePrefixer.yaml
- roleAnnotator.yaml
- deploymentLabeller.yaml
`)
// Finally, define the kustomization for the (arbitrarily named)
// staging environment.
th.WriteK("/app/staging", `
# Bring in the custom transformers.
transformers:
- ../mytransformers
# Also use the "classic" labeller, which behind the scenes uses
# the LabelTransformer, but with a broad configuration targeting
# many resources and fields (including selector fields).
# It's a big hammer - probably too big; the output shows all the
# places 'acmeCorp' now appears as a result. To avoid this,
# define your own config for your own LabelTransformer instance
# as shown above.
commonLabels:
company: acmeCorp
# Specify the resources to modify.
resources:
- deployment.yaml
- role.yaml
- service.yaml
`)
th.WriteF("/app/staging/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
metadata:
labels:
backend: flawless
spec:
containers:
- name: whatever
image: whatever
`)
th.WriteF("/app/staging/role.yaml", `
apiVersion: v1
kind: Role
metadata:
name: myRole
`)
th.WriteF("/app/staging/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
`)
m := th.Run("/app/staging", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
company: acmeCorp
pager: 867-5301
name: bob-myDeployment
spec:
selector:
matchLabels:
company: acmeCorp
template:
metadata:
labels:
backend: flawless
company: acmeCorp
spec:
containers:
- image: whatever
name: whatever
---
apiVersion: v1
kind: Role
metadata:
annotations:
status: probationary
labels:
company: acmeCorp
name: myRole
---
apiVersion: v1
kind: Service
metadata:
labels:
company: acmeCorp
name: bob-myService
spec:
selector:
company: acmeCorp
`)
}

View File

@@ -1,29 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestIssue596AllowDirectoriesThatAreSubstringsOfEachOther(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", "")
th.WriteK("/app/overlays/aws", `
resources:
- ../../base
`)
th.WriteK("/app/overlays/aws-nonprod", `
resources:
- ../aws
`)
th.WriteK("/app/overlays/aws-sandbox2.us-east-1", `
resources:
- ../aws-nonprod
`)
m := th.Run("/app/overlays/aws-sandbox2.us-east-1", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, "")
}

View File

@@ -1,100 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func findSecret(m resmap.ResMap) *resource.Resource {
for _, r := range m.Resources() {
if r.OrgId().Kind == "Secret" {
return r
}
}
return nil
}
func TestDisableNameSuffixHash(t *testing.T) {
th := kusttest_test.MakeHarness(t)
const kustomizationContent = `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
nameSuffix: -bar
namespace: ns1
commonLabels:
app: nginx
commonAnnotations:
note: This is a test annotation
resources:
- deployment.yaml
- namespace.yaml
generatorOptions:
disableNameSuffixHash: false
configMapGenerator:
- name: literalConfigMap
literals:
- DB_USERNAME=admin
- DB_PASSWORD=somepw
secretGenerator:
- name: secret
literals:
- DB_USERNAME=admin
- DB_PASSWORD=somepw
type: Opaque
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: dply1
path: jsonpatch.json
`
th.WriteK("/whatever", kustomizationContent)
th.WriteF("/whatever/deployment.yaml", `
apiVersion: apps/v1
metadata:
name: dply1
kind: Deployment
`)
th.WriteF("/whatever/namespace.yaml", `
apiVersion: v1
kind: Namespace
metadata:
name: ns1
`)
th.WriteF("/whatever/jsonpatch.json", `[
{"op": "add", "path": "/spec/replica", "value": "3"}
]`)
m := th.Run("/whatever", th.MakeDefaultOptions())
secret := findSecret(m)
if secret == nil {
t.Errorf("Expected to find a Secret")
}
if secret.GetName() != "foo-secret-bar-9btc7bt4kb" {
t.Errorf("unexpected secret resource name: %s", secret.GetName())
}
th.WriteK("/whatever",
strings.Replace(kustomizationContent,
"disableNameSuffixHash: false",
"disableNameSuffixHash: true", -1))
m = th.Run("/whatever", th.MakeDefaultOptions())
secret = findSecret(m)
if secret == nil {
t.Errorf("Expected to find a Secret")
}
if secret.GetName() != "foo-secret-bar" { // No hash at end.
t.Errorf("unexpected secret resource name: %s", secret.GetName())
}
}

View File

@@ -1,11 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package krusty is intended as the entry point package
// for those seeking to add kustomize ability to other
// programs.
//
// To use, follow the example of the kustomize CLI's 'build'
// command. Also, see the high level tests in this package,
// which serve a dual purpose as examples.
package krusty

View File

@@ -1,86 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty
import (
"sigs.k8s.io/kustomize/api/builtins"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/k8sdeps/validator"
fLdr "sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
)
// Kustomizer performs kustomizations. It's meant to behave
// similarly to the kustomize CLI, and can be used instead of
// performing an exec to a kustomize CLI subprocess.
// To use, load a filesystem with kustomization files (any
// number of overlays and bases), then make a Kustomizer
// injected with the given fileystem, then call Run.
type Kustomizer struct {
fSys filesys.FileSystem
options *Options
}
// MakeKustomizer returns an instance of Kustomizer.
func MakeKustomizer(fSys filesys.FileSystem, o *Options) *Kustomizer {
return &Kustomizer{fSys: fSys, options: o}
}
// Run performs a kustomization.
//
// It uses its internal filesystem reference to read the file at
// the given path argument, interpret it as a kustomization.yaml
// file, perform the kustomization it represents, and return the
// resulting resources.
//
// Any files referenced by the kustomization must be present in the
// internal filesystem. One may call Run any number of times,
// on any number of internal paths (e.g. the filesystem may contain
// multiple overlays, and Run can be called on each of them).
func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
pf := transformer.NewFactoryImpl()
rf := resmap.NewFactory(
resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()),
pf)
lr := fLdr.RestrictionNone
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
lr = fLdr.RestrictionRootOnly
}
ldr, err := fLdr.NewLoader(lr, path, b.fSys)
if err != nil {
return nil, err
}
defer ldr.Cleanup()
kt := target.NewKustTarget(
ldr,
validator.NewKustValidator(),
rf,
pf,
pLdr.NewLoader(b.options.PluginConfig, rf),
)
err = kt.Load()
if err != nil {
return nil, err
}
var m resmap.ResMap
if b.options.DoPrune {
m, err = kt.MakePruneConfigMap()
} else {
m, err = kt.MakeCustomizedResMap()
}
if err != nil {
return nil, err
}
if b.options.DoLegacyResourceSort {
builtins.NewLegacyOrderTransformerPlugin().Transform(m)
}
return m, nil
}

View File

@@ -1,27 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/krusty"
)
// A simple usage example to shows what happens when
// there are no files to read.
// For more substantial tests and examples,
// see other tests in this package.
func TestEmptyFileSystem(t *testing.T) {
fSys := filesys.MakeFsInMemory()
b := krusty.MakeKustomizer(fSys, krusty.MakeDefaultOptions())
_, err := b.Run("noSuchThing")
if err == nil {
t.Fatalf("expected error")
}
if err.Error() != "'noSuchThing' doesn't exist" {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@@ -1,40 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty
import (
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
)
// Options holds high-level kustomize configuration options,
// e.g. are plugins enabled, should the loader be restricted
// to the kustomization root, etc.
type Options struct {
// When true, sort the resources before emitting them,
// per a particular sort order. When false, don't do the
// sort, and instead respect the depth-first resource input
// order as specified by the kustomization file(s).
DoLegacyResourceSort bool
// Restrictions on what can be loaded from the file system.
// See type definition.
LoadRestrictions types.LoadRestrictions
// Create an inventory object for pruning.
DoPrune bool
// Options related to kustomize plugins.
PluginConfig *types.PluginConfig
}
// MakeDefaultOptions returns a default instance of Options.
func MakeDefaultOptions() *Options {
return &Options{
DoLegacyResourceSort: true,
LoadRestrictions: types.LoadRestrictionsRootOnly,
DoPrune: false,
PluginConfig: konfig.DisabledPluginConfig(),
}
}

View File

@@ -1,79 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// The PrintPluginEnv plugin is a toy plugin that emits
// its working directory and some environment variables,
// to add regression protection to plugin loading logic.
func TestPluginEnvironment(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepExecPlugin(
"someteam.example.com", "v1", "PrintPluginEnv")
defer th.Reset()
confirmBehavior(
kusttest_test.MakeHarnessWithFs(t, filesys.MakeFsInMemory()),
filesys.Separator)
dir := makeTmpDir(t)
defer os.RemoveAll(dir)
confirmBehavior(
kusttest_test.MakeHarnessWithFs(t, filesys.MakeFsOnDisk()),
dir)
}
func confirmBehavior(th kusttest_test.Harness, dir string) {
th.WriteK(dir, `
generators:
- config.yaml
`)
th.WriteF(filepath.Join(dir, "config.yaml"), `
apiVersion: someteam.example.com/v1
kind: PrintPluginEnv
metadata:
name: irrelevantHere
`)
m := th.Run(dir, th.MakeOptionsPluginsEnabled())
pHome, ok := os.LookupEnv(konfig.KustomizePluginHomeEnv)
if !ok {
th.GetT().Fatalf(
"expected env var '%s' to be defined",
konfig.KustomizePluginHomeEnv)
}
th.AssertActualEqualsExpected(m, `
apiVersion: v1
env:
kustomize_plugin_config_root: `+dir+`
kustomize_plugin_home: `+pHome+`
pwd: `+dir+`
kind: GeneratedEnv
metadata:
name: hello
`)
}
func makeTmpDir(t *testing.T) string {
base, err := os.Getwd()
if err != nil {
t.Fatalf("err %v", err)
}
dir, err := ioutil.TempDir(base, "kustomize-tmp-test-")
if err != nil {
t.Fatalf("err %v", err)
}
return dir
}

View File

@@ -1,34 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package loader has a data loading interface and various implementations.
package loader
import (
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/git"
)
// NewLoader returns a Loader pointed at the given target.
// If the target is remote, the loader will be restricted
// to the root and below only. If the target is local, the
// loader will have the restrictions passed in. Regardless,
// if a local target attempts to transitively load remote bases,
// the remote bases will all be root-only restricted.
func NewLoader(
lr LoadRestrictorFunc,
target string, fSys filesys.FileSystem) (ifc.Loader, error) {
repoSpec, err := git.NewRepoSpecFromUrl(target)
if err == nil {
// The target qualifies as a remote git target.
return newLoaderAtGitClone(
repoSpec, fSys, nil, git.ClonerUsingGitExec)
}
root, err := demandDirectoryRoot(fSys, target)
if err != nil {
return nil, err
}
return newLoaderAtConfirmedDir(
lr, root, fSys, nil, git.ClonerUsingGitExec), nil
}

View File

@@ -1,35 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package loader
import (
"fmt"
"sigs.k8s.io/kustomize/api/filesys"
)
type LoadRestrictorFunc func(
filesys.FileSystem, filesys.ConfirmedDir, string) (string, error)
func RestrictionRootOnly(
fSys filesys.FileSystem, root filesys.ConfirmedDir, path string) (string, error) {
d, f, err := fSys.CleanedAbs(path)
if err != nil {
return "", err
}
if f == "" {
return "", fmt.Errorf("'%s' must resolve to a file", path)
}
if !d.HasPrefix(root) {
return "", fmt.Errorf(
"security; file '%s' is not in or below '%s'",
path, root)
}
return d.Join(f), nil
}
func RestrictionNone(
_ filesys.FileSystem, _ filesys.ConfirmedDir, path string) (string, error) {
return path, nil
}

View File

@@ -1,67 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package loader
import (
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
)
func TestRestrictionNone(t *testing.T) {
fSys := filesys.MakeFsInMemory()
root := filesys.ConfirmedDir("irrelevant")
path := "whatever"
p, err := RestrictionNone(fSys, root, path)
if err != nil {
t.Fatal(err)
}
if p != path {
t.Fatalf("expected '%s', got '%s'", path, p)
}
}
func TestRestrictionRootOnly(t *testing.T) {
fSys := filesys.MakeFsInMemory()
root := filesys.ConfirmedDir(
filesys.Separator + filepath.Join("tmp", "foo"))
path := filepath.Join(string(root), "whatever", "beans")
fSys.Create(path)
p, err := RestrictionRootOnly(fSys, root, path)
if err != nil {
t.Fatal(err)
}
if p != path {
t.Fatalf("expected '%s', got '%s'", path, p)
}
// Legal.
path = filepath.Join(
string(root), "whatever", "..", "..", "foo", "whatever", "beans")
p, err = RestrictionRootOnly(fSys, root, path)
if err != nil {
t.Fatal(err)
}
path = filepath.Join(
string(root), "whatever", "beans")
if p != path {
t.Fatalf("expected '%s', got '%s'", path, p)
}
// Illegal; file exists but is out of bounds.
path = filepath.Join(filesys.Separator+"tmp", "illegal")
fSys.Create(path)
_, err = RestrictionRootOnly(fSys, root, path)
if err == nil {
t.Fatal("should have an error")
}
if !strings.Contains(
err.Error(),
"file '/tmp/illegal' is not in or below '/tmp/foo'") {
t.Fatalf("unexpected err: %s", err)
}
}

View File

@@ -1,20 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// A dummy main to help with releasing the kustomize API module.
package main
import (
"fmt"
"sigs.k8s.io/kustomize/api/provenance"
)
// TODO: delete this when we find a better way to generate release notes.
func main() {
fmt.Println(`
This 'main' exists only to make goreleaser create release notes for the API.
See https://github.com/goreleaser/goreleaser/issues/981
and https://github.com/kubernetes-sigs/kustomize/tree/master/releasing`)
fmt.Println(provenance.GetProvenance())
}

View File

@@ -1,126 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kusttest_test
import (
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
)
// Harness manages a kustomize environment for tests.
type Harness struct {
t *testing.T
fSys filesys.FileSystem
}
func MakeHarness(t *testing.T) Harness {
return MakeHarnessWithFs(t, filesys.MakeFsInMemory())
}
func MakeHarnessWithFs(
t *testing.T, fSys filesys.FileSystem) Harness {
return Harness{
t: t,
fSys: fSys,
}
}
func (th Harness) GetT() *testing.T {
return th.t
}
func (th Harness) GetFSys() filesys.FileSystem {
return th.fSys
}
func (th Harness) WriteK(path string, content string) {
th.fSys.WriteFile(
filepath.Join(
path,
konfig.DefaultKustomizationFileName()), []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`+content))
}
func (th Harness) WriteF(path string, content string) {
th.fSys.WriteFile(path, []byte(content))
}
func (th Harness) MakeDefaultOptions() krusty.Options {
return th.MakeOptionsPluginsDisabled()
}
// This has no impact on Builtin plugins, as they are always enabled.
func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
return krusty.Options{
LoadRestrictions: types.LoadRestrictionsRootOnly,
PluginConfig: konfig.DisabledPluginConfig(),
}
}
// Enables use of non-builtin plugins.
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
c, err := konfig.EnabledPluginConfig()
if err != nil {
if strings.Contains(err.Error(), "unable to find plugin root") {
th.t.Log(
"Tests that want to run with plugins enabled must be " +
"bookended by calls to MakeEnhancedHarness(), Reset().")
}
th.t.Fatal(err)
}
return krusty.Options{
LoadRestrictions: types.LoadRestrictionsRootOnly,
PluginConfig: c,
}
}
// Run, failing on error.
func (th Harness) Run(path string, o krusty.Options) resmap.ResMap {
m, err := krusty.MakeKustomizer(th.fSys, &o).Run(path)
if err != nil {
th.t.Fatal(err)
}
return m
}
// Run, failing if there is no error.
func (th Harness) RunWithErr(path string, o krusty.Options) error {
_, err := krusty.MakeKustomizer(th.fSys, &o).Run(path)
if err == nil {
th.t.Fatalf("expected error")
}
return err
}
func (th Harness) WriteLegacyConfigs(fName string) {
m := builtinpluginconsts.GetDefaultFieldSpecsAsMap()
var content []byte
for _, tCfg := range m {
content = append(content, []byte(tCfg)...)
}
err := th.fSys.WriteFile(fName, content)
if err != nil {
th.t.Fatalf("unable to add file %s", fName)
}
}
func (th Harness) AssertActualEqualsExpected(
m resmap.ResMap, expected string) {
th.AssertActualEqualsExpectedWithTweak(m, nil, expected)
}
func (th Harness) AssertActualEqualsExpectedWithTweak(
m resmap.ResMap, tweaker func([]byte) []byte, expected string) {
assertActualEqualsExpectedWithTweak(th, m, tweaker, expected)
}

View File

@@ -1,140 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kusttest_test
import (
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig"
fLdr "sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
)
// HarnessEnhanced manages a full plugin environment for tests.
type HarnessEnhanced struct {
Harness
pte *pluginTestEnv
rf *resmap.Factory
ldr ifc.Loader
pl *pLdr.Loader
}
func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
pte := newPluginTestEnv(t).set()
pc, err := konfig.EnabledPluginConfig()
if err != nil {
t.Fatal(err)
}
fSys := filesys.MakeFsInMemory()
rf := resmap.NewFactory(
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
transformer.NewFactoryImpl())
result := &HarnessEnhanced{
Harness: Harness{t: t, fSys: fSys},
pte: pte,
rf: rf,
pl: pLdr.NewLoader(pc, rf)}
result.ResetLoaderRoot(filesys.Separator)
return result
}
func (th *HarnessEnhanced) Reset() {
th.pte.reset()
}
func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced {
th.pte.buildGoPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
th.pte.prepExecPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
th.pte.buildGoPlugin(konfig.BuiltinPluginPackage, "", k)
return th
}
func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
if err := th.fSys.Mkdir(root); err != nil {
th.t.Fatal(err)
}
ldr, err := fLdr.NewLoader(
fLdr.RestrictionRootOnly, root, th.fSys)
if err != nil {
th.t.Fatalf("Unable to make loader: %v", err)
}
th.ldr = ldr
}
func (th *HarnessEnhanced) LoadAndRunGenerator(
config string) resmap.ResMap {
res, err := th.rf.RF().FromBytes([]byte(config))
if err != nil {
th.t.Fatalf("Err: %v", err)
}
g, err := th.pl.LoadGenerator(
th.ldr, valtest_test.MakeFakeValidator(), res)
if err != nil {
th.t.Fatalf("Err: %v", err)
}
rm, err := g.Generate()
if err != nil {
th.t.Fatalf("Err: %v", err)
}
return rm
}
func (th *HarnessEnhanced) LoadAndRunTransformer(
config, input string) resmap.ResMap {
resMap, err := th.RunTransformer(config, input)
if err != nil {
th.t.Fatalf("Err: %v", err)
}
return resMap
}
func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
config, input string) error {
_, err := th.RunTransformer(config, input)
return err
}
func (th *HarnessEnhanced) RunTransformer(
config, input string) (resmap.ResMap, error) {
resMap, err := th.rf.NewResMapFromBytes([]byte(input))
if err != nil {
th.t.Fatalf("Err: %v", err)
}
return th.RunTransformerFromResMap(config, resMap)
}
func (th *HarnessEnhanced) RunTransformerFromResMap(
config string, resMap resmap.ResMap) (resmap.ResMap, error) {
transConfig, err := th.rf.RF().FromBytes([]byte(config))
if err != nil {
th.t.Fatalf("Err: %v", err)
}
g, err := th.pl.LoadTransformer(
th.ldr, valtest_test.MakeFakeValidator(), transConfig)
if err != nil {
return nil, err
}
err = g.Transform(resMap)
return resMap, err
}

View File

@@ -1,104 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kusttest_test
import (
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/resmap"
)
type hasGetT interface {
GetT() *testing.T
}
func assertActualEqualsExpectedWithTweak(
ht hasGetT,
m resmap.ResMap,
tweaker func([]byte) []byte, expected string) {
if m == nil {
ht.GetT().Fatalf("Map should not be nil.")
}
// Ignore leading linefeed in expected value
// to ease readability of tests.
if len(expected) > 0 && expected[0] == 10 {
expected = expected[1:]
}
actual, err := m.AsYaml()
if err != nil {
ht.GetT().Fatalf("Unexpected err: %v", err)
}
if tweaker != nil {
actual = tweaker(actual)
}
if string(actual) != expected {
reportDiffAndFail(ht.GetT(), actual, expected)
}
}
// Pretty printing of file differences.
func reportDiffAndFail(
t *testing.T, actual []byte, expected string) {
sE, maxLen := convertToArray(expected)
sA, _ := convertToArray(string(actual))
fmt.Println("===== ACTUAL BEGIN ========================================")
fmt.Print(string(actual))
fmt.Println("===== ACTUAL END ==========================================")
format := fmt.Sprintf("%%s %%-%ds %%s\n", maxLen+4)
limit := 0
if len(sE) < len(sA) {
limit = len(sE)
} else {
limit = len(sA)
}
fmt.Printf(format, " ", "EXPECTED", "ACTUAL")
fmt.Printf(format, " ", "--------", "------")
for i := 0; i < limit; i++ {
fmt.Printf(format, hint(sE[i], sA[i]), sE[i], sA[i])
}
if len(sE) < len(sA) {
for i := len(sE); i < len(sA); i++ {
fmt.Printf(format, "X", "", sA[i])
}
} else {
for i := len(sA); i < len(sE); i++ {
fmt.Printf(format, "X", sE[i], "")
}
}
t.Fatalf("Expected not equal to actual")
}
func hint(a, b string) string {
if a == b {
return " "
}
return "X"
}
func convertToArray(x string) ([]string, int) {
a := strings.Split(strings.TrimSuffix(x, "\n"), "\n")
maxLen := 0
for i, v := range a {
z := tabToSpace(v)
if len(z) > maxLen {
maxLen = len(z)
}
a[i] = z
}
return a, maxLen
}
func tabToSpace(input string) string {
var result []string
for _, i := range input {
if i == 9 {
result = append(result, " ")
} else {
result = append(result, string(i))
}
}
return strings.Join(result, "")
}

View File

@@ -1,120 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kusttest_test
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/plugins/compiler"
"sigs.k8s.io/kustomize/api/konfig"
)
// pluginTestEnv manages plugins for tests.
// It manages a Go plugin compiler,
// makes and removes a temporary working directory,
// and sets/resets shell env vars as needed.
type pluginTestEnv struct {
t *testing.T
compiler *compiler.Compiler
srcRoot string
workDir string
oldXdg string
wasSet bool
}
// newPluginTestEnv returns a new instance of pluginTestEnv.
func newPluginTestEnv(t *testing.T) *pluginTestEnv {
return &pluginTestEnv{t: t}
}
// set creates a test environment.
// Uses a filesystem on disk for compilation (or copying) of
// plugin code - this FileSystem has nothing to do with
// the FileSystem used for loading config yaml in the tests.
func (x *pluginTestEnv) set() *pluginTestEnv {
x.createWorkDir()
var err error
x.srcRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
if err != nil {
x.t.Error(err)
}
x.compiler = compiler.NewCompiler(x.srcRoot, x.workDir)
x.setEnv()
return x
}
// reset restores the environment to pre-test state.
func (x *pluginTestEnv) reset() {
x.resetEnv()
x.removeWorkDir()
}
// buildGoPlugin compiles a Go plugin, leaving the newly
// created object code in the right place - a temporary
// working directory pointed to by KustomizePluginHomeEnv.
// This avoids overwriting anything the user/developer has
// otherwise created.
func (x *pluginTestEnv) buildGoPlugin(g, v, k string) {
err := x.compiler.Compile(g, v, k)
if err != nil {
x.t.Errorf("compile failed: %v", err)
}
}
// prepExecPlugin copies an exec plugin from it's
// home in the discovered srcRoot to the same temp
// directory where Go plugin object code is placed.
// Kustomize (and its tests) expect to find plugins
// (Go or Exec) in the same spot, and since the test
// framework is compiling Go plugins to a temp dir,
// it must likewise copy Exec plugins to that same
// temp dir.
func (x *pluginTestEnv) prepExecPlugin(g, v, k string) {
lowK := strings.ToLower(k)
src := filepath.Join(x.srcRoot, g, v, lowK, k)
tmp := filepath.Join(x.workDir, g, v, lowK, k)
if err := os.MkdirAll(filepath.Dir(tmp), 0755); err != nil {
x.t.Errorf("error making directory: %s", filepath.Dir(tmp))
}
cmd := exec.Command("cp", src, tmp)
cmd.Env = os.Environ()
if err := cmd.Run(); err != nil {
x.t.Errorf("error copying %s to %s: %v", src, tmp, err)
}
}
func (x *pluginTestEnv) createWorkDir() {
var err error
x.workDir, err = ioutil.TempDir("", "kustomize-plugin-tests")
if err != nil {
x.t.Errorf("failed to make work dir: %v", err)
}
}
func (x *pluginTestEnv) removeWorkDir() {
err := os.RemoveAll(x.workDir)
if err != nil {
x.t.Errorf(
"removing work dir: %s %v", x.workDir, err)
}
}
func (x *pluginTestEnv) setEnv() {
x.oldXdg, x.wasSet = os.LookupEnv(konfig.KustomizePluginHomeEnv)
os.Setenv(konfig.KustomizePluginHomeEnv, x.workDir)
}
func (x *pluginTestEnv) resetEnv() {
if x.wasSet {
os.Setenv(konfig.KustomizePluginHomeEnv, x.oldXdg)
} else {
os.Unsetenv(konfig.KustomizePluginHomeEnv)
}
}

View File

@@ -1,64 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import (
"errors"
"fmt"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
)
// mapTransformer applies a string->string map to fieldSpecs.
type mapTransformer struct {
m map[string]string
fieldSpecs []types.FieldSpec
}
var _ resmap.Transformer = &mapTransformer{}
// NewMapTransformer construct a mapTransformer.
func NewMapTransformer(
pc []types.FieldSpec, m map[string]string) (resmap.Transformer, error) {
if m == nil {
return newNoOpTransformer(), nil
}
if pc == nil {
return nil, errors.New("fieldSpecs is not expected to be nil")
}
return &mapTransformer{fieldSpecs: pc, m: m}, nil
}
// Transform apply each <key, value> pair in the mapTransformer to the
// fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
for _, path := range o.fieldSpecs {
if !r.OrgId().IsSelected(&path.Gvk) {
continue
}
err := MutateField(
r.Map(), path.PathSlice(),
path.CreateIfNotPresent, o.addMap)
if err != nil {
return err
}
}
}
return nil
}
func (o *mapTransformer) addMap(in interface{}) (interface{}, error) {
m, ok := in.(map[string]interface{})
if in == nil {
m = map[string]interface{}{}
} else if !ok {
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
}
for k, v := range o.m {
m[k] = v
}
return m, nil
}

View File

@@ -1,21 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import "sigs.k8s.io/kustomize/api/resmap"
// noOpTransformer contains a no-op transformer.
type noOpTransformer struct{}
var _ resmap.Transformer = &noOpTransformer{}
// newNoOpTransformer constructs a noOpTransformer.
func newNoOpTransformer() resmap.Transformer {
return &noOpTransformer{}
}
// Transform does nothing.
func (o *noOpTransformer) Transform(_ resmap.ResMap) error {
return nil
}

View File

@@ -1,9 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package types holds the definition of the kustomization struct and
// supporting structs. It's the k8s API conformant object that describes
// a set of generation and transformation operations to create and/or
// modify k8s resources.
// A kustomization file is a serialization of this struct.
package types

View File

@@ -1,33 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
import (
"fmt"
"github.com/pkg/errors"
)
type errOnlyBuiltinPluginsAllowed struct {
name string
}
func (e *errOnlyBuiltinPluginsAllowed) Error() string {
return fmt.Sprintf(
"external plugins disabled; unable to load external plugin '%s'",
e.name)
}
func NewErrOnlyBuiltinPluginsAllowed(n string) *errOnlyBuiltinPluginsAllowed {
return &errOnlyBuiltinPluginsAllowed{name: n}
}
func IsErrOnlyBuiltinPluginsAllowed(err error) bool {
_, ok := err.(*errOnlyBuiltinPluginsAllowed)
if ok {
return true
}
_, ok = errors.Cause(err).(*errOnlyBuiltinPluginsAllowed)
return ok
}

View File

@@ -1,40 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
import (
"fmt"
"strings"
"github.com/pkg/errors"
)
type errUnableToFind struct {
// What are we unable to find?
what string
// What things did we try?
attempts []Pair
}
func (e *errUnableToFind) Error() string {
var m []string
for _, p := range e.attempts {
m = append(m, "('"+p.Value+"'; "+p.Key+")")
}
return fmt.Sprintf(
"unable to find plugin root - tried: %s", strings.Join(m, ", "))
}
func NewErrUnableToFind(w string, a []Pair) *errUnableToFind {
return &errUnableToFind{what: w, attempts: a}
}
func IsErrUnableToFind(err error) bool {
_, ok := err.(*errUnableToFind)
if ok {
return true
}
_, ok = errors.Cause(err).(*errUnableToFind)
return ok
}

View File

@@ -1,12 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
//go:generate stringer -type=GarbagePolicy
type GarbagePolicy int
const (
GarbageIgnore GarbagePolicy = iota + 1
GarbageCollect
)

View File

@@ -1,50 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
import (
"strconv"
"strings"
)
// GenArgs contains both GeneratorArgs and GeneratorOptions.
type GenArgs struct {
args *GeneratorArgs
opts *GeneratorOptions
}
// NewGenArgs returns a new object of GenArgs
func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
return &GenArgs{
args: args,
opts: opts,
}
}
func (g *GenArgs) String() string {
if g == nil {
return "{nilGenArgs}"
}
return "{" +
strings.Join([]string{
"nsfx:" + strconv.FormatBool(g.ShouldAddHashSuffixToName()),
"beh:" + g.Behavior().String()},
",") +
"}"
}
// ShouldAddHashSuffixToName returns true if a resource
// content hash should be appended to the name of the resource.
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
return g.args != nil &&
(g.opts == nil || !g.opts.DisableNameSuffixHash)
}
// Behavior returns Behavior field of GeneratorArgs
func (g *GenArgs) Behavior() GenerationBehavior {
if g.args == nil {
return BehaviorUnspecified
}
return NewGenerationBehavior(g.args.Behavior)
}

View File

@@ -1,37 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types_test
import (
"testing"
. "sigs.k8s.io/kustomize/api/types"
)
func TestGenArgs_String(t *testing.T) {
tests := []struct {
ga *GenArgs
expected string
}{
{
ga: nil,
expected: "{nilGenArgs}",
},
{
ga: &GenArgs{},
expected: "{nsfx:false,beh:unspecified}",
},
{
ga: NewGenArgs(
&GeneratorArgs{Behavior: "merge"},
&GeneratorOptions{DisableNameSuffixHash: false}),
expected: "{nsfx:true,beh:merge}",
},
}
for _, test := range tests {
if test.ga.String() != test.expected {
t.Fatalf("Expected '%s', got '%s'", test.expected, test.ga.String())
}
}
}

View File

@@ -1,24 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// GeneratorArgs contains arguments common to ConfigMap and Secret generators.
type GeneratorArgs struct {
// Namespace for the configmap, optional
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
// Name - actually the partial name - of the generated resource.
// The full name ends up being something like
// NamePrefix + this.Name + hash(content of generated resource).
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Behavior of generated resource, must be one of:
// 'create': create a new one
// 'replace': replace the existing one
// 'merge': merge with the existing one
Behavior string `json:"behavior,omitempty" yaml:"behavior,omitempty"`
// KvPairSources for the generator.
KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"`
}

View File

@@ -1,16 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Inventory records all objects touched in a build operation.
type Inventory struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
ConfigMap NameArgs `json:"configMap,omitempty" yaml:"configMap,omitempty"`
}
// NameArgs holds both namespace and name.
type NameArgs struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}

View File

@@ -1,24 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Restrictions on what things can be referred to
// in a kustomization file.
//
//go:generate stringer -type=LoadRestrictions
type LoadRestrictions int
const (
LoadRestrictionsUnknown LoadRestrictions = iota
// Files referenced by a kustomization file must be in
// or under the directory holding the kustomization
// file itself.
LoadRestrictionsRootOnly
// The kustomization file may specify absolute or
// relative paths to patch or resources files outside
// its own tree.
LoadRestrictionsNone
)

View File

@@ -1,25 +0,0 @@
// Code generated by "stringer -type=LoadRestrictions"; DO NOT EDIT.
package types
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[LoadRestrictionsUnknown-0]
_ = x[LoadRestrictionsRootOnly-1]
_ = x[LoadRestrictionsNone-2]
}
const _LoadRestrictions_name = "LoadRestrictionsUnknownLoadRestrictionsRootOnlyLoadRestrictionsNone"
var _LoadRestrictions_index = [...]uint8{0, 23, 47, 67}
func (i LoadRestrictions) String() string {
if i < 0 || i >= LoadRestrictions(len(_LoadRestrictions_index)-1) {
return "LoadRestrictions(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _LoadRestrictions_name[_LoadRestrictions_index[i]:_LoadRestrictions_index[i+1]]
}

View File

@@ -1,10 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Pair is a key value pair.
type Pair struct {
Key string
Value string
}

View File

@@ -1,19 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Patch represent either a Strategic Merge Patch or a JSON patch
// and its targets.
// The content of the patch can either be from a file
// or from an inline string.
type Patch struct {
// Path is a relative file path to the patch file.
Path string `json:"path,omitempty" yaml:"path,omitempty"`
// Patch is the content of a patch.
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
// Target points to the resources that the patch is applied to
Target *Selector `json:"target,omitempty" yaml:"target,omitempty"`
}

View File

@@ -1,21 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// PatchJson6902 represents a json patch for an object
// with format documented https://tools.ietf.org/html/rfc6902.
type PatchJson6902 struct {
// PatchTarget refers to a Kubernetes object that the json patch will be
// applied to. It must refer to a Kubernetes resource under the
// purview of this kustomization. PatchTarget should use the
// raw name of the object (the name specified in its YAML,
// before addition of a namePrefix and a nameSuffix).
Target *PatchTarget `json:"target" yaml:"target"`
// relative file path for a json patch file inside a kustomization
Path string `json:"path,omitempty" yaml:"path,omitempty"`
// inline patch string
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
}

View File

@@ -1,31 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// PluginConfig holds plugin configuration.
type PluginConfig struct {
// AbsPluginHome is the home of kustomize plugins.
// Kustomize plugin configuration files are k8s-style objects
// containing the fields 'apiVersion' and 'kind', e.g.
// apiVersion: apps/v1
// kind: Deployment
// When kustomize reads a plugin configuration file (as as result
// of seeing the file name in the 'generators:' or 'transformers:'
// field in a kustomization file), it must then locate the plugin
// code (Go plugin or exec plugin).
// Every kustomize plugin (its code, its tests, supporting data
// files, etc.) must be housed in its own directory at
// ${AbsPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
// where
// - ${AbsPluginHome} is an absolute path, defined below.
// - ${pluginApiVersion} is taken from the plugin config file.
// - ${pluginKind} is taken from the plugin config file.
// The value of AbsPluginHome can be any absolute path, but might
// default to $XDG_CONFIG_HOME/kustomize/plugin.
AbsPluginHome string
// PluginRestrictions defines the plugin restriction state.
// See type for more information.
PluginRestrictions PluginRestrictions
}

View File

@@ -1,28 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Some plugin classes
// - builtin: plugins defined in the kustomize repo.
// May be freely used and re-configured.
// - local: plugins that aren't builtin but are
// locally defined (presumably by the user), meaning
// the kustomization refers to them via a relative
// file path, not a URL.
// - remote: require a build-time download to obtain.
// Unadvised, unless one controls the
// serving site.
//
//go:generate stringer -type=PluginRestrictions
type PluginRestrictions int
const (
PluginRestrictionsUnknown PluginRestrictions = iota
// Non-builtin plugins completely disabled.
PluginRestrictionsBuiltinsOnly
// No restrictions, do whatever you want.
PluginRestrictionsNone
)

View File

@@ -1,25 +0,0 @@
// Code generated by "stringer -type=PluginRestrictions"; DO NOT EDIT.
package types
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[PluginRestrictionsUnknown-0]
_ = x[PluginRestrictionsBuiltinsOnly-1]
_ = x[PluginRestrictionsNone-2]
}
const _PluginRestrictions_name = "PluginRestrictionsUnknownPluginRestrictionsBuiltinsOnlyPluginRestrictionsNone"
var _PluginRestrictions_index = [...]uint8{0, 25, 55, 77}
func (i PluginRestrictions) String() string {
if i < 0 || i >= PluginRestrictions(len(_PluginRestrictions_index)-1) {
return "PluginRestrictions(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _PluginRestrictions_name[_PluginRestrictions_index[i]:_PluginRestrictions_index[i+1]]
}

View File

@@ -1,27 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// Replacement defines how to perform a substitution
// where it is from and where it is to.
type Replacement struct {
Source *ReplSource `json:"source" yaml:"source"`
Target *ReplTarget `json:"target" yaml:"target"`
}
// ReplSource defines where a substitution is from
// It can from two different kinds of sources
// - from a field of one resource
// - from a string
type ReplSource struct {
ObjRef *Target `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRef string `json:"fieldref,omitempty" yaml:"fiedldref,omitempty"`
Value string `json:"value,omitempty" yaml:"value,omitempty"`
}
// ReplTarget defines where a substitution is to.
type ReplTarget struct {
ObjRef *Selector `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRefs []string `json:"fieldrefs,omitempty" yaml:"fieldrefs,omitempty"`
}

View File

@@ -1,11 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta
// No need for a direct dependence; the fields are stable.
type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}

View File

@@ -1,54 +0,0 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
run:
deadline: 5m
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
# - dogsled
- dupl
# - errcheck
# - funlen
# - gochecknoinits
# - goconst
# - gocritic
- gocyclo
- gofmt
- goimports
# - golint
- gosec
- gosimple
- govet
- ineffassign
- interfacer
# - lll
- misspell
- nakedret
# - scopelint
- staticcheck
- structcheck
# - stylecheck
- typecheck
- unconvert
# - unparam
- unused
- varcheck
# - whitespace
linters-settings:
dupl:
threshold: 400
lll:
line-length: 170
gocyclo:
min-complexity: 30
golint:
min-confidence: 0.85

View File

@@ -1,2 +0,0 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

View File

@@ -1,39 +0,0 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy
GOBIN := $(shell go env GOPATH)/bin
build:
go build -v -o $(GOBIN)/config .
all: generate build license fix vet fmt test lint tidy
fix:
go fix ./...
fmt:
go fmt ./...
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
GOBIN=$(GOBIN) go generate ./...
license:
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
go mod tidy
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
$(GOBIN)/golangci-lint run ./...
test:
go test -cover ./...
vet:
go vet ./...

View File

@@ -1,25 +0,0 @@
# cmd/config
This package exists to expose config filters directly as cli commands for the purposes
of development of the kyaml package and as a reference implementation for using the libraries.
## Docs
All documentation is also built directly into the `config` command group using
`kustomize help config`.
- [tutorials](docs/tutorials)
- [commands](docs/commands)
- [api-conventions](docs/api-conventions)
## Build Command
Build the `config` command under `GOBIN`
$ make
## Run Tests
Run all tests, linting, etc and build
$ make all

View File

@@ -1,135 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// GetCatRunner returns a command CatRunner.
func GetCatRunner(name string) *CatRunner {
r := &CatRunner{}
c := &cobra.Command{
Use: "cat DIR...",
Short: commands.CatShort,
Long: commands.CatLong,
Example: commands.CatExamples,
RunE: r.runE,
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.Format, "format", true,
"format resource config yaml before printing.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false,
"annotate resources with their file origins.")
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "",
"if set, wrap the output in this list type kind.")
c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", "",
"if set, wrap the output in this list type apiVersion.")
c.Flags().StringVar(&r.FunctionConfig, "function-config", "",
"path to function config to put in ResourceList -- only if wrapped in a ResourceList.")
c.Flags().StringSliceVar(&r.Styles, "style", []string{},
"yaml styles to apply. may be 'TaggedStyle', 'DoubleQuotedStyle', 'LiteralStyle', "+
"'FoldedStyle', 'FlowStyle'.")
c.Flags().BoolVar(&r.StripComments, "strip-comments", false,
"remove comments from yaml.")
c.Flags().BoolVar(&r.IncludeLocal, "include-local", false,
"if true, include local-config in the output.")
c.Flags().BoolVar(&r.ExcludeNonLocal, "exclude-non-local", false,
"if true, exclude non-local-config in the output.")
c.Flags().StringVar(&r.OutputDest, "dest", "",
"if specified, write output to a file rather than stdout")
r.Command = c
return r
}
func CatCommand(name string) *cobra.Command {
return GetCatRunner(name).Command
}
// CatRunner contains the run function
type CatRunner struct {
IncludeSubpackages bool
Format bool
KeepAnnotations bool
WrapKind string
WrapApiVersion string
FunctionConfig string
OutputDest string
Styles []string
StripComments bool
IncludeLocal bool
ExcludeNonLocal bool
Command *cobra.Command
}
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
// if there is a function-config specified, emit it
var functionConfig *yaml.RNode
if r.FunctionConfig != "" {
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
OmitReaderAnnotations: !r.KeepAnnotations}.Read()
if err != nil {
return err
}
if len(configs) != 1 {
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
}
functionConfig = configs[0]
}
var inputs []kio.Reader
for _, a := range args {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
var fltr []kio.Filter
// don't include reconcilers
fltr = append(fltr, &filters.IsLocalConfig{
IncludeLocalConfig: r.IncludeLocal,
ExcludeNonLocalConfig: r.ExcludeNonLocal,
})
if r.Format {
fltr = append(fltr, filters.FormatFilter{})
}
if r.StripComments {
fltr = append(fltr, filters.StripCommentsFilter{})
}
var out = c.OutOrStdout()
if r.OutputDest != "" {
o, err := os.Create(r.OutputDest)
if err != nil {
return handleError(c, errors.Wrap(err))
}
defer o.Close()
out = o
}
var outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{
Writer: out,
KeepReaderAnnotations: r.KeepAnnotations,
WrappingKind: r.WrapKind,
WrappingAPIVersion: r.WrapApiVersion,
FunctionConfig: functionConfig,
Style: yaml.GetStyle(r.Styles...),
})
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())
}

View File

@@ -1,570 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// TODO(pwittrock): write tests for reading / writing ResourceLists
func TestCmd_files(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-cat-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCatRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar
labels:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
spec:
replicas: 3
`, b.String()) {
return
}
}
func TestCmd_filesWithReconcilers(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-cat-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/image:version
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCatRunner("")
r.Command.SetArgs([]string{d, "--include-local"})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
---
apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
configFn:
container:
image: gcr.io/example/image:version
spec:
replicas: 3
---
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
spec:
replicas: 3
`, b.String()) {
return
}
}
func TestCmd_filesWithoutNonReconcilers(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-cat-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
configFn:
container:
image: gcr.io/example/reconciler:v1
spec:
replicas: 3
---
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCatRunner("")
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `apiVersion: v1
kind: Abstraction
metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
configFn:
container:
image: gcr.io/example/reconciler:v1
spec:
replicas: 3
`, b.String()) {
return
}
}
func TestCmd_outputTruncateFile(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-cat-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
f, err := ioutil.TempFile("", "kustomize-cat-test-dest")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCatRunner("")
r.Command.SetArgs([]string{d, "--dest", f.Name()})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, ``, b.String()) {
return
}
actual, err := ioutil.ReadFile(f.Name())
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar
labels:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
spec:
replicas: 3
`, string(actual)) {
return
}
}
func TestCmd_outputCreateFile(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-cat-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 3
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
d2, err := ioutil.TempDir("", "kustomize-cat-test-dest")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d2)
f := filepath.Join(d2, "output.yaml")
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCatRunner("")
r.Command.SetArgs([]string{d, "--dest", f})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, ``, b.String()) {
return
}
actual, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar
labels:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
spec:
replicas: 3
`, string(actual)) {
return
}
}

View File

@@ -1,139 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"io"
"os"
"path/filepath"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
// GetWrapRunner returns a command runner.
func GetWrapRunner() *WrapRunner {
r := &WrapRunner{}
c := &cobra.Command{
Use: "wrap CMD...",
Short: "Wrap an executable so it implements the config fn interface",
Long: `Wrap an executable so it implements the config fn interface
wrap simplifies writing config functions by:
- invoking an executable command converting an input ResourceList into environment
- merging the output onto the original input as a set of patches
- setting filenames on any Resources missing them
config function authors may use wrap by using it to invoke a command from a container image
The following are equivalent:
kyaml wrap -- CMD
kyaml xargs -- CMD | kyaml merge | kyaml fmt --set-filenames
Environment Variables:
KUST_OVERRIDE_DIR:
Path to a directory containing patches to apply to after merging.
`,
Example: `
`,
RunE: r.runE,
SilenceUsage: true,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
Args: cobra.MinimumNArgs(1),
}
r.Command = c
r.XArgs = GetXArgsRunner()
c.Flags().BoolVar(&r.XArgs.EnvOnly,
"env-only", true, "only set env vars, not arguments.")
c.Flags().StringVar(&r.XArgs.WrapKind,
"wrap-kind", "List", "wrap the input xargs give to the command in this type.")
c.Flags().StringVar(&r.XArgs.WrapVersion,
"wrap-version", "v1", "wrap the input xargs give to the command in this type.")
return r
}
// WrapRunner contains the run function
type WrapRunner struct {
Command *cobra.Command
XArgs *XArgsRunner
getEnv func(key string) string
}
const (
KustMergeEnv = "KUST_MERGE"
KustOverrideDirEnv = "KUST_OVERRIDE_DIR"
)
func WrapCommand() *cobra.Command {
return GetWrapRunner().Command
}
func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
if r.getEnv == nil {
r.getEnv = os.Getenv
}
xargsIn := &bytes.Buffer{}
if _, err := io.Copy(xargsIn, c.InOrStdin()); err != nil {
return err
}
mergeInput := bytes.NewBuffer(xargsIn.Bytes())
// Run the command
xargsOut := &bytes.Buffer{}
r.XArgs.Command.SetArgs(args)
r.XArgs.Command.SetIn(xargsIn)
r.XArgs.Command.SetOut(xargsOut)
r.XArgs.Command.SetErr(os.Stderr)
if err := r.XArgs.Command.Execute(); err != nil {
return err
}
// merge the results
buff := &kio.PackageBuffer{}
var fltrs []kio.Filter
var inputs []kio.Reader
if r.getEnv(KustMergeEnv) == "" || r.getEnv(KustMergeEnv) == "true" || r.getEnv(KustMergeEnv) == "1" {
inputs = append(inputs, &kio.ByteReader{Reader: mergeInput})
fltrs = append(fltrs, &filters.MergeFilter{})
}
inputs = append(inputs, &kio.ByteReader{Reader: xargsOut})
if err := (kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: []kio.Writer{buff}}).
Execute(); err != nil {
return err
}
inputs, fltrs = []kio.Reader{buff}, nil
if r.getEnv(KustOverrideDirEnv) != "" {
// merge the overrides on top of the output
fltrs = append(fltrs, filters.MergeFilter{})
inputs = append(inputs,
kio.LocalPackageReader{
OmitReaderAnnotations: true, // don't set path annotations, as they would override
PackagePath: r.getEnv(KustOverrideDirEnv)})
}
fltrs = append(fltrs,
&filters.FileSetter{
FilenamePattern: filepath.Join("config", filters.DefaultFilenamePattern)},
&filters.FormatFilter{})
err := kio.Pipeline{
Inputs: inputs,
Filters: fltrs,
Outputs: []kio.Writer{kio.ByteWriter{
Sort: true,
KeepReaderAnnotations: true,
Writer: c.OutOrStdout(),
WrappingKind: kio.ResourceListKind,
WrappingAPIVersion: kio.ResourceListAPIVersion}}}.Execute()
return handleError(c, err)
}

View File

@@ -1,307 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
const (
input = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
functionConfig:
metadata:
name: test
spec:
replicas: 11
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test
labels:
app: nginx
name: test
spec:
replicas: 5
selector:
matchLabels:
app: nginx
name: test
template:
metadata:
labels:
app: nginx
name: test
spec:
containers:
- name: test
image: nginx:v1.7
ports:
- containerPort: 8080
name: http
resources:
limits:
cpu: 500m
- apiVersion: v1
kind: Service
metadata:
name: test
labels:
app: nginx
name: test
spec:
ports:
# This i the port.
- port: 8080
targetPort: 8080
name: http
selector:
app: nginx
name: test
`
output = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_deployment.yaml
spec:
replicas: 11
selector:
matchLabels:
name: test
app: nginx
template:
metadata:
labels:
name: test
app: nginx
spec:
containers:
- name: test
image: nginx:v1.7
ports:
- name: http
containerPort: 8080
resources:
limits:
cpu: 500m
- apiVersion: v1
kind: Service
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_service.yaml
spec:
selector:
name: test
app: nginx
ports:
- name: http
# This i the port.
port: 8080
targetPort: 8080
`
outputNoMerge = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_deployment.yaml
spec:
replicas: 11
selector:
matchLabels:
name: test
app: nginx
template:
metadata:
labels:
name: test
app: nginx
spec:
containers:
- name: test
image: nginx:v1.7
ports:
- name: http
containerPort: 8080
- apiVersion: v1
kind: Service
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_service.yaml
spec:
selector:
name: test
app: nginx
ports:
- name: http
# This i the port.
port: 8080
targetPort: 8080
`
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_deployment.yaml
spec:
replicas: 11
selector:
matchLabels:
name: test
app: nginx
template:
metadata:
labels:
name: test
app: nginx
spec:
containers:
- name: test
image: nginx:v1.9
ports:
- name: http
containerPort: 8080
resources:
limits:
cpu: 500m
- apiVersion: v1
kind: Service
metadata:
name: test
labels:
name: test
app: nginx
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: config/test_service.yaml
spec:
selector:
name: test
app: nginx
ports:
- name: http
# This i the port.
port: 8080
targetPort: 8080
`
)
func TestCmd_wrap(t *testing.T) {
_, dir, _, ok := runtime.Caller(0)
if !assert.True(t, ok) {
t.FailNow()
}
dir = filepath.Dir(dir)
c := GetWrapRunner()
c.Command.SetIn(bytes.NewBufferString(input))
out := &bytes.Buffer{}
c.Command.SetOut(out)
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
c.Command.SetArgs(args)
c.XArgs.Args = args
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Equal(t, output, out.String())
}
func TestCmd_wrapNoMerge(t *testing.T) {
_, dir, _, ok := runtime.Caller(0)
if !assert.True(t, ok) {
t.FailNow()
}
dir = filepath.Dir(dir)
c := GetWrapRunner()
c.getEnv = func(key string) string {
if key == KustMergeEnv {
return "false"
}
return ""
}
c.Command.SetIn(bytes.NewBufferString(input))
out := &bytes.Buffer{}
c.Command.SetOut(out)
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
c.Command.SetArgs(args)
c.XArgs.Args = args
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Equal(t, outputNoMerge, out.String())
}
func TestCmd_wrapOverride(t *testing.T) {
_, dir, _, ok := runtime.Caller(0)
if !assert.True(t, ok) {
t.FailNow()
}
dir = filepath.Dir(dir)
c := GetWrapRunner()
c.getEnv = func(key string) string {
if key == KustOverrideDirEnv {
return filepath.Join(dir, "test")
}
return ""
}
c.Command.SetIn(bytes.NewBufferString(input))
out := &bytes.Buffer{}
c.Command.SetOut(out)
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
c.Command.SetArgs(args)
c.XArgs.Args = args
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Equal(t, outputOverride, out.String())
}

View File

@@ -1,226 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"fmt"
"os"
"os/exec"
"strings"
"unicode"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// GetXArgsRunner returns a command runner.
func GetXArgsRunner() *XArgsRunner {
r := &XArgsRunner{}
c := &cobra.Command{
Use: "xargs -- CMD...",
Short: "Convert functionConfig to commandline flags and envs",
Long: `Convert functionConfig to commandline flags and envs.
xargs reads a ResourceList from stdin and parses the functionConfig field. xargs then
reads each of the fields under .spec and parses them as flags. If the fields have non-scalar
values, then xargs encoded the values as yaml strings.
CMD:
The command to run and pass the functionConfig as arguments.
`,
Example: `
# given this example functionConfig in config.yaml
kind: Foo
spec:
flag1: value1
flag2: value2
items:
- 2
- 1
# this command:
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml run xargs -- app
# is equivalent to this command:
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | app --flag1=value1 --flag2=value2 2 1
# echo: prints the app arguments
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- echo
--flag1=value1 --flag2=value2 2 1
# env: prints the app env
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- env
# cat: prints the app stdin -- prints the package contents and functionConfig wrapped in a
# ResourceList
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs --no-flags -- env
`,
RunE: r.runE,
SilenceUsage: true,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
Args: cobra.MinimumNArgs(1),
}
r.Command = c
r.Command.Flags().BoolVar(&r.EnvOnly, "env-only", false, "only add env vars, not flags")
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "List", "wrap the input xargs give to the command in this type.")
c.Flags().StringVar(&r.WrapVersion, "wrap-version", "v1", "wrap the input xargs give to the command in this type.")
return r
}
// Runner contains the run function
type XArgsRunner struct {
Command *cobra.Command
Args []string
EnvOnly bool
WrapKind string
WrapVersion string
}
func XArgsCommand() *cobra.Command {
return GetXArgsRunner().Command
}
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
if len(r.Args) == 0 {
r.Args = os.Args
}
cmdIndex := -1
for i := range r.Args {
if r.Args[i] == "--" {
cmdIndex = i + 1
break
}
}
if cmdIndex < 0 {
return fmt.Errorf("must specify -- before command")
}
r.Args = r.Args[cmdIndex:]
run := exec.Command(r.Args[0])
if len(r.Args) > 1 {
r.Args = r.Args[cmdIndex+1:]
} else {
r.Args = []string{}
}
run.Stdout = c.OutOrStdout()
run.Stderr = c.ErrOrStderr()
rw := &kio.ByteReadWriter{
Reader: c.InOrStdin(),
}
nodes, err := rw.Read()
if err != nil {
return err
}
env := os.Environ()
// append the config to the flags
if err = func() error {
if rw.FunctionConfig == nil {
return nil
}
str, err := rw.FunctionConfig.String()
if err != nil {
return err
}
// add the API object to the env
env = append(env, fmt.Sprintf("KUST_FUNCTION_CONFIG=%s", str))
// parse the fields
meta := rw.FunctionConfig.Field("metadata")
if meta != nil {
err = meta.Value.VisitFields(func(node *yaml.MapNode) error {
if !r.EnvOnly {
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
}
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
node.Value.YNode().Value))
return nil
})
if err != nil {
return err
}
}
spec := rw.FunctionConfig.Field("spec")
if spec != nil {
err = spec.Value.VisitFields(func(node *yaml.MapNode) error {
if !r.EnvOnly {
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
}
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
node.Value.YNode().Value))
return nil
})
if err != nil {
return err
}
}
if !r.EnvOnly {
items := rw.FunctionConfig.Field("items")
if items != nil {
err = items.Value.VisitElements(func(node *yaml.RNode) error {
r.Args = append(r.Args, parseYNode(node.YNode()))
return nil
})
if err != nil {
return err
}
}
}
if r.WrapKind != "" {
if kind := rw.FunctionConfig.Field("kind"); !yaml.IsFieldEmpty(kind) {
kind.Value.YNode().Value = r.WrapKind
}
rw.WrappingKind = r.WrapKind
}
if r.WrapVersion != "" {
if version := rw.FunctionConfig.Field("apiVersion"); !yaml.IsFieldEmpty(version) {
version.Value.YNode().Value = r.WrapVersion
}
rw.WrappingAPIVersion = r.WrapVersion
}
return nil
}(); err != nil {
return err
}
run.Args = append(run.Args, r.Args...)
run.Env = append(run.Env, env...)
// write ResourceList to stdin
if err = func() error {
in, err := run.StdinPipe()
if err != nil {
return err
}
defer in.Close()
rw.Writer = in
if r.WrapKind != kio.ResourceListKind {
rw.FunctionConfig = nil
}
return rw.Write(nodes)
}(); err != nil {
return err
}
return handleError(c, run.Run())
}
func parseYNode(node *yaml.Node) string {
node.Value = strings.TrimSpace(node.Value)
for _, b := range node.Value {
if unicode.IsSpace(b) {
// wrap in '' -- contains whitespace
return fmt.Sprintf("'%s'", node.Value)
}
}
return node.Value
}

View File

@@ -1,117 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
const (
flagsInput = `kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx
- apiVersion: apps/v1
kind: Service
spec: {}
functionConfig:
kind: Foo
spec:
a: b
c: d
e: f
items:
- 1
- 3
- 2
- 4
`
resourceInput = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx
- apiVersion: apps/v1
kind: Service
spec: {}
functionConfig:
kind: Foo
`
resourceOutput = `apiVersion: v1
kind: List
items:
- apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx
- apiVersion: apps/v1
kind: Service
spec: {}
`
)
func TestXArgs_flags(t *testing.T) {
c := cmd.GetXArgsRunner()
c.Command.SetIn(bytes.NewBufferString(flagsInput))
out := &bytes.Buffer{}
c.Command.SetOut(out)
c.Command.SetArgs([]string{"--", "echo"})
c.Args = []string{"--", "echo"}
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Equal(t, `--a=b --c=d --e=f 1 3 2 4
`, out.String())
}
func TestXArgs_input(t *testing.T) {
c := cmd.GetXArgsRunner()
c.Command.SetIn(bytes.NewBufferString(resourceInput))
out := &bytes.Buffer{}
c.Command.SetOut(out)
c.Command.SetArgs([]string{"--", "cat"})
c.Args = []string{"--", "cat"}
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Equal(t, resourceOutput, out.String())
}
func TestCmd_env(t *testing.T) {
c := cmd.GetXArgsRunner()
c.Command.SetIn(bytes.NewBufferString(flagsInput))
out := &bytes.Buffer{}
c.Command.SetOut(out)
c.Command.SetArgs([]string{"--env-only", "--", "env"})
c.Args = []string{"--", "env"}
if !assert.NoError(t, c.Command.Execute()) {
t.FailNow()
}
assert.Contains(t, out.String(), "\nA=b\nC=d\nE=f\n")
}

View File

@@ -1,68 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package complete
import (
"os"
"strings"
"github.com/posener/complete/v2"
"github.com/posener/complete/v2/predict"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
)
// NewCommand returns a new install-completion command
func NewCommand() *cobra.Command {
return &cobra.Command{
Use: "install-completion",
Short: commands.CompletionShort,
Long: commands.CompletionLong,
PreRunE: preRunE,
Run: run,
}
}
func preRunE(cmd *cobra.Command, args []string) error {
// install by default
if os.Getenv("COMP_INSTALL") == "" {
if err := errors.Wrap(os.Setenv("COMP_INSTALL", "1")); err != nil {
return err
}
}
return nil
}
func run(cmd *cobra.Command, args []string) {
// find the root command
for cmd.Parent() != nil {
cmd = cmd.Parent()
}
// do completion
Complete(cmd).Complete("kustomize")
}
// Complete returns a completion command for a cobra command
func Complete(cmd *cobra.Command) *complete.Command {
cc := &complete.Command{
Flags: map[string]complete.Predictor{},
Sub: map[string]*complete.Command{},
}
// add completion for each subcommand
for i := range cmd.Commands() {
c := cmd.Commands()[i]
name := strings.Split(c.Use, " ")[0]
cc.Sub[name] = Complete(c)
}
// add completion for each flag
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
cc.Flags[flag.Name] = predict.Nothing
})
return cc
}

View File

@@ -1,88 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"sort"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func GetCountRunner(name string) *CountRunner {
r := &CountRunner{}
c := &cobra.Command{
Use: "count DIR...",
Short: commands.CountShort,
Long: commands.CountLong,
Example: commands.CountExamples,
RunE: r.runE,
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.Kind, "kind", true,
"count resources by kind.")
r.Command = c
return r
}
func CountCommand(name string) *cobra.Command {
return GetCountRunner(name).Command
}
// CountRunner contains the run function
type CountRunner struct {
IncludeSubpackages bool
Kind bool
Command *cobra.Command
}
func (r *CountRunner) runE(c *cobra.Command, args []string) error {
var inputs []kio.Reader
for _, a := range args {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
var out []kio.Writer
if r.Kind {
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
count := map[string]int{}
k := sets.String{}
for _, n := range nodes {
m, _ := n.GetMeta()
count[m.Kind]++
k.Insert(m.Kind)
}
order := k.List()
sort.Strings(order)
for _, k := range order {
fmt.Fprintf(c.OutOrStdout(), "%s: %d\n", k, count[k])
}
return nil
}))
} else {
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
fmt.Fprintf(c.OutOrStdout(), "%d\n", len(nodes))
return nil
}))
}
return handleError(c, kio.Pipeline{
Inputs: inputs,
Outputs: out,
}.Execute())
}

View File

@@ -1,73 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
func TestCountCommand_files(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-count-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetCountRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, "Deployment: 2\nService: 1\n", b.String()) {
return
}
}

View File

@@ -1,93 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
// FmtCmd returns a command FmtRunner.
func GetFmtRunner(name string) *FmtRunner {
r := &FmtRunner{}
c := &cobra.Command{
Use: "fmt",
Short: commands.FmtShort,
Long: commands.FmtLong,
Example: commands.FmtExamples,
RunE: r.runE,
PreRunE: r.preRunE,
}
fixDocs(name, c)
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
`pattern to use for generating filenames for resources -- may contain the following
formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace', '%k': 'kind'}`)
c.Flags().BoolVar(&r.SetFilenames, "set-filenames", false,
`if true, set default filenames on Resources without them`)
c.Flags().BoolVar(&r.KeepAnnotations, "keep-annotations", false,
`if true, keep index and filename annotations set on Resources.`)
c.Flags().BoolVar(&r.Override, "override", false,
`if true, override existing filepath annotations.`)
r.Command = c
return r
}
func FmtCommand(name string) *cobra.Command {
return GetFmtRunner(name).Command
}
// FmtRunner contains the run function
type FmtRunner struct {
Command *cobra.Command
FilenamePattern string
SetFilenames bool
KeepAnnotations bool
Override bool
}
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
if r.SetFilenames {
r.KeepAnnotations = true
}
return nil
}
func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
f := []kio.Filter{filters.FormatFilter{}}
// format with file names
if r.SetFilenames {
f = append(f, &filters.FileSetter{
FilenamePattern: r.FilenamePattern,
Override: r.Override,
})
}
// format stdin if there are no args
if len(args) == 0 {
rw := &kio.ByteReadWriter{
Reader: c.InOrStdin(),
Writer: c.OutOrStdout(),
KeepReaderAnnotations: r.KeepAnnotations,
}
return handleError(c, kio.Pipeline{
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute())
}
for i := range args {
path := args[i]
rw := &kio.LocalPackageReadWriter{
NoDeleteFiles: true,
PackagePath: path,
KeepReaderAnnotations: r.KeepAnnotations}
err := kio.Pipeline{
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute()
if err != nil {
return handleError(c, err)
}
}
return nil
}

View File

@@ -1,162 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
)
// TestCmd_files verifies the fmt command formats the files
func TestFmtCommand_files(t *testing.T) {
f1, err := ioutil.TempFile("", "cmdfmt*.yaml")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(f1.Name())
err = ioutil.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
if !assert.NoError(t, err) {
return
}
f2, err := ioutil.TempFile("", "cmdfmt*.yaml")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(f2.Name())
err = ioutil.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
r := cmd.GetFmtRunner("")
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
err = r.Command.Execute()
if !assert.NoError(t, err) {
return
}
// verify the contents
b, err := ioutil.ReadFile(f1.Name())
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
return
}
b, err = ioutil.ReadFile(f2.Name())
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
return
}
}
func TestFmtCommand_stdin(t *testing.T) {
out := &bytes.Buffer{}
r := cmd.GetFmtRunner("")
r.Command.SetOut(out)
r.Command.SetIn(bytes.NewReader(testyaml.UnformattedYaml1))
// fmt the input
err := r.Command.Execute()
assert.NoError(t, err)
// verify the output
assert.Equal(t, string(testyaml.FormattedYaml1), out.String())
}
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
// the files are formatted and the input is ignored
func TestFmtCmd_filesAndStdin(t *testing.T) {
f1, err := ioutil.TempFile("", "cmdfmt*.yaml")
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
if !assert.NoError(t, err) {
return
}
f2, err := ioutil.TempFile("", "cmdfmt*.yaml")
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
if !assert.NoError(t, err) {
return
}
out := &bytes.Buffer{}
in := &bytes.Buffer{}
r := cmd.GetFmtRunner("")
r.Command.SetOut(out)
r.Command.SetIn(in)
// fmt the files
r = cmd.GetFmtRunner("")
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
err = r.Command.Execute()
if !assert.NoError(t, err) {
return
}
// verify the output
b, err := ioutil.ReadFile(f1.Name())
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
return
}
b, err = ioutil.ReadFile(f2.Name())
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
return
}
err = r.Command.Execute()
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, "", out.String()) {
return
}
}
// TestCmd_files verifies the fmt command formats the files
func TestCmd_failFiles(t *testing.T) {
// fmt the files
r := cmd.GetFmtRunner("")
r.Command.SetArgs([]string{"notrealfile"})
err := r.Command.Execute()
assert.EqualError(t, err, "lstat notrealfile: no such file or directory")
}
// TestCmd_files verifies the fmt command formats the files
func TestCmd_failFileContents(t *testing.T) {
out := &bytes.Buffer{}
r := cmd.GetFmtRunner("")
r.Command.SetOut(out)
r.Command.SetIn(strings.NewReader(`{`))
// fmt the input
err := r.Command.Execute()
// expect an error
assert.EqualError(t, err, "yaml: line 1: did not find expected node content")
}

View File

@@ -1,125 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/api/resource"
)
// Cmd returns a command GrepRunner.
func GetGrepRunner(name string) *GrepRunner {
r := &GrepRunner{}
c := &cobra.Command{
Use: "grep QUERY [DIR]...",
Short: commands.GrepShort,
Long: commands.GrepLong,
Example: commands.GrepExamples,
PreRunE: r.preRunE,
RunE: r.runE,
Args: cobra.MinimumNArgs(1),
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
"annotate resources with their file origins.")
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "v", false,
" Selected Resources are those not matching any of the specified patterns..")
r.Command = c
return r
}
func GrepCommand(name string) *cobra.Command {
return GetGrepRunner(name).Command
}
// GrepRunner contains the run function
type GrepRunner struct {
IncludeSubpackages bool
KeepAnnotations bool
Command *cobra.Command
filters.GrepFilter
Format bool
}
func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
r.GrepFilter.Compare = func(a, b string) (int, error) {
qa, err := resource.ParseQuantity(a)
if err != nil {
return 0, fmt.Errorf("%s: %v", a, err)
}
qb, err := resource.ParseQuantity(b)
if err != nil {
return 0, err
}
return qa.Cmp(qb), err
}
parts, err := parseFieldPath(args[0])
if err != nil {
return err
}
var last []string
if strings.Contains(parts[len(parts)-1], ">=") {
last = strings.Split(parts[len(parts)-1], ">=")
r.MatchType = filters.GreaterThanEq
} else if strings.Contains(parts[len(parts)-1], "<=") {
last = strings.Split(parts[len(parts)-1], "<=")
r.MatchType = filters.LessThanEq
} else if strings.Contains(parts[len(parts)-1], ">") {
last = strings.Split(parts[len(parts)-1], ">")
r.MatchType = filters.GreaterThan
} else if strings.Contains(parts[len(parts)-1], "<") {
last = strings.Split(parts[len(parts)-1], "<")
r.MatchType = filters.LessThan
} else {
last = strings.Split(parts[len(parts)-1], "=")
r.MatchType = filters.Regexp
}
if len(last) > 2 {
return fmt.Errorf(
"ambiguous match -- multiple of ['<', '>', '<=', '>=', '=' in final path element: %s",
parts[len(parts)-1])
}
if len(last) > 1 {
r.Value = last[1]
}
r.Path = append(parts[:len(parts)-1], last[0])
return nil
}
func (r *GrepRunner) runE(c *cobra.Command, args []string) error {
var filters = []kio.Filter{r.GrepFilter}
var inputs []kio.Reader
for _, a := range args[1:] {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
return handleError(c, kio.Pipeline{
Inputs: inputs,
Filters: filters,
Outputs: []kio.Writer{kio.ByteWriter{
Writer: c.OutOrStdout(),
KeepReaderAnnotations: r.KeepAnnotations,
}},
}.Execute())
}

View File

@@ -1,272 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// TestGrepCommand_files verifies grep reads the files and filters them
func TestGrepCommand_files(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-kyaml-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetGrepRunner("")
r.Command.SetArgs([]string{"metadata.name=foo", d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/index: 0
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/index: 1
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
`, b.String()) {
return
}
}
// TestCmd_stdin verifies the grep command reads stdin if no files are provided
func TestGrepCmd_stdin(t *testing.T) {
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetGrepRunner("")
r.Command.SetArgs([]string{"metadata.name=foo"})
r.Command.SetOut(b)
r.Command.SetIn(bytes.NewBufferString(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
---
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`))
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
config.kubernetes.io/index: 0
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
config.kubernetes.io/index: 1
spec:
selector:
app: nginx
`, b.String()) {
return
}
}
// TestGrepCmd_errInputs verifies the grep command errors on invalid matches
func TestGrepCmd_errInputs(t *testing.T) {
b := &bytes.Buffer{}
r := cmd.GetGrepRunner("")
r.Command.SetArgs([]string{"metadata.name=foo=bar"})
r.Command.SetOut(b)
r.Command.SetIn(bytes.NewBufferString(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
`))
err := r.Command.Execute()
if !assert.Error(t, err) {
return
}
assert.Contains(t, err.Error(), "ambiguous match")
// fmt the files
b = &bytes.Buffer{}
r = cmd.GetGrepRunner("")
r.Command.SetArgs([]string{"spec.template.spec.containers[a[b=c].image=foo"})
r.Command.SetOut(b)
r.Command.SetIn(bytes.NewBufferString(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
`))
err = r.Command.Execute()
if !assert.Error(t, err) {
return
}
assert.Contains(t, err.Error(), "unrecognized path element:")
}
// TestGrepCommand_escapeDots verifies the grep command correctly escapes '\.' in inputs
func TestGrepCommand_escapeDots(t *testing.T) {
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetGrepRunner("")
r.Command.SetArgs([]string{"spec.template.spec.containers[name=nginx].image=nginx:1\\.7\\.9",
"--annotate=false"})
r.Command.SetOut(b)
r.Command.SetIn(bytes.NewBufferString(`
kind: Deployment
metadata:
labels:
app: nginx1.8
name: nginx1.8
annotations:
app: nginx1.8
spec:
replicas: 1
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1
---
kind: Deployment
metadata:
labels:
app: nginx1.7
name: nginx1.7
annotations:
app: nginx1.7
spec:
replicas: 1
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9
`))
err := r.Command.Execute()
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, `kind: Deployment
metadata:
labels:
app: nginx1.7
name: nginx1.7
annotations:
app: nginx1.7
spec:
replicas: 1
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9
`, b.String()) {
return
}
}

View File

@@ -1,68 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
func GetMergeRunner(name string) *MergeRunner {
r := &MergeRunner{}
c := &cobra.Command{
Use: "merge [SOURCE_DIR...] [DESTINATION_DIR]",
Short: commands.MergeShort,
Long: commands.MergeLong,
Example: commands.MergeExamples,
RunE: r.runE,
}
fixDocs(name, c)
r.Command = c
r.Command.Flags().BoolVar(&r.InvertOrder, "invert-order", false,
"if true, merge Resources in the reverse order")
return r
}
func MergeCommand(name string) *cobra.Command {
return GetMergeRunner(name).Command
}
// MergeRunner contains the run function
type MergeRunner struct {
Command *cobra.Command
InvertOrder bool
}
func (r *MergeRunner) runE(c *cobra.Command, args []string) error {
var inputs []kio.Reader
// add the packages in reverse order -- the arg list should be highest precedence first
// e.g. merge from -> to, but the MergeFilter is highest precedence last
for i := len(args) - 1; i >= 0; i-- {
inputs = append(inputs, kio.LocalPackageReader{PackagePath: args[i]})
}
// if there is no "from" package, read from stdin
rw := &kio.ByteReadWriter{
Reader: c.InOrStdin(),
Writer: c.OutOrStdout(),
KeepReaderAnnotations: true,
}
if len(inputs) < 2 {
inputs = append(inputs, rw)
}
// write to the "to" package if specified
var outputs []kio.Writer
if len(args) != 0 {
outputs = append(outputs, kio.LocalPackageWriter{PackagePath: args[len(args)-1]})
}
// if there is no "to" package, write to stdout
if len(outputs) == 0 {
outputs = append(outputs, rw)
}
filters := []kio.Filter{filters.MergeFilter{}, filters.FormatFilter{}}
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: filters, Outputs: outputs}.Execute())
}

View File

@@ -1,56 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/runfn"
)
// GetCatRunner returns a RunFnRunner.
func GetRunFnRunner(name string) *RunFnRunner {
r := &RunFnRunner{}
c := &cobra.Command{
Use: "run DIR",
Aliases: []string{"run-fns"},
Short: commands.RunFnsShort,
Long: commands.RunFnsLong,
Example: commands.RunFnsExamples,
RunE: r.runE,
Args: cobra.ExactArgs(1),
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
r.Command = c
r.Command.Flags().BoolVar(
&r.DryRun, "dry-run", false, "print results to stdout")
r.Command.Flags().StringSliceVar(
&r.FnPaths, "fn-path", []string{},
"directories containing functions without configuration")
r.Command.AddCommand(XArgsCommand())
r.Command.AddCommand(WrapCommand())
return r
}
func RunFnCommand(name string) *cobra.Command {
return GetRunFnRunner(name).Command
}
// RunFnRunner contains the run function
type RunFnRunner struct {
IncludeSubpackages bool
Command *cobra.Command
DryRun bool
FnPaths []string
}
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
rec := runfn.RunFns{Path: args[0], FunctionPaths: r.FnPaths}
if r.DryRun {
rec.Output = c.OutOrStdout()
}
return handleError(c, rec.Execute())
}

View File

@@ -1,15 +0,0 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
template:
spec:
containers:
- name: test
image: nginx:v1.9

View File

@@ -1,51 +0,0 @@
#!/bin/bash
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
cat <<End-of-message
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${NAME}
labels:
app: nginx
name: ${NAME}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: nginx
name: ${NAME}
template:
metadata:
labels:
app: nginx
name: ${NAME}
spec:
containers:
- name: ${NAME}
image: nginx:v1.7
ports:
- containerPort: 8080
name: http
---
apiVersion: v1
kind: Service
metadata:
name: ${NAME}
labels:
app: nginx
name: ${NAME}
spec:
ports:
# This i the port.
- port: 8080
targetPort: 8080
name: http
selector:
app: nginx
name: ${NAME}
End-of-message

View File

@@ -1,187 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/cmd/config/cmddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func GetTreeRunner(name string) *TreeRunner {
r := &TreeRunner{}
c := &cobra.Command{
Use: "tree DIR",
Short: commands.TreeShort,
Long: commands.TreeLong,
Example: commands.TreeExamples,
RunE: r.runE,
Args: cobra.MaximumNArgs(1),
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
// TODO(pwittrock): Figure out if these are the right things to expose, and consider making it
// a list of options instead of individual flags
c.Flags().BoolVar(&r.name, "name", false, "print name field")
c.Flags().BoolVar(&r.resources, "resources", false, "print resources field")
c.Flags().BoolVar(&r.ports, "ports", false, "print ports field")
c.Flags().BoolVar(&r.images, "image", false, "print image field")
c.Flags().BoolVar(&r.replicas, "replicas", false, "print replicas field")
c.Flags().BoolVar(&r.args, "args", false, "print args field")
c.Flags().BoolVar(&r.cmd, "command", false, "print command field")
c.Flags().BoolVar(&r.env, "env", false, "print env field")
c.Flags().BoolVar(&r.all, "all", false, "print all field infos")
c.Flags().StringSliceVar(&r.fields, "field", []string{}, "print field")
c.Flags().BoolVar(&r.includeLocal, "include-local", false,
"if true, include local-config in the output.")
c.Flags().BoolVar(&r.excludeNonLocal, "exclude-non-local", false,
"if true, exclude non-local-config in the output.")
c.Flags().StringVar(&r.structure, "graph-structure", "directory",
"Graph structure to use for printing the tree. may be any of: "+
strings.Join(kio.GraphStructures, ","))
r.Command = c
return r
}
func TreeCommand(name string) *cobra.Command {
return GetTreeRunner(name).Command
}
// TreeRunner contains the run function
type TreeRunner struct {
IncludeSubpackages bool
Command *cobra.Command
name bool
resources bool
ports bool
images bool
replicas bool
all bool
env bool
args bool
cmd bool
fields []string
includeLocal bool
excludeNonLocal bool
structure string
}
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
var input kio.Reader
var root = "."
if len(args) == 1 {
root = filepath.Clean(args[0])
input = kio.LocalPackageReader{PackagePath: args[0]}
} else {
input = &kio.ByteReader{Reader: c.InOrStdin()}
}
var fields []kio.TreeWriterField
for _, field := range r.fields {
path, err := parseFieldPath(field)
if err != nil {
return err
}
fields = append(fields, newField(path...))
}
if r.name || (r.all && !c.Flag("name").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "name"),
newField("spec", "template", "spec", "containers", "[name=.*]", "name"),
)
}
if r.images || (r.all && !c.Flag("image").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "image"),
newField("spec", "template", "spec", "containers", "[name=.*]", "image"),
)
}
if r.cmd || (r.all && !c.Flag("command").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "command"),
newField("spec", "template", "spec", "containers", "[name=.*]", "command"),
)
}
if r.args || (r.all && !c.Flag("args").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "args"),
newField("spec", "template", "spec", "containers", "[name=.*]", "args"),
)
}
if r.env || (r.all && !c.Flag("env").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "env"),
newField("spec", "template", "spec", "containers", "[name=.*]", "env"),
)
}
if r.replicas || (r.all && !c.Flag("replicas").Changed) {
fields = append(fields,
newField("spec", "replicas"),
)
}
if r.resources || (r.all && !c.Flag("resources").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "resources"),
newField("spec", "template", "spec", "containers", "[name=.*]", "resources"),
)
}
if r.ports || (r.all && !c.Flag("ports").Changed) {
fields = append(fields,
newField("spec", "containers", "[name=.*]", "ports"),
newField("spec", "template", "spec", "containers", "[name=.*]", "ports"),
newField("spec", "ports"),
)
}
// show reconcilers in tree
fltrs := []kio.Filter{&filters.IsLocalConfig{
IncludeLocalConfig: r.includeLocal,
ExcludeNonLocalConfig: r.excludeNonLocal,
}}
return handleError(c, kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: fltrs,
Outputs: []kio.Writer{kio.TreeWriter{
Root: root,
Writer: c.OutOrStdout(),
Fields: fields,
Structure: kio.TreeStructure(r.structure)}},
}.Execute())
}
func newField(val ...string) kio.TreeWriterField {
if strings.HasPrefix(strings.Join(val, "."), "spec.template.spec.containers") {
return kio.TreeWriterField{
Name: "spec.template.spec.containers",
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
SubName: val[len(val)-1],
}
}
if strings.HasPrefix(strings.Join(val, "."), "spec.containers") {
return kio.TreeWriterField{
Name: "spec.containers",
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
SubName: val[len(val)-1],
}
}
return kio.TreeWriterField{
Name: strings.Join(val, "."),
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
}
}

View File

@@ -1,355 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// TestCmd_files verifies fmt reads the files and filters them
func TestTreeCommand_files(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 1
---
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetTreeRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, fmt.Sprintf(`%s
├── [f1.yaml] Deployment foo
├── [f1.yaml] Service foo
└── [f2.yaml] Deployment bar
`, d), b.String()) {
return
}
}
func TestTreeCommand_stdin(t *testing.T) {
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetTreeRunner("")
r.Command.SetArgs([]string{})
r.Command.SetIn(bytes.NewBufferString(`apiVersion: extensions/v1
kind: Deployment
metadata:
labels:
app: nginx2
name: foo3
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
apiVersion: extensions/v1
kind: Deployment
metadata:
labels:
app: nginx2
name: foo3
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx2
name: foo3
namespace: default
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx2
name: foo2
namespace: default2
annotations:
app: nginx2
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx3
name: foo
namespace: default
annotations:
app: nginx3
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
replicas: 1
---
kind: Deployment
metadata:
labels:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: bar-package
config.kubernetes.io/path: f2.yaml
name: bar
spec:
replicas: 3
---
kind: Service
metadata:
name: foo
namespace: default
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f1.yaml
spec:
selector:
app: nginx
`))
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, fmt.Sprintf(`.
├── [f1.yaml] Deployment default/foo
├── [f1.yaml] Service default/foo
├── [f1.yaml] Deployment default/foo3
├── [f1.yaml] Deployment default/foo3
├── [f1.yaml] Deployment default/foo3
├── [f1.yaml] Deployment default2/foo2
└── bar-package
└── [f2.yaml] Deployment bar
`), b.String()) {
return
}
}
func TestTreeCommand_includeReconcilers(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: gcr.io/example/reconciler:v1
kind: Abstraction
metadata:
name: foo
spec:
replicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetTreeRunner("")
r.Command.SetArgs([]string{d, "--include-local"})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, fmt.Sprintf(`%s
├── [f1.yaml] Deployment foo
├── [f1.yaml] Service foo
├── [f2.yaml] Deployment bar
└── [f2.yaml] Abstraction foo
`, d), b.String()) {
return
}
}
func TestTreeCommand_excludeNonReconcilers(t *testing.T) {
d, err := ioutil.TempDir("", "kustmoize-tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := cmd.GetTreeRunner("")
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, fmt.Sprintf(`%s
└── [f2.yaml] Abstraction foo
`, d), b.String()) {
return
}
}

View File

@@ -1,74 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"os"
"strings"
"github.com/go-errors/errors"
"github.com/spf13/cobra"
)
// parseFieldPath parse a flag value into a field path
func parseFieldPath(path string) ([]string, error) {
// fixup '\.' so we don't split on it
match := strings.ReplaceAll(path, "\\.", "$$$$")
parts := strings.Split(match, ".")
for i := range parts {
parts[i] = strings.ReplaceAll(parts[i], "$$$$", ".")
}
// split the list index from the list field
var newParts []string
for i := range parts {
if !strings.Contains(parts[i], "[") {
newParts = append(newParts, parts[i])
continue
}
p := strings.Split(parts[i], "[")
if len(p) != 2 {
return nil, fmt.Errorf("unrecognized path element: %s. "+
"Should be of the form 'list[field=value]'", parts[i])
}
p[1] = "[" + p[1]
newParts = append(newParts, p[0], p[1])
}
return newParts, nil
}
func handleError(c *cobra.Command, err error) error {
if err == nil {
return nil
}
if StackOnError {
if err, ok := err.(*errors.Error); ok {
fmt.Fprint(os.Stderr, fmt.Sprintf("%s", err.Stack()))
}
}
if ExitOnError {
fmt.Fprintf(c.ErrOrStderr(), "Error: %v\n", err)
os.Exit(1)
}
return err
}
// ExitOnError if true, will cause commands to call os.Exit instead of returning an error.
// Used for skipping printing usage on failure.
var ExitOnError bool
// StackOnError if true, will print a stack trace on failure.
var StackOnError bool
const cmdName = "kustomize config"
// FixDocs replaces instances of old with new in the docs for c
func fixDocs(new string, c *cobra.Command) {
c.Use = strings.ReplaceAll(c.Use, cmdName, new)
c.Short = strings.ReplaceAll(c.Short, cmdName, new)
c.Long = strings.ReplaceAll(c.Long, cmdName, new)
c.Example = strings.ReplaceAll(c.Example, cmdName, new)
}

Some files were not shown because too many files have changed in this diff Show More