Update go dependencies

This commit is contained in:
Manuel Alejandro de Brito Fontes 2019-05-22 18:19:08 -04:00
parent c639f490b1
commit d3c957192e
No known key found for this signature in database
GPG key ID: 786136016A8BA02A
125 changed files with 12284 additions and 0 deletions

201
vendor/sigs.k8s.io/controller-runtime/LICENSE generated vendored Normal file
View file

@ -0,0 +1,201 @@
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.

View file

@ -0,0 +1,95 @@
/*
Copyright 2017 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 config
import (
"flag"
"fmt"
"os"
"os/user"
"path/filepath"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)
var (
kubeconfig, masterURL string
log = logf.KBLog.WithName("client").WithName("config")
)
func init() {
// TODO: Fix this to allow double vendoring this library but still register flags on behalf of users
flag.StringVar(&kubeconfig, "kubeconfig", "",
"Paths to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&masterURL, "master", "",
"The address of the Kubernetes API server. Overrides any value in kubeconfig. "+
"Only required if out-of-cluster.")
}
// GetConfig creates a *rest.Config for talking to a Kubernetes apiserver.
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
// in cluster and use the cluster provided kubeconfig.
//
// Config precedence
//
// * --kubeconfig flag pointing at a file
//
// * KUBECONFIG environment variable pointing at a file
//
// * In-cluster config if running in cluster
//
// * $HOME/.kube/config if exists
func GetConfig() (*rest.Config, error) {
// If a flag is specified with the config location, use that
if len(kubeconfig) > 0 {
return clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
}
// If an env variable is specified with the config locaiton, use that
if len(os.Getenv("KUBECONFIG")) > 0 {
return clientcmd.BuildConfigFromFlags(masterURL, os.Getenv("KUBECONFIG"))
}
// If no explicit location, try the in-cluster config
if c, err := rest.InClusterConfig(); err == nil {
return c, nil
}
// If no in-cluster config, try the default location in the user's home directory
if usr, err := user.Current(); err == nil {
if c, err := clientcmd.BuildConfigFromFlags(
"", filepath.Join(usr.HomeDir, ".kube", "config")); err == nil {
return c, nil
}
}
return nil, fmt.Errorf("could not locate a kubeconfig")
}
// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
// If --kubeconfig is set, will use the kubeconfig file at that location. Otherwise will assume running
// in cluster and use the cluster provided kubeconfig.
//
// Will log an error and exit if there is an error creating the rest.Config.
func GetConfigOrDie() *rest.Config {
config, err := GetConfig()
if err != nil {
log.Error(err, "unable to get kubeconfig")
os.Exit(1)
}
return config
}

View file

@ -0,0 +1,18 @@
/*
Copyright 2017 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 config contains libraries for initializing rest configs for talking to the Kubernetes API
package config

View file

@ -0,0 +1,224 @@
/*
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 envtest
import (
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/ghodss/yaml"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
)
// CRDInstallOptions are the options for installing CRDs
type CRDInstallOptions struct {
// Paths is the path to the directory containing CRDs
Paths []string
// CRDs is a list of CRDs to install
CRDs []*apiextensionsv1beta1.CustomResourceDefinition
// ErrorIfPathMissing will cause an error if a Path does not exist
ErrorIfPathMissing bool
// maxTime is the max time to wait
maxTime time.Duration
// pollInterval is the interval to check
pollInterval time.Duration
}
const defaultPollInterval = 100 * time.Millisecond
const defaultMaxWait = 10 * time.Second
// InstallCRDs installs a collection of CRDs into a cluster by reading the crd yaml files from a directory
func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
defaultCRDOptions(&options)
// Read the CRD yamls into options.CRDs
if err := readCRDFiles(&options); err != nil {
return nil, err
}
// Create the CRDs in the apiserver
if err := CreateCRDs(config, options.CRDs); err != nil {
return options.CRDs, err
}
// Wait for the CRDs to appear as Resources in the apiserver
if err := WaitForCRDs(config, options.CRDs, options); err != nil {
return options.CRDs, err
}
return options.CRDs, nil
}
// readCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs
func readCRDFiles(options *CRDInstallOptions) error {
if len(options.Paths) > 0 {
for _, path := range options.Paths {
if _, err := os.Stat(path); !options.ErrorIfPathMissing && os.IsNotExist(err) {
continue
}
new, err := readCRDs(path)
if err != nil {
return err
}
options.CRDs = append(options.CRDs, new...)
}
}
return nil
}
// defaultCRDOptions sets the default values for CRDs
func defaultCRDOptions(o *CRDInstallOptions) {
if o.maxTime == 0 {
o.maxTime = defaultMaxWait
}
if o.pollInterval == 0 {
o.pollInterval = defaultPollInterval
}
}
// WaitForCRDs waits for the CRDs to appear in discovery
func WaitForCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourceDefinition, options CRDInstallOptions) error {
// Add each CRD to a map of GroupVersion to Resource
waitingFor := map[schema.GroupVersion]*sets.String{}
for _, crd := range crds {
gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version}
if _, found := waitingFor[gv]; !found {
// Initialize the set
waitingFor[gv] = &sets.String{}
}
// Add the Resource
waitingFor[gv].Insert(crd.Spec.Names.Plural)
}
// Poll until all resources are found in discovery
p := &poller{config: config, waitingFor: waitingFor}
return wait.PollImmediate(options.pollInterval, options.maxTime, p.poll)
}
// poller checks if all the resources have been found in discovery, and returns false if not
type poller struct {
// config is used to get discovery
config *rest.Config
// waitingFor is the map of resources keyed by group version that have not yet been found in discovery
waitingFor map[schema.GroupVersion]*sets.String
}
// poll checks if all the resources have been found in discovery, and returns false if not
func (p *poller) poll() (done bool, err error) {
// Create a new clientset to avoid any client caching of discovery
cs, err := clientset.NewForConfig(p.config)
if err != nil {
return false, err
}
allFound := true
for gv, resources := range p.waitingFor {
// All resources found, do nothing
if resources.Len() == 0 {
delete(p.waitingFor, gv)
continue
}
// Get the Resources for this GroupVersion
// TODO: Maybe the controller-runtime client should be able to do this...
resourceList, err := cs.Discovery().ServerResourcesForGroupVersion(gv.Group + "/" + gv.Version)
if err != nil {
return false, nil
}
// Remove each found resource from the resources set that we are waiting for
for _, resource := range resourceList.APIResources {
resources.Delete(resource.Name)
}
// Still waiting on some resources in this group version
if resources.Len() != 0 {
allFound = false
}
}
return allFound, nil
}
// CreateCRDs creates the CRDs
func CreateCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResourceDefinition) error {
cs, err := clientset.NewForConfig(config)
if err != nil {
return err
}
// Create each CRD
for _, crd := range crds {
log.V(1).Info("installing CRD", "crd", crd)
if _, err := cs.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil {
return err
}
}
return nil
}
// readCRDs reads the CRDs from files and Unmarshals them into structs
func readCRDs(path string) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
// Get the CRD files
var files []os.FileInfo
var err error
log.V(1).Info("reading CRDs from path", "path", path)
if files, err = ioutil.ReadDir(path); err != nil {
return nil, err
}
// White list the file extensions that may contain CRDs
crdExts := sets.NewString(".json", ".yaml", ".yml")
var crds []*apiextensionsv1beta1.CustomResourceDefinition
for _, file := range files {
// Only parse whitelisted file types
if !crdExts.Has(filepath.Ext(file.Name())) {
continue
}
// Unmarshal the file into a struct
b, err := ioutil.ReadFile(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
crd := &apiextensionsv1beta1.CustomResourceDefinition{}
if err = yaml.Unmarshal(b, crd); err != nil {
return nil, err
}
// Check that it is actually a CRD
if crd.Spec.Names.Kind == "" || crd.Spec.Group == "" {
continue
}
log.V(1).Info("read CRD from file", "file", file)
crds = append(crds, crd)
}
return crds, nil
}

View file

@ -0,0 +1,18 @@
/*
Copyright 2017 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 envtest provides libraries for integration testing by starting a local control plane
package envtest

View file

@ -0,0 +1,11 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: foos.bar.example.com
spec:
group: bar.example.com
names:
kind: Foo
plural: foos
scope: Namespaced
version: "v1beta1"

View file

@ -0,0 +1,11 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: bazs.qux.example.com
spec:
group: qux.example.com
names:
kind: Baz
plural: bazs
scope: Namespaced
version: "v1beta1"

View file

@ -0,0 +1,11 @@
package envtest
import (
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
// are correctly parsed by test automation.
// See issue https://github.com/jstemmer/go-junit-report/issues/31
// It's re-exported here to avoid compatibility breakage/mass rewrites.
type NewlineReporter = printer.NewlineReporter

View file

@ -0,0 +1,18 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9

View file

@ -0,0 +1,51 @@
/*
Copyright 2016 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 printer
import (
"fmt"
"github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/types"
)
var _ ginkgo.Reporter = NewlineReporter{}
// NewlineReporter is Reporter that Prints a newline after the default Reporter output so that the results
// are correctly parsed by test automation.
// See issue https://github.com/jstemmer/go-junit-report/issues/31
type NewlineReporter struct{}
// SpecSuiteWillBegin implements ginkgo.Reporter
func (NewlineReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) {
}
// BeforeSuiteDidRun implements ginkgo.Reporter
func (NewlineReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) {}
// AfterSuiteDidRun implements ginkgo.Reporter
func (NewlineReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) {}
// SpecWillRun implements ginkgo.Reporter
func (NewlineReporter) SpecWillRun(specSummary *types.SpecSummary) {}
// SpecDidComplete implements ginkgo.Reporter
func (NewlineReporter) SpecDidComplete(specSummary *types.SpecSummary) {}
// SpecSuiteDidEnd Prints a newline between "35 Passed | 0 Failed | 0 Pending | 0 Skipped" and "--- PASS:"
func (NewlineReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { fmt.Printf("\n") }

View file

@ -0,0 +1,222 @@
/*
Copyright 2016 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 envtest
import (
"fmt"
"os"
"path/filepath"
"time"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/testing_frameworks/integration"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)
var log = logf.KBLog.WithName("test-env")
// Default binary path for test framework
const (
envKubeAPIServerBin = "TEST_ASSET_KUBE_APISERVER"
envEtcdBin = "TEST_ASSET_ETCD"
envKubectlBin = "TEST_ASSET_KUBECTL"
envKubebuilderPath = "KUBEBUILDER_ASSETS"
envStartTimeout = "KUBEBUILDER_CONTROLPLANE_START_TIMEOUT"
envStopTimeout = "KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT"
defaultKubebuilderPath = "/usr/local/kubebuilder/bin"
StartTimeout = 60
StopTimeout = 60
defaultKubebuilderControlPlaneStartTimeout = 20 * time.Second
defaultKubebuilderControlPlaneStopTimeout = 20 * time.Second
)
func defaultAssetPath(binary string) string {
assetPath := os.Getenv(envKubebuilderPath)
if assetPath == "" {
assetPath = defaultKubebuilderPath
}
return filepath.Join(assetPath, binary)
}
// DefaultKubeAPIServerFlags are default flags necessary to bring up apiserver.
var DefaultKubeAPIServerFlags = []string{
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}",
"--cert-dir={{ .CertDir }}",
"--insecure-port={{ if .URL }}{{ .URL.Port }}{{ end }}",
"--insecure-bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}",
"--secure-port={{ if .SecurePort }}{{ .SecurePort }}{{ end }}",
"--admission-control=AlwaysAdmit",
}
// Environment creates a Kubernetes test environment that will start / stop the Kubernetes control plane and
// install extension APIs
type Environment struct {
// ControlPlane is the ControlPlane including the apiserver and etcd
ControlPlane integration.ControlPlane
// Config can be used to talk to the apiserver
Config *rest.Config
// CRDs is a list of CRDs to install
CRDs []*apiextensionsv1beta1.CustomResourceDefinition
// CRDDirectoryPaths is a list of paths containing CRD yaml or json configs.
CRDDirectoryPaths []string
// UseExisting indicates that this environments should use an
// existing kubeconfig, instead of trying to stand up a new control plane.
// This is useful in cases that need aggregated API servers and the like.
UseExistingCluster bool
// ControlPlaneStartTimeout is the maximum duration each controlplane component
// may take to start. It defaults to the KUBEBUILDER_CONTROLPLANE_START_TIMEOUT
// environment variable or 20 seconds if unspecified
ControlPlaneStartTimeout time.Duration
// ControlPlaneStopTimeout is the maximum duration each controlplane component
// may take to stop. It defaults to the KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT
// environment variable or 20 seconds if unspecified
ControlPlaneStopTimeout time.Duration
// KubeAPIServerFlags is the set of flags passed while starting the api server.
KubeAPIServerFlags []string
}
// Stop stops a running server
func (te *Environment) Stop() error {
if te.UseExistingCluster {
return nil
}
return te.ControlPlane.Stop()
}
// getAPIServerFlags returns flags to be used with the Kubernetes API server.
func (te Environment) getAPIServerFlags() []string {
// Set default API server flags if not set.
if len(te.KubeAPIServerFlags) == 0 {
return DefaultKubeAPIServerFlags
}
return te.KubeAPIServerFlags
}
// Start starts a local Kubernetes server and updates te.ApiserverPort with the port it is listening on
func (te *Environment) Start() (*rest.Config, error) {
if te.UseExistingCluster {
log.V(1).Info("using existing cluster")
if te.Config == nil {
// we want to allow people to pass in their own config, so
// only load a config if it hasn't already been set.
log.V(1).Info("automatically acquiring client configuration")
var err error
te.Config, err = config.GetConfig()
if err != nil {
return nil, err
}
}
} else {
te.ControlPlane = integration.ControlPlane{}
te.ControlPlane.APIServer = &integration.APIServer{Args: te.getAPIServerFlags()}
te.ControlPlane.Etcd = &integration.Etcd{}
if os.Getenv(envKubeAPIServerBin) == "" {
te.ControlPlane.APIServer.Path = defaultAssetPath("kube-apiserver")
}
if os.Getenv(envEtcdBin) == "" {
te.ControlPlane.Etcd.Path = defaultAssetPath("etcd")
}
if os.Getenv(envKubectlBin) == "" {
// we can't just set the path manually (it's behind a function), so set the environment variable instead
if err := os.Setenv(envKubectlBin, defaultAssetPath("kubectl")); err != nil {
return nil, err
}
}
if err := te.defaultTimeouts(); err != nil {
return nil, fmt.Errorf("failed to default controlplane timeouts: %v", err)
}
te.ControlPlane.Etcd.StartTimeout = te.ControlPlaneStartTimeout
te.ControlPlane.Etcd.StopTimeout = te.ControlPlaneStopTimeout
te.ControlPlane.APIServer.StartTimeout = te.ControlPlaneStartTimeout
te.ControlPlane.APIServer.StopTimeout = te.ControlPlaneStopTimeout
log.V(1).Info("starting control plane", "api server flags", te.ControlPlane.APIServer.Args)
if err := te.startControlPlane(); err != nil {
return nil, err
}
// Create the *rest.Config for creating new clients
te.Config = &rest.Config{
Host: te.ControlPlane.APIURL().Host,
}
}
log.V(1).Info("installing CRDs")
_, err := InstallCRDs(te.Config, CRDInstallOptions{
Paths: te.CRDDirectoryPaths,
CRDs: te.CRDs,
})
return te.Config, err
}
func (te *Environment) startControlPlane() error {
numTries, maxRetries := 0, 5
var err error
for ; numTries < maxRetries; numTries++ {
// Start the control plane - retry if it fails
err = te.ControlPlane.Start()
if err == nil {
break
}
log.Error(err, "unable to start the controlplane", "tries", numTries)
}
if numTries == maxRetries {
return fmt.Errorf("failed to start the controlplane. retried %d times: %v", numTries, err)
}
return nil
}
func (te *Environment) defaultTimeouts() error {
var err error
if te.ControlPlaneStartTimeout == 0 {
if envVal := os.Getenv(envStartTimeout); envVal != "" {
te.ControlPlaneStartTimeout, err = time.ParseDuration(envVal)
if err != nil {
return err
}
} else {
te.ControlPlaneStartTimeout = defaultKubebuilderControlPlaneStartTimeout
}
}
if te.ControlPlaneStopTimeout == 0 {
if envVal := os.Getenv(envStopTimeout); envVal != "" {
te.ControlPlaneStopTimeout, err = time.ParseDuration(envVal)
if err != nil {
return err
}
} else {
te.ControlPlaneStopTimeout = defaultKubebuilderControlPlaneStopTimeout
}
}
return nil
}

View file

@ -0,0 +1,126 @@
/*
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 log
import (
"github.com/go-logr/logr"
)
// loggerPromise knows how to populate a concrete logr.Logger
// with options, given an actual base logger later on down the line.
type loggerPromise struct {
logger *DelegatingLogger
childPromises []*loggerPromise
name *string
tags []interface{}
}
// WithName provides a new Logger with the name appended
func (p *loggerPromise) WithName(l *DelegatingLogger, name string) *loggerPromise {
res := &loggerPromise{
logger: l,
name: &name,
}
p.childPromises = append(p.childPromises, res)
return res
}
// WithValues provides a new Logger with the tags appended
func (p *loggerPromise) WithValues(l *DelegatingLogger, tags ...interface{}) *loggerPromise {
res := &loggerPromise{
logger: l,
tags: tags,
}
p.childPromises = append(p.childPromises, res)
return res
}
// Fulfill instantiates the Logger with the provided logger
func (p *loggerPromise) Fulfill(parentLogger logr.Logger) {
var logger = parentLogger
if p.name != nil {
logger = logger.WithName(*p.name)
}
if p.tags != nil {
logger = logger.WithValues(p.tags...)
}
p.logger.Logger = logger
p.logger.promise = nil
for _, childPromise := range p.childPromises {
childPromise.Fulfill(logger)
}
}
// DelegatingLogger is a logr.Logger that delegates to another logr.Logger.
// If the underlying promise is not nil, it registers calls to sub-loggers with
// the logging factory to be populated later, and returns a new delegating
// logger. It expects to have *some* logr.Logger set at all times (generally
// a no-op logger before the promises are fulfilled).
type DelegatingLogger struct {
logr.Logger
promise *loggerPromise
}
// WithName provides a new Logger with the name appended
func (l *DelegatingLogger) WithName(name string) logr.Logger {
if l.promise == nil {
return l.Logger.WithName(name)
}
res := &DelegatingLogger{Logger: l.Logger}
promise := l.promise.WithName(res, name)
res.promise = promise
return res
}
// WithValues provides a new Logger with the tags appended
func (l *DelegatingLogger) WithValues(tags ...interface{}) logr.Logger {
if l.promise == nil {
return l.Logger.WithValues(tags...)
}
res := &DelegatingLogger{Logger: l.Logger}
promise := l.promise.WithValues(res, tags...)
res.promise = promise
return res
}
// Fulfill switches the logger over to use the actual logger
// provided, instead of the temporary initial one, if this method
// has not been previously called.
func (l *DelegatingLogger) Fulfill(actual logr.Logger) {
if l.promise != nil {
l.promise.Fulfill(actual)
}
}
// NewDelegatingLogger constructs a new DelegatingLogger which uses
// the given logger before it's promise is fulfilled.
func NewDelegatingLogger(initial logr.Logger) *DelegatingLogger {
l := &DelegatingLogger{
Logger: initial,
promise: &loggerPromise{},
}
l.promise.logger = l
return l
}

View file

@ -0,0 +1,129 @@
/*
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 log contains utilities for fetching a new logger
// when one is not already available.
package log
import (
"fmt"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
// KubeAwareEncoder is a Kubernetes-aware Zap Encoder.
// Instead of trying to force Kubernetes objects to implement
// ObjectMarshaller, we just implement a wrapper around a normal
// ObjectMarshaller that checks for Kubernetes objects.
type KubeAwareEncoder struct {
// Encoder is the zapcore.Encoder that this encoder delegates to
zapcore.Encoder
// Verbose controls whether or not the full object is printed.
// If false, only name, namespace, api version, and kind are printed.
// Otherwise, the full object is logged.
Verbose bool
}
// namespacedNameWrapper is a zapcore.ObjectMarshaler for Kubernetes NamespacedName
type namespacedNameWrapper struct {
types.NamespacedName
}
func (w namespacedNameWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
if w.Namespace != "" {
enc.AddString("namespace", w.Namespace)
}
enc.AddString("name", w.Name)
return nil
}
// kubeObjectWrapper is a zapcore.ObjectMarshaler for Kubernetes objects.
type kubeObjectWrapper struct {
obj runtime.Object
}
// MarshalLogObject implements zapcore.ObjectMarshaler
func (w kubeObjectWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
// TODO(directxman12): log kind and apiversion if not set explicitly (common case)
// -- needs an a scheme to convert to the GVK.
gvk := w.obj.GetObjectKind().GroupVersionKind()
if gvk.Version != "" {
enc.AddString("apiVersion", gvk.GroupVersion().String())
enc.AddString("kind", gvk.Kind)
}
objMeta, err := meta.Accessor(w.obj)
if err != nil {
return fmt.Errorf("got runtime.Object without object metadata: %v", w.obj)
}
ns := objMeta.GetNamespace()
if ns != "" {
enc.AddString("namespace", ns)
}
enc.AddString("name", objMeta.GetName())
return nil
}
// NB(directxman12): can't just override AddReflected, since the encoder calls AddReflected on itself directly
// Clone implements zapcore.Encoder
func (k *KubeAwareEncoder) Clone() zapcore.Encoder {
return &KubeAwareEncoder{
Encoder: k.Encoder.Clone(),
}
}
// EncodeEntry implements zapcore.Encoder
func (k *KubeAwareEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
if k.Verbose {
// Kubernetes objects implement fmt.Stringer, so if we
// want verbose output, just delegate to that.
return k.Encoder.EncodeEntry(entry, fields)
}
for i, field := range fields {
// intercept stringer fields that happen to be Kubernetes runtime.Object or
// types.NamespacedName values (Kubernetes runtime.Objects commonly
// implement String, apparently).
if field.Type == zapcore.StringerType {
switch val := field.Interface.(type) {
case runtime.Object:
fields[i] = zapcore.Field{
Type: zapcore.ObjectMarshalerType,
Key: field.Key,
Interface: kubeObjectWrapper{obj: val},
}
case types.NamespacedName:
fields[i] = zapcore.Field{
Type: zapcore.ObjectMarshalerType,
Key: field.Key,
Interface: namespacedNameWrapper{NamespacedName: val},
}
}
}
}
return k.Encoder.EncodeEntry(entry, fields)
}

View file

@ -0,0 +1,85 @@
/*
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 log contains utilities for fetching a new logger
// when one is not already available.
package log
import (
"io"
"os"
"time"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// ZapLogger is a Logger implementation.
// If development is true, a Zap development config will be used
// (stacktraces on warnings, no sampling), otherwise a Zap production
// config will be used (stacktraces on errors, sampling).
func ZapLogger(development bool) logr.Logger {
return ZapLoggerTo(os.Stderr, development)
}
// ZapLoggerTo returns a new Logger implementation using Zap which logs
// to the given destination, instead of stderr. It otherise behaves like
// ZapLogger.
func ZapLoggerTo(destWriter io.Writer, development bool) logr.Logger {
// this basically mimics New<type>Config, but with a custom sink
sink := zapcore.AddSync(destWriter)
var enc zapcore.Encoder
var lvl zap.AtomicLevel
var opts []zap.Option
if development {
encCfg := zap.NewDevelopmentEncoderConfig()
enc = zapcore.NewConsoleEncoder(encCfg)
lvl = zap.NewAtomicLevelAt(zap.DebugLevel)
opts = append(opts, zap.Development(), zap.AddStacktrace(zap.ErrorLevel))
} else {
encCfg := zap.NewProductionEncoderConfig()
enc = zapcore.NewJSONEncoder(encCfg)
lvl = zap.NewAtomicLevelAt(zap.InfoLevel)
opts = append(opts, zap.AddStacktrace(zap.WarnLevel),
zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSampler(core, time.Second, 100, 100)
}))
}
opts = append(opts, zap.AddCallerSkip(1), zap.ErrorOutput(sink))
log := zap.New(zapcore.NewCore(&KubeAwareEncoder{Encoder: enc, Verbose: development}, sink, lvl))
log = log.WithOptions(opts...)
return zapr.NewLogger(log)
}
// SetLogger sets a concrete logging implementation for all deferred Loggers.
func SetLogger(l logr.Logger) {
Log.Fulfill(l)
}
// Log is the base logger used by kubebuilder. It delegates
// to another logr.Logger. You *must* call SetLogger to
// get any actual logging.
var Log = NewDelegatingLogger(NullLogger{})
// KBLog is a base parent logger.
var KBLog logr.Logger
func init() {
KBLog = Log.WithName("kubebuilder")
}

View 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 log
import (
"github.com/go-logr/logr"
)
// NB: this is the same as the null logger logr/testing,
// but avoids accidentally adding the testing flags to
// all binaries.
// NullLogger is a logr.Logger that does nothing.
type NullLogger struct{}
var _ logr.Logger = NullLogger{}
// Info implements logr.InfoLogger
func (NullLogger) Info(_ string, _ ...interface{}) {
// Do nothing.
}
// Enabled implements logr.InfoLogger
func (NullLogger) Enabled() bool {
return false
}
// Error implements logr.Logger
func (NullLogger) Error(_ error, _ string, _ ...interface{}) {
// Do nothing.
}
// V implements logr.Logger
func (log NullLogger) V(_ int) logr.InfoLogger {
return log
}
// WithName implements logr.Logger
func (log NullLogger) WithName(_ string) logr.Logger {
return log
}
// WithValues implements logr.Logger
func (log NullLogger) WithValues(_ ...interface{}) logr.Logger {
return log
}