Compare commits

...

68 Commits

Author SHA1 Message Date
Phillip Wittrock
e59e477702 update go.mod for release 2020-01-03 10:01:16 -08:00
Phillip Wittrock
4264ad0ad4 release cmd/config 0.0.5 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-03 10:01:06 -08:00
Kubernetes Prow Robot
2880c2ae5d Merge pull request #2026 from pwittrock/master
kyaml `setters`: support full and partial field replacement
2020-01-02 16:33:41 -08:00
Phillip Wittrock
e0b766ee46 Fix issue with setting set-by 2020-01-02 15:55:38 -08:00
Phillip Wittrock
ac9b0a3e9e kyaml setters: support full and partial field replacement 2020-01-02 14:00:43 -08:00
Kubernetes Prow Robot
3577a7e174 Merge pull request #2022 from mortent/Docs
Better documentation for the resource and status commands.
2020-01-02 09:51:41 -08:00
Kubernetes Prow Robot
e4274bccba Merge pull request #2024 from pwittrock/release
Release kyaml and cmd/config 0.0.4
2020-01-02 09:49:40 -08:00
Phillip Wittrock
2554cd00eb update go.mod for release 2020-01-02 09:02:06 -08:00
Phillip Wittrock
383244cd63 Release cmd/config 0.0.4
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-02 09:01:56 -08:00
Phillip Wittrock
4b64801d44 release kyaml and cmd/config 0.0.4 2020-01-02 08:59:12 -08:00
Phillip Wittrock
a9c5f90805 fix releasing message 2020-01-02 08:58:01 -08:00
Kubernetes Prow Robot
29a1b96b96 Merge pull request #2023 from pwittrock/set
Refactor `set`
2020-01-02 08:57:42 -08:00
Phillip Wittrock
b37abbf057 Refactor set
- Implement inline setters as OpenAPI extensions
- Naming cleanup substitute -> set
- Documentation cleanup
- Simplify implementation
2020-01-02 08:32:38 -08:00
Kubernetes Prow Robot
3bef339186 Merge pull request #1890 from joncwong/merge3
Integrate merge3 as command for 3-way merges
2020-01-02 07:43:41 -08:00
Kubernetes Prow Robot
fc4a73f816 Merge pull request #2014 from alexeldeib/ace/varName
fix empty var names + clean up cross platform tests
2020-01-02 07:39:40 -08:00
Kubernetes Prow Robot
6e6878730c Merge pull request #2010 from prachirp/funcAnnotation
Add support for config.k8s.io/function annotation
2020-01-02 07:21:40 -08:00
Morten Torkildsen
71ce46416e Better documentation for the resource and status commands. 2020-01-01 14:05:14 -08:00
Ace Eldeib
c4d3a2ff3f tests: make paths for x-plat friendly 2019-12-29 17:28:45 -08:00
Ace Eldeib
e0f62c67f6 fix: avoid passing empty variable names 2019-12-29 16:48:53 -08:00
Jonathan Wong
ef82c736b9 Remove newline 2019-12-24 21:46:59 -08:00
Jonathan Wong
fc57f530ee Clean up merge3 changes 2019-12-24 21:18:40 -08:00
Jonathan Wong
4bdfb1c511 Remove refactored cmd directory 2019-12-24 21:14:27 -08:00
Kubernetes Prow Robot
697a6e9759 Merge pull request #2009 from KnVerey/default_structure
config tree defaults to graph structure when ownerRefs available
2019-12-24 10:07:29 -08:00
Prachi Pendse
f6320ca379 Add support for config.k8s.io/function annotation 2019-12-23 13:44:42 -08:00
Katrina Verey
24cf0c1fdc config tree defaults to graph structure when ownerRefs available 2019-12-23 16:36:25 -05:00
Kubernetes Prow Robot
3900166fdf Merge pull request #2006 from pwittrock/set
Move setters to be available as libraries
2019-12-21 16:53:33 -08:00
Phillip Wittrock
ad4eb87e2e move setters to be available as libraries 2019-12-21 16:29:06 -08:00
Kubernetes Prow Robot
19928abb6f Merge pull request #2005 from pwittrock/complete
completion should suggest directories if use contains DIR
2019-12-21 16:21:32 -08:00
Phillip Wittrock
8a1874d20d completion should suggest directories if use contains DIR 2019-12-21 16:04:23 -08:00
Kubernetes Prow Robot
a280cdf5ee Merge pull request #2000 from pwittrock/master
release `kyaml` and `cmd/config` 0.0.3
2019-12-20 09:57:33 -08:00
Phillip Wittrock
b9da33afd4 update go.mod for release 2019-12-20 09:44:25 -08:00
Phillip Wittrock
23e339b86c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-20 09:44:21 -08:00
Phillip Wittrock
fdf8f1b3df release kyaml and cmd/config 0.0.3 2019-12-20 09:40:10 -08:00
Kubernetes Prow Robot
105f25860e Merge pull request #1999 from pwittrock/fix-fmt
fix issues caused by dropping or overriding style
2019-12-20 09:27:33 -08:00
Jonathan Wong
e35e0bff60 Update docs for merge3 2019-12-20 03:48:29 -08:00
Jonathan Wong
8095b16c9a Add test for merge3 command 2019-12-20 03:47:45 -08:00
Jonathan Wong
d04b4a2899 Remove unnecessary 1-line comment 2019-12-20 03:47:45 -08:00
Jonathan Wong
aafeb75ef1 Rebase merge3 branch into master 2019-12-20 03:43:11 -08:00
Phillip Wittrock
98431f6a00 fix kyaml issue where dropping Style created issues
dropping the node style creates a compatibility issue where quotes around "on" are dropped
because yaml.v3 interprets it as a string.

other yaml parsers interpret on as a bool value, and parse it as a bool rather than string.

fix: retain the original style so it is kept as quoted.

- fmt: don't drop the styles
- merge2: keep the style when merging elements
- setting a field: if changing the value of a scalar field, retain its style by default
2019-12-19 20:25:31 -08:00
Jeff Regan
1ce3d9e099 Merge pull request #1985 from haiyanmeng/expose-es
Crawler Improvements
2019-12-19 18:05:49 -08:00
Kubernetes Prow Robot
e199c7f805 Merge pull request #1998 from seans3/kustomize-apply-deps
Update go.mod to point to most recent version of k8s.io/kubectl
2019-12-19 13:35:57 -08:00
Sean Sullivan
49f17586ca Update go.mod to point to most recent version of k8s.io/kubectl 2019-12-19 11:20:12 -08:00
Haiyan Meng
be2e03681d Remove unused param from IndexFunc 2019-12-18 15:56:44 -08:00
Haiyan Meng
127541f610 Support diffrent modes of running the crawler 2019-12-18 15:56:44 -08:00
Haiyan Meng
f5ff254203 Update deps 2019-12-18 15:56:44 -08:00
Haiyan Meng
a35f002139 Run goimports 2019-12-18 15:56:44 -08:00
Haiyan Meng
bef157d6b3 Fix insert/updating document logic 2019-12-18 15:56:44 -08:00
Haiyan Meng
2c2aa928cc Delete non-existing documents from the index 2019-12-18 15:56:44 -08:00
Haiyan Meng
1eb713157c Sort the string slice fields of a document to avoid updating the index
unnecessarily
2019-12-18 15:56:44 -08:00
Haiyan Meng
272b7a6fcd Use UpdateRequest to insert/update a document
Currently, `IndexRequest` is used to insert/update a document, which
increases the version of the document every time IndexRequest.Do is
called.
2019-12-18 15:56:44 -08:00
Haiyan Meng
5598d35e4b Add a summary for doCrawl 2019-12-18 15:56:44 -08:00
Haiyan Meng
8c89f0946c Avoid to index a document if FetchDcoument or SetCreated fails 2019-12-18 15:56:44 -08:00
Haiyan Meng
12fc8f41c7 Add support for github paths starting with "git@github.com:" 2019-12-18 15:56:44 -08:00
Haiyan Meng
e44d1298df Return errors if http Client.Do resp status code is not 2xx 2019-12-18 15:56:44 -08:00
Kubernetes Prow Robot
7e56c2c768 Merge pull request #1995 from iliazlobin/varreference
Handle variables in annotations
2019-12-18 11:27:57 -08:00
Ilia Zlobin
cc8b100331 Handle variables in annotations 2019-12-18 17:04:43 +03:00
Kubernetes Prow Robot
2c1cd6de41 Merge pull request #1880 from mortent/documentation
Add documentation for the kstatus library
2019-12-17 19:57:57 -08:00
Kubernetes Prow Robot
32b55109f7 Merge pull request #1978 from oke-py/doc
Fixed a wrong path and run `make all`
2019-12-17 16:39:58 -08:00
Kubernetes Prow Robot
21bf05d05e Merge pull request #1986 from pwittrock/release
Update api to 0.3.1
2019-12-17 15:17:58 -08:00
Kubernetes Prow Robot
d5e88977b3 Merge pull request #1981 from pwittrock/fieldmeta
Rename ownedBy and refactor kyaml travis
2019-12-17 15:01:59 -08:00
Phillip Wittrock
c66dd497c3 Update api to 0.3.1 2019-12-17 14:12:25 -08:00
Phillip Wittrock
502f86a982 rename ownedBy json value to setBy 2019-12-17 11:50:18 -08:00
Phillip Wittrock
ae2bfc8ee6 Fix travis kyaml exit and targets
- properly exit non-0 if there are any uncommitted files
- make functions/examples targes
- add missing licenses
- refactor into loop
2019-12-17 11:50:18 -08:00
Naoki Oketani
7a384bc0d8 Fixed a wrong path and run make all 2019-12-17 18:22:49 +09:00
Phillip Wittrock
9555009df8 update go.mod for release 2019-12-16 14:21:02 -08:00
Phillip Wittrock
91a10c560c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-16 14:20:58 -08:00
Phillip Wittrock
780cb19c4d drop replace 2019-12-13 13:50:17 -08:00
Morten Torkildsen
c7307a9b28 Add documentation for the kstatus library 2019-12-10 20:31:08 -08:00
97 changed files with 2581 additions and 1646 deletions

View File

@@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
@@ -21,14 +22,53 @@ const (
redisCacheURL = "REDIS_CACHE_URL"
redisKeyURL = "REDIS_KEY_URL"
retryCount = 3
githubUserEnv = "GITHUB_USER"
githubRepoEnv = "GITHUB_REPO"
)
func main() {
githubUser := os.Getenv(githubUserEnv)
githubRepo := os.Getenv(githubRepoEnv)
type CrawlMode int
const (
CrawlUnknown CrawlMode = iota
// Crawl all the kustomization files in all the repositories of a Github user
CrawlUser
// Crawl all the kustomization files in a Github repo
CrawlRepo
// Crawl all the documents in the index
CrawlIndex
// Crawl all the kustomization files on Github
CrawlGithub
// Crawl all the documents in the index and crawling all the kustomization files on Github
CrawlIndexAndGithub
)
func NewCrawlMode(s string) CrawlMode {
switch s {
case "github-user":
return CrawlUser
case "github-repo":
return CrawlRepo
case "":
return CrawlIndexAndGithub
case "index":
return CrawlIndex
case "github":
return CrawlGithub
default:
return CrawlUnknown
}
}
func Usage() {
fmt.Printf("Usage: %s [mode] [githubUser|githubRepo]\n", os.Args[0])
fmt.Printf("\tmode can be one of [github-user, github-repo, index, github]\n")
fmt.Printf("%s: crawl all the documents in the index and crawling all the kustomization files on Github\n", os.Args[0])
fmt.Printf("%s index: crawl all the documents in the index\n", os.Args[0])
fmt.Printf("%s gihub: crawl all the kustomization files on Github\n", os.Args[0])
fmt.Printf("%s github-user <github-user>: Crawl all the kustomization files in all the repositories of a Github user\n", os.Args[0])
fmt.Printf("\tFor example, %s github-user kubernetes-sigs\n", os.Args[0])
fmt.Printf("%s github-repo <github-repo>: Crawl all the kustomization files in a Github repo\n", os.Args[0])
fmt.Printf("\tFor example, %s github-repo kubernetes-sigs/kustomize\n", os.Args[0])
}
func main() {
githubToken := os.Getenv(githubAccessTokenVar)
if githubToken == "" {
fmt.Printf("Must set the variable '%s' to make github requests.\n",
@@ -43,8 +83,6 @@ func main() {
return
}
seedDocs := make(crawler.CrawlSeed, 0)
cacheURL := os.Getenv(redisCacheURL)
cache, err := redis.DialURL(cacheURL)
clientCache := &http.Client{}
@@ -65,12 +103,17 @@ func main() {
}
// Index updates the value in the index.
index := func(cdoc crawler.CrawledDocument, crwlr crawler.Crawler) error {
indexFunc := func(cdoc crawler.CrawledDocument, mode index.Mode) error {
switch d := cdoc.(type) {
case *doc.KustomizationDocument:
fmt.Println("Inserting: ", d)
_, err := idx.Put(d.ID(), d)
return err
switch mode {
case index.Delete:
fmt.Println("Deleting: ", d)
return idx.Delete(d.ID())
default:
fmt.Println("Inserting: ", d)
return idx.Put(d.ID(), d)
}
default:
return fmt.Errorf("type %T not supported", d)
}
@@ -80,30 +123,41 @@ func main() {
// This helps avoid indexing a given document multiple times.
seen := make(map[string]struct{})
var ghCrawler crawler.Crawler
if githubRepo != "" {
ghCrawler = github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml"),
github.Repo(githubRepo)),
)
} else if githubUser != "" {
ghCrawler = github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml"),
github.User(githubUser)),
)
var mode CrawlMode
if len(os.Args) == 1 {
mode = CrawlIndexAndGithub
} else {
ghCrawler = github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml")),
)
mode = NewCrawlMode(os.Args[1])
}
// get all the documents in the index
ghCrawlerConstructor := func(user, repo string) crawler.Crawler {
if user != "" {
return github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml"),
github.User(user)),
)
} else if repo != "" {
return github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml"),
github.Repo(repo)),
)
} else {
return github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml")),
)
}
}
seedDocs := make(crawler.CrawlSeed, 0)
// get all the documents in the index
getSeedDocsFunc := func() {
query := []byte(`{ "query":{ "match_all":{} } }`)
it := idx.IterateQuery(query, 10000, 60*time.Second)
for it.Next() {
@@ -116,7 +170,35 @@ func main() {
}
}
crawlers := []crawler.Crawler{ghCrawler}
crawler.CrawlFromSeed(ctx, seedDocs, crawlers, docConverter, index, seen)
crawler.CrawlGithub(ctx, crawlers, docConverter, index, seen)
switch mode {
case CrawlIndexAndGithub:
getSeedDocsFunc()
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
crawler.CrawlFromSeed(ctx, seedDocs, crawlers, docConverter, indexFunc, seen)
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlIndex:
getSeedDocsFunc()
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
crawler.CrawlFromSeed(ctx, seedDocs, crawlers, docConverter, indexFunc, seen)
case CrawlGithub:
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlUser:
if len(os.Args) < 3 {
Usage()
log.Fatalf("Please specify a github user!")
}
crawlers := []crawler.Crawler{ghCrawlerConstructor(os.Args[2], "")}
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlRepo:
if len(os.Args) < 3 {
Usage()
log.Fatalf("Please specify a github repo!")
}
crawlers := []crawler.Crawler{ghCrawlerConstructor("", os.Args[2])}
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlUnknown:
Usage()
log.Fatalf("The crawler mode must be one of [github-user, github-repo, index, github]")
}
}

View File

@@ -0,0 +1,54 @@
There are three ways of running the crawler job.
# Crawling all the documents in the index and crawling all the kustomization files on Github
This is the default setting of the crawler job. The `command` and `args` field
of the container should be:
```
command: ["/crawler"]
args: []
```
Or
```
command: ["/crawler"]
args: [""]
```
# Crawling all the documents in the index
The `command` and `args` field of the container should be:
```
command: ["/crawler"]
args: ["index"]
```
# Crawling all the kustomization files on Github
The `command` and `args` field of the container should be:
```
command: ["/crawler"]
args: ["github"]
```
# Crawling all the kustomization files in a Github repo
The `command` and `args` field of the container should be like:
```
command: ["/crawler"]
args: ["github-repo", "kubernetes-sigs/kustomize"]
```
# Crawling all the kustomization files in all the repositories of a Github user
The `command` and `args` field of the container should be like:
```
command: ["/crawler"]
args: ["github-user", "kubernetes-sigs"]
```

View File

@@ -8,8 +8,10 @@ spec:
restartPolicy: OnFailure
containers:
- name: crawler
image: gcr.io/kustomize-search/crawler:latest
image: gcr.io/haiyanmeng-gke-dev/crawler:v1
imagePullPolicy: Always
command: ["/crawler"]
args: ["github-repo", "kubernetes-sigs/kustomize"]
env:
- name: GITHUB_ACCESS_TOKEN
valueFrom:

View File

@@ -10,6 +10,8 @@ import (
"os"
"sync"
"sigs.k8s.io/kustomize/api/internal/crawl/index"
_ "github.com/gomodule/redigo/redis"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
@@ -47,7 +49,7 @@ type CrawledDocument interface {
type CrawlSeed []*doc.Document
type IndexFunc func(CrawledDocument, Crawler) error
type IndexFunc func(CrawledDocument, index.Mode) error
type Converter func(*doc.Document) (CrawledDocument, error)
func logIfErr(err error) {
@@ -72,17 +74,18 @@ func addBranches(cdoc CrawledDocument, match Crawler, indx IndexFunc,
seen[cdoc.ID()] = struct{}{}
// Insert into index
err := indx(cdoc, match)
logIfErr(err)
if err != nil {
if err := indx(cdoc, index.InsertOrUpdate); err != nil {
logger.Printf("Failed to insert or update %s %s: %v",
cdoc.GetDocument().RepositoryURL, cdoc.GetDocument().FilePath, err)
return
}
deps, err := cdoc.GetResources()
logIfErr(err)
if err != nil {
logger.Println(err)
return
}
for _, dep := range deps {
if _, ok := seen[dep.ID()]; ok {
continue
@@ -93,7 +96,16 @@ func addBranches(cdoc CrawledDocument, match Crawler, indx IndexFunc,
func doCrawl(ctx context.Context, docsPtr *CrawlSeed, crawlers []Crawler, conv Converter, indx IndexFunc,
seen map[string]struct{}, stack *CrawlSeed) {
docCount := 0
UpdatedDocCount := 0
seenDocCount := 0
cachedDocCount := 0
findMatchErrCount := 0
FetchDocumentErrCount := 0
SetCreatedErrCount := 0
convErrCount := 0
deleteDocCount := 0
// During the execution of the for loop, more Documents may be added into (*docsPtr).
for len(*docsPtr) > 0 {
// get the last Document in (*docPtr), which will be crawled in this iteration.
@@ -103,38 +115,68 @@ func doCrawl(ctx context.Context, docsPtr *CrawlSeed, crawlers []Crawler, conv C
*docsPtr = (*docsPtr)[:(len(*docsPtr) - 1)]
if _, ok := seen[tail.ID()]; ok {
seenDocCount++
continue
}
if tail.WasCached() {
logger.Printf("%s %s is cached already", tail.RepositoryURL, tail.FilePath)
cachedDocCount++
continue
}
docCount++
match := findMatch(tail, crawlers)
if match == nil {
logIfErr(fmt.Errorf(
"%v could not match any crawler", tail))
logIfErr(fmt.Errorf("%v could not match any crawler", tail))
findMatchErrCount++
continue
}
logger.Println("Crawling ", tail.RepositoryURL, tail.FilePath)
err := match.FetchDocument(ctx, tail)
logIfErr(err)
// If there was no change or there is an error, we don't have
// to branch out, since the dependencies are already in the
// index, or we cannot find the document.
if err != nil || tail.WasCached() {
if tail.WasCached() {
logger.Println(tail.RepositoryURL, tail.FilePath, "is cached already")
if err := match.FetchDocument(ctx, tail); err != nil {
logger.Printf("FetchDocument failed on %s %s: %v",
tail.RepositoryURL, tail.FilePath, err)
FetchDocumentErrCount++
// delete the document from the index
cdoc := &doc.KustomizationDocument{
Document: *tail,
}
seen[cdoc.ID()] = struct{}{}
if err := indx(cdoc, index.Delete); err != nil {
logger.Printf("Failed to delete %s %s: %v",
cdoc.RepositoryURL, cdoc.FilePath, err)
}
deleteDocCount++
continue
}
logIfErr(match.SetCreated(ctx, tail))
if err := match.SetCreated(ctx, tail); err != nil {
logger.Printf("SetCreated failed on %s %s: %v",
tail.RepositoryURL, tail.FilePath, err)
SetCreatedErrCount++
}
cdoc, err := conv(tail)
logIfErr(err)
// If conv returns an error, cdoc can still be added into the index so that
// cdoc.Document can be searched.
if err != nil {
logger.Printf("conv failed on %s %s: %v",
tail.RepositoryURL, tail.FilePath, err)
convErrCount++
}
UpdatedDocCount++
addBranches(cdoc, match, indx, seen, stack)
}
logger.Printf("%d documents were crawled by doCrawl\n", docCount)
logger.Printf("Summary of doCrawl:\n")
logger.Printf("\t%d documents were updated\n", UpdatedDocCount)
logger.Printf("\t%d documents were seen by the crawler already and skipped\n", seenDocCount)
logger.Printf("\t%d documents were cached already and skipped\n", cachedDocCount)
logger.Printf("\t%d documents didn't have a matching crawler and skipped\n", findMatchErrCount)
logger.Printf("\t%d documents cannot be fetched, %d out of them are deleted\n",
FetchDocumentErrCount, deleteDocCount)
logger.Printf("\t%d documents cannot update its creation time but still were inserted or updated in the index\n", SetCreatedErrCount)
logger.Printf("\t%d documents cannot be converted but still were inserted or updated in the index\n", convErrCount)
}
// CrawlFromSeed updates all the documents in seed, and crawls all the new

View File

@@ -11,6 +11,8 @@ import (
"testing"
"time"
"sigs.k8s.io/kustomize/api/internal/crawl/index"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
"sigs.k8s.io/kustomize/api/konfig"
)
@@ -316,7 +318,7 @@ resources:
Document: *d,
}, nil
},
func(d CrawledDocument, cr Crawler) error {
func(d CrawledDocument, mode index.Mode) error {
visited[d.ID()]++
return nil
},

View File

@@ -93,6 +93,9 @@ func (gc githubCrawler) Crawl(
return nil
}
// FetchDocument first tries to fetch the document with d.FilePath. If it fails,
// it will try to add each string in konfig.RecognizedKustomizationFileNames() to
// d.FilePath, and try to fetch the document again.
func (gc githubCrawler) FetchDocument(_ context.Context, d *doc.Document) error {
repoURL := d.RepositoryURL + "/" + d.FilePath + "?ref=" + d.DefaultBranch
repoSpec, err := git.NewRepoSpecFromUrl(repoURL)
@@ -115,18 +118,18 @@ func (gc githubCrawler) FetchDocument(_ context.Context, d *doc.Document) error
d.FilePath = d.FilePath + path
return nil
}
return err
}
resp, err := gc.client.GetRawUserContent(url)
if err := handle(resp, err, ""); err == nil {
resp, errGetRawUserContent := gc.client.GetRawUserContent(url)
if err := handle(resp, errGetRawUserContent, ""); err == nil {
return nil
}
for _, file := range konfig.RecognizedKustomizationFileNames() {
resp, err = gc.client.GetRawUserContent(url + "/" + file)
err := handle(resp, err, "/"+file)
if err != nil {
continue
resp, errGetRawUserContent = gc.client.GetRawUserContent(url + "/" + file)
if err = handle(resp, errGetRawUserContent, "/"+file); err == nil {
return nil
}
}
return fmt.Errorf("file not found: %s, error: %v", url, err)
@@ -559,7 +562,15 @@ func (gcl GhClient) Do(query string) (*http.Response, error) {
return nil, err
}
req.Header.Add("Authorization", fmt.Sprintf("token %s", gcl.accessToken))
return gcl.client.Do(req)
// gcl.client.Do: a non-2xx status code doesn't cause an error.
// See https://golang.org/pkg/net/http/#Client.Do for more info.
resp, err := gcl.client.Do(req)
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("GhClient.Do(%s) failed with response code: %d",
query, resp.StatusCode)
}
return resp, err
}
func (gcl GhClient) getWithRetry(
@@ -569,13 +580,10 @@ func (gcl GhClient) getWithRetry(
retryCount := gcl.retryCount
for err == nil &&
resp.StatusCode == http.StatusForbidden &&
retryCount > 0 {
for resp.StatusCode == http.StatusForbidden && retryCount > 0 {
retryTime := resp.Header.Get("Retry-After")
i, err := strconv.Atoi(retryTime)
if err != nil {
i, errAtoi := strconv.Atoi(retryTime)
if errAtoi != nil {
return resp, fmt.Errorf(
"query '%s' forbidden without 'Retry-After'", query)
}

View File

@@ -2,6 +2,7 @@ package doc
import (
"fmt"
"sort"
"strings"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
@@ -46,7 +47,7 @@ type set map[string]struct{}
func (doc *KustomizationDocument) String() string {
return fmt.Sprintf("%s %s %s %v %v %v len(identifiers):%v len(values):%v",
doc.RepositoryURL, doc.FilePath, doc.DefaultBranch, doc.CreationTime,
doc.IsSame, doc.Kinds, len(doc.Identifiers), len(doc.Values))
doc.IsSame, doc.Kinds, len(doc.Identifiers), len(doc.Values))
}
// Implements the CrawlerDocument interface.
@@ -116,6 +117,8 @@ func (doc *KustomizationDocument) readBytes() ([]map[string]interface{}, error)
return configs, nil
}
// ParseYAML parses doc.Document and sets the following fields of doc:
// Kinds, Values, Identifiers.
func (doc *KustomizationDocument) ParseYAML() error {
doc.Identifiers = make([]string, 0)
doc.Values = make([]string, 0)
@@ -159,6 +162,13 @@ func (doc *KustomizationDocument) ParseYAML() error {
doc.Identifiers = append(doc.Identifiers, key)
}
// Without sorting these fields, every time when the string order in these fields changes,
// the document in the index will be updated.
// Sorting these fields are necessary to avoid a document being updated unnecessarily.
sort.Strings(doc.Kinds)
sort.Strings(doc.Values)
sort.Strings(doc.Identifiers)
return nil
}

View File

@@ -78,11 +78,17 @@ func (doc *Document) ID() string {
}
func (doc *Document) RepositoryFullName() string {
doc.RepositoryURL = strings.TrimRight(doc.RepositoryURL, "/")
sections := strings.Split(doc.RepositoryURL, "/")
url := strings.TrimRight(doc.RepositoryURL, "/")
gitPrefix := "git@github.com:"
if strings.HasPrefix(url, gitPrefix) {
url = url[len(gitPrefix):]
}
sections := strings.Split(url, "/")
l := len(sections)
if l < 2 {
return doc.RepositoryURL
return url
}
return path.Join(sections[l-2], sections[l-1])
}

View File

@@ -92,6 +92,12 @@ func TestDocument_RepositoryFullName(t *testing.T) {
},
expectedRepositoryFullName: "",
},
{
doc: Document{
RepositoryURL: "git@github.com:user/repo",
},
expectedRepositoryFullName: "user/repo",
},
}
for _, tc := range testCases {

View File

@@ -1,9 +1,9 @@
module sigs.k8s.io/kustomize/api/internal/crawl
go 1.13
go 1.12
require (
github.com/elastic/go-elasticsearch/v6 v6.8.2
github.com/elastic/go-elasticsearch/v6 v6.8.5
github.com/gomodule/redigo v2.0.0+incompatible
github.com/gorilla/mux v1.7.3
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79

View File

@@ -2,14 +2,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
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=
@@ -44,14 +40,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
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/elastic/go-elasticsearch/v6 v6.8.5 h1:U2HtkBseC1FNBmDr0TR2tKltL6FxoY+niDAlj5M8TK8=
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
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.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
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=
@@ -95,13 +87,11 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
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.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
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-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -115,12 +105,9 @@ github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvL
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/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.19.1/go.mod h1:2CEc4Fxx3vxDv7g8DyXkHCBF73AOzAymcJAprs2vCps=
github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk=
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/lint-1 v0.0.0-20191013205115-297bf364a8e0/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=
@@ -132,9 +119,8 @@ github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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=
@@ -144,11 +130,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
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.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
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=
@@ -166,7 +150,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
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=
@@ -200,9 +183,7 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
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/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/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=
@@ -217,7 +198,6 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
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/monopole/mdrip v1.0.1/go.mod h1:/7E04hlzRG9Jrp6WILZfYYm/REoJWL2l+MlsCO1eH74=
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=
@@ -251,13 +231,10 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
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/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
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=
@@ -288,13 +265,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
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/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/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/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
@@ -315,7 +290,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -338,9 +312,8 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
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/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -364,8 +337,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
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-20190826190057-c7b8b68b1456/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/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/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=
@@ -374,7 +346,6 @@ 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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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=
@@ -393,8 +364,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
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/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -429,8 +398,11 @@ 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.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.17.0 h1:H9d/lw+VkZKEVIUc8F3wgiQ+FUXTTr21M87jXLU7yqM=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
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=
@@ -439,17 +411,12 @@ 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=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/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/api v0.3.0/go.mod h1:4jaPCtRzxfQLFdYq4gYo40dBGW1hyPp/f4AuiZB5dAQ=
sigs.k8s.io/kustomize/pluginator/v2 v2.0.0/go.mod h1:zrXhTv8BAKt0egmZX/8AtMOSFUSWM9YuoHvvqz8/eHE=
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/kustomize/api v0.3.0 h1:riR/YsL75nGb+aIPFdIRiqu21+OZbAXQybDS7+FUYRg=
sigs.k8s.io/kustomize/api v0.3.0/go.mod h1:DWNMJBV1xvLruMpihGgnIPznMwHpwUSrxz6v3gnw5kw=
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=

View File

@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"time"
es "github.com/elastic/go-elasticsearch/v6"
@@ -179,44 +178,47 @@ func (idx *index) DeleteIndex() error {
}
// Insert or update the document by ID.
func (idx *index) Put(uniqueID string, doc interface{}) (string, error) {
body, err := json.Marshal(doc)
func (idx *index) Put(uniqueID string, doc interface{}) error {
exists, err := idx.Exists(uniqueID)
if err != nil {
return "", err
return err
}
req := esapi.IndexRequest{
Index: idx.name,
Body: bytes.NewReader(body),
DocumentID: uniqueID,
}
res, err := req.Do(idx.ctx, idx.client)
var id string
readId := func(reader io.Reader) error {
type InsertResult struct {
ID string `json:"_id,omitempty"`
if exists {
docBytes, err := json.Marshal(doc)
if err != nil {
return err
}
var ir InsertResult
data, err := ioutil.ReadAll(reader)
body := byteJoin(`{"doc":`, docBytes, `}`)
// For a document with a given id, every call of IndexRequest.Do will increase the version of a document.
// To avoid increasing the document version unnecessarily, use UpdateRequest here.
req := esapi.UpdateRequest{
Index: idx.name,
Body: bytes.NewReader(body),
DocumentID: uniqueID,
}
res, err := req.Do(idx.ctx, idx.client)
err = idx.responseErrorOrNil("could not update document",
res, err, ignoreResponseBody)
} else {
body, err := json.Marshal(doc)
if err != nil {
return err
}
err = json.Unmarshal(data, &ir)
if err != nil {
return err
req := esapi.IndexRequest{
Index: idx.name,
Body: bytes.NewReader(body),
DocumentID: uniqueID,
}
id = ir.ID
res, err := req.Do(idx.ctx, idx.client)
return nil
err = idx.responseErrorOrNil("could not insert document",
res, err, ignoreResponseBody)
}
// populates the id field.
err = idx.responseErrorOrNil("could not insert document",
res, err, readId)
return id, err
return err
}
type scrollUpdater func(string, readerFunc) error
@@ -296,3 +298,24 @@ func (idx *index) Delete(id string) error {
fmt.Sprintf("could not delete id(%s) from index(%s)", id, idx.name),
res, err, ignoreResponseBody)
}
// Check whether a given document id is in the index
func (idx *index) Exists(id string) (bool, error) {
op := idx.client.Exists
res, err := op(
idx.name,
id,
op.WithContext(idx.ctx),
op.WithPretty(),
)
if !res.IsError() {
return true, nil
} else if res.StatusCode == 404 {
return false, nil
} else {
return false, idx.responseErrorOrNil(
fmt.Sprintf("could not check the existence of id(%s) from index(%s)", id, idx.name),
res, err, ignoreResponseBody)
}
}

View File

@@ -16,6 +16,12 @@ const (
AggregationKeyword = "aggs"
)
type Mode int
const (
InsertOrUpdate = iota
Delete
)
// Redefinition of Hits structure. Must match the json string of
// KustomizeResult.Hits.Hits. Declared as a convenience for iteration.
type KustomizeHits []struct {
@@ -293,12 +299,13 @@ func (ki *KustomizeIndex) IterateQuery(query []byte, batchSize int,
}
// type specific Put for inserting structured kustomization documents.
func (ki *KustomizeIndex) Put(id string, doc *doc.KustomizationDocument) (string, error) {
id, err := ki.index.Put(id, doc)
if err != nil {
return id, fmt.Errorf("could not insert in elastic: %v", err)
}
return id, nil
func (ki *KustomizeIndex) Put(id string, doc *doc.KustomizationDocument) error {
return ki.index.Put(id, doc)
}
// Delete a document with a given id from the kustomize index.
func (ki *KustomizeIndex) Delete(id string) error {
return ki.index.Delete(id)
}
// Kustomize search options: What metrics should be returned? Kind Aggregation,

View File

@@ -190,5 +190,7 @@ varReference:
kind: StatefulSet
- path: metadata/labels
- path: metadata/annotations
`
)

View File

@@ -1157,6 +1157,8 @@ vars:
name: my-deployment
labels:
my-label: $(NAMESPACE)
annotations:
my-annotation: $(NAMESPACE)
spec:
template:
spec:
@@ -1176,6 +1178,8 @@ vars:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
my-annotation: my-namespace
labels:
my-label: my-namespace
name: my-deployment

View File

@@ -52,6 +52,10 @@ func Complete(cmd *cobra.Command) *complete.Command {
Flags: map[string]complete.Predictor{},
Sub: map[string]*complete.Command{},
}
if strings.Contains(cmd.Use, "DIR") {
// if usage contains directory, then use a file predictor
cc.Args = predict.Dirs("*")
}
// add completion for each subcommand
for i := range cmd.Commands() {

View File

@@ -74,10 +74,11 @@ func NewConfigCommand(name string) *cobra.Command {
root.AddCommand(commands.CatCommand(name))
root.AddCommand(commands.FmtCommand(name))
root.AddCommand(commands.MergeCommand(name))
root.AddCommand(commands.Merge3Command(name))
root.AddCommand(commands.CountCommand(name))
root.AddCommand(commands.RunFnCommand(name))
root.AddCommand(commands.SubCommand(name))
root.AddCommand(commands.SubSetCommand(name))
root.AddCommand(commands.SetCommand(name))
root.AddCommand(commands.CreateSetterCommand(name))
root.AddCommand(&cobra.Command{
Use: "docs-merge",

View File

@@ -0,0 +1,93 @@
## create-setter
[Alpha] Create a custom setter for a Resource field
### Synopsis
Create a custom setter for a Resource field by inlining OpenAPI as comments.
DIR
A directory containing Resource configuration.
NAME
The name of the setter to create.
VALUE
The current value of the field, or a substring within the field.
### Creating a Custom Setter
**Given the YAML:**
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080
...
**Create a new setter:**
# create a setter for ports
$ kustomize config set create DIR/ http-port 8080 --type "integer" --field "port"
Resources fields with a field name matching `--field` and field value matching `VALUE` will
have a line comment added marking this field as settable.
**Newly modified YAML:**
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080 # {"type":"integer","x-kustomize":{"partialFieldSetters":[{"name":"http-port","value":"8080"}]}}
...
Setters may also be defined directly by editing the yaml and adding the comment.
Users may not set the field value using the `set` command:
# change the http-port value to 8081
$ kustomize config set DIR/ http-port 8081
### Using default values
The default values for a setter may be:
- valid field values (e.g. `8080` or `008080` for a port)
- invalid values that adhere to the schema (e.g. `0000` for a port)
- values that do not adhere to the schema (e.g. `[PORT]` for port)
A setter may be for a substring of a full field:
$ kustomize config set create DIR/ image-tag v1.0.01 --type "string" --field "image"
image: gcr.io/example/app:v1.0.1 # # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"image-tag","value":"v1.0.1"}]}}
A single field value may have multiple setters applied to it for different parts of the field.
### Examples
# create a setter for port fields matching "8080"
kustomize config create-setter DIR/ port 8080 --type "integer" --field port \
--description "default port used by the app"
# create a setter for a substring of a field rather than the full field -- e.g. only the
# image tag, not the full image
kustomize config create-setter DIR/ image-tag v1.0.1 --type "string" \
--field image --description "current stable release"

View File

@@ -0,0 +1,23 @@
## merge3
[Alpha] Merge diff of Resource configuration files into a destination (3-way)
### Synopsis
[Alpha] Merge diff of Resource configuration files into a destination (3-way)
Merge3 performs a 3-way merge by applying the diff between 2 sets of Resources to a 3rd set.
Merge3 may be for rebasing changes to a forked set of configuration -- e.g. compute the difference between the original
set of Resources that was forked and an updated set of those Resources, then apply that difference to the fork.
If a field value differs between the ORIGINAL_DIR and UPDATED_DIR, the value from the UPDATED_DIR is taken and applied
to the Resource in the DEST_DIR.
For information on merge rules, run:
kustomize config docs-merge3
### Examples
kustomize config merge3 --ancestor a/ --from b/ --to c/

View File

@@ -0,0 +1,87 @@
## set
[Alpha] Set values on Resources fields values.
### Synopsis
Set values on Resources fields. May set either the complete or partial field value.
`set` identifies setters using field metadata published as OpenAPI extensions.
`set` parses both the Kubernetes OpenAPI, as well OpenAPI published inline in
the configuration as comments.
`set` maybe be used to:
- edit configuration programmatically from the cli
- create reusable bundles of configuration with custom setters
DIR
A directory containing Resource configuration.
NAME
Optional. The name of the setter to perform or display.
VALUE
Optional. The value to set on the field.
To print the possible setters for the Resources in a directory, run `set` on
a directory -- e.g. `kustomize config set DIR/`.
#### Tips
- A description of the value may be specified with `--description`.
- The last setter for the field's value may be defined with `--set-by`.
- Create custom setters on Resources, Kustomization.yaml's, patches, etc
The description and setBy fields are left unmodified unless specified with flags.
To create a custom setter for a field see: `kustomize help config create-setter`
### Examples
Resource YAML: Name Prefix Setter
# DIR/resources.yaml
...
metadata:
name: PREFIX-app1 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}}
...
---
...
metadata:
name: PREFIX-app2 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}}
...
List setters: Show the possible setters
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT OWNER
name-prefix '' PREFIX string 2
Perform substitution: set a new value, owner and description
$ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev"
performed 2 substitutions
Show substitutions: Show the new values
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' test string 2 true dev
New Resource YAML:
# DIR/resources.yaml
...
metadata:
name: test-app1 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
...
---
...
metadata:
name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
...

View File

@@ -1,98 +0,0 @@
## set
[Alpha] Set values on Resources fields by substituting values.
### Synopsis
Set values on Resources fields by substituting predefined markers for new values.
`set` looks for markers specified on Resource fields and substitute a new user defined
value for the existing value.
`set` maybe be used to:
- edit configuration programmatically from the cli or scripts
- create reusable bundles of configuration
DIR
A directory containing Resource configuration.
NAME
Optional. The name of the substitution to perform or display.
VALUE
Optional. The new value to substitute into the field.
To print the possible substitutions for the Resources in a directory, run `set` on
a directory -- e.g. `kustomize config set DIR/`.
#### Tips
- A description of the value may be specified with `--description`.
- An owner for the field's value may be defined with `--owned-by`.
- Prevent overriding previous substitutions with `--override=false`.
- Revert previous substitutions with `--revert`.
- Create substitutions on Kustomization.yaml's, patches, etc
When overriding or reverting previous substitutions, the description and owner are left
unmodified unless specified with flags.
To create a substitution for a field see: `kustomize help config set create`
### Examples
Resource YAML: Name substitution
# dir/resources.yaml
...
metadata:
name: PREFIX-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]}
...
---
...
metadata:
name: PREFIX-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]}
...
Show substitutions: Show the possible substitutions
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix '' PREFIX- string 2 false
Perform substitution: set a new value, owner and description
$ config set dir prefix "test-" --description "test environment" --owned-by "dev"
performed 2 substitutions
Show substitutions: Show the new values
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' test- string 2 true dev
New Resource YAML:
# dir/resources.yaml
...
metadata:
name: test-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"ownedBy":"dev","description":"test environment"}
...
---
...
metadata:
name: test-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"ownedBy":"dev","description":"test environment"}
...
Revert substitution:
config set dir prefix --revert
performed 2 substitutions
config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' PREFIX- string 2 false dev

View File

@@ -1,167 +0,0 @@
## sub-set-marker
[Alpha] Create a new substitution for a Resource field
### Synopsis
Create a new substitution for a Resource field -- recognized by `kustomize config set`.
DIR
A directory containing Resource configuration.
NAME
The name of the substitution to create.
VALUE
The current value of the field, or a substring of the field.
#### Tips: Picking Good Marker
Substitutions may be defined by directly editing yaml **or** by running `kustomize config set create`
to create a new substitution.
Given the YAML:
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080
...
Create a new set marker:
# create a substitution for ports
$ kustomize config set create dir/ http-port 8080 --type "int" --field "port"
Modified YAML:
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]"}],"type":"int"}
...
Change the value using the `set` command:
# change the http-port value to 8081
$ kustomize config set dir/ http-port 8081
Resources fields with a field name matching `--field` and field value matching `VALUE` will
have a line comment added marking this field as settable.
Substitution markers may be:
- valid field values (e.g. `8080` for a port)
- Note: `008080` would be preferred because it is more recognizable as a marker
- invalid values that adhere to the schema (e.g. `0000` for a port)
- values that do not adhere to the schema (e.g. `[PORT]` for port)
Markers **SHOULD be clearly identifiable as a marker and either**:
- **adhere to the field schema** -- e.g. use a valid value
port: 008080 # {"substitutions":[{"name":"port","marker":"008080"}],"type":"int"}
- **be pre-filled in with a value** -- e.g. set the value when setting the marker
port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]","value":"8080""}],"type":"int"}
**Note:** The important thing is that in both cases the Resource configuration may be directly
applied to a cluster and validated by tools without the tool knowing about the substitution
marker.
The difference between the preceding examples is that:
- the former will be shown as `SUBSTITUTED=false` (`config sub dir/` exits non-0)
- the latter with show up as `SUBSTITUTED=true` (`config sub dir/` exits 0)
When choosing the which to use, consider that checks for unsubstituted values MAY be
configured as pre-commit checks -- if you want to these checks to fail if the value
hasn't been substituted, then don't specify a `value`.
Markers which are invalid field values MAY be chosen in cases where it is preferred to have
the create or update request fail rather than succeed if the substitution has not yet been
performed.
A substitution may be a substring of the full field:
$ kustomize config set create dir/ app-image-tag v1.0.01 --type "string" --field "image"
image: gcr.io/example/app:v1.0.1 # {"substitutions":[{"name":"app-image-tag","marker":"[MARKER]","value":"v1.0.1"}]}
A single field value may have multiple substitutions applied to it:
name: PREFIX-app-SUFFIX # {"substitutions":[{"name":"prefix","marker":"PREFIX-"},{"name":"suffix","marker":"-SUFFIX"}]}
#### Substitution Format
Substitutions are defined as json encoded FieldMeta comments on fields.
FieldMeta Schema read by `sub`:
{
"title": "FieldMeta",
"type": "object",
"properties": {
"substitutions": {
"type": "array",
"description": "Possible substitutions that may be performed against this field.",
"items": {
"type": "object",
"properties": {
"name": "Name of the substitution.",
"marker": "Marker for the value to be substituted.",
"value": "Current substituted value"
}
}
},
"type": {
"type": "string",
"description": "The value type. Defaults to string."
"enum": ["string", "int", "float", "bool"]
},
"description": {
"type": "string",
"description": "A description of the field's current value. Optional."
},
"ownedBy": {
"type": "string",
"description": "The current owner of the field. Optional."
},
}
}
### Examples
# set a substitution for port fields matching "8080"
kustomize config sub create dir/ port 8080 --type "int" --field port \
--description "default port used by the app"
# set a substitution for port fields matching "8080", using "0000" as a marker.
kustomize config sub dir/ port 8080 --marker "0000" --type "int" \
--field port --description "default port used by the app"
# substitute a substring of a field rather than the full field -- e.g. only the
# image tag, not the full image
kustomize config sub dir/ app-image-tag v1.0.1 --type "string" --substring \
--field port --description "current stable release"

View File

@@ -20,8 +20,9 @@ container names, etc.
kustomize config tree supports printing arbitrary fields using the '--field' flag.
By default, kustomize config tree uses the directory structure for the tree structure, however when printing
from the cluster, the Resource graph structure may be used instead.
By default, kustomize config tree uses Resource graph structure if any relationships between resources (ownerReferences)
are detected, as is typically the case when printing from a cluster. Otherwise, directory graph structure is used. The
graph structure can also be selected explicitly using the '--graph-structure' flag.
### Examples
@@ -42,8 +43,7 @@ from the cluster, the Resource graph structure may be used instead.
--field="status.conditions[type=Completed].status"
# print live Resources from a cluster using owners for graph structure
kubectl get all -o yaml | kustomize config tree --replicas --name --image \
--graph-structure=owners
kubectl get all -o yaml | kustomize config tree --replicas --name --image
# print live Resources with status condition fields
kubectl get all -o yaml | kustomize config tree \

View File

@@ -39,7 +39,7 @@
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the `local-resources/example-use.yaml` by changing the replicas
- modify the `local-resource/example-use.yaml` by changing the replicas
re-run `run`. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
@@ -86,7 +86,7 @@
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the `local-resources/example-use.yaml` by changing the replicas
- modify the `local-resource/example-use.yaml` by changing the replicas
re-run `run`. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.

View File

@@ -11,7 +11,5 @@ require (
github.com/stretchr/testify v1.4.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
k8s.io/apimachinery v0.17.0
sigs.k8s.io/kustomize/kyaml v0.0.0
sigs.k8s.io/kustomize/kyaml v0.0.5
)
replace sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml

View File

@@ -1,8 +1,11 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
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/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
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 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
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=
@@ -16,16 +19,25 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
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/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
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 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
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 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
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.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
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 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -43,7 +55,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -51,21 +62,20 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
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 h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -81,9 +91,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
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/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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=
@@ -94,11 +102,9 @@ github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w=
github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
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=
@@ -119,6 +125,9 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20190613194153-d28f0bde5980/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-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -128,12 +137,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/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/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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
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=
@@ -153,5 +162,7 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
sigs.k8s.io/kustomize/kyaml v0.0.5 h1:m2kwhCiA6Z0ofJnT1R663zj1wxCi5jpOATYtg+7VhpM=
sigs.k8s.io/kustomize/kyaml v0.0.5/go.mod h1:waxTrzQRK9i6/5fR5HNo8xa4YwvWn8t85vMnOGFEZik=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -114,8 +114,8 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, b.String()) {
@@ -218,8 +218,8 @@ metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
configFn:
container:
image: gcr.io/example/image:version
@@ -314,8 +314,8 @@ metadata:
name: foo
annotations:
config.kubernetes.io/local-config: "true"
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
configFn:
container:
image: gcr.io/example/reconciler:v1
@@ -438,8 +438,8 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, string(actual)) {
@@ -560,8 +560,8 @@ metadata:
app: nginx
annotations:
app: nginx
config.kubernetes.io/package: .
config.kubernetes.io/path: f2.yaml
config.kubernetes.io/package: '.'
config.kubernetes.io/path: 'f2.yaml'
spec:
replicas: 3
`, string(actual)) {

View File

@@ -0,0 +1,75 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/setters"
)
// NewCreateSetterRunner returns a command runner.
func NewCreateSetterRunner(parent string) *CreateSetterRunner {
r := &CreateSetterRunner{}
set := &cobra.Command{
Use: "create-setter DIR NAME VALUE",
Args: cobra.ExactArgs(3),
Short: commands.CreateSetterShort,
Long: commands.CreateSetterLong,
Example: commands.CreateSetterExamples,
PreRunE: r.preRunE,
RunE: r.runE,
}
set.Flags().StringVar(&r.Set.SetPartialField.SetBy, "set-by", "",
"set the setBy annotation.")
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
"set the description of the field value.")
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
"name of the field to set -- e.g. --field port")
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
"name of the Resource on which to create the setter.")
set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "",
"kind of the Resource on which to create the setter.")
set.Flags().StringVar(&r.Set.SetPartialField.Type, "type", "",
"valid OpenAPI field type -- e.g. integer,boolean,string.")
set.Flags().BoolVar(&r.Set.SetPartialField.Partial, "partial", false,
"create a partial setter for only part of the field value.")
fixDocs(parent, set)
set.MarkFlagRequired("type")
set.MarkFlagRequired("field")
r.Command = set
return r
}
func CreateSetterCommand(parent string) *cobra.Command {
return NewCreateSetterRunner(parent).Command
}
type CreateSetterRunner struct {
Command *cobra.Command
Set setters.CreateSetter
}
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.set(c, args))
}
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
r.Set.SetPartialField.Setter.Name = args[1]
r.Set.SetPartialField.Setter.Value = args[2]
return nil
}
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
err := kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{&r.Set},
Outputs: []kio.Writer{rw}}.Execute()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,124 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"fmt"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/setters"
)
// NewSetRunner returns a command runner.
func NewSetRunner(parent string) *SetRunner {
r := &SetRunner{}
c := &cobra.Command{
Use: "set DIR [NAME] [VALUE]",
Args: cobra.RangeArgs(1, 3),
Short: commands.SetShort,
Long: commands.SetLong,
Example: commands.SetExamples,
PreRunE: r.preRunE,
RunE: r.runE,
}
fixDocs(parent, c)
r.Command = c
c.Flags().StringVar(&r.Perform.SetBy, "set-by", "",
"annotate the field with who set it")
c.Flags().StringVar(&r.Perform.Description, "description", "",
"annotate the field with a description of its value")
return r
}
func SetCommand(parent string) *cobra.Command {
return NewSetRunner(parent).Command
}
type SetRunner struct {
Command *cobra.Command
Lookup setters.LookupSetters
Perform setters.PerformSetters
}
func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
if len(args) > 1 {
r.Perform.Name = args[1]
r.Lookup.Name = args[1]
}
if len(args) > 2 {
r.Perform.Value = args[2]
}
return nil
}
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
if len(args) == 3 {
return handleError(c, r.perform(c, args))
}
return handleError(c, r.lookup(c, args))
}
func (r *SetRunner) lookup(c *cobra.Command, args []string) error {
// lookup the setters
err := kio.Pipeline{
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}},
Filters: []kio.Filter{&r.Lookup},
}.Execute()
if err != nil {
return err
}
table := tablewriter.NewWriter(c.OutOrStdout())
table.SetRowLine(false)
table.SetBorder(false)
table.SetHeaderLine(false)
table.SetColumnSeparator(" ")
table.SetCenterSeparator(" ")
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{
"NAME", "DESCRIPTION", "VALUE", "TYPE", "COUNT", "SETBY",
})
for i := range r.Lookup.SetterCounts {
s := r.Lookup.SetterCounts[i]
v := s.Value
if s.Value == "" {
v = s.Value
}
table.Append([]string{
s.Name,
"'" + s.Description + "'",
v,
fmt.Sprintf("%v", s.Type),
fmt.Sprintf("%d", s.Count),
s.SetBy,
})
}
table.Render()
return nil
}
// perform the setters
func (r *SetRunner) perform(c *cobra.Command, args []string) error {
rw := &kio.LocalPackageReadWriter{
PackagePath: args[0],
}
// perform the setters in the package
err := kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{&r.Perform},
Outputs: []kio.Writer{rw},
}.Execute()
if err != nil {
return err
}
fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", r.Perform.Count)
return nil
}

View File

@@ -1,162 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package commands
import (
"fmt"
"os"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/cmd/config/internal/sub"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// NewSubRunner returns a command runner.
func NewSubRunner(parent string) *SubRunner {
r := &SubRunner{}
c := &cobra.Command{
Use: "set DIR [NAME] [VALUE]",
Args: cobra.RangeArgs(1, 3),
Short: commands.SubShort,
Long: commands.SubLong,
Example: commands.SubExamples,
Aliases: []string{"sub"},
PreRunE: r.preRunE,
RunE: r.runE,
}
c.Flags().BoolVar(&r.Perform.Override, "override", true,
"override previously substituted values.")
c.Flags().BoolVar(&r.Perform.Revert, "revert", false,
"override previously substituted values.")
fixDocs(parent, c)
r.Command = c
c.AddCommand(SubSetCommand(parent))
return r
}
func SubCommand(parent string) *cobra.Command {
return NewSubRunner(parent).Command
}
type SubRunner struct {
Command *cobra.Command
Lookup sub.LookupSubstitutions
Perform sub.PerformSubstitutions
}
func (r *SubRunner) preRunE(c *cobra.Command, args []string) error {
if len(args) > 1 {
r.Perform.Name = args[1]
r.Lookup.Name = args[1]
}
if len(args) > 2 {
r.Perform.NewValue = args[2]
}
if len(args) < 2 && r.Perform.Revert {
return errors.Errorf("must specify NAME with --revert")
}
var mutex int
if r.Perform.Revert {
mutex++
}
if r.Perform.Override {
mutex++
}
if mutex > 1 {
return errors.Errorf("--revert, --override are mutually exclusive")
}
return nil
}
func (r *SubRunner) runE(c *cobra.Command, args []string) error {
if len(args) == 3 {
return handleError(c, r.perform(c, args))
}
if len(args) == 2 && r.Perform.Revert {
return handleError(c, r.perform(c, args))
}
return handleError(c, r.lookup(c, args))
}
func (r *SubRunner) lookup(c *cobra.Command, args []string) error {
// lookup the substitutions
err := kio.Pipeline{
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}},
Filters: []kio.Filter{&r.Lookup},
}.Execute()
if err != nil {
return err
}
remaining := false
table := tablewriter.NewWriter(c.OutOrStdout())
table.SetRowLine(false)
table.SetBorder(false)
table.SetHeaderLine(false)
table.SetColumnSeparator(" ")
table.SetCenterSeparator(" ")
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{
"NAME", "DESCRIPTION", "VALUE", "TYPE", "COUNT", "SUBSTITUTED", "OWNER",
})
for i := range r.Lookup.SubstitutionCounts {
s := r.Lookup.SubstitutionCounts[i]
remaining = remaining || s.Count > s.CountComplete
v := s.CurrentValue
if s.CurrentValue == "" {
v = s.Marker
}
table.Append([]string{
s.Name,
"'" + s.Description + "'",
v,
fmt.Sprintf("%v", s.Type),
fmt.Sprintf("%d", s.Count),
fmt.Sprintf("%v", s.Count == s.CountComplete),
s.OwnedBy,
})
}
table.Render()
if remaining {
os.Exit(1)
}
return nil
}
// perform the substitutions
func (r *SubRunner) perform(c *cobra.Command, args []string) error {
rw := &kio.LocalPackageReadWriter{
PackagePath: args[0],
}
// perform the substitutions in the package
err := kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{&r.Perform},
Outputs: []kio.Writer{rw},
}.Execute()
if err != nil {
return err
}
fmt.Fprintf(c.OutOrStdout(), "performed %d substitutions\n", r.Perform.Count)
return nil
}

View File

@@ -1,93 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/cmd/config/internal/sub"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// NewSubSetRunner returns a command runner.
func NewSubSetRunner(parent string) *SubSetRunner {
r := &SubSetRunner{}
set := &cobra.Command{
Use: "create PKG_DIR NAME [VALUE]",
Args: cobra.ExactArgs(3),
Short: commands.SubsetShort,
Long: commands.SubsetLong,
Example: commands.SubsetExamples,
PreRunE: r.preRunE,
RunE: r.runE,
}
set.Flags().StringVar(&r.Set.Marker.OwnedBy, "owned-by", "",
"set this owner on for the current value.")
set.Flags().StringVar(&r.Set.Marker.Description, "description", "",
"set this description for the current value description.")
set.Flags().StringVar(&r.Set.Marker.Substitution.Marker, "marker", "[MARKER]",
"use this marker.")
set.Flags().StringVar(&r.Set.Marker.Field, "field", "",
"name of the field to set -- e.g. --field port")
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
"name of the Resource on which to set the substitution.")
set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "",
"kind of the Resource on which to set substitution.")
set.Flags().StringVar(&r.Set.Marker.Type, "type", "",
"field type -- e.g. int,float,bool,string.")
set.Flags().BoolVar(&r.Set.Marker.PartialMatch, "substring", false,
"if true, the value may be a substring of the current value.")
fixDocs(parent, set)
set.MarkFlagRequired("type")
set.MarkFlagRequired("field")
r.Command = set
return r
}
func SubSetCommand(parent string) *cobra.Command {
return NewSubSetRunner(parent).Command
}
type SubSetRunner struct {
Command *cobra.Command
Set sub.SetSubstitutionMarker
}
func (r *SubSetRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.set(c, args))
}
func (r *SubSetRunner) preRunE(c *cobra.Command, args []string) error {
r.Set.Marker.Substitution.Name = args[1]
r.Set.Marker.Substitution.Value = args[2]
return nil
}
// perform the substitutions
func (r *SubSetRunner) set(c *cobra.Command, args []string) error {
rw := &kio.LocalPackageReadWriter{
PackagePath: args[0],
}
// add the substitution marker to the Resource
err := kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{&r.Set},
Outputs: []kio.Writer{rw},
}.Execute()
if err != nil {
return err
}
return nil
}

View File

@@ -78,8 +78,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_deployment.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml'
spec:
replicas: 11
selector:
@@ -109,8 +109,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_service.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml'
spec:
selector:
name: test
@@ -133,8 +133,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_deployment.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml'
spec:
replicas: 11
selector:
@@ -161,8 +161,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_service.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml'
spec:
selector:
name: test
@@ -185,8 +185,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_deployment.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml'
spec:
replicas: 11
selector:
@@ -216,8 +216,8 @@ items:
name: test
app: nginx
annotations:
config.kubernetes.io/index: "0"
config.kubernetes.io/path: config/test_service.yaml
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml'
spec:
selector:
name: test

View File

@@ -14,7 +14,7 @@ import (
func GetFmtRunner(name string) *FmtRunner {
r := &FmtRunner{}
c := &cobra.Command{
Use: "fmt",
Use: "fmt DIR...",
Short: commands.FmtShort,
Long: commands.FmtLong,
Example: commands.FmtExamples,

View File

@@ -0,0 +1,55 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
func GetMerge3Runner(name string) *Merge3Runner {
r := &Merge3Runner{}
c := &cobra.Command{
Use: "merge3 --ancestor [ORIGINAL_DIR] --from [UPDATED_DIR] --to [DESTINATION_DIR]",
Short: commands.Merge3Short,
Long: commands.Merge3Long,
Example: commands.Merge3Examples,
RunE: r.runE,
}
fixDocs(name, c)
c.Flags().StringVar(&r.ancestor, "ancestor", "",
"Path to original package")
c.Flags().StringVar(&r.fromDir, "from", "",
"Path to updated package")
c.Flags().StringVar(&r.toDir, "to", "",
"Path to destination package")
r.Command = c
return r
}
func Merge3Command(name string) *cobra.Command {
return GetMerge3Runner(name).Command
}
// Merge3Runner contains the run function
type Merge3Runner struct {
Command *cobra.Command
ancestor string
fromDir string
toDir string
}
func (r *Merge3Runner) runE(c *cobra.Command, args []string) error {
err := filters.Merge3{
OriginalPath: r.ancestor,
UpdatedPath: r.fromDir,
DestPath: r.toDir,
}.Merge()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,236 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
)
// TestMerge3Command verifies the merge3 correctly applies the diff between 2 sets of resources into another
func TestMerge3Command(t *testing.T) {
datadir, err := ioutil.TempDir("", "test-data")
defer os.RemoveAll(datadir)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(datadir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: java
spec:
replicas: 1
selector:
matchLabels:
app: java
template:
metadata:
labels:
app: java
spec:
restartPolicy: Always
containers:
- name: app
image: gcr.io/project/app:version
command:
- java
- -jar
- /app.jar
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
env:
- name: JAVA_OPTS
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
-Djava.security.egd=file:/dev/./urandom
imagePullPolicy: Always
minReadySeconds: 5
`), 0600)
if !assert.NoError(t, err) {
return
}
expected_dir, err := ioutil.TempDir("", "test-data-expected")
defer os.RemoveAll(expected_dir)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(expected_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: java
new-local: label
new-remote: label
spec:
replicas: 3
selector:
matchLabels:
app: java
template:
metadata:
labels:
app: java
spec:
restartPolicy: Always
containers:
- name: app
image: gcr.io/project/app:version
command:
- java
- -jar
- /app.jar
- otherstuff
args:
- foo
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
env:
- name: JAVA_OPTS
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
-Djava.security.egd=file:/dev/./urandom
imagePullPolicy: Always
minReadySeconds: 20
`), 0600)
if !assert.NoError(t, err) {
return
}
updated_dir, err := ioutil.TempDir("", "test-data-updated")
defer os.RemoveAll(updated_dir)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(updated_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: java
new-remote: label
spec:
replicas: 3
selector:
matchLabels:
app: java
template:
metadata:
labels:
app: java
spec:
restartPolicy: Always
containers:
- name: app
image: gcr.io/project/app:version
command:
- java
- -jar
- /app.jar
- otherstuff
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
env:
- name: JAVA_OPTS
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
-Djava.security.egd=file:/dev/./urandom
imagePullPolicy: Always
minReadySeconds: 5
`), 0600)
if !assert.NoError(t, err) {
return
}
dest_dir, err := ioutil.TempDir("", "test-data-dest")
defer os.RemoveAll(dest_dir)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(dest_dir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: java
new-local: label
spec:
replicas: 2
selector:
matchLabels:
app: java
template:
metadata:
labels:
app: java
spec:
restartPolicy: Always
containers:
- name: app
image: gcr.io/project/app:version
command:
- java
- -jar
- /app.jar
args:
- foo
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config
env:
- name: JAVA_OPTS
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
-Djava.security.egd=file:/dev/./urandom
imagePullPolicy: Always
minReadySeconds: 20
`), 0600)
if !assert.NoError(t, err) {
return
}
// Perform merge3 with newly created sets
r := commands.GetMerge3Runner("")
r.Command.SetArgs([]string{
"--ancestor",
datadir,
"--from",
updated_dir,
"--to",
dest_dir,
})
if !assert.NoError(t, r.Command.Execute()) {
return
}
diffs, err := copyutil.Diff(dest_dir, expected_dir)
if !assert.NoError(t, err) {
t.FailNow()
}
// Verify there are no diffs
if !assert.Empty(t, diffs.List()) {
t.FailNow()
}
}

View File

@@ -45,7 +45,7 @@ func GetTreeRunner(name string) *TreeRunner {
"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",
c.Flags().StringVar(&r.structure, "graph-structure", "",
"Graph structure to use for printing the tree. may be any of: "+
strings.Join(kio.GraphStructures, ","))

View File

@@ -49,6 +49,32 @@ var CountExamples = `
# print Resource counts from a directory
kustomize config count my-dir/`
var CreateSetterShort = `[Alpha] Create a custom setter for a Resource field`
var CreateSetterLong = `
Create a custom setter for a Resource field by inlining OpenAPI as comments.
DIR
A directory containing Resource configuration.
NAME
The name of the setter to create.
VALUE
The current value of the field, or a substring within the field.
`
var CreateSetterExamples = `
# create a setter for port fields matching "8080"
kustomize config create-setter DIR/ port 8080 --type "integer" --field port \
--description "default port used by the app"
# create a setter for a substring of a field rather than the full field -- e.g. only the
# image tag, not the full image
kustomize config create-setter DIR/ image-tag v1.0.1 --type "string" \
--field image --description "current stable release"`
var FmtShort = `[Alpha] Format yaml configuration files.`
var FmtLong = `
[Alpha] Format yaml configuration files.
@@ -136,6 +162,25 @@ For information on merge rules, run:
var MergeExamples = `
cat resources_and_patches.yaml | kustomize config merge > merged_resources.yaml`
var Merge3Short = `[Alpha] Merge diff of Resource configuration files into a destination (3-way)`
var Merge3Long = `
[Alpha] Merge diff of Resource configuration files into a destination (3-way)
Merge3 performs a 3-way merge by applying the diff between 2 sets of Resources to a 3rd set.
Merge3 may be for rebasing changes to a forked set of configuration -- e.g. compute the difference between the original
set of Resources that was forked and an updated set of those Resources, then apply that difference to the fork.
If a field value differs between the ORIGINAL_DIR and UPDATED_DIR, the value from the UPDATED_DIR is taken and applied
to the Resource in the DEST_DIR.
For information on merge rules, run:
kustomize config docs-merge3
`
var Merge3Examples = `
kustomize config merge3 --ancestor a/ --from b/ --to c/`
var RunFnsShort = `[Alpha] Reoncile config functions to Resources.`
var RunFnsLong = `
[Alpha] Reconcile config functions to Resources.
@@ -185,17 +230,18 @@ order they appear in the file).
var RunFnsExamples = `
kustomize config run example/`
var SubShort = `[Alpha] Set values on Resources fields by substituting values.`
var SubLong = `
Set values on Resources fields by substituting predefined markers for new values.
var SetShort = `[Alpha] Set values on Resources fields values.`
var SetLong = `
Set values on Resources fields. May set either the complete or partial field value.
` + "`" + `set` + "`" + ` looks for markers specified on Resource fields and substitute a new user defined
value for the existing value.
` + "`" + `set` + "`" + ` identifies setters using field metadata published as OpenAPI extensions.
` + "`" + `set` + "`" + ` parses both the Kubernetes OpenAPI, as well OpenAPI published inline in
the configuration as comments.
` + "`" + `set` + "`" + ` maybe be used to:
- edit configuration programmatically from the cli or scripts
- create reusable bundles of configuration
- edit configuration programmatically from the cli
- create reusable bundles of configuration with custom setters
DIR
@@ -203,244 +249,69 @@ value for the existing value.
NAME
Optional. The name of the substitution to perform or display.
Optional. The name of the setter to perform or display.
VALUE
Optional. The new value to substitute into the field.
Optional. The value to set on the field.
To print the possible substitutions for the Resources in a directory, run ` + "`" + `set` + "`" + ` on
To print the possible setters for the Resources in a directory, run ` + "`" + `set` + "`" + ` on
a directory -- e.g. ` + "`" + `kustomize config set DIR/` + "`" + `.
#### Tips
- A description of the value may be specified with ` + "`" + `--description` + "`" + `.
- An owner for the field's value may be defined with ` + "`" + `--owned-by` + "`" + `.
- Prevent overriding previous substitutions with ` + "`" + `--override=false` + "`" + `.
- Revert previous substitutions with ` + "`" + `--revert` + "`" + `.
- Create substitutions on Kustomization.yaml's, patches, etc
- The last setter for the field's value may be defined with ` + "`" + `--set-by` + "`" + `.
- Create custom setters on Resources, Kustomization.yaml's, patches, etc
When overriding or reverting previous substitutions, the description and owner are left
unmodified unless specified with flags.
The description and setBy fields are left unmodified unless specified with flags.
To create a substitution for a field see: ` + "`" + `kustomize help config set create` + "`" + `
To create a custom setter for a field see: ` + "`" + `kustomize help config create-setter` + "`" + `
`
var SubExamples = `
Resource YAML: Name substitution
var SetExamples = `
Resource YAML: Name Prefix Setter
# dir/resources.yaml
# DIR/resources.yaml
...
metadata:
name: PREFIX-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]}
name: PREFIX-app1 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}}
...
---
...
metadata:
name: PREFIX-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]}
name: PREFIX-app2 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}}
...
Show substitutions: Show the possible substitutions
List setters: Show the possible setters
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix '' PREFIX- string 2 false
$ config set DIR/
NAME DESCRIPTION VALUE TYPE COUNT OWNER
name-prefix '' PREFIX string 2
Perform substitution: set a new value, owner and description
$ config set dir prefix "test-" --description "test environment" --owned-by "dev"
$ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev"
performed 2 substitutions
Show substitutions: Show the new values
$ config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' test- string 2 true dev
prefix 'test environment' test string 2 true dev
New Resource YAML:
# dir/resources.yaml
# DIR/resources.yaml
...
metadata:
name: test-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"ownedBy":"dev","description":"test environment"}
name: test-app1 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
...
---
...
metadata:
name: test-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"ownedBy":"dev","description":"test environment"}
...
Revert substitution:
config set dir prefix --revert
performed 2 substitutions
config set dir
NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER
prefix 'test environment' PREFIX- string 2 false dev `
var SubsetShort = `[Alpha] Create a new substitution for a Resource field`
var SubsetLong = `
Create a new substitution for a Resource field -- recognized by ` + "`" + `kustomize config set` + "`" + `.
DIR
A directory containing Resource configuration.
NAME
The name of the substitution to create.
VALUE
The current value of the field, or a substring of the field.
#### Tips: Picking Good Marker
Substitutions may be defined by directly editing yaml **or** by running ` + "`" + `kustomize config set create` + "`" + `
to create a new substitution.
Given the YAML:
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080
...
Create a new set marker:
# create a substitution for ports
$ kustomize config set create dir/ http-port 8080 --type "int" --field "port"
Modified YAML:
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]"}],"type":"int"}
...
Change the value using the ` + "`" + `set` + "`" + ` command:
# change the http-port value to 8081
$ kustomize config set dir/ http-port 8081
Resources fields with a field name matching ` + "`" + `--field` + "`" + ` and field value matching ` + "`" + `VALUE` + "`" + ` will
have a line comment added marking this field as settable.
Substitution markers may be:
- valid field values (e.g. ` + "`" + `8080` + "`" + ` for a port)
- Note: ` + "`" + `008080` + "`" + ` would be preferred because it is more recognizable as a marker
- invalid values that adhere to the schema (e.g. ` + "`" + `0000` + "`" + ` for a port)
- values that do not adhere to the schema (e.g. ` + "`" + `[PORT]` + "`" + ` for port)
Markers **SHOULD be clearly identifiable as a marker and either**:
- **adhere to the field schema** -- e.g. use a valid value
port: 008080 # {"substitutions":[{"name":"port","marker":"008080"}],"type":"int"}
- **be pre-filled in with a value** -- e.g. set the value when setting the marker
port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]","value":"8080""}],"type":"int"}
**Note:** The important thing is that in both cases the Resource configuration may be directly
applied to a cluster and validated by tools without the tool knowing about the substitution
marker.
The difference between the preceding examples is that:
- the former will be shown as ` + "`" + `SUBSTITUTED=false` + "`" + ` (` + "`" + `config sub dir/` + "`" + ` exits non-0)
- the latter with show up as ` + "`" + `SUBSTITUTED=true` + "`" + ` (` + "`" + `config sub dir/` + "`" + ` exits 0)
When choosing the which to use, consider that checks for unsubstituted values MAY be
configured as pre-commit checks -- if you want to these checks to fail if the value
hasn't been substituted, then don't specify a ` + "`" + `value` + "`" + `.
Markers which are invalid field values MAY be chosen in cases where it is preferred to have
the create or update request fail rather than succeed if the substitution has not yet been
performed.
A substitution may be a substring of the full field:
$ kustomize config set create dir/ app-image-tag v1.0.01 --type "string" --field "image"
image: gcr.io/example/app:v1.0.1 # {"substitutions":[{"name":"app-image-tag","marker":"[MARKER]","value":"v1.0.1"}]}
A single field value may have multiple substitutions applied to it:
name: PREFIX-app-SUFFIX # {"substitutions":[{"name":"prefix","marker":"PREFIX-"},{"name":"suffix","marker":"-SUFFIX"}]}
#### Substitution Format
Substitutions are defined as json encoded FieldMeta comments on fields.
FieldMeta Schema read by ` + "`" + `sub` + "`" + `:
{
"title": "FieldMeta",
"type": "object",
"properties": {
"substitutions": {
"type": "array",
"description": "Possible substitutions that may be performed against this field.",
"items": {
"type": "object",
"properties": {
"name": "Name of the substitution.",
"marker": "Marker for the value to be substituted.",
"value": "Current substituted value"
}
}
},
"type": {
"type": "string",
"description": "The value type. Defaults to string."
"enum": ["string", "int", "float", "bool"]
},
"description": {
"type": "string",
"description": "A description of the field's current value. Optional."
},
"ownedBy": {
"type": "string",
"description": "The current owner of the field. Optional."
},
}
}
`
var SubsetExamples = `
# set a substitution for port fields matching "8080"
kustomize config sub create dir/ port 8080 --type "int" --field port \
--description "default port used by the app"
# set a substitution for port fields matching "8080", using "0000" as a marker.
kustomize config sub dir/ port 8080 --marker "0000" --type "int" \
--field port --description "default port used by the app"
# substitute a substring of a field rather than the full field -- e.g. only the
# image tag, not the full image
kustomize config sub dir/ app-image-tag v1.0.1 --type "string" --substring \
--field port --description "current stable release"`
name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
...`
var TreeShort = `[Alpha] Display Resource structure from a directory or stdin.`
var TreeLong = `
@@ -460,8 +331,9 @@ container names, etc.
kustomize config tree supports printing arbitrary fields using the '--field' flag.
By default, kustomize config tree uses the directory structure for the tree structure, however when printing
from the cluster, the Resource graph structure may be used instead.
By default, kustomize config tree uses Resource graph structure if any relationships between resources (ownerReferences)
are detected, as is typically the case when printing from a cluster. Otherwise, directory graph structure is used. The
graph structure can also be selected explicitly using the '--graph-structure' flag.
`
var TreeExamples = `
# print Resources using directory structure
@@ -481,8 +353,7 @@ var TreeExamples = `
--field="status.conditions[type=Completed].status"
# print live Resources from a cluster using owners for graph structure
kubectl get all -o yaml | kustomize config tree --replicas --name --image \
--graph-structure=owners
kubectl get all -o yaml | kustomize config tree --replicas --name --image
# print live Resources with status condition fields
kubectl get all -o yaml | kustomize config tree \

View File

@@ -316,7 +316,7 @@ var FunctionBasicsLong = `
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the ` + "`" + `local-resources/example-use.yaml` + "`" + ` by changing the replicas
- modify the ` + "`" + `local-resource/example-use.yaml` + "`" + ` by changing the replicas
re-run ` + "`" + `run` + "`" + `. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.
@@ -363,7 +363,7 @@ var FunctionBasicsLong = `
#### 2. Modify the Generated Resources
- modify the generated Resources by adding an annotation, sidecar container, etc.
- modify the ` + "`" + `local-resources/example-use.yaml` + "`" + ` by changing the replicas
- modify the ` + "`" + `local-resource/example-use.yaml` + "`" + ` by changing the replicas
re-run ` + "`" + `run` + "`" + `. this will apply the updated replicas to the generated Resources,
but keep the fields that you manually added to the generated Resource configuration.

View File

@@ -1,105 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &Marker{}
// substituteResource substitutes a Marker value on a field
type Marker struct {
// Path is the path of the field to add the substitution for
Field string
// Substitution is the substitution to add
Substitution fieldmeta.Substitution
// PartialMatch if true will match if the Substitution value is a substring of the current
// value.
PartialMatch bool
Description string
OwnedBy string
Type string
// currentFieldName is the name of the current field being processed
currentFieldName string
}
// Filter performs the substitutions for a single object
func (m *Marker) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
return m.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
// set the current field name
n := m.currentFieldName
defer func() { m.currentFieldName = n }()
m.currentFieldName = node.Key.YNode().Value
_, err := m.Filter(node.Value)
return err
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
_, err := m.Filter(node)
return err
})
case yaml.ScalarNode:
if m.currentFieldName != m.Field {
return object, nil
}
if err := m.createSub(object); err != nil {
return nil, err
}
return object, nil
default:
return object, nil
}
}
func (as *Marker) createSub(field *yaml.RNode) error {
// doesn't match the supplied value
if field.YNode().Value != as.Substitution.Value {
if !as.PartialMatch || !strings.Contains(field.YNode().Value, as.Substitution.Value) {
return nil
}
}
fm := fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return errors.Wrap(err)
}
fm.OwnedBy = as.OwnedBy
fm.Description = as.Description
fm.Type = fieldmeta.FieldValueType(as.Type)
if as.Substitution.Marker == "" {
as.Substitution.Marker = "[MARKER]"
}
found := false
for i := range fm.Substitutions {
s := fm.Substitutions[i]
if s.Name == as.Substitution.Name {
// update the substitution if we find it
found = true
fm.Substitutions[i] = as.Substitution
break
}
}
if !found {
// add the substitution if it wasn't found
fm.Substitutions = append(fm.Substitutions, as.Substitution)
}
if err := fm.Write(field); err != nil {
return errors.Wrap(err)
}
return nil
}

View File

@@ -1,53 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ kio.Filter = &PerformSubstitutions{}
// Sub performs substitutions
type PerformSubstitutions struct {
// Name is the name of the substitution to perform
Name string
// NewValue is the substitution value
NewValue string
// Override if set to true will re-substitute already fields with a new value
Override bool
// Revert if set to true will substitute fields back to the marker value
Revert bool
// Description, if set will annotate the field with a description.
Description string
// OwnedBy, if set will annotate the field with an owner.
OwnedBy string
// Count is the number of substitutions performed by Filter.
Count int
}
func (s *PerformSubstitutions) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range input {
p := &performSubstitutions{
Name: s.Name,
Override: s.Override,
Revert: s.Revert,
NewValue: s.NewValue,
OwnedBy: s.OwnedBy,
Description: s.Description,
}
if err := input[i].PipeE(p); err != nil {
return nil, err
}
s.Count += p.Count
}
return input, nil
}

View File

@@ -1,155 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package sub substitutes strings in fields
package sub
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &performSubstitutions{}
// substituteResource substitutes a Marker value on a field
type performSubstitutions struct {
// Name of the substitution to perform.
Name string
// Override if set to true will replace previously substituted values
Override bool
// Revert if set to true will undo previously substituted values
Revert bool
// NewValue is the new value to set. Mutually exclusive with Revert.
NewValue string
// Description, if set will annotate the field with a description.
Description string
// OwnedBy, if set will annotate the field with an owner.
OwnedBy string
// Count will be incremented for each substituted value.
Count int
}
// Filter performs the substitutions for a single object
func (fs *performSubstitutions) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
return fs.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
_, err := fs.Filter(node.Value)
return err
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
_, err := fs.Filter(node)
return err
})
case yaml.ScalarNode:
s, f, err := fs.findSub(object)
if err != nil {
return nil, err
}
if s == nil {
return object, nil
}
return object, fs.substitute(object, s, f)
default:
return object, nil
}
}
// findSub finds the substitution matching the name if one exists
func (fs *performSubstitutions) findSub(field *yaml.RNode) (
*fieldmeta.Substitution, *fieldmeta.FieldMeta, error) {
// check if there are any substitutions for this field
var fm = &fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return nil, nil, err
}
if fs.OwnedBy != "" {
fm.OwnedBy = fs.OwnedBy
}
if fs.Description != "" {
fm.Description = fs.Description
}
// check if there is a matching substitution
for i := range fm.Substitutions {
if fm.Substitutions[i].Name == fs.Name {
// validate the value if we are not reverting to the marker.
// markers are allowed to be invalid.
// only validate if there is a substitution matching the name
if !fs.Revert {
if err := fm.Type.Validate(fs.NewValue); err != nil {
return nil, nil, err
}
}
return &fm.Substitutions[i], fm, nil
}
}
return nil, nil, nil
}
// substitute performs the substitution for the given field, substitution, and metadata
func (fs *performSubstitutions) substitute(
field *yaml.RNode, s *fieldmeta.Substitution, f *fieldmeta.FieldMeta) error {
// undo or override previous substitutions by substituting the marker back
// NOTE: check if s.Value != "" so we never try to substitute the empty string back
if (fs.Revert || fs.Override) && s.Value != "" {
// revert to the marker value
if strings.Contains(field.YNode().Value, s.Value) {
// revert the substitution
field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Value, s.Marker)
// only use the tag matching the type if the marker parses to that type
field.YNode().Tag = f.Type.TagForValue(s.Marker)
// record that the config has been modified
}
}
if fs.Revert {
fs.Count++
s.Value = "" // value has been cleared and replaced with marker
if err := f.Write(field); err != nil {
return err
}
return nil
}
if s.Value == fs.NewValue || !strings.Contains(field.YNode().Value, s.Marker) {
// no substitutions necessary -- already substituted or doesn't have the marker
return nil
}
// replace the marker with the new value
field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Marker, fs.NewValue)
// be sure to set the tag so the yaml doesn't incorrectly quote ints, bools or floats
field.YNode().Tag = f.Type.Tag()
field.YNode().Style = 0
// record that the config has been modified
fs.Count++
// update the comment on the field
s.Value = fs.NewValue
if err := f.Write(field); err != nil {
return err
}
return nil
}

View File

@@ -1,69 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
import (
"sort"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ kio.Filter = &LookupSubstitutions{}
// Sub performs substitutions
type LookupSubstitutions struct {
// Name is the name of the substitution to match. If unspecified, all substitutions will
// be matched.
Name string
// SubstitutionCounts are the aggregate substitutions matched.
SubstitutionCounts []FieldSubstitutionCount
}
type FieldSubstitutionCount struct {
// Count is the number of substitutions possible to perform
Count int
// CountComplete is the number of substitutions that have already been performed
// independent of this object.
CountComplete int
// FieldSubstitution is the substitution found
FieldSubstitution
}
func (l *LookupSubstitutions) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
subs := map[string]*FieldSubstitutionCount{}
for i := range input {
// lookup substitutions for this object
ls := &lookupSubstitutions{Name: l.Name}
if err := input[i].PipeE(ls); err != nil {
return nil, err
}
// aggregate counts for each substitution
for j := range ls.Substitutions {
sub := ls.Substitutions[j]
curr, found := subs[sub.Name]
if !found {
curr = &FieldSubstitutionCount{FieldSubstitution: sub}
subs[sub.Name] = curr
}
curr.Count++
if sub.CurrentValue != "" {
curr.CountComplete++
}
}
}
// pull out and sort the results
for _, v := range subs {
l.SubstitutionCounts = append(l.SubstitutionCounts, *v)
}
sort.Slice(l.SubstitutionCounts, func(i, j int) bool {
return l.SubstitutionCounts[i].Name < l.SubstitutionCounts[j].Name
})
return input, nil
}

View File

@@ -1,65 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
import (
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &lookupSubstitutions{}
// substituteResource substitutes a Marker value on a field
type lookupSubstitutions struct {
// Name of the substitution to lookup. If unspecified lookup all substitutions.
Name string
// FieldSubstitution is the list of substitutions that were found
Substitutions []FieldSubstitution
}
func (ls *lookupSubstitutions) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
return ls.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
_, err := ls.Filter(node.Value)
return err
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
_, err := ls.Filter(node)
return err
})
case yaml.ScalarNode:
return object, ls.lookup(object)
default:
return object, nil
}
}
// lookup finds any substitutions for this field
func (ls *lookupSubstitutions) lookup(field *yaml.RNode) error {
// check if there is a substitution for this field
var fm = &fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return err
}
for i := range fm.Substitutions {
s := fm.Substitutions[i]
if ls.Name == "" || ls.Name == s.Name {
ls.Substitutions = append(ls.Substitutions, FieldSubstitution{
Name: s.Name,
CurrentValue: s.Value,
Description: fm.Description,
Marker: s.Marker,
Type: fm.Type,
OwnedBy: fm.OwnedBy,
})
}
}
return nil
}

View File

@@ -1,29 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
import (
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
)
// FieldSubstitution is a possible field substitution read from a field
type FieldSubstitution struct {
// Name is the name of the substitution
Name string
// Description is a description of the fields current value
Description string
// Value is the current substituted value for the field.
CurrentValue string
// Type is the type of the substitution
Type fieldmeta.FieldValueType
// Marker is the marker used
Marker string
// OwnedBy, if set will annotate the field with an owner.
OwnedBy string
}

View File

@@ -6,7 +6,8 @@ require (
github.com/spf13/cobra v0.0.5
k8s.io/cli-runtime v0.17.0
k8s.io/client-go v0.17.0
k8s.io/kubectl v0.17.0
k8s.io/component-base v0.17.0 // indirect
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/kustomize/kyaml v0.0.0
)

View File

@@ -85,6 +85,7 @@ github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL9
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.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@@ -344,15 +345,22 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20191214185829-ca1d04f8b0d3/go.mod h1:itOjKREfmUTvcjantxOsyYU5mbFsU7qUnyUuRfF5+5M=
k8s.io/api v0.17.0 h1:H9d/lw+VkZKEVIUc8F3wgiQ+FUXTTr21M87jXLU7yqM=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apimachinery v0.0.0-20191214185652-442f8fb2f03a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY=
k8s.io/apimachinery v0.0.0-20191216025728-0ee8b4573e3a/go.mod h1:Ng1IY8TS7sC44KJxT/WUR6qFRfWwahYYYpNXyYRKOCY=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY=
k8s.io/cli-runtime v0.17.0 h1:XEuStbJBHCQlEKFyTQmceDKEWOSYHZkcYWKp3SsQ9Hk=
k8s.io/cli-runtime v0.17.0/go.mod h1:1E5iQpMODZq2lMWLUJELwRu2MLWIzwvMgDBpn3Y81Qo=
k8s.io/client-go v0.0.0-20191214190045-a32a6f7a3052/go.mod h1:tAaoc/sYuIL0+njJefSAmE28CIcxyaFV4kbIujBlY2s=
k8s.io/client-go v0.0.0-20191219150334-0b8da7416048/go.mod h1:ZEe8ZASDUAuqVGJ+UN0ka0PfaR+b6a6E1PGsSNZRui8=
k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/code-generator v0.0.0-20191214185510-0b9b3c99f9f2/go.mod h1:BjGKcoq1MRUmcssvHiSxodCco1T6nVIt4YeCT5CMSao=
k8s.io/component-base v0.0.0-20191214190519-d868452632e2/go.mod h1:wupxkh1T/oUDqyTtcIjiEfpbmIHGm8By/vqpSKC6z8c=
k8s.io/component-base v0.17.0 h1:BnDFcmBDq+RPpxXjmuYnZXb59XNN9CaFrX8ba9+3xrA=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -363,9 +371,9 @@ 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/kubectl v0.17.0 h1:xD4EWlL+epc/JTO1gvSjmV9yiYF0Z2wiHK2DIek6URY=
k8s.io/kubectl v0.17.0/go.mod h1:jIPrUAW656Vzn9wZCCe0PC+oTcu56u2HgFD21Xbfk1s=
k8s.io/metrics v0.17.0/go.mod h1:EH1D3YAwN6d7bMelrElnLhLg72l/ERStyv2SIQVt6Do=
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd h1:nZX5+wEqTu/EBIYjrZlFOA63z4+Zcy96lDkCZPU9a9c=
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo=
k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=

View File

@@ -17,8 +17,8 @@ fmt:
go fmt ./...
generate:
#(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/resource)
#GOBIN=$(GOBIN) go 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)

View File

@@ -9,6 +9,7 @@ require (
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90
sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f // indirect
sigs.k8s.io/kustomize/kstatus v0.0.0-20191204200457-7c1b477ff62d
sigs.k8s.io/kustomize/kyaml v0.0.0-20191202204815-0a19a5dbd9b8
)

View File

@@ -74,6 +74,7 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
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.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
@@ -88,6 +89,7 @@ github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nA
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
@@ -95,6 +97,7 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
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-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -167,6 +170,7 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/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/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
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=
@@ -280,6 +284,7 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
@@ -402,6 +407,9 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f h1:Wdh26pJ0THtsuSB1DCkaLc1Ssv2NDddB7E7vCSdTHdg=
sigs.k8s.io/kustomize/cmd/mdtogo v0.0.0-20191222005333-3900166fdf4f/go.mod h1:arffnBwv6VTLUY3hxATxJ2fwNMWy92GSXm6UXEjFddQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
@@ -6,23 +9,27 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// GetEventsRunner returns a command EventsRunner.
func GetEventsRunner() *EventsRunner {
r := &EventsRunner{}
c := &cobra.Command{
Use: "events",
Short: "Events",
RunE: r.runE,
Use: "events DIR...",
Short: commands.EventsShort,
Long: commands.EventsLong,
Example: commands.EventsExamples,
RunE: r.runE,
}
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().DurationVar(&r.Interval, "interval", 2*time.Second,
"check every n seconds. Default is every 2 seconds.")
"check every n seconds.")
c.Flags().DurationVar(&r.Timeout, "timeout", 60*time.Second,
"give up after n seconds. Default is 60 seconds.")
"give up after n seconds.")
r.Command = c
return r
@@ -32,6 +39,8 @@ func EventsCommand() *cobra.Command {
return GetEventsRunner().Command
}
// EventsRunner captures the parameters for the command
// and contains the run function.
type EventsRunner struct {
IncludeSubpackages bool
Interval time.Duration
@@ -41,13 +50,17 @@ type EventsRunner struct {
func (r *EventsRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background()
// Create a client and use it to set up a new resolver.
client, err := getClient()
if err != nil {
return errors.Wrap(err, "error creating client")
}
resolver := wait.NewResolver(client, r.Interval)
// Set up a CaptureIdentifierFilter and run all inputs through the
// filter with the pipeline to capture the inventory of resources
// which we are interested in.
captureFilter := &CaptureIdentifiersFilter{}
filters := []kio.Filter{captureFilter}
@@ -70,12 +83,17 @@ func (r *EventsRunner) runE(c *cobra.Command, args []string) error {
return errors.Wrap(err, "error reading manifests")
}
// Create a new printer that knows how to print updates about
// resourdes and their aggregate status in the events format.
printer := newEventPrinter(c.OutOrStdout(), c.OutOrStderr())
ctx, cancel := context.WithTimeout(ctx, r.Timeout)
defer cancel()
resChannel := resolver.WaitForStatus(ctx, captureFilter.Identifiers)
// Print events until the channel is closed. This will happen
// either because all resources has reached the Current status
// or it has timed out.
for msg := range resChannel {
printer.printEvent(msg)
}

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
@@ -6,17 +9,21 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/status"
"sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// GetFetchRunner returns a command FetchRunner.
func GetFetchRunner() *FetchRunner {
r := &FetchRunner{}
c := &cobra.Command{
Use: "fetch",
Short: "Fetch",
RunE: r.runE,
Use: "fetch DIR...",
Short: commands.FetchShort,
Long: commands.FetchLong,
Example: commands.FetchExamples,
RunE: r.runE,
}
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
@@ -29,6 +36,8 @@ func FetchCommand() *cobra.Command {
return GetFetchRunner().Command
}
// FetchRunner captures the parameters for the command and contains
// the run function.
type FetchRunner struct {
IncludeSubpackages bool
Command *cobra.Command
@@ -36,6 +45,8 @@ type FetchRunner struct {
func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background()
// Create a new client and use it to set up a resolver.
client, err := getClient()
if err != nil {
return errors.Wrap(err, "error creating client")
@@ -43,6 +54,9 @@ func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
resolver := wait.NewResolver(client, time.Minute)
// Set up a CaptureIdentifierFilter and run all inputs through the
// filter with the pipeline to capture the inventory of resources
// which we are interested in.
captureFilter := &CaptureIdentifiersFilter{}
filters := []kio.Filter{captureFilter}
@@ -65,16 +79,27 @@ func (r *FetchRunner) runE(c *cobra.Command, args []string) error {
return errors.Wrap(err, "error reading manifests")
}
// Pass in the inventory of resources to the FetchAndResolve function
// on the resolver. It will return the status (or an error) for each
// resource in the inventory.
results := resolver.FetchAndResolve(ctx, captureFilter.Identifiers)
// Create new printer that knows how to print resource statuses
// in a table format and ask it to print the results.
newTablePrinter(FetchStatusInfo{results}, c.OutOrStdout(), c.OutOrStderr(), false).Print()
return nil
}
// FetchStatusInfo wraps the results from the FetchAndResolve function
// to the format expected in the TablePrinter.
type FetchStatusInfo struct {
Results []wait.ResourceResult
}
// CurrentStatus returns the latest information known about the
// status of each of the resources. For FetchStatusInfo, the result
// is never updated, so it just returns the information provided
// by the slice of wait.ResourceResult at creation.
func (f FetchStatusInfo) CurrentStatus() StatusData {
var resourceData []ResourceStatusData
for _, res := range f.Results {

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
@@ -19,6 +22,8 @@ func init() {
_ = clientgoscheme.AddToScheme(scheme)
}
// getClient returns a client for talking to a Kubernetes cluster. The client
// is from controller-runtime.
func getClient() (client.Client, error) {
config := ctrl.GetConfigOrDie()
mapper, err := apiutil.NewDiscoveryRESTMapper(config)
@@ -28,6 +33,8 @@ func getClient() (client.Client, error) {
return client.New(config, client.Options{Scheme: scheme, Mapper: mapper})
}
// CaptureIdentifiersFilter implements the Filter interface in the kio package. It
// captures the identifiers for all resources passed through the pipeline.
type CaptureIdentifiersFilter struct {
Identifiers []wait.ResourceIdentifier
}

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
@@ -7,18 +10,21 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/resource/status/generateddocs/commands"
"sigs.k8s.io/kustomize/kstatus/status"
"sigs.k8s.io/kustomize/kstatus/wait"
"sigs.k8s.io/kustomize/kyaml/kio"
)
// GetWaitRunner return a command WaitRunner.
func GetWaitRunner() *WaitRunner {
r := &WaitRunner{}
c := &cobra.Command{
Use: "wait",
Short: "Wait",
RunE: r.runE,
Use: "wait DIR...",
Short: commands.WaitShort,
Long: commands.WaitLong,
Example: commands.WaitExamples,
RunE: r.runE,
}
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
@@ -35,6 +41,8 @@ func WaitCommand() *cobra.Command {
return GetWaitRunner().Command
}
// WaitRunner captures the parameters for the command and contains
// the run function.
type WaitRunner struct {
IncludeSubpackages bool
Interval time.Duration
@@ -42,6 +50,9 @@ type WaitRunner struct {
Command *cobra.Command
}
// runE implements the logic of the command and will call the Wait command in the wait
// package, use a ResourceStatusCollector to capture the events from the channel, and the
// TablePrinter to display the information.
func (r *WaitRunner) runE(c *cobra.Command, args []string) error {
ctx := context.Background()
client, err := getClient()
@@ -98,6 +109,9 @@ func (r *WaitRunner) runE(c *cobra.Command, args []string) error {
return nil
}
// ResourceStatusCollector captures the latest state seen for all resources
// based on the events from the Wait channel. This is used by the TablePrinter
// to display status for all resources.
type ResourceStatusCollector struct {
mux sync.RWMutex
@@ -105,6 +119,8 @@ type ResourceStatusCollector struct {
ResourceStatuses []*ResourceStatus
}
// updateResourceStatus takes the given event and update the status info
// in the ResourceStatusCollector.
func (r *ResourceStatusCollector) updateResourceStatus(msg wait.Event) {
r.mux.Lock()
defer r.mux.Unlock()
@@ -121,18 +137,23 @@ func (r *ResourceStatusCollector) updateResourceStatus(msg wait.Event) {
}
}
// updateAggregateStatus sets the aggregate status of the ResourceStatusCollector to the
// given value.
func (r *ResourceStatusCollector) updateAggregateStatus(aggregateStatus status.Status) {
r.mux.Lock()
defer r.mux.Unlock()
r.AggregateStatus = aggregateStatus
}
// ResourceStatus contains the status information for a single resource.
type ResourceStatus struct {
Identifier wait.ResourceIdentifier
Status status.Status
Message string
}
// newResourceStatusCollector creates a new ResourceStatusCollector with the given
// resources and sets the status for all of them to Unknown.
func newResourceStatusCollector(identifiers []wait.ResourceIdentifier) *ResourceStatusCollector {
var statuses []*ResourceStatus
@@ -150,10 +171,16 @@ func newResourceStatusCollector(identifiers []wait.ResourceIdentifier) *Resource
}
}
// CollectorStatusInfo is a wrapper around the ResourceStatusCollector
// to make it adhere to the interface of the TableWriter.
type CollectorStatusInfo struct {
Collector *ResourceStatusCollector
}
// CurrentStatus implements the interface for the TableWriter and
// returns a copy of the current status of the resources in the
// ResourceStatusCollector. This is done to make sure the TableWriter
// does not have to deal with synchronization when accessing the data.
func (f CollectorStatusInfo) CurrentStatus() StatusData {
f.Collector.mux.RLock()
defer f.Collector.mux.RUnlock()

View File

@@ -0,0 +1,22 @@
## events
[Alpha] Poll the cluster until all provided resources have become Current and list the status change events.
### Synopsis
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be status change events.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status events my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status events --timeout=5m

View File

@@ -0,0 +1,21 @@
## fetch
[Alpha] Fetch the state of the provided resources from the cluster and display status in a table.
### Synopsis
[Alpha] Fetches the state of all provided resources from the cluster and displays the status in
a table.
The list of resources are provided as manifests either on the filesystem or on StdIn.
DIR:
Path to local directory.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status fetch my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status fetch

View File

@@ -0,0 +1,22 @@
## wait
[Alpha] Poll the cluster until all provided resources have become Current and display progress in a table.
### Synopsis
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be presented as a table.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
### Examples
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status wait my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status wait --timeout=5m

View File

@@ -0,0 +1,57 @@
// Code generated by "mdtogo"; DO NOT EDIT.
package commands
var EventsShort=`[Alpha] Poll the cluster until all provided resources have become Current and list the status change events.`
var EventsLong=`
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be status change events.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
`
var EventsExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status events my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status events --timeout=5m`
var FetchShort=`[Alpha] Fetch the state of the provided resources from the cluster and display status in a table.`
var FetchLong=`
[Alpha] Fetches the state of all provided resources from the cluster and displays the status in
a table.
The list of resources are provided as manifests either on the filesystem or on StdIn.
DIR:
Path to local directory.
`
var FetchExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status fetch my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status fetch`
var WaitShort=`[Alpha] Poll the cluster until all provided resources have become Current and display progress in a table. `
var WaitLong=`
[Alpha] Poll the cluster for the state of all the provided resources until either they have all become
Current or the timeout is reached. The output will be presented as a table.
The list of resources which should be polled are provided as manifests either on the filesystem or
on StdIn.
DIR:
Path to local directory. If not provided, input is expected on StdIn.
`
var WaitExamples=`
# Read resources from the filesystem and wait up to 1 minute for all of them to become Current
resource status wait my-dir/
# Fetch all resources in the cluster and wait up to 5 minutes for all of them to become Current
kubectl get all --all-namespaces -oyaml | resource status wait --timeout=5m`

View File

@@ -1,3 +1,8 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate $GOBIN/mdtogo docs/commands generateddocs/commands --license=none
package status
import (
@@ -8,7 +13,7 @@ import (
func StatusCommand() *cobra.Command {
var status = &cobra.Command{
Use: "status",
Short: "status reference command",
Short: "[Alpha] Commands for working with resource status.",
}
status.AddCommand(cmd.FetchCommand())

View File

@@ -29,7 +29,7 @@ with `metadata.configFn` and running:
kustomize config run local-resource/
This generates the `local-resources/config` directory containing the template output.
This generates the `local-resource/config` directory containing the template output.
- the template output may be modified by adding fields -- such as initContainers,
sidecarConatiners, cpu resource limits, etc -- and these fields will be retained

View File

@@ -7,6 +7,8 @@ license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
all: license
image:
docker build image -t gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
docker push gcr.io/kustomize-functions/example-cockroachdb:v0.1.0

View File

@@ -24,7 +24,7 @@ with `metadata.configFn` and running:
kustomize config run local-resource/
This generates the `local-resources/config` directory containing the template output.
This generates the `local-resource/config` directory containing the template output.
- the template output may be modified by adding fields -- such as initContainers,
sidecarConatiners, cpu resource limits, etc -- and these fields will be retained

View File

@@ -1,3 +1,6 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM alpine:latest as schemas
RUN apk --no-cache add git
RUN git clone --depth 1 https://github.com/instrumenta/kubernetes-json-schema.git

115
kstatus/README.md Normal file
View File

@@ -0,0 +1,115 @@
# kstatus
kstatus provides tools for checking the status of Kubernetes resources. The primary use case is knowing when
(or if) a given set of resources in cluster has successfully reconciled an apply operation.
## Concepts
This effort has several goals, some with a shorter timeline than others. Initially, we want to provide
a library that makes it easier to decide when changes to a set of resources have been reconciled in a cluster.
To support types that do not yet publish status information, we will initially fallback on type specific rules.
The library already contains rules for many of the most common built-in types such a Deployment and StatefulSet.
For custom resource definitions (CRDs), there currently isn't much guidance on which properties should be exposed in the status
object and which conditions should be used. As part of this effort we want to define a set of standard conditions
that the library will understand and that we encourage developers to adopt in their CRDs. These standard conditions will
be focused on providing the necessary information for understanding status of the reconcile after `apply` and it is not
expected that these will necessarily be the only conditions exposed in a custom resource. Developers will be free to add as many conditions
as they wish, but if the CRDs adopt the standard conditions defined here, this library will handle them correctly.
The `status` objects for built-in types don't all conform to a common behavior. Not all built-in types expose conditions,
and even among the types that does, the types of conditions vary widely. Long-term, we hope to add support for the
standard conditions to the built-in types as well. This would remove the need for type-specific rules for determining
status.
### Statuses
The library currently defines the following statuses for resource:
* __InProgress__: The actual state of the resource has not yet reached the desired state as specified in the
resource manifest, i.e. the resource reconcile has not yet completed. Newly created resources will usually
start with this status, although some resources like ConfigMaps are Current immediately.
* __Failed__: The process of reconciling the actual state with the desired state has encountered and error
or it has made insufficient progress.
* __Current__: The actual state of the resource matches the desired state. The reconcile process is considered
complete until there are changes to either the desired or the actual state.
* __Terminating__: The resource is in the process of being deleted.
* __Unknown__: This is for situations when the library are unable to determine the status of a resource.
### Conditions
The conditions defined in the library are designed to adhere to the "abnormal-true" pattern, i.e. that
conditions are present and with a value of true whenever something unusual happens. So the absence of
any conditions means everything is normal. Normal in this situation simply means that the latest observed
generation of the resource manifest by the controller have been fully reconciled with the actual state.
* __InProgress__: The controller is currently working on reconciling the latest changes.
* __Failed__: The controller has encountered an error during the reconcile process or it has made
insufficient progress (timeout).
The use of the "abnormal-true" pattern has some challenges. If the controller is not running, or for some
reason not able to update the resource, it will look like it is in a good state when that is not true. The
solution to this issue is to adopt the pattern used by several of the built-in types where there is an
`observedGeneration` property on the status object which is set by the controller during the reconcile loop.
If the `generation` and the `observedGeneration` of a resource does not match, it means there are changes
that the controller has not yet seen, and therefore not acted upon.
## Features
The library is currently separated into two packages, one that provides the basic functionality, and another that
builds upon the basics to provide a higher level API.
**sigs.k8s.io/kustomize/kstatus/status**: Provides two basic functions. First, it provides the `Compute` function
that takes a single resource and computes the status for this resource based on the fields in the status object for
the resource. Second, it provides the `Augment` function that computes the appropriate standard conditions based on
the status object and then amends them to the conditions in the resource. Both of these functions currently operate
on Unstructured types, but this should eventually be changed to rely on the kyaml library. Both of these functions
compute the status and conditions solely based on the data in the resource passed in. It does not communicate with
a cluster to get the latest state of the resources.
**sigs.k8s.io/kustomize/kstatus/wait**: This package builds upon the status package and provides functionality that
will fetch the latest state from a cluster. It provides the `FetchAndResolve` function that takes list of resource
identifiers, fetches the latest state for all the resources from the cluster, computes the status for all of them and
returns the results. `WaitForStatus` accepts a list of resource identifiers and will poll cluster for the status of
the resources until all resources have reached the `Current` status.
## Challenges
### Status is not obvious for all resource types
For some types of resources, it is pretty clear what the different statuses mean. For others, it
is far less obvious. For example, what does it mean that a PodDisruptionBudget is Current? Based on
the assumptions above it probably should be whenever the controller has observed the resource
and updated the status object of the PDB with information on allowed disruptions. But currently, a PDB is
considered Current when the number of healthy replicas meets the threshold given in the PDB. Also, should
the presence of a PDB influence when a Deployment is considered Current? This would mean that a Deployment
should be considered Current whenever the number of replicas reach the threshold set by the corresponding
PDB. This is not currently supported as described below.
### Status is decided based on single resource
Currently the status of a resource is decided solely based on information from
the state of that resource. This is an issue for resources that create other resources
and that doesn't provide sufficient information within their own status object. An example
is the Service resource that doesn't provide much status information but do generate Endpoint
resources that could be used to determine status. Similar, the status of a Deployment could be
based on its generated ReplicaSets and Pods.
Not having the generated resources also limits the amount of details that can be provided
when something isn't working as expected.
## Future
### Depend on kyaml instead of k8s libraries
The sigs.k8s.io/kustomize/kstatus/status package currently depends on k8s libraries. This can be
challenging if someone wants to vendor the library within their own project. We want to replace
the dependencies on k8s libraries with kyaml for the status package. The wait package needs to
talk to a k8s cluster, so this package will continue to rely on the k8s libraries.
### Use watches instead of polling
We currently poll for updates to resources, but it would be possible to set up
watches instead. This could also be combined with deciding status based on not only a single
resource, but also all its generated resources. This would lead to a design that seems similar
to a controller, so maybe a solution like this could be built on top of controller-runtime.
A challenge here is that the rules for each built-in type would need to be expressed in a different
way that what we currently do.

View File

@@ -6,7 +6,7 @@ require (
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
sigs.k8s.io/kustomize/api v0.3.0
sigs.k8s.io/kustomize/api v0.3.1
sigs.k8s.io/kustomize/cmd/config v0.0.2
sigs.k8s.io/kustomize/cmd/kubectl v0.0.2
sigs.k8s.io/kustomize/kyaml v0.0.2

View File

@@ -596,6 +596,8 @@ sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbL
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/kustomize/api v0.3.0 h1:e7Erw2n8lT8+IWUukktozF0bgWwH2fFC+qsXP0gabg0=
sigs.k8s.io/kustomize/api v0.3.0/go.mod h1:4jaPCtRzxfQLFdYq4gYo40dBGW1hyPp/f4AuiZB5dAQ=
sigs.k8s.io/kustomize/api v0.3.1 h1:oqMIXvS6tFEUVuKIRUKDa05eC4Hh+cb9JYg8Zhp2d24=
sigs.k8s.io/kustomize/api v0.3.1/go.mod h1:A+ATnlHqzictQfQC1q3KB/T6MSr0UWQsrrLxMWkge2E=
sigs.k8s.io/kustomize/cmd/config v0.0.2 h1:FphfIoGJ0jGGJJXq9WoG5sqqEIuTeDGx58E5NWHV8Hc=
sigs.k8s.io/kustomize/cmd/config v0.0.2/go.mod h1:c6IBoPpAAm5a2aD+0iH8IfeyCF5GPsY5Ws57Dwpcvg0=
sigs.k8s.io/kustomize/cmd/kubectl v0.0.2 h1:MxUAU5ie0tqx2MuDrUlcAL+Mgt8LVFcXc2scinSD8/w=

View File

@@ -4,6 +4,7 @@
package copyutil_test
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -104,7 +105,9 @@ func TestDiff_srcDestContentsDiffer(t *testing.T) {
diff, err := Diff(s, d)
assert.NoError(t, err)
assert.ElementsMatch(t, diff.List(), []string{"a1/f.yaml"})
assert.ElementsMatch(t, diff.List(), []string{
fmt.Sprintf("a1%sf.yaml", string(filepath.Separator)),
})
}
// TestDiff_srcDestContentsDifferInDirs verifies if identical files
@@ -130,7 +133,11 @@ func TestDiff_srcDestContentsDifferInDirs(t *testing.T) {
diff, err := Diff(s, d)
assert.NoError(t, err)
assert.ElementsMatch(t, diff.List(), []string{
"a1", "a1/f.yaml", "b1/f.yaml", "b1"})
"a1",
fmt.Sprintf("a1%sf.yaml", string(filepath.Separator)),
fmt.Sprintf("b1%sf.yaml", string(filepath.Separator)),
"b1",
})
}
// TestDiff_skipGitSrc verifies that .git directories in the source

View File

@@ -4,37 +4,36 @@
package fieldmeta
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"github.com/go-openapi/spec"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// FieldMeta contains metadata that may be attached to fields as comments
type FieldMeta struct {
// Substitutions are substitutions that may be performed against this field
Substitutions []Substitution `yaml:"substitutions,omitempty" json:"substitutions,omitempty"`
// OwnedBy records the owner of this field
OwnedBy string `yaml:"ownedBy,omitempty" json:"ownedBy,omitempty"`
// DefaultedBy records that this field was default, but may be changed by other owners
DefaultedBy string `yaml:"defaultedBy,omitempty" json:"defaultedBy,omitempty"`
// Description is a description of the current field value, e.g. why it was set
Description string `yaml:"description,omitempty" json:"description,omitempty"`
// Type is the type of the field value
Type FieldValueType `yaml:"type,omitempty" json:"type,omitempty"`
Schema spec.Schema
Extensions XKustomize
}
// Substitution defines a substitution that may be performed against the field
type Substitution struct {
// Name is the name of the substitution and read by tools
Name string `yaml:"name,omitempty" json:"name,omitempty"`
// Marker is the marker used for replacement
Marker string `yaml:"marker,omitempty" json:"marker,omitempty"`
// Value is the current value that has been substituted for the Marker
Value string `yaml:"value,omitempty" json:"value,omitempty"`
type XKustomize struct {
SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
}
// PartialFieldSetter defines how to set part of a field rather than the full field
// value. e.g. the tag part of an image field
type PartialFieldSetter struct {
// Name is the name of this setter.
Name string `yaml:"name" json:"name"`
// Value is the current value that has been set.
Value string `yaml:"value" json:"value"`
}
// Read reads the FieldMeta from a node
@@ -43,16 +42,30 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
v := strings.TrimLeft(n.YNode().LineComment, "#")
// if it doesn't Unmarshal that is fine, it means there is no metadata
// other comments are valid, they just don't parse
d := yaml.NewDecoder(bytes.NewBuffer([]byte(v)))
d.KnownFields(false)
_ = d.Decode(fm)
// TODO: consider most sophisticated parsing techniques similar to what is used
// for go struct tags.
if err := fm.Schema.UnmarshalJSON([]byte(v)); err != nil {
// note: don't return an error if the comment isn't a fieldmeta struct
return nil
}
fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
if fe == nil {
return nil
}
b, err := json.Marshal(fe)
if err != nil {
return err
}
return json.Unmarshal(b, &fm.Extensions)
}
return nil
}
// Write writes the FieldMeta to a node
func (fm *FieldMeta) Write(n *yaml.RNode) error {
b, err := json.Marshal(fm)
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
b, err := json.Marshal(fm.Schema)
if err != nil {
return err
}
@@ -67,11 +80,9 @@ const (
// String defines a string flag
String FieldValueType = "string"
// Bool defines a bool flag
Bool = "bool"
// Float defines a float flag
Float = "float"
Bool = "boolean"
// Int defines an int flag
Int = "int"
Int = "integer"
)
func (it FieldValueType) String() string {
@@ -91,10 +102,6 @@ func (it FieldValueType) Validate(value string) error {
if _, err := strconv.ParseBool(value); err != nil {
return errors.WrapPrefixf(err, "value must be a bool")
}
case Float:
if _, err := strconv.ParseFloat(value, 64); err != nil {
return errors.WrapPrefixf(err, "value must be a float")
}
}
return nil
}
@@ -107,8 +114,6 @@ func (it FieldValueType) Tag() string {
return "!!bool"
case Int:
return "!!int"
case Float:
return "!!float"
}
return ""
}
@@ -127,11 +132,6 @@ func (it FieldValueType) TagForValue(value string) string {
return ""
}
return "!!int"
case Float:
if _, err := strconv.ParseFloat(string(it), 64); err != nil {
return ""
}
return "!!float"
}
return ""
}

View File

@@ -5,10 +5,8 @@ go 1.12
require (
github.com/davecgh/go-spew v1.1.1
github.com/go-errors/errors v1.0.1
github.com/kr/pretty v0.1.0 // indirect
github.com/go-openapi/spec v0.19.5
github.com/stretchr/testify v1.4.0
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d
)

View File

@@ -1,25 +1,50 @@
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
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/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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=

View File

@@ -130,7 +130,11 @@ func (c *ContainerFilter) getArgs() []string {
// export the local environment vars to the container
for _, pair := range os.Environ() {
args = append(args, "-e", strings.Split(pair, "=")[0])
tokens := strings.Split(pair, "=")
if tokens[0] == "" {
continue
}
args = append(args, "-e", tokens[0])
}
return append(args, c.Image)
}
@@ -197,6 +201,13 @@ func GetContainerName(n *yaml.RNode) (string, string) {
// path to the function, this will be mounted into the container
path := meta.Annotations[kioutil.PathAnnotation]
functionAnnotation := meta.Annotations["config.k8s.io/function"]
if functionAnnotation != "" {
annotationContent, _ := yaml.Parse(functionAnnotation)
image, _ := annotationContent.Pipe(yaml.Lookup("container", "image"))
return image.YNode().Value, path
}
container := meta.Annotations["config.kubernetes.io/container"]
if container != "" {
return container, path

View File

@@ -131,7 +131,11 @@ metadata:
}
for _, e := range os.Environ() {
// the process env
expected = append(expected, "-e", strings.Split(e, "=")[0])
tokens := strings.Split(e, "=")
if tokens[0] == "" {
continue
}
expected = append(expected, "-e", tokens[0])
}
expected = append(expected, "example.com:version")
assert.Equal(t, expected, cmd.Args)
@@ -316,7 +320,7 @@ metadata:
c, _ := GetContainerName(n)
assert.Equal(t, "gcr.io/foo/bar:something", c)
// container from annotation
// container from config.kubernetes.io/container annotation
n, err = yaml.Parse(`apiVersion: v1
kind: MyThing
metadata:
@@ -329,6 +333,21 @@ metadata:
c, _ = GetContainerName(n)
assert.Equal(t, "gcr.io/foo/bar:something", c)
// container from config.k8s.io/function annotation
n, err = yaml.Parse(`apiVersion: v1
kind: MyThing
metadata:
annotations:
config.k8s.io/function: |
container:
image: gcr.io/foo/bar:something
`)
if !assert.NoError(t, err) {
return
}
c, _ = GetContainerName(n)
assert.Equal(t, "gcr.io/foo/bar:something", c)
// doesn't have a container
n, err = yaml.Parse(`apiVersion: v1
kind: MyThing

View File

@@ -90,7 +90,6 @@ type formatter struct {
// fmtNode recursively formats the Document Contents.
func (f *formatter) fmtNode(n *yaml.Node, path string) error {
n.Style = 0
// sort the order of mapping fields
if n.Kind == yaml.MappingNode {
sort.Sort(sortedMapContents(*n))

View File

@@ -17,6 +17,35 @@ import (
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
)
func TestFormatInput_Style(t *testing.T) {
y := `
apiVersion: v1
kind: Foo
metadata:
name: foo
spec:
notBoolean: "true"
notBoolean2: "on"
isBoolean: on
isBoolean2: true
`
expected := `apiVersion: v1
kind: Foo
metadata:
name: foo
spec:
isBoolean: on
isBoolean2: true
notBoolean: "true"
notBoolean2: "on"
`
s, err := FormatInput(strings.NewReader(y))
assert.NoError(t, err)
assert.Equal(t, expected, s.String())
}
// TestFormatInput_configMap verifies a ConfigMap yaml is formatted correctly
func TestFormatInput_configMap(t *testing.T) {
y := `
@@ -229,7 +258,7 @@ webhooks:
- CONNECT
- CREATE
- UPDATE # this list is indented by 2
scope: Namespaced
scope: "Namespaced"
timeoutSeconds: 1
`
s, err := FormatInput(strings.NewReader(y))
@@ -467,7 +496,7 @@ func TestFormatFileOrDirectory_YamlExtFileWithJson(t *testing.T) {
// check the result is formatted as yaml
b, err := ioutil.ReadFile(f.Name())
assert.NoError(t, err)
assert.Equal(t, string(testyaml.FormattedYaml1), string(b))
assert.Equal(t, string(testyaml.FormattedJSON1), string(b))
}
// TestFormatFileOrDirectory_partialKubernetesYamlFile verifies that if a yaml file contains both

View File

@@ -55,3 +55,7 @@ status2:
- 1
- 2
`)
var FormattedJSON1 = []byte(`{"apiVersion": "example.com/v1beta1", "kind": "MyType", "spec": "a", "status": {"conditions": [
3, 1, 2]}}
`)

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -269,15 +270,15 @@ func TestLocalPackageReader_Read_nestedDirs(t *testing.T) {
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`c: d # second
metadata:
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`# second thing
e: f
@@ -288,8 +289,8 @@ g:
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/b_test.yaml'
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}b_test.yaml'
`,
}
for i := range nodes {
@@ -297,7 +298,8 @@ metadata:
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, expected[i], val) {
want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator))
if !assert.Equal(t, want, val) {
return
}
}
@@ -321,25 +323,29 @@ func TestLocalPackageReader_Read_matchRegex(t *testing.T) {
assert.FailNow(t, "wrong number items")
}
val, err := nodes[0].String()
assert.NoError(t, err)
assert.Equal(t, `a: b #first
expected := []string{
`a: b #first
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
val, err = nodes[1].String()
assert.NoError(t, err)
assert.Equal(t, `c: d # second
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`c: d # second
metadata:
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
}
for i, node := range nodes {
val, err := node.String()
assert.NoError(t, err)
want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator))
assert.Equal(t, want, val)
}
}
func TestLocalPackageReader_Read_skipSubpackage(t *testing.T) {
@@ -360,25 +366,29 @@ func TestLocalPackageReader_Read_skipSubpackage(t *testing.T) {
assert.FailNow(t, "wrong number items")
}
val, err := nodes[0].String()
assert.NoError(t, err)
assert.Equal(t, `a: b #first
expected := []string{
`a: b #first
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
val, err = nodes[1].String()
assert.NoError(t, err)
assert.Equal(t, `c: d # second
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`c: d # second
metadata:
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
}
for i, node := range nodes {
val, err := node.String()
assert.NoError(t, err)
want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator))
assert.Equal(t, want, val)
}
}
func TestLocalPackageReader_Read_includeSubpackage(t *testing.T) {
@@ -398,29 +408,23 @@ func TestLocalPackageReader_Read_includeSubpackage(t *testing.T) {
if !assert.Len(t, nodes, 3) {
assert.FailNow(t, "wrong number items")
}
val, err := nodes[0].String()
assert.NoError(t, err)
assert.Equal(t, `a: b #first
expected := []string{
`a: b #first
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
val, err = nodes[1].String()
assert.NoError(t, err)
assert.Equal(t, `c: d # second
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`c: d # second
metadata:
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/package: 'a/b'
config.kubernetes.io/path: 'a/b/a_test.yaml'
`, val)
val, err = nodes[2].String()
assert.NoError(t, err)
assert.Equal(t, `# second thing
config.kubernetes.io/package: 'a${SEP}b'
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
`,
`# second thing
e: f
g:
h:
@@ -429,9 +433,17 @@ g:
metadata:
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/package: 'a/c'
config.kubernetes.io/path: 'a/c/c_test.yaml'
`, val)
config.kubernetes.io/package: 'a${SEP}c'
config.kubernetes.io/path: 'a${SEP}c${SEP}c_test.yaml'
`,
}
for i, node := range nodes {
val, err := node.String()
assert.NoError(t, err)
want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator))
assert.Equal(t, want, val)
}
}
// func TestLocalPackageReaderWriter_DeleteFiles(t *testing.T) {

View File

@@ -68,13 +68,13 @@ func TestLocalPackageWriter_Write_keepReaderAnnotations(t *testing.T) {
metadata:
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: a/b/a_test.yaml
config.kubernetes.io/path: "a/b/a_test.yaml"
---
c: d # second
metadata:
annotations:
config.kubernetes.io/index: 1
config.kubernetes.io/path: a/b/a_test.yaml
config.kubernetes.io/path: "a/b/a_test.yaml"
`, string(b))
b, err = ioutil.ReadFile(filepath.Join(d, "a", "b", "b_test.yaml"))
@@ -89,7 +89,7 @@ g:
metadata:
annotations:
config.kubernetes.io/index: 0
config.kubernetes.io/path: a/b/b_test.yaml
config.kubernetes.io/path: "a/b/b_test.yaml"
`, string(b))
}

View File

@@ -98,9 +98,15 @@ func (p TreeWriter) Write(nodes []*yaml.RNode) error {
return p.packageStructure(nodes)
case TreeStructureGraph:
return p.graphStructure(nodes)
default:
return p.packageStructure(nodes)
}
// If any resource has an owner reference, default to the graph structure. Otherwise, use package structure.
for _, node := range nodes {
if owners, _ := node.Pipe(yaml.Lookup("metadata", "ownerReferences")); owners != nil {
return p.graphStructure(nodes)
}
}
return p.packageStructure(nodes)
}
// node wraps a tree node, and any children nodes

View File

@@ -12,7 +12,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestPrinter_Write(t *testing.T) {
func TestPrinter_Write_Package_Structure(t *testing.T) {
in := `kind: Deployment
metadata:
labels:
@@ -66,7 +66,7 @@ spec:
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}},
Outputs: []Writer{TreeWriter{Writer: out, Structure: TreeStructurePackage}},
}.Execute()
if !assert.NoError(t, err) {
t.FailNow()
@@ -85,7 +85,7 @@ spec:
}
}
func TestPrinter_Write_base(t *testing.T) {
func TestPrinter_Write_Package_Structure_base(t *testing.T) {
in := `kind: Deployment
metadata:
labels:
@@ -139,7 +139,7 @@ spec:
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}},
Outputs: []Writer{TreeWriter{Writer: out, Structure: TreeStructurePackage}},
}.Execute()
if !assert.NoError(t, err) {
t.FailNow()
@@ -157,7 +157,7 @@ spec:
}
}
func TestPrinter_Write_sort(t *testing.T) {
func TestPrinter_Write_Package_Structure_sort(t *testing.T) {
in := `apiVersion: extensions/v1
kind: Deployment
metadata:
@@ -255,7 +255,7 @@ spec:
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}},
Outputs: []Writer{TreeWriter{Writer: out, Structure: TreeStructurePackage}},
}.Execute()
if !assert.NoError(t, err) {
t.FailNow()
@@ -287,7 +287,7 @@ func TestPrinter_metaError(t *testing.T) {
}
}
func TestPrinter_Write_owners(t *testing.T) {
func TestPrinter_Write_Graph_Structure(t *testing.T) {
in := `
apiVersion: v1
kind: Pod
@@ -384,3 +384,210 @@ metadata:
t.FailNow()
}
}
func TestPrinter_Write_Structure_Defaulting_when_ownerRefs_present(t *testing.T) {
in := `
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-0
namespace: myapp-staging
ownerReferences:
- apiVersion: apps/v1
kind: StatefulSet
name: cockroachdb
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-1
namespace: myapp-staging
ownerReferences:
- apiVersion: apps/v1
kind: StatefulSet
name: cockroachdb
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-2
namespace: myapp-staging
ownerReferences:
- apiVersion: apps/v1
kind: StatefulSet
name: cockroachdb
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.0
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cockroachdb
namespace: myapp-staging
ownerReferences:
- apiVersion: app.k8s.io/v1beta1
kind: Application
name: myapp
spec:
replicas: 3
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Service
metadata:
name: cockroachdb
namespace: myapp-staging
ownerReferences:
- apiVersion: app.k8s.io/v1beta1
kind: Application
name: myapp
---
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
labels:
app.kubernetes.io/name: myapp
name: myapp
namespace: myapp-staging
`
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}}, // Structure unspecified
}.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, `.
└── [Resource] Application myapp-staging/myapp
├── [Resource] Service myapp-staging/cockroachdb
└── [Resource] StatefulSet myapp-staging/cockroachdb
├── [Resource] Pod myapp-staging/cockroachdb-0
├── [Resource] Pod myapp-staging/cockroachdb-1
└── [Resource] Pod myapp-staging/cockroachdb-2
`, out.String()) {
t.FailNow()
}
}
func TestPrinter_Write_Structure_Defaulting_when_ownerRefs_absent(t *testing.T) {
in := `
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-0
namespace: myapp-staging
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-1
namespace: myapp-staging
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Pod
metadata:
name: cockroachdb-2
namespace: myapp-staging
spec:
containers:
- name: cockroachdb
image: cockraochdb:1.1.0
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cockroachdb
namespace: myapp-staging
spec:
replicas: 3
containers:
- name: cockroachdb
image: cockraochdb:1.1.1
---
apiVersion: v1
kind: Service
metadata:
name: cockroachdb
namespace: myapp-staging
---
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
labels:
app.kubernetes.io/name: myapp
name: myapp
namespace: myapp-staging
`
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}}, // Structure unspecified
}.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, `
└──
├── [.] Service myapp-staging/cockroachdb
├── [.] StatefulSet myapp-staging/cockroachdb
├── [.] Pod myapp-staging/cockroachdb-0
├── [.] Pod myapp-staging/cockroachdb-1
├── [.] Pod myapp-staging/cockroachdb-2
└── [.] Application myapp-staging/myapp
`, out.String()) {
t.FailNow()
}
}
func TestPrinter_Write_error_when_owner_missing(t *testing.T) {
in := `
---
apiVersion: v1
kind: Service
metadata:
name: cockroachdb
namespace: myapp-staging
ownerReferences:
- apiVersion: app.k8s.io/v1beta1
kind: Application
name: nginx
---
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
labels:
app.kubernetes.io/name: myapp
name: myapp
namespace: myapp-staging
`
out := &bytes.Buffer{}
err := Pipeline{
Inputs: []Reader{&ByteReader{Reader: bytes.NewBufferString(in)}},
Outputs: []Writer{TreeWriter{Writer: out}},
}.Execute()
assert.Error(t, err)
assert.Equal(t, "owner 'Application myapp-staging/nginx' not found in input, but found as an owner of input objects", err.Error())
}

View File

@@ -1,25 +1,25 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package sub
package setters
import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ kio.Filter = &SetSubstitutionMarker{}
var _ kio.Filter = &CreateSetter{}
// Sub performs substitutions
type SetSubstitutionMarker struct {
// Marker is the marker to set
Marker Marker
// CreateSetter creates a custom setter as an OpenAPI property through a comment
type CreateSetter struct {
// customFieldSetter is the marker to set
SetPartialField customFieldSetter
// ResourceMeta defines the Resource to set the marker on
ResourceMeta yaml.ResourceMeta
}
func (s *SetSubstitutionMarker) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
func (s *CreateSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range input {
m, err := input[i].GetMeta()
if err != nil {
@@ -31,7 +31,7 @@ func (s *SetSubstitutionMarker) Filter(input []*yaml.RNode) ([]*yaml.RNode, erro
if s.ResourceMeta.Kind != "" && m.Kind != s.ResourceMeta.Kind {
continue
}
if err := input[i].PipeE(&s.Marker); err != nil {
if err := input[i].PipeE(&s.SetPartialField); err != nil {
return nil, err
}
}

117
kyaml/setters/addyaml.go Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &customFieldSetter{}
// customFieldSetter creates a new custom field setter
type customFieldSetter struct {
// Path is the path of the field to add the setter for
Field string
// Setter is the setter to add
Setter fieldmeta.PartialFieldSetter
// Description is the description to add to the OpenAPI
Description string
// SetBy is the setBy to add to the OpenAPI extension
SetBy string
Type string
// Partial will create a partial setter if set to true
Partial bool
// currentFieldName is the name of the current field being processed
currentFieldName string
}
// Filter performs the setter for a single object
func (m *customFieldSetter) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
return m.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
// record the current field name, resetting it back to its original value
// when done
n := m.currentFieldName
defer func() { m.currentFieldName = n }()
m.currentFieldName = node.Key.YNode().Value
return node.Value.PipeE(m)
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
return node.PipeE(m)
})
case yaml.ScalarNode:
// only create the setter for fields with the given name
if m.currentFieldName != m.Field {
return object, nil
}
if err := m.create(object); err != nil {
return nil, err
}
return object, nil
default:
return object, nil
}
}
func (m *customFieldSetter) create(field *yaml.RNode) error {
fm := fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return errors.Wrap(err)
}
if m.Description != "" {
fm.Schema.Description = m.Description
}
fm.Extensions.SetBy = m.SetBy
fm.Schema.Type = []string{m.Type}
if !m.Partial {
// doesn't match the supplied value
if field.YNode().Value != m.Setter.Value {
return nil
}
// full setter
fm.Extensions.FieldSetter = &m.Setter
fm.Extensions.PartialFieldSetters = nil
} else {
// doesn't match the supplied value
if !strings.Contains(field.YNode().Value, m.Setter.Value) {
return nil
}
found := false
for i := range fm.Extensions.PartialFieldSetters {
s := fm.Extensions.PartialFieldSetters[i]
if s.Name == m.Setter.Name {
// update the setter if we find it
found = true
fm.Extensions.PartialFieldSetters[i] = m.Setter
break
}
}
if !found {
// add the setter if it wasn't found
fm.Extensions.PartialFieldSetters = append(fm.Extensions.PartialFieldSetters, m.Setter)
}
}
if err := fm.Write(field); err != nil {
return errors.Wrap(err)
}
return nil
}

45
kyaml/setters/dokio.go Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters
import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ kio.Filter = &PerformSetters{}
// PerformSetters sets field values
type PerformSetters struct {
// Name is the name of the setter to perform
Name string
// Value is the value to set
Value string
// Description, if set will annotate the field with a description.
Description string
// SetBy, if set will annotate the field with who set it.
SetBy string
// Count is set by Filter and is the number of fields modified.
Count int
}
func (s *PerformSetters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range input {
p := &fieldSetter{
Name: s.Name,
Value: s.Value,
Description: s.Description,
SetBy: s.SetBy,
}
if err := input[i].PipeE(p); err != nil {
return nil, err
}
s.Count += p.Count
}
return input, nil
}

130
kyaml/setters/doyaml.go Normal file
View File

@@ -0,0 +1,130 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package sub substitutes strings in fields
package setters
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &fieldSetter{}
// fieldSetter sets part or all of a field value.
type fieldSetter struct {
// Name is the name of the setter to perform.
Name string
// Value is the value to set.
Value string
// Description, if specified will set 'description' for the field. Optional.
Description string
// SetBy, if specified will set 'setBy' for the field. Optional.
SetBy string
// Count is incremented by Filter for each field that is set.
Count int
}
// Filter implements yaml.Filter
func (fs *fieldSetter) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
// Document is the root of the object and always contains 1 node
return fs.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
// Traverse each field value
return node.Value.PipeE(fs)
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
// Traverse each list element
return node.PipeE(fs)
})
case yaml.ScalarNode:
// Check if there is a setter matching the name
s, f, partial, err := fs.findSetter(object)
if err != nil {
return nil, err
}
if s == nil {
// no matching setter
return object, nil
}
// set the field value
return object, fs.set(object, s, f, partial)
default:
return object, nil
}
}
// findPartialSetter finds the setter matching the name if one exists
func (fs *fieldSetter) findSetter(field *yaml.RNode) (
*fieldmeta.PartialFieldSetter, *fieldmeta.FieldMeta, bool, error) {
// check if there are any substitutions for this field
var fm = &fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return nil, nil, false, err
}
if fs.SetBy != "" {
fm.Extensions.SetBy = fs.SetBy
}
if fs.Description != "" {
fm.Schema.Description = fs.Description
}
if fm.Extensions.FieldSetter != nil && fm.Extensions.FieldSetter.Name == fs.Name {
return fm.Extensions.FieldSetter, fm, false, nil
}
// check if there is a matching substitution
for i := range fm.Extensions.PartialFieldSetters {
if fm.Extensions.PartialFieldSetters[i].Name == fs.Name {
return &fm.Extensions.PartialFieldSetters[i], fm, true, nil
}
}
return nil, nil, false, nil
}
// set performs the substitution for the given field, substitution, and metadata
func (fs *fieldSetter) set(
field *yaml.RNode, s *fieldmeta.PartialFieldSetter,
f *fieldmeta.FieldMeta, partial bool) error {
if s.Value == fs.Value || !strings.Contains(field.YNode().Value, s.Value) {
// no substitutions necessary -- already substituted or doesn't have the set value
// which acts as a marker
return nil
}
// record that the config has been modified
fs.Count++
if !partial {
// full setter
field.YNode().Value = fs.Value
} else {
// replace the current value with the new value
field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Value, fs.Value)
}
// be sure to set the tag to the matching type so the yaml doesn't incorrectly quote
//integers or booleans as strings
fType := fieldmeta.FieldValueType(f.Schema.Type[0])
if err := fType.Validate(field.YNode().Value); err != nil {
return err
}
field.YNode().Tag = fType.Tag()
// update the comment on the field
s.Value = fs.Value
if err := f.Write(field); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,65 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters
import (
"sort"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ kio.Filter = &LookupSetters{}
// LookupSetters identifies setters for a collection of Resources
type LookupSetters struct {
// Name is the name of the setter to match. Optional.
Name string
// SetterCounts is populated by Filter and contains the count of fields matching each setter.
SetterCounts []setterCount
}
// setterCount records the identified setters and number of fields matching those setters
type setterCount struct {
// Count is the number of substitutions possible to perform
Count int
// setter is the substitution found
setter
}
// Filter implements kio.Filter
func (l *LookupSetters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
setters := map[string]*setterCount{}
for i := range input {
// lookup substitutions for this object
ls := &lookupSetters{Name: l.Name}
if err := input[i].PipeE(ls); err != nil {
return nil, err
}
// aggregate counts for each setter by name. takes the description and value from
// the first setter for each name encountered.
for j := range ls.Setters {
setter := ls.Setters[j]
curr, found := setters[setter.Name]
if !found {
curr = &setterCount{setter: setter}
setters[setter.Name] = curr
}
curr.Count++
}
}
// pull out and sort the results by setter name
for _, v := range setters {
l.SetterCounts = append(l.SetterCounts, *v)
}
sort.Slice(l.SetterCounts, func(i, j int) bool {
return l.SetterCounts[i].Name < l.SetterCounts[j].Name
})
return input, nil
}

View File

@@ -0,0 +1,86 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters
import (
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var _ yaml.Filter = &lookupSetters{}
// lookupSetters looks up setters for a Resource
type lookupSetters struct {
// Name of the setter to lookup. Optional
Name string
// Setters is a list of setters that were found
Setters []setter
}
type setter struct {
fieldmeta.PartialFieldSetter
Description string
Type string
SetBy string
}
func (ls *lookupSetters) Filter(object *yaml.RNode) (*yaml.RNode, error) {
switch object.YNode().Kind {
case yaml.DocumentNode:
// skip the document node
return ls.Filter(yaml.NewRNode(object.YNode().Content[0]))
case yaml.MappingNode:
return object, object.VisitFields(func(node *yaml.MapNode) error {
return node.Value.PipeE(ls)
})
case yaml.SequenceNode:
return object, object.VisitElements(func(node *yaml.RNode) error {
return node.PipeE(ls)
})
case yaml.ScalarNode:
return object, ls.lookup(object)
default:
return object, nil
}
}
// lookup finds any setters for a field
func (ls *lookupSetters) lookup(field *yaml.RNode) error {
// check if there is a substitution for this field
var fm = &fieldmeta.FieldMeta{}
if err := fm.Read(field); err != nil {
return err
}
if fm.Extensions.FieldSetter != nil {
if ls.Name != "" && ls.Name != fm.Extensions.FieldSetter.Name {
// skip this setter, it doesn't match the specified setter
return nil
}
// full setter
ls.Setters = append(ls.Setters, setter{
PartialFieldSetter: *fm.Extensions.FieldSetter,
Description: fm.Schema.Description,
Type: fm.Schema.Type[0],
SetBy: fm.Extensions.SetBy,
})
return nil
}
// partial setters
for i := range fm.Extensions.PartialFieldSetters {
if ls.Name != "" && ls.Name != fm.Extensions.PartialFieldSetters[i].Name {
// skip this setter
continue
}
ls.Setters = append(ls.Setters, setter{
PartialFieldSetter: fm.Extensions.PartialFieldSetters[i],
Description: fm.Schema.Description,
Type: fm.Schema.Type[0],
SetBy: fm.Extensions.SetBy,
})
}
return nil
}

View File

@@ -446,14 +446,18 @@ type FieldSetter struct {
// value on the ScalarNode.
Name string `yaml:"name,omitempty"`
// YNode is the value to set.
// Value is the value to set.
// Optional if Kind is set.
Value *RNode `yaml:"value,omitempty"`
StringValue string `yaml:"stringValue,omitempty"`
// OverrideStyle can be set to override the style of the existing node
// when setting it. Otherwise, if an existing node is found, the style is
// retained.
OverrideStyle bool `yaml:"overrideStyle,omitempty"`
}
// FieldSetter returns an GrepFilter that sets the named field to the given value.
func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
if s.StringValue != "" && s.Value == nil {
s.Value = NewScalarRNode(s.StringValue)
@@ -463,6 +467,12 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
if err := ErrorIfInvalid(rn, yaml.ScalarNode); err != nil {
return rn, err
}
// only apply the style if there is not an existing style
// or we want to override it
if !s.OverrideStyle || s.Value.YNode().Style == 0 {
// keep the original style if it exists
s.Value.YNode().Style = rn.YNode().Style
}
rn.SetYNode(s.Value.YNode())
return rn, nil
}
@@ -477,6 +487,12 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
return nil, err
}
if field != nil {
// only apply the style if there is not an existing style
// or we want to override it
if !s.OverrideStyle || field.YNode().Style == 0 {
// keep the original style if it exists
s.Value.YNode().Style = field.YNode().Style
}
// need to def ref the Node since field is ephemeral
field.SetYNode(s.Value.YNode())
return field, nil

View File

@@ -646,6 +646,25 @@ metadata:
`, assertNoErrorString(t)(r0.String()))
}
func TestUpdateAnnotation_Fn(t *testing.T) {
// create metadata.annotations field
r0 := assertNoError(t)(Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a.b.c: "h.i.j"
`))
rn := assertNoError(t)(r0.Pipe(SetAnnotation("a.b.c", "d.e.f")))
assert.Equal(t, "\"d.e.f\"\n", assertNoErrorString(t)(rn.String()))
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a.b.c: "d.e.f"
`, assertNoErrorString(t)(r0.String()))
}
func TestRNode_GetMeta(t *testing.T) {
s := `apiVersion: v1/apps
kind: Deployment

View File

@@ -23,8 +23,7 @@ kind: Deployment
items:
- name: foo
image: foo:v1
command:
- run.sh
command: ['run.sh']
`,
},
@@ -47,8 +46,7 @@ kind: Deployment
items:
- name: foo
image: foo:v1
command:
- run.sh
command: ['run.sh']
`,
},
@@ -62,15 +60,14 @@ items:
`,
`
kind: Deployment
items: {}
items: []
`,
`
kind: Deployment
items:
- name: foo
image: foo:v1
command:
- run.sh
command: ['run.sh']
`,
},
@@ -90,8 +87,7 @@ kind: Deployment
items:
- name: foo
image: foo:v1
command:
- run.sh
command: ['run.sh']
`,
},
@@ -118,8 +114,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
},
@@ -146,8 +141,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
},
@@ -174,8 +168,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
},
@@ -205,8 +198,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
},
@@ -225,8 +217,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
`
kind: Deployment
@@ -235,8 +226,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
},
@@ -255,8 +245,7 @@ items:
image: foo:v1
- name: bar
image: bar:v1
command:
- run2.sh
command: ['run2.sh']
`,
`
kind: Deployment

View File

@@ -43,6 +43,9 @@ func (m Merger) VisitMap(nodes walk.Sources) (*yaml.RNode, error) {
if err := m.SetComments(nodes); err != nil {
return nil, err
}
if err := m.SetStyle(nodes); err != nil {
return nil, err
}
if yaml.IsEmpty(nodes.Dest()) {
// Add
return nodes.Origin(), nil
@@ -59,6 +62,9 @@ func (m Merger) VisitScalar(nodes walk.Sources) (*yaml.RNode, error) {
if err := m.SetComments(nodes); err != nil {
return nil, err
}
if err := m.SetStyle(nodes); err != nil {
return nil, err
}
// Override value
if nodes.Origin() != nil {
return nodes.Origin(), nil
@@ -71,6 +77,9 @@ func (m Merger) VisitList(nodes walk.Sources, kind walk.ListKind) (*yaml.RNode,
if err := m.SetComments(nodes); err != nil {
return nil, err
}
if err := m.SetStyle(nodes); err != nil {
return nil, err
}
if kind == walk.NonAssociateList {
// Override value
if nodes.Origin() != nil {
@@ -92,6 +101,25 @@ func (m Merger) VisitList(nodes walk.Sources, kind walk.ListKind) (*yaml.RNode,
return nodes.Dest(), nil
}
func (m Merger) SetStyle(sources walk.Sources) error {
source := sources.Origin()
dest := sources.Dest()
if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
// avoid panic
return nil
}
// copy the style from the source.
// special case: if the dest was an empty map or seq, then it probably had
// folded style applied, but we actually want to keep the style of the origin
// in this case (even if it was the default). otherwise the merged elements
// will get folded even though this probably isn't what is desired.
if dest.YNode().Kind != yaml.ScalarNode && len(dest.YNode().Content) == 0 {
dest.YNode().Style = source.YNode().Style
}
return nil
}
// SetComments copies the dest comments to the source comments if they are present
// on the source.
func (m Merger) SetComments(sources walk.Sources) error {

View File

@@ -83,10 +83,7 @@ spec:
containers:
- name: nginx
image: nginx:1.7.9
args:
- c
- a
- b
args: ['c', 'a', 'b']
env:
- name: DEMO_GREETING
value: "Hello from the environment"
@@ -139,10 +136,7 @@ spec:
containers:
- name: nginx
image: nginx:1.7.9
args:
- c
- a
- b
args: ['c', 'a', 'b']
env:
- name: DEMO_GREETING
value: "Hello from the environment"
@@ -208,10 +202,7 @@ spec:
containers:
- name: nginx
image: nginx:1.7.9
args:
- c
- a
- b
args: ['c', 'a', 'b']
env:
- name: DEMO_GREETING
value: "Hello from the environment"
@@ -276,10 +267,7 @@ spec:
containers:
- name: nginx
image: nginx:1.7.9
args:
- c
- a
- b
args: ['c', 'a', 'b']
env:
- name: DEMO_GREETING
value: "New Demo Greeting"
@@ -343,10 +331,7 @@ spec:
containers:
- name: nginx
image: nginx:1.7.9
args:
- e
- d
- f
args: ['e', 'd', 'f']
env:
- name: DEMO_GREETING
value: "Hello from the environment"

View File

@@ -174,8 +174,7 @@ kind: Deployment
containers:
- name: foo
image: foo:bar
command:
- run2.sh
command: ['run2.sh']
`, nil},
//
@@ -206,8 +205,7 @@ kind: Deployment
containers:
- name: foo
image: foo:bar
command:
- run2.sh
command: ['run2.sh']
`, nil},
//
@@ -239,8 +237,7 @@ kind: Deployment
containers:
- name: foo
image: foo:bar
command:
- run2.sh
command: ['run2.sh']
`, nil},
//
@@ -367,8 +364,7 @@ kind: Deployment
containers:
- name: foo
image: foo:bar
command:
- run.sh
command: ['run.sh']
`, nil},
//

View File

@@ -146,9 +146,6 @@ func NewListRNode(values ...string) *RNode {
// NewRNode returns a new RNode pointer containing the provided Node.
func NewRNode(value *yaml.Node) *RNode {
if value != nil {
value.Style = 0
}
return &RNode{value: value}
}
@@ -389,7 +386,7 @@ const (
Flow = "Flow"
)
// String returns a string valuer for a Node, applying the supplied formatting options
// String returns a string value for a Node, applying the supplied formatting options
func String(node *yaml.Node, opts ...string) (string, error) {
if node == nil {
return "", nil

View File

@@ -6,17 +6,17 @@
# kyaml version
export kyaml_major=0
export kyaml_minor=0
export kyaml_patch=2
export kyaml_patch=4
# kustomize api version
export api_major=0
export api_minor=3
export api_patch=0
export api_patch=1
# cmd/config version
export cmd_config_major=0
export cmd_config_minor=0
export cmd_config_patch=2
export cmd_config_patch=4
# cmd/kubectl version
export cmd_kubectl_major=0
@@ -26,7 +26,7 @@ export cmd_kubectl_patch=2
# kustomize version
export kustomize_major=3
export kustomize_minor=5
export kustomize_patch=1
export kustomize_patch=3
export pluginator_major=2
export pluginator_minor=1

View File

@@ -55,7 +55,7 @@ function releaseModule {
git tag -a $tag -m "Release $tag on branch $branch"
git push upstream $tag
else
printf "\nSkipping push binary $binary -- run with NO_DRY_RUN=true to push the release.\n\n"
printf "\nSkipping push module $module -- run with NO_DRY_RUN=true to push the release.\n\n"
fi
# cleanup release artifacts
@@ -63,24 +63,10 @@ function releaseModule {
rm -rf $wktree
git worktree prune
git branch -D $branch
echo "$module complete"
}
function releaseBinary {
# move the latest tag for the binary to trigger cloudbuild
binary=$1
echo "binary: $binary"
if [ "$NO_DRY_RUN" == "true" ]; then
git tag -d latest_$binary
git push upstream :latest_$binary
git tag -a latest_$binary
git push upstream latest_$binary
else
echo "Skipping push binary $binary -- run with NO_DRY_RUN=true to push the release."
fi
}
modules="kyaml api cmd/config cmd/kubectl pluginator kustomize"
# configure the branch and tag names
@@ -112,7 +98,11 @@ fi
# release the module
releaseModule $module
# release the kustomize binary if the module is kustomize
if [ "$module" == "kustomize" ]; then
releaseBinary $module
# TODO: Do this for all modules
pushd .
getter=$(mktemp -d /tmp/kustomize-releases-XXXXXX)
cd $getter
go get sigs.k8s.io/kustomize/$module/v3
popd
fi

View File

@@ -1,3 +1,6 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
# Exits with status 0 if it can be determined that the
# current PR should not trigger all travis checks.
#

View File

@@ -1,16 +1,21 @@
#!/bin/bash
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
set -e
cd kyaml
make all
cd ../cmd/config
make all
cd ../kubectl
make all
# run all tests for kyaml and related commands
targets="kyaml cmd/config cmd/kubectl functions/examples/injection-tshirt-sizes functions/examples/template-go-nginx functions/examples/template-heredoc-cockroachdb functions/examples/validator-kubeval functions/examples/validator-resource-requests"
for target in $targets; do
pushd .
cd $target
make all
popd
done
# make sure no files were generated or changed by make
cd ../..
# ignore changes to go.mod and go.sum -- they are too flaky
find . -name go.mod | xargs git checkout --
find . -name go.sum | xargs git checkout --
git add .
git diff-index HEAD --
git diff-index HEAD --exit-code