mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-09-15 12:18:57 +00:00
Merge branch 'master' into ListSubstitions
This commit is contained in:
28
.github/workflows/go.yml
vendored
Normal file
28
.github/workflows/go.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Go
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.13
|
||||||
|
id: go
|
||||||
|
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: ./travis/kyaml-pre-commit.sh
|
||||||
|
env:
|
||||||
|
KUSTOMIZE_DOCKER_E2E: true
|
||||||
@@ -154,9 +154,8 @@ LEGUME=chickpea
|
|||||||
`)
|
`)
|
||||||
th.WriteF("/app/overlay/configmap/dummy.txt",
|
th.WriteF("/app/overlay/configmap/dummy.txt",
|
||||||
`Lorem ipsum dolor sit amet, consectetur
|
`Lorem ipsum dolor sit amet, consectetur
|
||||||
|
adipiscing elit, sed do eiusmod tempor
|
||||||
adipiscing elit, sed do eiusmod tempor
|
incididunt ut labore et dolore magna aliqua.
|
||||||
incididunt ut labore et dolore magna aliqua.
|
|
||||||
`)
|
`)
|
||||||
th.WriteF("/app/overlay/deployment/deployment.yaml", `
|
th.WriteF("/app/overlay/deployment/deployment.yaml", `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
@@ -293,11 +292,8 @@ metadata:
|
|||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
nonsense: |
|
nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
|
||||||
Lorem ipsum dolor sit amet, consectetur
|
tempor\nincididunt ut labore et dolore magna aliqua. \n"
|
||||||
|
|
||||||
adipiscing elit, sed do eiusmod tempor
|
|
||||||
incididunt ut labore et dolore magna aliqua.
|
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@@ -306,6 +302,6 @@ metadata:
|
|||||||
app: mungebot
|
app: mungebot
|
||||||
org: kubernetes
|
org: kubernetes
|
||||||
repo: test-infra
|
repo: test-infra
|
||||||
name: test-infra-app-config-hh272bg5d4
|
name: test-infra-app-config-f462h769f9
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -86,17 +85,11 @@ func (kvl *loader) keyValuesFromFileSources(sources []string) ([]types.Pair, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kvs = append(kvs, types.Pair{Key: k, Value: trimTrailingSpacesInLines(string(content))})
|
kvs = append(kvs, types.Pair{Key: k, Value: string(content)})
|
||||||
}
|
}
|
||||||
return kvs, nil
|
return kvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// trimTrailingSpacesInLines takes string with multiple lines and trims the trailing white spaces and tabs from each line.
|
|
||||||
func trimTrailingSpacesInLines(str string) string {
|
|
||||||
re := regexp.MustCompile(`[ \t]*\n`)
|
|
||||||
return re.ReplaceAllString(str, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvl *loader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) {
|
func (kvl *loader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) {
|
||||||
var kvs []types.Pair
|
var kvs []types.Pair
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
|
|||||||
@@ -95,12 +95,3 @@ func TestKeyValuesFromFileSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTrimTrailingSpacesInLines(t *testing.T) {
|
|
||||||
input := "\"fooKey\": \"fooValue\" \t\n \t\t \n\t\"barKey\": \"barValue\""
|
|
||||||
expected := "\"fooKey\": \"fooValue\"\n\n\t\"barKey\": \"barValue\""
|
|
||||||
res := trimTrailingSpacesInLines(input)
|
|
||||||
if !reflect.DeepEqual(res, expected) {
|
|
||||||
t.Errorf("Trim trailing spaces in lines should succeed, got: %s exptected: %s", res, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ linters:
|
|||||||
# - gochecknoinits
|
# - gochecknoinits
|
||||||
# - goconst
|
# - goconst
|
||||||
# - gocritic
|
# - gocritic
|
||||||
- gocyclo
|
# - gocyclo
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
# - golint
|
# - golint
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ lint:
|
|||||||
$(GOBIN)/golangci-lint run ./...
|
$(GOBIN)/golangci-lint run ./...
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test -cover ./...
|
go test -v -timeout 45m -cover ./...
|
||||||
|
|
||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|||||||
@@ -3,15 +3,12 @@ module sigs.k8s.io/kustomize/cmd/config
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible // indirect
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10 // indirect
|
|
||||||
github.com/go-errors/errors v1.0.1
|
github.com/go-errors/errors v1.0.1
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/posener/complete/v2 v2.0.1-alpha.12
|
github.com/posener/complete/v2 v2.0.1-alpha.12
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect
|
|
||||||
k8s.io/apimachinery v0.17.0
|
k8s.io/apimachinery v0.17.0
|
||||||
sigs.k8s.io/kustomize/kyaml v0.1.7
|
sigs.k8s.io/kustomize/kyaml v0.1.7
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,11 +23,9 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -161,7 +159,6 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
|
||||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
@@ -180,7 +177,6 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
|
|||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -192,12 +188,10 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
|||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
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 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
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=
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||||
@@ -206,12 +200,9 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -219,10 +210,8 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-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-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 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -232,17 +221,13 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -256,11 +241,6 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200430040329-4b814e061378/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
|||||||
661
cmd/config/internal/commands/e2e/e2e_test.go
Normal file
661
cmd/config/internal/commands/e2e/e2e_test.go
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunE2e(t *testing.T) {
|
||||||
|
binDir, err := ioutil.TempDir("", "kustomize-test-")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
//defer os.RemoveAll(binDir)
|
||||||
|
build(t, binDir)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args func(string) []string
|
||||||
|
files func(string) map[string]string
|
||||||
|
expectedFiles func(string) map[string]string
|
||||||
|
expectedErr string
|
||||||
|
skipIfFalseEnv string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exec_function_no_args",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{
|
||||||
|
"--enable-exec", "--exec-path", filepath.Join(d, "e2econtainerconfig"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: ''
|
||||||
|
a-int-value: '0'
|
||||||
|
a-bool-value: 'false'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "exec_function_args",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{
|
||||||
|
"--enable-exec", "--exec-path", filepath.Join(d, "e2econtainerconfig"),
|
||||||
|
"--", "stringValue=a", "intValue=1", "boolValue=true",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '1'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "exec_function_config",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{"--enable-exec"}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig")),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
a-string-value: ''
|
||||||
|
a-int-value: '0'
|
||||||
|
a-bool-value: 'false'
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig"))}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Starklark function tests
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "exec_function_config",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{"--enable-exec"}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig")),
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '2'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig")),
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '2'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// NOTE: Do not change the expected value of this test. It is to ensure that
|
||||||
|
// exec functions are off by default when run from the CLI.
|
||||||
|
// exec functions execute arbitrary code outside of a sandbox environment.
|
||||||
|
//
|
||||||
|
name: "exec_function_config_disabled",
|
||||||
|
args: func(d string) []string { return []string{} },
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig")),
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": fmt.Sprintf(`
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
exec:
|
||||||
|
path: "%s"
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`, filepath.Join(d, "e2econtainerconfig")),
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "exec_function_no_enable",
|
||||||
|
expectedErr: "must specify --enable-exec with --exec-path",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{"--exec-path", filepath.Join(d, "e2econtainerconfig")}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: ''
|
||||||
|
a-int-value: '0'
|
||||||
|
a-bool-value: 'false'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Container
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "container_function_no_args",
|
||||||
|
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{"--image", "gcr.io/kustomize-functions/e2econtainerconfig"}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: ''
|
||||||
|
a-int-value: '0'
|
||||||
|
a-bool-value: 'false'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "container_function_args",
|
||||||
|
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{
|
||||||
|
"--image", "gcr.io/kustomize-functions/e2econtainerconfig",
|
||||||
|
"--", "stringValue=a", "intValue=1", "boolValue=true",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '1'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "container_function_config",
|
||||||
|
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
|
||||||
|
args: func(d string) []string { return []string{} },
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
container:
|
||||||
|
image: "gcr.io/kustomize-functions/e2econtainerconfig"
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
container:
|
||||||
|
image: "gcr.io/kustomize-functions/e2econtainerconfig"
|
||||||
|
a-string-value: ''
|
||||||
|
a-int-value: '0'
|
||||||
|
a-bool-value: 'false'
|
||||||
|
`}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "container_function_config",
|
||||||
|
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
|
||||||
|
args: func(d string) []string { return []string{} },
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": `
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
container:
|
||||||
|
image: "gcr.io/kustomize-functions/e2econtainerconfig"
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`,
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": `
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
container:
|
||||||
|
image: "gcr.io/kustomize-functions/e2econtainerconfig"
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '2'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
data:
|
||||||
|
stringValue: a
|
||||||
|
intValue: 2
|
||||||
|
boolValue: true
|
||||||
|
`,
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-string-value: 'a'
|
||||||
|
a-int-value: '2'
|
||||||
|
a-bool-value: 'true'
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "starlark_function_config",
|
||||||
|
args: func(d string) []string { return []string{"--enable-star"} },
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"script.star": `
|
||||||
|
# set the foo annotation on each resource
|
||||||
|
def run(r, fc):
|
||||||
|
for resource in r:
|
||||||
|
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
|
||||||
|
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
|
||||||
|
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
|
||||||
|
|
||||||
|
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
|
||||||
|
`,
|
||||||
|
"config.yaml": `
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
starlark:
|
||||||
|
path: script.star
|
||||||
|
name: fn
|
||||||
|
data:
|
||||||
|
boolValue: true
|
||||||
|
intValue: 2
|
||||||
|
stringValue: a
|
||||||
|
`,
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"config.yaml": `
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Input
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-bool-value: true
|
||||||
|
a-int-value: 2
|
||||||
|
a-string-value: a
|
||||||
|
config.kubernetes.io/function: |
|
||||||
|
starlark:
|
||||||
|
path: script.star
|
||||||
|
name: fn
|
||||||
|
data:
|
||||||
|
boolValue: true
|
||||||
|
intValue: 2
|
||||||
|
stringValue: a
|
||||||
|
`,
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-bool-value: true
|
||||||
|
a-int-value: 2
|
||||||
|
a-string-value: a
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "starlark_function_path",
|
||||||
|
args: func(d string) []string {
|
||||||
|
return []string{
|
||||||
|
"--enable-star", "--star-path", "script.star",
|
||||||
|
"--", "stringValue=a", "intValue=2", "boolValue=true",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
files: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"script.star": `
|
||||||
|
# set the foo annotation on each resource
|
||||||
|
def run(r, fc):
|
||||||
|
for resource in r:
|
||||||
|
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
|
||||||
|
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
|
||||||
|
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
|
||||||
|
|
||||||
|
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
|
||||||
|
`,
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedFiles: func(d string) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"deployment.yaml": `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
a-bool-value: true
|
||||||
|
a-int-value: 2
|
||||||
|
a-string-value: a
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
tt := tests[i]
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.skipIfFalseEnv != "" && os.Getenv(tt.skipIfFalseEnv) == "false" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "kustomize-test-data-")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
os.Chdir(dir)
|
||||||
|
|
||||||
|
// write the input
|
||||||
|
for path, data := range tt.files(binDir) {
|
||||||
|
err := ioutil.WriteFile(path, []byte(data), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args := append([]string{"run", "."}, tt.args(binDir)...)
|
||||||
|
cmd := exec.Command(filepath.Join(binDir, "kyaml"), args...)
|
||||||
|
cmd.Dir = dir
|
||||||
|
var stdErr, stdOut bytes.Buffer
|
||||||
|
cmd.Stdout = &stdOut
|
||||||
|
cmd.Stderr = &stdErr
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if tt.expectedErr != "" {
|
||||||
|
if !assert.Contains(t, stdErr.String(), tt.expectedErr) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, err, stdErr.String()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
for path, data := range tt.expectedFiles(binDir) {
|
||||||
|
b, err := ioutil.ReadFile(path)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(data), strings.TrimSpace(string(b))) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func build(t *testing.T, binDir string) {
|
||||||
|
build := exec.Command("go", "build", "-o",
|
||||||
|
filepath.Join(binDir, "e2econtainerconfig"))
|
||||||
|
build.Dir = "e2econtainerconfig"
|
||||||
|
build.Stdout = os.Stdout
|
||||||
|
build.Stderr = os.Stderr
|
||||||
|
if !assert.NoError(t, build.Run()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
build = exec.Command("go", "build", "-o", filepath.Join(binDir, "kyaml"))
|
||||||
|
build.Dir = filepath.Join("..", "..", "..")
|
||||||
|
build.Stdout = os.Stdout
|
||||||
|
build.Stderr = os.Stderr
|
||||||
|
if !assert.NoError(t, build.Run()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("KUSTOMIZE_DOCKER_E2E") == "false" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
build = exec.Command(
|
||||||
|
"docker", "build", ".", "-t", "gcr.io/kustomize-functions/e2econtainerconfig")
|
||||||
|
build.Dir = "e2econtainerconfig"
|
||||||
|
build.Stdout = os.Stdout
|
||||||
|
build.Stderr = os.Stderr
|
||||||
|
if !assert.NoError(t, build.Run()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
FROM golang:1.13-stretch
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
WORKDIR /go/src/
|
||||||
|
COPY . .
|
||||||
|
RUN go build -v -o /usr/local/bin/function ./
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
|
||||||
|
CMD ["function"]
|
||||||
13
cmd/config/internal/commands/e2e/e2econtainerconfig/Makefile
Normal file
13
cmd/config/internal/commands/e2e/e2econtainerconfig/Makefile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
.PHONY: generate license fix vet fmt test build tidy
|
||||||
|
|
||||||
|
GOBIN := $(shell go env GOPATH)/bin
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test
|
||||||
|
|
||||||
|
image:
|
||||||
|
docker build . -t gcr.io/kustomize-functions/e2econtainerconfig
|
||||||
|
docker push gcr.io/kustomize-functions/e2econtainerconfig
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Package main contains a function to be used for e2e testing.
|
||||||
|
//
|
||||||
|
// The function is written using the framework, and parses the ResourceList.functionConfig
|
||||||
|
// into a go struct.
|
||||||
|
//
|
||||||
|
// The function will set 3 annotations on each resource.
|
||||||
|
//
|
||||||
|
// See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
|
||||||
|
package main
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module sigs.k8s.io/kustomize/cmd/config/internal/commands/e2e/e2econtainerconfig
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167
|
||||||
|
|
||||||
183
cmd/config/internal/commands/e2e/e2econtainerconfig/go.sum
Normal file
183
cmd/config/internal/commands/e2e/e2econtainerconfig/go.sum
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
|
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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-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/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||||
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
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=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167 h1:138Q3rZVU5mLBF6dFekNuP6D4ZeF4mndI54RBJ8kK8c=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167/go.mod h1:I4OFZ1vTPdteiqqCBwW3DI0swPzxBpd99y9CHN5IMUU=
|
||||||
57
cmd/config/internal/commands/e2e/e2econtainerconfig/main.go
Normal file
57
cmd/config/internal/commands/e2e/e2econtainerconfig/main.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data contains the items
|
||||||
|
type Data struct {
|
||||||
|
StringValue string `yaml:"stringValue,omitempty"`
|
||||||
|
|
||||||
|
IntValue int `yaml:"intValue,omitempty"`
|
||||||
|
|
||||||
|
BoolValue bool `yaml:"boolValue,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example defines the ResourceList.functionConfig schema.
|
||||||
|
type Example struct {
|
||||||
|
// Data contains configuration data for the Example
|
||||||
|
// Nest values under Data so that the function can accept a ConfigMap as its
|
||||||
|
// functionConfig (`run` generates a ConfigMap for the functionConfig when run with --)
|
||||||
|
Data Data `yaml:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
functionConfig := &Example{}
|
||||||
|
|
||||||
|
cmd := framework.Command(functionConfig, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
for i := range items {
|
||||||
|
if err := items[i].PipeE(yaml.SetAnnotation("a-string-value",
|
||||||
|
functionConfig.Data.StringValue)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := items[i].PipeE(yaml.SetAnnotation("a-int-value",
|
||||||
|
fmt.Sprintf("%v", functionConfig.Data.IntValue))); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := items[i].PipeE(yaml.SetAnnotation("a-bool-value",
|
||||||
|
fmt.Sprintf("%v", functionConfig.Data.BoolValue))); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -41,15 +41,17 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
|||||||
r.Command.Flags().StringVar(
|
r.Command.Flags().StringVar(
|
||||||
&r.Image, "image", "",
|
&r.Image, "image", "",
|
||||||
"run this image as a function instead of discovering them.")
|
"run this image as a function instead of discovering them.")
|
||||||
|
// NOTE: exec plugins execute arbitrary code -- never change the default value of this flag!!!
|
||||||
r.Command.Flags().BoolVar(
|
r.Command.Flags().BoolVar(
|
||||||
&r.EnableStar, "enable-star", false, "enable support for starlark functions.")
|
&r.EnableExec, "enable-exec", false /*do not change!*/, "enable support for exec functions -- note: exec functions run arbitrary code -- do not use for untrusted configs!!! (Alpha)")
|
||||||
r.Command.Flags().MarkHidden("enable-star")
|
|
||||||
r.Command.Flags().StringVar(
|
r.Command.Flags().StringVar(
|
||||||
&r.StarPath, "star-path", "", "run a starlark script as a function.")
|
&r.ExecPath, "exec-path", "", "run an executable as a function. (Alpha)")
|
||||||
r.Command.Flags().MarkHidden("star-path")
|
r.Command.Flags().BoolVar(
|
||||||
|
&r.EnableStar, "enable-star", false, "enable support for starlark functions. (Alpha)")
|
||||||
r.Command.Flags().StringVar(
|
r.Command.Flags().StringVar(
|
||||||
&r.StarName, "star-name", "", "name of starlark program.")
|
&r.StarPath, "star-path", "", "run a starlark script as a function. (Alpha)")
|
||||||
r.Command.Flags().MarkHidden("star-name")
|
r.Command.Flags().StringVar(
|
||||||
|
&r.StarName, "star-name", "", "name of starlark program. (Alpha)")
|
||||||
|
|
||||||
r.Command.Flags().StringVar(
|
r.Command.Flags().StringVar(
|
||||||
&r.ResultsDir, "results-dir", "", "write function results to this dir")
|
&r.ResultsDir, "results-dir", "", "write function results to this dir")
|
||||||
@@ -79,6 +81,8 @@ type RunFnRunner struct {
|
|||||||
EnableStar bool
|
EnableStar bool
|
||||||
StarPath string
|
StarPath string
|
||||||
StarName string
|
StarName string
|
||||||
|
EnableExec bool
|
||||||
|
ExecPath string
|
||||||
RunFns runfn.RunFns
|
RunFns runfn.RunFns
|
||||||
ResultsDir string
|
ResultsDir string
|
||||||
Network bool
|
Network bool
|
||||||
@@ -94,14 +98,13 @@ func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
// Functions to run.
|
// Functions to run.
|
||||||
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) (
|
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) (
|
||||||
[]*yaml.RNode, error) {
|
[]*yaml.RNode, error) {
|
||||||
if r.Image == "" && r.StarPath == "" {
|
if r.Image == "" && r.StarPath == "" && r.ExecPath == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var fn *yaml.RNode
|
var fn *yaml.RNode
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// if image isn't specified, then Functions is empty
|
|
||||||
if r.Image != "" {
|
if r.Image != "" {
|
||||||
// create the function spec to set as an annotation
|
// create the function spec to set as an annotation
|
||||||
fn, err = yaml.Parse(`container: {}`)
|
fn, err = yaml.Parse(`container: {}`)
|
||||||
@@ -143,8 +146,19 @@ func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else if r.EnableExec && r.ExecPath != "" {
|
||||||
return nil, nil
|
// create the function spec to set as an annotation
|
||||||
|
fn, err = yaml.Parse(`exec: {}`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fn.PipeE(
|
||||||
|
yaml.Lookup("exec"),
|
||||||
|
yaml.SetField("path", yaml.NewScalarRNode(r.ExecPath)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the function config
|
// create the function config
|
||||||
@@ -208,10 +222,10 @@ data: {}
|
|||||||
return []*yaml.RNode{rc}, nil
|
return []*yaml.RNode{rc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toStorageMounts(mounts []string) []filters.StorageMount {
|
func toStorageMounts(mounts []string) []runtimeutil.StorageMount {
|
||||||
var sms []filters.StorageMount
|
var sms []runtimeutil.StorageMount
|
||||||
for _, mount := range mounts {
|
for _, mount := range mounts {
|
||||||
sms = append(sms, filters.StringToStorageMount(mount))
|
sms = append(sms, runtimeutil.StringToStorageMount(mount))
|
||||||
}
|
}
|
||||||
return sms
|
return sms
|
||||||
}
|
}
|
||||||
@@ -221,8 +235,12 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
return errors.Errorf("must specify --enable-star with --star-path")
|
return errors.Errorf("must specify --enable-star with --star-path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !r.EnableExec && r.ExecPath != "" {
|
||||||
|
return errors.Errorf("must specify --enable-exec with --exec-path")
|
||||||
|
}
|
||||||
|
|
||||||
if c.ArgsLenAtDash() >= 0 && r.Image == "" &&
|
if c.ArgsLenAtDash() >= 0 && r.Image == "" &&
|
||||||
!(r.EnableStar && r.StarPath != "") {
|
!(r.EnableStar && r.StarPath != "") && !(r.EnableExec && r.ExecPath != "") {
|
||||||
return errors.Errorf("must specify --image")
|
return errors.Errorf("must specify --image")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +288,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
Network: r.Network,
|
Network: r.Network,
|
||||||
NetworkName: r.NetworkName,
|
NetworkName: r.NetworkName,
|
||||||
EnableStarlark: r.EnableStar,
|
EnableStarlark: r.EnableStar,
|
||||||
|
EnableExec: r.EnableExec,
|
||||||
StorageMounts: storageMounts,
|
StorageMounts: storageMounts,
|
||||||
ResultsDir: r.ResultsDir,
|
ResultsDir: r.ResultsDir,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,16 @@
|
|||||||
|
|
||||||
## 发行说明
|
## 发行说明
|
||||||
|
|
||||||
|
* [kustomize/3.2.2](/../../releases/tag/kustomize%2Fv3.2.2) - 基于 kustomize Go API [3.3.0](../v3.3.0.md) 的 `kustomize` CLI。
|
||||||
|
|
||||||
|
* [API 3.3.0](../v3.3.0.md) - kustomize Go API 的首个 release,该 release 不包含 `kustomize` CLI,且之后 CLI 和 API 将独立发布。
|
||||||
|
|
||||||
|
* [kustomize/3.2.1](/../../releases/tag/kustomize%2Fv3.2.1) - `kustomize` CLI 基于 kustomize Go API release [3.2.0](../v3.2.0.md) 发布的补丁。
|
||||||
|
|
||||||
|
* [3.2.0](../v3.2.0.md)
|
||||||
|
|
||||||
|
* [3.1.1](../v3.1.0.md)
|
||||||
|
|
||||||
* [3.1](../v3.1.0.md) - 2019年7月下旬,扩展 patches 和改进的资源匹配。
|
* [3.1](../v3.1.0.md) - 2019年7月下旬,扩展 patches 和改进的资源匹配。
|
||||||
|
|
||||||
* [3.0](../v3.0.0.md) - 2019年6月下旬,插件开发者发布。
|
* [3.0](../v3.0.0.md) - 2019年6月下旬,插件开发者发布。
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
# Kustomization 文件字段
|
# Kustomization 文件字段
|
||||||
|
|
||||||
介绍 [kustomization](../glossary.md#kustomization) 文件中各字段的含义。
|
[field-name-namespace]: ../plugins/builtins.md#field-name-namespace
|
||||||
|
[field-name-images]: ../plugins/builtins.md#field-name-images
|
||||||
|
[field-names-namePrefix-nameSuffix]: ../plugins/builtins.md#field-names-namePrefix-nameSuffix
|
||||||
|
[field-name-patches]: ../plugins/builtins.md#field-name-patches
|
||||||
|
[field-name-patchesStrategicMerge]: ../plugins/builtins.md#field-name-patchesStrategicMerge
|
||||||
|
[field-name-patchesJson6902]: ../plugins/builtins.md#field-name-patchesJson6902
|
||||||
|
[field-name-replicas]: ../plugins/builtins.md#field-name-replicas
|
||||||
|
[field-name-secretGenerator]: ../plugins/builtins.md#field-name-secretGenerator
|
||||||
|
[field-name-commonLabels]: ../plugins/builtins.md#field-name-commonLabels
|
||||||
|
[field-name-commonAnnotations]: ../plugins/builtins.md#field-name-commonAnnotations
|
||||||
|
[field-name-configMapGenerator]: ../plugins/builtins.md#field-name-configMapGenerator
|
||||||
|
|
||||||
|
介绍 [kustomization.yaml](../glossary.md#kustomization) 配置文件中各字段的含义。
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
@@ -8,36 +20,36 @@
|
|||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|[resources](#resources) | list | 包含 k8s API 对象的文件,或其他包含 kustomizations 文件的目录。 |
|
|[resources](#resources) | list | 包含 k8s API 对象的文件,或其他包含 `kustomization.yaml` 文件的目录。 |
|
||||||
|[CRDs](#crds)| list | CDR 文件,以允许在资源列表中指定自定义资源。 |
|
|[CRDs](#crds)| list | CDR 文件,允许在资源列表中指定自定义资源。 |
|
||||||
|
|
||||||
## Generators
|
## Generators
|
||||||
|
|
||||||
生成可定制的对象。
|
资源生成器。
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|[configMapGenerator](#configmapgenerator)| list | 列表中的每个条目都将创建一个 ConfigMap (它是n个 ConfigMap 的生成器)。 |
|
|[configMapGenerator](#configmapgenerator)| list | 列表中的每个条目都将生成一个 ConfigMap (合计可以生成 n 个 ConfigMap)。 |
|
||||||
|[secretGenerator](#secretgenerator)| list | 此列表中的每个条目都将创建一个 Secret 资源(它是n个 secrets 的生成器)。 |
|
|[secretGenerator](#secretgenerator)| list | 列表中的每个条目都将生成一个 Secret(合计可以生成 n 个 Secrets)。 |
|
||||||
|[generatorOptions](#generatoroptions)| string | generatorOptions 可以修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。 |
|
|[generatorOptions](#generatoroptions)| string | generatorOptions 可以修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。 |
|
||||||
|[generators](#generators)| list | [插件](../plugins)配置文件。 |
|
|[generators](#generators)| list | [插件](../plugins)配置文件。 |
|
||||||
|
|
||||||
## Transformers
|
## Transformers
|
||||||
|
|
||||||
可用的转换。
|
资源字段转换项。
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [commonLabels](#commonlabels) | string | 为所有资源和 selectors 增加 Labels 。 |
|
| [commonLabels](#commonlabels) | string | 为所有资源和 selectors 增加 Labels 。 |
|
||||||
| [commonAnnotations](#commonannotations) | string | 为所有资源增加 Annotations 。 |
|
| [commonAnnotations](#commonannotations) | string | 为所有资源增加 Annotations 。 |
|
||||||
| [images](#images) | list | 修改镜像的名称、tag 或 image digest ,而无需使用 patches 。 |
|
| [images](#images) | list | 无需使用 patches,即可修改镜像的名称、tag 或 image digest。 |
|
||||||
| [inventory](#inventory) | struct | 用于生成一个包含清单信息的对象。 |
|
| [inventory](#inventory) | struct | 用于生成一个包含清单信息的对象。 |
|
||||||
| [namespace](#namespace) | string | 为所有 resources 添加 namespace 。 |
|
| [namespace](#namespace) | string | 为所有 resources 添加 namespace 。 |
|
||||||
| [namePrefix](#nameprefix) | string | 该字段的值将添加在所有资源的名称之前。 |
|
| [namePrefix](#nameprefix) | string | 为所有资源的名称添加前缀。 |
|
||||||
| [nameSuffix](#namesuffix) | string | 该字段的值将添加在所有资源的名称后面。 |
|
| [nameSuffix](#namesuffix) | string | 为所有资源的名称添加后缀。 |
|
||||||
| [replicas](#replicas) | list | 修改资源的副本数。 |
|
| [replicas](#replicas) | list | 修改资源的副本数。 |
|
||||||
| [patchesStrategicMerge](#patchesstrategicmerge) | list | 此列表中的每个条目都应可以解析为部分或完整的资源定义文件。 |
|
| [patchesStrategicMerge](#patchesstrategicmerge) | list | 此列表中的每个条目都应可以解析为部分或完整的资源定义文件。 |
|
||||||
| [patchesJson6902](#patchesjson6902) | list | 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch 。 |
|
| [patchesJson6902](#patchesjson6902) | list | 列表中的每个条目都应可以解析为 Kubernetes 对象和将应用于该对象的 JSON patch 。 |
|
||||||
| [transformers](#transformers) | list | [插件](../plugins)配置文件。 |
|
| [transformers](#transformers) | list | [插件](../plugins)配置文件。 |
|
||||||
|
|
||||||
## Meta
|
## Meta
|
||||||
@@ -46,7 +58,7 @@
|
|||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [vars](#vars) | string | 获取一个对象中的字段并插入到另外的对象中。 |
|
| [vars](#vars) | string | 将一个对象中的字段值插入另一个对象中。 |
|
||||||
| [apiVersion](#apiversion) | string | [k8s metadata] 字段。 |
|
| [apiVersion](#apiversion) | string | [k8s metadata] 字段。 |
|
||||||
| [kind](#kind) | string | [k8s metadata] 字段。 |
|
| [kind](#kind) | string | [k8s metadata] 字段。 |
|
||||||
|
|
||||||
@@ -67,59 +79,26 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
|
|
||||||
### commonLabels
|
### commonLabels
|
||||||
|
|
||||||
为所有资源和 selectors 增加 Labels
|
详见 [field-name-commonLabels]。
|
||||||
|
|
||||||
```
|
|
||||||
commonLabels:
|
|
||||||
someName: someValue
|
|
||||||
owner: alice
|
|
||||||
app: bingo
|
|
||||||
```
|
|
||||||
|
|
||||||
### commonAnnotations
|
### commonAnnotations
|
||||||
|
|
||||||
为所有资源增加 Annotations ,和 labels 一样是 key:value 的键值对。
|
详见 [field-name-commonAnnotations].
|
||||||
|
|
||||||
```
|
|
||||||
commonAnnotations:
|
|
||||||
oncallPager: 800-555-1212
|
|
||||||
```
|
|
||||||
|
|
||||||
### configMapGenerator
|
### configMapGenerator
|
||||||
|
|
||||||
列表中的每个条目都将创建一个 ConfigMap (它是n个 ConfigMap 的生成器)。
|
详见 [field-name-configMapGenerator].
|
||||||
|
|
||||||
下面的示例创建了两个 ConfigMaps:
|
|
||||||
|
|
||||||
- 一个具有给定文件的名称和内容
|
|
||||||
- 另一个包含 key/value 键值对数据
|
|
||||||
|
|
||||||
每个 configMapGenerator 项都可以使用 `behavior: [create|replace|merge]` 参数。
|
|
||||||
|
|
||||||
允许 overlay 从父级修改或替换现有的 configMap。
|
|
||||||
|
|
||||||
```
|
|
||||||
configMapGenerator:
|
|
||||||
- name: myJavaServerProps
|
|
||||||
files:
|
|
||||||
- application.properties
|
|
||||||
- more.properties
|
|
||||||
- name: myJavaServerEnvVars
|
|
||||||
literals:
|
|
||||||
- JAVA_HOME=/opt/java/jdk
|
|
||||||
- JAVA_TOOL_OPTIONS=-agentlib:hprof
|
|
||||||
```
|
|
||||||
|
|
||||||
### crds
|
### crds
|
||||||
|
|
||||||
此列表中的每个条目都应该是自定义资源定义(CRD)文件的相对路径。
|
此列表中的每个条目都应该是自定义资源定义(CRD)文件的相对路径。
|
||||||
|
|
||||||
该字段的存在是为了让 kustomize 知道用户自定义的 CRD ,并对这些类型中的对象应用适当的转换。
|
该字段的存在是为了让 kustomize 识别用户自定义的 CRD ,并对这些类型中的对象应用适当的转换。
|
||||||
|
|
||||||
典型用例:CRD 引用 ConfigMap 对象
|
典型用例:CRD 引用 ConfigMap 对象
|
||||||
|
|
||||||
在 kustomization 中,ConfigMap 对象名称可能会通过 namePrefix 、nameSuffix 或 hashing 来更改 CRD 对象中此 ConfigMap 对象的名称,
|
在 kustomization 中,ConfigMap 对象名称可能会通过 `namePrefix` 、`nameSuffix` 或 `hashing` 来更改 CRD 对象中该 ConfigMap 对象的名称,
|
||||||
引用时需要以相同的方式使用 namePrefix 、 nameSuffix 或 hashing 来进行更新。
|
引用时需要以相同的方式使用 `namePrefix` 、 `nameSuffix` 或 `hashing` 来进行更新。
|
||||||
|
|
||||||
Annotations 可以放入 openAPI 的定义中:
|
Annotations 可以放入 openAPI 的定义中:
|
||||||
|
|
||||||
@@ -140,7 +119,7 @@ crds:
|
|||||||
|
|
||||||
### generatorOptions
|
### generatorOptions
|
||||||
|
|
||||||
generatorOptions 修改所有 [ConfigMapGenerator](#configmapgenerator) 和 [SecretGenerator](#secretgenerator) 的行为。
|
generatorOptions 可以修改所有 [ConfigMapGenerator](#configmapgenerator) 和 [SecretGenerator](#secretgenerator) 的行为。
|
||||||
|
|
||||||
```
|
```
|
||||||
generatorOptions:
|
generatorOptions:
|
||||||
@@ -166,41 +145,7 @@ generators:
|
|||||||
|
|
||||||
### images
|
### images
|
||||||
|
|
||||||
修改镜像的名称、tag 或 image digest ,而无需使用 patches 。例如,对于这种 kubernetes Deployment 片段:
|
详见 [field-name-images]。
|
||||||
|
|
||||||
```
|
|
||||||
containers:
|
|
||||||
- name: mypostgresdb
|
|
||||||
image: postgres:8
|
|
||||||
- name: nginxapp
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: myapp
|
|
||||||
image: my-demo-app:latest
|
|
||||||
- name: alpine-app
|
|
||||||
image: alpine:3.7
|
|
||||||
```
|
|
||||||
|
|
||||||
可以通过以下方式更改 `image` :
|
|
||||||
|
|
||||||
- `postgres:8` to `my-registry/my-postgres:v1`,
|
|
||||||
- nginx tag `1.7.9` to `1.8.0`,
|
|
||||||
- image name `my-demo-app` to `my-app`,
|
|
||||||
- alpine's tag `3.7` to a digest value
|
|
||||||
|
|
||||||
可以在 *kustomization* 中添加以下内容:
|
|
||||||
|
|
||||||
```
|
|
||||||
images:
|
|
||||||
- name: postgres
|
|
||||||
newName: my-registry/my-postgres
|
|
||||||
newTag: v1
|
|
||||||
- name: nginx
|
|
||||||
newTag: 1.8.0
|
|
||||||
- name: my-demo-app
|
|
||||||
newName: my-app
|
|
||||||
- name: alpine
|
|
||||||
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
|
||||||
```
|
|
||||||
|
|
||||||
### inventory
|
### inventory
|
||||||
|
|
||||||
@@ -217,120 +162,31 @@ kind: Kustomization
|
|||||||
|
|
||||||
### namespace
|
### namespace
|
||||||
|
|
||||||
为所有 resources 添加 namespace 。
|
详见 [field-name-namespace]。
|
||||||
|
|
||||||
```
|
|
||||||
namespace: my-namespace
|
|
||||||
```
|
|
||||||
|
|
||||||
### namePrefix
|
### namePrefix
|
||||||
|
|
||||||
该字段的值将添加在所有资源的名称之前,例如 将资源名称 `wordpress` 变为 `alices-wordpress` 。
|
详见 [field-names-namePrefix-nameSuffix]。
|
||||||
|
|
||||||
```
|
|
||||||
namePrefix: alices-
|
|
||||||
```
|
|
||||||
|
|
||||||
### nameSuffix
|
### nameSuffix
|
||||||
|
|
||||||
该字段的值将添加在所有资源的名称后面,例如 将资源名称 `wordpress` 变为 `wordpress-v2` 。
|
详见 [field-names-namePrefix-nameSuffix]。
|
||||||
|
|
||||||
如果资源类型为 ConfigMap 或 Secret ,则在哈希值之前附加后缀。
|
### patches
|
||||||
|
|
||||||
```
|
详见 [field-name-patches]。
|
||||||
nameSuffix: -v2
|
|
||||||
```
|
|
||||||
|
|
||||||
### patchesStrategicMerge
|
### patchesStrategicMerge
|
||||||
|
|
||||||
此列表中的每个条目都应可以解析为部分或完整的资源定义文件。
|
详见 [field-name-patchesStrategicMerge]。
|
||||||
|
|
||||||
这些(也可能是部分的)资源文件中的 name 必须与已经通过 `resources` 加载的 name 字段匹配,或者通过 `bases` 中的 name 字段匹配。这些条目将用于 _patch_(修改)已知资源。
|
|
||||||
|
|
||||||
推荐使用小的 patches,例如:修改内存的 request/limit,更改 ConfigMap 中的 env 变量等小的 patches 易于维护和查看,并且易于在 overlays 中混合使用。
|
|
||||||
|
|
||||||
```
|
|
||||||
patchesStrategicMerge:
|
|
||||||
- service_port_8888.yaml
|
|
||||||
- deployment_increase_replicas.yaml
|
|
||||||
- deployment_increase_memory.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### patchesJson6902
|
### patchesJson6902
|
||||||
|
|
||||||
patchesJson6902 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch
|
详见 [field-name-patchesJson6902]。
|
||||||
|
|
||||||
JSON patch 的文档地址:https://tools.ietf.org/html/rfc6902
|
|
||||||
|
|
||||||
目标字段指向的 kubernetes 对象的 group、 version、 kind、 name 和 namespace 在同一 kustomization 内 path 字段内容是 JSON patch 文件的相对路径。
|
|
||||||
|
|
||||||
patch 文件中的内容可以如下这种 JSON 格式:
|
|
||||||
|
|
||||||
```
|
|
||||||
[
|
|
||||||
{"op": "add", "path": "/some/new/path", "value": "value"},
|
|
||||||
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
也可以使用 YAML 格式表示:
|
|
||||||
|
|
||||||
```
|
|
||||||
- op: add
|
|
||||||
path: /some/new/path
|
|
||||||
value: value
|
|
||||||
- op: replace
|
|
||||||
path: /some/existing/path
|
|
||||||
value: new value
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
patchesJson6902:
|
|
||||||
- target:
|
|
||||||
version: v1
|
|
||||||
kind: Deployment
|
|
||||||
name: my-deployment
|
|
||||||
path: add_init_container.yaml
|
|
||||||
- target:
|
|
||||||
version: v1
|
|
||||||
kind: Service
|
|
||||||
name: my-service
|
|
||||||
path: add_service_annotation.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### replicas
|
### replicas
|
||||||
|
|
||||||
修改资源的副本数。
|
详见 [field-name-replicas]。
|
||||||
|
|
||||||
例如:对于如下 kubernetes Deployment 片段:
|
|
||||||
|
|
||||||
```
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: deployment-name
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
```
|
|
||||||
|
|
||||||
在 kustomization 中添加以下内容,将副本数更改为5:
|
|
||||||
|
|
||||||
```
|
|
||||||
replicas:
|
|
||||||
- name: deployment-name
|
|
||||||
count: 5
|
|
||||||
```
|
|
||||||
|
|
||||||
该字段内容为列表,所以可以同时修改许多资源。
|
|
||||||
|
|
||||||
#### Limitation
|
|
||||||
|
|
||||||
由于这个声明无法设置 `kind:` 或 `group:` 它将匹配任何可以匹配名称的 `group` 和 `kind` ,并且它是以下之一:
|
|
||||||
- `Deployment`
|
|
||||||
- `ReplicationController`
|
|
||||||
- `ReplicaSet`
|
|
||||||
- `StatefulSet`
|
|
||||||
|
|
||||||
对于更复杂的用例,请使用 patch 。
|
|
||||||
|
|
||||||
### resources
|
### resources
|
||||||
|
|
||||||
@@ -348,8 +204,7 @@ resource:
|
|||||||
|
|
||||||
将以深度优先的顺序读取和处理资源。
|
将以深度优先的顺序读取和处理资源。
|
||||||
|
|
||||||
|
文件应包含 YAML 格式的 k8s 资源。一个资源描述文件可以含有多个由(`---`)分隔的资源。
|
||||||
文件应包含 YAML 格式的 k8s 资源。一个资源描述文件可以含有多个由(“---”)分隔的资源。
|
|
||||||
应该包含 `resources` 字段的 kustomization 文件的指定文件目录的相对路径。
|
应该包含 `resources` 字段的 kustomization 文件的指定文件目录的相对路径。
|
||||||
|
|
||||||
[hashicorp URL]: https://github.com/hashicorp/go-getter#url-format
|
[hashicorp URL]: https://github.com/hashicorp/go-getter#url-format
|
||||||
@@ -358,31 +213,11 @@ resource:
|
|||||||
|
|
||||||
### secretGenerator
|
### secretGenerator
|
||||||
|
|
||||||
此列表中的每个条目都将创建一个 Secret 资源(它是n个 secrets 的生成器)。
|
详见 [field-name-secretGenerator]。
|
||||||
|
|
||||||
```
|
|
||||||
secretGenerator:
|
|
||||||
- name: app-tls
|
|
||||||
files:
|
|
||||||
- secret/tls.cert
|
|
||||||
- secret/tls.key
|
|
||||||
type: "kubernetes.io/tls"
|
|
||||||
- name: app-tls-namespaced
|
|
||||||
# you can define a namespace to generate secret in, defaults to: "default"
|
|
||||||
namespace: apps
|
|
||||||
files:
|
|
||||||
- tls.crt=catsecret/tls.cert
|
|
||||||
- tls.key=secret/tls.key
|
|
||||||
type: "kubernetes.io/tls"
|
|
||||||
- name: env_file_secret
|
|
||||||
envs:
|
|
||||||
- env.txt
|
|
||||||
type: Opaque
|
|
||||||
```
|
|
||||||
|
|
||||||
### vars
|
### vars
|
||||||
|
|
||||||
Vars 用于从一个 resource 字段中获取文本,并将该文本插入指定位置 - 反射功能。
|
Vars 用于从一个 resource 字段中获取值,并将该值插入指定位置 - 反射功能。
|
||||||
|
|
||||||
例如,假设需要在容器的 command 中指定了 Service 对象的名称,并在容器的 env 中指定了 Secret 对象的名称来确保以下内容可以正常工作:
|
例如,假设需要在容器的 command 中指定了 Service 对象的名称,并在容器的 env 中指定了 Secret 对象的名称来确保以下内容可以正常工作:
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +1,120 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package framework contains a framework for writing functions in go.
|
// Package framework contains a framework for writing functions in go. The function spec
|
||||||
|
// is defined at: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
|
||||||
//
|
//
|
||||||
// Example
|
// Examples
|
||||||
//
|
//
|
||||||
// Example function implementation to set an annotation on each resource.
|
// Example function implementation using framework.ResourceList with functionConfig
|
||||||
//
|
//
|
||||||
|
// type Spec struct {
|
||||||
|
// Value string `yaml:"value,omitempty"`
|
||||||
|
// }
|
||||||
|
// type Example struct {
|
||||||
|
// Spec Spec `yaml:"spec,omitempty"`
|
||||||
|
// }
|
||||||
|
// functionConfig := &Example{}
|
||||||
|
//
|
||||||
|
// rl := framework.ResourceList{FunctionConfig: functionConfig}
|
||||||
|
// if err := rl.Read(); err != nil { return err }
|
||||||
|
//
|
||||||
|
// for i := range rl.Items {
|
||||||
|
// // modify the items...
|
||||||
|
// }
|
||||||
|
// if err := rl.Write(); err != nil { return err }
|
||||||
|
//
|
||||||
|
// Example function implementation using framework.Command with flags
|
||||||
|
//
|
||||||
|
// var value string
|
||||||
// cmd := framework.Command(nil, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
// cmd := framework.Command(nil, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
// for i := range items {
|
// for i := range items {
|
||||||
// if err := items[i].PipeE(yaml.SetAnnotation("value", value)); err != nil {
|
// // modify the items...
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// }
|
// }
|
||||||
// return items, nil
|
// return items, nil
|
||||||
// })
|
// })
|
||||||
// cmd.Flags().StringVar(&value, "value", "", "annotation value")
|
// cmd.Flags().StringVar(&value, "value", "", "annotation value")
|
||||||
// if err := cmd.Execute(); err != nil {
|
// if err := cmd.Execute(); err != nil { return err }
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
// Architecture
|
// Architecture
|
||||||
//
|
//
|
||||||
// Functions are implemented as a go function which accept a slice of resources (items)
|
// Functions modify a slice of resources (ResourceList.items) which are read as input and written
|
||||||
// and returns a modified slice of resources (items).
|
// as output. The function itself may be configured through a functionConfig
|
||||||
|
// (ResourceList.functionConfig).
|
||||||
|
//
|
||||||
|
// Example Function Input:
|
||||||
|
//
|
||||||
|
// kind: ResourceList
|
||||||
|
// items:
|
||||||
|
// - kind: Deployment
|
||||||
|
// ...
|
||||||
|
// - kind: Service
|
||||||
|
// ....
|
||||||
|
// functionConfig:
|
||||||
|
// kind: Example
|
||||||
|
// spec:
|
||||||
|
// value: foo
|
||||||
|
//
|
||||||
|
// The functionConfig may be specified declaratively and run with
|
||||||
|
//
|
||||||
|
// config run DIR/
|
||||||
|
//
|
||||||
|
// Declarative function declaration:
|
||||||
|
//
|
||||||
|
// kind: Example
|
||||||
|
// metadata:
|
||||||
|
// annotations:
|
||||||
|
// # run the function by creating this container and providing this
|
||||||
|
// # Example as the functionConfig
|
||||||
|
// config.kubernetes.io/function: |
|
||||||
|
// image: image/containing/fuction:impl
|
||||||
|
// spec:
|
||||||
|
// value: foo
|
||||||
|
//
|
||||||
|
// The framework takes care of serializing and deserializing the ResourceList.
|
||||||
|
//
|
||||||
|
// Generated ResourceList.functionConfig -- ConfigMaps
|
||||||
|
//
|
||||||
|
// Functions may also be specified imperatively and run using:
|
||||||
|
//
|
||||||
|
// config run DIR/ --image image/containing/fuction:impl -- value=foo
|
||||||
|
//
|
||||||
|
// When run imperatively, a ConfigMap is generated for the functionConfig, and the command
|
||||||
|
// arguments are set as ConfigMap data entries.
|
||||||
|
//
|
||||||
|
// kind: ConfigMap
|
||||||
|
// data:
|
||||||
|
// value: foo
|
||||||
|
//
|
||||||
|
// To write a function that can be run imperatively on the commandline, have it take a
|
||||||
|
// ConfigMap as its functionConfig.
|
||||||
//
|
//
|
||||||
// Mutator and Generator Functions
|
// Mutator and Generator Functions
|
||||||
//
|
//
|
||||||
// Functions may add, delete or modify resources for the returned slice.
|
// Functions may add, delete or modify resources by modifying the items slice.
|
||||||
|
// When using framework.Command this is done through returning the new items slice.
|
||||||
|
// When using framework.ResourceList this is done through modifying ResourceList.Items in place.
|
||||||
//
|
//
|
||||||
// Validator Functions
|
// Validator Functions
|
||||||
//
|
//
|
||||||
// Functions may validate resources, returning Results as go errors. Results may contain
|
// A function may validate resources by providing a Result.
|
||||||
// different items for different validation failures.
|
// When using framework.Command this is done through returning a framework.Result as an error.
|
||||||
|
// WHen using framework.ResourceList this is done through setting ResourceList.Result.
|
||||||
//
|
//
|
||||||
// Configuring Functions
|
// Configuring Functions
|
||||||
//
|
//
|
||||||
// Functions may be configured through a functionConfig (i.e. a client side custom resource),
|
// Functions may be configured through a functionConfig (i.e. a client side custom resource),
|
||||||
// or through flags (which the framework parses from a ConfigMap provided as input).
|
// or through flags (which the framework parses from a ConfigMap provided as input).
|
||||||
// Any flags registered on the cobra.Command will be parsed from the functionConfig input
|
//
|
||||||
// if they are defined as functionConfig.data entries.
|
// When using framework.Command, any flags registered on the cobra.Command will be parsed
|
||||||
|
// from the functionConfig input if they are defined as functionConfig.data entries.
|
||||||
|
//
|
||||||
|
// When using framework.ResourceList, any flags set on the ResourceList.Flags will be
|
||||||
|
// parsed from the functionConfig input if they are defined as functionConfig.data entries.
|
||||||
//
|
//
|
||||||
// Functions may also access environment variables set by the caller.
|
// Functions may also access environment variables set by the caller.
|
||||||
//
|
//
|
||||||
// Function Input
|
// Building a container image for the function
|
||||||
//
|
|
||||||
// The framework parses the function ResourceList.items into a slice of yaml.RNodes, and
|
|
||||||
// parses the ResourceList.functionConfig into a passed in struct (optional).
|
|
||||||
//
|
|
||||||
// Building the Container
|
|
||||||
//
|
//
|
||||||
// The go program must be built into a container to be run as a function. The framework
|
// The go program must be built into a container to be run as a function. The framework
|
||||||
// can be used to generate a Dockerfile to build the function container.
|
// can be used to generate a Dockerfile to build the function container.
|
||||||
|
|||||||
@@ -7,10 +7,82 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const service = "Service"
|
||||||
|
|
||||||
|
// ExampleResourceList_modify implements a function that sets an annotation on each resource.
|
||||||
|
// The annotation value is configured via a flag value parsed from ResourceList.functionConfig.data
|
||||||
|
func ExampleResourceList_modify() {
|
||||||
|
// for testing purposes only -- normally read from stdin when Executing
|
||||||
|
input := bytes.NewBufferString(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
# items are provided as nodes
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
# functionConfig is parsed into flags by framework.Command
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
value: baz
|
||||||
|
`)
|
||||||
|
|
||||||
|
// configure the annotation value using a flag parsed from
|
||||||
|
// ResourceList.functionConfig.data.value
|
||||||
|
fs := pflag.NewFlagSet("tests", pflag.ContinueOnError)
|
||||||
|
value := fs.String("value", "", "annotation value")
|
||||||
|
rl := framework.ResourceList{
|
||||||
|
Flags: fs,
|
||||||
|
Reader: input, // for testing only
|
||||||
|
}
|
||||||
|
if err := rl.Read(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := range rl.Items {
|
||||||
|
// set the annotation on each resource item
|
||||||
|
if err := rl.Items[i].PipeE(yaml.SetAnnotation("value", *value)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := rl.Write(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
// kind: ResourceList
|
||||||
|
// items:
|
||||||
|
// - apiVersion: apps/v1
|
||||||
|
// kind: Deployment
|
||||||
|
// metadata:
|
||||||
|
// name: foo
|
||||||
|
// annotations:
|
||||||
|
// value: 'baz'
|
||||||
|
// - apiVersion: v1
|
||||||
|
// kind: Service
|
||||||
|
// metadata:
|
||||||
|
// name: foo
|
||||||
|
// annotations:
|
||||||
|
// value: 'baz'
|
||||||
|
// functionConfig:
|
||||||
|
// apiVersion: v1
|
||||||
|
// kind: ConfigMap
|
||||||
|
// data:
|
||||||
|
// value: baz
|
||||||
|
}
|
||||||
|
|
||||||
// ExampleCommand_modify implements a function that sets an annotation on each resource.
|
// ExampleCommand_modify implements a function that sets an annotation on each resource.
|
||||||
// The annotation value is configured via a flag value parsed from
|
// The annotation value is configured via a flag value parsed from
|
||||||
// ResourceList.functionConfig.data
|
// ResourceList.functionConfig.data
|
||||||
@@ -102,7 +174,7 @@ func ExampleCommand_generateReplace() {
|
|||||||
|
|
||||||
// something we already generated, remove it from the list so we regenerate it
|
// something we already generated, remove it from the list so we regenerate it
|
||||||
if meta.Name == functionConfig.Spec.Name &&
|
if meta.Name == functionConfig.Spec.Name &&
|
||||||
meta.Kind == "Service" &&
|
meta.Kind == service &&
|
||||||
meta.APIVersion == "v1" {
|
meta.APIVersion == "v1" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -163,6 +235,95 @@ functionConfig:
|
|||||||
// name: bar
|
// name: bar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExampleResourceList_generateReplace generates a resource from a functionConfig.
|
||||||
|
// If the resource already exist s, it replaces the resource with a new copy.
|
||||||
|
func ExampleResourceList_generateReplace() {
|
||||||
|
input := bytes.NewBufferString(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
# items are provided as nodes
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
# functionConfig is parsed into flags by framework.Command
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: ExampleServiceGenerator
|
||||||
|
spec:
|
||||||
|
name: bar
|
||||||
|
`)
|
||||||
|
|
||||||
|
// function API definition which will be parsed from the ResourceList.functionConfig
|
||||||
|
// read from stdin
|
||||||
|
type Spec struct {
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
}
|
||||||
|
type ExampleServiceGenerator struct {
|
||||||
|
Spec Spec `yaml:"spec,omitempty"`
|
||||||
|
}
|
||||||
|
functionConfig := &ExampleServiceGenerator{}
|
||||||
|
|
||||||
|
rl := framework.ResourceList{
|
||||||
|
FunctionConfig: functionConfig,
|
||||||
|
Reader: input, // for testing only
|
||||||
|
}
|
||||||
|
if err := rl.Read(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the last generated resource
|
||||||
|
var newNodes []*yaml.RNode
|
||||||
|
for i := range rl.Items {
|
||||||
|
meta, err := rl.Items[i].GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// something we already generated, remove it from the list so we regenerate it
|
||||||
|
if meta.Name == functionConfig.Spec.Name &&
|
||||||
|
meta.Kind == service &&
|
||||||
|
meta.APIVersion == "v1" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newNodes = append(newNodes, rl.Items[i])
|
||||||
|
}
|
||||||
|
rl.Items = newNodes
|
||||||
|
|
||||||
|
// generate the resource again
|
||||||
|
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: %s
|
||||||
|
`, functionConfig.Spec.Name))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rl.Items = append(rl.Items, n)
|
||||||
|
|
||||||
|
if err := rl.Write(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
// kind: ResourceList
|
||||||
|
// items:
|
||||||
|
// - apiVersion: apps/v1
|
||||||
|
// kind: Deployment
|
||||||
|
// metadata:
|
||||||
|
// name: foo
|
||||||
|
// - apiVersion: v1
|
||||||
|
// kind: Service
|
||||||
|
// metadata:
|
||||||
|
// name: bar
|
||||||
|
// functionConfig:
|
||||||
|
// apiVersion: example.com/v1alpha1
|
||||||
|
// kind: ExampleServiceGenerator
|
||||||
|
// spec:
|
||||||
|
// name: bar
|
||||||
|
}
|
||||||
|
|
||||||
// ExampleCommand_generateUpdate generates a resource, updating the previously generated
|
// ExampleCommand_generateUpdate generates a resource, updating the previously generated
|
||||||
// copy rather than replacing it.
|
// copy rather than replacing it.
|
||||||
//
|
//
|
||||||
@@ -191,7 +352,7 @@ func ExampleCommand_generateUpdate() {
|
|||||||
// something we already generated, reconcile it to make sure it matches what
|
// something we already generated, reconcile it to make sure it matches what
|
||||||
// is specified by the functionConfig
|
// is specified by the functionConfig
|
||||||
if meta.Name == functionConfig.Spec.Name &&
|
if meta.Name == functionConfig.Spec.Name &&
|
||||||
meta.Kind == "Service" &&
|
meta.Kind == service &&
|
||||||
meta.APIVersion == "v1" {
|
meta.APIVersion == "v1" {
|
||||||
// set some values
|
// set some values
|
||||||
for k, v := range functionConfig.Spec.Annotations {
|
for k, v := range functionConfig.Spec.Annotations {
|
||||||
@@ -281,7 +442,7 @@ functionConfig:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExampleCommand_validate validates that all Deployment resources have the replicas field set.
|
// ExampleCommand_validate validates that all Deployment resources have the replicas field set.
|
||||||
// If any Deployments do not contain spec.replicas, then the function will return Results
|
// If any Deployments do not contain spec.replicas, then the function will return results
|
||||||
// which will be set on ResourceList.results
|
// which will be set on ResourceList.results
|
||||||
func ExampleCommand_validate() {
|
func ExampleCommand_validate() {
|
||||||
cmd := framework.Command(nil, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
cmd := framework.Command(nil, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
@@ -320,7 +481,7 @@ func ExampleCommand_validate() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// framework will only consider Results an error if it has at least 1 item
|
// framework will only consider results an error if it has at least 1 item
|
||||||
return nodes, framework.Result{
|
return nodes, framework.Result{
|
||||||
Name: "replicas-validator",
|
Name: "replicas-validator",
|
||||||
Items: validationResults,
|
Items: validationResults,
|
||||||
|
|||||||
@@ -5,22 +5,176 @@ package framework
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command provides a cobra.Command for running the function.
|
// ResourceList reads the function input and writes the function output.
|
||||||
//
|
//
|
||||||
// If functionConfig is nil, the function may be configured with flags parsed from
|
// Adheres to the spec: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
|
||||||
// the ResourceList.functionConfig by creating flags on the returned command.
|
type ResourceList struct {
|
||||||
|
// FunctionConfig is the ResourceList.functionConfig input value. If FunctionConfig
|
||||||
|
// is set to a value such as a struct or map[string]interface{} before ResourceList.Read()
|
||||||
|
// is called, then the functionConfig will be parsed into that value.
|
||||||
|
// If it is nil, the functionConfig will be set to a map[string]interface{}
|
||||||
|
// before it is parsed.
|
||||||
|
//
|
||||||
|
// e.g. given the function input:
|
||||||
|
//
|
||||||
|
// kind: ResourceList
|
||||||
|
// functionConfig:
|
||||||
|
// kind: Example
|
||||||
|
// spec:
|
||||||
|
// foo: var
|
||||||
|
//
|
||||||
|
// FunctionConfig will contain the Example unmarshalled into its value.
|
||||||
|
FunctionConfig interface{}
|
||||||
|
|
||||||
|
// Items is the ResourceList.items input and output value. Items will be set by
|
||||||
|
// ResourceList.Read() and written by ResourceList.Write().
|
||||||
|
//
|
||||||
|
// e.g. given the function input:
|
||||||
|
//
|
||||||
|
// kind: ResourceList
|
||||||
|
// items:
|
||||||
|
// - kind: Deployment
|
||||||
|
// ...
|
||||||
|
// - kind: Service
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// Items will be a slice containing the Deployment and Service resources
|
||||||
|
Items []*yaml.RNode
|
||||||
|
|
||||||
|
// Result is ResourceList.result output value. Result will be written by
|
||||||
|
// ResourceList.Write()
|
||||||
|
Result *Result
|
||||||
|
|
||||||
|
// Flags are an optional set of flags to parse the ResourceList.functionConfig.data.
|
||||||
|
// If non-nil, ResourceList.Read() will set the flag value for each flag name matching
|
||||||
|
// a ResourceList.functionConfig.data map entry.
|
||||||
|
//
|
||||||
|
// e.g. given the function input:
|
||||||
|
//
|
||||||
|
// kind: ResourceList
|
||||||
|
// functionConfig:
|
||||||
|
// data:
|
||||||
|
// foo: bar
|
||||||
|
// a: b
|
||||||
|
//
|
||||||
|
// The flags --a=b and --foo=bar will be set in Flags.
|
||||||
|
Flags *pflag.FlagSet
|
||||||
|
|
||||||
|
// Reader is used to read the function input (ResourceList).
|
||||||
|
// Defaults to os.Stdin.
|
||||||
|
Reader io.Reader
|
||||||
|
|
||||||
|
// Writer is used to write the function output (ResourceList)
|
||||||
|
// Defaults to os.Stdout.
|
||||||
|
Writer io.Writer
|
||||||
|
|
||||||
|
// rw reads function input and writes function output
|
||||||
|
rw *kio.ByteReadWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads the ResourceList
|
||||||
|
func (r *ResourceList) Read() error {
|
||||||
|
if r.Reader == nil {
|
||||||
|
r.Reader = os.Stdin
|
||||||
|
}
|
||||||
|
if r.Writer == nil {
|
||||||
|
r.Writer = os.Stdout
|
||||||
|
}
|
||||||
|
r.rw = &kio.ByteReadWriter{
|
||||||
|
Reader: r.Reader,
|
||||||
|
Writer: r.Writer,
|
||||||
|
KeepReaderAnnotations: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
r.Items, err = r.rw.Read()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the functionConfig
|
||||||
|
return func() error {
|
||||||
|
if r.rw.FunctionConfig == nil {
|
||||||
|
// no function config exists
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r.FunctionConfig == nil {
|
||||||
|
// set directly from r.rw
|
||||||
|
r.FunctionConfig = r.rw.FunctionConfig
|
||||||
|
} else {
|
||||||
|
// unmarshal the functionConfig into the provided value
|
||||||
|
err := yaml.Unmarshal([]byte(r.rw.FunctionConfig.MustString()), r.FunctionConfig)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the functionConfig values as flags so they are easy to access
|
||||||
|
if r.Flags == nil || !r.Flags.HasFlags() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// flags are always set from the "data" field
|
||||||
|
data, err := r.rw.FunctionConfig.Pipe(yaml.Lookup("data"))
|
||||||
|
if err != nil || data == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return data.VisitFields(func(node *yaml.MapNode) error {
|
||||||
|
f := r.Flags.Lookup(node.Key.YNode().Value)
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.Value.Set(node.Value.YNode().Value)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the ResourceList
|
||||||
|
func (r *ResourceList) Write() error {
|
||||||
|
// set the ResourceList.results for validating functions
|
||||||
|
if r.Result != nil {
|
||||||
|
if len(r.Result.Items) > 0 {
|
||||||
|
b, err := yaml.Marshal(r.Result)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
y, err := yaml.Parse(string(b))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
r.rw.Results = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the results
|
||||||
|
return r.rw.Write(r.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns a cobra.Command to run a function.
|
||||||
|
//
|
||||||
|
// The cobra.Command will use a ResourceList to Read() the input, run the provided function,
|
||||||
|
// and Write() the output.
|
||||||
|
//
|
||||||
|
// If functionConfig is non-nil, the ResourceList.functionConfig will be unmarshalled into it.
|
||||||
|
//
|
||||||
|
// The returned cobra.Command will have a "gen" subcommand which can be used to generate
|
||||||
|
// a Dockerfile to build the function into a container image
|
||||||
|
//
|
||||||
|
// go run main.go gen DIR/
|
||||||
func Command(functionConfig interface{}, function Function) cobra.Command {
|
func Command(functionConfig interface{}, function Function) cobra.Command {
|
||||||
cmd := cobra.Command{}
|
cmd := cobra.Command{}
|
||||||
addGenerate(&cmd)
|
AddGenerateDockerfile(&cmd)
|
||||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
err := execute(function, functionConfig, cmd)
|
err := execute(function, functionConfig, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,7 +187,9 @@ func Command(functionConfig interface{}, function Function) cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func addGenerate(cmd *cobra.Command) {
|
// AddGenerateDockerfile adds a "gen" subcommand to create a Dockerfile for building
|
||||||
|
// the function as a container.
|
||||||
|
func AddGenerateDockerfile(cmd *cobra.Command) {
|
||||||
gen := &cobra.Command{
|
gen := &cobra.Command{
|
||||||
Use: "gen",
|
Use: "gen",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
@@ -54,89 +210,33 @@ CMD ["function"]
|
|||||||
}
|
}
|
||||||
|
|
||||||
func execute(function Function, functionConfig interface{}, cmd *cobra.Command) error {
|
func execute(function Function, functionConfig interface{}, cmd *cobra.Command) error {
|
||||||
rw := &kio.ByteReadWriter{
|
rl := ResourceList{
|
||||||
Reader: cmd.InOrStdin(),
|
FunctionConfig: functionConfig,
|
||||||
Writer: cmd.OutOrStdout(),
|
Flags: cmd.Flags(),
|
||||||
KeepReaderAnnotations: true,
|
Writer: cmd.OutOrStdout(),
|
||||||
}
|
Reader: cmd.InOrStdin(),
|
||||||
nodes, err := rw.Read()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the functionConfig
|
if err := rl.Read(); err != nil {
|
||||||
if rw.FunctionConfig != nil {
|
return err
|
||||||
if functionConfig == nil {
|
|
||||||
functionConfig = map[string]interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmarshal into the provided structure
|
|
||||||
err := yaml.Unmarshal([]byte(rw.FunctionConfig.MustString()), functionConfig)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the functionConfig values as flags so they are easy to access
|
|
||||||
err = func() error {
|
|
||||||
if !cmd.HasFlags() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// kpt serializes function arguments as a ConfigMap, read them from
|
|
||||||
// the data field.
|
|
||||||
fc, ok := functionConfig.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
// serialized as something else
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if fc["data"] == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
data := fc["data"].(map[string]interface{})
|
|
||||||
// set the value of each flag from the ResourceList.function config input
|
|
||||||
// values
|
|
||||||
for k, v := range data {
|
|
||||||
s, ok := v.(string)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err = cmd.Flag(k).Value.Set(s); err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the function implementation
|
// run the function implementation
|
||||||
nodes, err = function(nodes)
|
var err error
|
||||||
|
rl.Items, err = function(rl.Items)
|
||||||
|
|
||||||
// set the ResourceList.results for validating functions
|
// set the ResourceList.results for validating functions
|
||||||
var result *Result
|
var result *Result
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if val, ok := err.(Result); ok {
|
if val, ok := err.(Result); ok {
|
||||||
if len(val.Items) > 0 {
|
rl.Result = &val
|
||||||
result = &val
|
|
||||||
b, err := yaml.Marshal(val)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
y, err := yaml.Parse(string(b))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
rw.Results = y
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the results
|
if err := rl.Write(); err != nil {
|
||||||
if err := rw.Write(nodes); err != nil {
|
return err
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result != nil && result.ExitCode() != 0 {
|
if result != nil && result.ExitCode() != 0 {
|
||||||
|
|||||||
202
kyaml/fn/runtime/container/container.go
Normal file
202
kyaml/fn/runtime/container/container.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter filters Resources using a container image.
|
||||||
|
// The container must start a process that reads the list of
|
||||||
|
// input Resources from stdin, reads the Configuration from the env
|
||||||
|
// API_CONFIG, and writes the filtered Resources to stdout.
|
||||||
|
// If there is a error or validation failure, the process must exit
|
||||||
|
// non-zero.
|
||||||
|
// The full set of environment variables from the parent process
|
||||||
|
// are passed to the container.
|
||||||
|
//
|
||||||
|
// Function Scoping:
|
||||||
|
// Filter applies the function only to Resources to which it is scoped.
|
||||||
|
//
|
||||||
|
// Resources are scoped to a function if any of the following are true:
|
||||||
|
// - the Resource were read from the same directory as the function config
|
||||||
|
// - the Resource were read from a subdirectory of the function config directory
|
||||||
|
// - the function config is in a directory named "functions" and
|
||||||
|
// they were read from a subdirectory of "functions" parent
|
||||||
|
// - the function config doesn't have a path annotation (considered globally scoped)
|
||||||
|
// - the Filter has GlobalScope == true
|
||||||
|
//
|
||||||
|
// In Scope Examples:
|
||||||
|
//
|
||||||
|
// Example 1: deployment.yaml and service.yaml in function.yaml scope
|
||||||
|
// same directory as the function config directory
|
||||||
|
// .
|
||||||
|
// ├── function.yaml
|
||||||
|
// ├── deployment.yaml
|
||||||
|
// └── service.yaml
|
||||||
|
//
|
||||||
|
// Example 2: apps/deployment.yaml and apps/service.yaml in function.yaml scope
|
||||||
|
// subdirectory of the function config directory
|
||||||
|
// .
|
||||||
|
// ├── function.yaml
|
||||||
|
// └── apps
|
||||||
|
// ├── deployment.yaml
|
||||||
|
// └── service.yaml
|
||||||
|
//
|
||||||
|
// Example 3: apps/deployment.yaml and apps/service.yaml in functions/function.yaml scope
|
||||||
|
// function config is in a directory named "functions"
|
||||||
|
// .
|
||||||
|
// ├── functions
|
||||||
|
// │ └── function.yaml
|
||||||
|
// └── apps
|
||||||
|
// ├── deployment.yaml
|
||||||
|
// └── service.yaml
|
||||||
|
//
|
||||||
|
// Out of Scope Examples:
|
||||||
|
//
|
||||||
|
// Example 1: apps/deployment.yaml and apps/service.yaml NOT in stuff/function.yaml scope
|
||||||
|
// .
|
||||||
|
// ├── stuff
|
||||||
|
// │ └── function.yaml
|
||||||
|
// └── apps
|
||||||
|
// ├── deployment.yaml
|
||||||
|
// └── service.yaml
|
||||||
|
//
|
||||||
|
// Example 2: apps/deployment.yaml and apps/service.yaml NOT in stuff/functions/function.yaml scope
|
||||||
|
// .
|
||||||
|
// ├── stuff
|
||||||
|
// │ └── functions
|
||||||
|
// │ └── function.yaml
|
||||||
|
// └── apps
|
||||||
|
// ├── deployment.yaml
|
||||||
|
// └── service.yaml
|
||||||
|
//
|
||||||
|
// Default Paths:
|
||||||
|
// Resources emitted by functions will have default path applied as annotations
|
||||||
|
// if none is present.
|
||||||
|
// The default path will be the function-dir/ (or parent directory in the case of "functions")
|
||||||
|
// + function-file-name/ + namespace/ + kind_name.yaml
|
||||||
|
//
|
||||||
|
// Example 1: Given a function in fn.yaml that produces a Deployment name foo and a Service named bar
|
||||||
|
// dir
|
||||||
|
// └── fn.yaml
|
||||||
|
//
|
||||||
|
// Would default newly generated Resources to:
|
||||||
|
//
|
||||||
|
// dir
|
||||||
|
// ├── fn.yaml
|
||||||
|
// └── fn
|
||||||
|
// ├── deployment_foo.yaml
|
||||||
|
// └── service_bar.yaml
|
||||||
|
//
|
||||||
|
// Example 2: Given a function in functions/fn.yaml that produces a Deployment name foo and a Service named bar
|
||||||
|
// dir
|
||||||
|
// └── fn.yaml
|
||||||
|
//
|
||||||
|
// Would default newly generated Resources to:
|
||||||
|
//
|
||||||
|
// dir
|
||||||
|
// ├── functions
|
||||||
|
// │ └── fn.yaml
|
||||||
|
// └── fn
|
||||||
|
// ├── deployment_foo.yaml
|
||||||
|
// └── service_bar.yaml
|
||||||
|
//
|
||||||
|
// Example 3: Given a function in fn.yaml that produces a Deployment name foo, namespace baz and a Service named bar namespace baz
|
||||||
|
// dir
|
||||||
|
// └── fn.yaml
|
||||||
|
//
|
||||||
|
// Would default newly generated Resources to:
|
||||||
|
//
|
||||||
|
// dir
|
||||||
|
// ├── fn.yaml
|
||||||
|
// └── fn
|
||||||
|
// └── baz
|
||||||
|
// ├── deployment_foo.yaml
|
||||||
|
// └── service_bar.yaml
|
||||||
|
type Filter struct {
|
||||||
|
|
||||||
|
// Image is the container image to use to create a container.
|
||||||
|
Image string `yaml:"image,omitempty"`
|
||||||
|
|
||||||
|
// Network is the container network to use.
|
||||||
|
Network string `yaml:"network,omitempty"`
|
||||||
|
|
||||||
|
// StorageMounts is a list of storage options that the container will have mounted.
|
||||||
|
StorageMounts []runtimeutil.StorageMount `yaml:"mounts,omitempty"`
|
||||||
|
|
||||||
|
Exec runtimeexec.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Filter) String() string {
|
||||||
|
if c.Exec.DeferFailure {
|
||||||
|
return fmt.Sprintf("%s deferFailure: %v", c.Image, c.Exec.DeferFailure)
|
||||||
|
}
|
||||||
|
return c.Image
|
||||||
|
}
|
||||||
|
func (c Filter) GetExit() error {
|
||||||
|
return c.Exec.GetExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
c.setupExec()
|
||||||
|
return c.Exec.Filter(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Filter) setupExec() {
|
||||||
|
// don't init 2x
|
||||||
|
if c.Exec.Path != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path, args := c.getCommand()
|
||||||
|
c.Exec.Path = path
|
||||||
|
c.Exec.Args = args
|
||||||
|
}
|
||||||
|
|
||||||
|
// getArgs returns the command + args to run to spawn the container
|
||||||
|
func (c *Filter) getCommand() (string, []string) {
|
||||||
|
// run the container using docker. this is simpler than using the docker
|
||||||
|
// libraries, and ensures things like auth work the same as if the container
|
||||||
|
// was run from the cli.
|
||||||
|
|
||||||
|
network := "none"
|
||||||
|
if c.Network != "" {
|
||||||
|
network = c.Network
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"run",
|
||||||
|
"--rm", // delete the container afterward
|
||||||
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
|
||||||
|
"--network", network,
|
||||||
|
|
||||||
|
// added security options
|
||||||
|
"--user", "nobody", // run as nobody
|
||||||
|
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
||||||
|
// note: don't make fs readonly because things like heredoc rely on writing tmp files
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(joncwong): Allow StorageMount fields to have default values.
|
||||||
|
for _, storageMount := range c.StorageMounts {
|
||||||
|
args = append(args, "--mount", storageMount.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("LOG_TO_STDERR", "true")
|
||||||
|
os.Setenv("STRUCTURED_RESULTS", "true")
|
||||||
|
|
||||||
|
// export the local environment vars to the container
|
||||||
|
for _, pair := range os.Environ() {
|
||||||
|
args = append(args, "-e", strings.Split(pair, "=")[0])
|
||||||
|
}
|
||||||
|
a := append(args, c.Image)
|
||||||
|
|
||||||
|
return "docker", a
|
||||||
|
}
|
||||||
203
kyaml/fn/runtime/container/container_test.go
Normal file
203
kyaml/fn/runtime/container/container_test.go
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilter_setupExec(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
functionConfig string
|
||||||
|
expectedArgs []string
|
||||||
|
instance Filter
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "command",
|
||||||
|
functionConfig: `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
expectedArgs: []string{
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
||||||
|
"--network", "none",
|
||||||
|
"--user", "nobody",
|
||||||
|
"--security-opt=no-new-privileges",
|
||||||
|
},
|
||||||
|
instance: Filter{Image: "example.com:version"},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "network",
|
||||||
|
functionConfig: `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
expectedArgs: []string{
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
||||||
|
"--network", "test-1",
|
||||||
|
"--user", "nobody",
|
||||||
|
"--security-opt=no-new-privileges",
|
||||||
|
},
|
||||||
|
instance: Filter{Image: "example.com:version", Network: "test-1"},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "storage_mounts",
|
||||||
|
functionConfig: `apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`,
|
||||||
|
expectedArgs: []string{
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
||||||
|
"--network", "none",
|
||||||
|
"--user", "nobody",
|
||||||
|
"--security-opt=no-new-privileges",
|
||||||
|
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "bind", "/mount/path", "/local/"),
|
||||||
|
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "volume", "myvol", "/local/"),
|
||||||
|
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "tmpfs", "", "/local/"),
|
||||||
|
},
|
||||||
|
instance: Filter{
|
||||||
|
Image: "example.com:version",
|
||||||
|
StorageMounts: []runtimeutil.StorageMount{
|
||||||
|
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
|
||||||
|
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
|
||||||
|
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
tt := tests[i]
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cfg, err := yaml.Parse(tt.functionConfig)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
tt.instance.Exec.FunctionConfig = cfg
|
||||||
|
|
||||||
|
os.Setenv("KYAML_TEST", "FOO")
|
||||||
|
tt.instance.setupExec()
|
||||||
|
|
||||||
|
// configure expected env
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
// the process env
|
||||||
|
tt.expectedArgs = append(tt.expectedArgs, "-e", strings.Split(e, "=")[0])
|
||||||
|
}
|
||||||
|
tt.expectedArgs = append(tt.expectedArgs, tt.instance.Image)
|
||||||
|
|
||||||
|
if !assert.Equal(t, "docker", tt.instance.Exec.Path) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, tt.expectedArgs, tt.instance.Exec.Args) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter_Filter(t *testing.T) {
|
||||||
|
cfg, err := yaml.Parse(`apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
`)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := (&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
`)}).Read()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := Filter{}
|
||||||
|
instance.Exec.FunctionConfig = cfg
|
||||||
|
instance.Exec.Path = "sed"
|
||||||
|
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
|
||||||
|
output, err := instance.Filter(input)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err = kio.ByteWriter{Writer: b, KeepReaderAnnotations: true}.Write(output)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.Equal(t, `apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '1'
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`, b.String()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestFilter_String(t *testing.T) {
|
||||||
|
instance := Filter{Image: "foo"}
|
||||||
|
if !assert.Equal(t, "foo", instance.String()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.Exec.DeferFailure = true
|
||||||
|
if !assert.Equal(t, "foo deferFailure: true", instance.String()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter_ExitCode(t *testing.T) {
|
||||||
|
instance := Filter{}
|
||||||
|
instance.Exec.Path = "/not/real/command"
|
||||||
|
instance.Exec.DeferFailure = true
|
||||||
|
_, err := instance.Filter(nil)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.EqualError(t, instance.GetExit(), "fork/exec /not/real/command: no such file or directory") {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
5
kyaml/fn/runtime/exec/doc.go
Normal file
5
kyaml/fn/runtime/exec/doc.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package exec contains the exec function implementation.
|
||||||
|
package exec
|
||||||
34
kyaml/fn/runtime/exec/exec.go
Normal file
34
kyaml/fn/runtime/exec/exec.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package exec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
// Path is the path to the executable to run
|
||||||
|
Path string `yaml:"path,omitempty"`
|
||||||
|
|
||||||
|
// Args are the arguments to the executable
|
||||||
|
Args []string `yaml:"args,omitempty"`
|
||||||
|
|
||||||
|
runtimeutil.FunctionFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
c.FunctionFilter.Run = c.Run
|
||||||
|
return c.FunctionFilter.Filter(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
|
||||||
|
cmd := exec.Command(c.Path, c.Args...)
|
||||||
|
cmd.Stdin = reader
|
||||||
|
cmd.Stdout = writer
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
112
kyaml/fn/runtime/exec/exec_test.go
Normal file
112
kyaml/fn/runtime/exec/exec_test.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package exec_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFunctionFilter_Filter(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
functionConfig string
|
||||||
|
expectedOutput []string
|
||||||
|
expectedError string
|
||||||
|
instance exec.Filter
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exec_sed",
|
||||||
|
input: []string{
|
||||||
|
`apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo`,
|
||||||
|
`apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
|
||||||
|
`,
|
||||||
|
`apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedError: "",
|
||||||
|
instance: exec.Filter{
|
||||||
|
Path: "sed",
|
||||||
|
Args: []string{"s/Deployment/StatefulSet/g"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
tt := tests[i]
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// initialize the inputs for the FunctionFilter
|
||||||
|
var inputs []*yaml.RNode
|
||||||
|
for i := range tt.input {
|
||||||
|
node, err := yaml.Parse(tt.input[i])
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
inputs = append(inputs, node)
|
||||||
|
}
|
||||||
|
if tt.functionConfig != "" {
|
||||||
|
fc, err := yaml.Parse(tt.functionConfig)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
tt.instance.FunctionConfig = fc
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the function
|
||||||
|
output, err := tt.instance.Filter(inputs)
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
if tt.expectedError != "" {
|
||||||
|
if !assert.EqualError(t, err, tt.expectedError) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify the output
|
||||||
|
var actual []string
|
||||||
|
for i := range output {
|
||||||
|
s, err := output[i].String()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
actual = append(actual, strings.TrimSpace(s))
|
||||||
|
}
|
||||||
|
var expected []string
|
||||||
|
for i := range tt.expectedOutput {
|
||||||
|
expected = append(expected, strings.TrimSpace(tt.expectedOutput[i]))
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, expected, actual) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
kyaml/fn/runtime/runtimeutil/doc.go
Normal file
5
kyaml/fn/runtime/runtimeutil/doc.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package runtimeutil contains libraries for implementing function runtimes.
|
||||||
|
package runtimeutil
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package filters
|
package runtimeutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,10 +30,17 @@ type FunctionSpec struct {
|
|||||||
// Starlark is the spec for running a function as a starlark script
|
// Starlark is the spec for running a function as a starlark script
|
||||||
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
|
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
|
||||||
|
|
||||||
|
// ExecSpec is the spec for running a function as an executable
|
||||||
|
Exec ExecSpec `json:"exec,omitempty" yaml:"exec,omitempty"`
|
||||||
|
|
||||||
// Mounts are the storage or directories to mount into the container
|
// Mounts are the storage or directories to mount into the container
|
||||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExecSpec struct {
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerSpec defines a spec for running a function as a container
|
// ContainerSpec defines a spec for running a function as a container
|
||||||
type ContainerSpec struct {
|
type ContainerSpec struct {
|
||||||
// Image is the container image to run
|
// Image is the container image to run
|
||||||
@@ -72,6 +82,10 @@ type StorageMount struct {
|
|||||||
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
|
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StorageMount) String() string {
|
||||||
|
return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath)
|
||||||
|
}
|
||||||
|
|
||||||
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
|
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
|
||||||
// nil if the resource does not have a FunctionSpec.
|
// nil if the resource does not have a FunctionSpec.
|
||||||
//
|
//
|
||||||
@@ -119,3 +133,52 @@ func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *Funct
|
|||||||
_ = yaml.Unmarshal([]byte(s), &fs)
|
_ = yaml.Unmarshal([]byte(s), &fs)
|
||||||
return &fs
|
return &fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringToStorageMount(s string) StorageMount {
|
||||||
|
m := make(map[string]string)
|
||||||
|
options := strings.Split(s, ",")
|
||||||
|
for _, option := range options {
|
||||||
|
keyVal := strings.SplitN(option, "=", 2)
|
||||||
|
m[keyVal[0]] = keyVal[1]
|
||||||
|
}
|
||||||
|
var sm StorageMount
|
||||||
|
for key, value := range m {
|
||||||
|
switch {
|
||||||
|
case key == "type":
|
||||||
|
sm.MountType = value
|
||||||
|
case key == "src":
|
||||||
|
sm.Src = value
|
||||||
|
case key == "dst":
|
||||||
|
sm.DstPath = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sm
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReconcilerFilter filters Resources based on whether or not they are Reconciler Resource.
|
||||||
|
// Resources with an apiVersion starting with '*.gcr.io', 'gcr.io' or 'docker.io' are considered
|
||||||
|
// Reconciler Resources.
|
||||||
|
type IsReconcilerFilter struct {
|
||||||
|
// ExcludeReconcilers if set to true, then Reconcilers will be excluded -- e.g.
|
||||||
|
// Resources with a reconcile container through the apiVersion (gcr.io prefix) or
|
||||||
|
// through the annotations
|
||||||
|
ExcludeReconcilers bool `yaml:"excludeReconcilers,omitempty"`
|
||||||
|
|
||||||
|
// IncludeNonReconcilers if set to true, the NonReconciler will be included.
|
||||||
|
IncludeNonReconcilers bool `yaml:"includeNonReconcilers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter implements kio.Filter
|
||||||
|
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
var out []*yaml.RNode
|
||||||
|
for i := range inputs {
|
||||||
|
isFnResource := GetFunctionSpec(inputs[i]) != nil
|
||||||
|
if isFnResource && !c.ExcludeReconcilers {
|
||||||
|
out = append(out, inputs[i])
|
||||||
|
}
|
||||||
|
if !isFnResource && c.IncludeNonReconcilers {
|
||||||
|
out = append(out, inputs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
198
kyaml/fn/runtime/runtimeutil/runtimeutil.go
Normal file
198
kyaml/fn/runtime/runtimeutil/runtimeutil.go
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runtimeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FunctionFilter wraps another filter to be invoked in the context of a function.
|
||||||
|
// FunctionFilter manages scoping the function, deferring failures, and saving results
|
||||||
|
// to files.
|
||||||
|
type FunctionFilter struct {
|
||||||
|
// Run implements the function.
|
||||||
|
Run func(reader io.Reader, writer io.Writer) error
|
||||||
|
|
||||||
|
// FunctionConfig is passed to the function through ResourceList.functionConfig.
|
||||||
|
FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty"`
|
||||||
|
|
||||||
|
// GlobalScope explicitly scopes the function to all input resources rather than only those
|
||||||
|
// resources scoped to it by path.
|
||||||
|
GlobalScope bool
|
||||||
|
|
||||||
|
// ResultsFile is the file to write function ResourceList.results to.
|
||||||
|
// If unset, results will not be written.
|
||||||
|
ResultsFile string
|
||||||
|
|
||||||
|
// DeferFailure will cause the Filter to return a nil error even if Run returns an error.
|
||||||
|
// The Run error will be available through GetExit().
|
||||||
|
DeferFailure bool
|
||||||
|
|
||||||
|
// results saves the results emitted from Run
|
||||||
|
results *yaml.RNode
|
||||||
|
|
||||||
|
// exit saves the error returned from Run
|
||||||
|
exit error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExit returns the error from Run
|
||||||
|
func (c FunctionFilter) GetExit() error {
|
||||||
|
return c.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
|
||||||
|
const functionsDirectoryName = "functions"
|
||||||
|
|
||||||
|
// getFunctionScope returns the path of the directory containing the function config,
|
||||||
|
// or its parent directory if the base directory is named "functions"
|
||||||
|
func (c *FunctionFilter) getFunctionScope() (string, error) {
|
||||||
|
m, err := c.FunctionConfig.GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||||
|
if !found {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
functionDir := path.Clean(path.Dir(p))
|
||||||
|
|
||||||
|
if path.Base(functionDir) == functionsDirectoryName {
|
||||||
|
// the scope of functions in a directory called "functions" is 1 level higher
|
||||||
|
// this is similar to how the golang "internal" directory scoping works
|
||||||
|
functionDir = path.Dir(functionDir)
|
||||||
|
}
|
||||||
|
return functionDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scope partitions the input nodes into 2 slices. The first slice contains only Resources
|
||||||
|
// which are scoped under dir, and the second slice contains the Resources which are not.
|
||||||
|
func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
|
||||||
|
// scope container filtered Resources to Resources under that directory
|
||||||
|
var input, saved []*yaml.RNode
|
||||||
|
if c.GlobalScope {
|
||||||
|
return nodes, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// global function
|
||||||
|
if dir == "" || dir == "." {
|
||||||
|
return nodes, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify Resources read from directories under the function configuration
|
||||||
|
for i := range nodes {
|
||||||
|
m, err := nodes[i].GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||||
|
if !found {
|
||||||
|
// this Resource isn't scoped under the function -- don't know where it came from
|
||||||
|
// consider it out of scope
|
||||||
|
saved = append(saved, nodes[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceDir := path.Clean(path.Dir(p))
|
||||||
|
if path.Base(resourceDir) == functionsDirectoryName {
|
||||||
|
// Functions in the `functions` directory are scoped to
|
||||||
|
// themselves, and should see themselves as input
|
||||||
|
resourceDir = path.Dir(resourceDir)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(resourceDir, dir) {
|
||||||
|
// this Resource doesn't fall under the function scope if it
|
||||||
|
// isn't in a subdirectory of where the function lives
|
||||||
|
saved = append(saved, nodes[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// this input is scoped under the function
|
||||||
|
input = append(input, nodes[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return input, saved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
in := &bytes.Buffer{}
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// only process Resources scoped to this function, save the others
|
||||||
|
functionDir, err := c.getFunctionScope()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
input, saved, err := c.scope(functionDir, nodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the input
|
||||||
|
err = kio.ByteWriter{
|
||||||
|
WrappingAPIVersion: kio.ResourceListAPIVersion,
|
||||||
|
WrappingKind: kio.ResourceListKind,
|
||||||
|
Writer: in,
|
||||||
|
KeepReaderAnnotations: true,
|
||||||
|
FunctionConfig: c.FunctionConfig}.Write(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture the command stdout for the return value
|
||||||
|
r := &kio.ByteReader{Reader: out}
|
||||||
|
|
||||||
|
// don't exit immediately if the function fails -- write out the validation
|
||||||
|
c.exit = c.Run(in, out)
|
||||||
|
|
||||||
|
output, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.doResults(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.exit != nil && !c.DeferFailure {
|
||||||
|
return append(output, saved...), c.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
// annotate any generated Resources with a path and index if they don't already have one
|
||||||
|
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit both the Resources output from the function, and the out-of-scope Resources
|
||||||
|
// which were not provided to the function
|
||||||
|
return append(output, saved...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FunctionFilter) doResults(r *kio.ByteReader) error {
|
||||||
|
// Write the results to a file if configured to do so
|
||||||
|
if c.ResultsFile != "" && r.Results != nil {
|
||||||
|
results, err := r.Results.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Results != nil {
|
||||||
|
c.results = r.Results
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
1267
kyaml/fn/runtime/runtimeutil/runtimeutil_test.go
Normal file
1267
kyaml/fn/runtime/runtimeutil/runtimeutil_test.go
Normal file
File diff suppressed because it is too large
Load Diff
8
kyaml/fn/runtime/runtimeutil/types.go
Normal file
8
kyaml/fn/runtime/runtimeutil/types.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runtimeutil
|
||||||
|
|
||||||
|
type DeferFailureFunction interface {
|
||||||
|
GetExit() error
|
||||||
|
}
|
||||||
@@ -60,22 +60,6 @@ func env() (starlark.Value, error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeToValue(node *yaml.RNode) (starlark.Value, error) {
|
|
||||||
s, err := node.String()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
var in map[string]interface{}
|
|
||||||
if err := yaml.Unmarshal([]byte(s), &in); err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
value, err := util.Marshal(in)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func interfaceToValue(i interface{}) (starlark.Value, error) {
|
func interfaceToValue(i interface{}) (starlark.Value, error) {
|
||||||
b, err := json.Marshal(i)
|
b, err := json.Marshal(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -11,8 +11,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/starlark"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ run(ctx.resource_list["items"])
|
|||||||
// name: deployment-1
|
// name: deployment-1
|
||||||
// annotations:
|
// annotations:
|
||||||
// foo: bar
|
// foo: bar
|
||||||
|
// config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||||
// spec:
|
// spec:
|
||||||
// template:
|
// template:
|
||||||
// spec:
|
// spec:
|
||||||
@@ -88,6 +90,7 @@ run(ctx.resource_list["items"])
|
|||||||
// name: deployment-2
|
// name: deployment-2
|
||||||
// annotations:
|
// annotations:
|
||||||
// foo: bar
|
// foo: bar
|
||||||
|
// config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||||
// spec:
|
// spec:
|
||||||
// template:
|
// template:
|
||||||
// spec:
|
// spec:
|
||||||
@@ -141,7 +144,7 @@ def run(items, value):
|
|||||||
|
|
||||||
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["value"])
|
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["value"])
|
||||||
`,
|
`,
|
||||||
FunctionConfig: fc,
|
FunctionFilter: runtimeutil.FunctionFilter{FunctionConfig: fc},
|
||||||
}
|
}
|
||||||
|
|
||||||
// output contains the transformed resources
|
// output contains the transformed resources
|
||||||
@@ -165,6 +168,7 @@ run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["val
|
|||||||
// name: deployment-1
|
// name: deployment-1
|
||||||
// annotations:
|
// annotations:
|
||||||
// foo: hello world
|
// foo: hello world
|
||||||
|
// config.kubernetes.io/path: 'deployment_deployment-1.yaml'
|
||||||
// spec:
|
// spec:
|
||||||
// template:
|
// template:
|
||||||
// spec:
|
// spec:
|
||||||
@@ -178,6 +182,7 @@ run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["val
|
|||||||
// name: deployment-2
|
// name: deployment-2
|
||||||
// annotations:
|
// annotations:
|
||||||
// foo: hello world
|
// foo: hello world
|
||||||
|
// config.kubernetes.io/path: 'deployment_deployment-2.yaml'
|
||||||
// spec:
|
// spec:
|
||||||
// template:
|
// template:
|
||||||
// spec:
|
// spec:
|
||||||
224
kyaml/fn/runtime/starlark/starlark.go
Normal file
224
kyaml/fn/runtime/starlark/starlark.go
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package starlark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/qri-io/starlib/util"
|
||||||
|
"go.starlark.net/starlark"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/comments"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter transforms a set of resources through the provided program
|
||||||
|
type Filter struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Program is a starlark script which will be run against the resources
|
||||||
|
Program string
|
||||||
|
|
||||||
|
// URL is the url of a starlark program to fetch and run
|
||||||
|
URL string
|
||||||
|
|
||||||
|
// Path is the path to a starlark program to read and run
|
||||||
|
Path string
|
||||||
|
|
||||||
|
runtimeutil.FunctionFilter
|
||||||
|
|
||||||
|
ids map[string]*yaml.RNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *Filter) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"name: %v path: %v url: %v program: %v", sf.Name, sf.Path, sf.URL, sf.Program)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
err := sf.setup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sf.FunctionFilter.Run = sf.Run
|
||||||
|
|
||||||
|
return sf.FunctionFilter.Filter(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *Filter) setup() error {
|
||||||
|
if sf.URL != "" && sf.Path != "" ||
|
||||||
|
sf.URL != "" && sf.Program != "" ||
|
||||||
|
sf.Path != "" && sf.Program != "" {
|
||||||
|
return errors.Errorf("Filter Path, Program and URL are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the program from a file
|
||||||
|
if sf.Path != "" {
|
||||||
|
b, err := ioutil.ReadFile(sf.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.Program = string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the program from a URL
|
||||||
|
if sf.URL != "" {
|
||||||
|
err := func() error {
|
||||||
|
resp, err := http.Get(sf.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.Program = string(b)
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *Filter) Run(reader io.Reader, writer io.Writer) error {
|
||||||
|
// retain map of inputs to outputs by id so if the name is changed by the
|
||||||
|
// starlark program, we are able to match the same resources
|
||||||
|
value, err := sf.readResourceList(reader)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the starlark as program as transformation function
|
||||||
|
thread := &starlark.Thread{Name: sf.Name}
|
||||||
|
|
||||||
|
ctx := &Context{resourceList: value}
|
||||||
|
pd, err := ctx.predeclared()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, pd)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sf.writeResourceList(value, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inputToResourceList transforms input into a starlark.Value
|
||||||
|
func (sf *Filter) readResourceList(reader io.Reader) (starlark.Value, error) {
|
||||||
|
// read and parse the inputs
|
||||||
|
rl := bytes.Buffer{}
|
||||||
|
_, err := rl.ReadFrom(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
rn, err := yaml.Parse(rl.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the id on each node to map inputs to outputs
|
||||||
|
var id int
|
||||||
|
sf.ids = map[string]*yaml.RNode{}
|
||||||
|
items, err := rn.Pipe(yaml.Lookup("items"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
err = items.VisitElements(func(node *yaml.RNode) error {
|
||||||
|
id++
|
||||||
|
idStr := fmt.Sprintf("%v", id)
|
||||||
|
sf.ids[idStr] = node
|
||||||
|
return node.PipeE(yaml.SetAnnotation("config.k8s.io/id", idStr))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to a starlark value
|
||||||
|
b, err := yaml.Marshal(rn.Document()) // convert to bytes
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
var in map[string]interface{}
|
||||||
|
err = yaml.Unmarshal(b, &in) // convert to map[string]interface{}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
return util.Marshal(in) // convert to starlark value
|
||||||
|
}
|
||||||
|
|
||||||
|
// resourceListToOutput converts the output of the starlark program to the filter output
|
||||||
|
func (sf *Filter) writeResourceList(value starlark.Value, writer io.Writer) error {
|
||||||
|
// convert the modified resourceList back into a slice of RNodes
|
||||||
|
// by first converting to a map[string]interface{}
|
||||||
|
out, err := util.Unmarshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
b, err := yaml.Marshal(out)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rl, err := yaml.Parse(string(b))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// preserve the comments from the input
|
||||||
|
items, err := rl.Pipe(yaml.Lookup("items"))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
err = items.VisitElements(func(node *yaml.RNode) error {
|
||||||
|
anID, err := node.Pipe(yaml.GetAnnotation("config.k8s.io/id"))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
if anID == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var in *yaml.RNode
|
||||||
|
var found bool
|
||||||
|
if in, found = sf.ids[anID.YNode().Value]; !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := node.PipeE(yaml.ClearAnnotation("config.k8s.io/id")); err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
if err := comments.CopyComments(in, node); err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// starlark will serialize the resources sorting the fields alphabetically,
|
||||||
|
// format them to have a better ordering
|
||||||
|
fmtFltr := filters.FormatFilter{}
|
||||||
|
if _, err := fmtFltr.Filter([]*yaml.RNode{node}); err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := rl.String()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = writer.Write([]byte(s))
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -95,6 +96,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: annotation-value
|
foo: annotation-value
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -133,6 +135,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: Deployment enables declarative updates for Pods and ReplicaSets.
|
foo: Deployment enables declarative updates for Pods and ReplicaSets.
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -174,6 +177,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -213,6 +217,8 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -266,6 +272,7 @@ metadata:
|
|||||||
name: nginx-deployment-1
|
name: nginx-deployment-1
|
||||||
annotations:
|
annotations:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -280,6 +287,7 @@ metadata:
|
|||||||
name: nginx-deployment-2
|
name: nginx-deployment-2
|
||||||
annotations:
|
annotations:
|
||||||
foo: bar
|
foo: bar
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -318,13 +326,10 @@ run(ctx.resource_list["items"])
|
|||||||
expected: `
|
expected: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
|
||||||
name: nginx-deployment-2
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment-1
|
name: nginx-deployment-1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -332,6 +337,13 @@ spec:
|
|||||||
- name: nginx
|
- name: nginx
|
||||||
# head comment
|
# head comment
|
||||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment-2
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment-2.yaml'
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -357,6 +369,8 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment-1
|
name: nginx-deployment-1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment-1.yaml'
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -395,6 +409,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: hello world
|
foo: hello world
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -406,7 +421,7 @@ spec:
|
|||||||
expectedFunctionConfig: `
|
expectedFunctionConfig: `
|
||||||
kind: Script
|
kind: Script
|
||||||
spec:
|
spec:
|
||||||
value: hello world
|
value: "hello world"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -447,6 +462,7 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
annotations:
|
annotations:
|
||||||
foo: hello world
|
foo: hello world
|
||||||
|
config.kubernetes.io/path: 'deployment_nginx-deployment.yaml'
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
@@ -458,7 +474,7 @@ spec:
|
|||||||
expectedFunctionConfig: `
|
expectedFunctionConfig: `
|
||||||
kind: Script
|
kind: Script
|
||||||
spec:
|
spec:
|
||||||
value: updated
|
value: "hello world"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ require (
|
|||||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d
|
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d
|
||||||
github.com/sergi/go-diff v1.1.0
|
github.com/sergi/go-diff v1.1.0
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
|
github.com/spf13/pflag v1.0.3
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
|
||||||
|
|||||||
@@ -1,450 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerFilter filters Resources using a container image.
|
|
||||||
// The container must start a process that reads the list of
|
|
||||||
// input Resources from stdin, reads the Configuration from the env
|
|
||||||
// API_CONFIG, and writes the filtered Resources to stdout.
|
|
||||||
// If there is a error or validation failure, the process must exit
|
|
||||||
// non-zero.
|
|
||||||
// The full set of environment variables from the parent process
|
|
||||||
// are passed to the container.
|
|
||||||
//
|
|
||||||
// Function Scoping:
|
|
||||||
// ContainerFilter applies the function only to Resources to which it is scoped.
|
|
||||||
//
|
|
||||||
// Resources are scoped to a function if any of the following are true:
|
|
||||||
// - the Resource were read from the same directory as the function config
|
|
||||||
// - the Resource were read from a subdirectory of the function config directory
|
|
||||||
// - the function config is in a directory named "functions" and
|
|
||||||
// they were read from a subdirectory of "functions" parent
|
|
||||||
// - the function config doesn't have a path annotation (considered globally scoped)
|
|
||||||
// - the ContainerFilter has GlobalScope == true
|
|
||||||
//
|
|
||||||
// In Scope Examples:
|
|
||||||
//
|
|
||||||
// Example 1: deployment.yaml and service.yaml in function.yaml scope
|
|
||||||
// same directory as the function config directory
|
|
||||||
// .
|
|
||||||
// ├── function.yaml
|
|
||||||
// ├── deployment.yaml
|
|
||||||
// └── service.yaml
|
|
||||||
//
|
|
||||||
// Example 2: apps/deployment.yaml and apps/service.yaml in function.yaml scope
|
|
||||||
// subdirectory of the function config directory
|
|
||||||
// .
|
|
||||||
// ├── function.yaml
|
|
||||||
// └── apps
|
|
||||||
// ├── deployment.yaml
|
|
||||||
// └── service.yaml
|
|
||||||
//
|
|
||||||
// Example 3: apps/deployment.yaml and apps/service.yaml in functions/function.yaml scope
|
|
||||||
// function config is in a directory named "functions"
|
|
||||||
// .
|
|
||||||
// ├── functions
|
|
||||||
// │ └── function.yaml
|
|
||||||
// └── apps
|
|
||||||
// ├── deployment.yaml
|
|
||||||
// └── service.yaml
|
|
||||||
//
|
|
||||||
// Out of Scope Examples:
|
|
||||||
//
|
|
||||||
// Example 1: apps/deployment.yaml and apps/service.yaml NOT in stuff/function.yaml scope
|
|
||||||
// .
|
|
||||||
// ├── stuff
|
|
||||||
// │ └── function.yaml
|
|
||||||
// └── apps
|
|
||||||
// ├── deployment.yaml
|
|
||||||
// └── service.yaml
|
|
||||||
//
|
|
||||||
// Example 2: apps/deployment.yaml and apps/service.yaml NOT in stuff/functions/function.yaml scope
|
|
||||||
// .
|
|
||||||
// ├── stuff
|
|
||||||
// │ └── functions
|
|
||||||
// │ └── function.yaml
|
|
||||||
// └── apps
|
|
||||||
// ├── deployment.yaml
|
|
||||||
// └── service.yaml
|
|
||||||
//
|
|
||||||
// Default Paths:
|
|
||||||
// Resources emitted by functions will have default path applied as annotations
|
|
||||||
// if none is present.
|
|
||||||
// The default path will be the function-dir/ (or parent directory in the case of "functions")
|
|
||||||
// + function-file-name/ + namespace/ + kind_name.yaml
|
|
||||||
//
|
|
||||||
// Example 1: Given a function in fn.yaml that produces a Deployment name foo and a Service named bar
|
|
||||||
// dir
|
|
||||||
// └── fn.yaml
|
|
||||||
//
|
|
||||||
// Would default newly generated Resources to:
|
|
||||||
//
|
|
||||||
// dir
|
|
||||||
// ├── fn.yaml
|
|
||||||
// └── fn
|
|
||||||
// ├── deployment_foo.yaml
|
|
||||||
// └── service_bar.yaml
|
|
||||||
//
|
|
||||||
// Example 2: Given a function in functions/fn.yaml that produces a Deployment name foo and a Service named bar
|
|
||||||
// dir
|
|
||||||
// └── fn.yaml
|
|
||||||
//
|
|
||||||
// Would default newly generated Resources to:
|
|
||||||
//
|
|
||||||
// dir
|
|
||||||
// ├── functions
|
|
||||||
// │ └── fn.yaml
|
|
||||||
// └── fn
|
|
||||||
// ├── deployment_foo.yaml
|
|
||||||
// └── service_bar.yaml
|
|
||||||
//
|
|
||||||
// Example 3: Given a function in fn.yaml that produces a Deployment name foo, namespace baz and a Service named bar namespace baz
|
|
||||||
// dir
|
|
||||||
// └── fn.yaml
|
|
||||||
//
|
|
||||||
// Would default newly generated Resources to:
|
|
||||||
//
|
|
||||||
// dir
|
|
||||||
// ├── fn.yaml
|
|
||||||
// └── fn
|
|
||||||
// └── baz
|
|
||||||
// ├── deployment_foo.yaml
|
|
||||||
// └── service_bar.yaml
|
|
||||||
type ContainerFilter struct {
|
|
||||||
|
|
||||||
// Image is the container image to use to create a container.
|
|
||||||
Image string `yaml:"image,omitempty"`
|
|
||||||
|
|
||||||
// Network is the container network to use.
|
|
||||||
Network string `yaml:"network,omitempty"`
|
|
||||||
|
|
||||||
// StorageMounts is a list of storage options that the container will have mounted.
|
|
||||||
StorageMounts []StorageMount `yaml:"mounts,omitempty"`
|
|
||||||
|
|
||||||
// Config is the API configuration for the container and passed through the
|
|
||||||
// API_CONFIG env var to the container.
|
|
||||||
// Typically a Kubernetes style Resource Config.
|
|
||||||
Config *yaml.RNode `yaml:"config,omitempty"`
|
|
||||||
|
|
||||||
// GlobalScope will cause the function to be run against all input
|
|
||||||
// nodes instead of only nodes scoped under the function.
|
|
||||||
GlobalScope bool
|
|
||||||
|
|
||||||
ResultsFile string
|
|
||||||
|
|
||||||
Results *yaml.RNode
|
|
||||||
|
|
||||||
DeferFailure bool
|
|
||||||
|
|
||||||
Exit error
|
|
||||||
|
|
||||||
// SetFlowStyleForConfig sets the style for config to Flow when serializing it
|
|
||||||
SetFlowStyleForConfig bool
|
|
||||||
|
|
||||||
// args may be specified by tests to override how a container is spawned
|
|
||||||
args []string
|
|
||||||
|
|
||||||
checkInput func(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c ContainerFilter) GetExit() error {
|
|
||||||
return c.Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeferFailureFunction interface {
|
|
||||||
GetExit() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c ContainerFilter) String() string {
|
|
||||||
if c.DeferFailure {
|
|
||||||
return fmt.Sprintf("%s deferFailure: %v", c.Image, c.DeferFailure)
|
|
||||||
}
|
|
||||||
return c.Image
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StorageMount) String() string {
|
|
||||||
return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringToStorageMount(s string) StorageMount {
|
|
||||||
m := make(map[string]string)
|
|
||||||
options := strings.Split(s, ",")
|
|
||||||
for _, option := range options {
|
|
||||||
keyVal := strings.SplitN(option, "=", 2)
|
|
||||||
m[keyVal[0]] = keyVal[1]
|
|
||||||
}
|
|
||||||
var sm StorageMount
|
|
||||||
for key, value := range m {
|
|
||||||
switch {
|
|
||||||
case key == "type":
|
|
||||||
sm.MountType = value
|
|
||||||
case key == "src":
|
|
||||||
sm.Src = value
|
|
||||||
case key == "dst":
|
|
||||||
sm.DstPath = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sm
|
|
||||||
}
|
|
||||||
|
|
||||||
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
|
|
||||||
const functionsDirectoryName = "functions"
|
|
||||||
|
|
||||||
// getFunctionScope returns the path of the directory containing the function config,
|
|
||||||
// or its parent directory if the base directory is named "functions"
|
|
||||||
func (c *ContainerFilter) getFunctionScope() (string, error) {
|
|
||||||
m, err := c.Config.GetMeta()
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrap(err)
|
|
||||||
}
|
|
||||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
|
||||||
if !found {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
functionDir := path.Clean(path.Dir(p))
|
|
||||||
|
|
||||||
if path.Base(functionDir) == functionsDirectoryName {
|
|
||||||
// the scope of functions in a directory called "functions" is 1 level higher
|
|
||||||
// this is similar to how the golang "internal" directory scoping works
|
|
||||||
functionDir = path.Dir(functionDir)
|
|
||||||
}
|
|
||||||
return functionDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// scope partitions the input nodes into 2 slices. The first slice contains only Resources
|
|
||||||
// which are scoped under dir, and the second slice contains the Resources which are not.
|
|
||||||
func (c *ContainerFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
|
|
||||||
// scope container filtered Resources to Resources under that directory
|
|
||||||
var input, saved []*yaml.RNode
|
|
||||||
if c.GlobalScope {
|
|
||||||
return nodes, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// global function
|
|
||||||
if dir == "" || dir == "." {
|
|
||||||
return nodes, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// identify Resources read from directories under the function configuration
|
|
||||||
for i := range nodes {
|
|
||||||
m, err := nodes[i].GetMeta()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
|
||||||
if !found {
|
|
||||||
// this Resource isn't scoped under the function -- don't know where it came from
|
|
||||||
// consider it out of scope
|
|
||||||
saved = append(saved, nodes[i])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceDir := path.Clean(path.Dir(p))
|
|
||||||
if path.Base(resourceDir) == functionsDirectoryName {
|
|
||||||
// Functions in the `functions` directory are scoped to
|
|
||||||
// themselves, and should see themselves as input
|
|
||||||
resourceDir = path.Dir(resourceDir)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(resourceDir, dir) {
|
|
||||||
// this Resource doesn't fall under the function scope if it
|
|
||||||
// isn't in a subdirectory of where the function lives
|
|
||||||
saved = append(saved, nodes[i])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// this input is scoped under the function
|
|
||||||
input = append(input, nodes[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return input, saved, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GrepFilter implements kio.GrepFilter
|
|
||||||
func (c *ContainerFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
||||||
// get the command to filter the Resources
|
|
||||||
cmd := c.getCommand()
|
|
||||||
|
|
||||||
in := &bytes.Buffer{}
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
|
|
||||||
// only process Resources scoped to this function, save the others
|
|
||||||
functionDir, err := c.getFunctionScope()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
input, saved, err := c.scope(functionDir, nodes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the input
|
|
||||||
err = kio.ByteWriter{
|
|
||||||
WrappingAPIVersion: kio.ResourceListAPIVersion,
|
|
||||||
WrappingKind: kio.ResourceListKind,
|
|
||||||
Writer: in,
|
|
||||||
KeepReaderAnnotations: true,
|
|
||||||
FunctionConfig: c.Config}.Write(input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// capture the command stdout for the return value
|
|
||||||
r := &kio.ByteReader{Reader: out}
|
|
||||||
|
|
||||||
// do the filtering
|
|
||||||
if c.checkInput != nil {
|
|
||||||
c.checkInput(in.String())
|
|
||||||
}
|
|
||||||
cmd.Stdin = in
|
|
||||||
cmd.Stdout = out
|
|
||||||
|
|
||||||
// don't exit immediately if the function fails -- write out the validation
|
|
||||||
c.Exit = cmd.Run()
|
|
||||||
|
|
||||||
output, err := r.Read()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.doResults(r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Exit != nil && !c.DeferFailure {
|
|
||||||
return append(output, saved...), c.Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
// annotate any generated Resources with a path and index if they don't already have one
|
|
||||||
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// emit both the Resources output from the function, and the out-of-scope Resources
|
|
||||||
// which were not provided to the function
|
|
||||||
return append(output, saved...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ContainerFilter) doResults(r *kio.ByteReader) error {
|
|
||||||
// Write the results to a file if configured to do so
|
|
||||||
if c.ResultsFile != "" && r.Results != nil {
|
|
||||||
results, err := r.Results.String()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Results != nil {
|
|
||||||
c.Results = r.Results
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getArgs returns the command + args to run to spawn the container
|
|
||||||
func (c *ContainerFilter) getArgs() []string {
|
|
||||||
// run the container using docker. this is simpler than using the docker
|
|
||||||
// libraries, and ensures things like auth work the same as if the container
|
|
||||||
// was run from the cli.
|
|
||||||
|
|
||||||
network := "none"
|
|
||||||
if c.Network != "" {
|
|
||||||
network = c.Network
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"docker", "run",
|
|
||||||
"--rm", // delete the container afterward
|
|
||||||
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
|
|
||||||
|
|
||||||
// added security options
|
|
||||||
"--network", network,
|
|
||||||
"--user", "nobody", // run as nobody
|
|
||||||
// don't make fs readonly because things like heredoc rely on writing tmp files
|
|
||||||
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(joncwong): Allow StorageMount fields to have default values.
|
|
||||||
for _, storageMount := range c.StorageMounts {
|
|
||||||
args = append(args, "--mount", storageMount.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// tell functions to write error messages to stderr as well as results
|
|
||||||
os.Setenv("LOG_TO_STDERR", "true")
|
|
||||||
os.Setenv("STRUCTURED_RESULTS", "true")
|
|
||||||
|
|
||||||
// export the local environment vars to the container
|
|
||||||
for _, pair := range os.Environ() {
|
|
||||||
tokens := strings.Split(pair, "=")
|
|
||||||
if tokens[0] == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
args = append(args, "-e", tokens[0])
|
|
||||||
}
|
|
||||||
return append(args, c.Image)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCommand returns a command which will apply the Filter using the container image
|
|
||||||
func (c *ContainerFilter) getCommand() *exec.Cmd {
|
|
||||||
if c.SetFlowStyleForConfig {
|
|
||||||
c.Config.YNode().Style = yaml.FlowStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.args) == 0 {
|
|
||||||
c.args = c.getArgs()
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(c.args[0], c.args[1:]...)
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
|
|
||||||
// set stderr for err messaging
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsReconcilerFilter filters Resources based on whether or not they are Reconciler Resource.
|
|
||||||
// Resources with an apiVersion starting with '*.gcr.io', 'gcr.io' or 'docker.io' are considered
|
|
||||||
// Reconciler Resources.
|
|
||||||
type IsReconcilerFilter struct {
|
|
||||||
// ExcludeReconcilers if set to true, then Reconcilers will be excluded -- e.g.
|
|
||||||
// Resources with a reconcile container through the apiVersion (gcr.io prefix) or
|
|
||||||
// through the annotations
|
|
||||||
ExcludeReconcilers bool `yaml:"excludeReconcilers,omitempty"`
|
|
||||||
|
|
||||||
// IncludeNonReconcilers if set to true, the NonReconciler will be included.
|
|
||||||
IncludeNonReconcilers bool `yaml:"includeNonReconcilers,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter implements kio.Filter
|
|
||||||
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
||||||
var out []*yaml.RNode
|
|
||||||
for i := range inputs {
|
|
||||||
isFnResource := GetFunctionSpec(inputs[i]) != nil
|
|
||||||
if isFnResource && !c.ExcludeReconcilers {
|
|
||||||
out = append(out, inputs[i])
|
|
||||||
}
|
|
||||||
if !isFnResource && c.IncludeNonReconcilers {
|
|
||||||
out = append(out, inputs[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,17 +14,19 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/starlark"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunFns runs the set of configuration functions in a local directory against
|
// RunFns runs the set of configuration functions in a local directory against
|
||||||
// the Resources in that directory
|
// the Resources in that directory
|
||||||
type RunFns struct {
|
type RunFns struct {
|
||||||
StorageMounts []filters.StorageMount
|
StorageMounts []runtimeutil.StorageMount
|
||||||
|
|
||||||
// Path is the path to the directory containing functions
|
// Path is the path to the directory containing functions
|
||||||
Path string
|
Path string
|
||||||
@@ -63,6 +65,9 @@ type RunFns struct {
|
|||||||
// EnableStarlark will enable functions run as starlark scripts
|
// EnableStarlark will enable functions run as starlark scripts
|
||||||
EnableStarlark bool
|
EnableStarlark bool
|
||||||
|
|
||||||
|
// EnableExec will enable exec functions
|
||||||
|
EnableExec bool
|
||||||
|
|
||||||
// DisableContainers will disable functions run as containers
|
// DisableContainers will disable functions run as containers
|
||||||
DisableContainers bool
|
DisableContainers bool
|
||||||
|
|
||||||
@@ -75,7 +80,7 @@ type RunFns struct {
|
|||||||
// functionFilterProvider provides a filter to perform the function.
|
// functionFilterProvider provides a filter to perform the function.
|
||||||
// this is a variable so it can be mocked in tests
|
// this is a variable so it can be mocked in tests
|
||||||
functionFilterProvider func(
|
functionFilterProvider func(
|
||||||
filter filters.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
|
filter runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs the command
|
// Execute runs the command
|
||||||
@@ -173,7 +178,7 @@ func (r RunFns) runFunctions(
|
|||||||
// check for deferred function errors
|
// check for deferred function errors
|
||||||
var errs []string
|
var errs []string
|
||||||
for i := range fltrs {
|
for i := range fltrs {
|
||||||
cf, ok := fltrs[i].(filters.DeferFailureFunction)
|
cf, ok := fltrs[i].(runtimeutil.DeferFailureFunction)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -196,7 +201,7 @@ func (r RunFns) getFunctionsFromInput(nodes []*yaml.RNode) ([]kio.Filter, error)
|
|||||||
buff := &kio.PackageBuffer{}
|
buff := &kio.PackageBuffer{}
|
||||||
err := kio.Pipeline{
|
err := kio.Pipeline{
|
||||||
Inputs: []kio.Reader{&kio.PackageBuffer{Nodes: nodes}},
|
Inputs: []kio.Reader{&kio.PackageBuffer{Nodes: nodes}},
|
||||||
Filters: []kio.Filter{&filters.IsReconcilerFilter{}},
|
Filters: []kio.Filter{&runtimeutil.IsReconcilerFilter{}},
|
||||||
Outputs: []kio.Writer{buff},
|
Outputs: []kio.Writer{buff},
|
||||||
}.Execute()
|
}.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -235,7 +240,7 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
|
|||||||
var fltrs []kio.Filter
|
var fltrs []kio.Filter
|
||||||
for i := range fns {
|
for i := range fns {
|
||||||
api := fns[i]
|
api := fns[i]
|
||||||
spec := filters.GetFunctionSpec(api)
|
spec := runtimeutil.GetFunctionSpec(api)
|
||||||
if spec.Container.Network.Required {
|
if spec.Container.Network.Required {
|
||||||
if !r.Network {
|
if !r.Network {
|
||||||
// TODO(eddiezane): Provide error info about which function needs the network
|
// TODO(eddiezane): Provide error info about which function needs the network
|
||||||
@@ -251,9 +256,9 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
|
|||||||
if c == nil {
|
if c == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cf, ok := c.(*filters.ContainerFilter)
|
cf, ok := c.(*container.Filter)
|
||||||
if global && ok {
|
if global && ok {
|
||||||
cf.GlobalScope = true
|
cf.Exec.GlobalScope = true
|
||||||
}
|
}
|
||||||
fltrs = append(fltrs, c)
|
fltrs = append(fltrs, c)
|
||||||
}
|
}
|
||||||
@@ -331,25 +336,25 @@ func (r *RunFns) init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ffp provides function filters
|
// ffp provides function filters
|
||||||
func (r *RunFns) ffp(spec filters.FunctionSpec, api *yaml.RNode) (kio.Filter, error) {
|
func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter, error) {
|
||||||
|
var resultsFile string
|
||||||
|
if r.ResultsDir != "" {
|
||||||
|
resultsFile = filepath.Join(r.ResultsDir, fmt.Sprintf(
|
||||||
|
"results-%v.yaml", r.resultsCount))
|
||||||
|
atomic.AddUint32(&r.resultsCount, 1)
|
||||||
|
}
|
||||||
if !r.DisableContainers && spec.Container.Image != "" {
|
if !r.DisableContainers && spec.Container.Image != "" {
|
||||||
var resultsFile string
|
|
||||||
// TODO: Add a test for this behavior
|
// TODO: Add a test for this behavior
|
||||||
|
cf := &container.Filter{
|
||||||
if r.ResultsDir != "" {
|
|
||||||
resultsFile = filepath.Join(r.ResultsDir, fmt.Sprintf(
|
|
||||||
"results-%v.yaml", r.resultsCount))
|
|
||||||
atomic.AddUint32(&r.resultsCount, 1)
|
|
||||||
}
|
|
||||||
return &filters.ContainerFilter{
|
|
||||||
Image: spec.Container.Image,
|
Image: spec.Container.Image,
|
||||||
Config: api,
|
|
||||||
Network: spec.Network,
|
Network: spec.Network,
|
||||||
StorageMounts: r.StorageMounts,
|
StorageMounts: r.StorageMounts,
|
||||||
GlobalScope: r.GlobalScope,
|
}
|
||||||
ResultsFile: resultsFile,
|
cf.Exec.FunctionConfig = api
|
||||||
DeferFailure: spec.DeferFailure,
|
cf.Exec.GlobalScope = r.GlobalScope
|
||||||
}, nil
|
cf.Exec.ResultsFile = resultsFile
|
||||||
|
cf.Exec.DeferFailure = spec.DeferFailure
|
||||||
|
return cf, nil
|
||||||
}
|
}
|
||||||
if r.EnableStarlark && spec.Starlark.Path != "" {
|
if r.EnableStarlark && spec.Starlark.Path != "" {
|
||||||
// the script path is relative to the function config file
|
// the script path is relative to the function config file
|
||||||
@@ -369,11 +374,24 @@ func (r *RunFns) ffp(spec filters.FunctionSpec, api *yaml.RNode) (kio.Filter, er
|
|||||||
}
|
}
|
||||||
p = path.Join(r.Path, path.Dir(p), spec.Starlark.Path)
|
p = path.Join(r.Path, path.Dir(p), spec.Starlark.Path)
|
||||||
|
|
||||||
return &starlark.Filter{
|
sf := &starlark.Filter{Name: spec.Starlark.Name, Path: p}
|
||||||
Name: spec.Starlark.Name,
|
|
||||||
Path: p,
|
sf.FunctionConfig = api
|
||||||
FunctionConfig: api,
|
sf.GlobalScope = r.GlobalScope
|
||||||
}, nil
|
sf.ResultsFile = resultsFile
|
||||||
|
sf.DeferFailure = spec.DeferFailure
|
||||||
|
return sf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.EnableExec && spec.Exec.Path != "" {
|
||||||
|
ef := &exec.Filter{Path: spec.Exec.Path}
|
||||||
|
|
||||||
|
ef.FunctionConfig = api
|
||||||
|
ef.GlobalScope = r.GlobalScope
|
||||||
|
ef.ResultsFile = resultsFile
|
||||||
|
ef.DeferFailure = spec.DeferFailure
|
||||||
|
return ef, nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
@@ -48,8 +50,8 @@ func TestRunFns_init(t *testing.T) {
|
|||||||
api, err := yaml.Parse(`apiVersion: apps/v1
|
api, err := yaml.Parse(`apiVersion: apps/v1
|
||||||
kind:
|
kind:
|
||||||
`)
|
`)
|
||||||
spec := filters.FunctionSpec{
|
spec := runtimeutil.FunctionSpec{
|
||||||
Container: filters.ContainerSpec{
|
Container: runtimeutil.ContainerSpec{
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -57,7 +59,9 @@ kind:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
filter, _ := instance.functionFilterProvider(spec, api)
|
filter, _ := instance.functionFilterProvider(spec, api)
|
||||||
assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter)
|
cf := &container.Filter{Image: "example.com:version"}
|
||||||
|
cf.Exec.FunctionConfig = api
|
||||||
|
assert.Equal(t, cf, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunFns_Execute__initGlobalScope(t *testing.T) {
|
func TestRunFns_Execute__initGlobalScope(t *testing.T) {
|
||||||
@@ -76,8 +80,8 @@ kind:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
spec := filters.FunctionSpec{
|
spec := runtimeutil.FunctionSpec{
|
||||||
Container: filters.ContainerSpec{
|
Container: runtimeutil.ContainerSpec{
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -85,8 +89,10 @@ kind:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
filter, _ := instance.functionFilterProvider(spec, api)
|
filter, _ := instance.functionFilterProvider(spec, api)
|
||||||
assert.Equal(t, &filters.ContainerFilter{
|
cf := &container.Filter{Image: "example.com:version"}
|
||||||
Image: "example.com:version", Config: api, GlobalScope: true}, filter)
|
cf.Exec.FunctionConfig = api
|
||||||
|
cf.Exec.GlobalScope = true
|
||||||
|
assert.Equal(t, cf, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunFns_Execute__initDefault(t *testing.T) {
|
func TestRunFns_Execute__initDefault(t *testing.T) {
|
||||||
@@ -143,12 +149,12 @@ func TestRunFns_Execute__initDefault(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "explicit directories in mounts",
|
name: "explicit directories in mounts",
|
||||||
instance: RunFns{StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}}},
|
instance: RunFns{StorageMounts: []runtimeutil.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}}},
|
||||||
expected: RunFns{
|
expected: RunFns{
|
||||||
Output: os.Stdout,
|
Output: os.Stdout,
|
||||||
Input: os.Stdin,
|
Input: os.Stdin,
|
||||||
NoFunctionsFromInput: getFalse(),
|
NoFunctionsFromInput: getFalse(),
|
||||||
StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}},
|
StorageMounts: []runtimeutil.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -751,7 +757,7 @@ replace: StatefulSet
|
|||||||
var fltrs []*TestFilter
|
var fltrs []*TestFilter
|
||||||
instance := RunFns{
|
instance := RunFns{
|
||||||
Path: dir,
|
Path: dir,
|
||||||
functionFilterProvider: func(f filters.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
|
functionFilterProvider: func(f runtimeutil.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
|
||||||
tf := &TestFilter{
|
tf := &TestFilter{
|
||||||
Exit: errors.Errorf("message: %s", f.Container.Image),
|
Exit: errors.Errorf("message: %s", f.Container.Image),
|
||||||
}
|
}
|
||||||
@@ -927,8 +933,8 @@ func setupTest(t *testing.T) string {
|
|||||||
// getFilterProvider fakes the creation of a filter, replacing the ContainerFiler with
|
// getFilterProvider fakes the creation of a filter, replacing the ContainerFiler with
|
||||||
// a filter to s/kind: Deployment/kind: StatefulSet/g.
|
// a filter to s/kind: Deployment/kind: StatefulSet/g.
|
||||||
// this can be used to simulate running a filter.
|
// this can be used to simulate running a filter.
|
||||||
func getFilterProvider(t *testing.T) func(filters.FunctionSpec, *yaml.RNode) (kio.Filter, error) {
|
func getFilterProvider(t *testing.T) func(runtimeutil.FunctionSpec, *yaml.RNode) (kio.Filter, error) {
|
||||||
return func(f filters.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
|
return func(f runtimeutil.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
|
||||||
// parse the filter from the input
|
// parse the filter from the input
|
||||||
filter := yaml.YFilter{}
|
filter := yaml.YFilter{}
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
|
|||||||
@@ -1,251 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package starlark
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/qri-io/starlib/util"
|
|
||||||
"go.starlark.net/starlark"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/comments"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Filter transforms a set of resources through the provided program
|
|
||||||
type Filter struct {
|
|
||||||
Name string
|
|
||||||
|
|
||||||
// Program is a starlark script which will be run against the resources
|
|
||||||
Program string
|
|
||||||
|
|
||||||
// URL is the url of a starlark program to fetch and run
|
|
||||||
URL string
|
|
||||||
|
|
||||||
// Path is the path to a starlark program to read and run
|
|
||||||
Path string
|
|
||||||
|
|
||||||
// FunctionConfig is the value to be provided for resourceList.functionConfig as specified by
|
|
||||||
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md.
|
|
||||||
FunctionConfig *yaml.RNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *Filter) String() string {
|
|
||||||
return fmt.Sprintf("name: %v path: %v url: %v program: %v", sf.Name, sf.Path, sf.URL, sf.Program)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *Filter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
||||||
if sf.URL != "" && sf.Path != "" ||
|
|
||||||
sf.URL != "" && sf.Program != "" ||
|
|
||||||
sf.Path != "" && sf.Program != "" {
|
|
||||||
return nil, errors.Errorf("Filter Path, Program and URL are mutually exclusive")
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the program from a file
|
|
||||||
if sf.Path != "" {
|
|
||||||
b, err := ioutil.ReadFile(sf.Path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sf.Program = string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the program from a URL
|
|
||||||
if sf.URL != "" {
|
|
||||||
err := func() error {
|
|
||||||
resp, err := http.Get(sf.URL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sf.Program = string(b)
|
|
||||||
return nil
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retain map of inputs to outputs by id so if the name is changed by the
|
|
||||||
// starlark program, we are able to match the same resources
|
|
||||||
value, ids, err := sf.inputToResourceList(input)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the starlark as program as transformation function
|
|
||||||
thread := &starlark.Thread{Name: sf.Name}
|
|
||||||
|
|
||||||
ctx := &Context{
|
|
||||||
resourceList: value,
|
|
||||||
}
|
|
||||||
pd, err := ctx.predeclared()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, pd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := sf.resourceListToOutput(value, ids)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// starlark will serialize the resources sorting the fields alphabetically,
|
|
||||||
// format them to have a better ordering
|
|
||||||
return filters.FormatFilter{}.Filter(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tuple maps an input resource to the output resource
|
|
||||||
type tuple struct {
|
|
||||||
// in is the RNode provided to the starlark program
|
|
||||||
in *yaml.RNode
|
|
||||||
// out is the RNode emitted by the starlark program with the id matching in
|
|
||||||
out *yaml.RNode
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputToResourceList transforms input into a starlark.Value
|
|
||||||
func (sf *Filter) inputToResourceList(
|
|
||||||
input []*yaml.RNode) (starlark.Value, map[int]*tuple, error) {
|
|
||||||
var id int
|
|
||||||
ids := map[int]*tuple{}
|
|
||||||
|
|
||||||
// convert into a ResourceList which will be converted to a starlark dictionary
|
|
||||||
// create the ResourceList
|
|
||||||
resourceList, err := yaml.Parse(`kind: ResourceList`)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the functionConfig
|
|
||||||
if sf.FunctionConfig != nil {
|
|
||||||
if err := resourceList.PipeE(
|
|
||||||
yaml.FieldSetter{Name: "functionConfig", Value: sf.FunctionConfig}); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the inputs should be provided as the list "items"
|
|
||||||
items, err := resourceList.Pipe(yaml.LookupCreate(yaml.SequenceNode, "items"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
// add the input as items, give each resource an id
|
|
||||||
for i := range input {
|
|
||||||
item := input[i]
|
|
||||||
|
|
||||||
// create an id for tracking the resource through the program
|
|
||||||
err := item.PipeE(yaml.SetAnnotation("config.k8s.io/id", fmt.Sprintf("%d", id)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
ids[id] = &tuple{in: item}
|
|
||||||
id++
|
|
||||||
|
|
||||||
items.YNode().Content = append(items.YNode().Content, item.YNode())
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the ResourceList into a starlark dictionary by
|
|
||||||
// first converting it into a map[string]interface{}
|
|
||||||
value, err := nodeToValue(resourceList)
|
|
||||||
return value, ids, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourceListToOutput converts the output of the starlark program to the filter output
|
|
||||||
func (sf *Filter) resourceListToOutput(
|
|
||||||
value starlark.Value, ids map[int]*tuple) ([]*yaml.RNode, error) {
|
|
||||||
// convert the modified resourceList back into a slice of RNodes
|
|
||||||
// by first converting to a map[string]interface{}
|
|
||||||
out, err := util.Unmarshal(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
o := out.(map[string]interface{})
|
|
||||||
|
|
||||||
// parse the function config
|
|
||||||
if _, found := o["functionConfig"]; found {
|
|
||||||
fc := (o["functionConfig"].(map[string]interface{}))
|
|
||||||
b, err := yaml.Marshal(fc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
sf.FunctionConfig, err = yaml.Parse(string(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the items
|
|
||||||
// copy the items out of the ResourceList, and into the Filter output
|
|
||||||
var results []*yaml.RNode
|
|
||||||
it := (o["items"].([]interface{}))
|
|
||||||
for i := range it {
|
|
||||||
// convert the resource back to the native yaml form
|
|
||||||
b, err := yaml.Marshal(it[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
node, err := yaml.Parse(string(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// match it to an input
|
|
||||||
idS, err := node.Pipe(yaml.GetAnnotation("config.k8s.io/id"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
if idS == nil {
|
|
||||||
// no matching input -- new resource
|
|
||||||
results = append(results, node)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := strconv.Atoi(idS.YNode().Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
if match, found := ids[id]; found {
|
|
||||||
// matching resources
|
|
||||||
match.out = node
|
|
||||||
} else {
|
|
||||||
// no matching input with the same id -- new resource
|
|
||||||
// this may be an error case, the outputs probably shouldn't have ids
|
|
||||||
// assigned by the starlark program
|
|
||||||
results = append(results, node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retain the comments instead of dropping them by copying them from the original inputs
|
|
||||||
for i := 0; i < len(ids); i++ {
|
|
||||||
v := ids[i]
|
|
||||||
if v.out == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := comments.CopyComments(v.in, v.out); err != nil {
|
|
||||||
return nil, errors.Wrap(err)
|
|
||||||
}
|
|
||||||
results = append(results, v.out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete the ids from resources, these were only to track through the starlark program
|
|
||||||
// and that is finished.
|
|
||||||
for i := range results {
|
|
||||||
err := results[i].PipeE(yaml.ClearAnnotation("config.k8s.io/id"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# chartVersion: 9.0.1
|
# chartVersion: 9.0.1
|
||||||
# helmHome: /abs/path/to/helm/config
|
# helmHome: /abs/path/to/helm/config
|
||||||
# helmBin: /abs/path/to/helmBin
|
# helmBin: /abs/path/to/helmBin
|
||||||
# releaseNam: nameOfHelmRelease
|
# releaseName: nameOfHelmRelease
|
||||||
# releaseNamespace: namespaceWhereHelmWouldApply
|
# releaseNamespace: namespaceWhereHelmWouldApply
|
||||||
#
|
#
|
||||||
# fetches the given chart from stable/$chartName,
|
# fetches the given chart from stable/$chartName,
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ functions/examples/validator-resource-requests
|
|||||||
functions/examples/application-cr
|
functions/examples/application-cr
|
||||||
"
|
"
|
||||||
|
|
||||||
|
# don't run e2e tests in CI by default
|
||||||
|
export KUSTOMIZE_DOCKER_E2E=${KUSTOMIZE_DOCKER_E2E:-"false"}
|
||||||
|
|
||||||
for target in $targets; do
|
for target in $targets; do
|
||||||
echo "----- Making $target -----"
|
echo "----- Making $target -----"
|
||||||
pushd .
|
pushd .
|
||||||
|
|||||||
Reference in New Issue
Block a user