mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-22 14:28:18 +00:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f701a0041 | ||
|
|
593f9231ae | ||
|
|
59df8a0dda | ||
|
|
6b93973bad | ||
|
|
df3ec571fb | ||
|
|
f03fad7a96 | ||
|
|
f714e9faf3 | ||
|
|
3e1a3d83da | ||
|
|
8ba2ea9ca7 | ||
|
|
7dc8ef1028 | ||
|
|
ef51cceff5 | ||
|
|
a40c2502de | ||
|
|
0201f9cba8 | ||
|
|
7c1277f24c | ||
|
|
29f03dfb55 | ||
|
|
02d2d38c21 | ||
|
|
6757efe290 | ||
|
|
5990af8ced | ||
|
|
6cddc25f0e | ||
|
|
8bd773b536 | ||
|
|
d9ba209543 | ||
|
|
c51646e3db | ||
|
|
4f9d00c021 | ||
|
|
0042c4be54 | ||
|
|
910eb322e0 | ||
|
|
064b768176 | ||
|
|
4daa655516 | ||
|
|
d6910e9788 | ||
|
|
eed16afb00 | ||
|
|
6ec77b27da | ||
|
|
621ed52bab | ||
|
|
b8c2ed20d1 | ||
|
|
19ad9c2d46 | ||
|
|
41cc210fa0 | ||
|
|
3488b542ac | ||
|
|
04a030bcf0 | ||
|
|
25415c5501 | ||
|
|
a094be45d9 | ||
|
|
fdb8a7d74a | ||
|
|
d481dbad62 | ||
|
|
c1e7f1b957 | ||
|
|
93094c78eb | ||
|
|
a14609f730 | ||
|
|
a8984578e4 | ||
|
|
51e9fec65d | ||
|
|
38b7f42f9e | ||
|
|
e574948577 | ||
|
|
ebf1efe07e | ||
|
|
83bc67c8ad | ||
|
|
1648eceb47 | ||
|
|
538aaaf217 | ||
|
|
5b35443533 | ||
|
|
e089a56e05 | ||
|
|
5c4a778e6a | ||
|
|
e0ec8028eb | ||
|
|
578ff2e45c | ||
|
|
d04877a9e7 | ||
|
|
727b5ebd7f | ||
|
|
af1e1e6942 | ||
|
|
d05bb6b199 | ||
|
|
ba953484bf | ||
|
|
fdf78b1d7d | ||
|
|
95fed47c1c | ||
|
|
4cf916e6f4 | ||
|
|
23bf326d93 | ||
|
|
bcd4d185a7 | ||
|
|
57a5fa593c | ||
|
|
421ca3fb3c | ||
|
|
29945c2c7a | ||
|
|
9d82d54c5b | ||
|
|
4827d9984f | ||
|
|
d718fe3ee1 | ||
|
|
a8fbe35ecf | ||
|
|
5947f696ff | ||
|
|
40e0bbeec2 | ||
|
|
ecbf3c5f51 | ||
|
|
dfa952f0d5 | ||
|
|
793577d044 | ||
|
|
1224dc0c87 | ||
|
|
885c1952a4 |
129
Gopkg.lock
generated
129
Gopkg.lock
generated
@@ -17,54 +17,6 @@
|
||||
pruneopts = "NUT"
|
||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ddb1b23598131d6a52d5df3caec2c76d50a4c108c579297b3e044d17b71aa5d4"
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
packages = [
|
||||
"aws",
|
||||
"aws/awserr",
|
||||
"aws/awsutil",
|
||||
"aws/client",
|
||||
"aws/client/metadata",
|
||||
"aws/corehandlers",
|
||||
"aws/credentials",
|
||||
"aws/credentials/ec2rolecreds",
|
||||
"aws/credentials/endpointcreds",
|
||||
"aws/credentials/stscreds",
|
||||
"aws/csm",
|
||||
"aws/defaults",
|
||||
"aws/ec2metadata",
|
||||
"aws/endpoints",
|
||||
"aws/request",
|
||||
"aws/session",
|
||||
"aws/signer/v4",
|
||||
"internal/sdkio",
|
||||
"internal/sdkrand",
|
||||
"internal/sdkuri",
|
||||
"internal/shareddefaults",
|
||||
"private/protocol",
|
||||
"private/protocol/eventstream",
|
||||
"private/protocol/eventstream/eventstreamapi",
|
||||
"private/protocol/query",
|
||||
"private/protocol/query/queryutil",
|
||||
"private/protocol/rest",
|
||||
"private/protocol/restxml",
|
||||
"private/protocol/xml/xmlutil",
|
||||
"service/s3",
|
||||
"service/sts",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "fde4ded7becdeae4d26bf1212916aabba79349b4"
|
||||
version = "v1.14.12"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:37011b20a70e205b93ebea5287e1afa5618db54bf3998c36ff5a8e4b146a170a"
|
||||
name = "github.com/bgentry/go-netrc"
|
||||
packages = ["netrc"]
|
||||
pruneopts = "NUT"
|
||||
revision = "9fd32a8b3d3d3f9d43c341bfe098430e07609480"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
@@ -100,14 +52,6 @@
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f59266de09e138237bf9df9deb57b9ebbdb1e33b8399bb739c3745e7d3d2787b"
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "358ee7663966325963d4e8b2e1fbd570c5195153"
|
||||
version = "v1.38.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441"
|
||||
@@ -193,41 +137,6 @@
|
||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a5d940c38bf56f121721bfa747c66356df387cb9d5318c570c6d4170aab62862"
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:096df5bfd020b0be2621d2c400468b4956adc4dab078289661a0c00708c09227"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
packages = [
|
||||
".",
|
||||
"helper/url",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "4bda8fa99001c61db3cad96b421d4c12a81f256d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fbab03227343a0285fc74a68dd2ff46cda7edecbbe5a3e98d2cecd00cc67b217"
|
||||
name = "github.com/hashicorp/go-safetemp"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "b1a1dbde6fdc11e3ae79efd9039009e22d4ae240"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0b06ffe0c0764e413a6738e3f045d6bb14117359aef80a09f8c60fbff2ecad6b"
|
||||
name = "github.com/hashicorp/go-version"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "270f2f71b1ee587f3b609f00f422b76a6b28f348"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:406338ad39ab2e37b7f4452906442a3dbf0eb3379dd1f06aafb5c07e769a5fbb"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
@@ -236,13 +145,6 @@
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc"
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "0b12d6b5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:42c47ace7ccb114261ef7e0d418d274921514ab50a3bf6bdb9e51c3dde8ce13d"
|
||||
name = "github.com/json-iterator/go"
|
||||
@@ -263,22 +165,6 @@
|
||||
pruneopts = "NUT"
|
||||
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a4df73029d2c42fabcb6b41e327d2f87e685284ec03edf76921c267d9cfc9c23"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "58046073cbffe2f25d425fe1331102f55cf719de"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:18b773b92ac82a451c1276bd2776c1e55ce057ee202691ab33c8d6690efcc048"
|
||||
name = "github.com/mitchellh/go-testing-interface"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
||||
name = "github.com/modern-go/concurrent"
|
||||
@@ -319,19 +205,6 @@
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:07e8742c479bab0066149ad02a710024154e76874fd0a2dba002d87702725825"
|
||||
name = "github.com/ulikunitz/xz"
|
||||
packages = [
|
||||
".",
|
||||
"internal/hash",
|
||||
"internal/xlog",
|
||||
"lzma",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
|
||||
version = "v0.5.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d1a6ebe75268a41b6fbb1d43947cf8688d8580423b7484fa5ae608beef6df24d"
|
||||
@@ -493,8 +366,6 @@
|
||||
input-imports = [
|
||||
"github.com/evanphx/json-patch",
|
||||
"github.com/ghodss/yaml",
|
||||
"github.com/golang/glog",
|
||||
"github.com/hashicorp/go-getter",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/cobra",
|
||||
"gopkg.in/yaml.v2",
|
||||
|
||||
13
Gopkg.toml
13
Gopkg.toml
@@ -1,4 +1,3 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
@@ -34,10 +33,6 @@
|
||||
name = "github.com/ghodss/yaml"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/glog"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "0.0.2"
|
||||
@@ -61,11 +56,3 @@
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/go-openapi/spec"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/krishicks/yaml-patch"
|
||||
version = "0.0.10"
|
||||
|
||||
281
build/vendor_kustomize.diff
Executable file
281
build/vendor_kustomize.diff
Executable file
@@ -0,0 +1,281 @@
|
||||
commit 1b893558aa83ac6491e5ba416b493170a9045fec
|
||||
Author: Jingfang Liu <jingfangliu@google.com>
|
||||
Date: Mon Nov 12 10:26:12 2018 -0800
|
||||
|
||||
last change
|
||||
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml
|
||||
new file mode 100644
|
||||
index 0000000000..0008853094
|
||||
--- /dev/null
|
||||
+++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml
|
||||
@@ -0,0 +1,8 @@
|
||||
+
|
||||
+apiVersion: v1
|
||||
+kind: ConfigMap
|
||||
+metadata:
|
||||
+ name: the-map
|
||||
+data:
|
||||
+ altGreeting: "Good Morning!"
|
||||
+ enableRisky: "false"
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml
|
||||
new file mode 100644
|
||||
index 0000000000..6e79409080
|
||||
--- /dev/null
|
||||
+++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml
|
||||
@@ -0,0 +1,30 @@
|
||||
+apiVersion: apps/v1
|
||||
+kind: Deployment
|
||||
+metadata:
|
||||
+ name: the-deployment
|
||||
+spec:
|
||||
+ replicas: 3
|
||||
+ template:
|
||||
+ metadata:
|
||||
+ labels:
|
||||
+ deployment: hello
|
||||
+ spec:
|
||||
+ containers:
|
||||
+ - name: the-container
|
||||
+ image: monopole/hello:1
|
||||
+ command: ["/hello",
|
||||
+ "--port=8080",
|
||||
+ "--enableRiskyFeature=$(ENABLE_RISKY)"]
|
||||
+ ports:
|
||||
+ - containerPort: 8080
|
||||
+ env:
|
||||
+ - name: ALT_GREETING
|
||||
+ valueFrom:
|
||||
+ configMapKeyRef:
|
||||
+ name: the-map
|
||||
+ key: altGreeting
|
||||
+ - name: ENABLE_RISKY
|
||||
+ valueFrom:
|
||||
+ configMapKeyRef:
|
||||
+ name: the-map
|
||||
+ key: enableRisky
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml
|
||||
new file mode 100644
|
||||
index 0000000000..6e1e3202d5
|
||||
--- /dev/null
|
||||
+++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml
|
||||
@@ -0,0 +1,5 @@
|
||||
+nameprefix: test-
|
||||
+ resources:
|
||||
+- deployment.yaml
|
||||
+- service.yaml
|
||||
+- configMap.yaml
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml
|
||||
new file mode 100644
|
||||
index 0000000000..2942cdb7df
|
||||
--- /dev/null
|
||||
+++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml
|
||||
@@ -0,0 +1,13 @@
|
||||
+kind: Service
|
||||
+apiVersion: v1
|
||||
+metadata:
|
||||
+ name: the-service
|
||||
+spec:
|
||||
+ selector:
|
||||
+ deployment: hello
|
||||
+ type: LoadBalancer
|
||||
+ ports:
|
||||
+ - protocol: TCP
|
||||
+ port: 8666
|
||||
+ targetPort: 8080
|
||||
+
|
||||
\ No newline at end of file
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD
|
||||
index 22b34de008..b91d1c0130 100644
|
||||
--- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD
|
||||
+++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD
|
||||
@@ -35,12 +35,15 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
+ "//staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/discovery:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/restmapper:go_default_library",
|
||||
"//vendor/golang.org/x/text/encoding/unicode:go_default_library",
|
||||
"//vendor/golang.org/x/text/transform:go_default_library",
|
||||
+ "//vendor/sigs.k8s.io/kustomize/pkg/commands/build:go_default_library",
|
||||
+ "//vendor/sigs.k8s.io/kustomize/pkg/fs:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go
|
||||
index 7fd526b33c..801f13f772 100644
|
||||
--- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go
|
||||
+++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go
|
||||
@@ -465,27 +465,48 @@ func TestPathBuilderWithMultipleInvalid(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryBuilder(t *testing.T) {
|
||||
- b := newDefaultBuilder().
|
||||
- FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../../artifacts/guestbook"}}).
|
||||
- NamespaceParam("test").DefaultNamespace()
|
||||
+ tests := []struct {
|
||||
+ directories []string
|
||||
+ singleItem bool
|
||||
+ number int
|
||||
+ expectedNames []string
|
||||
+ }{
|
||||
+ {[]string{"../../../artifacts/guestbook"}, false, 3, []string{"redis-master"}},
|
||||
+ {[]string{"../../../artifacts/kustomization"}, true, 3, []string{"test-the-deployment"}},
|
||||
+ {[]string{"../../../artifacts/guestbook", "../../../artifacts/kustomization"}, false, 6, []string{"redis-master", "test-the-deployment"}},
|
||||
+ }
|
||||
|
||||
- test := &testVisitor{}
|
||||
- singleItemImplied := false
|
||||
+ for _, tt := range tests {
|
||||
+ b := newDefaultBuilder().
|
||||
+ FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: tt.directories}).
|
||||
+ NamespaceParam("test").DefaultNamespace()
|
||||
|
||||
- err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
|
||||
- if err != nil || singleItemImplied || len(test.Infos) < 3 {
|
||||
- t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
|
||||
- }
|
||||
+ test := &testVisitor{}
|
||||
+ singleItemImplied := false
|
||||
|
||||
- found := false
|
||||
- for _, info := range test.Infos {
|
||||
- if info.Name == "redis-master" && info.Namespace == "test" && info.Object != nil {
|
||||
- found = true
|
||||
- break
|
||||
+ err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
|
||||
+ if err != nil || singleItemImplied != tt.singleItem || len(test.Infos) < tt.number {
|
||||
+ t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
|
||||
+ }
|
||||
+
|
||||
+ contained := func(name string) bool {
|
||||
+ for _, info := range test.Infos {
|
||||
+ if info.Name == name && info.Namespace == "test" && info.Object != nil {
|
||||
+ return true
|
||||
+ }
|
||||
+ }
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
+ allFound := true
|
||||
+ for _, name := range tt.expectedNames {
|
||||
+ if !contained(name) {
|
||||
+ allFound = false
|
||||
+ }
|
||||
+ }
|
||||
+ if !allFound {
|
||||
+ t.Errorf("unexpected responses: %#v", test.Infos)
|
||||
}
|
||||
- }
|
||||
- if !found {
|
||||
- t.Errorf("unexpected responses: %#v", test.Infos)
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go
|
||||
index 32c1a691a5..d7a37e1cde 100644
|
||||
--- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go
|
||||
+++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go
|
||||
@@ -20,10 +20,12 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
+ "io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
+ "strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
@@ -38,6 +40,9 @@ import (
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
+ "k8s.io/cli-runtime/pkg/kustomize/k8sdeps"
|
||||
+ "sigs.k8s.io/kustomize/pkg/commands/build"
|
||||
+ "sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -452,7 +457,10 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
-
|
||||
+ if isKustomizationDir(path) {
|
||||
+ visitors = append(visitors, NewKustomizationVisitor(mapper, path, schema))
|
||||
+ return filepath.SkipDir
|
||||
+ }
|
||||
if fi.IsDir() {
|
||||
if path != paths && !recursive {
|
||||
return filepath.SkipDir
|
||||
@@ -463,7 +471,10 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext
|
||||
if path != paths && ignoreFile(path, extensions) {
|
||||
return nil
|
||||
}
|
||||
-
|
||||
+ if strings.HasSuffix(path, "kustomization.yaml") {
|
||||
+ visitors = append(visitors, NewKustomizationVisitor(mapper, filepath.Dir(path), schema))
|
||||
+ return nil
|
||||
+ }
|
||||
visitor := &FileVisitor{
|
||||
Path: path,
|
||||
StreamVisitor: NewStreamVisitor(nil, mapper, path, schema),
|
||||
@@ -479,6 +490,14 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext
|
||||
return visitors, nil
|
||||
}
|
||||
|
||||
+func isKustomizationDir(path string) bool {
|
||||
+ if _, err := os.Stat(filepath.Join(path, "kustomization.yaml")); err == nil {
|
||||
+ return true
|
||||
+ }
|
||||
+ return false
|
||||
+}
|
||||
+
|
||||
+
|
||||
// FileVisitor is wrapping around a StreamVisitor, to handle open/close files
|
||||
type FileVisitor struct {
|
||||
Path string
|
||||
@@ -507,6 +526,37 @@ func (v *FileVisitor) Visit(fn VisitorFunc) error {
|
||||
return v.StreamVisitor.Visit(fn)
|
||||
}
|
||||
|
||||
+// KustomizationVisitor prorvides the output of kustomization build
|
||||
+type KustomizationVisitor struct {
|
||||
+ Path string
|
||||
+ *StreamVisitor
|
||||
+}
|
||||
+
|
||||
+// Visit in a KustomizationVisitor build the kustomization output
|
||||
+func (v *KustomizationVisitor) Visit(fn VisitorFunc) error {
|
||||
+ fSys := fs.MakeRealFS()
|
||||
+ f := k8sdeps.NewFactory()
|
||||
+ var out bytes.Buffer
|
||||
+ cmd := build.NewCmdBuild(&out, fSys, f.ResmapF, f.TransformerF)
|
||||
+ cmd.SetArgs([]string{v.Path})
|
||||
+ // we want to silence usage, error output, and any future output from cobra
|
||||
+ // we will get error output as a golang error from execute
|
||||
+ cmd.SetOutput(ioutil.Discard)
|
||||
+ _, err := cmd.ExecuteC()
|
||||
+ if err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ v.StreamVisitor.Reader = bytes.NewReader(out.Bytes())
|
||||
+ return v.StreamVisitor.Visit(fn)
|
||||
+}
|
||||
+
|
||||
+func NewKustomizationVisitor(mapper *mapper, path string, schema ContentValidator) *KustomizationVisitor {
|
||||
+ return &KustomizationVisitor{
|
||||
+ Path: path,
|
||||
+ StreamVisitor: NewStreamVisitor(nil, mapper, path, schema),
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
// StreamVisitor reads objects from an io.Reader and walks them. A stream visitor can only be
|
||||
// visited once.
|
||||
// TODO: depends on objects being in JSON format before being passed to decode - need to implement
|
||||
@@ -17,6 +17,8 @@
|
||||
set -e
|
||||
set -x
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
# vendor_kustomize.sh creates the change in kubernetes repo for vendoring kustomize
|
||||
|
||||
function setUpWorkspace {
|
||||
@@ -26,27 +28,27 @@ function setUpWorkspace {
|
||||
}
|
||||
|
||||
function cloneK8s {
|
||||
mkdir -p $GOPATH/src/k8s.io
|
||||
cd $GOPATH/src/k8s.io
|
||||
mkdir -p $KPATH/src/k8s.io
|
||||
cd $KPATH/src/k8s.io
|
||||
|
||||
git clone git@github.com:kubernetes/kubernetes.git
|
||||
}
|
||||
|
||||
function godepRestore {
|
||||
cd $GOPATH/src/k8s.io/kubernetes
|
||||
cd $KPATH/src/k8s.io/kubernetes
|
||||
|
||||
# restore dependencies
|
||||
hack/godep-restore.sh
|
||||
hack/run-in-gopath.sh hack/godep-restore.sh
|
||||
}
|
||||
|
||||
function getKustomizeDeps {
|
||||
# get Kustomize and Kustomize dependencies
|
||||
godep get sigs.k8s.io/kustomize/pkg/commands
|
||||
godep get github.com/bgentry/go-netrc/netrc
|
||||
godep get github.com/hashicorp/go-cleanhttp
|
||||
godep get github.com/hashicorp/go-getter
|
||||
godep get github.com/hashicorp/go-safetemp
|
||||
godep get github.com/hashicorp/go-version
|
||||
hack/run-in-gopath.sh godep get sigs.k8s.io/kustomize/pkg/commands
|
||||
hack/run-in-gopath.sh godep get github.com/bgentry/go-netrc/netrc
|
||||
hack/run-in-gopath.sh godep get github.com/hashicorp/go-cleanhttp
|
||||
hack/run-in-gopath.sh godep get github.com/hashicorp/go-getter
|
||||
hack/run-in-gopath.sh godep get github.com/hashicorp/go-safetemp
|
||||
hack/run-in-gopath.sh godep get github.com/hashicorp/go-version
|
||||
|
||||
# The hashes below passed bin/pre-commit.sh with kustomize HEAD at time of merger.
|
||||
DEPS=(
|
||||
@@ -61,7 +63,7 @@ function getKustomizeDeps {
|
||||
)
|
||||
|
||||
function foo {
|
||||
cd $GOPATH/src/github.com/$1
|
||||
cd $KPATH/src/k8s.io/kubernetes/_output/local/go/src/github.com/$1
|
||||
git checkout $2
|
||||
}
|
||||
for i in "${DEPS[@]}"; do
|
||||
@@ -70,58 +72,35 @@ function getKustomizeDeps {
|
||||
}
|
||||
|
||||
function updateK8s {
|
||||
# Copy k8sdeps from Kustomize to kubectl
|
||||
mkdir -p $GOPATH/src/k8s.io/kubernetes/pkg/kubectl/kustomize
|
||||
cp -r $GOPATH/src/sigs.k8s.io/kustomize/k8sdeps \
|
||||
$GOPATH/src/k8s.io/kubernetes/pkg/kubectl/kustomize/k8sdeps
|
||||
# Copy k8sdeps from Kustomize to cli-runtime in staging
|
||||
mkdir -p $KPATH/src/k8s.io/kubernetes/staging/src/k8s.io/cli-runtime/pkg/kustomize
|
||||
cp -r $KPATH/src/k8s.io/kubernetes/_output/local/go/src/sigs.k8s.io/kustomize/k8sdeps \
|
||||
$KPATH/src/k8s.io/kubernetes/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps
|
||||
|
||||
# Change import path of k8sdeps
|
||||
find $GOPATH/src/k8s.io/kubernetes/pkg/kubectl/kustomize/k8sdeps \
|
||||
find $KPATH/src/k8s.io/kubernetes/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps \
|
||||
-type f -name "*.go" | \
|
||||
xargs sed -i \
|
||||
's!sigs.k8s.io/kustomize/k8sdeps!k8s.io/kubernetes/pkg/kubectl/kustomize/k8sdeps!'
|
||||
's!sigs.k8s.io/kustomize/k8sdeps!k8s.io/cli-runtime/pkg/kustomize/k8sdeps!'
|
||||
|
||||
|
||||
# Add kustomize command to kubectl
|
||||
cat > $GOPATH/kubectl.diff << EOF
|
||||
diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go
|
||||
index 43a541ecc9..2d23bfd27d 100644
|
||||
--- a/pkg/kubectl/cmd/cmd.go
|
||||
+++ b/pkg/kubectl/cmd/cmd.go
|
||||
@@ -74,6 +74,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/templates"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
+ "k8s.io/kubernetes/pkg/kubectl/kustomize/k8sdeps"
|
||||
+ "sigs.k8s.io/kustomize/pkg/commands"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -505,6 +507,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
|
||||
replace.NewCmdReplace(f, ioStreams),
|
||||
wait.NewCmdWait(f, ioStreams),
|
||||
convert.NewCmdConvert(f, ioStreams),
|
||||
+ templates.NormalizeAll(commands.NewDefaultCommand(k8sdeps.NewFactory())),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
EOF
|
||||
cp $DIR/vendor_kustomize.diff $KPATH/vendor_kustomize.diff
|
||||
|
||||
cd $GOPATH/src/k8s.io/kubernetes
|
||||
git apply --ignore-space-change --ignore-whitespace $GOPATH/kubectl.diff
|
||||
git apply --ignore-space-change --ignore-whitespace $KPATH/vendor_kustomize.diff
|
||||
}
|
||||
|
||||
function godepSave {
|
||||
# Save all dependencies into k8s.io/kubernetes/vendor by running
|
||||
# hack/godep-save.sh
|
||||
./hack/godep-save.sh
|
||||
hack/run-in-gopath.sh hack/godep-save.sh
|
||||
}
|
||||
|
||||
function verify {
|
||||
# make sure in k8s.io/kubernetes/vendor/sigs.k8s.io/kustomize
|
||||
# there is no internal package
|
||||
test 0 == $(ls $GOPATH/src/k8s.io/kubernetes/vendor/sigs.k8s.io/kustomize | grep “internal” | wc -l)
|
||||
test 0 == $(ls $KPATH/src/k8s.io/kubernetes/vendor/sigs.k8s.io/kustomize | grep “internal” | wc -l)
|
||||
|
||||
# Make sure it compiles.
|
||||
test 0 == $(bazel build cmd/kubectl:kubectl)
|
||||
@@ -130,10 +109,6 @@ function verify {
|
||||
echo "The change for vendoring kustomize is ready in $GOPATH/src/k8s.io/kubernetes.\n Next step, open a PR for it.\n"
|
||||
}
|
||||
|
||||
function updateDocs {
|
||||
./hack/update-generated-docs.sh
|
||||
}
|
||||
|
||||
setUpWorkspace
|
||||
cloneK8s
|
||||
godepRestore
|
||||
@@ -141,4 +116,3 @@ getKustomizeDeps
|
||||
updateK8s
|
||||
godepSave
|
||||
verify
|
||||
updateDocs
|
||||
|
||||
@@ -150,7 +150,7 @@ Here's an [example](kustomization.yaml).
|
||||
A kustomization contains fields falling into these categories:
|
||||
|
||||
* _Customization operators_ for modifying operands, e.g.
|
||||
_namePrefix_, _commonLabels_, _patches_, etc.
|
||||
_namePrefix_, _nameSuffix_, _commonLabels_, _patches_, etc.
|
||||
|
||||
* _Customization operands_:
|
||||
* [resources] - completely specified k8s API objects,
|
||||
|
||||
@@ -40,6 +40,13 @@ namespace: my-namespace
|
||||
# "wordpress" becomes "alices-wordpress".
|
||||
namePrefix: alices-
|
||||
|
||||
# Value of this field is appended to the
|
||||
# names of all resources, e.g. a deployment named
|
||||
# "wordpress" becomes "wordpress-v2".
|
||||
# The suffix is appended before content hash
|
||||
# if resource type is ConfigMap or Secret.
|
||||
nameSuffix: -v2
|
||||
|
||||
# Labels to add to all resources and selectors.
|
||||
commonLabels:
|
||||
someName: someValue
|
||||
@@ -102,6 +109,24 @@ secretGenerator:
|
||||
envCommand: printf \"DB_USERNAME=admin\nDB_PASSWORD=somepw\"
|
||||
type: Opaque
|
||||
|
||||
# generatorOptions modify behavior of all ConfigMap and Secret generators
|
||||
generatorOptions:
|
||||
# labels to add to all generated resources
|
||||
labels:
|
||||
kustomize.generated.resources: somevalue
|
||||
# annotations to add to all generated resources
|
||||
annotations:
|
||||
kustomize.generated.resource: somevalue
|
||||
# timeoutSeconds specifies the timeout for commands
|
||||
timeoutSeconds: 30
|
||||
# shell and arguments to use as a context for commands used in resource
|
||||
# generation. Default at time of writing: ["sh", "-c"]
|
||||
shell: ["sh", "-c"]
|
||||
# disableNameSuffixHash is true disables the default behavior of adding a
|
||||
# suffix to the names of generated resources that is a hash of
|
||||
# the resource contents.
|
||||
disableNameSuffixHash: true
|
||||
|
||||
# Each entry in this list should resolve to a directory
|
||||
# containing a kustomization file, else the
|
||||
# customization fails.
|
||||
@@ -187,9 +212,9 @@ patchesJson6902:
|
||||
# transformation for any objects in those types.
|
||||
#
|
||||
# Typical use case: A CRD object refers to a ConfigMap object.
|
||||
# In kustomization, the ConfigMap object name may change by adding namePrefix or hashing
|
||||
# In kustomization, the ConfigMap object name may change by adding namePrefix, nameSuffix, or hashing
|
||||
# The name reference for this ConfigMap object in CRD object need to be
|
||||
# updated with namePrefix or hashing in the same way.
|
||||
# updated with namePrefix, nameSuffix, or hashing in the same way.
|
||||
crds:
|
||||
- crds/typeA.yaml
|
||||
- crds/typeB.yaml
|
||||
@@ -216,7 +241,7 @@ crds:
|
||||
# the image field of container number 2 inside of a pod can currently not be done.
|
||||
#
|
||||
# Not every location of a variable is supported. To see a complete list of locations
|
||||
# see the file [refvars.go](https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/refvars.go#L20).
|
||||
# see the file [varreference.go](https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go#L20).
|
||||
#
|
||||
# An example of a situation where you'd not use vars is when you'd like to set a
|
||||
# pod's `serviceAccountName`. In that case you would just reference the ServiceAccount
|
||||
|
||||
@@ -29,11 +29,13 @@ go get sigs.k8s.io/kustomize
|
||||
|
||||
* [configGenerations](configGeneration.md) -
|
||||
Rolling update when ConfigMapGenerator changes
|
||||
|
||||
* [generatorOptions](generatorOptions.md) - Modifying behavior of all ConfigMap and Secret generators.
|
||||
|
||||
* [breakfast](breakfast.md) - Customize breakfast for
|
||||
Alice and Bob.
|
||||
|
||||
* [container args](wordpress/README.md) - Injecting k8s runtime data into container arguments (e.g. to point wordpress to a SQL service).
|
||||
* [vars](wordpress/README.md) - Injecting k8s runtime data into container arguments (e.g. to point wordpress to a SQL service) by vars.
|
||||
|
||||
* [image tags](imageTags.md) - Updating image tags without applying a patch.
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ cat <<EOF >$OVERLAYS/development/kustomization.yaml
|
||||
bases:
|
||||
- ../../base
|
||||
namePrefix: dev-
|
||||
nameSuffix: -v1
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
behavior: merge
|
||||
@@ -215,11 +216,12 @@ kustomize build $OVERLAYS/development
|
||||
The name of the generated `ConfigMap` is visible in this
|
||||
output.
|
||||
|
||||
The name should be something like `dev-my-configmap-b5m75ck895`:
|
||||
The name should be something like `dev-my-configmap-v1-2gccmccgd5`:
|
||||
|
||||
* `"dev-"` comes from the `namePrefix` field,
|
||||
* `"my-configmap"` comes from the `configMapGenerator/name` field,
|
||||
* `"-b5m75ck895"` comes from a deterministic hash that `kustomize`
|
||||
* `"-v1"` comes from the `nameSuffix` field,
|
||||
* `"-2gccmccgd5"` comes from a deterministic hash that `kustomize`
|
||||
computes from the contents of the configMap.
|
||||
|
||||
The hash suffix is critical. If the configMap content
|
||||
@@ -291,7 +293,7 @@ kustomize build $OVERLAYS/production
|
||||
```
|
||||
|
||||
A CICD process could apply this directly to
|
||||
the cluser using:
|
||||
the cluster using:
|
||||
|
||||
> ```
|
||||
> kustomize build $OVERLAYS/production | kubectl apply -f -
|
||||
|
||||
@@ -60,6 +60,7 @@ mkdir -p $OVERLAYS/staging
|
||||
|
||||
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
|
||||
namePrefix: staging-
|
||||
nameSuffix: -v1
|
||||
commonLabels:
|
||||
variant: staging
|
||||
org: acmeCorporation
|
||||
@@ -150,13 +151,17 @@ The configMap name is prefixed by _staging-_, per the
|
||||
`namePrefix` field in
|
||||
`$OVERLAYS/staging/kustomization.yaml`.
|
||||
|
||||
The configMap name is suffixed by _-v1_, per the
|
||||
`nameSuffix` field in
|
||||
`$OVERLAYS/staging/kustomization.yaml`.
|
||||
|
||||
The suffix to the configMap name is generated from a
|
||||
hash of the maps content - in this case the name suffix
|
||||
is _hhhhkfmgmk_:
|
||||
is _k25m8k5k5m_:
|
||||
|
||||
<!-- @grepStagingHash @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
|
||||
kustomize build $OVERLAYS/staging | grep k25m8k5k5m
|
||||
```
|
||||
|
||||
Now modify the map patch, to change the greeting
|
||||
@@ -183,20 +188,20 @@ kustomize build $OVERLAYS/staging |\
|
||||
```
|
||||
|
||||
Confirm that the change in configMap content resulted
|
||||
in three new names ending in _khk45ktkd9_ - one in the
|
||||
in three new names ending in _cd7kdh48fd_ - one in the
|
||||
configMap name itself, and two in the deployment that
|
||||
uses the map:
|
||||
|
||||
<!-- @countHashes @test -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l); \
|
||||
$(kustomize build $OVERLAYS/staging | grep cd7kdh48fd | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
Applying these resources to the cluster will result in
|
||||
a rolling update of the deployments pods, retargetting
|
||||
them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_
|
||||
them from the _k25m8k5k5m_ maps to the _cd7kdh48fd_
|
||||
maps. The system will later garbage collect the
|
||||
unused maps.
|
||||
|
||||
|
||||
62
examples/generatorOptions.md
Normal file
62
examples/generatorOptions.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Generator Options
|
||||
|
||||
Kustomize provides options to modify the behavior of ConfigMap and Secret generators. These options include
|
||||
|
||||
- disable appending a content hash suffix to the names of generated resources
|
||||
- adding labels to generated resources
|
||||
- adding annotations to generated resources
|
||||
- changing shell and arguments for getting data from commands
|
||||
- changing timeout for executing commands
|
||||
|
||||
This demo shows how to use these options. First create a workspace.
|
||||
```
|
||||
DEMO_HOME=$(mkdir -d)
|
||||
```
|
||||
|
||||
Create a kustomization and add a ConfigMap generator to it.
|
||||
|
||||
<!-- @createCMGenerator @test -->
|
||||
```
|
||||
cat > $DEMO_HOME/kustomization.yaml << EOF
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
literals:
|
||||
- foo=bar
|
||||
- baz=qux
|
||||
EOF
|
||||
```
|
||||
|
||||
Add following generatorOptions
|
||||
<!-- @addGeneratorOptions @test -->
|
||||
```
|
||||
cat >> $DEMO_HOME/kustomization.yaml << EOF
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: true
|
||||
labels:
|
||||
kustomize.generated.resource: somevalue
|
||||
annotations:
|
||||
annotations.only.for.generated: othervalue
|
||||
EOF
|
||||
```
|
||||
Run `kustomize build` and make sure that the generated ConfigMap
|
||||
|
||||
- doesn't have name suffix
|
||||
<!-- @verify @test -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "name: my-configmap$" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
- has label `kustomize.generated.resource: somevalue`
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep -A 1 "labels" | grep "kustomize.generated.resource" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
- has annotation `annotations.only.for.generated: othervalue`
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep -A 1 "annotations" | grep "annotations.only.for.generated" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Kustomize computes the resources by applying a series of transformers:
|
||||
- namespace transformer
|
||||
- prefix transformer
|
||||
- prefix/suffix transformer
|
||||
- label transformer
|
||||
- annotation transformer
|
||||
- name reference transformer
|
||||
@@ -22,8 +22,8 @@ create: false
|
||||
```
|
||||
If `create` is set to true, it indicates the transformer to create the path if it is not found in the resources. This is most useful for label and annotation transformers, where the path for labels or annotations may not be set before the transformation.
|
||||
|
||||
## prefix transformer
|
||||
Name prefix transformer adds prefix to the `metadata/name` field for all resources with following configuration:
|
||||
## prefix/suffix transformer
|
||||
Name prefix suffix transformer adds prefix and suffix to the `metadata/name` field for all resources with following configuration:
|
||||
```
|
||||
namePrefix:
|
||||
- path: metadata/name
|
||||
@@ -89,8 +89,8 @@ nameReference:
|
||||
|
||||
## cusotmizing transformer configurations
|
||||
|
||||
Kustomize has a default set of configurations. They can be saved to local directory through `kustomize config save -d`. Kustomize allows modifying those configuration files and using them in `kustomize build` through `-t`. This tutorial shows how to customize those configurations to
|
||||
- [support a crd type](crd/README.md)
|
||||
Kustomize has a default set of configurations. They can be saved to local directory through `kustomize config save -d`. Kustomize allows modifying those configuration files and using them in kustomization.yaml file. This tutorial shows how to customize those configurations to
|
||||
- [support a CRD type](crd/README.md)
|
||||
- disabling adding commonLabels to fields in some kind of resources
|
||||
- add extra fields for variable substitution
|
||||
- add extra fields for name reference
|
||||
|
||||
@@ -1,39 +1,57 @@
|
||||
## Transformer Configurations - CRD
|
||||
## Supporting Custom Resources (defined by a CRD)
|
||||
|
||||
This tutorial shows how to add transformer configurations to support a CRD type.
|
||||
This tutorial shows how to add transformer configurations to support a custom resource.
|
||||
|
||||
### Get Default Config
|
||||
Get the default transformer configurations by
|
||||
Create a workspace by
|
||||
<!-- @createws @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
### Get the native config as a starting point
|
||||
|
||||
Get the default transformer configurations using this command:
|
||||
|
||||
<!-- @saveConfig @test -->
|
||||
```
|
||||
kustomize config save -d ~/.kustomize/config
|
||||
kustomize config save -d $DEMO_HOME/kustomizeconfig
|
||||
```
|
||||
The default configurations are save in directory `~/.kustomize/config` as several files
|
||||
The default configurations are saved
|
||||
in the directory `$DEMO_HOME/kusotmizeconfig` as several files
|
||||
|
||||
> ```
|
||||
> commonannotations.yaml commonlabels.yaml nameprefix.yaml namereference.yaml namespace.yaml varreference.yaml
|
||||
> commonannotations.yaml
|
||||
> commonlabels.yaml
|
||||
> nameprefix.yaml
|
||||
> namereference.yaml
|
||||
> namespace.yaml
|
||||
> varreference.yaml
|
||||
> ```
|
||||
|
||||
### Add Config for a CRD
|
||||
All transformers will be involved for a CRD type. The default configurations already include some common fieldSpec for all types:
|
||||
These files contain the field specifications for native resources
|
||||
that transformation directives like `namePrefix`, `commonLabels`, etc.
|
||||
need to do their work.
|
||||
|
||||
These default configurations already include some common
|
||||
field specifictions for all types:
|
||||
|
||||
- nameprefix is added to `.metadata.name`
|
||||
- namespace is added to `.metadata.namespace`
|
||||
- labels is added to `.metadata.labels`
|
||||
- annotations is added to `.metadata.annotations`
|
||||
|
||||
Thus those fieldSpec don't need to be added to support a CRD type.
|
||||
Consider a CRD type `MyKind` with fields
|
||||
### Adding a custom resource
|
||||
|
||||
Consider a CRD of kind `MyKind` with fields
|
||||
- `.spec.secretRef.name` reference a Secret
|
||||
- `.spec.beeRef.name` reference an instance of CRD `Bee`
|
||||
- `.spec.containers.command` as the list of container commands
|
||||
- `.spec.selectors` as the label selectors
|
||||
|
||||
Add following file to configure the transformers for the above fields
|
||||
Add the following file to configure the transformers for the above fields
|
||||
<!-- @addConfig @test -->
|
||||
```
|
||||
cat > ~/.kustomize/config/mykind.yaml << EOF
|
||||
cat > $DEMO_HOME/kustomizeconfig/mykind.yaml << EOF
|
||||
|
||||
commonLabels:
|
||||
- path: spec/selectors
|
||||
@@ -60,31 +78,12 @@ EOF
|
||||
```
|
||||
|
||||
### Apply config
|
||||
Create a kustomization with a `MyKind` instance.
|
||||
|
||||
<!-- @createKustomization @test -->
|
||||
Create a file with some resources that
|
||||
includes an instance of `MyKind`:
|
||||
|
||||
<!-- @createResource @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
|
||||
cat > $DEMO_HOME/kustomization.yaml << EOF
|
||||
resources:
|
||||
- resources.yaml
|
||||
|
||||
namePrefix: test-
|
||||
|
||||
commonLabels:
|
||||
foo: bar
|
||||
|
||||
vars:
|
||||
- name: BEE_ACTION
|
||||
objref:
|
||||
kind: Bee
|
||||
name: bee
|
||||
apiVersion: v1beta1
|
||||
fieldref:
|
||||
fieldpath: spec.action
|
||||
EOF
|
||||
|
||||
cat > $DEMO_HOME/resources.yaml << EOF
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
@@ -118,54 +117,60 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Run `kustomize build` with customized transformer configurations and verify that
|
||||
the namereference is correctly resolved.
|
||||
Create a kustomization referring to it:
|
||||
|
||||
<!-- @createKustomization @test -->
|
||||
```
|
||||
cat > $DEMO_HOME/kustomization.yaml << EOF
|
||||
resources:
|
||||
- resources.yaml
|
||||
|
||||
namePrefix: test-
|
||||
|
||||
commonLabels:
|
||||
foo: bar
|
||||
|
||||
vars:
|
||||
- name: BEE_ACTION
|
||||
objref:
|
||||
kind: Bee
|
||||
name: bee
|
||||
apiVersion: v1beta1
|
||||
fieldref:
|
||||
fieldpath: spec.action
|
||||
EOF
|
||||
```
|
||||
|
||||
Use the customized transformer configurations by specifying them
|
||||
in the kustomization file:
|
||||
<!-- @addTransformerConfigs @test -->
|
||||
```
|
||||
cat >> $DEMO_HOME/kustomization.yaml << EOF
|
||||
configurations:
|
||||
- kustomizeconfig/mykind.yaml
|
||||
- kustomizeconfig/commonannotations.yaml
|
||||
- kustomizeconfig/commonlabels.yaml
|
||||
- kustomizeconfig/nameprefix.yaml
|
||||
- kustomizeconfig/namereference.yaml
|
||||
- kustomizeconfig/namespace.yaml
|
||||
- kustomizeconfig/varreference.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Run `kustomize build` and verify that the namereference is correctly resolved.
|
||||
|
||||
<!-- @build @test -->
|
||||
```
|
||||
test 2 == \
|
||||
$(kustomize build $DEMO_HOME -t ~/.kustomize/config | grep -A 2 ".*Ref" | grep "test-" | wc -l); \
|
||||
$(kustomize build $DEMO_HOME | grep -A 2 ".*Ref" | grep "test-" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
Run `kustomize build` with customized transformer configurations and verify that
|
||||
the vars correctly resolved.
|
||||
Run `kustomize build` and verify that the vars correctly resolved.
|
||||
|
||||
<!-- @verify @test -->
|
||||
```
|
||||
test 0 == \
|
||||
$(kustomize build $DEMO_HOME -t ~/.kustomize/config | grep "BEE_ACTION" | wc -l); \
|
||||
$(kustomize build $DEMO_HOME | grep "BEE_ACTION" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
To understand this better, compare the output using default transformer configurations.
|
||||
|
||||
<!-- @compareOutput -->
|
||||
```
|
||||
diff \
|
||||
<(kustomize build $DEMO_HOME) \
|
||||
<(kustomize build $DEMO_HOME -t ~/.kustomize/config ) |\
|
||||
more
|
||||
```
|
||||
|
||||
The difference output should look something like
|
||||
> ```
|
||||
> 20,21c20,21
|
||||
> < action: $(BEE_ACTION)
|
||||
> < name: bee
|
||||
> ---
|
||||
> > action: fly
|
||||
> > name: test-bee
|
||||
> 25c25
|
||||
> < - $(BEE_ACTION)
|
||||
> ---
|
||||
> > - fly
|
||||
> 28c28,30
|
||||
> < name: crdsecret
|
||||
> ---
|
||||
> > name: test-crdsecret
|
||||
> > selectors:
|
||||
> > foo: bar
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Demo: Injecting k8s runtime data into containers
|
||||
|
||||
In this tutorial, you will learn how to use `kustomize` to declare a variable reference and substitute it in container's command.
|
||||
In this tutorial, you will learn how to use `kustomize` to declare a variable reference and substitute it in container's command. Note that, the substitution is not for arbitrary fields, it is only applicable to container env, args and command.
|
||||
|
||||
To run WordPress, it's necessary to
|
||||
|
||||
|
||||
@@ -129,9 +129,9 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
}
|
||||
|
||||
fSys := fs.MakeFakeFS()
|
||||
fSys.WriteFile("configmap/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
|
||||
fSys.WriteFile("configmap/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
|
||||
f := NewConfigMapFactory(fSys, loader.NewFileLoader(fSys))
|
||||
fSys.WriteFile("/configmap/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
|
||||
fSys.WriteFile("/configmap/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
|
||||
f := NewConfigMapFactory(fSys, loader.NewFileLoaderAtRoot(fSys))
|
||||
for _, tc := range testCases {
|
||||
cm, err := f.MakeConfigMap(&tc.input, tc.options)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ package kunstruct
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
@@ -29,21 +30,21 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// KunstructurerFactoryImpl hides construction using apimachinery types.
|
||||
type KunstructurerFactoryImpl struct {
|
||||
cmfactory *configmapandsecret.ConfigMapFactory
|
||||
secfactory *configmapandsecret.SecretFactory
|
||||
// KunstructuredFactoryImpl hides construction using apimachinery types.
|
||||
type KunstructuredFactoryImpl struct {
|
||||
cmFactory *configmapandsecret.ConfigMapFactory
|
||||
secretFactory *configmapandsecret.SecretFactory
|
||||
}
|
||||
|
||||
var _ ifc.KunstructuredFactory = &KunstructurerFactoryImpl{}
|
||||
var _ ifc.KunstructuredFactory = &KunstructuredFactoryImpl{}
|
||||
|
||||
// NewKunstructuredFactoryImpl returns a factory.
|
||||
func NewKunstructuredFactoryImpl() ifc.KunstructuredFactory {
|
||||
return &KunstructurerFactoryImpl{}
|
||||
return &KunstructuredFactoryImpl{}
|
||||
}
|
||||
|
||||
// SliceFromBytes returns a slice of Kunstructured.
|
||||
func (kf *KunstructurerFactoryImpl) SliceFromBytes(
|
||||
func (kf *KunstructuredFactoryImpl) SliceFromBytes(
|
||||
in []byte) ([]ifc.Kunstructured, error) {
|
||||
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(in), 1024)
|
||||
var result []ifc.Kunstructured
|
||||
@@ -52,6 +53,10 @@ func (kf *KunstructurerFactoryImpl) SliceFromBytes(
|
||||
var out unstructured.Unstructured
|
||||
err = decoder.Decode(&out)
|
||||
if err == nil {
|
||||
err = kf.validate(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, &UnstructAdapter{Unstructured: out})
|
||||
}
|
||||
}
|
||||
@@ -66,14 +71,14 @@ func isEmptyYamlError(err error) bool {
|
||||
}
|
||||
|
||||
// FromMap returns an instance of Kunstructured.
|
||||
func (kf *KunstructurerFactoryImpl) FromMap(
|
||||
func (kf *KunstructuredFactoryImpl) FromMap(
|
||||
m map[string]interface{}) ifc.Kunstructured {
|
||||
return &UnstructAdapter{Unstructured: unstructured.Unstructured{Object: m}}
|
||||
}
|
||||
|
||||
// MakeConfigMap returns an instance of Kunstructured for ConfigMap
|
||||
func (kf *KunstructurerFactoryImpl) MakeConfigMap(args *types.ConfigMapArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
|
||||
cm, err := kf.cmfactory.MakeConfigMap(args, options)
|
||||
func (kf *KunstructuredFactoryImpl) MakeConfigMap(args *types.ConfigMapArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
|
||||
cm, err := kf.cmFactory.MakeConfigMap(args, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -81,8 +86,8 @@ func (kf *KunstructurerFactoryImpl) MakeConfigMap(args *types.ConfigMapArgs, opt
|
||||
}
|
||||
|
||||
// MakeSecret returns an instance of Kunstructured for Secret
|
||||
func (kf *KunstructurerFactoryImpl) MakeSecret(args *types.SecretArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
|
||||
sec, err := kf.secfactory.MakeSecret(args, options)
|
||||
func (kf *KunstructuredFactoryImpl) MakeSecret(args *types.SecretArgs, options *types.GeneratorOptions) (ifc.Kunstructured, error) {
|
||||
sec, err := kf.secretFactory.MakeSecret(args, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -90,7 +95,18 @@ func (kf *KunstructurerFactoryImpl) MakeSecret(args *types.SecretArgs, options *
|
||||
}
|
||||
|
||||
// Set sets loader, filesystem and workdirectory
|
||||
func (kf *KunstructurerFactoryImpl) Set(fs fs.FileSystem, ldr ifc.Loader) {
|
||||
kf.cmfactory = configmapandsecret.NewConfigMapFactory(fs, ldr)
|
||||
kf.secfactory = configmapandsecret.NewSecretFactory(fs, ldr.Root())
|
||||
func (kf *KunstructuredFactoryImpl) Set(fs fs.FileSystem, ldr ifc.Loader) {
|
||||
kf.cmFactory = configmapandsecret.NewConfigMapFactory(fs, ldr)
|
||||
kf.secretFactory = configmapandsecret.NewSecretFactory(fs, ldr.Root())
|
||||
}
|
||||
|
||||
// validate validates that u has kind and name
|
||||
func (kf *KunstructuredFactoryImpl) validate(u unstructured.Unstructured) error {
|
||||
if u.GetName() == "" {
|
||||
return fmt.Errorf("Missing metadata.name in object %v", u)
|
||||
}
|
||||
if u.GetKind() == "" {
|
||||
return fmt.Errorf("Missing kind in object %v", u)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,6 +100,18 @@ WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
|
||||
expectedOut: []ifc.Kunstructured{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Missing .metadata.name in object",
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
annotations:
|
||||
foo: bar
|
||||
`),
|
||||
expectedOut: nil,
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
71
k8sdeps/kunstruct/helper.go
Normal file
71
k8sdeps/kunstruct/helper.go
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package kunstruct provides unstructured from api machinery and factory for creating unstructured
|
||||
package kunstruct
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseFields(path string) ([]string, error) {
|
||||
if !strings.Contains(path, "[") {
|
||||
return strings.Split(path, "."), nil
|
||||
}
|
||||
|
||||
var fields []string
|
||||
start := 0
|
||||
insideParentheses := false
|
||||
for i := range path {
|
||||
switch path[i] {
|
||||
case '.':
|
||||
if !insideParentheses {
|
||||
fields = append(fields, path[start:i])
|
||||
start = i + 1
|
||||
}
|
||||
case '[':
|
||||
if !insideParentheses {
|
||||
if i == start {
|
||||
start = i + 1
|
||||
} else {
|
||||
fields = append(fields, path[start:i])
|
||||
start = i + 1
|
||||
}
|
||||
insideParentheses = true
|
||||
} else {
|
||||
return nil, fmt.Errorf("nested parentheses are not allowed: %s", path)
|
||||
}
|
||||
case ']':
|
||||
if insideParentheses {
|
||||
fields = append(fields, path[start:i])
|
||||
start = i + 1
|
||||
insideParentheses = false
|
||||
} else {
|
||||
return nil, fmt.Errorf("Invalid field path %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
if start < len(path)-1 {
|
||||
fields = append(fields, path[start:])
|
||||
}
|
||||
for i, f := range fields {
|
||||
if strings.HasPrefix(f, "\"") || strings.HasPrefix(f, "'") {
|
||||
fields[i] = strings.Trim(f, "\"'")
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package kunstruct
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -80,8 +79,12 @@ func (fs *UnstructAdapter) SetMap(m map[string]interface{}) {
|
||||
|
||||
// GetFieldValue returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetFieldValue(path string) (string, error) {
|
||||
fields, err := parseFields(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s, found, err := unstructured.NestedString(
|
||||
fs.UnstructuredContent(), strings.Split(path, ".")...)
|
||||
fs.UnstructuredContent(), fields...)
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
@@ -33,25 +32,16 @@ func NewNameHashTransformer() transformers.Transformer {
|
||||
return &nameHashTransformer{}
|
||||
}
|
||||
|
||||
// Transform appends hash to configmaps and secrets.
|
||||
// Transform appends hash to generated resources.
|
||||
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
|
||||
for _, res := range m {
|
||||
if res.IsGenerated() {
|
||||
err := o.appendHash(res)
|
||||
h, err := NewKustHash().Hash(res.Map())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *nameHashTransformer) appendHash(res *resource.Resource) error {
|
||||
h, err := NewKustHash().Hash(res.Map())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", res.GetName(), h)
|
||||
res.SetName(nameWithHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,14 +19,11 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"sigs.k8s.io/kustomize/k8sdeps"
|
||||
"sigs.k8s.io/kustomize/pkg/commands"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
|
||||
if err := commands.NewDefaultCommand(k8sdeps.NewFactory()).Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package build
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -28,13 +27,11 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/target"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
)
|
||||
|
||||
type buildOptions struct {
|
||||
kustomizationPath string
|
||||
outputPath string
|
||||
transformerconfigPaths []string
|
||||
kustomizationPath string
|
||||
outputPath string
|
||||
}
|
||||
|
||||
var examples = `
|
||||
@@ -50,11 +47,6 @@ url examples:
|
||||
sigs.k8s.io/kustomize//examples/multibases?ref=v1.0.6
|
||||
github.com/Liujingfang1/mysql
|
||||
github.com/Liujingfang1/kustomize//examples/helloWorld?ref=repoUrl2
|
||||
|
||||
Advanced usage:
|
||||
Use different transformer configurations by passing files to kustomize
|
||||
build somedir -t someconfigdir
|
||||
build somedir -t some-transformer-configfile,another-transformer-configfile
|
||||
`
|
||||
|
||||
// NewCmdBuild creates a new build command.
|
||||
@@ -63,7 +55,6 @@ func NewCmdBuild(
|
||||
rf *resmap.Factory,
|
||||
ptf transformer.Factory) *cobra.Command {
|
||||
var o buildOptions
|
||||
var p string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build [path]",
|
||||
@@ -71,7 +62,7 @@ func NewCmdBuild(
|
||||
Example: examples,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args, p, fs)
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -82,15 +73,11 @@ func NewCmdBuild(
|
||||
&o.outputPath,
|
||||
"output", "o", "",
|
||||
"If specified, write the build output to this path.")
|
||||
cmd.Flags().StringVarP(
|
||||
&p,
|
||||
"transformer-config", "t", "",
|
||||
"If specified, use the transformer configs load from these files.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates build command.
|
||||
func (o *buildOptions) Validate(args []string, p string, fs fs.FileSystem) error {
|
||||
func (o *buildOptions) Validate(args []string) error {
|
||||
if len(args) > 1 {
|
||||
return errors.New("specify one path to " + constants.KustomizationFileName)
|
||||
}
|
||||
@@ -100,41 +87,19 @@ func (o *buildOptions) Validate(args []string, p string, fs fs.FileSystem) error
|
||||
o.kustomizationPath = args[0]
|
||||
}
|
||||
|
||||
if p == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fs.IsDir(p) {
|
||||
paths, err := fs.Glob(p + "/*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.transformerconfigPaths = paths
|
||||
} else {
|
||||
o.transformerconfigPaths = strings.Split(p, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunBuild runs build command.
|
||||
func (o *buildOptions) RunBuild(
|
||||
out io.Writer, fSys fs.FileSystem,
|
||||
rf *resmap.Factory,
|
||||
ptf transformer.Factory) error {
|
||||
rootLoader, err := loader.NewLoader(o.kustomizationPath, "", fSys)
|
||||
rf *resmap.Factory, ptf transformer.Factory) error {
|
||||
ldr, err := loader.NewLoader(o.kustomizationPath, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tc, err := makeTransformerconfig(fSys, o.transformerconfigPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rootLoader.Cleanup()
|
||||
kt, err := target.NewKustTarget(
|
||||
rootLoader, fSys,
|
||||
rf,
|
||||
ptf, tc)
|
||||
defer ldr.Cleanup()
|
||||
kt, err := target.NewKustTarget(ldr, fSys, rf, ptf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -153,18 +118,3 @@ func (o *buildOptions) RunBuild(
|
||||
_, err = out.Write(res)
|
||||
return err
|
||||
}
|
||||
|
||||
// makeTransformerConfig returns a complete TransformerConfig object from either files
|
||||
// or the default configs
|
||||
func makeTransformerconfig(
|
||||
fSys fs.FileSystem, paths []string) (*config.TransformerConfig, error) {
|
||||
if paths == nil || len(paths) == 0 {
|
||||
return config.NewFactory(nil).DefaultConfig(), nil
|
||||
}
|
||||
ldr, err := loader.NewLoader(".", "", fSys)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(
|
||||
err, "cannot create transformer configuration loader")
|
||||
}
|
||||
return config.NewFactory(ldr).FromFiles(paths)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestBuildValidate(t *testing.T) {
|
||||
}
|
||||
for _, mycase := range cases {
|
||||
opts := buildOptions{}
|
||||
e := opts.Validate(mycase.args, "", nil)
|
||||
e := opts.Validate(mycase.args)
|
||||
if len(mycase.erMsg) > 0 {
|
||||
if e == nil {
|
||||
t.Errorf("%s: Expected an error %v", mycase.name, mycase.erMsg)
|
||||
|
||||
@@ -2,3 +2,4 @@ resources:
|
||||
- serviceaccount.yaml
|
||||
- rolebinding.yaml
|
||||
namePrefix: base-
|
||||
nameSuffix: -suffix
|
||||
|
||||
@@ -2,6 +2,7 @@ bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: a-
|
||||
nameSuffix: -suffixA
|
||||
|
||||
resources:
|
||||
- serviceaccount.yaml
|
||||
|
||||
@@ -2,3 +2,4 @@ bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: b-
|
||||
nameSuffix: -suffixB
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
description: multibases with name reference
|
||||
args: []
|
||||
filename: testdata/testcase-multibases-conflict/combined
|
||||
expectedError: detected conflicts when resolving name references serviceaccount
|
||||
expectedError: Multiple matches for name noGroup_v1_ServiceAccount
|
||||
|
||||
@@ -2,3 +2,4 @@ resources:
|
||||
- serviceaccount.yaml
|
||||
- rolebinding.yaml
|
||||
namePrefix: base-
|
||||
nameSuffix: -suffix
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: a-base-serviceaccount
|
||||
name: a-base-serviceaccount-suffix-suffixA
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: b-base-serviceaccount
|
||||
name: b-base-serviceaccount-suffix-suffixB
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: a-base-rolebinding
|
||||
name: a-base-rolebinding-suffix-suffixA
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: a-base-serviceaccount
|
||||
name: a-base-serviceaccount-suffix-suffixA
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: b-base-rolebinding
|
||||
name: b-base-rolebinding-suffix-suffixB
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: b-base-serviceaccount
|
||||
name: b-base-serviceaccount-suffix-suffixB
|
||||
|
||||
@@ -2,3 +2,4 @@ bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: a-
|
||||
nameSuffix: -suffixA
|
||||
|
||||
@@ -2,3 +2,4 @@ bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: b-
|
||||
nameSuffix: -suffixB
|
||||
|
||||
@@ -77,16 +77,10 @@ metadata:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/path: _status/vars
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
name: dev-base-cockroachdb-public
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 26257
|
||||
@@ -100,10 +94,16 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/path: _status/vars
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb-public
|
||||
name: dev-base-cockroachdb
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 26257
|
||||
|
||||
@@ -67,7 +67,7 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
|
||||
}
|
||||
|
||||
// Add the flagsAndArgs map to the kustomization file.
|
||||
kf.Set(fSys, loader.NewFileLoader(fSys))
|
||||
kf.Set(fSys, loader.NewFileLoaderAtCwd(fSys))
|
||||
err = addConfigMap(kustomization, flagsAndArgs, kf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -36,6 +36,9 @@ func NewCmdEdit(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory
|
||||
|
||||
# Sets the nameprefix field
|
||||
kustomize edit set nameprefix <prefix-value>
|
||||
|
||||
# Sets the namesuffix field
|
||||
kustomize edit set namesuffix <suffix-value>
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
|
||||
@@ -31,12 +31,16 @@ func NewCmdSet(fsys fs.FileSystem, v ifc.Validator) *cobra.Command {
|
||||
Example: `
|
||||
# Sets the nameprefix field
|
||||
kustomize edit set nameprefix <prefix-value>
|
||||
|
||||
# Sets the namesuffix field
|
||||
kustomize edit set namesuffix <suffix-value>
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
|
||||
c.AddCommand(
|
||||
newCmdSetNamePrefix(fsys),
|
||||
newCmdSetNameSuffix(fsys),
|
||||
newCmdSetNamespace(fsys, v),
|
||||
newCmdSetImageTag(fsys),
|
||||
)
|
||||
|
||||
86
pkg/commands/edit/set/set_name_suffix.go
Normal file
86
pkg/commands/edit/set/set_name_suffix.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package set
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type setNameSuffixOptions struct {
|
||||
suffix string
|
||||
}
|
||||
|
||||
// newCmdSetNameSuffix sets the value of the nameSuffix field in the kustomization.
|
||||
func newCmdSetNameSuffix(fsys fs.FileSystem) *cobra.Command {
|
||||
var o setNameSuffixOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "namesuffix",
|
||||
Short: "Sets the value of the nameSuffix field in the kustomization file.",
|
||||
Example: `
|
||||
The command
|
||||
set namesuffix -- -acme
|
||||
will add the field "nameSuffix: -acme" to the kustomization file if it doesn't exist,
|
||||
and overwrite the value with "-acme" if the field does exist.
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.Complete(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunSetNameSuffix(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates setNameSuffix command.
|
||||
func (o *setNameSuffixOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("must specify exactly one suffix value")
|
||||
}
|
||||
// TODO: add further validation on the value.
|
||||
o.suffix = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes setNameSuffix command.
|
||||
func (o *setNameSuffixOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunSetNameSuffix runs setNameSuffix command (does real work).
|
||||
func (o *setNameSuffixOptions) RunSetNameSuffix(fSys fs.FileSystem) error {
|
||||
mf, err := kustfile.NewKustomizationFile(fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.NameSuffix = o.suffix
|
||||
return mf.Write(m)
|
||||
}
|
||||
60
pkg/commands/edit/set/set_name_suffix_test.go
Normal file
60
pkg/commands/edit/set/set_name_suffix_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package set
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
goodSuffixValue = "-acme"
|
||||
)
|
||||
|
||||
func TestSetNameSuffixHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteTestKustomization()
|
||||
|
||||
cmd := newCmdSetNameSuffix(fakeFS)
|
||||
args := []string{goodSuffixValue}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadTestKustomization()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(content), goodSuffixValue) {
|
||||
t.Errorf("expected suffix value in kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNameSuffixNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdSetNameSuffix(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify exactly one suffix value" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -55,6 +54,7 @@ func determineFieldOrder() []string {
|
||||
"Resources",
|
||||
"Bases",
|
||||
"NamePrefix",
|
||||
"NameSuffix",
|
||||
"Namespace",
|
||||
"Crds",
|
||||
"CommonLabels",
|
||||
@@ -67,6 +67,7 @@ func determineFieldOrder() []string {
|
||||
"GeneratorOptions",
|
||||
"Vars",
|
||||
"ImageTags",
|
||||
"Configurations",
|
||||
}
|
||||
|
||||
// Add deprecated fields here.
|
||||
@@ -118,7 +119,7 @@ type kustomizationFile struct {
|
||||
|
||||
// NewKustomizationFile returns a new instance.
|
||||
func NewKustomizationFile(fSys fs.FileSystem) (*kustomizationFile, error) { // nolint
|
||||
mf := &kustomizationFile{path: constants.KustomizationFileName, fSys: fSys}
|
||||
mf := &kustomizationFile{fSys: fSys}
|
||||
err := mf.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -127,19 +128,16 @@ func NewKustomizationFile(fSys fs.FileSystem) (*kustomizationFile, error) { // n
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) validate() error {
|
||||
if !mf.fSys.Exists(mf.path) {
|
||||
return fmt.Errorf("Missing kustomization file '%s'.\n", mf.path)
|
||||
}
|
||||
if mf.fSys.IsDir(mf.path) {
|
||||
mf.path = path.Join(mf.path, constants.KustomizationFileName)
|
||||
if !mf.fSys.Exists(mf.path) {
|
||||
return fmt.Errorf("Missing kustomization file '%s'.\n", mf.path)
|
||||
}
|
||||
if mf.fSys.Exists(constants.KustomizationFileName) {
|
||||
mf.path = constants.KustomizationFileName
|
||||
} else if mf.fSys.Exists(constants.SecondaryKustomizationFileName) {
|
||||
mf.path = constants.SecondaryKustomizationFileName
|
||||
} else {
|
||||
if !strings.HasSuffix(mf.path, constants.KustomizationFileName) {
|
||||
return fmt.Errorf("Kustomization file path (%s) should have %s suffix\n",
|
||||
mf.path, constants.KustomizationFileSuffix)
|
||||
}
|
||||
return fmt.Errorf("Missing kustomization file '%s'.\n", constants.KustomizationFileName)
|
||||
}
|
||||
|
||||
if mf.fSys.IsDir(mf.path) {
|
||||
return fmt.Errorf("%s should be a file", mf.path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
@@ -32,6 +33,7 @@ func TestFieldOrder(t *testing.T) {
|
||||
"Resources",
|
||||
"Bases",
|
||||
"NamePrefix",
|
||||
"NameSuffix",
|
||||
"Namespace",
|
||||
"Crds",
|
||||
"CommonLabels",
|
||||
@@ -43,6 +45,7 @@ func TestFieldOrder(t *testing.T) {
|
||||
"GeneratorOptions",
|
||||
"Vars",
|
||||
"ImageTags",
|
||||
"Configurations",
|
||||
}
|
||||
actual := determineFieldOrder()
|
||||
if len(expected) != len(actual) {
|
||||
@@ -84,6 +87,7 @@ func TestWriteAndRead(t *testing.T) {
|
||||
func TestDeprecationOfPatches(t *testing.T) {
|
||||
hasDeprecatedFields := []byte(`
|
||||
namePrefix: acme
|
||||
nameSuffix: emca
|
||||
patches:
|
||||
- alice
|
||||
patchesStrategicMerge:
|
||||
@@ -102,6 +106,9 @@ patchesStrategicMerge:
|
||||
if k.NamePrefix != "acme" {
|
||||
t.Fatalf("Unexpected name prefix")
|
||||
}
|
||||
if k.NameSuffix != "emca" {
|
||||
t.Fatalf("Unexpected name suffix")
|
||||
}
|
||||
if len(k.Patches) > 0 {
|
||||
t.Fatalf("Expected nothing in Patches.")
|
||||
}
|
||||
@@ -141,6 +148,25 @@ func TestNewNotExist(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecondarySuffix(t *testing.T) {
|
||||
kcontent := `
|
||||
configMapGenerator:
|
||||
- literals:
|
||||
- foo=bar
|
||||
- baz=qux
|
||||
name: my-configmap
|
||||
`
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.SecondaryKustomizationFileName, []byte(kcontent))
|
||||
k, err := NewKustomizationFile(fakeFS)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
if k.path != constants.SecondaryKustomizationFileName {
|
||||
t.Fatalf("Load incorrect file path %s", k.path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreserveComments(t *testing.T) {
|
||||
kustomizationContentWithComments := []byte(
|
||||
`# shem qing some comments
|
||||
@@ -221,7 +247,7 @@ patchesStrategicMerge:
|
||||
- pod.yaml
|
||||
# generator options
|
||||
generatorOptions:
|
||||
disableHash: true
|
||||
disableNameSuffixHash: true
|
||||
`)
|
||||
|
||||
expected := []byte(`
|
||||
@@ -259,7 +285,7 @@ patchesStrategicMerge:
|
||||
- pod.yaml
|
||||
# generator options
|
||||
generatorOptions:
|
||||
disableHash: true
|
||||
disableNameSuffixHash: true
|
||||
`)
|
||||
fSys := fs.MakeFakeFS()
|
||||
fSys.WriteTestKustomizationWith(kustomizationContentWithComments)
|
||||
|
||||
@@ -20,5 +20,12 @@ package constants
|
||||
// KustomizationFileSuffix is expected suffix for KustomizationFileName.
|
||||
const KustomizationFileSuffix = ".yaml"
|
||||
|
||||
// SecondaryKustomizationFileSuffix is the second expected suffix when KustomizationFileSuffix is not found
|
||||
const SecondaryKustomizationFileSuffix = ".yml"
|
||||
|
||||
// KustomizationFileName is the Well-Known File Name for a kustomize configuration file.
|
||||
const KustomizationFileName = "kustomization" + KustomizationFileSuffix
|
||||
|
||||
// SecondaryKustomizationFileName is the secondary File Name for a kustomize configuration file when
|
||||
// KustomizationFileName is not found
|
||||
const SecondaryKustomizationFileName = "kustomization" + SecondaryKustomizationFileSuffix
|
||||
|
||||
@@ -19,24 +19,29 @@ package fs
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
)
|
||||
|
||||
var _ FileSystem = &FakeFS{}
|
||||
var _ FileSystem = &fakeFs{}
|
||||
|
||||
// FakeFS implements FileSystem using a fake in-memory filesystem.
|
||||
type FakeFS struct {
|
||||
// fakeFs implements FileSystem using a fake in-memory filesystem.
|
||||
type fakeFs struct {
|
||||
m map[string]*FakeFile
|
||||
}
|
||||
|
||||
// MakeFakeFS returns an instance of FakeFS with no files in it.
|
||||
func MakeFakeFS() *FakeFS {
|
||||
return &FakeFS{m: map[string]*FakeFile{}}
|
||||
// MakeFakeFS returns an instance of fakeFs with no files in it.
|
||||
func MakeFakeFS() *fakeFs {
|
||||
result := &fakeFs{m: map[string]*FakeFile{}}
|
||||
result.Mkdir("/")
|
||||
return result
|
||||
}
|
||||
|
||||
// kustomizationContent is used in tests.
|
||||
const kustomizationContent = `namePrefix: some-prefix
|
||||
nameSuffix: some-suffix
|
||||
# Labels to add to all objects and selectors.
|
||||
# These labels would also be used to form the selector for apply --prune
|
||||
# Named differently than “labels” to avoid confusion with metadata for this object
|
||||
@@ -54,7 +59,7 @@ secretGenerator: []
|
||||
`
|
||||
|
||||
// Create assures a fake file appears in the in-memory file system.
|
||||
func (fs *FakeFS) Create(name string) (File, error) {
|
||||
func (fs *fakeFs) Create(name string) (File, error) {
|
||||
f := &FakeFile{}
|
||||
f.open = true
|
||||
fs.m[name] = f
|
||||
@@ -62,18 +67,33 @@ func (fs *FakeFS) Create(name string) (File, error) {
|
||||
}
|
||||
|
||||
// Mkdir assures a fake directory appears in the in-memory file system.
|
||||
func (fs *FakeFS) Mkdir(name string) error {
|
||||
func (fs *fakeFs) Mkdir(name string) error {
|
||||
fs.m[name] = makeDir(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MkdirAll delegates to Mkdir
|
||||
func (fs *FakeFS) MkdirAll(name string) error {
|
||||
func (fs *fakeFs) MkdirAll(name string) error {
|
||||
return fs.Mkdir(name)
|
||||
}
|
||||
|
||||
// RemoveAll presumably does rm -r on a path.
|
||||
// There's no error.
|
||||
func (fs *fakeFs) RemoveAll(name string) error {
|
||||
var toRemove []string
|
||||
for k := range fs.m {
|
||||
if strings.HasPrefix(k, name) {
|
||||
toRemove = append(toRemove, k)
|
||||
}
|
||||
}
|
||||
for _, k := range toRemove {
|
||||
delete(fs.m, k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open returns a fake file in the open state.
|
||||
func (fs *FakeFS) Open(name string) (File, error) {
|
||||
func (fs *fakeFs) Open(name string) (File, error) {
|
||||
if _, found := fs.m[name]; !found {
|
||||
return nil, fmt.Errorf("file %q cannot be opened", name)
|
||||
}
|
||||
@@ -81,13 +101,13 @@ func (fs *FakeFS) Open(name string) (File, error) {
|
||||
}
|
||||
|
||||
// Exists returns true if file is known.
|
||||
func (fs *FakeFS) Exists(name string) bool {
|
||||
func (fs *fakeFs) Exists(name string) bool {
|
||||
_, found := fs.m[name]
|
||||
return found
|
||||
}
|
||||
|
||||
// Glob returns the list of matching files
|
||||
func (fs *FakeFS) Glob(pattern string) ([]string, error) {
|
||||
func (fs *fakeFs) Glob(pattern string) ([]string, error) {
|
||||
var result []string
|
||||
for p := range fs.m {
|
||||
if fs.pathMatch(p, pattern) {
|
||||
@@ -99,28 +119,36 @@ func (fs *FakeFS) Glob(pattern string) ([]string, error) {
|
||||
}
|
||||
|
||||
// IsDir returns true if the file exists and is a directory.
|
||||
func (fs *FakeFS) IsDir(name string) bool {
|
||||
func (fs *fakeFs) IsDir(name string) bool {
|
||||
f, found := fs.m[name]
|
||||
if !found {
|
||||
return false
|
||||
if found && f.dir {
|
||||
return true
|
||||
}
|
||||
return f.dir
|
||||
if !strings.HasSuffix(name, "/") {
|
||||
name = name + "/"
|
||||
}
|
||||
for k := range fs.m {
|
||||
if strings.HasPrefix(k, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadFile always returns an empty bytes and error depending on content of m.
|
||||
func (fs *FakeFS) ReadFile(name string) ([]byte, error) {
|
||||
func (fs *fakeFs) ReadFile(name string) ([]byte, error) {
|
||||
if ff, found := fs.m[name]; found {
|
||||
return ff.content, nil
|
||||
}
|
||||
return nil, fmt.Errorf("cannot read file %q", name)
|
||||
}
|
||||
|
||||
func (fs *FakeFS) ReadTestKustomization() ([]byte, error) {
|
||||
func (fs *fakeFs) ReadTestKustomization() ([]byte, error) {
|
||||
return fs.ReadFile(constants.KustomizationFileName)
|
||||
}
|
||||
|
||||
// WriteFile always succeeds and does nothing.
|
||||
func (fs *FakeFS) WriteFile(name string, c []byte) error {
|
||||
func (fs *fakeFs) WriteFile(name string, c []byte) error {
|
||||
ff := &FakeFile{}
|
||||
ff.Write(c)
|
||||
fs.m[name] = ff
|
||||
@@ -128,16 +156,16 @@ func (fs *FakeFS) WriteFile(name string, c []byte) error {
|
||||
}
|
||||
|
||||
// WriteTestKustomization writes a standard test file.
|
||||
func (fs *FakeFS) WriteTestKustomization() {
|
||||
func (fs *fakeFs) WriteTestKustomization() {
|
||||
fs.WriteTestKustomizationWith([]byte(kustomizationContent))
|
||||
}
|
||||
|
||||
// WriteTestKustomizationWith writes a standard test file.
|
||||
func (fs *FakeFS) WriteTestKustomizationWith(bytes []byte) {
|
||||
func (fs *fakeFs) WriteTestKustomizationWith(bytes []byte) {
|
||||
fs.WriteFile(constants.KustomizationFileName, bytes)
|
||||
}
|
||||
|
||||
func (fs *FakeFS) pathMatch(path, pattern string) bool {
|
||||
func (fs *fakeFs) pathMatch(path, pattern string) bool {
|
||||
match, _ := filepath.Match(pattern, path)
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ func TestExists(t *testing.T) {
|
||||
if x.Exists("foo") {
|
||||
t.Fatalf("expected no foo")
|
||||
}
|
||||
x.Mkdir("/")
|
||||
if !x.IsDir("/") {
|
||||
t.Fatalf("expected dir at /")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDir(t *testing.T) {
|
||||
@@ -36,14 +40,62 @@ func TestIsDir(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !x.Exists(expectedName) {
|
||||
t.Fatalf(expectedName + " should exist")
|
||||
}
|
||||
shouldExist(t, x, expectedName)
|
||||
if !x.IsDir(expectedName) {
|
||||
t.Fatalf(expectedName + " should be a dir")
|
||||
}
|
||||
}
|
||||
|
||||
func shouldExist(t *testing.T, fs FileSystem, name string) {
|
||||
if !fs.Exists(name) {
|
||||
t.Fatalf(name + " should exist")
|
||||
}
|
||||
}
|
||||
|
||||
func shouldNotExist(t *testing.T, fs FileSystem, name string) {
|
||||
if fs.Exists(name) {
|
||||
t.Fatalf(name + " should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAll(t *testing.T) {
|
||||
x := MakeFakeFS()
|
||||
x.WriteFile("/foo/project/file.yaml", []byte("Unused"))
|
||||
x.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused"))
|
||||
x.WriteFile("/foo/apple/subdir/file.yaml", []byte("Unused"))
|
||||
shouldExist(t, x, "/foo/project/file.yaml")
|
||||
shouldExist(t, x, "/foo/project/subdir/file.yaml")
|
||||
shouldExist(t, x, "/foo/apple/subdir/file.yaml")
|
||||
err := x.RemoveAll("/foo/project")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
shouldNotExist(t, x, "/foo/project/file.yaml")
|
||||
shouldNotExist(t, x, "/foo/project/subdir/file.yaml")
|
||||
shouldExist(t, x, "/foo/apple/subdir/file.yaml")
|
||||
}
|
||||
|
||||
func TestIsDirDeeper(t *testing.T) {
|
||||
x := MakeFakeFS()
|
||||
x.WriteFile("/foo/project/file.yaml", []byte("Unused"))
|
||||
x.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused"))
|
||||
if !x.IsDir("/") {
|
||||
t.Fatalf("/ should be a dir")
|
||||
}
|
||||
if !x.IsDir("/foo") {
|
||||
t.Fatalf("/foo should be a dir")
|
||||
}
|
||||
if !x.IsDir("/foo/project") {
|
||||
t.Fatalf("/foo/project should be a dir")
|
||||
}
|
||||
if x.IsDir("/fo") {
|
||||
t.Fatalf("/fo should not be a dir")
|
||||
}
|
||||
if x.IsDir("/x") {
|
||||
t.Fatalf("/x should not be a dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
x := MakeFakeFS()
|
||||
f, err := x.Create("foo")
|
||||
@@ -53,9 +105,7 @@ func TestCreate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error")
|
||||
}
|
||||
if !x.Exists("foo") {
|
||||
t.Fatalf("expected foo to exist")
|
||||
}
|
||||
shouldExist(t, x, "foo")
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
|
||||
@@ -27,6 +27,7 @@ type FileSystem interface {
|
||||
Create(name string) (File, error)
|
||||
Mkdir(name string) error
|
||||
MkdirAll(name string) error
|
||||
RemoveAll(name string) error
|
||||
Open(name string) (File, error)
|
||||
IsDir(name string) bool
|
||||
Exists(name string) bool
|
||||
|
||||
@@ -45,6 +45,11 @@ func (realFS) MkdirAll(name string) error {
|
||||
return os.MkdirAll(name, 0777|os.ModeDir)
|
||||
}
|
||||
|
||||
// RemoveAll delegates to os.RemoveAll.
|
||||
func (realFS) RemoveAll(name string) error {
|
||||
return os.RemoveAll(name)
|
||||
}
|
||||
|
||||
// Open delegates to os.Open.
|
||||
func (realFS) Open(name string) (File, error) { return os.Open(name) }
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ func (x Gvk) Equals(o Gvk) bool {
|
||||
// In some cases order just specified to provide determinism.
|
||||
var order = []string{
|
||||
"Namespace",
|
||||
"StorageClass",
|
||||
"CustomResourceDefinition",
|
||||
"ServiceAccount",
|
||||
"Role",
|
||||
@@ -97,7 +98,9 @@ func (x Gvk) IsLessThan(o Gvk) bool {
|
||||
indexI, foundI := typeOrders[x.Kind]
|
||||
indexJ, foundJ := typeOrders[o.Kind]
|
||||
if foundI && foundJ {
|
||||
return indexI < indexJ
|
||||
if indexI != indexJ {
|
||||
return indexI < indexJ
|
||||
}
|
||||
}
|
||||
if foundI && !foundJ {
|
||||
return true
|
||||
|
||||
@@ -48,6 +48,16 @@ var lessThanTests = []struct {
|
||||
Gvk{Group: "a", Version: "b", Kind: "ClusterRole"}},
|
||||
{Gvk{Group: "a", Version: "b", Kind: "a"},
|
||||
Gvk{Group: "a", Version: "b", Kind: "b"}},
|
||||
{Gvk{Group: "a", Version: "b", Kind: "Namespace"},
|
||||
Gvk{Group: "a", Version: "c", Kind: "Namespace"}},
|
||||
{Gvk{Group: "a", Version: "c", Kind: "Namespace"},
|
||||
Gvk{Group: "b", Version: "c", Kind: "Namespace"}},
|
||||
{Gvk{Group: "b", Version: "c", Kind: "Namespace"},
|
||||
Gvk{Group: "a", Version: "c", Kind: "ClusterRole"}},
|
||||
{Gvk{Group: "a", Version: "c", Kind: "Namespace"},
|
||||
Gvk{Group: "a", Version: "b", Kind: "ClusterRole"}},
|
||||
{Gvk{Group: "a", Version: "d", Kind: "Namespace"},
|
||||
Gvk{Group: "b", Version: "c", Kind: "Namespace"}},
|
||||
}
|
||||
|
||||
func TestIsLessThan1(t *testing.T) {
|
||||
@@ -55,6 +65,9 @@ func TestIsLessThan1(t *testing.T) {
|
||||
if !hey.x1.IsLessThan(hey.x2) {
|
||||
t.Fatalf("%v should be less than %v", hey.x1, hey.x2)
|
||||
}
|
||||
if hey.x2.IsLessThan(hey.x1) {
|
||||
t.Fatalf("%v should not be less than %v", hey.x2, hey.x1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
package loadertest
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
@@ -29,15 +30,17 @@ type FakeLoader struct {
|
||||
delegate ifc.Loader
|
||||
}
|
||||
|
||||
// NewFakeLoader returns a Loader that delegates calls, and encapsulates
|
||||
// a fake file system that the Loader reads from. "initialDir" parameter
|
||||
// must be an full, absolute directory (trailing slash doesn't matter).
|
||||
// NewFakeLoader returns a Loader that uses a fake filesystem.
|
||||
// The argument should be an absolute file path.
|
||||
func NewFakeLoader(initialDir string) FakeLoader {
|
||||
// Create fake filesystem and inject it into initial Loader.
|
||||
fakefs := fs.MakeFakeFS()
|
||||
rootLoader := loader.NewFileLoader(fakefs)
|
||||
ldr, _ := rootLoader.New(initialDir)
|
||||
return FakeLoader{fs: fakefs, delegate: ldr}
|
||||
fSys := fs.MakeFakeFS()
|
||||
fSys.Mkdir(initialDir)
|
||||
ldr, err := loader.NewLoader(initialDir, fSys)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to make loader: %v", err)
|
||||
}
|
||||
return FakeLoader{fs: fSys, delegate: ldr}
|
||||
}
|
||||
|
||||
// AddFile adds a fake file to the file system.
|
||||
|
||||
@@ -18,93 +18,207 @@ package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
)
|
||||
|
||||
const currentDir = "."
|
||||
// TODO: 2018/Nov/20 remove this before next release.
|
||||
// Leave only the true path. Retaining only for
|
||||
// quick revert.
|
||||
const enforceRelocatable = true
|
||||
|
||||
// fileLoader loads files from a file system.
|
||||
// fileLoader loads files, returning an array of bytes.
|
||||
// It also enforces two kustomization requirements:
|
||||
//
|
||||
// 1) relocatable
|
||||
//
|
||||
// A kustomization and the resources, bases,
|
||||
// patches, etc. that it depends on should be
|
||||
// relocatable, so all path specifications
|
||||
// must be relative, not absolute. The paths
|
||||
// are taken relative to the location of the
|
||||
// kusttomization file.
|
||||
//
|
||||
// 2) acyclic
|
||||
//
|
||||
// There should be no cycles in overlay to base
|
||||
// relationships, including no cycles between
|
||||
// git repositories.
|
||||
//
|
||||
// The loader has a notion of a current working directory
|
||||
// (CWD), called 'root', that is independent of the CWD
|
||||
// of the process. When `Load` is called with a file path
|
||||
// argument, the load is done relative to this root,
|
||||
// not relative to the process CWD.
|
||||
//
|
||||
// The loader offers a `New` method returning a new loader
|
||||
// with a new root. The new root can be one of two things,
|
||||
// a remote git repo URL, or a directory specified relative
|
||||
// to the current root. In the former case, the remote
|
||||
// repository is locally cloned, and the new loader is
|
||||
// rooted on a path in that clone.
|
||||
//
|
||||
// Crucially, a root history is used to so that New fails
|
||||
// if its argument either matches or is a parent of the
|
||||
// current or any previously used root.
|
||||
//
|
||||
// This disallows:
|
||||
//
|
||||
// * A base that is a git repository that, in turn,
|
||||
// specifies a base repository seen previously
|
||||
// in the loading process (a cycle).
|
||||
//
|
||||
// * An overlay depending on a base positioned at or
|
||||
// above it. I.e. '../foo' is OK, but '.', '..',
|
||||
// '../..', etc. are disallowed. Allowing such a
|
||||
// base has no advantages and encourage cycles,
|
||||
// particularly if some future change were to
|
||||
// introduce globbing to file specifications in
|
||||
// the kustomization file.
|
||||
//
|
||||
type fileLoader struct {
|
||||
root string
|
||||
// Previously visited directories, tracked to
|
||||
// avoid cycles. The last entry is the current root.
|
||||
roots []string
|
||||
// File system utilities.
|
||||
fSys fs.FileSystem
|
||||
// Used to clone repositories.
|
||||
cloner gitCloner
|
||||
// Used to clean up, as needed.
|
||||
cleaner func() error
|
||||
}
|
||||
|
||||
// NewFileLoader returns a new fileLoader.
|
||||
func NewFileLoader(fSys fs.FileSystem) *fileLoader {
|
||||
return newFileLoaderAtRoot("", fSys)
|
||||
// NewFileLoaderAtCwd returns a loader that loads from ".".
|
||||
func NewFileLoaderAtCwd(fSys fs.FileSystem) *fileLoader {
|
||||
return newLoaderOrDie(fSys, ".")
|
||||
}
|
||||
|
||||
// newFileLoaderAtRoot returns a new fileLoader with given root.
|
||||
func newFileLoaderAtRoot(root string, fs fs.FileSystem) *fileLoader {
|
||||
return &fileLoader{root: root, fSys: fs}
|
||||
// NewFileLoaderAtRoot returns a loader that loads from "/".
|
||||
func NewFileLoaderAtRoot(fSys fs.FileSystem) *fileLoader {
|
||||
return newLoaderOrDie(fSys, "/")
|
||||
}
|
||||
|
||||
// Root returns the root location for this Loader.
|
||||
// Root returns the absolute path that is prepended to any
|
||||
// relative paths used in Load.
|
||||
func (l *fileLoader) Root() string {
|
||||
return l.root
|
||||
return l.roots[len(l.roots)-1]
|
||||
}
|
||||
|
||||
// Returns a new Loader rooted at newRoot. "newRoot" MUST be
|
||||
// a directory (not a file). The directory can have a trailing
|
||||
// slash or not.
|
||||
// Example: "/home/seans/project" or "/home/seans/project/"
|
||||
// NOT "/home/seans/project/file.yaml".
|
||||
func (l *fileLoader) New(newRoot string) (ifc.Loader, error) {
|
||||
return NewLoader(newRoot, l.root, l.fSys)
|
||||
}
|
||||
|
||||
// IsAbsPath return true if the location calculated with the root
|
||||
// and location params a full file path.
|
||||
func (l *fileLoader) IsAbsPath(root string, location string) bool {
|
||||
fullFilePath, err := l.fullLocation(root, location)
|
||||
func newLoaderOrDie(fSys fs.FileSystem, path string) *fileLoader {
|
||||
l, err := newFileLoaderAt(
|
||||
path, fSys, []string{}, simpleGitCloner)
|
||||
if err != nil {
|
||||
return false
|
||||
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
||||
}
|
||||
return filepath.IsAbs(fullFilePath)
|
||||
return l
|
||||
}
|
||||
|
||||
// fullLocation returns some notion of a full path.
|
||||
// If location is a full file path, then ignore root. If location is relative, then
|
||||
// join the root path with the location path. Either root or location can be empty,
|
||||
// but not both. Special case for ".": Expands to current working directory.
|
||||
// Example: "/home/seans/project", "subdir/bar" -> "/home/seans/project/subdir/bar".
|
||||
func (l *fileLoader) fullLocation(root string, location string) (string, error) {
|
||||
// First, validate the parameters
|
||||
if len(root) == 0 && len(location) == 0 {
|
||||
return "", fmt.Errorf("unable to calculate full location: root and location empty")
|
||||
// newFileLoaderAt returns a new fileLoader with given root.
|
||||
func newFileLoaderAt(
|
||||
root string, fSys fs.FileSystem,
|
||||
roots []string, cloner gitCloner) (*fileLoader, error) {
|
||||
if root == "" {
|
||||
return nil, fmt.Errorf(
|
||||
"loader root cannot be empty")
|
||||
}
|
||||
// Special case current directory, expanding to full file path.
|
||||
if location == currentDir {
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
root, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"absolute path error in '%s' : %v", root, err)
|
||||
}
|
||||
if !fSys.IsDir(root) {
|
||||
return nil, fmt.Errorf("absolute root dir '%s' does not exist", root)
|
||||
}
|
||||
return &fileLoader{
|
||||
roots: append(roots, root),
|
||||
fSys: fSys,
|
||||
cloner: cloner,
|
||||
cleaner: func() error { return nil },
|
||||
}, nil
|
||||
}
|
||||
|
||||
// New returns a new Loader, rooted relative to current loader,
|
||||
// or rooted in a temp directory holding a git repo clone.
|
||||
func (l *fileLoader) New(root string) (ifc.Loader, error) {
|
||||
if root == "" {
|
||||
return nil, fmt.Errorf("new root cannot be empty")
|
||||
}
|
||||
if isRepoUrl(root) {
|
||||
if err := l.seenBefore(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
location = currentDir
|
||||
return newGitLoader(root, l.fSys, l.roots, l.cloner)
|
||||
}
|
||||
// Assume the location is a full file path. If not, then join root with location.
|
||||
fullLocation := location
|
||||
if !filepath.IsAbs(location) {
|
||||
fullLocation = filepath.Join(root, location)
|
||||
if enforceRelocatable && filepath.IsAbs(root) {
|
||||
return nil, fmt.Errorf("new root '%s' cannot be absolute", root)
|
||||
}
|
||||
return fullLocation, nil
|
||||
}
|
||||
|
||||
// Load returns the bytes from reading a file at fullFilePath.
|
||||
// Implements the Loader interface.
|
||||
func (l *fileLoader) Load(location string) ([]byte, error) {
|
||||
fullLocation, err := l.fullLocation(l.root, location)
|
||||
// Get absolute path to squeeze out "..", ".", etc.
|
||||
// to facilitate the seenBefore test.
|
||||
absRoot, err := filepath.Abs(filepath.Join(l.Root(), root))
|
||||
if err != nil {
|
||||
fmt.Printf("Trouble in fulllocation: %v\n", err)
|
||||
return nil, fmt.Errorf(
|
||||
"problem joining '%s' to '%s': %v", l.Root(), root, err)
|
||||
}
|
||||
if err := l.seenBefore(absRoot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.fSys.ReadFile(fullLocation)
|
||||
return newFileLoaderAt(absRoot, l.fSys, l.roots, l.cloner)
|
||||
}
|
||||
|
||||
// Cleanup does nothing
|
||||
func (l *fileLoader) Cleanup() error {
|
||||
// newGitLoader returns a new Loader pinned to a temporary
|
||||
// directory holding a cloned git repo.
|
||||
func newGitLoader(
|
||||
root string, fSys fs.FileSystem,
|
||||
roots []string, cloner gitCloner) (ifc.Loader, error) {
|
||||
tmpDirForRepo, pathInRepo, err := cloner(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trueRoot := filepath.Join(tmpDirForRepo, pathInRepo)
|
||||
if !fSys.IsDir(trueRoot) {
|
||||
return nil, fmt.Errorf(
|
||||
"something wrong cloning '%s'; unable to find '%s'",
|
||||
root, trueRoot)
|
||||
}
|
||||
return &fileLoader{
|
||||
roots: append(roots, root, trueRoot),
|
||||
fSys: fSys,
|
||||
cloner: cloner,
|
||||
cleaner: func() error { return fSys.RemoveAll(tmpDirForRepo) },
|
||||
}, nil
|
||||
}
|
||||
|
||||
// seenBefore tests whether the current or any previously
|
||||
// visited root begins with the given path.
|
||||
func (l *fileLoader) seenBefore(path string) error {
|
||||
for _, r := range l.roots {
|
||||
if strings.HasPrefix(r, path) {
|
||||
return fmt.Errorf(
|
||||
"cycle detected: new root '%s' contains previous root '%s'",
|
||||
path, r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load returns content of file at the given relative path.
|
||||
func (l *fileLoader) Load(path string) ([]byte, error) {
|
||||
if filepath.IsAbs(path) {
|
||||
if enforceRelocatable {
|
||||
return nil, fmt.Errorf(
|
||||
"must use relative path; '%s' is absolute", path)
|
||||
}
|
||||
} else {
|
||||
path = filepath.Join(l.Root(), path)
|
||||
}
|
||||
return l.fSys.ReadFile(path)
|
||||
}
|
||||
|
||||
// Cleanup runs the cleaner.
|
||||
func (l *fileLoader) Cleanup() error {
|
||||
return l.cleaner()
|
||||
}
|
||||
|
||||
@@ -17,102 +17,184 @@ limitations under the License.
|
||||
package loader
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestLoader_Root(t *testing.T) {
|
||||
type testData struct {
|
||||
path string
|
||||
expectedContent string
|
||||
}
|
||||
|
||||
// Initialize the fake file system and the root loader.
|
||||
fakefs := fs.MakeFakeFS()
|
||||
fakefs.WriteFile("/home/seans/project/file.yaml", []byte("Unused"))
|
||||
fakefs.WriteFile("/home/seans/project/subdir/file.yaml", []byte("Unused"))
|
||||
fakefs.WriteFile("/home/seans/project2/file.yaml", []byte("Unused"))
|
||||
rootLoader := NewFileLoader(fakefs)
|
||||
var testCases = []testData{
|
||||
{
|
||||
path: "foo/project/fileA.yaml",
|
||||
expectedContent: "fileA content",
|
||||
},
|
||||
{
|
||||
path: "foo/project/subdir1/fileB.yaml",
|
||||
expectedContent: "fileB content",
|
||||
},
|
||||
{
|
||||
path: "foo/project/subdir2/fileC.yaml",
|
||||
expectedContent: "fileC content",
|
||||
},
|
||||
{
|
||||
path: "foo/project/fileD.yaml",
|
||||
expectedContent: "fileD content",
|
||||
},
|
||||
}
|
||||
|
||||
_, err := rootLoader.New("")
|
||||
func MakeFakeFs(td []testData) fs.FileSystem {
|
||||
fSys := fs.MakeFakeFS()
|
||||
for _, x := range td {
|
||||
fSys.WriteFile("/"+x.path, []byte(x.expectedContent))
|
||||
}
|
||||
return fSys
|
||||
}
|
||||
|
||||
func TestLoaderLoad(t *testing.T) {
|
||||
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCases))
|
||||
if "/" != l1.Root() {
|
||||
t.Fatalf("incorrect root: '%s'\n", l1.Root())
|
||||
}
|
||||
for _, x := range testCases {
|
||||
b, err := l1.Load(x.path)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
|
||||
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
|
||||
}
|
||||
}
|
||||
l2, err := l1.New("foo/project")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
if "/foo/project" != l2.Root() {
|
||||
t.Fatalf("incorrect root: %s\n", l2.Root())
|
||||
}
|
||||
for _, x := range testCases {
|
||||
b, err := l2.Load(strings.TrimPrefix(x.path, "foo/project/"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
|
||||
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
|
||||
}
|
||||
}
|
||||
l2, err = l1.New("foo/project/") // Assure trailing slash stripped
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
if "/foo/project" != l2.Root() {
|
||||
t.Fatalf("incorrect root: %s\n", l2.Root())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoaderNewSubDir(t *testing.T) {
|
||||
l1, err := NewFileLoaderAtRoot(MakeFakeFs(testCases)).New("foo/project")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
l2, err := l1.New("subdir1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
if "/foo/project/subdir1" != l2.Root() {
|
||||
t.Fatalf("incorrect root: %s\n", l2.Root())
|
||||
}
|
||||
x := testCases[1]
|
||||
b, err := l2.Load("fileB.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
|
||||
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoaderBadRelative(t *testing.T) {
|
||||
l1, err := NewFileLoaderAtRoot(MakeFakeFs(testCases)).New("foo/project/subdir1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
if "/foo/project/subdir1" != l1.Root() {
|
||||
t.Fatalf("incorrect root: %s\n", l1.Root())
|
||||
}
|
||||
|
||||
// Cannot cd into a file.
|
||||
l2, err := l1.New("fileB.yaml")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's not okay to stay at the same place.
|
||||
l2, err = l1.New(".")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's not okay to go up and back down into same place.
|
||||
l2, err = l1.New("../subdir1")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's not okay to go up via a relative path.
|
||||
l2, err = l1.New("..")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's not okay to go up via an absolute path.
|
||||
l2, err = l1.New("/foo/project")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's not okay to go to the root.
|
||||
l2, err = l1.New("/")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l2.Root())
|
||||
}
|
||||
|
||||
// It's okay to go up and down to a sibling.
|
||||
l2, err = l1.New("../subdir2")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected new error %v", err)
|
||||
}
|
||||
if "/foo/project/subdir2" != l2.Root() {
|
||||
t.Fatalf("incorrect root: %s\n", l2.Root())
|
||||
}
|
||||
x := testCases[2]
|
||||
b, err := l2.Load("fileC.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected load error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte(x.expectedContent), b) {
|
||||
t.Fatalf("in load expected %s, but got %s", x.expectedContent, b)
|
||||
}
|
||||
|
||||
// It's not OK to go over to a previously visited directory.
|
||||
// Must disallow going back and forth in a cycle.
|
||||
l1, err = l2.New("../subdir1")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, but got root %s", l1.Root())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoaderMisc(t *testing.T) {
|
||||
l := NewFileLoaderAtRoot(MakeFakeFs(testCases))
|
||||
_, err := l.New("")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for empty root location not returned")
|
||||
}
|
||||
_, err = rootLoader.New("https://google.com/project")
|
||||
_, err = l.New("https://google.com/project")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error")
|
||||
}
|
||||
|
||||
// Test with trailing slash in directory.
|
||||
loader, err := rootLoader.New("/home/seans/project/")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected in New(): %v\n", err)
|
||||
}
|
||||
if "/home/seans/project" != loader.Root() {
|
||||
t.Fatalf("Incorrect Loader Root: %s\n", loader.Root())
|
||||
}
|
||||
|
||||
subLoader, err := loader.New("subdir")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected in New(): %v\n", err)
|
||||
}
|
||||
if "/home/seans/project/subdir" != subLoader.Root() {
|
||||
t.Fatalf("Incorrect Loader Root: %s\n", subLoader.Root())
|
||||
}
|
||||
|
||||
// Test without trailing slash in directory.
|
||||
anotherLoader, err := loader.New("/home/seans/project2")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected in New(): %v\n", err)
|
||||
}
|
||||
if "/home/seans/project2" != anotherLoader.Root() {
|
||||
t.Fatalf("Incorrect Loader Root: %s\n", anotherLoader.Root())
|
||||
}
|
||||
|
||||
// Current directory should be expanded to a full absolute file path.
|
||||
currentDirLoader, err := loader.New(".")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected in New(): %v\n", err)
|
||||
}
|
||||
if !filepath.IsAbs(currentDirLoader.Root()) {
|
||||
t.Fatalf("Incorrect Loader Root: %s\n", currentDirLoader.Root())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoader_Load(t *testing.T) {
|
||||
|
||||
// Initialize the fake file system and the root loader.
|
||||
fakefs := fs.MakeFakeFS()
|
||||
fakefs.WriteFile("/home/seans/project/file.yaml", []byte("This is a yaml file"))
|
||||
fakefs.WriteFile("/home/seans/project/subdir/file.yaml", []byte("Subdirectory file content"))
|
||||
fakefs.WriteFile("/home/seans/project2/file.yaml", []byte("This is another yaml file"))
|
||||
rootLoader := NewFileLoader(fakefs)
|
||||
|
||||
loader, err := rootLoader.New("/home/seans/project")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected in New(): %v\n", err)
|
||||
}
|
||||
fileBytes, err := loader.Load("file.yaml") // Load relative to root location
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error in Load(): %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte("This is a yaml file"), fileBytes) {
|
||||
t.Fatalf("Load failed. Expected %s, but got %s", "This is a yaml file", fileBytes)
|
||||
}
|
||||
|
||||
fileBytes, err = loader.Load("subdir/file.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error in Load(): %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte("Subdirectory file content"), fileBytes) {
|
||||
t.Fatalf("Load failed. Expected %s, but got %s", "Subdirectory file content", fileBytes)
|
||||
}
|
||||
|
||||
fileBytes, err = loader.Load("/home/seans/project2/file.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error in Load(): %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual([]byte("This is another yaml file"), fileBytes) {
|
||||
t.Fatalf("Load failed. Expected %s, but got %s", "This is another yaml file", fileBytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
133
pkg/loader/gitcloner.go
Normal file
133
pkg/loader/gitcloner.go
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// gitCloner is a function that can clone a git repo.
|
||||
type gitCloner func(url string) (
|
||||
// Directory where the repo is cloned to.
|
||||
checkoutDir string,
|
||||
// Relative path in the checkoutDir to location
|
||||
// of kustomization file.
|
||||
pathInCoDir string,
|
||||
// Any error encountered when cloning.
|
||||
err error)
|
||||
|
||||
// isRepoUrl checks if a string is likely a github repo Url.
|
||||
func isRepoUrl(arg string) bool {
|
||||
arg = strings.ToLower(arg)
|
||||
return !filepath.IsAbs(arg) &&
|
||||
(strings.HasPrefix(arg, "git::") ||
|
||||
strings.HasPrefix(arg, "gh:") ||
|
||||
strings.HasPrefix(arg, "github.com") ||
|
||||
strings.HasPrefix(arg, "git@github.com:") ||
|
||||
strings.Index(arg, "github.com/") > -1)
|
||||
}
|
||||
|
||||
func makeTmpDir() (string, error) {
|
||||
return ioutil.TempDir("", "kustomize-")
|
||||
}
|
||||
|
||||
func simpleGitCloner(spec string) (
|
||||
checkoutDir string, pathInCoDir string, err error) {
|
||||
gitProgram, err := exec.LookPath("git")
|
||||
if err != nil {
|
||||
return "", "", errors.Wrap(err, "no 'git' program on path")
|
||||
}
|
||||
checkoutDir, err = makeTmpDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
repo, pathInCoDir, gitRef, err := parseGithubUrl(spec)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(
|
||||
gitProgram,
|
||||
"clone",
|
||||
"https://github.com/"+repo+".git",
|
||||
checkoutDir)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", "",
|
||||
errors.Wrapf(err, "trouble cloning %s", spec)
|
||||
}
|
||||
if gitRef == "" {
|
||||
return
|
||||
}
|
||||
cmd = exec.Command(gitProgram, "checkout", gitRef)
|
||||
cmd.Dir = checkoutDir
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return "", "",
|
||||
errors.Wrapf(err, "trouble checking out href %s", gitRef)
|
||||
}
|
||||
return checkoutDir, pathInCoDir, nil
|
||||
}
|
||||
|
||||
const refQuery = "?ref="
|
||||
|
||||
// From strings like git@github.com:someOrg/someRepo.git or
|
||||
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||
// the parts.
|
||||
func parseGithubUrl(n string) (
|
||||
repo string, path string, gitRef string, err error) {
|
||||
for _, p := range []string{
|
||||
// Order matters here.
|
||||
"git::", "gh:", "https://", "http://",
|
||||
"git@", "github.com:", "github.com/", "gitlab.com/"} {
|
||||
if strings.ToLower(n[:len(p)]) == p {
|
||||
n = n[len(p):]
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(n, ".git") {
|
||||
n = n[0 : len(n)-len(".git")]
|
||||
}
|
||||
i := strings.Index(n, string(filepath.Separator))
|
||||
if i < 1 {
|
||||
return "", "", "", errors.New("no separator")
|
||||
}
|
||||
j := strings.Index(n[i+1:], string(filepath.Separator))
|
||||
if j >= 0 {
|
||||
j += i + 1
|
||||
repo = n[:j]
|
||||
path, gitRef = peelQuery(n[j+1:])
|
||||
} else {
|
||||
path = ""
|
||||
repo, gitRef = peelQuery(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func peelQuery(arg string) (string, string) {
|
||||
j := strings.Index(arg, refQuery)
|
||||
if j >= 0 {
|
||||
return arg[:j], arg[j+len(refQuery):]
|
||||
}
|
||||
return arg, ""
|
||||
}
|
||||
241
pkg/loader/gitcloner_test.go
Normal file
241
pkg/loader/gitcloner_test.go
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestIsRepoURL(t *testing.T) {
|
||||
|
||||
testcases := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
input: "https://github.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "github.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "git@github.com:org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "gh:org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "git::https://gitlab.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "/github.com/org/repo",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "/abs/path/to/file",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "../relative",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "foo",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: ".",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
actual := isRepoUrl(tc.input)
|
||||
if actual != tc.expected {
|
||||
t.Errorf("unexpected error: unexpected result %t for input %s", actual, tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitOnNthSlash(v string, n int) (string, string) {
|
||||
left := ""
|
||||
for i := 0; i < n; i++ {
|
||||
k := strings.Index(v, "/")
|
||||
if k < 0 {
|
||||
break
|
||||
}
|
||||
left = left + v[:k+1]
|
||||
v = v[k+1:]
|
||||
}
|
||||
return left[:len(left)-1], v
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
path := "a/b/c/d/e/f/g"
|
||||
if left, right := splitOnNthSlash(path, 2); left != "a/b" || right != "c/d/e/f/g" {
|
||||
t.Fatalf("got left='%s', right='%s'", left, right)
|
||||
}
|
||||
if left, right := splitOnNthSlash(path, 3); left != "a/b/c" || right != "d/e/f/g" {
|
||||
t.Fatalf("got left='%s', right='%s'", left, right)
|
||||
}
|
||||
if left, right := splitOnNthSlash(path, 6); left != "a/b/c/d/e/f" || right != "g" {
|
||||
t.Fatalf("got left='%s', right='%s'", left, right)
|
||||
}
|
||||
}
|
||||
|
||||
// makeFakeGitCloner returns a cloner that ignores the
|
||||
// URL argument and returns a path in a fake file system
|
||||
// that should already hold the 'repo' contents.
|
||||
func makeFakeGitCloner(t *testing.T, fSys fs.FileSystem, coRoot string) gitCloner {
|
||||
if !fSys.IsDir(coRoot) {
|
||||
t.Fatalf("expecting a directory at '%s'", coRoot)
|
||||
}
|
||||
return func(url string) (
|
||||
checkoutDir string, pathInCoDir string, err error) {
|
||||
_, path := splitOnNthSlash(url, 3)
|
||||
if !fSys.IsDir(coRoot + "/" + path) {
|
||||
t.Fatalf("expecting a directory at '%s'/'%s'",
|
||||
coRoot, path)
|
||||
}
|
||||
return coRoot, path, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitLoader(t *testing.T) {
|
||||
rootUrl := "github.com/someOrg/someRepo"
|
||||
pathInRepo := "foo/base"
|
||||
url := rootUrl + "/" + pathInRepo
|
||||
if !isRepoUrl(url) {
|
||||
t.Fatalf("'%s' should be accepted as a repo url", url)
|
||||
}
|
||||
|
||||
coRoot := "/tmp"
|
||||
fSys := fs.MakeFakeFS()
|
||||
fSys.MkdirAll(coRoot)
|
||||
fSys.MkdirAll(coRoot + "/" + pathInRepo)
|
||||
fSys.WriteFile(
|
||||
coRoot+"/"+pathInRepo+"/"+constants.KustomizationFileName,
|
||||
[]byte(`
|
||||
whatever
|
||||
`))
|
||||
l, err := newGitLoader(
|
||||
url, fSys, []string{},
|
||||
makeFakeGitCloner(t, fSys, coRoot))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v\n", err)
|
||||
}
|
||||
if coRoot+"/"+pathInRepo != l.Root() {
|
||||
t.Fatalf("expected root '%s', got '%s'\n",
|
||||
coRoot+"/"+pathInRepo, l.Root())
|
||||
}
|
||||
if _, err = l.New(url); err == nil {
|
||||
t.Fatalf("expected cycle error")
|
||||
}
|
||||
if _, err = l.New(rootUrl + "/" + "foo"); err == nil {
|
||||
t.Fatalf("expected cycle error")
|
||||
}
|
||||
|
||||
pathInRepo = "foo/overlay"
|
||||
fSys.MkdirAll(coRoot + "/" + pathInRepo)
|
||||
url = rootUrl + "/" + pathInRepo
|
||||
if !isRepoUrl(url) {
|
||||
t.Fatalf("'%s' should be accepted as a repo url", url)
|
||||
}
|
||||
l2, err := l.New(url)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if coRoot+"/"+pathInRepo != l2.Root() {
|
||||
t.Fatalf("expected root '%s', got '%s'\n",
|
||||
coRoot+"/"+pathInRepo, l2.Root())
|
||||
}
|
||||
}
|
||||
|
||||
var repoNames = []string{"someOrg/someRepo", "kubernetes/website"}
|
||||
|
||||
var paths = []string{"README.md", "foo/krusty.txt", ""}
|
||||
|
||||
var hrefArgs = []string{"someBranch", ""}
|
||||
|
||||
var extractFmts = []string{
|
||||
"gh:%s",
|
||||
"GH:%s",
|
||||
"gitHub.com/%s",
|
||||
"https://github.com/%s",
|
||||
"hTTps://github.com/%s",
|
||||
"git::https://gitlab.com/%s",
|
||||
"github.com:%s",
|
||||
}
|
||||
|
||||
func TestParseGithubUrl(t *testing.T) {
|
||||
for _, repoName := range repoNames {
|
||||
for _, pathName := range paths {
|
||||
for _, extractFmt := range extractFmts {
|
||||
for _, hrefArg := range hrefArgs {
|
||||
spec := repoName
|
||||
if len(pathName) > 0 {
|
||||
spec = filepath.Join(spec, pathName)
|
||||
}
|
||||
input := fmt.Sprintf(extractFmt, spec)
|
||||
if hrefArg != "" {
|
||||
input = input + refQuery + hrefArg
|
||||
}
|
||||
if !isRepoUrl(input) {
|
||||
t.Errorf("Should smell like github arg: %s\n", input)
|
||||
continue
|
||||
}
|
||||
repo, path, gitRef, err := parseGithubUrl(input)
|
||||
if err != nil {
|
||||
t.Errorf("problem %v", err)
|
||||
}
|
||||
if repo != repoName {
|
||||
t.Errorf("\n"+
|
||||
" from %s\n"+
|
||||
" actual Repo %s\n"+
|
||||
"expected Repo %s\n", input, repo, repoName)
|
||||
}
|
||||
if path != pathName {
|
||||
t.Errorf("\n"+
|
||||
" from %s\n"+
|
||||
" actual Path %s\n"+
|
||||
"expected Path %s\n", input, path, pathName)
|
||||
}
|
||||
if gitRef != hrefArg {
|
||||
t.Errorf("\n"+
|
||||
" from %s\n"+
|
||||
" actual Href %s\n"+
|
||||
"expected Href %s\n", input, gitRef, hrefArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-getter"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
// githubLoader loads files from a checkout github repo
|
||||
type githubLoader struct {
|
||||
repo string
|
||||
// targetDir will hold the local repo
|
||||
targetDir string
|
||||
// checkoutDir is for the whole repository
|
||||
checkoutDir string
|
||||
loader *fileLoader
|
||||
}
|
||||
|
||||
// Root returns the root location for this Loader.
|
||||
func (l *githubLoader) Root() string {
|
||||
return l.targetDir
|
||||
}
|
||||
|
||||
// New delegates to fileLoader.New
|
||||
func (l *githubLoader) New(newRoot string) (ifc.Loader, error) {
|
||||
return l.loader.New(newRoot)
|
||||
}
|
||||
|
||||
// Load delegates to fileLoader.Load
|
||||
func (l *githubLoader) Load(location string) ([]byte, error) {
|
||||
return l.loader.Load(location)
|
||||
}
|
||||
|
||||
// Cleanup removes the checked out repo
|
||||
func (l *githubLoader) Cleanup() error {
|
||||
return os.RemoveAll(l.checkoutDir)
|
||||
}
|
||||
|
||||
// newGithubLoader returns a new fileLoader with given github Url.
|
||||
func newGithubLoader(repoUrl string, fs fs.FileSystem) (*githubLoader, error) {
|
||||
dir, err := ioutil.TempDir("", "kustomize-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repodir := filepath.Join(dir, "repo")
|
||||
src, subdir := getter.SourceDirSubdir(repoUrl)
|
||||
err = checkout(src, repodir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target := filepath.Join(repodir, subdir)
|
||||
l := newFileLoaderAtRoot(target, fs)
|
||||
return &githubLoader{
|
||||
repo: repoUrl,
|
||||
targetDir: target,
|
||||
checkoutDir: repodir,
|
||||
loader: l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isRepoUrl checks if a string is a repo Url
|
||||
func isRepoUrl(s string) bool {
|
||||
if strings.HasPrefix(s, "https://") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(s, "git::") {
|
||||
return true
|
||||
}
|
||||
host := strings.SplitN(s, "/", 2)[0]
|
||||
return strings.Contains(host, ".com") || strings.Contains(host, ".org")
|
||||
}
|
||||
|
||||
// Checkout clones a github repo with specified commit/tag/branch
|
||||
func checkout(url, dir string) error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &getter.Client{
|
||||
Src: url,
|
||||
Dst: dir,
|
||||
Pwd: pwd,
|
||||
Mode: getter.ClientModeDir,
|
||||
}
|
||||
return client.Get()
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsRepoURL(t *testing.T) {
|
||||
|
||||
testcases := []struct {
|
||||
input string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
input: "https://github.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "github.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
input: "/github.com/org/repo",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "/abs/path/to/file",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "../relative",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
input: "git::https://gitlab.com/org/repo",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
actual := isRepoUrl(tc.input)
|
||||
if actual != tc.expected {
|
||||
t.Errorf("unexpected error: unexpected result %t for input %s", actual, tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,52 +18,16 @@ limitations under the License.
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
)
|
||||
|
||||
// NewLoader returns a Loader given a target
|
||||
// The target can be a local disk directory or a github Url
|
||||
func NewLoader(target, r string, fSys fs.FileSystem) (ifc.Loader, error) {
|
||||
if !isValidLoaderPath(target, r) {
|
||||
return nil, fmt.Errorf("Not valid path: root='%s', loc='%s'\n", r, target)
|
||||
// NewLoader returns a Loader.
|
||||
func NewLoader(root string, fSys fs.FileSystem) (ifc.Loader, error) {
|
||||
if isRepoUrl(root) {
|
||||
return newGitLoader(
|
||||
root, fSys, []string{}, simpleGitCloner)
|
||||
}
|
||||
|
||||
if !isLocalTarget(target, fSys) && isRepoUrl(target) {
|
||||
return newGithubLoader(target, fSys)
|
||||
}
|
||||
|
||||
l := newFileLoaderAtRoot(r, fSys)
|
||||
if isRootLoaderPath(r) {
|
||||
absPath, err := filepath.Abs(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target = absPath
|
||||
}
|
||||
|
||||
if !l.IsAbsPath(l.root, target) {
|
||||
return nil, fmt.Errorf("Not abs path: l.root='%s', loc='%s'\n", l.root, target)
|
||||
}
|
||||
root, err := l.fullLocation(l.root, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newFileLoaderAtRoot(root, l.fSys), nil
|
||||
}
|
||||
|
||||
func isValidLoaderPath(target, root string) bool {
|
||||
return target != "" || root != ""
|
||||
}
|
||||
|
||||
func isRootLoaderPath(root string) bool {
|
||||
return root == ""
|
||||
}
|
||||
|
||||
// isLocalTarget checks if a file exists in the filesystem
|
||||
func isLocalTarget(s string, fs fs.FileSystem) bool {
|
||||
return fs.Exists(s)
|
||||
return newFileLoaderAt(
|
||||
root, fSys, []string{}, simpleGitCloner)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type Json6902 struct {
|
||||
// applied to. It must refer to a Kubernetes resource under the
|
||||
// purview of this kustomization. Target should use the
|
||||
// raw name of the object (the name specified in its YAML,
|
||||
// before addition of a namePrefix).
|
||||
// before addition of a namePrefix and a nameSuffix).
|
||||
Target *Target `json:"target" yaml:"target"`
|
||||
|
||||
// relative file path for a json patch file inside a kustomization
|
||||
|
||||
@@ -82,7 +82,7 @@ func TestNewPatchJson6902FactoryJSON(t *testing.T) {
|
||||
target:
|
||||
kind: Deployment
|
||||
name: some-name
|
||||
path: /testpath/patch.json
|
||||
path: patch.json
|
||||
`)
|
||||
p := patch.Json6902{}
|
||||
err = yaml.Unmarshal(jsonPatch, &p)
|
||||
@@ -120,7 +120,7 @@ func TestNewPatchJson6902FactoryYAML(t *testing.T) {
|
||||
target:
|
||||
name: some-name
|
||||
kind: Deployment
|
||||
path: /testpath/patch.yaml
|
||||
path: patch.yaml
|
||||
`)
|
||||
p := patch.Json6902{}
|
||||
err = yaml.Unmarshal(jsonPatch, &p)
|
||||
@@ -162,12 +162,12 @@ func TestNewPatchJson6902FactoryMulti(t *testing.T) {
|
||||
- target:
|
||||
kind: foo
|
||||
name: some-name
|
||||
path: /testpath/patch.json
|
||||
path: patch.json
|
||||
|
||||
- target:
|
||||
kind: foo
|
||||
name: some-name
|
||||
path: /testpath/patch.yaml
|
||||
path: patch.yaml
|
||||
`)
|
||||
var p []patch.Json6902
|
||||
err = yaml.Unmarshal(jsonPatches, &p)
|
||||
@@ -276,12 +276,12 @@ func TestNewPatchJson6902FactoryMultiConflict(t *testing.T) {
|
||||
- target:
|
||||
kind: foo
|
||||
name: some-name
|
||||
path: /testpath/patch.json
|
||||
path: patch.json
|
||||
|
||||
- target:
|
||||
kind: foo
|
||||
name: some-name
|
||||
path: /testpath/patch.yaml
|
||||
path: patch.yaml
|
||||
`)
|
||||
var p []patch.Json6902
|
||||
err = yaml.Unmarshal(jsonPatches, &p)
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package resid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
@@ -32,22 +33,46 @@ type ResId struct {
|
||||
// an untransformed resource has no prefix, fully transformed resource has an arbitrary number of prefixes
|
||||
// concatenated together.
|
||||
prefix string
|
||||
// nameSuffix of the resource
|
||||
// an untransformed resource has no suffix, fully transformed resource has an arbitrary number of suffixes
|
||||
// concatenated together.
|
||||
suffix string
|
||||
// namespace the resource belongs to
|
||||
// an untransformed resource has no namespace, fully transformed resource has the namespace from
|
||||
// the top most overlay
|
||||
namespace string
|
||||
}
|
||||
|
||||
// NewResIdWithPrefixSuffixNamespace creates new resource identifier with a prefix, suffix and a namespace
|
||||
func NewResIdWithPrefixSuffixNamespace(k gvk.Gvk, n, p, s, ns string) ResId {
|
||||
return ResId{gvKind: k, name: n, prefix: p, suffix: s, namespace: ns}
|
||||
}
|
||||
|
||||
// NewResIdWithPrefixNamespace creates new resource identifier with a prefix and a namespace
|
||||
func NewResIdWithPrefixNamespace(k gvk.Gvk, n, p, ns string) ResId {
|
||||
return ResId{gvKind: k, name: n, prefix: p, namespace: ns}
|
||||
}
|
||||
|
||||
// NewResIdWithSuffixNamespace creates new resource identifier with a suffix and a namespace
|
||||
func NewResIdWithSuffixNamespace(k gvk.Gvk, n, s, ns string) ResId {
|
||||
return ResId{gvKind: k, name: n, suffix: s, namespace: ns}
|
||||
}
|
||||
|
||||
// NewResIdWithPrefixSuffix creates new resource identifier with a prefix and suffix
|
||||
func NewResIdWithPrefixSuffix(k gvk.Gvk, n, p, s string) ResId {
|
||||
return ResId{gvKind: k, name: n, prefix: p, suffix: s}
|
||||
}
|
||||
|
||||
// NewResIdWithPrefix creates new resource identifier with a prefix
|
||||
func NewResIdWithPrefix(k gvk.Gvk, n, p string) ResId {
|
||||
return ResId{gvKind: k, name: n, prefix: p}
|
||||
}
|
||||
|
||||
// NewResIdWithSuffix creates new resource identifier with a suffix
|
||||
func NewResIdWithSuffix(k gvk.Gvk, n, s string) ResId {
|
||||
return ResId{gvKind: k, name: n, suffix: s}
|
||||
}
|
||||
|
||||
// NewResId creates new resource identifier
|
||||
func NewResId(k gvk.Gvk, n string) ResId {
|
||||
return ResId{gvKind: k, name: n}
|
||||
@@ -62,6 +87,7 @@ const (
|
||||
noNamespace = "noNamespace"
|
||||
noPrefix = "noPrefix"
|
||||
noName = "noName"
|
||||
noSuffix = "noSuffix"
|
||||
separator = "|"
|
||||
)
|
||||
|
||||
@@ -79,9 +105,13 @@ func (n ResId) String() string {
|
||||
if nm == "" {
|
||||
nm = noName
|
||||
}
|
||||
s := n.suffix
|
||||
if s == "" {
|
||||
s = noSuffix
|
||||
}
|
||||
|
||||
return strings.Join(
|
||||
[]string{n.gvKind.String(), ns, p, nm}, separator)
|
||||
[]string{n.gvKind.String(), ns, p, nm, s}, separator)
|
||||
}
|
||||
|
||||
// GvknString of ResId based on GVK and name
|
||||
@@ -90,7 +120,7 @@ func (n ResId) GvknString() string {
|
||||
}
|
||||
|
||||
// GvknEquals return if two ResId have the same Group/Version/Kind and name
|
||||
// The comparison excludes prefix
|
||||
// The comparison excludes prefix and suffix
|
||||
func (n ResId) GvknEquals(id ResId) bool {
|
||||
return n.gvKind.Equals(id.gvKind) && n.name == id.name
|
||||
}
|
||||
@@ -105,24 +135,44 @@ func (n ResId) Name() string {
|
||||
return n.name
|
||||
}
|
||||
|
||||
// NameWithPrefixSuffix returns resource name with prefix and suffix.
|
||||
func (n ResId) NameWithPrefixSuffix() string {
|
||||
prefix := strings.Join(n.prefixList(), "")
|
||||
suffix := strings.Join(n.suffixList(), "")
|
||||
return fmt.Sprintf("%s%s%s", prefix, n.name, suffix)
|
||||
}
|
||||
|
||||
// Prefix returns name prefix.
|
||||
func (n ResId) Prefix() string {
|
||||
return n.prefix
|
||||
}
|
||||
|
||||
// Suffix returns name suffix.
|
||||
func (n ResId) Suffix() string {
|
||||
return n.suffix
|
||||
}
|
||||
|
||||
// Namespace returns resource namespace.
|
||||
func (n ResId) Namespace() string {
|
||||
return n.namespace
|
||||
}
|
||||
|
||||
// CopyWithNewPrefix make a new copy from current ResId and append a new prefix
|
||||
func (n ResId) CopyWithNewPrefix(p string) ResId {
|
||||
return ResId{gvKind: n.gvKind, name: n.name, prefix: n.concatPrefix(p), namespace: n.namespace}
|
||||
// CopyWithNewPrefixSuffix make a new copy from current ResId and append a new prefix and suffix
|
||||
func (n ResId) CopyWithNewPrefixSuffix(p, s string) ResId {
|
||||
prefix := n.prefix
|
||||
if p != "" {
|
||||
prefix = n.concatPrefix(p)
|
||||
}
|
||||
suffix := n.suffix
|
||||
if s != "" {
|
||||
suffix = n.concatSuffix(s)
|
||||
}
|
||||
return ResId{gvKind: n.gvKind, name: n.name, prefix: prefix, suffix: suffix, namespace: n.namespace}
|
||||
}
|
||||
|
||||
// CopyWithNewNamespace make a new copy from current ResId and set a new namespace
|
||||
func (n ResId) CopyWithNewNamespace(ns string) ResId {
|
||||
return ResId{gvKind: n.gvKind, name: n.name, prefix: n.prefix, namespace: ns}
|
||||
return ResId{gvKind: n.gvKind, name: n.name, prefix: n.prefix, suffix: n.suffix, namespace: ns}
|
||||
}
|
||||
|
||||
// HasSameLeftmostPrefix check if two ResIds have the same
|
||||
@@ -133,6 +183,14 @@ func (n ResId) HasSameLeftmostPrefix(id ResId) bool {
|
||||
return prefixes1[0] == prefixes2[0]
|
||||
}
|
||||
|
||||
// HasSameRightmostSuffix check if two ResIds have the same
|
||||
// right most suffix.
|
||||
func (n ResId) HasSameRightmostSuffix(id ResId) bool {
|
||||
suffixes1 := n.suffixList()
|
||||
suffixes2 := id.suffixList()
|
||||
return suffixes1[len(suffixes1)-1] == suffixes2[len(suffixes2)-1]
|
||||
}
|
||||
|
||||
func (n ResId) concatPrefix(p string) string {
|
||||
if p == "" {
|
||||
return n.prefix
|
||||
@@ -143,6 +201,20 @@ func (n ResId) concatPrefix(p string) string {
|
||||
return p + ":" + n.prefix
|
||||
}
|
||||
|
||||
func (n ResId) concatSuffix(s string) string {
|
||||
if s == "" {
|
||||
return n.suffix
|
||||
}
|
||||
if n.suffix == "" {
|
||||
return s
|
||||
}
|
||||
return n.suffix + ":" + s
|
||||
}
|
||||
|
||||
func (n ResId) prefixList() []string {
|
||||
return strings.Split(n.prefix, ":")
|
||||
}
|
||||
|
||||
func (n ResId) suffixList() []string {
|
||||
return strings.Split(n.suffix, ":")
|
||||
}
|
||||
|
||||
@@ -11,27 +11,30 @@ var stringTests = []struct {
|
||||
s string
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
"g_v_k|ns|p|nm"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"g_v_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
"noGroup_v_k|ns|p|nm"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_v_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
"noGroup_noVersion_k|ns|p|nm"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_noVersion_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
"noGroup_noVersion_noKind|ns|p|nm"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_noVersion_noKind|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p"},
|
||||
"noGroup_noVersion_noKind|noNamespace|p|nm"},
|
||||
name: "nm", prefix: "p", suffix: "s"},
|
||||
"noGroup_noVersion_noKind|noNamespace|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm"},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|nm"},
|
||||
name: "nm", suffix: "s"},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
suffix: "s"},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|noName|s"},
|
||||
{ResId{gvKind: gvk.Gvk{}},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|noName"},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|noName|noSuffix"},
|
||||
{ResId{},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|noName"},
|
||||
"noGroup_noVersion_noKind|noNamespace|noPrefix|noName|noSuffix"},
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
@@ -47,23 +50,26 @@ var gvknStringTests = []struct {
|
||||
s string
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"g_v_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_v_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_noVersion_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", namespace: "ns"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"noGroup_noVersion_noKind|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p"},
|
||||
name: "nm", prefix: "p", suffix: "s"},
|
||||
"noGroup_noVersion_noKind|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm"},
|
||||
name: "nm", suffix: "s"},
|
||||
"noGroup_noVersion_noKind|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
suffix: "s"},
|
||||
"noGroup_noVersion_noKind|"},
|
||||
{ResId{gvKind: gvk.Gvk{}},
|
||||
"noGroup_noVersion_noKind|"},
|
||||
{ResId{},
|
||||
@@ -83,19 +89,19 @@ var GvknEqualsTest = []struct {
|
||||
x2 ResId
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "AA", namespace: "X"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "BB", namespace: "Z"}},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "AA", namespace: "X"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "BB", namespace: "Z"}},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "AA", namespace: "X"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "BB", namespace: "Z"}},
|
||||
{ResId{name: "nm", prefix: "AA", namespace: "X"},
|
||||
ResId{name: "nm", prefix: "BB", namespace: "Z"}},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
}
|
||||
|
||||
func TestEquals(t *testing.T) {
|
||||
|
||||
@@ -53,8 +53,8 @@ metadata:
|
||||
---
|
||||
`
|
||||
|
||||
l := loadertest.NewFakeLoader("/home/seans/project")
|
||||
if ferr := l.AddFile("/home/seans/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
|
||||
l := loadertest.NewFakeLoader("/whatever/project")
|
||||
if ferr := l.AddFile("/whatever/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
|
||||
t.Fatalf("Error adding fake file: %v\n", ferr)
|
||||
}
|
||||
expected := ResMap{resid.NewResId(deploy, "dply1"): rf.FromMap(
|
||||
@@ -85,7 +85,7 @@ metadata:
|
||||
}
|
||||
|
||||
m, _ := rmF.FromFiles(
|
||||
l, []string{"/home/seans/project/deployment.yaml"})
|
||||
l, []string{"deployment.yaml"})
|
||||
if len(m) != 3 {
|
||||
t.Fatalf("%#v should contain 3 appResource, but got %d", m, len(m))
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
expected ResMap
|
||||
}
|
||||
|
||||
l := loadertest.NewFakeLoader("/home/seans/project/")
|
||||
l := loadertest.NewFakeLoader("/whatever/project/")
|
||||
testCases := []testCase{
|
||||
{
|
||||
description: "construct config map from env",
|
||||
@@ -157,7 +157,7 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
filepath: "/home/seans/project/app.env",
|
||||
filepath: "/whatever/project/app.env",
|
||||
content: "DB_USERNAME=admin\nDB_PASSWORD=somepw",
|
||||
expected: ResMap{
|
||||
resid.NewResId(cmap, "envConfigMap"): rf.FromMap(
|
||||
@@ -183,7 +183,7 @@ func TestNewFromConfigMaps(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
filepath: "/home/seans/project/app-init.ini",
|
||||
filepath: "/whatever/project/app-init.ini",
|
||||
content: "FOO=bar\nBAR=baz\n",
|
||||
expected: ResMap{
|
||||
resid.NewResId(cmap, "fileConfigMap"): rf.FromMap(
|
||||
@@ -270,7 +270,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
||||
}
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir(".")
|
||||
rmF.Set(fakeFs, loader.NewFileLoader(fakeFs))
|
||||
rmF.Set(fakeFs, loader.NewFileLoaderAtRoot(fakeFs))
|
||||
actual, err := rmF.NewResMapFromSecretArgs(secrets, nil)
|
||||
|
||||
if err != nil {
|
||||
@@ -326,7 +326,7 @@ func TestSecretTimeout(t *testing.T) {
|
||||
}
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir(".")
|
||||
rmF.Set(fakeFs, loader.NewFileLoader(fakeFs))
|
||||
rmF.Set(fakeFs, loader.NewFileLoaderAtRoot(fakeFs))
|
||||
_, err := rmF.NewResMapFromSecretArgs(secrets, nil)
|
||||
|
||||
if err == nil {
|
||||
|
||||
@@ -20,11 +20,11 @@ package resmap
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/golang/glog"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
@@ -121,16 +121,17 @@ func (m ResMap) DeepCopy(rf *resource.Factory) ResMap {
|
||||
return mcopy
|
||||
}
|
||||
|
||||
// FilterBy returns a ResMap containing ResIds with the same namespace and nameprefix
|
||||
// with the inputId
|
||||
// If inputId is a cluster level resource, return the original resmap
|
||||
// FilterBy returns a subset ResMap containing ResIds with
|
||||
// the same namespace and leftmost name prefix and rightmost name
|
||||
// as the inputId. If inputId is a cluster level resource, this
|
||||
// returns the original ResMap.
|
||||
func (m ResMap) FilterBy(inputId resid.ResId) ResMap {
|
||||
if inputId.Gvk().IsClusterKind() {
|
||||
return m
|
||||
}
|
||||
result := ResMap{}
|
||||
for id, res := range m {
|
||||
if id.Namespace() == inputId.Namespace() && id.HasSameLeftmostPrefix(inputId) {
|
||||
if id.Namespace() == inputId.Namespace() && id.HasSameLeftmostPrefix(inputId) && id.HasSameRightmostSuffix(inputId) {
|
||||
result[id] = res
|
||||
}
|
||||
}
|
||||
@@ -180,17 +181,17 @@ func MergeWithOverride(maps ...ResMap) (ResMap, error) {
|
||||
id = matchedId[0]
|
||||
switch r.Behavior() {
|
||||
case ifc.BehaviorReplace:
|
||||
glog.V(4).Infof(
|
||||
log.Printf(
|
||||
"Replace %v with %v", result[id].Map(), r.Map())
|
||||
r.Replace(result[id])
|
||||
result[id] = r
|
||||
result[id].SetBehavior(ifc.BehaviorCreate)
|
||||
case ifc.BehaviorMerge:
|
||||
glog.V(4).Infof(
|
||||
log.Printf(
|
||||
"Merging %v with %v", result[id].Map(), r.Map())
|
||||
r.Merge(result[id])
|
||||
result[id] = r
|
||||
glog.V(4).Infof(
|
||||
log.Printf(
|
||||
"Merged object is %v", result[id].Map())
|
||||
result[id].SetBehavior(ifc.BehaviorCreate)
|
||||
default:
|
||||
|
||||
@@ -119,53 +119,147 @@ func TestDemandOneMatchForId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFilterBy(t *testing.T) {
|
||||
rm := ResMap{resid.NewResIdWithPrefixNamespace(cmap, "cm1", "prefix1", "ns1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
tests := map[string]struct {
|
||||
resMap ResMap
|
||||
filter resid.ResId
|
||||
expected ResMap
|
||||
}{
|
||||
"different namespace": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
}),
|
||||
}
|
||||
rm1 := ResMap{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns2"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace2"),
|
||||
expected: ResMap{},
|
||||
},
|
||||
"different prefix": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix2", "suffix", "namespace"),
|
||||
expected: ResMap{},
|
||||
},
|
||||
"different suffix": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix1", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix2", "namespace"),
|
||||
expected: ResMap{},
|
||||
},
|
||||
"same namespace, same prefix": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResIdWithPrefixNamespace(cmap, "config-map2", "prefix", "namespace"),
|
||||
expected: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
"same namespace, same suffix": {
|
||||
resMap: ResMap{resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResIdWithSuffixNamespace(cmap, "config-map2", "suffix", "namespace"),
|
||||
expected: ResMap{resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
"same namespace, same prefix, same suffix": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map2", "prefix", "suffix", "namespace"),
|
||||
expected: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
"filter by cluster-level Gvk": {
|
||||
resMap: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
filter: resid.NewResId(gvk.Gvk{Kind: "ClusterRoleBinding"}, "cluster-role-binding"),
|
||||
expected: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range rm {
|
||||
rm1[k] = v
|
||||
}
|
||||
|
||||
empty := rm1.FilterBy(resid.NewResIdWithPrefixNamespace(cmap, "cm4", "prefix1", "ns3"))
|
||||
if len(empty) != 0 {
|
||||
t.Fatalf("Expected empty filtered map but got %v", empty)
|
||||
}
|
||||
|
||||
ns1map := rm1.FilterBy(resid.NewResIdWithPrefixNamespace(cmap, "cm4", "prefix1", "ns1"))
|
||||
if !reflect.DeepEqual(rm, ns1map) {
|
||||
t.Fatalf("Expected %v but got back %v", rm, ns1map)
|
||||
}
|
||||
|
||||
clmap := rm1.FilterBy(resid.NewResId(gvk.Gvk{Kind: "ClusterRoleBinding"}, "crb"))
|
||||
if !reflect.DeepEqual(rm1, clmap) {
|
||||
t.Fatalf("Expected %v but got back %v", rm1, clmap)
|
||||
for name, test := range tests {
|
||||
test := test
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := test.resMap.FilterBy(test.filter)
|
||||
if !reflect.DeepEqual(test.expected, got) {
|
||||
t.Fatalf("Expected %v but got back %v", test.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepCopy(t *testing.T) {
|
||||
rm1 := ResMap{
|
||||
resid.NewResId(cmap, "cm1"): rf.FromMap(
|
||||
|
||||
@@ -26,14 +26,14 @@ import (
|
||||
|
||||
func TestSliceFromPatches(t *testing.T) {
|
||||
|
||||
patchGood1 := patch.StrategicMerge("/foo/patch1.yaml")
|
||||
patchGood1 := patch.StrategicMerge("patch1.yaml")
|
||||
patch1 := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pooh
|
||||
`
|
||||
patchGood2 := patch.StrategicMerge("/foo/patch2.yaml")
|
||||
patchGood2 := patch.StrategicMerge("patch2.yaml")
|
||||
patch2 := `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
@@ -45,14 +45,14 @@ metadata:
|
||||
---
|
||||
---
|
||||
`
|
||||
patchBad := patch.StrategicMerge("/foo/patch3.yaml")
|
||||
patchBad := patch.StrategicMerge("patch3.yaml")
|
||||
patch3 := `
|
||||
WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot
|
||||
`
|
||||
l := loadertest.NewFakeLoader("/foo")
|
||||
l.AddFile(string(patchGood1), []byte(patch1))
|
||||
l.AddFile(string(patchGood2), []byte(patch2))
|
||||
l.AddFile(string(patchBad), []byte(patch3))
|
||||
l := loadertest.NewFakeLoader("/")
|
||||
l.AddFile("/"+string(patchGood1), []byte(patch1))
|
||||
l.AddFile("/"+string(patchGood2), []byte(patch2))
|
||||
l.AddFile("/"+string(patchBad), []byte(patch3))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -21,17 +21,18 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc/transformer"
|
||||
interror "sigs.k8s.io/kustomize/pkg/internal/error"
|
||||
patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
@@ -44,18 +45,17 @@ type KustTarget struct {
|
||||
kustomization *types.Kustomization
|
||||
ldr ifc.Loader
|
||||
fSys fs.FileSystem
|
||||
rf *resmap.Factory
|
||||
tcfg *config.TransformerConfig
|
||||
ptf transformer.Factory
|
||||
rFactory *resmap.Factory
|
||||
tConfig *config.TransformerConfig
|
||||
tFactory transformer.Factory
|
||||
}
|
||||
|
||||
// NewKustTarget returns a new instance of KustTarget primed with a Loader.
|
||||
func NewKustTarget(
|
||||
ldr ifc.Loader, fSys fs.FileSystem,
|
||||
rf *resmap.Factory,
|
||||
ptf transformer.Factory,
|
||||
tcfg *config.TransformerConfig) (*KustTarget, error) {
|
||||
content, err := ldr.Load(constants.KustomizationFileName)
|
||||
rFactory *resmap.Factory,
|
||||
tFactory transformer.Factory) (*KustTarget, error) {
|
||||
content, err := loadKustFile(ldr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,13 +66,17 @@ func NewKustTarget(
|
||||
return nil, err
|
||||
}
|
||||
k.DealWithDeprecatedFields()
|
||||
tConfig, err := makeTransformerConfig(ldr, k.Configurations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &KustTarget{
|
||||
kustomization: &k,
|
||||
ldr: ldr,
|
||||
fSys: fSys,
|
||||
rf: rf,
|
||||
tcfg: tcfg,
|
||||
ptf: ptf,
|
||||
rFactory: rFactory,
|
||||
tConfig: tConfig,
|
||||
tFactory: tFactory,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -81,12 +85,20 @@ func unmarshal(y []byte, o interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(j))
|
||||
dec.DisallowUnknownFields()
|
||||
return dec.Decode(o)
|
||||
}
|
||||
|
||||
// makeTransformerConfig returns a complete TransformerConfig object from either files
|
||||
// or the default configs
|
||||
func makeTransformerConfig(ldr ifc.Loader, paths []string) (*config.TransformerConfig, error) {
|
||||
if paths == nil || len(paths) == 0 {
|
||||
return config.NewFactory(nil).DefaultConfig(), nil
|
||||
}
|
||||
return config.NewFactory(ldr).FromFiles(paths)
|
||||
}
|
||||
|
||||
// MakeCustomizedResMap creates a ResMap per kustomization instructions.
|
||||
// The Resources in the returned ResMap are fully customized.
|
||||
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||
@@ -99,15 +111,19 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||
|
||||
// resolveRefsToGeneratedResources fixes all name references.
|
||||
func (kt *KustTarget) resolveRefsToGeneratedResources(m resmap.ResMap) (resmap.ResMap, error) {
|
||||
if kt.kustomization.GeneratorOptions == nil || !kt.kustomization.GeneratorOptions.DisableHash {
|
||||
err := kt.ptf.MakeHashTransformer().Transform(m)
|
||||
if kt.kustomization.GeneratorOptions == nil ||
|
||||
!kt.kustomization.GeneratorOptions.DisableNameSuffixHash {
|
||||
// This effects only generated resources.
|
||||
// It changes only the Name field in the
|
||||
// resource held in the ResMap's value, not
|
||||
// the Name in the key in the ResMap.
|
||||
err := kt.tFactory.MakeHashTransformer().Transform(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var r []transformers.Transformer
|
||||
t, err := transformers.NewNameReferenceTransformer(kt.tcfg.NameReference)
|
||||
t, err := transformers.NewNameReferenceTransformer(kt.tConfig.NameReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,7 +133,7 @@ func (kt *KustTarget) resolveRefsToGeneratedResources(m resmap.ResMap) (resmap.R
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t = transformers.NewRefVarTransformer(refVars, kt.tcfg.VarReference)
|
||||
t = transformers.NewRefVarTransformer(refVars, kt.tConfig.VarReference)
|
||||
r = append(r, t)
|
||||
|
||||
err = transformers.NewMultiTransformer(r).Transform(m)
|
||||
@@ -135,7 +151,7 @@ func (kt *KustTarget) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
errs.Append(errors.Wrap(err, "loadResMapFromBasesAndResources"))
|
||||
}
|
||||
crdTc, err := config.NewFactory(kt.ldr).LoadCRDs(kt.kustomization.Crds)
|
||||
kt.tcfg = kt.tcfg.Merge(crdTc)
|
||||
kt.tConfig = kt.tConfig.Merge(crdTc)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "LoadCRDs"))
|
||||
}
|
||||
@@ -148,12 +164,11 @@ func (kt *KustTarget) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patches, err := kt.rf.RF().SliceFromPatches(
|
||||
patches, err := kt.rFactory.RF().SliceFromPatches(
|
||||
kt.ldr, kt.kustomization.PatchesStrategicMerge)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "SliceFromPatches"))
|
||||
}
|
||||
|
||||
if len(errs.Get()) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
@@ -185,12 +200,14 @@ func (kt *KustTarget) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
|
||||
func (kt *KustTarget) generateConfigMapsAndSecrets(
|
||||
errs *interror.KustomizationErrors) (resmap.ResMap, error) {
|
||||
kt.rf.Set(kt.fSys, kt.ldr)
|
||||
cms, err := kt.rf.NewResMapFromConfigMapArgs(kt.kustomization.ConfigMapGenerator, kt.kustomization.GeneratorOptions)
|
||||
kt.rFactory.Set(kt.fSys, kt.ldr)
|
||||
cms, err := kt.rFactory.NewResMapFromConfigMapArgs(
|
||||
kt.kustomization.ConfigMapGenerator, kt.kustomization.GeneratorOptions)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResMapFromConfigMapArgs"))
|
||||
}
|
||||
secrets, err := kt.rf.NewResMapFromSecretArgs(kt.kustomization.SecretGenerator, kt.kustomization.GeneratorOptions)
|
||||
secrets, err := kt.rFactory.NewResMapFromSecretArgs(
|
||||
kt.kustomization.SecretGenerator, kt.kustomization.GeneratorOptions)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResMapFromSecretArgs"))
|
||||
}
|
||||
@@ -200,7 +217,7 @@ func (kt *KustTarget) generateConfigMapsAndSecrets(
|
||||
// Gets Bases and Resources as advertised.
|
||||
func (kt *KustTarget) loadResMapFromBasesAndResources() (resmap.ResMap, error) {
|
||||
bases, errs := kt.loadCustomizedBases()
|
||||
resources, err := kt.rf.FromFiles(
|
||||
resources, err := kt.rFactory.FromFiles(
|
||||
kt.ldr, kt.kustomization.Resources)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "rawResources failed to read Resources"))
|
||||
@@ -223,7 +240,8 @@ func (kt *KustTarget) loadCustomizedBases() (resmap.ResMap, *interror.Kustomizat
|
||||
continue
|
||||
}
|
||||
target, err := NewKustTarget(
|
||||
ldr, kt.fSys, kt.rf, kt.ptf, kt.tcfg)
|
||||
ldr, kt.fSys,
|
||||
kt.rFactory, kt.tFactory)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "couldn't make target for "+path))
|
||||
continue
|
||||
@@ -253,7 +271,7 @@ func (kt *KustTarget) loadBasesAsFlatList() ([]*KustTarget, error) {
|
||||
continue
|
||||
}
|
||||
target, err := NewKustTarget(
|
||||
ldr, kt.fSys, kt.rf, kt.ptf, kt.tcfg)
|
||||
ldr, kt.fSys, kt.rFactory, kt.tFactory)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
continue
|
||||
@@ -269,27 +287,30 @@ func (kt *KustTarget) loadBasesAsFlatList() ([]*KustTarget, error) {
|
||||
// newTransformer makes a Transformer that does everything except resolve generated names.
|
||||
func (kt *KustTarget) newTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
|
||||
var r []transformers.Transformer
|
||||
t, err := kt.ptf.MakePatchTransformer(patches, kt.rf.RF())
|
||||
t, err := kt.tFactory.MakePatchTransformer(patches, kt.rFactory.RF())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
r = append(r, transformers.NewNamespaceTransformer(
|
||||
string(kt.kustomization.Namespace), kt.tcfg.NameSpace))
|
||||
t, err = transformers.NewNamePrefixTransformer(
|
||||
string(kt.kustomization.NamePrefix), kt.tcfg.NamePrefix)
|
||||
string(kt.kustomization.Namespace), kt.tConfig.NameSpace))
|
||||
t, err = transformers.NewNamePrefixSuffixTransformer(
|
||||
string(kt.kustomization.NamePrefix),
|
||||
string(kt.kustomization.NameSuffix),
|
||||
kt.tConfig.NamePrefix,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
t, err = transformers.NewLabelsMapTransformer(
|
||||
kt.kustomization.CommonLabels, kt.tcfg.CommonLabels)
|
||||
kt.kustomization.CommonLabels, kt.tConfig.CommonLabels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
t, err = transformers.NewAnnotationsMapTransformer(
|
||||
kt.kustomization.CommonAnnotations, kt.tcfg.CommonAnnotations)
|
||||
kt.kustomization.CommonAnnotations, kt.tConfig.CommonAnnotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -312,7 +333,7 @@ func (kt *KustTarget) resolveRefVars(m resmap.ResMap) (map[string]string, error)
|
||||
}
|
||||
result[v.Name] = s
|
||||
} else {
|
||||
glog.Infof("couldn't resolve v: %v", v)
|
||||
log.Printf("couldn't resolve v: %v", v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
@@ -347,3 +368,16 @@ func (kt *KustTarget) getAllVars() ([]types.Var, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
||||
for _, kf := range []string{constants.KustomizationFileName, constants.SecondaryKustomizationFileName} {
|
||||
content, err := ldr.Load(kf)
|
||||
if err == nil {
|
||||
return content, nil
|
||||
}
|
||||
if !strings.Contains(err.Error(), "no such file or directory") {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no kustomization.yaml file under %s", ldr.Root())
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
kustomizationContent1 = `
|
||||
namePrefix: foo-
|
||||
nameSuffix: -bar
|
||||
namespace: ns1
|
||||
commonLabels:
|
||||
app: nginx
|
||||
@@ -96,8 +96,7 @@ func makeKustTarget(t *testing.T, l ifc.Loader) *KustTarget {
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir("/")
|
||||
kt, err := NewKustTarget(
|
||||
l, fakeFs, rf, transformer.NewFactoryImpl(),
|
||||
config.NewFactory(l).DefaultConfig())
|
||||
l, fakeFs, rf, transformer.NewFactoryImpl())
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected construction error %v", err)
|
||||
}
|
||||
@@ -132,12 +131,12 @@ var ns = gvk.Gvk{Version: "v1", Kind: "Namespace"}
|
||||
|
||||
func TestResources1(t *testing.T) {
|
||||
expected := resmap.ResMap{
|
||||
resid.NewResIdWithPrefixNamespace(deploy, "dply1", "foo-", "ns1"): rf.RF().FromMap(
|
||||
resid.NewResIdWithPrefixSuffixNamespace(deploy, "dply1", "foo-", "-bar", "ns1"): rf.RF().FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-dply1",
|
||||
"name": "foo-dply1-bar",
|
||||
"namespace": "ns1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
@@ -165,12 +164,12 @@ func TestResources1(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "literalConfigMap", "foo-", "ns1"): rf.RF().FromMap(
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "literalConfigMap", "foo-", "-bar", "ns1"): rf.RF().FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-literalConfigMap-mc92bgcbh5",
|
||||
"name": "foo-literalConfigMap-bar-8d2dkb8k24",
|
||||
"namespace": "ns1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
@@ -184,12 +183,12 @@ func TestResources1(t *testing.T) {
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
}).SetBehavior(ifc.BehaviorCreate),
|
||||
resid.NewResIdWithPrefixNamespace(secret, "secret", "foo-", "ns1"): rf.RF().FromMap(
|
||||
resid.NewResIdWithPrefixSuffixNamespace(secret, "secret", "foo-", "-bar", "ns1"): rf.RF().FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-secret-877fcfhgt5",
|
||||
"name": "foo-secret-bar-9btc7bt4kb",
|
||||
"namespace": "ns1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
@@ -204,12 +203,12 @@ func TestResources1(t *testing.T) {
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
}).SetBehavior(ifc.BehaviorCreate),
|
||||
resid.NewResIdWithPrefixNamespace(ns, "ns1", "foo-", ""): rf.RF().FromMap(
|
||||
resid.NewResIdWithPrefixSuffixNamespace(ns, "ns1", "foo-", "-bar", ""): rf.RF().FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Namespace",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-ns1",
|
||||
"name": "foo-ns1-bar",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
@@ -261,17 +260,17 @@ func TestSecretTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableHash(t *testing.T) {
|
||||
func TestDisableNameSuffixHash(t *testing.T) {
|
||||
kt := makeKustTarget(t, makeLoader1(t))
|
||||
kt.kustomization.GeneratorOptions = &types.GeneratorOptions{DisableHash: true}
|
||||
kt.kustomization.GeneratorOptions = &types.GeneratorOptions{DisableNameSuffixHash: true}
|
||||
actual, err := kt.MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected Resources error %v", err)
|
||||
}
|
||||
|
||||
for id, r := range actual {
|
||||
if !strings.HasSuffix(r.GetName(), id.Name()) {
|
||||
t.Fatalf("unexpected hash was added to %s: %s", id.Name(), r.GetName())
|
||||
if r.GetName() != id.NameWithPrefixSuffix() {
|
||||
t.Errorf("unexpected hash was added to %s: %s", id.NameWithPrefixSuffix(), r.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ nameReference:
|
||||
kind: Job
|
||||
- path: spec/jobTemplate/spec/template/spec/volumes/configMap/name
|
||||
kind: CronJob
|
||||
- path: spec/jobTemplate/spec/template/spec/containers/env/valueFrom/configmapKeyRef/name
|
||||
- path: spec/jobTemplate/spec/template/spec/containers/env/valueFrom/configMapKeyRef/name
|
||||
kind: CronJob
|
||||
- path: spec/jobTemplate/spec/template/spec/initContainers/env/valueFrom/configMapKeyRef/name
|
||||
kind: CronJob
|
||||
@@ -198,7 +198,7 @@ nameReference:
|
||||
kind: Job
|
||||
- path: spec/template/spec/imagePullSecrets/name
|
||||
kind: Job
|
||||
- path: spec/jobTemplate/spec/template/sepc/volumes/secret/secretName
|
||||
- path: spec/jobTemplate/spec/template/spec/volumes/secret/secretName
|
||||
kind: CronJob
|
||||
- path: spec/jobTemplate/spec/template/spec/containers/env/valueFrom/secretKeyRef/name
|
||||
kind: CronJob
|
||||
|
||||
@@ -38,8 +38,8 @@ namePrefix:
|
||||
kind: SomeKind
|
||||
`
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile("transformerconfig/test/config.yaml", []byte(transformerConfig))
|
||||
ldr := loader.NewFileLoader(fakeFS)
|
||||
fakeFS.WriteFile("/transformerconfig/test/config.yaml", []byte(transformerConfig))
|
||||
ldr := loader.NewFileLoaderAtRoot(fakeFS)
|
||||
expected := &TransformerConfig{
|
||||
NamePrefix: []FieldSpec{
|
||||
{
|
||||
@@ -55,7 +55,7 @@ func TestMakeTransformerConfigFromFiles(t *testing.T) {
|
||||
ldr, expected, _ := makeFakeLoaderAndOutput()
|
||||
tcfg, err := NewFactory(ldr).FromFiles([]string{"transformerconfig/test/config.yaml"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tcfg, expected) {
|
||||
|
||||
@@ -182,9 +182,11 @@ func TestLoadCRDs(t *testing.T) {
|
||||
NameReference: nbrs,
|
||||
}
|
||||
|
||||
actualTc, _ := NewFactory(makeLoader(t)).LoadCRDs(
|
||||
[]string{"/testpath/crd.json"})
|
||||
|
||||
actualTc, err := NewFactory(makeLoader(t)).LoadCRDs(
|
||||
[]string{"crd.json"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error:%v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualTc, expectedTc) {
|
||||
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
// TransformerConfig holds the data needed to perform transformations.
|
||||
type TransformerConfig struct {
|
||||
NamePrefix fsSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
|
||||
NameSuffix fsSlice `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
|
||||
NameSpace fsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
CommonLabels fsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
|
||||
CommonAnnotations fsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
|
||||
@@ -47,6 +48,11 @@ func (t *TransformerConfig) AddPrefixFieldSpec(fs FieldSpec) {
|
||||
t.NamePrefix = append(t.NamePrefix, fs)
|
||||
}
|
||||
|
||||
// AddSuffixFieldSpec adds a FieldSpec to NameSuffix
|
||||
func (t *TransformerConfig) AddSuffixFieldSpec(fs FieldSpec) {
|
||||
t.NameSuffix = append([]FieldSpec{fs}, t.NameSuffix...)
|
||||
}
|
||||
|
||||
// AddLabelFieldSpec adds a FieldSpec to CommonLabels
|
||||
func (t *TransformerConfig) AddLabelFieldSpec(fs FieldSpec) {
|
||||
t.CommonLabels = append(t.CommonLabels, fs)
|
||||
@@ -64,8 +70,12 @@ func (t *TransformerConfig) AddNamereferenceFieldSpec(nbrs NameBackReferences) {
|
||||
|
||||
// Merge merges two TransformerConfigs objects into a new TransformerConfig object
|
||||
func (t *TransformerConfig) Merge(input *TransformerConfig) *TransformerConfig {
|
||||
if input == nil {
|
||||
return t
|
||||
}
|
||||
merged := &TransformerConfig{}
|
||||
merged.NamePrefix = append(t.NamePrefix, input.NamePrefix...)
|
||||
merged.NameSuffix = append(input.NameSuffix, t.NameSuffix...)
|
||||
merged.NameSpace = append(t.NameSpace, input.NameSpace...)
|
||||
merged.CommonAnnotations = append(t.CommonAnnotations, input.CommonAnnotations...)
|
||||
merged.CommonLabels = append(t.CommonLabels, input.CommonLabels...)
|
||||
|
||||
@@ -61,6 +61,10 @@ func TestAddFieldSpecs(t *testing.T) {
|
||||
if len(cfg.NamePrefix) != 1 {
|
||||
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||
}
|
||||
cfg.AddSuffixFieldSpec(fieldSpec)
|
||||
if len(cfg.NameSuffix) != 1 {
|
||||
t.Fatalf("failed to add namesuffix FieldSpec")
|
||||
}
|
||||
cfg.AddLabelFieldSpec(fieldSpec)
|
||||
if len(cfg.CommonLabels) != 1 {
|
||||
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||
@@ -117,10 +121,12 @@ func TestMerge(t *testing.T) {
|
||||
cfga := &TransformerConfig{}
|
||||
cfga.AddNamereferenceFieldSpec(nameReference[0])
|
||||
cfga.AddPrefixFieldSpec(fieldSpecs[0])
|
||||
cfga.AddSuffixFieldSpec(fieldSpecs[0])
|
||||
|
||||
cfgb := &TransformerConfig{}
|
||||
cfgb.AddNamereferenceFieldSpec(nameReference[1])
|
||||
cfgb.AddPrefixFieldSpec(fieldSpecs[1])
|
||||
cfga.AddSuffixFieldSpec(fieldSpecs[1])
|
||||
|
||||
actual := cfga.Merge(cfgb)
|
||||
|
||||
@@ -128,6 +134,10 @@ func TestMerge(t *testing.T) {
|
||||
t.Fatal("merge failed for namePrefix FieldSpec")
|
||||
}
|
||||
|
||||
if len(actual.NameSuffix) != 2 {
|
||||
t.Fatal("merge failed for nameSuffix FieldSpec")
|
||||
}
|
||||
|
||||
if len(actual.NameReference) != 1 {
|
||||
t.Fatal("merge failed for namereference FieldSpec")
|
||||
}
|
||||
@@ -137,8 +147,15 @@ func TestMerge(t *testing.T) {
|
||||
expected.AddNamereferenceFieldSpec(nameReference[1])
|
||||
expected.AddPrefixFieldSpec(fieldSpecs[0])
|
||||
expected.AddPrefixFieldSpec(fieldSpecs[1])
|
||||
expected.AddSuffixFieldSpec(fieldSpecs[0])
|
||||
expected.AddSuffixFieldSpec(fieldSpecs[1])
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("expected: %v\n but got: %v\n", expected, actual)
|
||||
}
|
||||
|
||||
actual = cfga.Merge(nil)
|
||||
if !reflect.DeepEqual(actual, cfga) {
|
||||
t.Fatalf("expected: %v\n but got: %v\n", cfga, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (o *mapTransformer) Transform(m resmap.ResMap) error {
|
||||
func (o *mapTransformer) addMap(in interface{}) (interface{}, error) {
|
||||
m, ok := in.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%#v is expectd to be %T", in, m)
|
||||
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
|
||||
}
|
||||
for k, v := range o.m {
|
||||
m[k] = v
|
||||
|
||||
@@ -18,11 +18,17 @@ package transformers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type mutateFunc func(interface{}) (interface{}, error)
|
||||
|
||||
func mutateField(m map[string]interface{}, pathToField []string, createIfNotPresent bool, fns ...mutateFunc) error {
|
||||
func mutateField(
|
||||
m map[string]interface{},
|
||||
pathToField []string,
|
||||
createIfNotPresent bool,
|
||||
fns ...mutateFunc) error {
|
||||
if len(pathToField) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -49,6 +55,11 @@ func mutateField(m map[string]interface{}, pathToField []string, createIfNotPres
|
||||
v := m[pathToField[0]]
|
||||
newPathToField := pathToField[1:]
|
||||
switch typedV := v.(type) {
|
||||
case nil:
|
||||
log.Printf(
|
||||
"nil value at `%s` ignored in mutation attempt",
|
||||
strings.Join(pathToField, "."))
|
||||
return nil
|
||||
case map[string]interface{}:
|
||||
return mutateField(typedV, newPathToField, createIfNotPresent, fns...)
|
||||
case []interface{}:
|
||||
@@ -56,7 +67,7 @@ func mutateField(m map[string]interface{}, pathToField []string, createIfNotPres
|
||||
item := typedV[i]
|
||||
typedItem, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("%#v is expectd to be %T", item, typedItem)
|
||||
return fmt.Errorf("%#v is expected to be %T", item, typedItem)
|
||||
}
|
||||
err := mutateField(typedItem, newPathToField, createIfNotPresent, fns...)
|
||||
if err != nil {
|
||||
168
pkg/transformers/mutatefield_test.go
Normal file
168
pkg/transformers/mutatefield_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type noopMutator struct {
|
||||
wasCalled bool
|
||||
errorToReturn error
|
||||
}
|
||||
|
||||
var errExpected = fmt.Errorf("oops")
|
||||
|
||||
const originalValue = "tomato"
|
||||
const newValue = "notThe" + originalValue
|
||||
|
||||
func (m *noopMutator) mutate(in interface{}) (interface{}, error) {
|
||||
m.wasCalled = true
|
||||
return newValue, m.errorToReturn
|
||||
}
|
||||
|
||||
func makeTestDeployment() ifc.Kunstructured {
|
||||
factory := kunstruct.NewKunstructuredFactoryImpl()
|
||||
return factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"group": "apps",
|
||||
"apiVersion": "v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": originalValue,
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
"env": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "HELLO",
|
||||
"value": "hi there",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "GOODBYE",
|
||||
"value": "adios!",
|
||||
},
|
||||
},
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"vegetable": originalValue,
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "tangerine",
|
||||
"image": originalValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func getFieldValue(t *testing.T, obj ifc.Kunstructured, fieldName string) string {
|
||||
v, err := obj.GetFieldValue(fieldName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected field error: %v", err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func TestNoPath(t *testing.T) {
|
||||
obj := makeTestDeployment()
|
||||
m := &noopMutator{}
|
||||
err := mutateField(
|
||||
obj.Map(), []string{}, false, m.mutate)
|
||||
if m.wasCalled {
|
||||
t.Fatalf("mutator should not have been called.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHappyPath(t *testing.T) {
|
||||
obj := makeTestDeployment()
|
||||
v := getFieldValue(t, obj, "metadata.name")
|
||||
if v != originalValue {
|
||||
t.Fatalf("unexpected original value: %v", v)
|
||||
}
|
||||
v = getFieldValue(t, obj, "spec.template.metadata.labels.vegetable")
|
||||
if v != originalValue {
|
||||
t.Fatalf("unexpected original value: %v", v)
|
||||
}
|
||||
|
||||
m := &noopMutator{}
|
||||
err := mutateField(
|
||||
obj.Map(), []string{"metadata", "name"}, false, m.mutate)
|
||||
if !m.wasCalled {
|
||||
t.Fatalf("mutator should have been called.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected mutate error: %v", err)
|
||||
}
|
||||
v = getFieldValue(t, obj, "metadata.name")
|
||||
if v != newValue {
|
||||
t.Fatalf("unexpected new value: %v", v)
|
||||
}
|
||||
|
||||
m = &noopMutator{}
|
||||
err = mutateField(
|
||||
obj.Map(), []string{"spec", "template", "metadata", "labels", "vegetable"}, false, m.mutate)
|
||||
if !m.wasCalled {
|
||||
t.Fatalf("mutator should have been called.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected mutate error: %v", err)
|
||||
}
|
||||
v = getFieldValue(t, obj, "spec.template.metadata.labels.vegetable")
|
||||
if v != newValue {
|
||||
t.Fatalf("unexpected new value: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithError(t *testing.T) {
|
||||
obj := makeTestDeployment()
|
||||
m := noopMutator{errorToReturn: errExpected}
|
||||
err := mutateField(
|
||||
obj.Map(), []string{"metadata", "name"}, false, m.mutate)
|
||||
if !m.wasCalled {
|
||||
t.Fatalf("mutator was not called!")
|
||||
}
|
||||
if err != errExpected {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithNil(t *testing.T) {
|
||||
obj := makeTestDeployment()
|
||||
foo := obj.Map()["spec"]
|
||||
foo = foo.(map[string]interface{})["template"]
|
||||
foo = foo.(map[string]interface{})["metadata"]
|
||||
foo.(map[string]interface{})["labels"] = nil
|
||||
|
||||
m := &noopMutator{}
|
||||
err := mutateField(
|
||||
obj.Map(), []string{"spec", "template", "metadata", "labels", "vegetable"}, false, m.mutate)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
)
|
||||
@@ -42,22 +41,26 @@ func NewNameReferenceTransformer(
|
||||
return &nameReferenceTransformer{backRefs: br}, nil
|
||||
}
|
||||
|
||||
// Transform does the fields update according to fieldSpecs.
|
||||
// Transform does the field update according to fieldSpecs.
|
||||
// The old name is in the key in the map and the new name is in the object
|
||||
// associated with the key. e.g. if <k, v> is one of the key-value pair in the map,
|
||||
// then the old name is k.Name and the new name is v.GetName()
|
||||
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||
// TODO: Too much looping.
|
||||
// Even more hidden loops in FilterBy,
|
||||
// updateNameReference and FindByGVKN.
|
||||
for id := range m {
|
||||
objMap := m[id].Map()
|
||||
for _, backRef := range o.backRefs {
|
||||
for _, path := range backRef.FieldSpecs {
|
||||
if !id.Gvk().IsSelected(&path.Gvk) {
|
||||
continue
|
||||
}
|
||||
err := mutateField(objMap, path.PathSlice(), path.CreateIfNotPresent,
|
||||
o.updateNameReference(backRef.Gvk, m.FilterBy(id)))
|
||||
if err != nil {
|
||||
return err
|
||||
for _, fSpec := range backRef.FieldSpecs {
|
||||
if id.Gvk().IsSelected(&fSpec.Gvk) {
|
||||
err := mutateField(
|
||||
m[id].Map(), fSpec.PathSlice(),
|
||||
fSpec.CreateIfNotPresent,
|
||||
o.updateNameReference(
|
||||
backRef.Gvk, m.FilterBy(id)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,33 +69,26 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||
}
|
||||
|
||||
func (o *nameReferenceTransformer) updateNameReference(
|
||||
k gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) {
|
||||
backRef gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) {
|
||||
return func(in interface{}) (interface{}, error) {
|
||||
s, ok := in.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
|
||||
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
||||
}
|
||||
|
||||
for id, res := range m {
|
||||
if !id.Gvk().IsSelected(&k) {
|
||||
continue
|
||||
}
|
||||
if id.Name() == s {
|
||||
err := o.detectConflict(id, m, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if id.Gvk().IsSelected(&backRef) && id.Name() == s {
|
||||
matchedIds := m.FindByGVKN(id)
|
||||
// If there's more than one match, there's no way
|
||||
// to know which one to pick, so emit error.
|
||||
if len(matchedIds) > 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"Multiple matches for name %s:\n %v", id, matchedIds)
|
||||
}
|
||||
// Return transformed name of the object,
|
||||
// complete with prefixes, hashes, etc.
|
||||
return res.GetName(), nil
|
||||
}
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *nameReferenceTransformer) detectConflict(id resid.ResId, m resmap.ResMap, name string) error {
|
||||
matchedIds := m.FindByGVKN(id)
|
||||
if len(matchedIds) > 1 {
|
||||
return fmt.Errorf("detected conflicts when resolving name references %s:\n%v", name, matchedIds)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,42 +26,45 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
)
|
||||
|
||||
// namePrefixTransformer contains the prefix and the FieldSpecs
|
||||
// for each field needing a name prefix.
|
||||
type namePrefixTransformer struct {
|
||||
// namePrefixSuffixTransformer contains the prefix, suffix, and the FieldSpecs
|
||||
// for each field needing a name prefix and suffix.
|
||||
type namePrefixSuffixTransformer struct {
|
||||
prefix string
|
||||
suffix string
|
||||
fieldSpecsToUse []config.FieldSpec
|
||||
fieldSpecsToSkip []config.FieldSpec
|
||||
}
|
||||
|
||||
var _ Transformer = &namePrefixTransformer{}
|
||||
var _ Transformer = &namePrefixSuffixTransformer{}
|
||||
|
||||
var prefixFieldSpecsToSkip = []config.FieldSpec{
|
||||
var prefixSuffixFieldSpecsToSkip = []config.FieldSpec{
|
||||
{
|
||||
Gvk: gvk.Gvk{Kind: "CustomResourceDefinition"},
|
||||
},
|
||||
}
|
||||
|
||||
// deprecateNamePrefixFieldSpec will be moved into prefixFieldSpecsToSkip in next release
|
||||
var deprecateNamePrefixFieldSpec = config.FieldSpec{
|
||||
// deprecateNamePrefixSuffixFieldSpec will be moved into prefixSuffixFieldSpecsToSkip in next release
|
||||
var deprecateNamePrefixSuffixFieldSpec = config.FieldSpec{
|
||||
Gvk: gvk.Gvk{Kind: "Namespace"},
|
||||
}
|
||||
|
||||
// NewNamePrefixTransformer construct a namePrefixTransformer.
|
||||
func NewNamePrefixTransformer(np string, pc []config.FieldSpec) (Transformer, error) {
|
||||
if len(np) == 0 {
|
||||
// NewNamePrefixSuffixTransformer construct a namePrefixSuffixTransformer.
|
||||
func NewNamePrefixSuffixTransformer(np, ns string, pc []config.FieldSpec) (Transformer, error) {
|
||||
if len(np) == 0 && len(ns) == 0 {
|
||||
return NewNoOpTransformer(), nil
|
||||
}
|
||||
if pc == nil {
|
||||
return nil, errors.New("fieldSpecs is not expected to be nil")
|
||||
}
|
||||
return &namePrefixTransformer{fieldSpecsToUse: pc, prefix: np, fieldSpecsToSkip: prefixFieldSpecsToSkip}, nil
|
||||
return &namePrefixSuffixTransformer{fieldSpecsToUse: pc, prefix: np, suffix: ns, fieldSpecsToSkip: prefixSuffixFieldSpecsToSkip}, nil
|
||||
}
|
||||
|
||||
// Transform prepends the name prefix.
|
||||
func (o *namePrefixTransformer) Transform(m resmap.ResMap) error {
|
||||
// Transform prepends the name prefix and appends the name suffix.
|
||||
func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
|
||||
// Fill map "mf" with entries subject to name modification, and
|
||||
// delete these entries from "m", so that for now m retains only
|
||||
// the entries whose names will not be modified.
|
||||
mf := resmap.ResMap{}
|
||||
|
||||
for id := range m {
|
||||
found := false
|
||||
for _, path := range o.fieldSpecsToSkip {
|
||||
@@ -77,29 +80,29 @@ func (o *namePrefixTransformer) Transform(m resmap.ResMap) error {
|
||||
}
|
||||
|
||||
for id := range mf {
|
||||
if id.Gvk().IsSelected(&deprecateNamePrefixFieldSpec.Gvk) {
|
||||
log.Println("Adding nameprefix to Namespace resource will be deprecated in next release.")
|
||||
if id.Gvk().IsSelected(&deprecateNamePrefixSuffixFieldSpec.Gvk) {
|
||||
log.Println("Adding nameprefix and namesuffix to Namespace resource will be deprecated in next release.")
|
||||
}
|
||||
objMap := mf[id].Map()
|
||||
for _, path := range o.fieldSpecsToUse {
|
||||
if !id.Gvk().IsSelected(&path.Gvk) {
|
||||
continue
|
||||
}
|
||||
err := mutateField(objMap, path.PathSlice(), path.CreateIfNotPresent, o.addPrefix)
|
||||
err := mutateField(objMap, path.PathSlice(), path.CreateIfNotPresent, o.addPrefixSuffix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newId := id.CopyWithNewPrefix(o.prefix)
|
||||
newId := id.CopyWithNewPrefixSuffix(o.prefix, o.suffix)
|
||||
m[newId] = mf[id]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *namePrefixTransformer) addPrefix(in interface{}) (interface{}, error) {
|
||||
func (o *namePrefixSuffixTransformer) addPrefixSuffix(in interface{}) (interface{}, error) {
|
||||
s, ok := in.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
|
||||
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
||||
}
|
||||
return o.prefix + s, nil
|
||||
return fmt.Sprintf("%s%s%s", o.prefix, s, o.suffix), nil
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
func TestPrefixNameRun(t *testing.T) {
|
||||
func TestPrefixSuffixNameRun(t *testing.T) {
|
||||
rf := resource.NewFactory(
|
||||
kunstruct.NewKunstructuredFactoryImpl())
|
||||
m := resmap.ResMap{
|
||||
@@ -56,20 +56,20 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
expected := resmap.ResMap{
|
||||
resid.NewResIdWithPrefix(cmap, "cm1", "someprefix-"): rf.FromMap(
|
||||
resid.NewResIdWithPrefixSuffix(cmap, "cm1", "someprefix-", "-somesuffix"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "someprefix-cm1",
|
||||
"name": "someprefix-cm1-somesuffix",
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefix(cmap, "cm2", "someprefix-"): rf.FromMap(
|
||||
resid.NewResIdWithPrefixSuffix(cmap, "cm2", "someprefix-", "-somesuffix"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "someprefix-cm2",
|
||||
"name": "someprefix-cm2-somesuffix",
|
||||
},
|
||||
}),
|
||||
resid.NewResId(crd, "crd"): rf.FromMap(
|
||||
@@ -82,12 +82,12 @@ func TestPrefixNameRun(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
npt, err := NewNamePrefixTransformer(
|
||||
"someprefix-", defaultTransformerConfig.NamePrefix)
|
||||
npst, err := NewNamePrefixSuffixTransformer(
|
||||
"someprefix-", "-somesuffix", defaultTransformerConfig.NamePrefix)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
err = npt.Transform(m)
|
||||
err = npst.Transform(m)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (rv *refvarTransformer) Transform(resources resmap.ResMap) error {
|
||||
case interface{}:
|
||||
s, ok := in.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%#v is expectd to be %T", in, s)
|
||||
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
||||
}
|
||||
runtimeVal := expansion.Expand(s, mappingFunc)
|
||||
return runtimeVal, nil
|
||||
|
||||
@@ -42,6 +42,10 @@ type Kustomization struct {
|
||||
// file including generated configmaps and secrets.
|
||||
NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
|
||||
|
||||
// NameSuffix will suffix the names of all resources mentioned in the kustomization
|
||||
// file including generated configmaps and secrets.
|
||||
NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
|
||||
|
||||
// Namespace to add to all objects.
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
|
||||
@@ -116,6 +120,9 @@ type Kustomization struct {
|
||||
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
||||
GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
|
||||
|
||||
// Configurations is a list of transformer configuration files
|
||||
Configurations []string `json:"configurations,omitempty" yaml:"configurations,omitempty"`
|
||||
|
||||
//
|
||||
// Deprecated fields - See DealWithDeprecatedFields
|
||||
//
|
||||
@@ -249,8 +256,8 @@ type GeneratorOptions struct {
|
||||
// resource generation. Default at time of writing: {'sh', '-c'}.
|
||||
Shell []string `json:"shell,omitempty" yaml:"shell,omitempty"`
|
||||
|
||||
// DisableNameHash if true disables the default behavior of adding a
|
||||
// DisableNameSuffixHash if true disables the default behavior of adding a
|
||||
// suffix to the names of generated resources that is a hash of the
|
||||
// resource contents.
|
||||
DisableHash bool `json:"disableHash,omitempty" yaml:"disableHash,omitempty"`
|
||||
DisableNameSuffixHash bool `json:"disableNameSuffixHash,omitempty" yaml:"disableNameSuffixHash,omitempty"`
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ limitations under the License.
|
||||
package types
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
// Var represents a variable whose value will be sourced
|
||||
@@ -31,7 +32,7 @@ type Var struct {
|
||||
// ObjRef must refer to a Kubernetes resource under the
|
||||
// purview of this kustomization. ObjRef should use the
|
||||
// raw name of the object (the name specified in its YAML,
|
||||
// before addition of a namePrefix).
|
||||
// before addition of a namePrefix and a nameSuffix).
|
||||
ObjRef Target `json:"objref" yaml:"objref"`
|
||||
|
||||
// FieldRef refers to the field of the object referred to by
|
||||
|
||||
202
vendor/github.com/aws/aws-sdk-go/LICENSE.txt
generated
vendored
202
vendor/github.com/aws/aws-sdk-go/LICENSE.txt
generated
vendored
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
3
vendor/github.com/aws/aws-sdk-go/NOTICE.txt
generated
vendored
3
vendor/github.com/aws/aws-sdk-go/NOTICE.txt
generated
vendored
@@ -1,3 +0,0 @@
|
||||
AWS SDK for Go
|
||||
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Copyright 2014-2015 Stripe, Inc.
|
||||
145
vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
145
vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go
generated
vendored
@@ -1,145 +0,0 @@
|
||||
// Package awserr represents API error interface accessors for the SDK.
|
||||
package awserr
|
||||
|
||||
// An Error wraps lower level errors with code, message and an original error.
|
||||
// The underlying concrete error type may also satisfy other interfaces which
|
||||
// can be to used to obtain more specific information about the error.
|
||||
//
|
||||
// Calling Error() or String() will always include the full information about
|
||||
// an error based on its underlying type.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// output, err := s3manage.Upload(svc, input, opts)
|
||||
// if err != nil {
|
||||
// if awsErr, ok := err.(awserr.Error); ok {
|
||||
// // Get error details
|
||||
// log.Println("Error:", awsErr.Code(), awsErr.Message())
|
||||
//
|
||||
// // Prints out full error message, including original error if there was one.
|
||||
// log.Println("Error:", awsErr.Error())
|
||||
//
|
||||
// // Get original error
|
||||
// if origErr := awsErr.OrigErr(); origErr != nil {
|
||||
// // operate on original error.
|
||||
// }
|
||||
// } else {
|
||||
// fmt.Println(err.Error())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
type Error interface {
|
||||
// Satisfy the generic error interface.
|
||||
error
|
||||
|
||||
// Returns the short phrase depicting the classification of the error.
|
||||
Code() string
|
||||
|
||||
// Returns the error details message.
|
||||
Message() string
|
||||
|
||||
// Returns the original error if one was set. Nil is returned if not set.
|
||||
OrigErr() error
|
||||
}
|
||||
|
||||
// BatchError is a batch of errors which also wraps lower level errors with
|
||||
// code, message, and original errors. Calling Error() will include all errors
|
||||
// that occurred in the batch.
|
||||
//
|
||||
// Deprecated: Replaced with BatchedErrors. Only defined for backwards
|
||||
// compatibility.
|
||||
type BatchError interface {
|
||||
// Satisfy the generic error interface.
|
||||
error
|
||||
|
||||
// Returns the short phrase depicting the classification of the error.
|
||||
Code() string
|
||||
|
||||
// Returns the error details message.
|
||||
Message() string
|
||||
|
||||
// Returns the original error if one was set. Nil is returned if not set.
|
||||
OrigErrs() []error
|
||||
}
|
||||
|
||||
// BatchedErrors is a batch of errors which also wraps lower level errors with
|
||||
// code, message, and original errors. Calling Error() will include all errors
|
||||
// that occurred in the batch.
|
||||
//
|
||||
// Replaces BatchError
|
||||
type BatchedErrors interface {
|
||||
// Satisfy the base Error interface.
|
||||
Error
|
||||
|
||||
// Returns the original error if one was set. Nil is returned if not set.
|
||||
OrigErrs() []error
|
||||
}
|
||||
|
||||
// New returns an Error object described by the code, message, and origErr.
|
||||
//
|
||||
// If origErr satisfies the Error interface it will not be wrapped within a new
|
||||
// Error object and will instead be returned.
|
||||
func New(code, message string, origErr error) Error {
|
||||
var errs []error
|
||||
if origErr != nil {
|
||||
errs = append(errs, origErr)
|
||||
}
|
||||
return newBaseError(code, message, errs)
|
||||
}
|
||||
|
||||
// NewBatchError returns an BatchedErrors with a collection of errors as an
|
||||
// array of errors.
|
||||
func NewBatchError(code, message string, errs []error) BatchedErrors {
|
||||
return newBaseError(code, message, errs)
|
||||
}
|
||||
|
||||
// A RequestFailure is an interface to extract request failure information from
|
||||
// an Error such as the request ID of the failed request returned by a service.
|
||||
// RequestFailures may not always have a requestID value if the request failed
|
||||
// prior to reaching the service such as a connection error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// output, err := s3manage.Upload(svc, input, opts)
|
||||
// if err != nil {
|
||||
// if reqerr, ok := err.(RequestFailure); ok {
|
||||
// log.Println("Request failed", reqerr.Code(), reqerr.Message(), reqerr.RequestID())
|
||||
// } else {
|
||||
// log.Println("Error:", err.Error())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Combined with awserr.Error:
|
||||
//
|
||||
// output, err := s3manage.Upload(svc, input, opts)
|
||||
// if err != nil {
|
||||
// if awsErr, ok := err.(awserr.Error); ok {
|
||||
// // Generic AWS Error with Code, Message, and original error (if any)
|
||||
// fmt.Println(awsErr.Code(), awsErr.Message(), awsErr.OrigErr())
|
||||
//
|
||||
// if reqErr, ok := err.(awserr.RequestFailure); ok {
|
||||
// // A service error occurred
|
||||
// fmt.Println(reqErr.StatusCode(), reqErr.RequestID())
|
||||
// }
|
||||
// } else {
|
||||
// fmt.Println(err.Error())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
type RequestFailure interface {
|
||||
Error
|
||||
|
||||
// The status code of the HTTP response.
|
||||
StatusCode() int
|
||||
|
||||
// The request ID returned by the service for a request failure. This will
|
||||
// be empty if no request ID is available such as the request failed due
|
||||
// to a connection error.
|
||||
RequestID() string
|
||||
}
|
||||
|
||||
// NewRequestFailure returns a new request error wrapper for the given Error
|
||||
// provided.
|
||||
func NewRequestFailure(err Error, statusCode int, reqID string) RequestFailure {
|
||||
return newRequestError(err, statusCode, reqID)
|
||||
}
|
||||
194
vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
194
vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go
generated
vendored
@@ -1,194 +0,0 @@
|
||||
package awserr
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SprintError returns a string of the formatted error code.
|
||||
//
|
||||
// Both extra and origErr are optional. If they are included their lines
|
||||
// will be added, but if they are not included their lines will be ignored.
|
||||
func SprintError(code, message, extra string, origErr error) string {
|
||||
msg := fmt.Sprintf("%s: %s", code, message)
|
||||
if extra != "" {
|
||||
msg = fmt.Sprintf("%s\n\t%s", msg, extra)
|
||||
}
|
||||
if origErr != nil {
|
||||
msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error())
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// A baseError wraps the code and message which defines an error. It also
|
||||
// can be used to wrap an original error object.
|
||||
//
|
||||
// Should be used as the root for errors satisfying the awserr.Error. Also
|
||||
// for any error which does not fit into a specific error wrapper type.
|
||||
type baseError struct {
|
||||
// Classification of error
|
||||
code string
|
||||
|
||||
// Detailed information about error
|
||||
message string
|
||||
|
||||
// Optional original error this error is based off of. Allows building
|
||||
// chained errors.
|
||||
errs []error
|
||||
}
|
||||
|
||||
// newBaseError returns an error object for the code, message, and errors.
|
||||
//
|
||||
// code is a short no whitespace phrase depicting the classification of
|
||||
// the error that is being created.
|
||||
//
|
||||
// message is the free flow string containing detailed information about the
|
||||
// error.
|
||||
//
|
||||
// origErrs is the error objects which will be nested under the new errors to
|
||||
// be returned.
|
||||
func newBaseError(code, message string, origErrs []error) *baseError {
|
||||
b := &baseError{
|
||||
code: code,
|
||||
message: message,
|
||||
errs: origErrs,
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Error returns the string representation of the error.
|
||||
//
|
||||
// See ErrorWithExtra for formatting.
|
||||
//
|
||||
// Satisfies the error interface.
|
||||
func (b baseError) Error() string {
|
||||
size := len(b.errs)
|
||||
if size > 0 {
|
||||
return SprintError(b.code, b.message, "", errorList(b.errs))
|
||||
}
|
||||
|
||||
return SprintError(b.code, b.message, "", nil)
|
||||
}
|
||||
|
||||
// String returns the string representation of the error.
|
||||
// Alias for Error to satisfy the stringer interface.
|
||||
func (b baseError) String() string {
|
||||
return b.Error()
|
||||
}
|
||||
|
||||
// Code returns the short phrase depicting the classification of the error.
|
||||
func (b baseError) Code() string {
|
||||
return b.code
|
||||
}
|
||||
|
||||
// Message returns the error details message.
|
||||
func (b baseError) Message() string {
|
||||
return b.message
|
||||
}
|
||||
|
||||
// OrigErr returns the original error if one was set. Nil is returned if no
|
||||
// error was set. This only returns the first element in the list. If the full
|
||||
// list is needed, use BatchedErrors.
|
||||
func (b baseError) OrigErr() error {
|
||||
switch len(b.errs) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return b.errs[0]
|
||||
default:
|
||||
if err, ok := b.errs[0].(Error); ok {
|
||||
return NewBatchError(err.Code(), err.Message(), b.errs[1:])
|
||||
}
|
||||
return NewBatchError("BatchedErrors",
|
||||
"multiple errors occurred", b.errs)
|
||||
}
|
||||
}
|
||||
|
||||
// OrigErrs returns the original errors if one was set. An empty slice is
|
||||
// returned if no error was set.
|
||||
func (b baseError) OrigErrs() []error {
|
||||
return b.errs
|
||||
}
|
||||
|
||||
// So that the Error interface type can be included as an anonymous field
|
||||
// in the requestError struct and not conflict with the error.Error() method.
|
||||
type awsError Error
|
||||
|
||||
// A requestError wraps a request or service error.
|
||||
//
|
||||
// Composed of baseError for code, message, and original error.
|
||||
type requestError struct {
|
||||
awsError
|
||||
statusCode int
|
||||
requestID string
|
||||
}
|
||||
|
||||
// newRequestError returns a wrapped error with additional information for
|
||||
// request status code, and service requestID.
|
||||
//
|
||||
// Should be used to wrap all request which involve service requests. Even if
|
||||
// the request failed without a service response, but had an HTTP status code
|
||||
// that may be meaningful.
|
||||
//
|
||||
// Also wraps original errors via the baseError.
|
||||
func newRequestError(err Error, statusCode int, requestID string) *requestError {
|
||||
return &requestError{
|
||||
awsError: err,
|
||||
statusCode: statusCode,
|
||||
requestID: requestID,
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns the string representation of the error.
|
||||
// Satisfies the error interface.
|
||||
func (r requestError) Error() string {
|
||||
extra := fmt.Sprintf("status code: %d, request id: %s",
|
||||
r.statusCode, r.requestID)
|
||||
return SprintError(r.Code(), r.Message(), extra, r.OrigErr())
|
||||
}
|
||||
|
||||
// String returns the string representation of the error.
|
||||
// Alias for Error to satisfy the stringer interface.
|
||||
func (r requestError) String() string {
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// StatusCode returns the wrapped status code for the error
|
||||
func (r requestError) StatusCode() int {
|
||||
return r.statusCode
|
||||
}
|
||||
|
||||
// RequestID returns the wrapped requestID
|
||||
func (r requestError) RequestID() string {
|
||||
return r.requestID
|
||||
}
|
||||
|
||||
// OrigErrs returns the original errors if one was set. An empty slice is
|
||||
// returned if no error was set.
|
||||
func (r requestError) OrigErrs() []error {
|
||||
if b, ok := r.awsError.(BatchedErrors); ok {
|
||||
return b.OrigErrs()
|
||||
}
|
||||
return []error{r.OrigErr()}
|
||||
}
|
||||
|
||||
// An error list that satisfies the golang interface
|
||||
type errorList []error
|
||||
|
||||
// Error returns the string representation of the error.
|
||||
//
|
||||
// Satisfies the error interface.
|
||||
func (e errorList) Error() string {
|
||||
msg := ""
|
||||
// How do we want to handle the array size being zero
|
||||
if size := len(e); size > 0 {
|
||||
for i := 0; i < size; i++ {
|
||||
msg += fmt.Sprintf("%s", e[i].Error())
|
||||
// We check the next index to see if it is within the slice.
|
||||
// If it is, then we append a newline. We do this, because unit tests
|
||||
// could be broken with the additional '\n'
|
||||
if i+1 < size {
|
||||
msg += "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
108
vendor/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
108
vendor/github.com/aws/aws-sdk-go/aws/awsutil/copy.go
generated
vendored
@@ -1,108 +0,0 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Copy deeply copies a src structure to dst. Useful for copying request and
|
||||
// response structures.
|
||||
//
|
||||
// Can copy between structs of different type, but will only copy fields which
|
||||
// are assignable, and exist in both structs. Fields which are not assignable,
|
||||
// or do not exist in both structs are ignored.
|
||||
func Copy(dst, src interface{}) {
|
||||
dstval := reflect.ValueOf(dst)
|
||||
if !dstval.IsValid() {
|
||||
panic("Copy dst cannot be nil")
|
||||
}
|
||||
|
||||
rcopy(dstval, reflect.ValueOf(src), true)
|
||||
}
|
||||
|
||||
// CopyOf returns a copy of src while also allocating the memory for dst.
|
||||
// src must be a pointer type or this operation will fail.
|
||||
func CopyOf(src interface{}) (dst interface{}) {
|
||||
dsti := reflect.New(reflect.TypeOf(src).Elem())
|
||||
dst = dsti.Interface()
|
||||
rcopy(dsti, reflect.ValueOf(src), true)
|
||||
return
|
||||
}
|
||||
|
||||
// rcopy performs a recursive copy of values from the source to destination.
|
||||
//
|
||||
// root is used to skip certain aspects of the copy which are not valid
|
||||
// for the root node of a object.
|
||||
func rcopy(dst, src reflect.Value, root bool) {
|
||||
if !src.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch src.Kind() {
|
||||
case reflect.Ptr:
|
||||
if _, ok := src.Interface().(io.Reader); ok {
|
||||
if dst.Kind() == reflect.Ptr && dst.Elem().CanSet() {
|
||||
dst.Elem().Set(src)
|
||||
} else if dst.CanSet() {
|
||||
dst.Set(src)
|
||||
}
|
||||
} else {
|
||||
e := src.Type().Elem()
|
||||
if dst.CanSet() && !src.IsNil() {
|
||||
if _, ok := src.Interface().(*time.Time); !ok {
|
||||
dst.Set(reflect.New(e))
|
||||
} else {
|
||||
tempValue := reflect.New(e)
|
||||
tempValue.Elem().Set(src.Elem())
|
||||
// Sets time.Time's unexported values
|
||||
dst.Set(tempValue)
|
||||
}
|
||||
}
|
||||
if src.Elem().IsValid() {
|
||||
// Keep the current root state since the depth hasn't changed
|
||||
rcopy(dst.Elem(), src.Elem(), root)
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
t := dst.Type()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
name := t.Field(i).Name
|
||||
srcVal := src.FieldByName(name)
|
||||
dstVal := dst.FieldByName(name)
|
||||
if srcVal.IsValid() && dstVal.CanSet() {
|
||||
rcopy(dstVal, srcVal, false)
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
if src.IsNil() {
|
||||
break
|
||||
}
|
||||
|
||||
s := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||
dst.Set(s)
|
||||
for i := 0; i < src.Len(); i++ {
|
||||
rcopy(dst.Index(i), src.Index(i), false)
|
||||
}
|
||||
case reflect.Map:
|
||||
if src.IsNil() {
|
||||
break
|
||||
}
|
||||
|
||||
s := reflect.MakeMap(src.Type())
|
||||
dst.Set(s)
|
||||
for _, k := range src.MapKeys() {
|
||||
v := src.MapIndex(k)
|
||||
v2 := reflect.New(v.Type()).Elem()
|
||||
rcopy(v2, v, false)
|
||||
dst.SetMapIndex(k, v2)
|
||||
}
|
||||
default:
|
||||
// Assign the value if possible. If its not assignable, the value would
|
||||
// need to be converted and the impact of that may be unexpected, or is
|
||||
// not compatible with the dst type.
|
||||
if src.Type().AssignableTo(dst.Type()) {
|
||||
dst.Set(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
27
vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// DeepEqual returns if the two values are deeply equal like reflect.DeepEqual.
|
||||
// In addition to this, this method will also dereference the input values if
|
||||
// possible so the DeepEqual performed will not fail if one parameter is a
|
||||
// pointer and the other is not.
|
||||
//
|
||||
// DeepEqual will not perform indirection of nested values of the input parameters.
|
||||
func DeepEqual(a, b interface{}) bool {
|
||||
ra := reflect.Indirect(reflect.ValueOf(a))
|
||||
rb := reflect.Indirect(reflect.ValueOf(b))
|
||||
|
||||
if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid {
|
||||
// If the elements are both nil, and of the same type the are equal
|
||||
// If they are of different types they are not equal
|
||||
return reflect.TypeOf(a) == reflect.TypeOf(b)
|
||||
} else if raValid != rbValid {
|
||||
// Both values must be valid to be equal
|
||||
return false
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(ra.Interface(), rb.Interface())
|
||||
}
|
||||
222
vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
222
vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go
generated
vendored
@@ -1,222 +0,0 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jmespath/go-jmespath"
|
||||
)
|
||||
|
||||
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
|
||||
|
||||
// rValuesAtPath returns a slice of values found in value v. The values
|
||||
// in v are explored recursively so all nested values are collected.
|
||||
func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value {
|
||||
pathparts := strings.Split(path, "||")
|
||||
if len(pathparts) > 1 {
|
||||
for _, pathpart := range pathparts {
|
||||
vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
|
||||
if len(vals) > 0 {
|
||||
return vals
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
|
||||
components := strings.Split(path, ".")
|
||||
for len(values) > 0 && len(components) > 0 {
|
||||
var index *int64
|
||||
var indexStar bool
|
||||
c := strings.TrimSpace(components[0])
|
||||
if c == "" { // no actual component, illegal syntax
|
||||
return nil
|
||||
} else if caseSensitive && c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
|
||||
// TODO normalize case for user
|
||||
return nil // don't support unexported fields
|
||||
}
|
||||
|
||||
// parse this component
|
||||
if m := indexRe.FindStringSubmatch(c); m != nil {
|
||||
c = m[1]
|
||||
if m[2] == "" {
|
||||
index = nil
|
||||
indexStar = true
|
||||
} else {
|
||||
i, _ := strconv.ParseInt(m[2], 10, 32)
|
||||
index = &i
|
||||
indexStar = false
|
||||
}
|
||||
}
|
||||
|
||||
nextvals := []reflect.Value{}
|
||||
for _, value := range values {
|
||||
// pull component name out of struct member
|
||||
if value.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
|
||||
if c == "*" { // pull all members
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
if f := reflect.Indirect(value.Field(i)); f.IsValid() {
|
||||
nextvals = append(nextvals, f)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
value = value.FieldByNameFunc(func(name string) bool {
|
||||
if c == name {
|
||||
return true
|
||||
} else if !caseSensitive && strings.ToLower(name) == strings.ToLower(c) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 {
|
||||
if !value.IsNil() {
|
||||
value.Set(reflect.Zero(value.Type()))
|
||||
}
|
||||
return []reflect.Value{value}
|
||||
}
|
||||
|
||||
if createPath && value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
// TODO if the value is the terminus it should not be created
|
||||
// if the value to be set to its position is nil.
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
value = value.Elem()
|
||||
} else {
|
||||
value = reflect.Indirect(value)
|
||||
}
|
||||
|
||||
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||
if !createPath && value.IsNil() {
|
||||
value = reflect.ValueOf(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if value.IsValid() {
|
||||
nextvals = append(nextvals, value)
|
||||
}
|
||||
}
|
||||
values = nextvals
|
||||
|
||||
if indexStar || index != nil {
|
||||
nextvals = []reflect.Value{}
|
||||
for _, valItem := range values {
|
||||
value := reflect.Indirect(valItem)
|
||||
if value.Kind() != reflect.Slice {
|
||||
continue
|
||||
}
|
||||
|
||||
if indexStar { // grab all indices
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
idx := reflect.Indirect(value.Index(i))
|
||||
if idx.IsValid() {
|
||||
nextvals = append(nextvals, idx)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// pull out index
|
||||
i := int(*index)
|
||||
if i >= value.Len() { // check out of bounds
|
||||
if createPath {
|
||||
// TODO resize slice
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if i < 0 { // support negative indexing
|
||||
i = value.Len() + i
|
||||
}
|
||||
value = reflect.Indirect(value.Index(i))
|
||||
|
||||
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
|
||||
if !createPath && value.IsNil() {
|
||||
value = reflect.ValueOf(nil)
|
||||
}
|
||||
}
|
||||
|
||||
if value.IsValid() {
|
||||
nextvals = append(nextvals, value)
|
||||
}
|
||||
}
|
||||
values = nextvals
|
||||
}
|
||||
|
||||
components = components[1:]
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// ValuesAtPath returns a list of values at the case insensitive lexical
|
||||
// path inside of a structure.
|
||||
func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
|
||||
result, err := jmespath.Search(path, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(result)
|
||||
if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
|
||||
return nil, nil
|
||||
}
|
||||
if s, ok := result.([]interface{}); ok {
|
||||
return s, err
|
||||
}
|
||||
if v.Kind() == reflect.Map && v.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if v.Kind() == reflect.Slice {
|
||||
out := make([]interface{}, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
out[i] = v.Index(i).Interface()
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
return []interface{}{result}, nil
|
||||
}
|
||||
|
||||
// SetValueAtPath sets a value at the case insensitive lexical path inside
|
||||
// of a structure.
|
||||
func SetValueAtPath(i interface{}, path string, v interface{}) {
|
||||
if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
|
||||
for _, rval := range rvals {
|
||||
if rval.Kind() == reflect.Ptr && rval.IsNil() {
|
||||
continue
|
||||
}
|
||||
setValue(rval, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setValue(dstVal reflect.Value, src interface{}) {
|
||||
if dstVal.Kind() == reflect.Ptr {
|
||||
dstVal = reflect.Indirect(dstVal)
|
||||
}
|
||||
srcVal := reflect.ValueOf(src)
|
||||
|
||||
if !srcVal.IsValid() { // src is literal nil
|
||||
if dstVal.CanAddr() {
|
||||
// Convert to pointer so that pointer's value can be nil'ed
|
||||
// dstVal = dstVal.Addr()
|
||||
}
|
||||
dstVal.Set(reflect.Zero(dstVal.Type()))
|
||||
|
||||
} else if srcVal.Kind() == reflect.Ptr {
|
||||
if srcVal.IsNil() {
|
||||
srcVal = reflect.Zero(dstVal.Type())
|
||||
} else {
|
||||
srcVal = reflect.ValueOf(src).Elem()
|
||||
}
|
||||
dstVal.Set(srcVal)
|
||||
} else {
|
||||
dstVal.Set(srcVal)
|
||||
}
|
||||
|
||||
}
|
||||
113
vendor/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
113
vendor/github.com/aws/aws-sdk-go/aws/awsutil/prettify.go
generated
vendored
@@ -1,113 +0,0 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Prettify returns the string representation of a value.
|
||||
func Prettify(i interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
prettify(reflect.ValueOf(i), 0, &buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// prettify will recursively walk value v to build a textual
|
||||
// representation of the value.
|
||||
func prettify(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
strtype := v.Type().String()
|
||||
if strtype == "time.Time" {
|
||||
fmt.Fprintf(buf, "%s", v.Interface())
|
||||
break
|
||||
} else if strings.HasPrefix(strtype, "io.") {
|
||||
buf.WriteString("<buffer>")
|
||||
break
|
||||
}
|
||||
|
||||
buf.WriteString("{\n")
|
||||
|
||||
names := []string{}
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
name := v.Type().Field(i).Name
|
||||
f := v.Field(i)
|
||||
if name[0:1] == strings.ToLower(name[0:1]) {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice || f.Kind() == reflect.Map) && f.IsNil() {
|
||||
continue // ignore unset fields
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
for i, n := range names {
|
||||
val := v.FieldByName(n)
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(n + ": ")
|
||||
prettify(val, indent+2, buf)
|
||||
|
||||
if i < len(names)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
case reflect.Slice:
|
||||
strtype := v.Type().String()
|
||||
if strtype == "[]uint8" {
|
||||
fmt.Fprintf(buf, "<binary> len %d", v.Len())
|
||||
break
|
||||
}
|
||||
|
||||
nl, id, id2 := "", "", ""
|
||||
if v.Len() > 3 {
|
||||
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||
}
|
||||
buf.WriteString("[" + nl)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
buf.WriteString(id2)
|
||||
prettify(v.Index(i), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString("," + nl)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(nl + id + "]")
|
||||
case reflect.Map:
|
||||
buf.WriteString("{\n")
|
||||
|
||||
for i, k := range v.MapKeys() {
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(k.String() + ": ")
|
||||
prettify(v.MapIndex(k), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
default:
|
||||
if !v.IsValid() {
|
||||
fmt.Fprint(buf, "<invalid value>")
|
||||
return
|
||||
}
|
||||
format := "%v"
|
||||
switch v.Interface().(type) {
|
||||
case string:
|
||||
format = "%q"
|
||||
case io.ReadSeeker, io.Reader:
|
||||
format = "buffer(%p)"
|
||||
}
|
||||
fmt.Fprintf(buf, format, v.Interface())
|
||||
}
|
||||
}
|
||||
89
vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
89
vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringValue returns the string representation of a value.
|
||||
func StringValue(i interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
stringValue(reflect.ValueOf(i), 0, &buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
buf.WriteString("{\n")
|
||||
|
||||
names := []string{}
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
name := v.Type().Field(i).Name
|
||||
f := v.Field(i)
|
||||
if name[0:1] == strings.ToLower(name[0:1]) {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice) && f.IsNil() {
|
||||
continue // ignore unset fields
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
for i, n := range names {
|
||||
val := v.FieldByName(n)
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(n + ": ")
|
||||
stringValue(val, indent+2, buf)
|
||||
|
||||
if i < len(names)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
case reflect.Slice:
|
||||
nl, id, id2 := "", "", ""
|
||||
if v.Len() > 3 {
|
||||
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||
}
|
||||
buf.WriteString("[" + nl)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
buf.WriteString(id2)
|
||||
stringValue(v.Index(i), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString("," + nl)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(nl + id + "]")
|
||||
case reflect.Map:
|
||||
buf.WriteString("{\n")
|
||||
|
||||
for i, k := range v.MapKeys() {
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(k.String() + ": ")
|
||||
stringValue(v.MapIndex(k), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
default:
|
||||
format := "%v"
|
||||
switch v.Interface().(type) {
|
||||
case string:
|
||||
format = "%q"
|
||||
}
|
||||
fmt.Fprintf(buf, format, v.Interface())
|
||||
}
|
||||
}
|
||||
96
vendor/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
96
vendor/github.com/aws/aws-sdk-go/aws/client/client.go
generated
vendored
@@ -1,96 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// A Config provides configuration to a service client instance.
|
||||
type Config struct {
|
||||
Config *aws.Config
|
||||
Handlers request.Handlers
|
||||
Endpoint string
|
||||
SigningRegion string
|
||||
SigningName string
|
||||
|
||||
// States that the signing name did not come from a modeled source but
|
||||
// was derived based on other data. Used by service client constructors
|
||||
// to determine if the signin name can be overriden based on metadata the
|
||||
// service has.
|
||||
SigningNameDerived bool
|
||||
}
|
||||
|
||||
// ConfigProvider provides a generic way for a service client to receive
|
||||
// the ClientConfig without circular dependencies.
|
||||
type ConfigProvider interface {
|
||||
ClientConfig(serviceName string, cfgs ...*aws.Config) Config
|
||||
}
|
||||
|
||||
// ConfigNoResolveEndpointProvider same as ConfigProvider except it will not
|
||||
// resolve the endpoint automatically. The service client's endpoint must be
|
||||
// provided via the aws.Config.Endpoint field.
|
||||
type ConfigNoResolveEndpointProvider interface {
|
||||
ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) Config
|
||||
}
|
||||
|
||||
// A Client implements the base client request and response handling
|
||||
// used by all service clients.
|
||||
type Client struct {
|
||||
request.Retryer
|
||||
metadata.ClientInfo
|
||||
|
||||
Config aws.Config
|
||||
Handlers request.Handlers
|
||||
}
|
||||
|
||||
// New will return a pointer to a new initialized service client.
|
||||
func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, options ...func(*Client)) *Client {
|
||||
svc := &Client{
|
||||
Config: cfg,
|
||||
ClientInfo: info,
|
||||
Handlers: handlers.Copy(),
|
||||
}
|
||||
|
||||
switch retryer, ok := cfg.Retryer.(request.Retryer); {
|
||||
case ok:
|
||||
svc.Retryer = retryer
|
||||
case cfg.Retryer != nil && cfg.Logger != nil:
|
||||
s := fmt.Sprintf("WARNING: %T does not implement request.Retryer; using DefaultRetryer instead", cfg.Retryer)
|
||||
cfg.Logger.Log(s)
|
||||
fallthrough
|
||||
default:
|
||||
maxRetries := aws.IntValue(cfg.MaxRetries)
|
||||
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
|
||||
maxRetries = 3
|
||||
}
|
||||
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
|
||||
}
|
||||
|
||||
svc.AddDebugHandlers()
|
||||
|
||||
for _, option := range options {
|
||||
option(svc)
|
||||
}
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
// NewRequest returns a new Request pointer for the service API
|
||||
// operation and parameters.
|
||||
func (c *Client) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
|
||||
return request.New(c.Config, c.ClientInfo, c.Handlers, c.Retryer, operation, params, data)
|
||||
}
|
||||
|
||||
// AddDebugHandlers injects debug logging handlers into the service to log request
|
||||
// debug information.
|
||||
func (c *Client) AddDebugHandlers() {
|
||||
if !c.Config.LogLevel.AtLeast(aws.LogDebug) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler)
|
||||
c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler)
|
||||
}
|
||||
116
vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
116
vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go
generated
vendored
@@ -1,116 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||
)
|
||||
|
||||
// DefaultRetryer implements basic retry logic using exponential backoff for
|
||||
// most services. If you want to implement custom retry logic, implement the
|
||||
// request.Retryer interface or create a structure type that composes this
|
||||
// struct and override the specific methods. For example, to override only
|
||||
// the MaxRetries method:
|
||||
//
|
||||
// type retryer struct {
|
||||
// client.DefaultRetryer
|
||||
// }
|
||||
//
|
||||
// // This implementation always has 100 max retries
|
||||
// func (d retryer) MaxRetries() int { return 100 }
|
||||
type DefaultRetryer struct {
|
||||
NumMaxRetries int
|
||||
}
|
||||
|
||||
// MaxRetries returns the number of maximum returns the service will use to make
|
||||
// an individual API request.
|
||||
func (d DefaultRetryer) MaxRetries() int {
|
||||
return d.NumMaxRetries
|
||||
}
|
||||
|
||||
// RetryRules returns the delay duration before retrying this request again
|
||||
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||
// Set the upper limit of delay in retrying at ~five minutes
|
||||
minTime := 30
|
||||
throttle := d.shouldThrottle(r)
|
||||
if throttle {
|
||||
if delay, ok := getRetryDelay(r); ok {
|
||||
return delay
|
||||
}
|
||||
|
||||
minTime = 500
|
||||
}
|
||||
|
||||
retryCount := r.RetryCount
|
||||
if throttle && retryCount > 8 {
|
||||
retryCount = 8
|
||||
} else if retryCount > 13 {
|
||||
retryCount = 13
|
||||
}
|
||||
|
||||
delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime)
|
||||
return time.Duration(delay) * time.Millisecond
|
||||
}
|
||||
|
||||
// ShouldRetry returns true if the request should be retried.
|
||||
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
|
||||
// If one of the other handlers already set the retry state
|
||||
// we don't want to override it based on the service's state
|
||||
if r.Retryable != nil {
|
||||
return *r.Retryable
|
||||
}
|
||||
|
||||
if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 {
|
||||
return true
|
||||
}
|
||||
return r.IsErrorRetryable() || d.shouldThrottle(r)
|
||||
}
|
||||
|
||||
// ShouldThrottle returns true if the request should be throttled.
|
||||
func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
|
||||
switch r.HTTPResponse.StatusCode {
|
||||
case 429:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
default:
|
||||
return r.IsErrorThrottle()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// This will look in the Retry-After header, RFC 7231, for how long
|
||||
// it will wait before attempting another request
|
||||
func getRetryDelay(r *request.Request) (time.Duration, bool) {
|
||||
if !canUseRetryAfterHeader(r) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
delayStr := r.HTTPResponse.Header.Get("Retry-After")
|
||||
if len(delayStr) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
delay, err := strconv.Atoi(delayStr)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return time.Duration(delay) * time.Second, true
|
||||
}
|
||||
|
||||
// Will look at the status code to see if the retry header pertains to
|
||||
// the status code.
|
||||
func canUseRetryAfterHeader(r *request.Request) bool {
|
||||
switch r.HTTPResponse.StatusCode {
|
||||
case 429:
|
||||
case 503:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
184
vendor/github.com/aws/aws-sdk-go/aws/client/logger.go
generated
vendored
184
vendor/github.com/aws/aws-sdk-go/aws/client/logger.go
generated
vendored
@@ -1,184 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
const logReqMsg = `DEBUG: Request %s/%s Details:
|
||||
---[ REQUEST POST-SIGN ]-----------------------------
|
||||
%s
|
||||
-----------------------------------------------------`
|
||||
|
||||
const logReqErrMsg = `DEBUG ERROR: Request %s/%s:
|
||||
---[ REQUEST DUMP ERROR ]-----------------------------
|
||||
%s
|
||||
------------------------------------------------------`
|
||||
|
||||
type logWriter struct {
|
||||
// Logger is what we will use to log the payload of a response.
|
||||
Logger aws.Logger
|
||||
// buf stores the contents of what has been read
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (logger *logWriter) Write(b []byte) (int, error) {
|
||||
return logger.buf.Write(b)
|
||||
}
|
||||
|
||||
type teeReaderCloser struct {
|
||||
// io.Reader will be a tee reader that is used during logging.
|
||||
// This structure will read from a body and write the contents to a logger.
|
||||
io.Reader
|
||||
// Source is used just to close when we are done reading.
|
||||
Source io.ReadCloser
|
||||
}
|
||||
|
||||
func (reader *teeReaderCloser) Close() error {
|
||||
return reader.Source.Close()
|
||||
}
|
||||
|
||||
// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent
|
||||
// to a service. Will include the HTTP request body if the LogLevel of the
|
||||
// request matches LogDebugWithHTTPBody.
|
||||
var LogHTTPRequestHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogRequest",
|
||||
Fn: logRequest,
|
||||
}
|
||||
|
||||
func logRequest(r *request.Request) {
|
||||
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||
bodySeekable := aws.IsReaderSeekable(r.Body)
|
||||
|
||||
b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
if logBody {
|
||||
if !bodySeekable {
|
||||
r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body))
|
||||
}
|
||||
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
|
||||
// Body as a NoOpCloser and will not be reset after read by the HTTP
|
||||
// client reader.
|
||||
r.ResetBody()
|
||||
}
|
||||
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
|
||||
// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent
|
||||
// to a service. Will only log the HTTP request's headers. The request payload
|
||||
// will not be read.
|
||||
var LogHTTPRequestHeaderHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogRequestHeader",
|
||||
Fn: logRequestHeader,
|
||||
}
|
||||
|
||||
func logRequestHeader(r *request.Request) {
|
||||
b, err := httputil.DumpRequestOut(r.HTTPRequest, false)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
|
||||
const logRespMsg = `DEBUG: Response %s/%s Details:
|
||||
---[ RESPONSE ]--------------------------------------
|
||||
%s
|
||||
-----------------------------------------------------`
|
||||
|
||||
const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
|
||||
---[ RESPONSE DUMP ERROR ]-----------------------------
|
||||
%s
|
||||
-----------------------------------------------------`
|
||||
|
||||
// LogHTTPResponseHandler is a SDK request handler to log the HTTP response
|
||||
// received from a service. Will include the HTTP response body if the LogLevel
|
||||
// of the request matches LogDebugWithHTTPBody.
|
||||
var LogHTTPResponseHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogResponse",
|
||||
Fn: logResponse,
|
||||
}
|
||||
|
||||
func logResponse(r *request.Request) {
|
||||
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
|
||||
|
||||
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||
if logBody {
|
||||
r.HTTPResponse.Body = &teeReaderCloser{
|
||||
Reader: io.TeeReader(r.HTTPResponse.Body, lw),
|
||||
Source: r.HTTPResponse.Body,
|
||||
}
|
||||
}
|
||||
|
||||
handlerFn := func(req *request.Request) {
|
||||
b, err := httputil.DumpResponse(req.HTTPResponse, false)
|
||||
if err != nil {
|
||||
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||
req.ClientInfo.ServiceName, req.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
lw.Logger.Log(fmt.Sprintf(logRespMsg,
|
||||
req.ClientInfo.ServiceName, req.Operation.Name, string(b)))
|
||||
|
||||
if logBody {
|
||||
b, err := ioutil.ReadAll(lw.buf)
|
||||
if err != nil {
|
||||
lw.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||
req.ClientInfo.ServiceName, req.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
lw.Logger.Log(string(b))
|
||||
}
|
||||
}
|
||||
|
||||
const handlerName = "awsdk.client.LogResponse.ResponseBody"
|
||||
|
||||
r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{
|
||||
Name: handlerName, Fn: handlerFn,
|
||||
})
|
||||
r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{
|
||||
Name: handlerName, Fn: handlerFn,
|
||||
})
|
||||
}
|
||||
|
||||
// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP
|
||||
// response received from a service. Will only log the HTTP response's headers.
|
||||
// The response payload will not be read.
|
||||
var LogHTTPResponseHeaderHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogResponseHeader",
|
||||
Fn: logResponseHeader,
|
||||
}
|
||||
|
||||
func logResponseHeader(r *request.Request) {
|
||||
if r.Config.Logger == nil {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := httputil.DumpResponse(r.HTTPResponse, false)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
r.Config.Logger.Log(fmt.Sprintf(logRespMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
13
vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
13
vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go
generated
vendored
@@ -1,13 +0,0 @@
|
||||
package metadata
|
||||
|
||||
// ClientInfo wraps immutable data from the client.Client structure.
|
||||
type ClientInfo struct {
|
||||
ServiceName string
|
||||
ServiceID string
|
||||
APIVersion string
|
||||
Endpoint string
|
||||
SigningName string
|
||||
SigningRegion string
|
||||
JSONVersion string
|
||||
TargetPrefix string
|
||||
}
|
||||
492
vendor/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
492
vendor/github.com/aws/aws-sdk-go/aws/config.go
generated
vendored
@@ -1,492 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
)
|
||||
|
||||
// UseServiceDefaultRetries instructs the config to use the service's own
|
||||
// default number of retries. This will be the default action if
|
||||
// Config.MaxRetries is nil also.
|
||||
const UseServiceDefaultRetries = -1
|
||||
|
||||
// RequestRetryer is an alias for a type that implements the request.Retryer
|
||||
// interface.
|
||||
type RequestRetryer interface{}
|
||||
|
||||
// A Config provides service configuration for service clients. By default,
|
||||
// all clients will use the defaults.DefaultConfig tructure.
|
||||
//
|
||||
// // Create Session with MaxRetry configuration to be shared by multiple
|
||||
// // service clients.
|
||||
// sess := session.Must(session.NewSession(&aws.Config{
|
||||
// MaxRetries: aws.Int(3),
|
||||
// }))
|
||||
//
|
||||
// // Create S3 service client with a specific Region.
|
||||
// svc := s3.New(sess, &aws.Config{
|
||||
// Region: aws.String("us-west-2"),
|
||||
// })
|
||||
type Config struct {
|
||||
// Enables verbose error printing of all credential chain errors.
|
||||
// Should be used when wanting to see all errors while attempting to
|
||||
// retrieve credentials.
|
||||
CredentialsChainVerboseErrors *bool
|
||||
|
||||
// The credentials object to use when signing requests. Defaults to a
|
||||
// chain of credential providers to search for credentials in environment
|
||||
// variables, shared credential file, and EC2 Instance Roles.
|
||||
Credentials *credentials.Credentials
|
||||
|
||||
// An optional endpoint URL (hostname only or fully qualified URI)
|
||||
// that overrides the default generated endpoint for a client. Set this
|
||||
// to `""` to use the default generated endpoint.
|
||||
//
|
||||
// @note You must still provide a `Region` value when specifying an
|
||||
// endpoint for a client.
|
||||
Endpoint *string
|
||||
|
||||
// The resolver to use for looking up endpoints for AWS service clients
|
||||
// to use based on region.
|
||||
EndpointResolver endpoints.Resolver
|
||||
|
||||
// EnforceShouldRetryCheck is used in the AfterRetryHandler to always call
|
||||
// ShouldRetry regardless of whether or not if request.Retryable is set.
|
||||
// This will utilize ShouldRetry method of custom retryers. If EnforceShouldRetryCheck
|
||||
// is not set, then ShouldRetry will only be called if request.Retryable is nil.
|
||||
// Proper handling of the request.Retryable field is important when setting this field.
|
||||
EnforceShouldRetryCheck *bool
|
||||
|
||||
// The region to send requests to. This parameter is required and must
|
||||
// be configured globally or on a per-client basis unless otherwise
|
||||
// noted. A full list of regions is found in the "Regions and Endpoints"
|
||||
// document.
|
||||
//
|
||||
// @see http://docs.aws.amazon.com/general/latest/gr/rande.html
|
||||
// AWS Regions and Endpoints
|
||||
Region *string
|
||||
|
||||
// Set this to `true` to disable SSL when sending requests. Defaults
|
||||
// to `false`.
|
||||
DisableSSL *bool
|
||||
|
||||
// The HTTP client to use when sending requests. Defaults to
|
||||
// `http.DefaultClient`.
|
||||
HTTPClient *http.Client
|
||||
|
||||
// An integer value representing the logging level. The default log level
|
||||
// is zero (LogOff), which represents no logging. To enable logging set
|
||||
// to a LogLevel Value.
|
||||
LogLevel *LogLevelType
|
||||
|
||||
// The logger writer interface to write logging messages to. Defaults to
|
||||
// standard out.
|
||||
Logger Logger
|
||||
|
||||
// The maximum number of times that a request will be retried for failures.
|
||||
// Defaults to -1, which defers the max retry setting to the service
|
||||
// specific configuration.
|
||||
MaxRetries *int
|
||||
|
||||
// Retryer guides how HTTP requests should be retried in case of
|
||||
// recoverable failures.
|
||||
//
|
||||
// When nil or the value does not implement the request.Retryer interface,
|
||||
// the client.DefaultRetryer will be used.
|
||||
//
|
||||
// When both Retryer and MaxRetries are non-nil, the former is used and
|
||||
// the latter ignored.
|
||||
//
|
||||
// To set the Retryer field in a type-safe manner and with chaining, use
|
||||
// the request.WithRetryer helper function:
|
||||
//
|
||||
// cfg := request.WithRetryer(aws.NewConfig(), myRetryer)
|
||||
//
|
||||
Retryer RequestRetryer
|
||||
|
||||
// Disables semantic parameter validation, which validates input for
|
||||
// missing required fields and/or other semantic request input errors.
|
||||
DisableParamValidation *bool
|
||||
|
||||
// Disables the computation of request and response checksums, e.g.,
|
||||
// CRC32 checksums in Amazon DynamoDB.
|
||||
DisableComputeChecksums *bool
|
||||
|
||||
// Set this to `true` to force the request to use path-style addressing,
|
||||
// i.e., `http://s3.amazonaws.com/BUCKET/KEY`. By default, the S3 client
|
||||
// will use virtual hosted bucket addressing when possible
|
||||
// (`http://BUCKET.s3.amazonaws.com/KEY`).
|
||||
//
|
||||
// @note This configuration option is specific to the Amazon S3 service.
|
||||
// @see http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html
|
||||
// Amazon S3: Virtual Hosting of Buckets
|
||||
S3ForcePathStyle *bool
|
||||
|
||||
// Set this to `true` to disable the SDK adding the `Expect: 100-Continue`
|
||||
// header to PUT requests over 2MB of content. 100-Continue instructs the
|
||||
// HTTP client not to send the body until the service responds with a
|
||||
// `continue` status. This is useful to prevent sending the request body
|
||||
// until after the request is authenticated, and validated.
|
||||
//
|
||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
|
||||
//
|
||||
// 100-Continue is only enabled for Go 1.6 and above. See `http.Transport`'s
|
||||
// `ExpectContinueTimeout` for information on adjusting the continue wait
|
||||
// timeout. https://golang.org/pkg/net/http/#Transport
|
||||
//
|
||||
// You should use this flag to disble 100-Continue if you experience issues
|
||||
// with proxies or third party S3 compatible services.
|
||||
S3Disable100Continue *bool
|
||||
|
||||
// Set this to `true` to enable S3 Accelerate feature. For all operations
|
||||
// compatible with S3 Accelerate will use the accelerate endpoint for
|
||||
// requests. Requests not compatible will fall back to normal S3 requests.
|
||||
//
|
||||
// The bucket must be enable for accelerate to be used with S3 client with
|
||||
// accelerate enabled. If the bucket is not enabled for accelerate an error
|
||||
// will be returned. The bucket name must be DNS compatible to also work
|
||||
// with accelerate.
|
||||
S3UseAccelerate *bool
|
||||
|
||||
// S3DisableContentMD5Validation config option is temporarily disabled,
|
||||
// For S3 GetObject API calls, #1837.
|
||||
//
|
||||
// Set this to `true` to disable the S3 service client from automatically
|
||||
// adding the ContentMD5 to S3 Object Put and Upload API calls. This option
|
||||
// will also disable the SDK from performing object ContentMD5 validation
|
||||
// on GetObject API calls.
|
||||
S3DisableContentMD5Validation *bool
|
||||
|
||||
// Set this to `true` to disable the EC2Metadata client from overriding the
|
||||
// default http.Client's Timeout. This is helpful if you do not want the
|
||||
// EC2Metadata client to create a new http.Client. This options is only
|
||||
// meaningful if you're not already using a custom HTTP client with the
|
||||
// SDK. Enabled by default.
|
||||
//
|
||||
// Must be set and provided to the session.NewSession() in order to disable
|
||||
// the EC2Metadata overriding the timeout for default credentials chain.
|
||||
//
|
||||
// Example:
|
||||
// sess := session.Must(session.NewSession(aws.NewConfig()
|
||||
// .WithEC2MetadataDiableTimeoutOverride(true)))
|
||||
//
|
||||
// svc := s3.New(sess)
|
||||
//
|
||||
EC2MetadataDisableTimeoutOverride *bool
|
||||
|
||||
// Instructs the endpoint to be generated for a service client to
|
||||
// be the dual stack endpoint. The dual stack endpoint will support
|
||||
// both IPv4 and IPv6 addressing.
|
||||
//
|
||||
// Setting this for a service which does not support dual stack will fail
|
||||
// to make requets. It is not recommended to set this value on the session
|
||||
// as it will apply to all service clients created with the session. Even
|
||||
// services which don't support dual stack endpoints.
|
||||
//
|
||||
// If the Endpoint config value is also provided the UseDualStack flag
|
||||
// will be ignored.
|
||||
//
|
||||
// Only supported with.
|
||||
//
|
||||
// sess := session.Must(session.NewSession())
|
||||
//
|
||||
// svc := s3.New(sess, &aws.Config{
|
||||
// UseDualStack: aws.Bool(true),
|
||||
// })
|
||||
UseDualStack *bool
|
||||
|
||||
// SleepDelay is an override for the func the SDK will call when sleeping
|
||||
// during the lifecycle of a request. Specifically this will be used for
|
||||
// request delays. This value should only be used for testing. To adjust
|
||||
// the delay of a request see the aws/client.DefaultRetryer and
|
||||
// aws/request.Retryer.
|
||||
//
|
||||
// SleepDelay will prevent any Context from being used for canceling retry
|
||||
// delay of an API operation. It is recommended to not use SleepDelay at all
|
||||
// and specify a Retryer instead.
|
||||
SleepDelay func(time.Duration)
|
||||
|
||||
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
|
||||
// Will default to false. This would only be used for empty directory names in s3 requests.
|
||||
//
|
||||
// Example:
|
||||
// sess := session.Must(session.NewSession(&aws.Config{
|
||||
// DisableRestProtocolURICleaning: aws.Bool(true),
|
||||
// }))
|
||||
//
|
||||
// svc := s3.New(sess)
|
||||
// out, err := svc.GetObject(&s3.GetObjectInput {
|
||||
// Bucket: aws.String("bucketname"),
|
||||
// Key: aws.String("//foo//bar//moo"),
|
||||
// })
|
||||
DisableRestProtocolURICleaning *bool
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config pointer that can be chained with builder
|
||||
// methods to set multiple configuration values inline without using pointers.
|
||||
//
|
||||
// // Create Session with MaxRetry configuration to be shared by multiple
|
||||
// // service clients.
|
||||
// sess := session.Must(session.NewSession(aws.NewConfig().
|
||||
// WithMaxRetries(3),
|
||||
// ))
|
||||
//
|
||||
// // Create S3 service client with a specific Region.
|
||||
// svc := s3.New(sess, aws.NewConfig().
|
||||
// WithRegion("us-west-2"),
|
||||
// )
|
||||
func NewConfig() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
|
||||
// WithCredentialsChainVerboseErrors sets a config verbose errors boolean and returning
|
||||
// a Config pointer.
|
||||
func (c *Config) WithCredentialsChainVerboseErrors(verboseErrs bool) *Config {
|
||||
c.CredentialsChainVerboseErrors = &verboseErrs
|
||||
return c
|
||||
}
|
||||
|
||||
// WithCredentials sets a config Credentials value returning a Config pointer
|
||||
// for chaining.
|
||||
func (c *Config) WithCredentials(creds *credentials.Credentials) *Config {
|
||||
c.Credentials = creds
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEndpoint sets a config Endpoint value returning a Config pointer for
|
||||
// chaining.
|
||||
func (c *Config) WithEndpoint(endpoint string) *Config {
|
||||
c.Endpoint = &endpoint
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEndpointResolver sets a config EndpointResolver value returning a
|
||||
// Config pointer for chaining.
|
||||
func (c *Config) WithEndpointResolver(resolver endpoints.Resolver) *Config {
|
||||
c.EndpointResolver = resolver
|
||||
return c
|
||||
}
|
||||
|
||||
// WithRegion sets a config Region value returning a Config pointer for
|
||||
// chaining.
|
||||
func (c *Config) WithRegion(region string) *Config {
|
||||
c.Region = ®ion
|
||||
return c
|
||||
}
|
||||
|
||||
// WithDisableSSL sets a config DisableSSL value returning a Config pointer
|
||||
// for chaining.
|
||||
func (c *Config) WithDisableSSL(disable bool) *Config {
|
||||
c.DisableSSL = &disable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithHTTPClient sets a config HTTPClient value returning a Config pointer
|
||||
// for chaining.
|
||||
func (c *Config) WithHTTPClient(client *http.Client) *Config {
|
||||
c.HTTPClient = client
|
||||
return c
|
||||
}
|
||||
|
||||
// WithMaxRetries sets a config MaxRetries value returning a Config pointer
|
||||
// for chaining.
|
||||
func (c *Config) WithMaxRetries(max int) *Config {
|
||||
c.MaxRetries = &max
|
||||
return c
|
||||
}
|
||||
|
||||
// WithDisableParamValidation sets a config DisableParamValidation value
|
||||
// returning a Config pointer for chaining.
|
||||
func (c *Config) WithDisableParamValidation(disable bool) *Config {
|
||||
c.DisableParamValidation = &disable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithDisableComputeChecksums sets a config DisableComputeChecksums value
|
||||
// returning a Config pointer for chaining.
|
||||
func (c *Config) WithDisableComputeChecksums(disable bool) *Config {
|
||||
c.DisableComputeChecksums = &disable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithLogLevel sets a config LogLevel value returning a Config pointer for
|
||||
// chaining.
|
||||
func (c *Config) WithLogLevel(level LogLevelType) *Config {
|
||||
c.LogLevel = &level
|
||||
return c
|
||||
}
|
||||
|
||||
// WithLogger sets a config Logger value returning a Config pointer for
|
||||
// chaining.
|
||||
func (c *Config) WithLogger(logger Logger) *Config {
|
||||
c.Logger = logger
|
||||
return c
|
||||
}
|
||||
|
||||
// WithS3ForcePathStyle sets a config S3ForcePathStyle value returning a Config
|
||||
// pointer for chaining.
|
||||
func (c *Config) WithS3ForcePathStyle(force bool) *Config {
|
||||
c.S3ForcePathStyle = &force
|
||||
return c
|
||||
}
|
||||
|
||||
// WithS3Disable100Continue sets a config S3Disable100Continue value returning
|
||||
// a Config pointer for chaining.
|
||||
func (c *Config) WithS3Disable100Continue(disable bool) *Config {
|
||||
c.S3Disable100Continue = &disable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithS3UseAccelerate sets a config S3UseAccelerate value returning a Config
|
||||
// pointer for chaining.
|
||||
func (c *Config) WithS3UseAccelerate(enable bool) *Config {
|
||||
c.S3UseAccelerate = &enable
|
||||
return c
|
||||
|
||||
}
|
||||
|
||||
// WithS3DisableContentMD5Validation sets a config
|
||||
// S3DisableContentMD5Validation value returning a Config pointer for chaining.
|
||||
func (c *Config) WithS3DisableContentMD5Validation(enable bool) *Config {
|
||||
c.S3DisableContentMD5Validation = &enable
|
||||
return c
|
||||
|
||||
}
|
||||
|
||||
// WithUseDualStack sets a config UseDualStack value returning a Config
|
||||
// pointer for chaining.
|
||||
func (c *Config) WithUseDualStack(enable bool) *Config {
|
||||
c.UseDualStack = &enable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value
|
||||
// returning a Config pointer for chaining.
|
||||
func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config {
|
||||
c.EC2MetadataDisableTimeoutOverride = &enable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithSleepDelay overrides the function used to sleep while waiting for the
|
||||
// next retry. Defaults to time.Sleep.
|
||||
func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config {
|
||||
c.SleepDelay = fn
|
||||
return c
|
||||
}
|
||||
|
||||
// MergeIn merges the passed in configs into the existing config object.
|
||||
func (c *Config) MergeIn(cfgs ...*Config) {
|
||||
for _, other := range cfgs {
|
||||
mergeInConfig(c, other)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeInConfig(dst *Config, other *Config) {
|
||||
if other == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if other.CredentialsChainVerboseErrors != nil {
|
||||
dst.CredentialsChainVerboseErrors = other.CredentialsChainVerboseErrors
|
||||
}
|
||||
|
||||
if other.Credentials != nil {
|
||||
dst.Credentials = other.Credentials
|
||||
}
|
||||
|
||||
if other.Endpoint != nil {
|
||||
dst.Endpoint = other.Endpoint
|
||||
}
|
||||
|
||||
if other.EndpointResolver != nil {
|
||||
dst.EndpointResolver = other.EndpointResolver
|
||||
}
|
||||
|
||||
if other.Region != nil {
|
||||
dst.Region = other.Region
|
||||
}
|
||||
|
||||
if other.DisableSSL != nil {
|
||||
dst.DisableSSL = other.DisableSSL
|
||||
}
|
||||
|
||||
if other.HTTPClient != nil {
|
||||
dst.HTTPClient = other.HTTPClient
|
||||
}
|
||||
|
||||
if other.LogLevel != nil {
|
||||
dst.LogLevel = other.LogLevel
|
||||
}
|
||||
|
||||
if other.Logger != nil {
|
||||
dst.Logger = other.Logger
|
||||
}
|
||||
|
||||
if other.MaxRetries != nil {
|
||||
dst.MaxRetries = other.MaxRetries
|
||||
}
|
||||
|
||||
if other.Retryer != nil {
|
||||
dst.Retryer = other.Retryer
|
||||
}
|
||||
|
||||
if other.DisableParamValidation != nil {
|
||||
dst.DisableParamValidation = other.DisableParamValidation
|
||||
}
|
||||
|
||||
if other.DisableComputeChecksums != nil {
|
||||
dst.DisableComputeChecksums = other.DisableComputeChecksums
|
||||
}
|
||||
|
||||
if other.S3ForcePathStyle != nil {
|
||||
dst.S3ForcePathStyle = other.S3ForcePathStyle
|
||||
}
|
||||
|
||||
if other.S3Disable100Continue != nil {
|
||||
dst.S3Disable100Continue = other.S3Disable100Continue
|
||||
}
|
||||
|
||||
if other.S3UseAccelerate != nil {
|
||||
dst.S3UseAccelerate = other.S3UseAccelerate
|
||||
}
|
||||
|
||||
if other.S3DisableContentMD5Validation != nil {
|
||||
dst.S3DisableContentMD5Validation = other.S3DisableContentMD5Validation
|
||||
}
|
||||
|
||||
if other.UseDualStack != nil {
|
||||
dst.UseDualStack = other.UseDualStack
|
||||
}
|
||||
|
||||
if other.EC2MetadataDisableTimeoutOverride != nil {
|
||||
dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
|
||||
}
|
||||
|
||||
if other.SleepDelay != nil {
|
||||
dst.SleepDelay = other.SleepDelay
|
||||
}
|
||||
|
||||
if other.DisableRestProtocolURICleaning != nil {
|
||||
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
|
||||
}
|
||||
|
||||
if other.EnforceShouldRetryCheck != nil {
|
||||
dst.EnforceShouldRetryCheck = other.EnforceShouldRetryCheck
|
||||
}
|
||||
}
|
||||
|
||||
// Copy will return a shallow copy of the Config object. If any additional
|
||||
// configurations are provided they will be merged into the new config returned.
|
||||
func (c *Config) Copy(cfgs ...*Config) *Config {
|
||||
dst := &Config{}
|
||||
dst.MergeIn(c)
|
||||
|
||||
for _, cfg := range cfgs {
|
||||
dst.MergeIn(cfg)
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
71
vendor/github.com/aws/aws-sdk-go/aws/context.go
generated
vendored
71
vendor/github.com/aws/aws-sdk-go/aws/context.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Context is an copy of the Go v1.7 stdlib's context.Context interface.
|
||||
// It is represented as a SDK interface to enable you to use the "WithContext"
|
||||
// API methods with Go v1.6 and a Context type such as golang.org/x/net/context.
|
||||
//
|
||||
// See https://golang.org/pkg/context on how to use contexts.
|
||||
type Context interface {
|
||||
// Deadline returns the time when work done on behalf of this context
|
||||
// should be canceled. Deadline returns ok==false when no deadline is
|
||||
// set. Successive calls to Deadline return the same results.
|
||||
Deadline() (deadline time.Time, ok bool)
|
||||
|
||||
// Done returns a channel that's closed when work done on behalf of this
|
||||
// context should be canceled. Done may return nil if this context can
|
||||
// never be canceled. Successive calls to Done return the same value.
|
||||
Done() <-chan struct{}
|
||||
|
||||
// Err returns a non-nil error value after Done is closed. Err returns
|
||||
// Canceled if the context was canceled or DeadlineExceeded if the
|
||||
// context's deadline passed. No other values for Err are defined.
|
||||
// After Done is closed, successive calls to Err return the same value.
|
||||
Err() error
|
||||
|
||||
// Value returns the value associated with this context for key, or nil
|
||||
// if no value is associated with key. Successive calls to Value with
|
||||
// the same key returns the same result.
|
||||
//
|
||||
// Use context values only for request-scoped data that transits
|
||||
// processes and API boundaries, not for passing optional parameters to
|
||||
// functions.
|
||||
Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
// BackgroundContext returns a context that will never be canceled, has no
|
||||
// values, and no deadline. This context is used by the SDK to provide
|
||||
// backwards compatibility with non-context API operations and functionality.
|
||||
//
|
||||
// Go 1.6 and before:
|
||||
// This context function is equivalent to context.Background in the Go stdlib.
|
||||
//
|
||||
// Go 1.7 and later:
|
||||
// The context returned will be the value returned by context.Background()
|
||||
//
|
||||
// See https://golang.org/pkg/context for more information on Contexts.
|
||||
func BackgroundContext() Context {
|
||||
return backgroundCtx
|
||||
}
|
||||
|
||||
// SleepWithContext will wait for the timer duration to expire, or the context
|
||||
// is canceled. Which ever happens first. If the context is canceled the Context's
|
||||
// error will be returned.
|
||||
//
|
||||
// Expects Context to always return a non-nil error if the Done channel is closed.
|
||||
func SleepWithContext(ctx Context, dur time.Duration) error {
|
||||
t := time.NewTimer(dur)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
break
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
41
vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go
generated
vendored
41
vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go
generated
vendored
@@ -1,41 +0,0 @@
|
||||
// +build !go1.7
|
||||
|
||||
package aws
|
||||
|
||||
import "time"
|
||||
|
||||
// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to
|
||||
// provide a 1.6 and 1.5 safe version of context that is compatible with Go
|
||||
// 1.7's Context.
|
||||
//
|
||||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||
// struct{}, since vars of this type must have distinct addresses.
|
||||
type emptyCtx int
|
||||
|
||||
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (*emptyCtx) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *emptyCtx) String() string {
|
||||
switch e {
|
||||
case backgroundCtx:
|
||||
return "aws.BackgroundContext"
|
||||
}
|
||||
return "unknown empty Context"
|
||||
}
|
||||
|
||||
var (
|
||||
backgroundCtx = new(emptyCtx)
|
||||
)
|
||||
9
vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go
generated
vendored
9
vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// +build go1.7
|
||||
|
||||
package aws
|
||||
|
||||
import "context"
|
||||
|
||||
var (
|
||||
backgroundCtx = context.Background()
|
||||
)
|
||||
387
vendor/github.com/aws/aws-sdk-go/aws/convert_types.go
generated
vendored
387
vendor/github.com/aws/aws-sdk-go/aws/convert_types.go
generated
vendored
@@ -1,387 +0,0 @@
|
||||
package aws
|
||||
|
||||
import "time"
|
||||
|
||||
// String returns a pointer to the string value passed in.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// StringValue returns the value of the string pointer passed in or
|
||||
// "" if the pointer is nil.
|
||||
func StringValue(v *string) string {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StringSlice converts a slice of string values into a slice of
|
||||
// string pointers
|
||||
func StringSlice(src []string) []*string {
|
||||
dst := make([]*string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueSlice converts a slice of string pointers into a slice of
|
||||
// string values
|
||||
func StringValueSlice(src []*string) []string {
|
||||
dst := make([]string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringMap converts a string map of string values into a string
|
||||
// map of string pointers
|
||||
func StringMap(src map[string]string) map[string]*string {
|
||||
dst := make(map[string]*string)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueMap converts a string map of string pointers into a string
|
||||
// map of string values
|
||||
func StringValueMap(src map[string]*string) map[string]string {
|
||||
dst := make(map[string]string)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Bool returns a pointer to the bool value passed in.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// BoolValue returns the value of the bool pointer passed in or
|
||||
// false if the pointer is nil.
|
||||
func BoolValue(v *bool) bool {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BoolSlice converts a slice of bool values into a slice of
|
||||
// bool pointers
|
||||
func BoolSlice(src []bool) []*bool {
|
||||
dst := make([]*bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueSlice converts a slice of bool pointers into a slice of
|
||||
// bool values
|
||||
func BoolValueSlice(src []*bool) []bool {
|
||||
dst := make([]bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolMap converts a string map of bool values into a string
|
||||
// map of bool pointers
|
||||
func BoolMap(src map[string]bool) map[string]*bool {
|
||||
dst := make(map[string]*bool)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueMap converts a string map of bool pointers into a string
|
||||
// map of bool values
|
||||
func BoolValueMap(src map[string]*bool) map[string]bool {
|
||||
dst := make(map[string]bool)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int returns a pointer to the int value passed in.
|
||||
func Int(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
// IntValue returns the value of the int pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func IntValue(v *int) int {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntSlice converts a slice of int values into a slice of
|
||||
// int pointers
|
||||
func IntSlice(src []int) []*int {
|
||||
dst := make([]*int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueSlice converts a slice of int pointers into a slice of
|
||||
// int values
|
||||
func IntValueSlice(src []*int) []int {
|
||||
dst := make([]int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntMap converts a string map of int values into a string
|
||||
// map of int pointers
|
||||
func IntMap(src map[string]int) map[string]*int {
|
||||
dst := make(map[string]*int)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueMap converts a string map of int pointers into a string
|
||||
// map of int values
|
||||
func IntValueMap(src map[string]*int) map[string]int {
|
||||
dst := make(map[string]int)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64 returns a pointer to the int64 value passed in.
|
||||
func Int64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int64Value returns the value of the int64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Int64Value(v *int64) int64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int64Slice converts a slice of int64 values into a slice of
|
||||
// int64 pointers
|
||||
func Int64Slice(src []int64) []*int64 {
|
||||
dst := make([]*int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueSlice converts a slice of int64 pointers into a slice of
|
||||
// int64 values
|
||||
func Int64ValueSlice(src []*int64) []int64 {
|
||||
dst := make([]int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64Map converts a string map of int64 values into a string
|
||||
// map of int64 pointers
|
||||
func Int64Map(src map[string]int64) map[string]*int64 {
|
||||
dst := make(map[string]*int64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueMap converts a string map of int64 pointers into a string
|
||||
// map of int64 values
|
||||
func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
||||
dst := make(map[string]int64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64 returns a pointer to the float64 value passed in.
|
||||
func Float64(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float64Value returns the value of the float64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Float64Value(v *float64) float64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Float64Slice converts a slice of float64 values into a slice of
|
||||
// float64 pointers
|
||||
func Float64Slice(src []float64) []*float64 {
|
||||
dst := make([]*float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueSlice converts a slice of float64 pointers into a slice of
|
||||
// float64 values
|
||||
func Float64ValueSlice(src []*float64) []float64 {
|
||||
dst := make([]float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64Map converts a string map of float64 values into a string
|
||||
// map of float64 pointers
|
||||
func Float64Map(src map[string]float64) map[string]*float64 {
|
||||
dst := make(map[string]*float64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueMap converts a string map of float64 pointers into a string
|
||||
// map of float64 values
|
||||
func Float64ValueMap(src map[string]*float64) map[string]float64 {
|
||||
dst := make(map[string]float64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Time returns a pointer to the time.Time value passed in.
|
||||
func Time(v time.Time) *time.Time {
|
||||
return &v
|
||||
}
|
||||
|
||||
// TimeValue returns the value of the time.Time pointer passed in or
|
||||
// time.Time{} if the pointer is nil.
|
||||
func TimeValue(v *time.Time) time.Time {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// SecondsTimeValue converts an int64 pointer to a time.Time value
|
||||
// representing seconds since Epoch or time.Time{} if the pointer is nil.
|
||||
func SecondsTimeValue(v *int64) time.Time {
|
||||
if v != nil {
|
||||
return time.Unix((*v / 1000), 0)
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// MillisecondsTimeValue converts an int64 pointer to a time.Time value
|
||||
// representing milliseconds sinch Epoch or time.Time{} if the pointer is nil.
|
||||
func MillisecondsTimeValue(v *int64) time.Time {
|
||||
if v != nil {
|
||||
return time.Unix(0, (*v * 1000000))
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// TimeUnixMilli returns a Unix timestamp in milliseconds from "January 1, 1970 UTC".
|
||||
// The result is undefined if the Unix time cannot be represented by an int64.
|
||||
// Which includes calling TimeUnixMilli on a zero Time is undefined.
|
||||
//
|
||||
// This utility is useful for service API's such as CloudWatch Logs which require
|
||||
// their unix time values to be in milliseconds.
|
||||
//
|
||||
// See Go stdlib https://golang.org/pkg/time/#Time.UnixNano for more information.
|
||||
func TimeUnixMilli(t time.Time) int64 {
|
||||
return t.UnixNano() / int64(time.Millisecond/time.Nanosecond)
|
||||
}
|
||||
|
||||
// TimeSlice converts a slice of time.Time values into a slice of
|
||||
// time.Time pointers
|
||||
func TimeSlice(src []time.Time) []*time.Time {
|
||||
dst := make([]*time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueSlice converts a slice of time.Time pointers into a slice of
|
||||
// time.Time values
|
||||
func TimeValueSlice(src []*time.Time) []time.Time {
|
||||
dst := make([]time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeMap converts a string map of time.Time values into a string
|
||||
// map of time.Time pointers
|
||||
func TimeMap(src map[string]time.Time) map[string]*time.Time {
|
||||
dst := make(map[string]*time.Time)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueMap converts a string map of time.Time pointers into a string
|
||||
// map of time.Time values
|
||||
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
|
||||
dst := make(map[string]time.Time)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
228
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
228
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
generated
vendored
@@ -1,228 +0,0 @@
|
||||
package corehandlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// Interface for matching types which also have a Len method.
|
||||
type lener interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
// BuildContentLengthHandler builds the content length of a request based on the body,
|
||||
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
|
||||
// to determine request body length and no "Content-Length" was specified it will panic.
|
||||
//
|
||||
// The Content-Length will only be added to the request if the length of the body
|
||||
// is greater than 0. If the body is empty or the current `Content-Length`
|
||||
// header is <= 0, the header will also be stripped.
|
||||
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
|
||||
var length int64
|
||||
|
||||
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||
length, _ = strconv.ParseInt(slength, 10, 64)
|
||||
} else {
|
||||
if r.Body != nil {
|
||||
var err error
|
||||
length, err = aws.SeekerLen(r.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if length > 0 {
|
||||
r.HTTPRequest.ContentLength = length
|
||||
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||
} else {
|
||||
r.HTTPRequest.ContentLength = 0
|
||||
r.HTTPRequest.Header.Del("Content-Length")
|
||||
}
|
||||
}}
|
||||
|
||||
var reStatusCode = regexp.MustCompile(`^(\d{3})`)
|
||||
|
||||
// ValidateReqSigHandler is a request handler to ensure that the request's
|
||||
// signature doesn't expire before it is sent. This can happen when a request
|
||||
// is built and signed significantly before it is sent. Or significant delays
|
||||
// occur when retrying requests that would cause the signature to expire.
|
||||
var ValidateReqSigHandler = request.NamedHandler{
|
||||
Name: "core.ValidateReqSigHandler",
|
||||
Fn: func(r *request.Request) {
|
||||
// Unsigned requests are not signed
|
||||
if r.Config.Credentials == credentials.AnonymousCredentials {
|
||||
return
|
||||
}
|
||||
|
||||
signedTime := r.Time
|
||||
if !r.LastSignedAt.IsZero() {
|
||||
signedTime = r.LastSignedAt
|
||||
}
|
||||
|
||||
// 10 minutes to allow for some clock skew/delays in transmission.
|
||||
// Would be improved with aws/aws-sdk-go#423
|
||||
if signedTime.Add(10 * time.Minute).After(time.Now()) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("request expired, resigning")
|
||||
r.Sign()
|
||||
},
|
||||
}
|
||||
|
||||
// SendHandler is a request handler to send service request using HTTP client.
|
||||
var SendHandler = request.NamedHandler{
|
||||
Name: "core.SendHandler",
|
||||
Fn: func(r *request.Request) {
|
||||
sender := sendFollowRedirects
|
||||
if r.DisableFollowRedirects {
|
||||
sender = sendWithoutFollowRedirects
|
||||
}
|
||||
|
||||
if request.NoBody == r.HTTPRequest.Body {
|
||||
// Strip off the request body if the NoBody reader was used as a
|
||||
// place holder for a request body. This prevents the SDK from
|
||||
// making requests with a request body when it would be invalid
|
||||
// to do so.
|
||||
//
|
||||
// Use a shallow copy of the http.Request to ensure the race condition
|
||||
// of transport on Body will not trigger
|
||||
reqOrig, reqCopy := r.HTTPRequest, *r.HTTPRequest
|
||||
reqCopy.Body = nil
|
||||
r.HTTPRequest = &reqCopy
|
||||
defer func() {
|
||||
r.HTTPRequest = reqOrig
|
||||
}()
|
||||
}
|
||||
|
||||
var err error
|
||||
r.HTTPResponse, err = sender(r)
|
||||
if err != nil {
|
||||
handleSendError(r, err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func sendFollowRedirects(r *request.Request) (*http.Response, error) {
|
||||
return r.Config.HTTPClient.Do(r.HTTPRequest)
|
||||
}
|
||||
|
||||
func sendWithoutFollowRedirects(r *request.Request) (*http.Response, error) {
|
||||
transport := r.Config.HTTPClient.Transport
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
|
||||
return transport.RoundTrip(r.HTTPRequest)
|
||||
}
|
||||
|
||||
func handleSendError(r *request.Request, err error) {
|
||||
// Prevent leaking if an HTTPResponse was returned. Clean up
|
||||
// the body.
|
||||
if r.HTTPResponse != nil {
|
||||
r.HTTPResponse.Body.Close()
|
||||
}
|
||||
// Capture the case where url.Error is returned for error processing
|
||||
// response. e.g. 301 without location header comes back as string
|
||||
// error and r.HTTPResponse is nil. Other URL redirect errors will
|
||||
// comeback in a similar method.
|
||||
if e, ok := err.(*url.Error); ok && e.Err != nil {
|
||||
if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
|
||||
code, _ := strconv.ParseInt(s[1], 10, 64)
|
||||
r.HTTPResponse = &http.Response{
|
||||
StatusCode: int(code),
|
||||
Status: http.StatusText(int(code)),
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if r.HTTPResponse == nil {
|
||||
// Add a dummy request response object to ensure the HTTPResponse
|
||||
// value is consistent.
|
||||
r.HTTPResponse = &http.Response{
|
||||
StatusCode: int(0),
|
||||
Status: http.StatusText(int(0)),
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
}
|
||||
}
|
||||
// Catch all other request errors.
|
||||
r.Error = awserr.New("RequestError", "send request failed", err)
|
||||
r.Retryable = aws.Bool(true) // network errors are retryable
|
||||
|
||||
// Override the error with a context canceled error, if that was canceled.
|
||||
ctx := r.Context()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
r.Error = awserr.New(request.CanceledErrorCode,
|
||||
"request context canceled", ctx.Err())
|
||||
r.Retryable = aws.Bool(false)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateResponseHandler is a request handler to validate service response.
|
||||
var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
|
||||
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
|
||||
// this may be replaced by an UnmarshalError handler
|
||||
r.Error = awserr.New("UnknownError", "unknown error", nil)
|
||||
}
|
||||
}}
|
||||
|
||||
// AfterRetryHandler performs final checks to determine if the request should
|
||||
// be retried and how long to delay.
|
||||
var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
|
||||
// If one of the other handlers already set the retry state
|
||||
// we don't want to override it based on the service's state
|
||||
if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) {
|
||||
r.Retryable = aws.Bool(r.ShouldRetry(r))
|
||||
}
|
||||
|
||||
if r.WillRetry() {
|
||||
r.RetryDelay = r.RetryRules(r)
|
||||
|
||||
if sleepFn := r.Config.SleepDelay; sleepFn != nil {
|
||||
// Support SleepDelay for backwards compatibility and testing
|
||||
sleepFn(r.RetryDelay)
|
||||
} else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
|
||||
r.Error = awserr.New(request.CanceledErrorCode,
|
||||
"request context canceled", err)
|
||||
r.Retryable = aws.Bool(false)
|
||||
return
|
||||
}
|
||||
|
||||
// when the expired token exception occurs the credentials
|
||||
// need to be expired locally so that the next request to
|
||||
// get credentials will trigger a credentials refresh.
|
||||
if r.IsErrorExpired() {
|
||||
r.Config.Credentials.Expire()
|
||||
}
|
||||
|
||||
r.RetryCount++
|
||||
r.Error = nil
|
||||
}
|
||||
}}
|
||||
|
||||
// ValidateEndpointHandler is a request handler to validate a request had the
|
||||
// appropriate Region and Endpoint set. Will set r.Error if the endpoint or
|
||||
// region is not valid.
|
||||
var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
|
||||
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
|
||||
r.Error = aws.ErrMissingRegion
|
||||
} else if r.ClientInfo.Endpoint == "" {
|
||||
r.Error = aws.ErrMissingEndpoint
|
||||
}
|
||||
}}
|
||||
17
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
17
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go
generated
vendored
@@ -1,17 +0,0 @@
|
||||
package corehandlers
|
||||
|
||||
import "github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
// ValidateParametersHandler is a request handler to validate the input parameters.
|
||||
// Validating parameters only has meaning if done prior to the request being sent.
|
||||
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
|
||||
if !r.ParamsFilled() {
|
||||
return
|
||||
}
|
||||
|
||||
if v, ok := r.Params.(request.Validator); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
r.Error = err
|
||||
}
|
||||
}
|
||||
}}
|
||||
37
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go
generated
vendored
37
vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go
generated
vendored
@@ -1,37 +0,0 @@
|
||||
package corehandlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version
|
||||
// to the user agent.
|
||||
var SDKVersionUserAgentHandler = request.NamedHandler{
|
||||
Name: "core.SDKVersionUserAgentHandler",
|
||||
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
|
||||
runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
|
||||
const execEnvVar = `AWS_EXECUTION_ENV`
|
||||
const execEnvUAKey = `exec_env`
|
||||
|
||||
// AddHostExecEnvUserAgentHander is a request handler appending the SDK's
|
||||
// execution environment to the user agent.
|
||||
//
|
||||
// If the environment variable AWS_EXECUTION_ENV is set, its value will be
|
||||
// appended to the user agent string.
|
||||
var AddHostExecEnvUserAgentHander = request.NamedHandler{
|
||||
Name: "core.AddHostExecEnvUserAgentHander",
|
||||
Fn: func(r *request.Request) {
|
||||
v := os.Getenv(execEnvVar)
|
||||
if len(v) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
request.AddToUserAgent(r, execEnvUAKey+"/"+v)
|
||||
},
|
||||
}
|
||||
102
vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
102
vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoValidProvidersFoundInChain Is returned when there are no valid
|
||||
// providers in the ChainProvider.
|
||||
//
|
||||
// This has been deprecated. For verbose error messaging set
|
||||
// aws.Config.CredentialsChainVerboseErrors to true
|
||||
//
|
||||
// @readonly
|
||||
ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders",
|
||||
`no valid providers in chain. Deprecated.
|
||||
For verbose messaging see aws.Config.CredentialsChainVerboseErrors`,
|
||||
nil)
|
||||
)
|
||||
|
||||
// A ChainProvider will search for a provider which returns credentials
|
||||
// and cache that provider until Retrieve is called again.
|
||||
//
|
||||
// The ChainProvider provides a way of chaining multiple providers together
|
||||
// which will pick the first available using priority order of the Providers
|
||||
// in the list.
|
||||
//
|
||||
// If none of the Providers retrieve valid credentials Value, ChainProvider's
|
||||
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
|
||||
//
|
||||
// If a Provider is found which returns valid credentials Value ChainProvider
|
||||
// will cache that Provider for all calls to IsExpired(), until Retrieve is
|
||||
// called again.
|
||||
//
|
||||
// Example of ChainProvider to be used with an EnvProvider and EC2RoleProvider.
|
||||
// In this example EnvProvider will first check if any credentials are available
|
||||
// via the environment variables. If there are none ChainProvider will check
|
||||
// the next Provider in the list, EC2RoleProvider in this case. If EC2RoleProvider
|
||||
// does not return any credentials ChainProvider will return the error
|
||||
// ErrNoValidProvidersFoundInChain
|
||||
//
|
||||
// creds := credentials.NewChainCredentials(
|
||||
// []credentials.Provider{
|
||||
// &credentials.EnvProvider{},
|
||||
// &ec2rolecreds.EC2RoleProvider{
|
||||
// Client: ec2metadata.New(sess),
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// // Usage of ChainCredentials with aws.Config
|
||||
// svc := ec2.New(session.Must(session.NewSession(&aws.Config{
|
||||
// Credentials: creds,
|
||||
// })))
|
||||
//
|
||||
type ChainProvider struct {
|
||||
Providers []Provider
|
||||
curr Provider
|
||||
VerboseErrors bool
|
||||
}
|
||||
|
||||
// NewChainCredentials returns a pointer to a new Credentials object
|
||||
// wrapping a chain of providers.
|
||||
func NewChainCredentials(providers []Provider) *Credentials {
|
||||
return NewCredentials(&ChainProvider{
|
||||
Providers: append([]Provider{}, providers...),
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve returns the credentials value or error if no provider returned
|
||||
// without error.
|
||||
//
|
||||
// If a provider is found it will be cached and any calls to IsExpired()
|
||||
// will return the expired state of the cached provider.
|
||||
func (c *ChainProvider) Retrieve() (Value, error) {
|
||||
var errs []error
|
||||
for _, p := range c.Providers {
|
||||
creds, err := p.Retrieve()
|
||||
if err == nil {
|
||||
c.curr = p
|
||||
return creds, nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
c.curr = nil
|
||||
|
||||
var err error
|
||||
err = ErrNoValidProvidersFoundInChain
|
||||
if c.VerboseErrors {
|
||||
err = awserr.NewBatchError("NoCredentialProviders", "no valid providers in chain", errs)
|
||||
}
|
||||
return Value{}, err
|
||||
}
|
||||
|
||||
// IsExpired will returned the expired state of the currently cached provider
|
||||
// if there is one. If there is no current provider, true will be returned.
|
||||
func (c *ChainProvider) IsExpired() bool {
|
||||
if c.curr != nil {
|
||||
return c.curr.IsExpired()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user