From 84f7ac1f1175dc4a2a1e25795e1f52b05cf51ad4 Mon Sep 17 00:00:00 2001 From: tianshapjq Date: Tue, 12 Dec 2017 14:30:24 +0800 Subject: [PATCH 001/416] small nit in the annotations --- pkg/kubelet/container/os.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubelet/container/os.go b/pkg/kubelet/container/os.go index 6126063b308..bd27ae9f079 100644 --- a/pkg/kubelet/container/os.go +++ b/pkg/kubelet/container/os.go @@ -43,7 +43,7 @@ type OSInterface interface { // RealOS is used to dispatch the real system level operations. type RealOS struct{} -// MkDir will will call os.Mkdir to create a directory. +// MkdirAll will call os.MkdirAll to create a directory. func (RealOS) MkdirAll(path string, perm os.FileMode) error { return os.MkdirAll(path, perm) } From 9c591b0c94e723b6a5150e952f385d402252ca3d Mon Sep 17 00:00:00 2001 From: Anshul Sharma Date: Tue, 16 Jan 2018 14:51:52 +0530 Subject: [PATCH 002/416] Replace error string with const --- pkg/cloudprovider/providers/aws/aws_loadbalancer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/cloudprovider/providers/aws/aws_loadbalancer.go b/pkg/cloudprovider/providers/aws/aws_loadbalancer.go index 438b5617f19..3fc603438d4 100644 --- a/pkg/cloudprovider/providers/aws/aws_loadbalancer.go +++ b/pkg/cloudprovider/providers/aws/aws_loadbalancer.go @@ -1375,8 +1375,7 @@ func (c *Cloud) ensureSSLNegotiationPolicy(loadBalancer *elb.LoadBalancerDescrip if err != nil { if aerr, ok := err.(awserr.Error); ok { switch aerr.Code() { - case "PolicyNotFound": - // TODO change from string to `elb.ErrCodePolicyNotFoundException` once the AWS SDK is updated + case elb.ErrCodePolicyNotFoundException: default: return fmt.Errorf("error describing security policies on load balancer: %q", err) } From 21702e3c395260e92ed71b6d1200716b859a4bec Mon Sep 17 00:00:00 2001 From: tianshapjq Date: Thu, 1 Feb 2018 14:38:29 +0800 Subject: [PATCH 003/416] TODO has already been implemented --- pkg/kubelet/cm/devicemanager/manager.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/kubelet/cm/devicemanager/manager.go b/pkg/kubelet/cm/devicemanager/manager.go index 049e7df78f7..c19e7fd62e7 100644 --- a/pkg/kubelet/cm/devicemanager/manager.go +++ b/pkg/kubelet/cm/devicemanager/manager.go @@ -256,7 +256,6 @@ func (m *ManagerImpl) Devices() map[string][]pluginapi.Device { func (m *ManagerImpl) Allocate(node *schedulercache.NodeInfo, attrs *lifecycle.PodAdmitAttributes) error { pod := attrs.Pod devicesToReuse := make(map[string]sets.String) - // TODO: Reuse devices between init containers and regular containers. for _, container := range pod.Spec.InitContainers { if err := m.allocateContainerResources(pod, &container, devicesToReuse); err != nil { return err From 284d08bf7032a94b568a95d6de1e13d8c0ac5bc2 Mon Sep 17 00:00:00 2001 From: root <837829664@qq.com> Date: Wed, 28 Feb 2018 19:11:35 +0800 Subject: [PATCH 004/416] fix persist typo --- .../src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go index 1fe52c5241d..9c3ea0ab8d5 100644 --- a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go @@ -279,7 +279,7 @@ func (p *oidcAuthProvider) idToken() (string, error) { // Persist new config and if successful, update the in memory config. if err = p.persister.Persist(newCfg); err != nil { - return "", fmt.Errorf("could not perist new tokens: %v", err) + return "", fmt.Errorf("could not persist new tokens: %v", err) } p.cfg = newCfg From 5f518e6d4cb2b68be05bafac2963aceb39ea90b7 Mon Sep 17 00:00:00 2001 From: jennybuckley Date: Wed, 14 Mar 2018 10:27:44 -0700 Subject: [PATCH 005/416] Fix error handling in gc e2e test --- test/e2e/apimachinery/BUILD | 1 + test/e2e/apimachinery/garbage_collector.go | 72 +++++++++++----------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/test/e2e/apimachinery/BUILD b/test/e2e/apimachinery/BUILD index cbbf6662b21..31d1c8eb4c7 100644 --- a/test/e2e/apimachinery/BUILD +++ b/test/e2e/apimachinery/BUILD @@ -55,6 +55,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/test/e2e/apimachinery/garbage_collector.go b/test/e2e/apimachinery/garbage_collector.go index 70b06cfbea7..1fb028aa426 100644 --- a/test/e2e/apimachinery/garbage_collector.go +++ b/test/e2e/apimachinery/garbage_collector.go @@ -32,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/storage/names" clientset "k8s.io/client-go/kubernetes" @@ -175,7 +176,7 @@ func verifyRemainingDeploymentsReplicaSetsPods( } if len(deployments.Items) != deploymentNum { ret = false - By(fmt.Sprintf("expected %d Deploymentss, got %d Deployments", deploymentNum, len(deployments.Items))) + By(fmt.Sprintf("expected %d Deployments, got %d Deployments", deploymentNum, len(deployments.Items))) } pods, err := clientSet.CoreV1().Pods(f.Namespace.Name).List(metav1.ListOptions{}) if err != nil { @@ -445,17 +446,13 @@ var _ = SIGDescribe("Garbage collector", func() { framework.Failf("%v", err) } By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods") - if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) { - pods, err := podClient.List(metav1.ListOptions{}) - if err != nil { - return false, fmt.Errorf("Failed to list pods: %v", err) - } - if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a { - return false, fmt.Errorf("expect %d pods, got %d pods", e, a) - } - return false, nil - }); err != nil && err != wait.ErrWaitTimeout { - framework.Failf("%v", err) + time.Sleep(30 * time.Second) + pods, err := podClient.List(metav1.ListOptions{}) + if err != nil { + framework.Failf("Failed to list pods: %v", err) + } + if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a { + framework.Failf("expect %d pods, got %d pods", e, a) } gatherMetrics(f) }) @@ -494,17 +491,13 @@ var _ = SIGDescribe("Garbage collector", func() { framework.Failf("failed to delete the rc: %v", err) } By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods") - if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) { - pods, err := podClient.List(metav1.ListOptions{}) - if err != nil { - return false, fmt.Errorf("Failed to list pods: %v", err) - } - if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a { - return false, fmt.Errorf("expect %d pods, got %d pods", e, a) - } - return false, nil - }); err != nil && err != wait.ErrWaitTimeout { - framework.Failf("%v", err) + time.Sleep(30 * time.Second) + pods, err := podClient.List(metav1.ListOptions{}) + if err != nil { + framework.Failf("Failed to list pods: %v", err) + } + if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a { + framework.Failf("expect %d pods, got %d pods", e, a) } gatherMetrics(f) }) @@ -552,14 +545,17 @@ var _ = SIGDescribe("Garbage collector", func() { err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) { return verifyRemainingDeploymentsReplicaSetsPods(f, clientSet, deployment, 0, 0, 0) }) - if err == wait.ErrWaitTimeout { - err = fmt.Errorf("Failed to wait for all rs to be garbage collected: %v", err) + if err != nil { + errList := make([]error, 0) + errList = append(errList, err) remainingRSs, err := rsClient.List(metav1.ListOptions{}) if err != nil { - framework.Failf("failed to list RSs post mortem: %v", err) + errList = append(errList, fmt.Errorf("failed to list RSs post mortem: %v", err)) } else { - framework.Failf("remaining rs are: %#v", remainingRSs) + errList = append(errList, fmt.Errorf("remaining rs are: %#v", remainingRSs)) } + aggregatedError := utilerrors.NewAggregate(errList) + framework.Failf("Failed to wait for all rs to be garbage collected: %v", aggregatedError) } @@ -605,24 +601,28 @@ var _ = SIGDescribe("Garbage collector", func() { if err := deployClient.Delete(deployment.ObjectMeta.Name, deleteOptions); err != nil { framework.Failf("failed to delete the deployment: %v", err) } - By("wait for 2 Minute to see if the garbage collector mistakenly deletes the rs") - err = wait.PollImmediate(5*time.Second, 2*time.Minute, func() (bool, error) { - return verifyRemainingDeploymentsReplicaSetsPods(f, clientSet, deployment, 0, 1, 2) - }) + By("wait for 30 seconds to see if the garbage collector mistakenly deletes the rs") + time.Sleep(30 * time.Second) + ok, err := verifyRemainingDeploymentsReplicaSetsPods(f, clientSet, deployment, 0, 1, 2) if err != nil { - err = fmt.Errorf("Failed to wait to see if the garbage collecter mistakenly deletes the rs: %v", err) + framework.Failf("Unexpected error while verifying remaining deployments, rs, and pods: %v", err) + } + if !ok { + errList := make([]error, 0) remainingRSs, err := rsClient.List(metav1.ListOptions{}) if err != nil { - framework.Failf("failed to list RSs post mortem: %v", err) + errList = append(errList, fmt.Errorf("failed to list RSs post mortem: %v", err)) } else { - framework.Failf("remaining rs post mortem: %#v", remainingRSs) + errList = append(errList, fmt.Errorf("remaining rs post mortem: %#v", remainingRSs)) } remainingDSs, err := deployClient.List(metav1.ListOptions{}) if err != nil { - framework.Failf("failed to list Deployments post mortem: %v", err) + errList = append(errList, fmt.Errorf("failed to list Deployments post mortem: %v", err)) } else { - framework.Failf("remaining deployment's post mortem: %#v", remainingDSs) + errList = append(errList, fmt.Errorf("remaining deployment's post mortem: %#v", remainingDSs)) } + aggregatedError := utilerrors.NewAggregate(errList) + framework.Failf("Failed to verify remaining deployments, rs, and pods: %v", aggregatedError) } rs, err := clientSet.ExtensionsV1beta1().ReplicaSets(f.Namespace.Name).List(metav1.ListOptions{}) if err != nil { From 0f3e1dcfc250626841f82c81a324cbfcb24cd5e5 Mon Sep 17 00:00:00 2001 From: Tomoe Sugihara Date: Mon, 23 Apr 2018 15:38:20 +0900 Subject: [PATCH 006/416] Whitelist CronJob for kubectl apply --prune --- pkg/kubectl/cmd/apply.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index de24503d17a..a7048ad6768 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -536,6 +536,7 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n {"", "v1", "Secret", true}, {"", "v1", "Service", true}, {"batch", "v1", "Job", true}, + {"batch", "v1beta1", "CronJob", true}, {"extensions", "v1beta1", "DaemonSet", true}, {"extensions", "v1beta1", "Deployment", true}, {"extensions", "v1beta1", "Ingress", true}, From a7ed23be8e95a8db59e6298d63cdc8cdd9d481b2 Mon Sep 17 00:00:00 2001 From: Jan Klare Date: Fri, 20 Apr 2018 15:44:19 +0200 Subject: [PATCH 007/416] update fluentd-elasticsearch addon * elastic now provides a fully opensource version for their prebuild docker images (elasticsearch, kibana and so on). To avoid running into licensing conflicts for this addon example, we should rather use these images instead of the premium ones (were we also have to disable premium features manually right now) * remove disable flags for xpack, since *-oss images do not include this anymore * bump elasticsearch and kibana version from 5.6.4 to 6.2.4 * use oss version from elastic as baseimg for kibana and elasticsearch * bump fluentd version to ~>1.1.3 * bump gem 'fluent-plugin-elasticsearch' to '~>2.9.1' to allow usage of elasticsearch 6.x * bump fluentd-es-image to v2.1.0 * fix elasticserach run.sh to align with new elasticsearch upstream container structure --- .../addons/fluentd-elasticsearch/README.md | 19 ++++++++++--------- .../fluentd-elasticsearch/es-image/Dockerfile | 2 +- .../fluentd-elasticsearch/es-image/Makefile | 2 +- .../es-image/config/elasticsearch.yml | 3 --- .../fluentd-elasticsearch/es-image/run.sh | 2 +- .../fluentd-elasticsearch/es-statefulset.yaml | 8 ++++---- .../fluentd-elasticsearch/fluentd-es-ds.yaml | 10 +++++----- .../fluentd-es-image/Gemfile | 4 ++-- .../fluentd-es-image/Makefile | 2 +- .../kibana-deployment.yaml | 6 +----- 10 files changed, 26 insertions(+), 32 deletions(-) diff --git a/cluster/addons/fluentd-elasticsearch/README.md b/cluster/addons/fluentd-elasticsearch/README.md index d51b3b142d2..82f50df5911 100644 --- a/cluster/addons/fluentd-elasticsearch/README.md +++ b/cluster/addons/fluentd-elasticsearch/README.md @@ -19,15 +19,16 @@ a Deployment, but allows for maintaining state on storage volumes. ### Security -Elasticsearch has capabilities to enable authorization using the -[X-Pack plugin][xPack]. See configuration parameter `xpack.security.enabled` -in Elasticsearch and Kibana configurations. It can also be set via the -`XPACK_SECURITY_ENABLED` env variable. After enabling the feature, -follow [official documentation][setupCreds] to set up credentials in -Elasticsearch and Kibana. Don't forget to propagate those credentials also to -Fluentd in its [configuration][fluentdCreds], using for example -[environment variables][fluentdEnvVar]. You can utilize [ConfigMaps][configMap] -and [Secrets][secret] to store credentials in the Kubernetes apiserver. +Elasticsearch has capabilities to enable authorization using the [X-Pack +plugin][xPack]. For the sake of simplicity this example uses the fully open +source prebuild images from elastic that do not contain the X-Pack plugin. If +you need these features, please consider building the images from either the +"basic" or "platinum" version. After enabling these features, follow [official +documentation][setupCreds] to set up credentials in Elasticsearch and Kibana. +Don't forget to propagate those credentials also to Fluentd in its +[configuration][fluentdCreds], using for example [environment +variables][fluentdEnvVar]. You can utilize [ConfigMaps][configMap] and +[Secrets][secret] to store credentials in the Kubernetes apiserver. ### Initialization diff --git a/cluster/addons/fluentd-elasticsearch/es-image/Dockerfile b/cluster/addons/fluentd-elasticsearch/es-image/Dockerfile index 8e971d78349..de69bcf9f4d 100644 --- a/cluster/addons/fluentd-elasticsearch/es-image/Dockerfile +++ b/cluster/addons/fluentd-elasticsearch/es-image/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM docker.elastic.co/elasticsearch/elasticsearch:5.6.4 +FROM docker.elastic.co/elasticsearch/elasticsearch-oss:6.2.4 VOLUME ["/data"] EXPOSE 9200 9300 diff --git a/cluster/addons/fluentd-elasticsearch/es-image/Makefile b/cluster/addons/fluentd-elasticsearch/es-image/Makefile index be7ba47df18..3067c746121 100755 --- a/cluster/addons/fluentd-elasticsearch/es-image/Makefile +++ b/cluster/addons/fluentd-elasticsearch/es-image/Makefile @@ -16,7 +16,7 @@ PREFIX = staging-k8s.gcr.io IMAGE = elasticsearch -TAG = v5.6.4 +TAG = v6.2.4 build: docker build --pull -t $(PREFIX)/$(IMAGE):$(TAG) . diff --git a/cluster/addons/fluentd-elasticsearch/es-image/config/elasticsearch.yml b/cluster/addons/fluentd-elasticsearch/es-image/config/elasticsearch.yml index 23c59c53b79..f4ffee74a7f 100644 --- a/cluster/addons/fluentd-elasticsearch/es-image/config/elasticsearch.yml +++ b/cluster/addons/fluentd-elasticsearch/es-image/config/elasticsearch.yml @@ -12,6 +12,3 @@ path.data: /data network.host: 0.0.0.0 discovery.zen.minimum_master_nodes: ${MINIMUM_MASTER_NODES} - -xpack.security.enabled: false -xpack.monitoring.enabled: false diff --git a/cluster/addons/fluentd-elasticsearch/es-image/run.sh b/cluster/addons/fluentd-elasticsearch/es-image/run.sh index eb1f2f70f8f..e6d3d24c6c7 100755 --- a/cluster/addons/fluentd-elasticsearch/es-image/run.sh +++ b/cluster/addons/fluentd-elasticsearch/es-image/run.sh @@ -26,4 +26,4 @@ export MINIMUM_MASTER_NODES=${MINIMUM_MASTER_NODES:-2} chown -R elasticsearch:elasticsearch /data ./bin/elasticsearch_logging_discovery >> ./config/elasticsearch.yml -exec su elasticsearch -c ./bin/es-docker +exec su elasticsearch -c /usr/local/bin/docker-entrypoint.sh diff --git a/cluster/addons/fluentd-elasticsearch/es-statefulset.yaml b/cluster/addons/fluentd-elasticsearch/es-statefulset.yaml index 36adf388313..a6178c8a5e8 100644 --- a/cluster/addons/fluentd-elasticsearch/es-statefulset.yaml +++ b/cluster/addons/fluentd-elasticsearch/es-statefulset.yaml @@ -54,7 +54,7 @@ metadata: namespace: kube-system labels: k8s-app: elasticsearch-logging - version: v5.6.4 + version: v6.2.4 kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: @@ -63,17 +63,17 @@ spec: selector: matchLabels: k8s-app: elasticsearch-logging - version: v5.6.4 + version: v6.2.4 template: metadata: labels: k8s-app: elasticsearch-logging - version: v5.6.4 + version: v6.2.4 kubernetes.io/cluster-service: "true" spec: serviceAccountName: elasticsearch-logging containers: - - image: k8s.gcr.io/elasticsearch:v5.6.4 + - image: k8s.gcr.io/elasticsearch:v6.2.4 name: elasticsearch-logging resources: # need more cpu upon initialization, therefore burstable class diff --git a/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml b/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml index eecd6b4801b..c39cb5958d7 100644 --- a/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml +++ b/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml @@ -48,24 +48,24 @@ roleRef: apiVersion: apps/v1 kind: DaemonSet metadata: - name: fluentd-es-v2.0.4 + name: fluentd-es-v2.1.0 namespace: kube-system labels: k8s-app: fluentd-es - version: v2.0.4 + version: v2.1.0 kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: selector: matchLabels: k8s-app: fluentd-es - version: v2.0.4 + version: v2.1.0 template: metadata: labels: k8s-app: fluentd-es kubernetes.io/cluster-service: "true" - version: v2.0.4 + version: v2.1.0 # This annotation ensures that fluentd does not get evicted if the node # supports critical pod annotation based priority scheme. # Note that this does not guarantee admission on the nodes (#40573). @@ -76,7 +76,7 @@ spec: serviceAccountName: fluentd-es containers: - name: fluentd-es - image: k8s.gcr.io/fluentd-elasticsearch:v2.0.4 + image: k8s.gcr.io/fluentd-elasticsearch:v2.1.0 env: - name: FLUENTD_ARGS value: --no-supervisor -q diff --git a/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Gemfile b/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Gemfile index 93155aa4fc6..f97ad5fadb4 100644 --- a/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Gemfile +++ b/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Gemfile @@ -1,9 +1,9 @@ source 'https://rubygems.org' -gem 'fluentd', '<=1.1.0' +gem 'fluentd', '~>1.1.3' gem 'activesupport', '~>5.1.4' gem 'fluent-plugin-kubernetes_metadata_filter', '~>1.0.0' -gem 'fluent-plugin-elasticsearch', '~>2.4.1' +gem 'fluent-plugin-elasticsearch', '~>2.9.1' gem 'fluent-plugin-systemd', '~>0.3.1' gem 'fluent-plugin-detect-exceptions', '~>0.0.9' gem 'fluent-plugin-prometheus', '~>0.3.0' diff --git a/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Makefile b/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Makefile index 59b9f155c2b..bb0680cc4f1 100644 --- a/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Makefile +++ b/cluster/addons/fluentd-elasticsearch/fluentd-es-image/Makefile @@ -16,7 +16,7 @@ PREFIX = staging-k8s.gcr.io IMAGE = fluentd-elasticsearch -TAG = v2.0.4 +TAG = v2.1.0 build: docker build --pull -t $(PREFIX)/$(IMAGE):$(TAG) . diff --git a/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml b/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml index 8cd00fe52e5..c775cd9c68e 100644 --- a/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml +++ b/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: kibana-logging - image: docker.elastic.co/kibana/kibana:5.6.4 + image: docker.elastic.co/kibana/kibana-oss:6.2.4 resources: # need more cpu upon initialization, therefore burstable class limits: @@ -31,10 +31,6 @@ spec: value: http://elasticsearch-logging:9200 - name: SERVER_BASEPATH value: /api/v1/namespaces/kube-system/services/kibana-logging/proxy - - name: XPACK_MONITORING_ENABLED - value: "false" - - name: XPACK_SECURITY_ENABLED - value: "false" ports: - containerPort: 5601 name: ui From 87bd6b5335b9f624f75b7805bed70521b5a50826 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Sat, 28 Apr 2018 02:12:09 +0000 Subject: [PATCH 008/416] remove format operation in WaitForAttach --- pkg/volume/azure_dd/attacher.go | 4 -- pkg/volume/azure_dd/azure_common_linux.go | 42 ------------------- .../azure_dd/azure_common_unsupported.go | 3 -- 3 files changed, 49 deletions(-) diff --git a/pkg/volume/azure_dd/attacher.go b/pkg/volume/azure_dd/attacher.go index de6c5368f28..c43239851e1 100644 --- a/pkg/volume/azure_dd/attacher.go +++ b/pkg/volume/azure_dd/attacher.go @@ -186,10 +186,6 @@ func (a *azureDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath string, // did we find it? if newDevicePath != "" { - // the current sequence k8s uses for unformated disk (check-disk, mount, fail, mkfs.extX) hangs on - // Azure Managed disk scsi interface. this is a hack and will be replaced once we identify and solve - // the root case on Azure. - formatIfNotFormatted(newDevicePath, *volumeSource.FSType, exec) return true, nil } diff --git a/pkg/volume/azure_dd/azure_common_linux.go b/pkg/volume/azure_dd/azure_common_linux.go index 3c19b9244b5..5176e5721e1 100644 --- a/pkg/volume/azure_dd/azure_common_linux.go +++ b/pkg/volume/azure_dd/azure_common_linux.go @@ -178,45 +178,3 @@ func findDiskByLunWithConstraint(lun int, io ioHandler, azureDisks []string) (st } return "", err } - -func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) { - notFormatted, err := diskLooksUnformatted(disk, exec) - if err == nil && notFormatted { - args := []string{disk} - // Disk is unformatted so format it. - // Use 'ext4' as the default - if len(fstype) == 0 { - fstype = "ext4" - } - if fstype == "ext4" || fstype == "ext3" { - args = []string{"-E", "lazy_itable_init=0,lazy_journal_init=0", "-F", disk} - } - glog.Infof("azureDisk - Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", disk, fstype, args) - - _, err := exec.Run("mkfs."+fstype, args...) - if err == nil { - // the disk has been formatted successfully try to mount it again. - glog.Infof("azureDisk - Disk successfully formatted with 'mkfs.%s %v'", fstype, args) - } else { - glog.Warningf("azureDisk - Error formatting volume with 'mkfs.%s %v': %v", fstype, args, err) - } - } else { - if err != nil { - glog.Warningf("azureDisk - Failed to check if the disk %s formatted with error %s, will attach anyway", disk, err) - } else { - glog.Infof("azureDisk - Disk %s already formatted, will not format", disk) - } - } -} - -func diskLooksUnformatted(disk string, exec mount.Exec) (bool, error) { - args := []string{"-nd", "-o", "FSTYPE", disk} - glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args) - dataOut, err := exec.Run("lsblk", args...) - if err != nil { - glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err) - return false, err - } - output := libstrings.TrimSpace(string(dataOut)) - return output == "", nil -} diff --git a/pkg/volume/azure_dd/azure_common_unsupported.go b/pkg/volume/azure_dd/azure_common_unsupported.go index 6710af8fcb0..ba938ccc28a 100644 --- a/pkg/volume/azure_dd/azure_common_unsupported.go +++ b/pkg/volume/azure_dd/azure_common_unsupported.go @@ -26,6 +26,3 @@ func scsiHostRescan(io ioHandler, exec mount.Exec) { func findDiskByLun(lun int, io ioHandler, exec mount.Exec) (string, error) { return "", nil } - -func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) { -} From b4009c5444b0996f7f7ba2a3e8a5025efd6143e2 Mon Sep 17 00:00:00 2001 From: Cheng Xing Date: Mon, 30 Apr 2018 13:31:54 -0700 Subject: [PATCH 009/416] GCE PD plugin now prevents attaching a regional PD PV with pdName of a regular PD --- pkg/cloudprovider/providers/gce/gce_disks.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pkg/cloudprovider/providers/gce/gce_disks.go b/pkg/cloudprovider/providers/gce/gce_disks.go index 18b1c080932..c2320b76253 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks.go +++ b/pkg/cloudprovider/providers/gce/gce_disks.go @@ -535,14 +535,10 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn if regional && utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { disk, err = gce.getRegionalDiskByName(diskName) if err != nil { - glog.V(5).Infof("Could not find regional PD named %q to Attach. Will look for a zonal PD", diskName) - err = nil - } else { - mc = newDiskMetricContextRegional("attach", gce.region) + return err } - } - - if disk == nil { + mc = newDiskMetricContextRegional("attach", gce.region) + } else { disk, err = gce.getDiskByName(diskName, instance.Zone) if err != nil { return err From ae23e19f2048c6af01fd375f6895622beb0e4540 Mon Sep 17 00:00:00 2001 From: Nitesh Konkar Date: Tue, 8 May 2018 21:00:34 +0530 Subject: [PATCH 010/416] Push fat manifest for multi-arch images Signed-off-by: Nitesh Konkar --- test/images/Makefile | 4 +++- test/images/image-util.sh | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/images/Makefile b/test/images/Makefile index 6b7e7439b3a..eab02cc3a99 100644 --- a/test/images/Makefile +++ b/test/images/Makefile @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +include ../../hack/make-rules/Makefile.manifest + REGISTRY ?= gcr.io/kubernetes-e2e-test-images GOARM=7 QEMUVERSION=v2.9.1 @@ -34,7 +36,7 @@ all: all-container all-container: ./image-util.sh build $(WHAT) -all-push: all-container +all-push: all-container manifest-tool ./image-util.sh push $(WHAT) .PHONY: all all-push all-container diff --git a/test/images/image-util.sh b/test/images/image-util.sh index 0c092a4a99a..c933d2c8f99 100755 --- a/test/images/image-util.sh +++ b/test/images/image-util.sh @@ -99,6 +99,10 @@ push() { TAG=$(<${IMAGE}/VERSION) docker push ${REGISTRY}/${IMAGE}-${arch}:${TAG} done + + # Make archs list into OS/architecture pair. Eg: 'amd64 ppc64le' to 'linux/amd64,linux/ppc64le' + archs=$(echo $archs | sed -e 's/[^ ]* */linux\/&/g' -e 's/ /,/g') + manifest-tool push from-args --platforms ${archs} --template ${REGISTRY}/${IMAGE}-ARCH:${TAG} --target ${REGISTRY}/${IMAGE}:${TAG} } # This function is for building the go code From 9fa269a5e5e8ce6e8359f70bb1020d18eba1cd8f Mon Sep 17 00:00:00 2001 From: Borja Aranda Date: Thu, 8 Feb 2018 16:46:11 +0100 Subject: [PATCH 011/416] Fix kubectl auth can-i exit errcode --- hack/make-rules/test-cmd-util.sh | 6 ++++++ pkg/kubectl/cmd/auth/cani.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 5c60b50e931..6149a9d7b72 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -5219,6 +5219,12 @@ runTests() { output_message=$(kubectl auth can-i list jobs.batch/bar -n foo --quiet 2>&1 "${kube_flags[@]}") kube::test::if_empty_string "${output_message}" + + output_message=$(kubectl auth can-i get pods --subresource=log 2>&1 "${kube_flags[@]}"; echo $?) + kube::test::if_has_string "${output_message}" '0' + + output_message=$(kubectl auth can-i get pods --subresource=log --quiet 2>&1 "${kube_flags[@]}"; echo $?) + kube::test::if_has_string "${output_message}" '0' fi # kubectl auth reconcile diff --git a/pkg/kubectl/cmd/auth/cani.go b/pkg/kubectl/cmd/auth/cani.go index 88a93d8d229..a0a538f59ad 100644 --- a/pkg/kubectl/cmd/auth/cani.go +++ b/pkg/kubectl/cmd/auth/cani.go @@ -97,7 +97,7 @@ func NewCmdCanI(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C allowed, err := o.RunAccessCheck() if err == nil { - if o.Quiet && !allowed { + if !allowed { os.Exit(1) } } From 54d9c4ea25ecb0ffb829145238680486c7e9ee92 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 9 May 2018 11:00:41 +0200 Subject: [PATCH 012/416] e2e/storage: parameterize container images The CSI integration test for hostpath was hard-coded to use the latest stable release of the sidecar and hostpath container images. This makes sense for regression testing of changes made in Kubernetes itself, but the same test is also useful for testing the "canary" images on quay.io before tagging them as a new release or for testing locally produced images. Both is now possible via command line parameters. Testing "canary" images on quay.io: go run hack/e2e.go -- --provider=local --test \ --test_args="--ginkgo.focus=CSI.plugin.test.using.CSI.driver..hostPath -csiImageVersion=canary" Testing local container images: # https://docs.docker.com/registry/deploying/ docker run -d -p 5000:5000 --restart=always --name registry registry:2 for i in driver-registrar drivers external-attacher external-provisioner; do make -C $i REGISTRY_NAME=localhost:5000 push done go run hack/e2e.go -- --provider=local --test \ --test_args="--ginkgo.focus=CSI.plugin.test.using.CSI.driver..hostPath -csiImageVersion=canary -csiImageRegistry=localhost:5000" --- test/e2e/storage/csi_objects.go | 39 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/test/e2e/storage/csi_objects.go b/test/e2e/storage/csi_objects.go index d888abdbb5c..2836f11d1c6 100644 --- a/test/e2e/storage/csi_objects.go +++ b/test/e2e/storage/csi_objects.go @@ -20,6 +20,7 @@ limitations under the License. package storage import ( + "flag" "fmt" "time" @@ -37,12 +38,30 @@ import ( . "github.com/onsi/ginkgo" ) -const ( - csiHostPathPluginImage string = "quay.io/k8scsi/hostpathplugin:v0.2.0" - csiExternalAttacherImage string = "quay.io/k8scsi/csi-attacher:v0.2.0" - csiExternalProvisionerImage string = "quay.io/k8scsi/csi-provisioner:v0.2.1" - csiDriverRegistrarImage string = "quay.io/k8scsi/driver-registrar:v0.2.0" -) +var csiImageVersions = map[string]string{ + "hostpathplugin": "v0.2.0", + "csi-attacher": "v0.2.0", + "csi-provisioner": "v0.2.1", + "driver-registrar": "v0.2.0", +} +var csiImageVersion string +var csiImageRegistry string + +func init() { + flag.StringVar(&csiImageVersion, "csiImageVersion", "", "overrides the default tag used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") + flag.StringVar(&csiImageRegistry, "csiImageRegistry", "quay.io/k8scsi", "overrides the default repository used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") +} + +func csiContainerImage(image string) string { + var fullName string + fullName += csiImageRegistry + "/" + image + ":" + if csiImageVersion != "" { + fullName += csiImageVersion + } else { + fullName += csiImageVersions[image] + } + return fullName +} // Create the driver registrar cluster role if it doesn't exist, no teardown so that tests // are parallelizable. This role will be shared with many of the CSI tests. @@ -207,7 +226,7 @@ func csiHostPathPod( Containers: []v1.Container{ { Name: "external-provisioner", - Image: csiExternalProvisionerImage, + Image: csiContainerImage("csi-provisioner"), ImagePullPolicy: v1.PullAlways, Args: []string{ "--v=5", @@ -223,7 +242,7 @@ func csiHostPathPod( }, { Name: "driver-registrar", - Image: csiDriverRegistrarImage, + Image: csiContainerImage("driver-registrar"), ImagePullPolicy: v1.PullAlways, Args: []string{ "--v=5", @@ -248,7 +267,7 @@ func csiHostPathPod( }, { Name: "external-attacher", - Image: csiExternalAttacherImage, + Image: csiContainerImage("csi-attacher"), ImagePullPolicy: v1.PullAlways, Args: []string{ "--v=5", @@ -269,7 +288,7 @@ func csiHostPathPod( }, { Name: "hostpath-driver", - Image: csiHostPathPluginImage, + Image: csiContainerImage("hostpathplugin"), ImagePullPolicy: v1.PullAlways, SecurityContext: &v1.SecurityContext{ Privileged: &priv, From f3441a5c202d05c50547a78119927ecafcc8e765 Mon Sep 17 00:00:00 2001 From: ravisantoshgudimetla Date: Fri, 11 May 2018 12:33:08 -0400 Subject: [PATCH 013/416] Update e2e to use priorityClass beta --- pkg/kubectl/cmd/create/create_priorityclass_test.go | 2 +- test/e2e/autoscaling/cluster_size_autoscaling.go | 6 +++--- test/e2e/scheduling/preemption.go | 9 ++++----- test/integration/apiserver/print_test.go | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/kubectl/cmd/create/create_priorityclass_test.go b/pkg/kubectl/cmd/create/create_priorityclass_test.go index 44d30218278..31a78c6c8ce 100644 --- a/pkg/kubectl/cmd/create/create_priorityclass_test.go +++ b/pkg/kubectl/cmd/create/create_priorityclass_test.go @@ -38,7 +38,7 @@ func TestCreatePriorityClass(t *testing.T) { ns := legacyscheme.Codecs tf.Client = &fake.RESTClient{ - GroupVersion: schema.GroupVersion{Group: "scheduling.k8s.io", Version: "v1alpha1"}, + GroupVersion: schema.GroupVersion{Group: "scheduling.k8s.io", Version: "v1beta1"}, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ diff --git a/test/e2e/autoscaling/cluster_size_autoscaling.go b/test/e2e/autoscaling/cluster_size_autoscaling.go index 0b0b248a86b..11502049176 100644 --- a/test/e2e/autoscaling/cluster_size_autoscaling.go +++ b/test/e2e/autoscaling/cluster_size_autoscaling.go @@ -30,7 +30,7 @@ import ( "k8s.io/api/core/v1" policy "k8s.io/api/policy/v1beta1" - "k8s.io/api/scheduling/v1alpha1" + schedulerapi "k8s.io/api/scheduling/v1beta1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -2013,13 +2013,13 @@ func createPriorityClasses(f *framework.Framework) func() { highPriorityClassName: 1000, } for className, priority := range priorityClasses { - _, err := f.ClientSet.SchedulingV1alpha1().PriorityClasses().Create(&v1alpha1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: className}, Value: priority}) + _, err := f.ClientSet.SchedulingV1beta1().PriorityClasses().Create(&schedulerapi.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: className}, Value: priority}) Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) } return func() { for className := range priorityClasses { - f.ClientSet.SchedulingV1alpha1().PriorityClasses().Delete(className, nil) + f.ClientSet.SchedulingV1beta1().PriorityClasses().Delete(className, nil) } } } diff --git a/test/e2e/scheduling/preemption.go b/test/e2e/scheduling/preemption.go index b67c6bf8130..d545493bc59 100644 --- a/test/e2e/scheduling/preemption.go +++ b/test/e2e/scheduling/preemption.go @@ -21,7 +21,7 @@ import ( "time" "k8s.io/api/core/v1" - "k8s.io/api/scheduling/v1alpha1" + schedulerapi "k8s.io/api/scheduling/v1beta1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -51,12 +51,11 @@ var _ = SIGDescribe("SchedulerPreemption [Serial] [Feature:PodPreemption]", func cs = f.ClientSet ns = f.Namespace.Name nodeList = &v1.NodeList{} - - _, err := f.ClientSet.SchedulingV1alpha1().PriorityClasses().Create(&v1alpha1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: highPriorityClassName}, Value: highPriority}) + _, err := f.ClientSet.SchedulingV1beta1().PriorityClasses().Create(&schedulerapi.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: highPriorityClassName}, Value: highPriority}) Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) - _, err = f.ClientSet.SchedulingV1alpha1().PriorityClasses().Create(&v1alpha1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: mediumPriorityClassName}, Value: mediumPriority}) + _, err = f.ClientSet.SchedulingV1beta1().PriorityClasses().Create(&schedulerapi.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: mediumPriorityClassName}, Value: mediumPriority}) Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) - _, err = f.ClientSet.SchedulingV1alpha1().PriorityClasses().Create(&v1alpha1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: lowPriorityClassName}, Value: lowPriority}) + _, err = f.ClientSet.SchedulingV1beta1().PriorityClasses().Create(&schedulerapi.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: lowPriorityClassName}, Value: lowPriority}) Expect(err == nil || errors.IsAlreadyExists(err)).To(Equal(true)) framework.WaitForAllNodesHealthy(cs, time.Minute) diff --git a/test/integration/apiserver/print_test.go b/test/integration/apiserver/print_test.go index 30e7b73e08c..ddcda2b80f9 100644 --- a/test/integration/apiserver/print_test.go +++ b/test/integration/apiserver/print_test.go @@ -28,7 +28,7 @@ import ( batchv2alpha1 "k8s.io/api/batch/v2alpha1" rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" - schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1" + schedulerapi "k8s.io/api/scheduling/v1beta1" settingsv1alpha1 "k8s.io/api/settings/v1alpha1" storagev1alpha1 "k8s.io/api/storage/v1alpha1" "k8s.io/apimachinery/pkg/api/meta" @@ -140,7 +140,7 @@ func TestServerSidePrint(t *testing.T) { batchv2alpha1.SchemeGroupVersion, rbacv1alpha1.SchemeGroupVersion, settingsv1alpha1.SchemeGroupVersion, - schedulingv1alpha1.SchemeGroupVersion, + schedulerapi.SchemeGroupVersion, storagev1alpha1.SchemeGroupVersion) defer closeFn() From a975e7834da33463e8514f52e14f61e8476bba0e Mon Sep 17 00:00:00 2001 From: ravisantoshgudimetla Date: Mon, 14 May 2018 18:15:18 -0400 Subject: [PATCH 014/416] Switch to beta --- pkg/features/kube_features.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 3e4c6f269fd..c4e9432f024 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -302,7 +302,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS HugePages: {Default: true, PreRelease: utilfeature.Beta}, DebugContainers: {Default: false, PreRelease: utilfeature.Alpha}, PodShareProcessNamespace: {Default: false, PreRelease: utilfeature.Alpha}, - PodPriority: {Default: false, PreRelease: utilfeature.Alpha}, + PodPriority: {Default: true, PreRelease: utilfeature.Beta}, EnableEquivalenceClassCache: {Default: false, PreRelease: utilfeature.Alpha}, TaintNodesByCondition: {Default: false, PreRelease: utilfeature.Alpha}, MountPropagation: {Default: true, PreRelease: utilfeature.Beta}, From 441b6d4465b478bae34ef1015087f6453bc497c3 Mon Sep 17 00:00:00 2001 From: ravisantoshgudimetla Date: Fri, 11 May 2018 12:33:48 -0400 Subject: [PATCH 015/416] Build files generated --- test/e2e/autoscaling/BUILD | 2 +- test/e2e/scheduling/BUILD | 2 +- test/integration/apiserver/BUILD | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/autoscaling/BUILD b/test/e2e/autoscaling/BUILD index b7070d24687..217f5fd9c1e 100644 --- a/test/e2e/autoscaling/BUILD +++ b/test/e2e/autoscaling/BUILD @@ -34,7 +34,7 @@ go_library( "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/api/policy/v1beta1:go_default_library", - "//vendor/k8s.io/api/scheduling/v1alpha1:go_default_library", + "//vendor/k8s.io/api/scheduling/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/test/e2e/scheduling/BUILD b/test/e2e/scheduling/BUILD index 1e1cda3a2c4..f149eec6629 100644 --- a/test/e2e/scheduling/BUILD +++ b/test/e2e/scheduling/BUILD @@ -37,7 +37,7 @@ go_library( "//vendor/google.golang.org/api/compute/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", - "//vendor/k8s.io/api/scheduling/v1alpha1:go_default_library", + "//vendor/k8s.io/api/scheduling/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/test/integration/apiserver/BUILD b/test/integration/apiserver/BUILD index 238349f9498..1cf7c1709e0 100644 --- a/test/integration/apiserver/BUILD +++ b/test/integration/apiserver/BUILD @@ -33,7 +33,7 @@ go_test( "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/api/rbac/v1alpha1:go_default_library", - "//vendor/k8s.io/api/scheduling/v1alpha1:go_default_library", + "//vendor/k8s.io/api/scheduling/v1beta1:go_default_library", "//vendor/k8s.io/api/settings/v1alpha1:go_default_library", "//vendor/k8s.io/api/storage/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", From 582128a8374b2a7d40db8268568c01a73e5f3cc6 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Tue, 15 May 2018 22:15:23 +0200 Subject: [PATCH 016/416] Remove unnecessary roundtripping in get.go --- pkg/kubectl/cmd/get/get.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/pkg/kubectl/cmd/get/get.go b/pkg/kubectl/cmd/get/get.go index 8bed7793e67..5c4372893c1 100644 --- a/pkg/kubectl/cmd/get/get.go +++ b/pkg/kubectl/cmd/get/get.go @@ -343,7 +343,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e objs := make([]runtime.Object, len(infos)) for ix := range infos { if o.ServerPrint { - table, err := o.decodeIntoTable(cmdutil.InternalVersionJSONEncoder(), infos[ix].Object) + table, err := o.decodeIntoTable(infos[ix].Object) if err == nil { infos[ix].Object = table } else { @@ -589,34 +589,28 @@ func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConv return internalObject } -func (o *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.Object) (runtime.Object, error) { +func (o *GetOptions) decodeIntoTable(obj runtime.Object) (runtime.Object, error) { if obj.GetObjectKind().GroupVersionKind().Kind != "Table" { return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table") } - b, err := runtime.Encode(encoder, obj) - if err != nil { - return nil, err + unstr, ok := obj.(*unstructured.Unstructured) + if !ok { + return nil, fmt.Errorf("attempt to decode non-Unstructured object") } - table := &metav1beta1.Table{} - err = json.Unmarshal(b, table) - if err != nil { + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil { return nil, err } for i := range table.Rows { row := &table.Rows[i] if row.Object.Raw == nil || row.Object.Object != nil { - //if row already has Object.Object - //we don't change it continue } - converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw) if err != nil { - //if error happens, we just continue - continue + return nil, err } row.Object.Object = converted } From 228ba8e9090bf0cab1a4b82b65d8fcb6e1ad24fe Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Wed, 16 May 2018 12:06:01 +0800 Subject: [PATCH 017/416] Setup dns servers and search domains for Windows Pods --- pkg/kubelet/dockershim/BUILD | 11 +++++ pkg/kubelet/dockershim/docker_sandbox.go | 19 +-------- .../dockershim/docker_sandbox_others.go | 42 +++++++++++++++++++ .../dockershim/docker_sandbox_windows.go | 39 +++++++++++++++++ 4 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 pkg/kubelet/dockershim/docker_sandbox_others.go create mode 100644 pkg/kubelet/dockershim/docker_sandbox_windows.go diff --git a/pkg/kubelet/dockershim/BUILD b/pkg/kubelet/dockershim/BUILD index 6f90f2d0601..b3e9f3b382a 100644 --- a/pkg/kubelet/dockershim/BUILD +++ b/pkg/kubelet/dockershim/BUILD @@ -21,56 +21,67 @@ go_library( ] + select({ "@io_bazel_rules_go//go/platform:android": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:darwin": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:dragonfly": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:freebsd": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:linux": [ "docker_image_linux.go", + "docker_sandbox_others.go", "docker_stats_linux.go", "helpers_linux.go", ], "@io_bazel_rules_go//go/platform:nacl": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:netbsd": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:openbsd": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:plan9": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:solaris": [ "docker_image_unsupported.go", + "docker_sandbox_others.go", "docker_stats_unsupported.go", "helpers_unsupported.go", ], "@io_bazel_rules_go//go/platform:windows": [ "docker_image_windows.go", + "docker_sandbox_windows.go", "docker_stats_windows.go", "helpers_windows.go", ], diff --git a/pkg/kubelet/dockershim/docker_sandbox.go b/pkg/kubelet/dockershim/docker_sandbox.go index 13e9c42366e..c0a9ecfa9e2 100644 --- a/pkg/kubelet/dockershim/docker_sandbox.go +++ b/pkg/kubelet/dockershim/docker_sandbox.go @@ -538,21 +538,6 @@ func (ds *dockerService) ListPodSandbox(_ context.Context, r *runtimeapi.ListPod return &runtimeapi.ListPodSandboxResponse{Items: result}, nil } -// applySandboxLinuxOptions applies LinuxPodSandboxConfig to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig. -func (ds *dockerService) applySandboxLinuxOptions(hc *dockercontainer.HostConfig, lc *runtimeapi.LinuxPodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error { - if lc == nil { - return nil - } - // Apply security context. - if err := applySandboxSecurityContext(lc, createConfig.Config, hc, ds.network, separator); err != nil { - return err - } - - // Set sysctls. - hc.Sysctls = lc.Sysctls - return nil -} - func (ds *dockerService) applySandboxResources(hc *dockercontainer.HostConfig, lc *runtimeapi.LinuxPodSandboxConfig) error { hc.Resources = dockercontainer.Resources{ MemorySwap: DefaultMemorySwap(), @@ -593,8 +578,8 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeapi.PodSandboxConfig, HostConfig: hc, } - // Apply linux-specific options. - if err := ds.applySandboxLinuxOptions(hc, c.GetLinux(), createConfig, image, securityOptSeparator); err != nil { + // Apply platform-specific options. + if err := ds.applySandboxPlatformOptions(hc, c, createConfig, image, securityOptSeparator); err != nil { return nil, err } diff --git a/pkg/kubelet/dockershim/docker_sandbox_others.go b/pkg/kubelet/dockershim/docker_sandbox_others.go new file mode 100644 index 00000000000..108f31ce506 --- /dev/null +++ b/pkg/kubelet/dockershim/docker_sandbox_others.go @@ -0,0 +1,42 @@ +// +build !windows + +/* +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 dockershim + +import ( + dockertypes "github.com/docker/docker/api/types" + dockercontainer "github.com/docker/docker/api/types/container" + runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" +) + +// applySandboxPlatformOptions applies platform specific options to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig. +func (ds *dockerService) applySandboxPlatformOptions(hc *dockercontainer.HostConfig, config *runtimeapi.PodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error { + lc := config.GetLinux() + if lc == nil { + return nil + } + + // Apply security context. + if err := applySandboxSecurityContext(lc, createConfig.Config, hc, ds.network, separator); err != nil { + return err + } + + // Set sysctls. + hc.Sysctls = lc.Sysctls + return nil +} diff --git a/pkg/kubelet/dockershim/docker_sandbox_windows.go b/pkg/kubelet/dockershim/docker_sandbox_windows.go new file mode 100644 index 00000000000..d899e126bd3 --- /dev/null +++ b/pkg/kubelet/dockershim/docker_sandbox_windows.go @@ -0,0 +1,39 @@ +// +build windows + +/* +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 dockershim + +import ( + dockertypes "github.com/docker/docker/api/types" + dockercontainer "github.com/docker/docker/api/types/container" + runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" +) + +// applySandboxPlatformOptions applies platform specific options to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig. +func (ds *dockerService) applySandboxPlatformOptions(hc *dockercontainer.HostConfig, config *runtimeapi.PodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error { + dnsConfig := config.GetDnsConfig() + if dnsConfig == nil { + return nil + } + + // Setup DNS. + hc.DNS = dnsConfig.GetServers() + hc.DNSSearch = dnsConfig.GetSearches() + hc.DNSOptions = dnsConfig.GetOptions() + return nil +} From 9a7ff8dbe5f4f962b5edf2bab168ada635c66083 Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Mon, 9 Apr 2018 13:16:33 +0800 Subject: [PATCH 018/416] Add log stats for Windows containers --- pkg/volume/util/fs/BUILD | 3 +- pkg/volume/util/fs/fs_unsupported.go | 2 +- pkg/volume/util/fs/fs_windows.go | 76 ++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 pkg/volume/util/fs/fs_windows.go diff --git a/pkg/volume/util/fs/BUILD b/pkg/volume/util/fs/BUILD index 205ddaaaeb0..0e9be3016b6 100644 --- a/pkg/volume/util/fs/BUILD +++ b/pkg/volume/util/fs/BUILD @@ -34,7 +34,7 @@ go_library( "fs_unsupported.go", ], "@io_bazel_rules_go//go/platform:windows": [ - "fs_unsupported.go", + "fs_windows.go", ], "//conditions:default": [], }), @@ -74,6 +74,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", ], "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/golang.org/x/sys/windows:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", ], "//conditions:default": [], diff --git a/pkg/volume/util/fs/fs_unsupported.go b/pkg/volume/util/fs/fs_unsupported.go index da41fc8eee5..b48e4fd20cb 100644 --- a/pkg/volume/util/fs/fs_unsupported.go +++ b/pkg/volume/util/fs/fs_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin +// +build !linux,!darwin,!windows /* Copyright 2014 The Kubernetes Authors. diff --git a/pkg/volume/util/fs/fs_windows.go b/pkg/volume/util/fs/fs_windows.go new file mode 100644 index 00000000000..534604d2b2e --- /dev/null +++ b/pkg/volume/util/fs/fs_windows.go @@ -0,0 +1,76 @@ +// +build windows + +/* +Copyright 2014 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 fs + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" + + "k8s.io/apimachinery/pkg/api/resource" +) + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + procGetDiskFreeSpaceEx = modkernel32.NewProc("GetDiskFreeSpaceExW") +) + +// FSInfo returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error) +// for the filesystem that path resides upon. +func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) { + var freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes int64 + var err error + + ret, _, err := syscall.Syscall6( + procGetDiskFreeSpaceEx.Addr(), + 4, + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), + uintptr(unsafe.Pointer(&freeBytesAvailable)), + uintptr(unsafe.Pointer(&totalNumberOfBytes)), + uintptr(unsafe.Pointer(&totalNumberOfFreeBytes)), + 0, + 0, + ) + if ret == 0 { + return 0, 0, 0, 0, 0, 0, err + } + + return freeBytesAvailable, totalNumberOfBytes, totalNumberOfBytes - freeBytesAvailable, 0, 0, 0, nil +} + +func Du(path string) (*resource.Quantity, error) { + _, _, usage, _, _, _, err := FsInfo(path) + if err != nil { + return nil, err + } + + used, err := resource.ParseQuantity(fmt.Sprintf("%d", usage)) + if err != nil { + return nil, fmt.Errorf("failed to parse fs usage %d due to %v", usage, err) + } + used.Format = resource.BinarySI + return &used, nil +} + +// Always return zero since inodes is not supported on Windows. +func Find(path string) (int64, error) { + return 0, nil +} From 196381c0391cf9a7b356d4a9197b8d8affa08ffe Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Thu, 17 May 2018 14:21:49 +0800 Subject: [PATCH 019/416] Add fs status for Windows containers --- pkg/kubelet/cadvisor/cadvisor_windows.go | 8 ++++++-- pkg/kubelet/dockershim/docker_stats_windows.go | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/kubelet/cadvisor/cadvisor_windows.go b/pkg/kubelet/cadvisor/cadvisor_windows.go index 597721cd176..eb416a141d9 100644 --- a/pkg/kubelet/cadvisor/cadvisor_windows.go +++ b/pkg/kubelet/cadvisor/cadvisor_windows.go @@ -26,6 +26,7 @@ import ( ) type cadvisorClient struct { + rootPath string winStatsClient winstats.Client } @@ -34,7 +35,10 @@ var _ Interface = new(cadvisorClient) // New creates a cAdvisor and exports its API on the specified port if port > 0. func New(address string, port uint, imageFsInfoProvider ImageFsInfoProvider, rootPath string, usingLegacyStats bool) (Interface, error) { client, err := winstats.NewPerfCounterClient() - return &cadvisorClient{winStatsClient: client}, err + return &cadvisorClient{ + rootPath: rootPath, + winStatsClient: client, + }, err } func (cu *cadvisorClient) Start() error { @@ -70,7 +74,7 @@ func (cu *cadvisorClient) ImagesFsInfo() (cadvisorapiv2.FsInfo, error) { } func (cu *cadvisorClient) RootFsInfo() (cadvisorapiv2.FsInfo, error) { - return cadvisorapiv2.FsInfo{}, nil + return cu.GetDirFsInfo(cu.rootPath) } func (cu *cadvisorClient) WatchEvents(request *events.Request) (*events.EventChannel, error) { diff --git a/pkg/kubelet/dockershim/docker_stats_windows.go b/pkg/kubelet/dockershim/docker_stats_windows.go index a67a0511a04..a690f883b68 100644 --- a/pkg/kubelet/dockershim/docker_stats_windows.go +++ b/pkg/kubelet/dockershim/docker_stats_windows.go @@ -64,6 +64,11 @@ func (ds *dockerService) ListContainerStats(ctx context.Context, r *runtimeapi.L } func (ds *dockerService) getContainerStats(containerID string) (*runtimeapi.ContainerStats, error) { + info, err := ds.client.Info() + if err != nil { + return nil, err + } + statsJSON, err := ds.client.GetContainerStats(containerID) if err != nil { return nil, err @@ -101,6 +106,7 @@ func (ds *dockerService) getContainerStats(containerID string) (*runtimeapi.Cont }, WritableLayer: &runtimeapi.FilesystemUsage{ Timestamp: timestamp, + FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: info.DockerRootDir}, UsedBytes: &runtimeapi.UInt64Value{Value: uint64(*containerJSON.SizeRw)}, }, } From ff8b70c4094f8d2cc818ce7e88eb7735bd43ae61 Mon Sep 17 00:00:00 2001 From: Weibin Lin Date: Thu, 17 May 2018 14:33:47 +0800 Subject: [PATCH 020/416] Init ipvsInterface only when ipvs modules are present --- cmd/kube-proxy/app/server_others.go | 4 +++- pkg/proxy/ipvs/proxier.go | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/kube-proxy/app/server_others.go b/cmd/kube-proxy/app/server_others.go index 1ab70b1ab52..38806696d05 100644 --- a/cmd/kube-proxy/app/server_others.go +++ b/cmd/kube-proxy/app/server_others.go @@ -92,9 +92,11 @@ func newProxyServer( dbus = utildbus.New() iptInterface = utiliptables.New(execer, dbus, protocol) - ipvsInterface = utilipvs.New(execer) kernelHandler = ipvs.NewLinuxKernelHandler() ipsetInterface = utilipset.New(execer) + if canUse, _ := ipvs.CanUseIPVSProxier(kernelHandler, ipsetInterface); canUse { + ipvsInterface = utilipvs.New(execer) + } // We omit creation of pretty much everything if we run in cleanup mode if cleanupAndExit { diff --git a/pkg/proxy/ipvs/proxier.go b/pkg/proxy/ipvs/proxier.go index c920caf0813..3b45ce076a5 100644 --- a/pkg/proxy/ipvs/proxier.go +++ b/pkg/proxy/ipvs/proxier.go @@ -547,6 +547,9 @@ func cleanupIptablesLeftovers(ipt utiliptables.Interface) (encounteredError bool // CleanupLeftovers clean up all ipvs and iptables rules created by ipvs Proxier. func CleanupLeftovers(ipvs utilipvs.Interface, ipt utiliptables.Interface, ipset utilipset.Interface, cleanupIPVS bool) (encounteredError bool) { + if canUse, _ := CanUseIPVSProxier(NewLinuxKernelHandler(), ipset); !canUse { + return false + } if cleanupIPVS { // Return immediately when ipvs interface is nil - Probably initialization failed in somewhere. if ipvs == nil { From 7cbee06a00ffc93d7178634242deb0a9968eb916 Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Thu, 17 May 2018 15:28:46 +0800 Subject: [PATCH 021/416] Add Pod stats for Windows containers --- pkg/kubelet/stats/cri_stats_provider.go | 56 +++++++++++++++++++++---- pkg/kubelet/stats/helper.go | 8 ++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/pkg/kubelet/stats/cri_stats_provider.go b/pkg/kubelet/stats/cri_stats_provider.go index e82ca97a0d8..1af0a0cbfb3 100644 --- a/pkg/kubelet/stats/cri_stats_provider.go +++ b/pkg/kubelet/stats/cri_stats_provider.go @@ -142,12 +142,14 @@ func (p *criStatsProvider) ListPodStats() ([]statsapi.PodStats, error) { ps, found := sandboxIDToPodStats[podSandboxID] if !found { ps = buildPodStats(podSandbox) - // Fill stats from cadvisor is available for full set of required pod stats - p.addCadvisorPodNetworkStats(ps, podSandboxID, caInfos) - p.addCadvisorPodCPUMemoryStats(ps, types.UID(podSandbox.Metadata.Uid), allInfos) sandboxIDToPodStats[podSandboxID] = ps } + + // Fill available stats for full set of required pod stats cs := p.makeContainerStats(stats, container, &rootFsInfo, fsIDtoInfo, podSandbox.GetMetadata().GetUid()) + p.addPodNetworkStats(ps, podSandboxID, caInfos, cs) + p.addPodCPUMemoryStats(ps, types.UID(podSandbox.Metadata.Uid), allInfos, cs) + // If cadvisor stats is available for the container, use it to populate // container stats caStats, caFound := caInfos[containerID] @@ -263,29 +265,69 @@ func (p *criStatsProvider) makePodStorageStats(s *statsapi.PodStats, rootFsInfo return s } -func (p *criStatsProvider) addCadvisorPodNetworkStats( +func (p *criStatsProvider) addPodNetworkStats( ps *statsapi.PodStats, podSandboxID string, caInfos map[string]cadvisorapiv2.ContainerInfo, + cs *statsapi.ContainerStats, ) { caPodSandbox, found := caInfos[podSandboxID] + // try get network stats from cadvisor first. if found { ps.Network = cadvisorInfoToNetworkStats(ps.PodRef.Name, &caPodSandbox) - } else { - glog.V(4).Infof("Unable to find cadvisor stats for sandbox %q", podSandboxID) + return } + + // TODO: sum Pod network stats from container stats. + glog.V(4).Infof("Unable to find cadvisor stats for sandbox %q", podSandboxID) } -func (p *criStatsProvider) addCadvisorPodCPUMemoryStats( +func (p *criStatsProvider) addPodCPUMemoryStats( ps *statsapi.PodStats, podUID types.UID, allInfos map[string]cadvisorapiv2.ContainerInfo, + cs *statsapi.ContainerStats, ) { + // try get cpu and memory stats from cadvisor first. podCgroupInfo := getCadvisorPodInfoFromPodUID(podUID, allInfos) if podCgroupInfo != nil { cpu, memory := cadvisorInfoToCPUandMemoryStats(podCgroupInfo) ps.CPU = cpu ps.Memory = memory + return + } + + // Sum Pod cpu and memory stats from containers stats. + if cs.CPU != nil { + if ps.CPU == nil { + ps.CPU = &statsapi.CPUStats{} + } + + ps.CPU.Time = cs.StartTime + usageCoreNanoSeconds := getUint64Value(cs.CPU.UsageCoreNanoSeconds) + getUint64Value(ps.CPU.UsageCoreNanoSeconds) + usageNanoCores := getUint64Value(cs.CPU.UsageNanoCores) + getUint64Value(ps.CPU.UsageNanoCores) + ps.CPU.UsageCoreNanoSeconds = &usageCoreNanoSeconds + ps.CPU.UsageNanoCores = &usageNanoCores + } + + if cs.Memory != nil { + if ps.Memory == nil { + ps.Memory = &statsapi.MemoryStats{} + } + + ps.Memory.Time = cs.Memory.Time + availableBytes := getUint64Value(cs.Memory.AvailableBytes) + getUint64Value(ps.Memory.AvailableBytes) + usageBytes := getUint64Value(cs.Memory.UsageBytes) + getUint64Value(ps.Memory.UsageBytes) + workingSetBytes := getUint64Value(cs.Memory.WorkingSetBytes) + getUint64Value(ps.Memory.WorkingSetBytes) + rSSBytes := getUint64Value(cs.Memory.RSSBytes) + getUint64Value(ps.Memory.RSSBytes) + pageFaults := getUint64Value(cs.Memory.PageFaults) + getUint64Value(ps.Memory.PageFaults) + majorPageFaults := getUint64Value(cs.Memory.MajorPageFaults) + getUint64Value(ps.Memory.MajorPageFaults) + ps.Memory.AvailableBytes = &availableBytes + ps.Memory.UsageBytes = &usageBytes + ps.Memory.WorkingSetBytes = &workingSetBytes + ps.Memory.RSSBytes = &rSSBytes + ps.Memory.PageFaults = &pageFaults + ps.Memory.MajorPageFaults = &majorPageFaults } } diff --git a/pkg/kubelet/stats/helper.go b/pkg/kubelet/stats/helper.go index 77f3a6569e8..a195b9c634a 100644 --- a/pkg/kubelet/stats/helper.go +++ b/pkg/kubelet/stats/helper.go @@ -303,3 +303,11 @@ func buildRootfsStats(cstat *cadvisorapiv2.ContainerStats, imageFs *cadvisorapiv Inodes: imageFs.Inodes, } } + +func getUint64Value(value *uint64) uint64 { + if value == nil { + return 0 + } + + return *value +} From 6ad56325cab2b38d64b234163705f8c7132b8e3d Mon Sep 17 00:00:00 2001 From: xuzhonghu Date: Tue, 8 May 2018 16:43:24 +0800 Subject: [PATCH 022/416] kube-proxy should not depend on kubectl --- cmd/kube-proxy/app/BUILD | 1 - cmd/kube-proxy/app/server.go | 11 +++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/kube-proxy/app/BUILD b/cmd/kube-proxy/app/BUILD index 3ae5ab8f044..4b40cbdedcd 100644 --- a/cmd/kube-proxy/app/BUILD +++ b/cmd/kube-proxy/app/BUILD @@ -64,7 +64,6 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library", - "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubelet/qos:go_default_library", "//pkg/master/ports:go_default_library", "//pkg/proxy:go_default_library", diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index 60468d6990c..6161f15d9fe 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -51,7 +51,6 @@ import ( api "k8s.io/kubernetes/pkg/apis/core" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubelet/qos" "k8s.io/kubernetes/pkg/master/ports" "k8s.io/kubernetes/pkg/proxy" @@ -353,9 +352,13 @@ with the apiserver API to configure the proxy.`, glog.Fatalf("failed OS init: %v", err) } - cmdutil.CheckErr(opts.Complete()) - cmdutil.CheckErr(opts.Validate(args)) - cmdutil.CheckErr(opts.Run()) + if err := opts.Complete(); err != nil { + glog.Fatalf("failed complete: %v", err) + } + if err := opts.Validate(args); err != nil { + glog.Fatalf("failed validate: %v", err) + } + glog.Fatal(opts.Run()) }, } From f86af075264194b2ddbce8e6a71095b65e404ed4 Mon Sep 17 00:00:00 2001 From: Weibin Lin Date: Thu, 17 May 2018 17:10:25 +0800 Subject: [PATCH 023/416] Update ipvs docs --- check the prerequisite --- pkg/proxy/ipvs/README.md | 45 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/pkg/proxy/ipvs/README.md b/pkg/proxy/ipvs/README.md index e98c525504d..cd6fc1153ba 100644 --- a/pkg/proxy/ipvs/README.md +++ b/pkg/proxy/ipvs/README.md @@ -200,8 +200,7 @@ DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 /* default/nginx-s Currently, local-up scripts, GCE scripts and kubeadm support switching IPVS proxy mode via exporting environment variables or specifying flags. ### Prerequisite -Ensure the following kernel modules required by IPVS-based kube-proxy have been compiled into the node kernel (use `lsmod` to check): - +Ensure IPVS required kernel modules ```shell ip_vs ip_vs_rr @@ -209,6 +208,46 @@ ip_vs_wrr ip_vs_sh nf_conntrack_ipv4 ``` +1. have been compiled into the node kernel. Use + +`grep -e ipvs -e nf_conntrack_ipv4 /lib/modules/$(uname -r)/modules.builtin` + +and get results like the followings if compiled into kernel. +``` +kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko +kernel/net/netfilter/ipvs/ip_vs.ko +kernel/net/netfilter/ipvs/ip_vs_rr.ko +kernel/net/netfilter/ipvs/ip_vs_wrr.ko +kernel/net/netfilter/ipvs/ip_vs_lc.ko +kernel/net/netfilter/ipvs/ip_vs_wlc.ko +kernel/net/netfilter/ipvs/ip_vs_fo.ko +kernel/net/netfilter/ipvs/ip_vs_ovf.ko +kernel/net/netfilter/ipvs/ip_vs_lblc.ko +kernel/net/netfilter/ipvs/ip_vs_lblcr.ko +kernel/net/netfilter/ipvs/ip_vs_dh.ko +kernel/net/netfilter/ipvs/ip_vs_sh.ko +kernel/net/netfilter/ipvs/ip_vs_sed.ko +kernel/net/netfilter/ipvs/ip_vs_nq.ko +kernel/net/netfilter/ipvs/ip_vs_ftp.ko +``` + +OR + +2. have been loaded. +```shell +# load module +modprobe -- ip_vs +modprobe -- ip_vs_rr +modprobe -- ip_vs_wrr +modprobe -- ip_vs_sh +modprobe -- nf_conntrack_ipv4 + +# to check loaded modules, use +lsmod | grep -e ipvs -e nf_conntrack_ipv4 +# or +cut -f1 -d " " /proc/modules | grep -e ip_vs -e nf_conntrack_ipv4 + ``` + Packages such as `ipset` should also be installed on the node before using IPVS mode. Kube-proxy will fall back to IPTABLES mode if those requirements are not met. @@ -219,7 +258,7 @@ Kube-proxy will run in iptables mode by default in a [local-up cluster](https:// To use IPVS mode, users should export the env `KUBE_PROXY_MODE=ipvs` to specify the ipvs mode before [starting the cluster](https://github.com/kubernetes/community/blob/master/contributors/devel/running-locally.md#starting-the-cluster): ```shell -#before running `hack/local-up-cluster.sh` +# before running `hack/local-up-cluster.sh` export KUBE_PROXY_MODE=ipvs ``` From f66bafd12cc6d7503f529dc4f2dce1367c14c9a4 Mon Sep 17 00:00:00 2001 From: xuzhonghu Date: Thu, 17 May 2018 11:13:41 +0800 Subject: [PATCH 024/416] remove kube-proxy and kube-scheduler from pkg_kubectl_cmd_util_CONSUMES_BAD group --- build/visible_to/BUILD | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/build/visible_to/BUILD b/build/visible_to/BUILD index 752599ee075..693d8ea1833 100644 --- a/build/visible_to/BUILD +++ b/build/visible_to/BUILD @@ -200,22 +200,12 @@ package_group( ], ) -package_group( - name = "pkg_kubectl_cmd_util_CONSUMERS_BAD", - includes = [ - ":KUBEADM_BAD", - ], - packages = [ - "//cmd/kube-proxy/app", - ], -) - package_group( name = "pkg_kubectl_cmd_util_CONSUMERS", includes = [ ":COMMON_generators", ":COMMON_testing", - ":pkg_kubectl_cmd_util_CONSUMERS_BAD", + ":KUBEADM_BAD", ], packages = [ "//cmd/kubectl", From 6c2060db8616883da29f5f221e9a4c18eaa21c95 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Fri, 27 Apr 2018 13:01:29 +0000 Subject: [PATCH 025/416] fix formatAndMount func issue on Windows fix comments --- pkg/util/mount/mount_windows.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 0c10733e9de..2f0a6b7d97e 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -362,10 +362,23 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target) if err := ValidateDiskNumber(source); err != nil { - glog.Errorf("azureMount: formatAndMount failed, err: %v\n", err) + glog.Errorf("diskMount: formatAndMount failed, err: %v\n", err) return err } + if len(fstype) == 0 { + // Use 'NTFS' as the default + fstype = "NTFS" + } + + // format disk if it is unformatted(raw) + cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru", source) + cmd += fmt.Sprintf(" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", fstype) + if output, err := mounter.Exec.Run("powershell", "/c", cmd); err != nil { + return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output)) + } + glog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q\n", source, fstype) + driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec) if err != nil { return err From 045989e7bd073e27d680b946fe99dffd5e792075 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Sat, 28 Apr 2018 11:53:15 +0000 Subject: [PATCH 026/416] add formatAndMount unit test on Windows fix comments fix comments fix comments fix comments --- pkg/util/mount/mount_windows.go | 8 +-- pkg/util/mount/mount_windows_test.go | 73 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 2f0a6b7d97e..53ce163ad18 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -362,7 +362,7 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target) if err := ValidateDiskNumber(source); err != nil { - glog.Errorf("diskMount: formatAndMount failed, err: %v\n", err) + glog.Errorf("diskMount: formatAndMount failed, err: %v", err) return err } @@ -372,12 +372,12 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, } // format disk if it is unformatted(raw) - cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru", source) - cmd += fmt.Sprintf(" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", fstype) + cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+ + " | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype) if output, err := mounter.Exec.Run("powershell", "/c", cmd); err != nil { return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output)) } - glog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q\n", source, fstype) + glog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype) driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec) if err != nil { diff --git a/pkg/util/mount/mount_windows_test.go b/pkg/util/mount/mount_windows_test.go index 32b0903f183..a6e26bec8a6 100644 --- a/pkg/util/mount/mount_windows_test.go +++ b/pkg/util/mount/mount_windows_test.go @@ -24,6 +24,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -720,3 +721,75 @@ func TestIsLikelyNotMountPoint(t *testing.T) { } } } + +func TestFormatAndMount(t *testing.T) { + fakeMounter := ErrorMounter{&FakeMounter{}, 0, nil} + execCallback := func(cmd string, args ...string) ([]byte, error) { + for j := range args { + if strings.Contains(args[j], "Get-Disk -Number") { + return []byte("0"), nil + } + + if strings.Contains(args[j], "Get-Partition -DiskNumber") { + return []byte("0"), nil + } + + if strings.Contains(args[j], "mklink") { + return nil, nil + } + } + return nil, fmt.Errorf("Unexpected cmd %s, args %v", cmd, args) + } + fakeExec := NewFakeExec(execCallback) + + mounter := SafeFormatAndMount{ + Interface: &fakeMounter, + Exec: fakeExec, + } + + tests := []struct { + device string + target string + fstype string + mountOptions []string + expectError bool + }{ + { + "0", + "disk", + "NTFS", + []string{}, + false, + }, + { + "0", + "disk", + "", + []string{}, + false, + }, + { + "invalidDevice", + "disk", + "NTFS", + []string{}, + true, + }, + } + + for _, test := range tests { + base, err := ioutil.TempDir("", test.device) + if err != nil { + t.Fatalf(err.Error()) + } + defer os.RemoveAll(base) + + target := filepath.Join(base, test.target) + err = mounter.FormatAndMount(test.device, target, test.fstype, test.mountOptions) + if test.expectError { + assert.NotNil(t, err, "Expect error during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions) + } else { + assert.Nil(t, err, "Expect error is nil during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions) + } + } +} From 66da2ddcd03c236bd7a9db72fee08b3391fc9269 Mon Sep 17 00:00:00 2001 From: Pengfei Ni Date: Tue, 22 May 2018 10:51:26 +0800 Subject: [PATCH 027/416] Rename Du() to DiskUsage() for more expressive --- pkg/volume/metrics_du.go | 10 +++++----- pkg/volume/util/fs/fs.go | 3 ++- pkg/volume/util/fs/fs_unsupported.go | 3 ++- pkg/volume/util/fs/fs_windows.go | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pkg/volume/metrics_du.go b/pkg/volume/metrics_du.go index 88a985d5ac2..1cae99c1073 100644 --- a/pkg/volume/metrics_du.go +++ b/pkg/volume/metrics_du.go @@ -25,7 +25,7 @@ import ( var _ MetricsProvider = &metricsDu{} // metricsDu represents a MetricsProvider that calculates the used and -// available Volume space by executing the "du" command and gathering +// available Volume space by calling fs.DiskUsage() and gathering // filesystem info for the Volume path. type metricsDu struct { // the directory path the volume is mounted to. @@ -46,7 +46,7 @@ func (md *metricsDu) GetMetrics() (*Metrics, error) { return metrics, NewNoPathDefinedError() } - err := md.runDu(metrics) + err := md.runDiskUsage(metrics) if err != nil { return metrics, err } @@ -64,9 +64,9 @@ func (md *metricsDu) GetMetrics() (*Metrics, error) { return metrics, nil } -// runDu executes the "du" command and writes the results to metrics.Used -func (md *metricsDu) runDu(metrics *Metrics) error { - used, err := fs.Du(md.path) +// runDiskUsage gets disk usage of md.path and writes the results to metrics.Used +func (md *metricsDu) runDiskUsage(metrics *Metrics) error { + used, err := fs.DiskUsage(md.path) if err != nil { return err } diff --git a/pkg/volume/util/fs/fs.go b/pkg/volume/util/fs/fs.go index bbb4b0105c5..a80a167eea7 100644 --- a/pkg/volume/util/fs/fs.go +++ b/pkg/volume/util/fs/fs.go @@ -54,7 +54,8 @@ func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) { return available, capacity, usage, inodes, inodesFree, inodesUsed, nil } -func Du(path string) (*resource.Quantity, error) { +// DiskUsage gets disk usage of specified path. +func DiskUsage(path string) (*resource.Quantity, error) { // Uses the same niceness level as cadvisor.fs does when running du // Uses -B 1 to always scale to a blocksize of 1 byte out, err := exec.Command("nice", "-n", "19", "du", "-s", "-B", "1", path).CombinedOutput() diff --git a/pkg/volume/util/fs/fs_unsupported.go b/pkg/volume/util/fs/fs_unsupported.go index b48e4fd20cb..340b4fdc225 100644 --- a/pkg/volume/util/fs/fs_unsupported.go +++ b/pkg/volume/util/fs/fs_unsupported.go @@ -29,7 +29,8 @@ func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) { return 0, 0, 0, 0, 0, 0, fmt.Errorf("FsInfo not supported for this build.") } -func Du(path string) (*resource.Quantity, error) { +// DiskUsage gets disk usage of specified path. +func DiskUsage(path string) (*resource.Quantity, error) { return nil, fmt.Errorf("Du not supported for this build.") } diff --git a/pkg/volume/util/fs/fs_windows.go b/pkg/volume/util/fs/fs_windows.go index 534604d2b2e..ea8c4784ae9 100644 --- a/pkg/volume/util/fs/fs_windows.go +++ b/pkg/volume/util/fs/fs_windows.go @@ -56,7 +56,8 @@ func FsInfo(path string) (int64, int64, int64, int64, int64, int64, error) { return freeBytesAvailable, totalNumberOfBytes, totalNumberOfBytes - freeBytesAvailable, 0, 0, 0, nil } -func Du(path string) (*resource.Quantity, error) { +// DiskUsage gets disk usage of specified path. +func DiskUsage(path string) (*resource.Quantity, error) { _, _, usage, _, _, _, err := FsInfo(path) if err != nil { return nil, err From 49b3de6623874a0088392f590cde750edd6c26c8 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 10:04:58 +0200 Subject: [PATCH 028/416] Create nsenter OWNERS --- pkg/util/nsenter/OWNERS | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 pkg/util/nsenter/OWNERS diff --git a/pkg/util/nsenter/OWNERS b/pkg/util/nsenter/OWNERS new file mode 100644 index 00000000000..c4f27cb4381 --- /dev/null +++ b/pkg/util/nsenter/OWNERS @@ -0,0 +1,8 @@ +reviewers: + - jsafrane + - msau42 + - cofyc +approvers: + - jsafrane + - msau42 + - cofyc From 956bbfd1a6764e6bb17e756ffb5065c5b385fb95 Mon Sep 17 00:00:00 2001 From: stewart-yu Date: Fri, 18 May 2018 16:50:22 +0800 Subject: [PATCH 029/416] should not ignore err when convert controllermanagerconfiguration api --- .../app/controllermanager.go | 6 +++++- .../app/options/options.go | 17 +++++++++++------ .../app/options/options_test.go | 4 ++-- .../app/controllermanager.go | 6 +++++- .../app/options/options.go | 18 ++++++++++++------ .../app/options/options_test.go | 2 +- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/cmd/cloud-controller-manager/app/controllermanager.go b/cmd/cloud-controller-manager/app/controllermanager.go index 988bf5edf36..01c4bfe2ba0 100644 --- a/cmd/cloud-controller-manager/app/controllermanager.go +++ b/cmd/cloud-controller-manager/app/controllermanager.go @@ -54,7 +54,11 @@ const ( // NewCloudControllerManagerCommand creates a *cobra.Command object with default parameters func NewCloudControllerManagerCommand() *cobra.Command { - s := options.NewCloudControllerManagerOptions() + s, err := options.NewCloudControllerManagerOptions() + if err != nil { + glog.Fatalf("unable to initialize command options: %v", err) + } + cmd := &cobra.Command{ Use: "cloud-controller-manager", Long: `The Cloud controller manager is a daemon that embeds diff --git a/cmd/cloud-controller-manager/app/options/options.go b/cmd/cloud-controller-manager/app/options/options.go index 433d9935164..5c213555891 100644 --- a/cmd/cloud-controller-manager/app/options/options.go +++ b/cmd/cloud-controller-manager/app/options/options.go @@ -67,8 +67,11 @@ type CloudControllerManagerOptions struct { } // NewCloudControllerManagerOptions creates a new ExternalCMServer with a default config. -func NewCloudControllerManagerOptions() *CloudControllerManagerOptions { - componentConfig := NewDefaultComponentConfig(ports.InsecureCloudControllerManagerPort) +func NewCloudControllerManagerOptions() (*CloudControllerManagerOptions, error) { + componentConfig, err := NewDefaultComponentConfig(ports.InsecureCloudControllerManagerPort) + if err != nil { + return nil, err + } s := CloudControllerManagerOptions{ CloudProvider: &cmoptions.CloudProviderOptions{}, @@ -96,11 +99,11 @@ func NewCloudControllerManagerOptions() *CloudControllerManagerOptions { // TODO: enable HTTPS by default s.SecureServing.BindPort = 0 - return &s + return &s, nil } // NewDefaultComponentConfig returns cloud-controller manager configuration object. -func NewDefaultComponentConfig(insecurePort int32) componentconfig.CloudControllerManagerConfiguration { +func NewDefaultComponentConfig(insecurePort int32) (componentconfig.CloudControllerManagerConfiguration, error) { scheme := runtime.NewScheme() componentconfigv1alpha1.AddToScheme(scheme) componentconfig.AddToScheme(scheme) @@ -109,9 +112,11 @@ func NewDefaultComponentConfig(insecurePort int32) componentconfig.CloudControll scheme.Default(&versioned) internal := componentconfig.CloudControllerManagerConfiguration{} - scheme.Convert(&versioned, &internal, nil) + if err := scheme.Convert(&versioned, &internal, nil); err != nil { + return internal, err + } internal.KubeCloudShared.Port = insecurePort - return internal + return internal, nil } // AddFlags adds flags for a specific ExternalCMServer to the specified FlagSet diff --git a/cmd/cloud-controller-manager/app/options/options_test.go b/cmd/cloud-controller-manager/app/options/options_test.go index fe2f62f0b35..9df6e0f2163 100644 --- a/cmd/cloud-controller-manager/app/options/options_test.go +++ b/cmd/cloud-controller-manager/app/options/options_test.go @@ -32,7 +32,7 @@ import ( ) func TestDefaultFlags(t *testing.T) { - s := NewCloudControllerManagerOptions() + s, _ := NewCloudControllerManagerOptions() expected := &CloudControllerManagerOptions{ CloudProvider: &cmoptions.CloudProviderOptions{ @@ -95,7 +95,7 @@ func TestDefaultFlags(t *testing.T) { func TestAddFlags(t *testing.T) { f := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) - s := NewCloudControllerManagerOptions() + s, _ := NewCloudControllerManagerOptions() s.AddFlags(f) args := []string{ diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index b03ba75667e..321d6e41ba9 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -70,7 +70,11 @@ const ( // NewControllerManagerCommand creates a *cobra.Command object with default parameters func NewControllerManagerCommand() *cobra.Command { - s := options.NewKubeControllerManagerOptions() + s, err := options.NewKubeControllerManagerOptions() + if err != nil { + glog.Fatalf("unable to initialize command options: %v", err) + } + cmd := &cobra.Command{ Use: "kube-controller-manager", Long: `The Kubernetes controller manager is a daemon that embeds diff --git a/cmd/kube-controller-manager/app/options/options.go b/cmd/kube-controller-manager/app/options/options.go index 2841dba49e4..2b26024e5e8 100644 --- a/cmd/kube-controller-manager/app/options/options.go +++ b/cmd/kube-controller-manager/app/options/options.go @@ -90,8 +90,12 @@ type KubeControllerManagerOptions struct { } // NewKubeControllerManagerOptions creates a new KubeControllerManagerOptions with a default config. -func NewKubeControllerManagerOptions() *KubeControllerManagerOptions { - componentConfig := NewDefaultComponentConfig(ports.InsecureKubeControllerManagerPort) +func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { + componentConfig, err := NewDefaultComponentConfig(ports.InsecureKubeControllerManagerPort) + if err != nil { + return nil, err + } + s := KubeControllerManagerOptions{ CloudProvider: &cmoptions.CloudProviderOptions{}, Debugging: &cmoptions.DebuggingOptions{}, @@ -193,11 +197,11 @@ func NewKubeControllerManagerOptions() *KubeControllerManagerOptions { s.GarbageCollectorController.GCIgnoredResources = gcIgnoredResources - return &s + return &s, nil } // NewDefaultComponentConfig returns kube-controller manager configuration object. -func NewDefaultComponentConfig(insecurePort int32) componentconfig.KubeControllerManagerConfiguration { +func NewDefaultComponentConfig(insecurePort int32) (componentconfig.KubeControllerManagerConfiguration, error) { scheme := runtime.NewScheme() componentconfigv1alpha1.AddToScheme(scheme) componentconfig.AddToScheme(scheme) @@ -206,9 +210,11 @@ func NewDefaultComponentConfig(insecurePort int32) componentconfig.KubeControlle scheme.Default(&versioned) internal := componentconfig.KubeControllerManagerConfiguration{} - scheme.Convert(&versioned, &internal, nil) + if err := scheme.Convert(&versioned, &internal, nil); err != nil { + return internal, err + } internal.KubeCloudShared.Port = insecurePort - return internal + return internal, nil } // AddFlags adds flags for a specific KubeControllerManagerOptions to the specified FlagSet diff --git a/cmd/kube-controller-manager/app/options/options_test.go b/cmd/kube-controller-manager/app/options/options_test.go index 0e69b91b857..d32848ed8e3 100644 --- a/cmd/kube-controller-manager/app/options/options_test.go +++ b/cmd/kube-controller-manager/app/options/options_test.go @@ -34,7 +34,7 @@ import ( func TestAddFlags(t *testing.T) { f := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) - s := NewKubeControllerManagerOptions() + s, _ := NewKubeControllerManagerOptions() s.AddFlags(f, []string{""}, []string{""}) args := []string{ From 097094e5fa5065eaaab69167373d9ea308fcdd59 Mon Sep 17 00:00:00 2001 From: Guoliang Wang Date: Wed, 23 May 2018 13:56:17 +0800 Subject: [PATCH 030/416] Remove unused parameter (pod) --- pkg/scheduler/core/generic_scheduler.go | 4 +- pkg/scheduler/core/generic_scheduler_test.go | 56 +------------------- 2 files changed, 3 insertions(+), 57 deletions(-) diff --git a/pkg/scheduler/core/generic_scheduler.go b/pkg/scheduler/core/generic_scheduler.go index 960054b0d14..98f7acf4c57 100644 --- a/pkg/scheduler/core/generic_scheduler.go +++ b/pkg/scheduler/core/generic_scheduler.go @@ -219,7 +219,7 @@ func (g *genericScheduler) Preempt(pod *v1.Pod, nodeLister algorithm.NodeLister, if len(allNodes) == 0 { return nil, nil, nil, ErrNoNodesAvailable } - potentialNodes := nodesWherePreemptionMightHelp(pod, allNodes, fitError.FailedPredicates) + potentialNodes := nodesWherePreemptionMightHelp(allNodes, fitError.FailedPredicates) if len(potentialNodes) == 0 { glog.V(3).Infof("Preemption will not help schedule pod %v on any node.", pod.Name) // In this case, we should clean-up any existing nominated node name of the pod. @@ -969,7 +969,7 @@ func selectVictimsOnNode( // nodesWherePreemptionMightHelp returns a list of nodes with failed predicates // that may be satisfied by removing pods from the node. -func nodesWherePreemptionMightHelp(pod *v1.Pod, nodes []*v1.Node, failedPredicatesMap FailedPredicateMap) []*v1.Node { +func nodesWherePreemptionMightHelp(nodes []*v1.Node, failedPredicatesMap FailedPredicateMap) []*v1.Node { potentialNodes := []*v1.Node{} for _, node := range nodes { unresolvableReasonExist := false diff --git a/pkg/scheduler/core/generic_scheduler_test.go b/pkg/scheduler/core/generic_scheduler_test.go index 01c6d331ff5..704bb82ca88 100644 --- a/pkg/scheduler/core/generic_scheduler_test.go +++ b/pkg/scheduler/core/generic_scheduler_test.go @@ -1097,7 +1097,6 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { tests := []struct { name string failedPredMap FailedPredicateMap - pod *v1.Pod expected map[string]bool // set of expected node names. Value is ignored. }{ { @@ -1108,7 +1107,6 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { "machine3": []algorithm.PredicateFailureReason{algorithmpredicates.ErrTaintsTolerationsNotMatch}, "machine4": []algorithm.PredicateFailureReason{algorithmpredicates.ErrNodeLabelPresenceViolated}, }, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}}, expected: map[string]bool{}, }, { @@ -1118,23 +1116,6 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { "machine2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName}, "machine3": []algorithm.PredicateFailureReason{algorithmpredicates.ErrNodeUnschedulable}, }, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Affinity: &v1.Affinity{ - PodAffinity: &v1.PodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "service", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"securityscan", "value2"}, - }, - }, - }, - TopologyKey: "hostname", - }, - }, - }}}}, expected: map[string]bool{"machine1": true, "machine4": true}, }, { @@ -1143,40 +1124,6 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { "machine1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrPodAffinityNotMatch}, "machine2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName}, }, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Affinity: &v1.Affinity{ - PodAffinity: &v1.PodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "service", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"securityscan", "value2"}, - }, - }, - }, - TopologyKey: "hostname", - }, - }, - }, - PodAntiAffinity: &v1.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "service", - Operator: metav1.LabelSelectorOpNotIn, - Values: []string{"blah", "foo"}, - }, - }, - }, - TopologyKey: "region", - }, - }, - }, - }}}, expected: map[string]bool{"machine1": true, "machine3": true, "machine4": true}, }, { @@ -1187,13 +1134,12 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) { "machine3": []algorithm.PredicateFailureReason{algorithmpredicates.NewInsufficientResourceError(v1.ResourceMemory, 1000, 600, 400)}, "machine4": []algorithm.PredicateFailureReason{}, }, - pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}}, expected: map[string]bool{"machine3": true, "machine4": true}, }, } for _, test := range tests { - nodes := nodesWherePreemptionMightHelp(test.pod, makeNodeList(nodeNames), test.failedPredMap) + nodes := nodesWherePreemptionMightHelp(makeNodeList(nodeNames), test.failedPredMap) if len(test.expected) != len(nodes) { t.Errorf("test [%v]:number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", test.name, len(test.expected), len(nodes), nodes) } From 97b5299cd737c624887a39400e8377c8355a2a85 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Wed, 23 May 2018 10:17:59 +0200 Subject: [PATCH 031/416] Add GetMode to mounter interface. Kubelet must not call os.Lstat on raw volume paths when it runs in a container. Mounter knows where the file really is. --- pkg/kubelet/cm/container_manager_linux_test.go | 4 ++++ pkg/kubelet/kubelet_pods.go | 11 +++++------ pkg/util/mount/exec_mount.go | 4 ++++ pkg/util/mount/exec_mount_test.go | 4 ++++ pkg/util/mount/exec_mount_unsupported.go | 4 ++++ pkg/util/mount/fake.go | 4 ++++ pkg/util/mount/mount.go | 2 ++ pkg/util/mount/mount_linux.go | 13 +++++++++++++ pkg/util/mount/mount_unsupported.go | 4 ++++ pkg/util/mount/mount_windows.go | 8 ++++++++ pkg/util/mount/nsenter_mount.go | 8 ++++++++ pkg/util/mount/nsenter_mount_unsupported.go | 4 ++++ pkg/util/removeall/removeall_test.go | 4 ++++ pkg/volume/host_path/host_path_test.go | 4 ++++ 14 files changed, 72 insertions(+), 6 deletions(-) diff --git a/pkg/kubelet/cm/container_manager_linux_test.go b/pkg/kubelet/cm/container_manager_linux_test.go index cd4b0460c6f..be744604f39 100644 --- a/pkg/kubelet/cm/container_manager_linux_test.go +++ b/pkg/kubelet/cm/container_manager_linux_test.go @@ -120,6 +120,10 @@ func (mi *fakeMountInterface) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } +func (mi *fakeMountInterface) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} + func fakeContainerMgrMountInt() mount.Interface { return &fakeMountInterface{ []mount.MountPoint{ diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 46a17a01a58..a0bd2febf39 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -175,12 +175,6 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h return nil, cleanupAction, fmt.Errorf("unable to provision SubPath `%s`: %v", mount.SubPath, err) } - fileinfo, err := os.Lstat(hostPath) - if err != nil { - return nil, cleanupAction, err - } - perm := fileinfo.Mode() - volumePath, err := filepath.EvalSymlinks(hostPath) if err != nil { return nil, cleanupAction, err @@ -195,6 +189,11 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h // when the pod specifies an fsGroup, and if the directory is not created here, Docker will // later auto-create it with the incorrect mode 0750 // Make extra care not to escape the volume! + perm, err := mounter.GetMode(volumePath) + if err != nil { + return nil, cleanupAction, err + } + if err := mounter.SafeMakeDir(hostPath, volumePath, perm); err != nil { glog.Errorf("failed to mkdir %q: %v", hostPath, err) return nil, cleanupAction, err diff --git a/pkg/util/mount/exec_mount.go b/pkg/util/mount/exec_mount.go index fcb948aa34e..1dab57880b0 100644 --- a/pkg/util/mount/exec_mount.go +++ b/pkg/util/mount/exec_mount.go @@ -163,3 +163,7 @@ func (m *execMounter) GetFSGroup(pathname string) (int64, error) { func (m *execMounter) GetSELinuxSupport(pathname string) (bool, error) { return m.wrappedMounter.GetSELinuxSupport(pathname) } + +func (m *execMounter) GetMode(pathname string) (os.FileMode, error) { + return m.wrappedMounter.GetMode(pathname) +} diff --git a/pkg/util/mount/exec_mount_test.go b/pkg/util/mount/exec_mount_test.go index b3af0a46fbb..c48133ba2a2 100644 --- a/pkg/util/mount/exec_mount_test.go +++ b/pkg/util/mount/exec_mount_test.go @@ -176,3 +176,7 @@ func (fm *fakeMounter) GetFSGroup(pathname string) (int64, error) { func (fm *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } + +func (fm *fakeMounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} diff --git a/pkg/util/mount/exec_mount_unsupported.go b/pkg/util/mount/exec_mount_unsupported.go index cbb5bbc1591..f7cf757f26e 100644 --- a/pkg/util/mount/exec_mount_unsupported.go +++ b/pkg/util/mount/exec_mount_unsupported.go @@ -110,3 +110,7 @@ func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) { func (mounter *execMounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } + +func (mounter *execMounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} diff --git a/pkg/util/mount/fake.go b/pkg/util/mount/fake.go index f82f669b2eb..be4a033649a 100644 --- a/pkg/util/mount/fake.go +++ b/pkg/util/mount/fake.go @@ -232,3 +232,7 @@ func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) { func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("GetSELinuxSupport not implemented") } + +func (f *FakeMounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index 0c59ca9ebb7..d1edbfa266a 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -117,6 +117,8 @@ type Interface interface { // GetSELinuxSupport returns true if given path is on a mount that supports // SELinux. GetSELinuxSupport(pathname string) (bool, error) + // GetMode returns permissions of the path. + GetMode(pathname string) (os.FileMode, error) } type Subpath struct { diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index dbb864cd479..466fb9f8e03 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -982,6 +982,10 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { return getFSGroup(realpath) } +func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { + return getMode(pathname) +} + // This implementation is shared between Linux and NsEnterMounter func getFSGroup(pathname string) (int64, error) { info, err := os.Stat(pathname) @@ -991,6 +995,15 @@ func getFSGroup(pathname string) (int64, error) { return int64(info.Sys().(*syscall.Stat_t).Gid), nil } +// This implementation is shared between Linux and NsEnterMounter +func getMode(pathname string) (os.FileMode, error) { + info, err := os.Stat(pathname) + if err != nil { + return 0, err + } + return info.Mode(), nil +} + // This implementation is shared between Linux and NsEnterMounter func doSafeMakeDir(pathname string, base string, perm os.FileMode) error { glog.V(4).Infof("Creating directory %q within base %q", pathname, base) diff --git a/pkg/util/mount/mount_unsupported.go b/pkg/util/mount/mount_unsupported.go index 6e268e0f43d..2b7a329340b 100644 --- a/pkg/util/mount/mount_unsupported.go +++ b/pkg/util/mount/mount_unsupported.go @@ -138,3 +138,7 @@ func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) { func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } + +func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index f31f99bd66d..2c87755c9f2 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -461,6 +461,14 @@ func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) { return false, nil } +func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { + info, err := os.Stat(pathname) + if err != nil { + return 0, err + } + return info.Mode(), nil +} + // SafeMakeDir makes sure that the created directory does not escape given base directory mis-using symlinks. func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { return doSafeMakeDir(pathname, base, perm) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 4c48d673254..1936d521e78 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -347,3 +347,11 @@ func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) { return getSELinuxSupport(pathname, procMountInfoPath) } + +func (mounter *NsenterMounter) GetMode(pathname string) (os.FileMode, error) { + kubeletpath, err := mounter.ne.KubeletPath(pathname) + if err != nil { + return 0, err + } + return getMode(kubeletpath) +} diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index 8cf79de472c..007a456d490 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -110,3 +110,7 @@ func (*NsenterMounter) GetFSGroup(pathname string) (int64, error) { func (*NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } + +func (*NsenterMounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} diff --git a/pkg/util/removeall/removeall_test.go b/pkg/util/removeall/removeall_test.go index 134326369ff..878db734cdf 100644 --- a/pkg/util/removeall/removeall_test.go +++ b/pkg/util/removeall/removeall_test.go @@ -103,6 +103,10 @@ func (mounter *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) { return false, errors.New("not implemented") } +func (mounter *fakeMounter) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} + func (mounter *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) { name := path.Base(file) if strings.HasPrefix(name, "mount") { diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index 199880c247e..b8ae4fa9fc0 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -401,6 +401,10 @@ func (fftc *fakeFileTypeChecker) GetSELinuxSupport(pathname string) (bool, error return false, errors.New("not implemented") } +func (fftc *fakeFileTypeChecker) GetMode(pathname string) (os.FileMode, error) { + return 0, errors.New("not implemented") +} + func setUp() error { err := os.MkdirAll("/tmp/ExistingFolder", os.FileMode(0755)) if err != nil { From 7450d1b4277998eea436e5daa552a8bf94adef06 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:25 +0200 Subject: [PATCH 032/416] Allow EvalSymlinks target not to exist. Various NsEnterMounter function need to resolve the part of the path that exists and blindly add the part that doesn't. --- pkg/util/mount/nsenter_mount.go | 10 ++++++---- pkg/util/nsenter/nsenter.go | 30 +++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 1936d521e78..9531edddb25 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -329,7 +329,7 @@ func (mounter *NsenterMounter) SafeMakeDir(pathname string, base string, perm os } func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) { - hostpath, err := mounter.ne.EvalSymlinks(pathname) + hostpath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */) if err != nil { return nil, err } @@ -337,10 +337,11 @@ func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) { } func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { - kubeletpath, err := mounter.ne.KubeletPath(pathname) + hostPath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */) if err != nil { - return 0, err + return -1, err } + kubeletpath := mounter.ne.KubeletPath(hostPath) return getFSGroup(kubeletpath) } @@ -349,9 +350,10 @@ func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) } func (mounter *NsenterMounter) GetMode(pathname string) (os.FileMode, error) { - kubeletpath, err := mounter.ne.KubeletPath(pathname) + hostPath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */) if err != nil { return 0, err } + kubeletpath := mounter.ne.KubeletPath(hostPath) return getMode(kubeletpath) } diff --git a/pkg/util/nsenter/nsenter.go b/pkg/util/nsenter/nsenter.go index 11e9fe4a6dc..477950476b1 100644 --- a/pkg/util/nsenter/nsenter.go +++ b/pkg/util/nsenter/nsenter.go @@ -128,8 +128,23 @@ func (ne *Nsenter) SupportsSystemd() (string, bool) { // EvalSymlinks returns the path name on the host after evaluating symlinks on the // host. -func (ne *Nsenter) EvalSymlinks(pathname string) (string, error) { - args := []string{"-m", pathname} +// mustExist makes EvalSymlinks to return error when the path does not +// exist. When it's false, it evaluates symlinks of the existing part and +// blindly adds the non-existing part: +// pathname: /mnt/volume/non/existing/directory +// /mnt/volume exists +// non/existing/directory does not exist +// -> It resolves symlinks in /mnt/volume to say /mnt/foo and returns +// /mnt/foo/non/existing/directory. +func (ne *Nsenter) EvalSymlinks(pathname string, mustExist bool) (string, error) { + var args []string + if mustExist { + // "realpath -e: all components of the path must exist" + args = []string{"-e", pathname} + } else { + // "realpath -m: no path components need exist or be a directory" + args = []string{"-m", pathname} + } outBytes, err := ne.Exec("realpath", args).CombinedOutput() if err != nil { glog.Infof("failed to resolve symbolic links on %s: %v", pathname, err) @@ -139,11 +154,8 @@ func (ne *Nsenter) EvalSymlinks(pathname string) (string, error) { } // KubeletPath returns the path name that can be accessed by containerized -// kubelet, after evaluating symlinks on the host. -func (ne *Nsenter) KubeletPath(pathname string) (string, error) { - hostpath, err := ne.EvalSymlinks(pathname) - if err != nil { - return "", err - } - return filepath.Join(hostRootFsPath, hostpath), nil +// kubelet. It is recommended to resolve symlinks on the host by EvalSymlinks +// before calling this function +func (ne *Nsenter) KubeletPath(pathname string) string { + return filepath.Join(hostRootFsPath, pathname) } From 74ba0878a1240729cf49e0ec52f122cdbb1fee4d Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:25 +0200 Subject: [PATCH 033/416] Enhance ExistsPath check It should return error when the check fails (e.g. no permissions, symlink link loop etc.) --- pkg/kubelet/cm/container_manager_linux_test.go | 4 ++-- pkg/kubelet/kubelet_pods.go | 3 +-- pkg/util/mount/BUILD | 4 ++++ pkg/util/mount/exec_mount.go | 2 +- pkg/util/mount/exec_mount_test.go | 4 ++-- pkg/util/mount/exec_mount_unsupported.go | 4 ++-- pkg/util/mount/fake.go | 4 ++-- pkg/util/mount/mount.go | 6 +++--- pkg/util/mount/mount_linux.go | 9 +++------ pkg/util/mount/mount_unsupported.go | 7 ++----- pkg/util/mount/mount_windows.go | 18 ++++++++++-------- pkg/util/mount/nsenter_mount.go | 15 +++++++++------ pkg/util/mount/nsenter_mount_unsupported.go | 4 ++-- pkg/util/removeall/removeall_test.go | 4 ++-- pkg/volume/host_path/host_path.go | 3 ++- pkg/volume/host_path/host_path_test.go | 4 ++-- 16 files changed, 49 insertions(+), 46 deletions(-) diff --git a/pkg/kubelet/cm/container_manager_linux_test.go b/pkg/kubelet/cm/container_manager_linux_test.go index be744604f39..127140e751a 100644 --- a/pkg/kubelet/cm/container_manager_linux_test.go +++ b/pkg/kubelet/cm/container_manager_linux_test.go @@ -92,8 +92,8 @@ func (mi *fakeMountInterface) MakeFile(pathname string) error { return nil } -func (mi *fakeMountInterface) ExistsPath(pathname string) bool { - return true +func (mi *fakeMountInterface) ExistsPath(pathname string) (bool, error) { + return true, errors.New("not implemented") } func (mi *fakeMountInterface) PrepareSafeSubpath(subPath mount.Subpath) (newHostPath string, cleanupAction func(), err error) { diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index a0bd2febf39..c1d2c54f090 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -60,7 +60,6 @@ import ( "k8s.io/kubernetes/pkg/kubelet/status" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/util/format" - utilfile "k8s.io/kubernetes/pkg/util/file" mountutil "k8s.io/kubernetes/pkg/util/mount" volumeutil "k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" @@ -181,7 +180,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h } hostPath = filepath.Join(volumePath, mount.SubPath) - if subPathExists, err := utilfile.FileOrSymlinkExists(hostPath); err != nil { + if subPathExists, err := mounter.ExistsPath(hostPath); err != nil { glog.Errorf("Could not determine if subPath %s exists; will not attempt to change its permissions", hostPath) } else if !subPathExists { // Create the sub path now because if it's auto-created later when referenced, it may have an diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 9040cd93b26..6d2e65cbc96 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -72,11 +72,15 @@ go_library( "//vendor/k8s.io/utils/exec:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:linux": [ + "//pkg/util/file:go_default_library", "//pkg/util/io:go_default_library", "//pkg/util/nsenter:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", ], + "@io_bazel_rules_go//go/platform:windows": [ + "//pkg/util/file:go_default_library", + ], "//conditions:default": [], }), ) diff --git a/pkg/util/mount/exec_mount.go b/pkg/util/mount/exec_mount.go index 1dab57880b0..fe7dcbd7ef9 100644 --- a/pkg/util/mount/exec_mount.go +++ b/pkg/util/mount/exec_mount.go @@ -136,7 +136,7 @@ func (m *execMounter) MakeDir(pathname string) error { return m.wrappedMounter.MakeDir(pathname) } -func (m *execMounter) ExistsPath(pathname string) bool { +func (m *execMounter) ExistsPath(pathname string) (bool, error) { return m.wrappedMounter.ExistsPath(pathname) } diff --git a/pkg/util/mount/exec_mount_test.go b/pkg/util/mount/exec_mount_test.go index c48133ba2a2..9619356f71b 100644 --- a/pkg/util/mount/exec_mount_test.go +++ b/pkg/util/mount/exec_mount_test.go @@ -147,8 +147,8 @@ func (fm *fakeMounter) MakeFile(pathname string) error { func (fm *fakeMounter) MakeDir(pathname string) error { return nil } -func (fm *fakeMounter) ExistsPath(pathname string) bool { - return false +func (fm *fakeMounter) ExistsPath(pathname string) (bool, error) { + return false, errors.New("not implemented") } func (fm *fakeMounter) GetFileType(pathname string) (FileType, error) { return FileTypeFile, nil diff --git a/pkg/util/mount/exec_mount_unsupported.go b/pkg/util/mount/exec_mount_unsupported.go index f7cf757f26e..6854b32b26c 100644 --- a/pkg/util/mount/exec_mount_unsupported.go +++ b/pkg/util/mount/exec_mount_unsupported.go @@ -83,8 +83,8 @@ func (mounter *execMounter) MakeFile(pathname string) error { return nil } -func (mounter *execMounter) ExistsPath(pathname string) bool { - return true +func (mounter *execMounter) ExistsPath(pathname string) (bool, error) { + return true, errors.New("not implemented") } func (mounter *execMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { diff --git a/pkg/util/mount/fake.go b/pkg/util/mount/fake.go index be4a033649a..10832fd321b 100644 --- a/pkg/util/mount/fake.go +++ b/pkg/util/mount/fake.go @@ -201,8 +201,8 @@ func (f *FakeMounter) MakeFile(pathname string) error { return nil } -func (f *FakeMounter) ExistsPath(pathname string) bool { - return false +func (f *FakeMounter) ExistsPath(pathname string) (bool, error) { + return false, errors.New("not implemented") } func (f *FakeMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index d1edbfa266a..06ee97e5d27 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -91,9 +91,9 @@ type Interface interface { // else. E.g. if the directory already exists, it may exists outside of the // base due to symlinks. SafeMakeDir(pathname string, base string, perm os.FileMode) error - // ExistsPath checks whether the path exists. - // Will operate in the host mount namespace if kubelet is running in a container - ExistsPath(pathname string) bool + // Will operate in the host mount namespace if kubelet is running in a container. + // Error is returned on any other error than "file not found". + ExistsPath(pathname string) (bool, error) // CleanSubPaths removes any bind-mounts created by PrepareSafeSubpath in given // pod volume directory. CleanSubPaths(podDir string, volumeName string) error diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 466fb9f8e03..a72774ab415 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -33,6 +33,7 @@ import ( "github.com/golang/glog" "golang.org/x/sys/unix" "k8s.io/apimachinery/pkg/util/sets" + utilfile "k8s.io/kubernetes/pkg/util/file" utilio "k8s.io/kubernetes/pkg/util/io" utilexec "k8s.io/utils/exec" ) @@ -447,12 +448,8 @@ func (mounter *Mounter) MakeFile(pathname string) error { return nil } -func (mounter *Mounter) ExistsPath(pathname string) bool { - _, err := os.Stat(pathname) - if err != nil { - return false - } - return true +func (mounter *Mounter) ExistsPath(pathname string) (bool, error) { + return utilfile.FileExists(pathname) } // formatAndMount uses unix utils to format and mount the given disk diff --git a/pkg/util/mount/mount_unsupported.go b/pkg/util/mount/mount_unsupported.go index 2b7a329340b..6143aec1827 100644 --- a/pkg/util/mount/mount_unsupported.go +++ b/pkg/util/mount/mount_unsupported.go @@ -21,8 +21,6 @@ package mount import ( "errors" "os" - - "github.com/golang/glog" ) type Mounter struct { @@ -110,9 +108,8 @@ func (mounter *Mounter) MakeFile(pathname string) error { return unsupportedErr } -func (mounter *Mounter) ExistsPath(pathname string) bool { - glog.Errorf("%s", unsupportedErr) - return true +func (mounter *Mounter) ExistsPath(pathname string) (bool, error) { + return true, errors.New("not implemented") } func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 2c87755c9f2..3079b8c65de 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -29,6 +29,8 @@ import ( "syscall" "github.com/golang/glog" + + utilfile "k8s.io/kubernetes/pkg/util/file" ) // Mounter provides the default implementation of mount.Interface @@ -147,9 +149,13 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) { if stat.Mode()&os.ModeSymlink != 0 { target, err := os.Readlink(file) if err != nil { - return true, fmt.Errorf("Readlink error: %v", err) + return true, fmt.Errorf("readlink error: %v", err) } - return !mounter.ExistsPath(target), nil + exists, err := mounter.ExistsPath(target) + if err != nil { + return true, err + } + return !exists, nil } return true, nil @@ -232,12 +238,8 @@ func (mounter *Mounter) MakeFile(pathname string) error { } // ExistsPath checks whether the path exists -func (mounter *Mounter) ExistsPath(pathname string) bool { - _, err := os.Stat(pathname) - if err != nil { - return false - } - return true +func (mounter *Mounter) ExistsPath(pathname string) (bool, error) { + return utilfile.FileExists(pathname) } // check whether hostPath is within volume path diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 9531edddb25..4a920d63b51 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/golang/glog" + utilfile "k8s.io/kubernetes/pkg/util/file" utilio "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/util/nsenter" ) @@ -281,13 +282,15 @@ func (mounter *NsenterMounter) MakeFile(pathname string) error { return nil } -func (mounter *NsenterMounter) ExistsPath(pathname string) bool { - args := []string{pathname} - _, err := mounter.ne.Exec("ls", args).CombinedOutput() - if err == nil { - return true +func (mounter *NsenterMounter) ExistsPath(pathname string) (bool, error) { + // Resolve the symlinks but allow the target not to exist. EvalSymlinks + // would return an generic error when the target does not exist. + hostPath, err := mounter.ne.EvalSymlinks(pathname, false /* mustExist */) + if err != nil { + return false, err } - return false + kubeletpath := mounter.ne.KubeletPath(hostPath) + return utilfile.FileExists(kubeletpath) } func (mounter *NsenterMounter) CleanSubPaths(podDir string, volumeName string) error { diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index 007a456d490..401e1371f6a 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -83,8 +83,8 @@ func (*NsenterMounter) MakeFile(pathname string) error { return nil } -func (*NsenterMounter) ExistsPath(pathname string) bool { - return true +func (*NsenterMounter) ExistsPath(pathname string) (bool, error) { + return true, errors.New("not implemented") } func (*NsenterMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { diff --git a/pkg/util/removeall/removeall_test.go b/pkg/util/removeall/removeall_test.go index 878db734cdf..1b67cc981f8 100644 --- a/pkg/util/removeall/removeall_test.go +++ b/pkg/util/removeall/removeall_test.go @@ -75,8 +75,8 @@ func (mounter *fakeMounter) MakeFile(pathname string) error { return nil } -func (mounter *fakeMounter) ExistsPath(pathname string) bool { - return true +func (mounter *fakeMounter) ExistsPath(pathname string) (bool, error) { + return true, errors.New("not implemented") } func (mounter *fakeMounter) PrepareSafeSubpath(subPath mount.Subpath) (newHostPath string, cleanupAction func(), err error) { diff --git a/pkg/volume/host_path/host_path.go b/pkg/volume/host_path/host_path.go index 4a3cb76b6af..4bde9891cdf 100644 --- a/pkg/volume/host_path/host_path.go +++ b/pkg/volume/host_path/host_path.go @@ -350,7 +350,8 @@ type fileTypeChecker struct { } func (ftc *fileTypeChecker) Exists() bool { - return ftc.mounter.ExistsPath(ftc.path) + exists, err := ftc.mounter.ExistsPath(ftc.path) + return exists && err == nil } func (ftc *fileTypeChecker) IsFile() bool { diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index b8ae4fa9fc0..39696d765df 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -369,8 +369,8 @@ func (fftc *fakeFileTypeChecker) MakeDir(pathname string) error { return nil } -func (fftc *fakeFileTypeChecker) ExistsPath(pathname string) bool { - return true +func (fftc *fakeFileTypeChecker) ExistsPath(pathname string) (bool, error) { + return true, nil } func (fftc *fakeFileTypeChecker) GetFileType(_ string) (utilmount.FileType, error) { From 7e3fb502a80a005962306302878ec525af2c43f7 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:25 +0200 Subject: [PATCH 034/416] Change SafeMakeDir to resolve symlinks in mounter implementation Kubelet should not resolve symlinks outside of mounter interface. Only mounter interface knows, how to resolve them properly on the host. As consequence, declaration of SafeMakeDir changes to simplify the implementation: from SafeMakeDir(fullPath string, base string, perm os.FileMode) to SafeMakeDir(subdirectoryInBase string, base string, perm os.FileMode) --- pkg/kubelet/kubelet_pods.go | 13 +++++-------- pkg/util/mount/mount.go | 16 +++++++++------- pkg/util/mount/mount_linux.go | 15 ++++++++++++--- pkg/util/mount/mount_windows.go | 10 ++++++++-- pkg/util/mount/nsenter_mount.go | 17 +++++++++++++++-- 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index c1d2c54f090..55dfce9a589 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -174,10 +174,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h return nil, cleanupAction, fmt.Errorf("unable to provision SubPath `%s`: %v", mount.SubPath, err) } - volumePath, err := filepath.EvalSymlinks(hostPath) - if err != nil { - return nil, cleanupAction, err - } + volumePath := hostPath hostPath = filepath.Join(volumePath, mount.SubPath) if subPathExists, err := mounter.ExistsPath(hostPath); err != nil { @@ -192,10 +189,10 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h if err != nil { return nil, cleanupAction, err } - - if err := mounter.SafeMakeDir(hostPath, volumePath, perm); err != nil { - glog.Errorf("failed to mkdir %q: %v", hostPath, err) - return nil, cleanupAction, err + if err := mounter.SafeMakeDir(mount.SubPath, volumePath, perm); err != nil { + // Don't pass detailed error back to the user because it could give information about host filesystem + glog.Errorf("failed to create subPath directory for volumeMount %q of container %q: %v", mount.Name, container.Name, err) + return nil, cleanupAction, fmt.Errorf("failed to create subPath directory for volumeMount %q of container %q", mount.Name, container.Name) } } hostPath, cleanupAction, err = mounter.PrepareSafeSubpath(mountutil.Subpath{ diff --git a/pkg/util/mount/mount.go b/pkg/util/mount/mount.go index 06ee97e5d27..49351394dd8 100644 --- a/pkg/util/mount/mount.go +++ b/pkg/util/mount/mount.go @@ -84,13 +84,15 @@ type Interface interface { // MakeDir creates a new directory. // Will operate in the host mount namespace if kubelet is running in a container MakeDir(pathname string) error - // SafeMakeDir makes sure that the created directory does not escape given - // base directory mis-using symlinks. The directory is created in the same - // mount namespace as where kubelet is running. Note that the function makes - // sure that it creates the directory somewhere under the base, nothing - // else. E.g. if the directory already exists, it may exists outside of the - // base due to symlinks. - SafeMakeDir(pathname string, base string, perm os.FileMode) error + // SafeMakeDir creates subdir within given base. It makes sure that the + // created directory does not escape given base directory mis-using + // symlinks. Note that the function makes sure that it creates the directory + // somewhere under the base, nothing else. E.g. if the directory already + // exists, it may exist outside of the base due to symlinks. + // This method should be used if the directory to create is inside volume + // that's under user control. User must not be able to use symlinks to + // escape the volume to create directories somewhere else. + SafeMakeDir(subdir string, base string, perm os.FileMode) error // Will operate in the host mount namespace if kubelet is running in a container. // Error is returned on any other error than "file not found". ExistsPath(pathname string) (bool, error) diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index a72774ab415..9998792893f 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -955,8 +955,15 @@ func removeEmptyDirs(baseDir, endDir string) error { return nil } -func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { - return doSafeMakeDir(pathname, base, perm) +func (mounter *Mounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error { + realBase, err := filepath.EvalSymlinks(base) + if err != nil { + return fmt.Errorf("error resolving symlinks in %s: %s", base, err) + } + + realFullPath := filepath.Join(realBase, subdir) + + return doSafeMakeDir(realFullPath, realBase, perm) } func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) { @@ -1001,7 +1008,9 @@ func getMode(pathname string) (os.FileMode, error) { return info.Mode(), nil } -// This implementation is shared between Linux and NsEnterMounter +// This implementation is shared between Linux and NsEnterMounter. Both pathname +// and base must be either already resolved symlinks or thet will be resolved in +// kubelet's mount namespace (in case it runs containerized). func doSafeMakeDir(pathname string, base string, perm os.FileMode) error { glog.V(4).Infof("Creating directory %q within base %q", pathname, base) diff --git a/pkg/util/mount/mount_windows.go b/pkg/util/mount/mount_windows.go index 3079b8c65de..64d39a59f7e 100644 --- a/pkg/util/mount/mount_windows.go +++ b/pkg/util/mount/mount_windows.go @@ -472,8 +472,14 @@ func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) { } // SafeMakeDir makes sure that the created directory does not escape given base directory mis-using symlinks. -func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { - return doSafeMakeDir(pathname, base, perm) +func (mounter *Mounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error { + realBase, err := filepath.EvalSymlinks(base) + if err != nil { + return fmt.Errorf("error resolving symlinks in %s: %s", base, err) + } + + realFullPath := filepath.Join(realBase, subdir) + return doSafeMakeDir(realFullPath, realBase, perm) } func doSafeMakeDir(pathname string, base string, perm os.FileMode) error { diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 4a920d63b51..dfc690a64bc 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -327,8 +327,21 @@ func (mounter *NsenterMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath return newHostPath, cleanupAction, err } -func (mounter *NsenterMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error { - return doSafeMakeDir(pathname, base, perm) +func (mounter *NsenterMounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error { + fullSubdirPath := filepath.Join(base, subdir) + evaluatedSubdirPath, err := mounter.ne.EvalSymlinks(fullSubdirPath, false /* mustExist */) + if err != nil { + return fmt.Errorf("error resolving symlinks in %s: %s", fullSubdirPath, err) + } + kubeletSubdirPath := mounter.ne.KubeletPath(evaluatedSubdirPath) + + evaluatedBase, err := mounter.ne.EvalSymlinks(base, true /* mustExist */) + if err != nil { + return fmt.Errorf("error resolving symlinks in %s: %s", base, err) + } + kubeletBase := mounter.ne.KubeletPath(evaluatedBase) + + return doSafeMakeDir(kubeletSubdirPath, kubeletBase, perm) } func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) { From 225a879b07a63288a90cc7cb28a5d500975f2c63 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:25 +0200 Subject: [PATCH 035/416] Refactor doBindSubPath into smaller functions: - getSubpathBindTarget() computes final target of subpath bind-mount. - prepareSubpathTarget() creates target for bind-mount. - safeOpenSubPath() checks symlinks in Subpath and safely opens it. --- pkg/util/mount/mount_linux.go | 147 +++++++++++++++++------------ pkg/util/mount/mount_linux_test.go | 8 -- 2 files changed, 88 insertions(+), 67 deletions(-) diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 9998792893f..0671099b727 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -721,37 +721,109 @@ func getSELinuxSupport(path string, mountInfoFilename string) (bool, error) { func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { newHostPath, err = doBindSubPath(mounter, subPath, os.Getpid()) + // There is no action when the container starts. Bind-mount will be cleaned // when container stops by CleanSubPaths. cleanupAction = nil return newHostPath, cleanupAction, err } +// This implementation is shared between Linux and NsEnterMounter +func safeOpenSubPath(mounter Interface, subpath Subpath) (int, error) { + if !pathWithinBase(subpath.Path, subpath.VolumePath) { + return -1, fmt.Errorf("subpath %q not within volume path %q", subpath.Path, subpath.VolumePath) + } + fd, err := doSafeOpen(subpath.Path, subpath.VolumePath) + if err != nil { + return -1, fmt.Errorf("error opening subpath %v: %v", subpath.Path, err) + } + return fd, nil +} + +// prepareSubpathTarget creates target for bind-mount of subpath. It returns +// "true" when the target already exists and something is mounted there. +func prepareSubpathTarget(mounter Interface, subpath Subpath) (bool, string, error) { + // Early check for already bind-mounted subpath. + bindPathTarget := getSubpathBindTarget(subpath) + notMount, err := IsNotMountPoint(mounter, bindPathTarget) + if err != nil { + if !os.IsNotExist(err) { + return false, "", fmt.Errorf("error checking path %s for mount: %s", bindPathTarget, err) + } + // Ignore ErrorNotExist: the file/directory will be created below if it does not exist yet. + notMount = true + } + if !notMount { + // It's already mounted + glog.V(5).Infof("Skipping bind-mounting subpath %s: already mounted", bindPathTarget) + return true, bindPathTarget, nil + } + + // bindPathTarget is in /var/lib/kubelet and thus reachable without any + // translation even to containerized kubelet. + bindParent := filepath.Dir(bindPathTarget) + err = os.MkdirAll(bindParent, 0750) + if err != nil && !os.IsExist(err) { + return false, "", fmt.Errorf("error creating directory %s: %s", bindParent, err) + } + + t, err := os.Lstat(subpath.Path) + if err != nil { + return false, "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err) + } + + if t.Mode()&os.ModeDir > 0 { + if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) { + return false, "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err) + } + } else { + // "/bin/touch ". + // A file is enough for all possible targets (symlink, device, pipe, + // socket, ...), bind-mounting them into a file correctly changes type + // of the target file. + if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil { + return false, "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err) + } + } + return false, bindPathTarget, nil +} + +func getSubpathBindTarget(subpath Subpath) string { + // containerName is DNS label, i.e. safe as a directory name. + return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex)) +} + // This implementation is shared between Linux and NsEnterMounter // kubeletPid is PID of kubelet in the PID namespace where bind-mount is done, // i.e. pid on the *host* if kubelet runs in a container. func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath string, err error) { - // Check early for symlink. This is just a pre-check to avoid bind-mount - // before the final check. - evalSubPath, err := filepath.EvalSymlinks(subpath.Path) + // Evaluate all symlinks here once for all subsequent functions. + newVolumePath, err := filepath.EvalSymlinks(subpath.VolumePath) if err != nil { - return "", fmt.Errorf("evalSymlinks %q failed: %v", subpath.Path, err) + return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.VolumePath, err) } - glog.V(5).Infof("doBindSubPath %q, full subpath %q for volumepath %q", subpath.Path, evalSubPath, subpath.VolumePath) + newPath, err := filepath.EvalSymlinks(subpath.Path) + if err != nil { + return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.Path, err) + } + glog.V(5).Infof("doBindSubPath %q (%q) for volumepath %q", subpath.Path, newPath, subpath.VolumePath) + subpath.VolumePath = newVolumePath + subpath.Path = newPath - evalSubPath = filepath.Clean(evalSubPath) - if !pathWithinBase(evalSubPath, subpath.VolumePath) { - return "", fmt.Errorf("subpath %q not within volume path %q", evalSubPath, subpath.VolumePath) + // Check the subpath is correct and open it + fd, err := safeOpenSubPath(mounter, subpath) + if err != nil { + return "", err } + defer syscall.Close(fd) - // Prepare directory for bind mounts - // containerName is DNS label, i.e. safe as a directory name. - bindDir := filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName) - err = os.MkdirAll(bindDir, 0750) - if err != nil && !os.IsExist(err) { - return "", fmt.Errorf("error creating directory %s: %s", bindDir, err) + alreadyMounted, bindPathTarget, err := prepareSubpathTarget(mounter, subpath) + if err != nil { + return "", err + } + if alreadyMounted { + return bindPathTarget, nil } - bindPathTarget := filepath.Join(bindDir, strconv.Itoa(subpath.VolumeMountIndex)) success := false defer func() { @@ -764,49 +836,6 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath } }() - // Check it's not already bind-mounted - notMount, err := IsNotMountPoint(mounter, bindPathTarget) - if err != nil { - if !os.IsNotExist(err) { - return "", fmt.Errorf("error checking path %s for mount: %s", bindPathTarget, err) - } - // Ignore ErrorNotExist: the file/directory will be created below if it does not exist yet. - notMount = true - } - if !notMount { - // It's already mounted - glog.V(5).Infof("Skipping bind-mounting subpath %s: already mounted", bindPathTarget) - success = true - return bindPathTarget, nil - } - - // Create target of the bind mount. A directory for directories, empty file - // for everything else. - t, err := os.Lstat(subpath.Path) - if err != nil { - return "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err) - } - if t.Mode()&os.ModeDir > 0 { - if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) { - return "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err) - } - } else { - // "/bin/touch ". - // A file is enough for all possible targets (symlink, device, pipe, - // socket, ...), bind-mounting them into a file correctly changes type - // of the target file. - if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil { - return "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err) - } - } - - // Safe open subpath and get the fd - fd, err := doSafeOpen(evalSubPath, subpath.VolumePath) - if err != nil { - return "", fmt.Errorf("error opening subpath %v: %v", evalSubPath, err) - } - defer syscall.Close(fd) - mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd) // Do the bind mount @@ -819,8 +848,8 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil { return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err) } - success = true + glog.V(3).Infof("Bound SubPath %s into %s", subpath.Path, bindPathTarget) return bindPathTarget, nil } diff --git a/pkg/util/mount/mount_linux_test.go b/pkg/util/mount/mount_linux_test.go index 6028d608e97..86fb25597c6 100644 --- a/pkg/util/mount/mount_linux_test.go +++ b/pkg/util/mount/mount_linux_test.go @@ -1193,10 +1193,6 @@ func TestBindSubPath(t *testing.T) { return nil, "", "", err } - if err := os.MkdirAll(subpathMount, defaultPerm); err != nil { - return nil, "", "", err - } - socketFile, socketCreateError := createSocketFile(volpath) return mounts, volpath, socketFile, socketCreateError @@ -1212,10 +1208,6 @@ func TestBindSubPath(t *testing.T) { return nil, "", "", err } - if err := os.MkdirAll(subpathMount, defaultPerm); err != nil { - return nil, "", "", err - } - testFifo := filepath.Join(volpath, "mount_test.fifo") err := syscall.Mkfifo(testFifo, 0) return mounts, volpath, testFifo, err From 9f80de3772b41553770f1d0654d3e291a5bec50c Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Wed, 23 May 2018 10:15:12 +0200 Subject: [PATCH 036/416] Split NsEnterMounter and Mounter implementation of doBindSubpath nsenter implementation needs to mount different thing in the end and do different checks on the result. --- pkg/util/mount/BUILD | 1 + pkg/util/mount/mount_linux.go | 22 ++-- pkg/util/mount/mount_linux_test.go | 2 +- pkg/util/mount/nsenter_mount.go | 123 +++++++++++++++------ pkg/util/mount/nsenter_mount_test.go | 156 ++++++++++----------------- 5 files changed, 167 insertions(+), 137 deletions(-) diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 6d2e65cbc96..a12f1397bf5 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -106,6 +106,7 @@ go_test( ] + select({ "@io_bazel_rules_go//go/platform:linux": [ "//vendor/github.com/golang/glog:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], "@io_bazel_rules_go//go/platform:windows": [ diff --git a/pkg/util/mount/mount_linux.go b/pkg/util/mount/mount_linux.go index 0671099b727..27bb91249ce 100644 --- a/pkg/util/mount/mount_linux.go +++ b/pkg/util/mount/mount_linux.go @@ -720,7 +720,7 @@ func getSELinuxSupport(path string, mountInfoFilename string) (bool, error) { } func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { - newHostPath, err = doBindSubPath(mounter, subPath, os.Getpid()) + newHostPath, err = doBindSubPath(mounter, subPath) // There is no action when the container starts. Bind-mount will be cleaned // when container stops by CleanSubPaths. @@ -742,6 +742,11 @@ func safeOpenSubPath(mounter Interface, subpath Subpath) (int, error) { // prepareSubpathTarget creates target for bind-mount of subpath. It returns // "true" when the target already exists and something is mounted there. +// Given Subpath must have all paths with already resolved symlinks and with +// paths relevant to kubelet (when it runs in a container). +// This function is called also by NsEnterMounter. It works because +// /var/lib/kubelet is mounted from the host into the container with Kubelet as +// /var/lib/kubelet too. func prepareSubpathTarget(mounter Interface, subpath Subpath) (bool, string, error) { // Early check for already bind-mounted subpath. bindPathTarget := getSubpathBindTarget(subpath) @@ -793,10 +798,12 @@ func getSubpathBindTarget(subpath Subpath) string { return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex)) } -// This implementation is shared between Linux and NsEnterMounter -// kubeletPid is PID of kubelet in the PID namespace where bind-mount is done, -// i.e. pid on the *host* if kubelet runs in a container. -func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath string, err error) { +func doBindSubPath(mounter Interface, subpath Subpath) (hostPath string, err error) { + // Linux, kubelet runs on the host: + // - safely open the subpath + // - bind-mount /proc//fd/ to subpath target + // User can't change /proc//fd/ to point to a bad place. + // Evaluate all symlinks here once for all subsequent functions. newVolumePath, err := filepath.EvalSymlinks(subpath.VolumePath) if err != nil { @@ -810,7 +817,6 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath subpath.VolumePath = newVolumePath subpath.Path = newPath - // Check the subpath is correct and open it fd, err := safeOpenSubPath(mounter, subpath) if err != nil { return "", err @@ -836,6 +842,7 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath } }() + kubeletPid := os.Getpid() mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd) // Do the bind mount @@ -1193,6 +1200,9 @@ func findExistingPrefix(base, pathname string) (string, []string, error) { // Symlinks are disallowed (pathname must already resolve symlinks), // and the path must be within the base directory. func doSafeOpen(pathname string, base string) (int, error) { + pathname = filepath.Clean(pathname) + base = filepath.Clean(base) + // Calculate segments to follow subpath, err := filepath.Rel(base, pathname) if err != nil { diff --git a/pkg/util/mount/mount_linux_test.go b/pkg/util/mount/mount_linux_test.go index 86fb25597c6..36972c67c29 100644 --- a/pkg/util/mount/mount_linux_test.go +++ b/pkg/util/mount/mount_linux_test.go @@ -1291,7 +1291,7 @@ func TestBindSubPath(t *testing.T) { } _, subpathMount := getTestPaths(base) - bindPathTarget, err := doBindSubPath(fm, subpath, 1) + bindPathTarget, err := doBindSubPath(fm, subpath) if test.expectError { if err == nil { t.Errorf("test %q failed: expected error, got success", test.name) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index dfc690a64bc..e1e02b7d82b 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -22,13 +22,12 @@ import ( "fmt" "os" "path/filepath" - "regexp" - "strconv" "strings" + "syscall" "github.com/golang/glog" + "golang.org/x/sys/unix" utilfile "k8s.io/kubernetes/pkg/util/file" - utilio "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/util/nsenter" ) @@ -37,13 +36,6 @@ const ( hostProcMountsPath = "/rootfs/proc/1/mounts" // hostProcMountinfoPath is the default mount info path for rootfs hostProcMountinfoPath = "/rootfs/proc/1/mountinfo" - // hostProcSelfStatusPath is the default path to /proc/self/status on the host - hostProcSelfStatusPath = "/rootfs/proc/self/status" -) - -var ( - // pidRegExp matches "Pid: " in /proc/self/status - pidRegExp = regexp.MustCompile(`\nPid:\t([0-9]*)\n`) ) // Currently, all docker containers receive their own mount namespaces. @@ -297,29 +289,9 @@ func (mounter *NsenterMounter) CleanSubPaths(podDir string, volumeName string) e return doCleanSubPaths(mounter, podDir, volumeName) } -// getPidOnHost returns kubelet's pid in the host pid namespace -func (mounter *NsenterMounter) getPidOnHost(procStatusPath string) (int, error) { - // Get the PID from /rootfs/proc/self/status - statusBytes, err := utilio.ConsistentRead(procStatusPath, maxListTries) - if err != nil { - return 0, fmt.Errorf("error reading %s: %s", procStatusPath, err) - } - matches := pidRegExp.FindSubmatch(statusBytes) - if len(matches) < 2 { - return 0, fmt.Errorf("cannot parse %s: no Pid:", procStatusPath) - } - return strconv.Atoi(string(matches[1])) -} - func (mounter *NsenterMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) { - hostPid, err := mounter.getPidOnHost(hostProcSelfStatusPath) - if err != nil { - return "", nil, err - } - glog.V(4).Infof("Kubelet's PID on the host is %d", hostPid) - // Bind-mount the subpath to avoid using symlinks in subpaths. - newHostPath, err = doBindSubPath(mounter, subPath, hostPid) + newHostPath, err = doNsEnterBindSubPath(mounter, subPath) // There is no action when the container starts. Bind-mount will be cleaned // when container stops by CleanSubPaths. @@ -352,6 +324,95 @@ func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) { return getMountRefsByDev(mounter, hostpath) } +func doNsEnterBindSubPath(mounter *NsenterMounter, subpath Subpath) (hostPath string, err error) { + // Linux, kubelet runs in a container: + // - safely open the subpath + // - bind-mount the subpath to target (this can be unsafe) + // - check that we mounted the right thing by comparing device ID and inode + // of the subpath (via safely opened fd) and the target (that's under our + // control) + + // Evaluate all symlinks here once for all subsequent functions. + evaluatedHostVolumePath, err := mounter.ne.EvalSymlinks(subpath.VolumePath, true /*mustExist*/) + if err != nil { + return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.VolumePath, err) + } + evaluatedHostSubpath, err := mounter.ne.EvalSymlinks(subpath.Path, true /*mustExist*/) + if err != nil { + return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.Path, err) + } + glog.V(5).Infof("doBindSubPath %q (%q) for volumepath %q", subpath.Path, evaluatedHostSubpath, subpath.VolumePath) + subpath.VolumePath = mounter.ne.KubeletPath(evaluatedHostVolumePath) + subpath.Path = mounter.ne.KubeletPath(evaluatedHostSubpath) + + // Check the subpath is correct and open it + fd, err := safeOpenSubPath(mounter, subpath) + if err != nil { + return "", err + } + defer syscall.Close(fd) + + alreadyMounted, bindPathTarget, err := prepareSubpathTarget(mounter, subpath) + if err != nil { + return "", err + } + if alreadyMounted { + return bindPathTarget, nil + } + + success := false + defer func() { + // Cleanup subpath on error + if !success { + glog.V(4).Infof("doNsEnterBindSubPath() failed for %q, cleaning up subpath", bindPathTarget) + if cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil { + glog.Errorf("Failed to clean subpath %q: %v", bindPathTarget, cleanErr) + } + } + }() + + // Leap of faith: optimistically expect that nobody has modified previously + // expanded evalSubPath with evil symlinks and bind-mount it. + // Mount is done on the host! don't use kubelet path! + glog.V(5).Infof("bind mounting %q at %q", evaluatedHostSubpath, bindPathTarget) + if err = mounter.Mount(evaluatedHostSubpath, bindPathTarget, "" /*fstype*/, []string{"bind"}); err != nil { + return "", fmt.Errorf("error mounting %s: %s", evaluatedHostSubpath, err) + } + + // Check that the bind-mount target is the same inode and device as the + // source that we keept open, i.e. we mounted the right thing. + err = checkDeviceInode(fd, bindPathTarget) + if err != nil { + return "", fmt.Errorf("error checking bind mount for subpath %s: %s", subpath.VolumePath, err) + } + + success = true + glog.V(3).Infof("Bound SubPath %s into %s", subpath.Path, bindPathTarget) + return bindPathTarget, nil +} + +// checkDeviceInode checks that opened file and path represent the same file. +func checkDeviceInode(fd int, path string) error { + var srcStat, dstStat unix.Stat_t + err := unix.Fstat(fd, &srcStat) + if err != nil { + return fmt.Errorf("error running fstat on subpath FD: %v", err) + } + + err = unix.Stat(path, &dstStat) + if err != nil { + return fmt.Errorf("error running fstat on %s: %v", path, err) + } + + if srcStat.Dev != dstStat.Dev { + return fmt.Errorf("different device number") + } + if srcStat.Ino != dstStat.Ino { + return fmt.Errorf("different inode") + } + return nil +} + func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { hostPath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */) if err != nil { diff --git a/pkg/util/mount/nsenter_mount_test.go b/pkg/util/mount/nsenter_mount_test.go index 2aee4ad5f9d..2edb6febf97 100644 --- a/pkg/util/mount/nsenter_mount_test.go +++ b/pkg/util/mount/nsenter_mount_test.go @@ -21,9 +21,11 @@ package mount import ( "io/ioutil" "os" - "path" - "strconv" + "path/filepath" + "strings" "testing" + + "golang.org/x/sys/unix" ) func TestParseFindMnt(t *testing.T) { @@ -72,120 +74,76 @@ func TestParseFindMnt(t *testing.T) { } } -func TestGetPidOnHost(t *testing.T) { - tempDir, err := ioutil.TempDir("", "get_pid_on_host_tests") +func TestCheckDeviceInode(t *testing.T) { + testDir, err := ioutil.TempDir("", "nsenter-mounter-device-") if err != nil { - t.Fatalf(err.Error()) + t.Fatalf("Cannot create temporary directory: %s", err) } - defer os.RemoveAll(tempDir) + defer os.RemoveAll(testDir) tests := []struct { name string - procFile string - expectedPid int - expectError bool + srcPath string + dstPath string + expectError string }{ { - name: "valid status file", - procFile: `Name: cat -Umask: 0002 -State: R (running) -Tgid: 15041 -Ngid: 0 -Pid: 15041 -PPid: 22699 -TracerPid: 0 -Uid: 1000 1000 1000 1000 -Gid: 1000 1000 1000 1000 -FDSize: 256 -Groups: 10 135 156 157 158 973 984 1000 1001 -NStgid: 15041 -NSpid: 15041 -NSpgid: 15041 -NSsid: 22699 -VmPeak: 115016 kB -VmSize: 115016 kB -VmLck: 0 kB -VmPin: 0 kB -VmHWM: 816 kB -VmRSS: 816 kB -RssAnon: 64 kB -RssFile: 752 kB -RssShmem: 0 kB -VmData: 312 kB -VmStk: 136 kB -VmExe: 32 kB -VmLib: 2060 kB -VmPTE: 44 kB -VmPMD: 12 kB -VmSwap: 0 kB -HugetlbPages: 0 kB -Threads: 1 -SigQ: 2/60752 -SigPnd: 0000000000000000 -ShdPnd: 0000000000000000 -SigBlk: 0000000000000000 -SigIgn: 0000000000000000 -SigCgt: 0000000000000000 -CapInh: 0000000000000000 -CapPrm: 0000000000000000 -CapEff: 0000000000000000 -CapBnd: 0000003fffffffff -CapAmb: 0000000000000000 -NoNewPrivs: 0 -Seccomp: 0 -Cpus_allowed: ff -Cpus_allowed_list: 0-7 -Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001 -Mems_allowed_list: 0 -voluntary_ctxt_switches: 0 -nonvoluntary_ctxt_switches: 0 -`, - expectedPid: 15041, + name: "the same file", + srcPath: filepath.Join(testDir, "1"), + dstPath: filepath.Join(testDir, "1"), + expectError: "", }, { - name: "no Pid:", - procFile: `Name: cat -Umask: 0002 -State: R (running) -Tgid: 15041 -Ngid: 0 -PPid: 22699 -`, - expectedPid: 0, - expectError: true, + name: "different file on the same FS", + srcPath: filepath.Join(testDir, "2.1"), + dstPath: filepath.Join(testDir, "2.2"), + expectError: "different inode", }, { - name: "invalid Pid:", - procFile: `Name: cat -Umask: 0002 -State: R (running) -Tgid: 15041 -Ngid: 0 -Pid: invalid -PPid: 22699 -`, - expectedPid: 0, - expectError: true, + name: "different file on different device", + srcPath: filepath.Join(testDir, "3"), + // /proc is always on a different "device" than /tmp (or $TEMP) + dstPath: "/proc/self/status", + expectError: "different device", }, } - for i, test := range tests { - filename := path.Join(tempDir, strconv.Itoa(i)) - err := ioutil.WriteFile(filename, []byte(test.procFile), 0666) + for _, test := range tests { + if err := ioutil.WriteFile(test.srcPath, []byte{}, 0644); err != nil { + t.Errorf("Test %q: cannot create srcPath %s: %s", test.name, test.srcPath, err) + continue + } + + // Don't create dst if it exists + if _, err := os.Stat(test.dstPath); os.IsNotExist(err) { + if err := ioutil.WriteFile(test.dstPath, []byte{}, 0644); err != nil { + t.Errorf("Test %q: cannot create dstPath %s: %s", test.name, test.dstPath, err) + continue + } + } else if err != nil { + t.Errorf("Test %q: cannot check existence of dstPath %s: %s", test.name, test.dstPath, err) + continue + } + + fd, err := unix.Open(test.srcPath, unix.O_CREAT, 0644) if err != nil { - t.Fatalf(err.Error()) + t.Errorf("Test %q: cannot open srcPath %s: %s", test.name, test.srcPath, err) + continue } - mounter := NsenterMounter{} - pid, err := mounter.getPidOnHost(filename) - if err != nil && !test.expectError { - t.Errorf("Test %q: unexpected error: %s", test.name, err) + + err = checkDeviceInode(fd, test.dstPath) + + if test.expectError == "" && err != nil { + t.Errorf("Test %q: expected no error, got %s", test.name, err) } - if err == nil && test.expectError { - t.Errorf("Test %q: expected error, got none", test.name) - } - if pid != test.expectedPid { - t.Errorf("Test %q: expected pid %d, got %d", test.name, test.expectedPid, pid) + if test.expectError != "" { + if err == nil { + t.Errorf("Test %q: expected error, got none", test.name) + } else { + if !strings.Contains(err.Error(), test.expectError) { + t.Errorf("Test %q: expected error %q, got %q", test.name, test.expectError, err) + } + } } } } From a8a37fb714da452258f2ce84bbb51c73bb2f1ac8 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:25 +0200 Subject: [PATCH 037/416] Created directories in /var/lib/kubelet directly. --- cmd/kubelet/app/server.go | 2 +- pkg/util/mount/nsenter_mount.go | 26 ++++++++++++++++++--- pkg/util/mount/nsenter_mount_unsupported.go | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 10bbae78cfc..761cd49df32 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -360,7 +360,7 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err var writer kubeio.Writer = &kubeio.StdWriter{} if s.Containerized { glog.V(2).Info("Running kubelet in containerized mode") - mounter, err = mount.NewNsenterMounter() + mounter, err = mount.NewNsenterMounter(s.RootDirectory) if err != nil { return nil, err } diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index e1e02b7d82b..71660405f5e 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -43,9 +43,15 @@ const ( // the host's mount namespace. type NsenterMounter struct { ne *nsenter.Nsenter + // rootDir is location of /var/lib/kubelet directory. + rootDir string } -func NewNsenterMounter() (*NsenterMounter, error) { +// NewNsenterMounter creates a new mounter for kubelet that runs as a container. +// rootDir is location of /var/lib/kubelet directory (in case it's not on the +// default place). This directory must be available in the container +// on the same place as it's on the host. +func NewNsenterMounter(rootDir string) (*NsenterMounter, error) { ne, err := nsenter.NewNsenter() if err != nil { return nil, err @@ -305,14 +311,28 @@ func (mounter *NsenterMounter) SafeMakeDir(subdir string, base string, perm os.F if err != nil { return fmt.Errorf("error resolving symlinks in %s: %s", fullSubdirPath, err) } - kubeletSubdirPath := mounter.ne.KubeletPath(evaluatedSubdirPath) + evaluatedSubdirPath = filepath.Clean(evaluatedSubdirPath) evaluatedBase, err := mounter.ne.EvalSymlinks(base, true /* mustExist */) if err != nil { return fmt.Errorf("error resolving symlinks in %s: %s", base, err) } - kubeletBase := mounter.ne.KubeletPath(evaluatedBase) + evaluatedBase = filepath.Clean(evaluatedBase) + rootDir := filepath.Clean(mounter.rootDir) + if pathWithinBase(evaluatedBase, rootDir) { + // Base is in /var/lib/kubelet. This directory is shared between the + // container with kubelet and the host. We don't need to add '/rootfs'. + // This is useful when /rootfs is mounted as read-only - we can still + // create subpaths for paths in /var/lib/kubelet. + return doSafeMakeDir(evaluatedSubdirPath, evaluatedBase, perm) + } + + // Base is somewhere on the host's filesystem. Add /rootfs and try to make + // the directory there. + // This requires /rootfs to be writable. + kubeletSubdirPath := mounter.ne.KubeletPath(evaluatedSubdirPath) + kubeletBase := mounter.ne.KubeletPath(evaluatedBase) return doSafeMakeDir(kubeletSubdirPath, kubeletBase, perm) } diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index 401e1371f6a..ef5cb5a107d 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -25,7 +25,7 @@ import ( type NsenterMounter struct{} -func NewNsenterMounter() (*NsenterMounter, error) { +func NewNsenterMounter(rootDir string) (*NsenterMounter, error) { return &NsenterMounter{}, nil } From 9b741254400e68e79feb5d28250e966c01c9d34b Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:26 +0200 Subject: [PATCH 038/416] Pass Nsenter to NsenterMounter and NsenterWriter So Nsenter is initialized only once and with the right parameters. --- cmd/kubelet/app/BUILD | 2 ++ cmd/kubelet/app/server.go | 7 ++++-- pkg/util/io/writer.go | 20 +++++++++------ pkg/util/mount/BUILD | 28 +++++++++++++++++++++ pkg/util/mount/nsenter_mount.go | 12 +++------ pkg/util/mount/nsenter_mount_unsupported.go | 6 +++-- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index af2fcc09ad5..b2f3efbc793 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -110,6 +110,7 @@ go_library( "//pkg/util/io:go_default_library", "//pkg/util/mount:go_default_library", "//pkg/util/node:go_default_library", + "//pkg/util/nsenter:go_default_library", "//pkg/util/oom:go_default_library", "//pkg/util/rlimit:go_default_library", "//pkg/version:go_default_library", @@ -170,6 +171,7 @@ go_library( "//vendor/k8s.io/client-go/tools/record:go_default_library", "//vendor/k8s.io/client-go/util/cert:go_default_library", "//vendor/k8s.io/client-go/util/certificate:go_default_library", + "//vendor/k8s.io/utils/exec:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:linux": [ "//vendor/golang.org/x/exp/inotify:go_default_library", diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 761cd49df32..f4bd1a05050 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -91,10 +91,12 @@ import ( kubeio "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/util/mount" nodeutil "k8s.io/kubernetes/pkg/util/node" + "k8s.io/kubernetes/pkg/util/nsenter" "k8s.io/kubernetes/pkg/util/oom" "k8s.io/kubernetes/pkg/util/rlimit" "k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/pkg/version/verflag" + "k8s.io/utils/exec" ) const ( @@ -360,11 +362,12 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err var writer kubeio.Writer = &kubeio.StdWriter{} if s.Containerized { glog.V(2).Info("Running kubelet in containerized mode") - mounter, err = mount.NewNsenterMounter(s.RootDirectory) + ne, err := nsenter.NewNsenter(nsenter.DefaultHostRootFsPath, exec.New()) if err != nil { return nil, err } - writer = &kubeio.NsenterWriter{} + mounter = mount.NewNsenterMounter(s.RootDirectory, ne) + writer = kubeio.NewNsenterWriter(ne) } var dockerClientConfig *dockershim.ClientConfig diff --git a/pkg/util/io/writer.go b/pkg/util/io/writer.go index 4c0c1c4b37c..7c457d04a7d 100644 --- a/pkg/util/io/writer.go +++ b/pkg/util/io/writer.go @@ -50,18 +50,24 @@ func (writer *StdWriter) WriteFile(filename string, data []byte, perm os.FileMod // it will not see the mounted device in its own namespace. To work around this // limitation one has to first enter hosts namespace (by using 'nsenter') and // only then write data. -type NsenterWriter struct{} +type NsenterWriter struct { + ne *nsenter.Nsenter +} + +// NewNsenterWriter creates a new Writer that allows writing data to file using +// nsenter command. +func NewNsenterWriter(ne *nsenter.Nsenter) *NsenterWriter { + return &NsenterWriter{ + ne: ne, + } +} // WriteFile calls 'nsenter cat - > ' and 'nsenter chmod' to create a // file on the host. func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error { - ne, err := nsenter.NewNsenter() - if err != nil { - return err - } echoArgs := []string{"-c", fmt.Sprintf("cat > %s", filename)} glog.V(5).Infof("nsenter: write data to file %s by nsenter", filename) - command := ne.Exec("sh", echoArgs) + command := writer.ne.Exec("sh", echoArgs) command.SetStdin(bytes.NewBuffer(data)) outputBytes, err := command.CombinedOutput() if err != nil { @@ -71,7 +77,7 @@ func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.Fil chmodArgs := []string{fmt.Sprintf("%o", perm), filename} glog.V(5).Infof("nsenter: change permissions of file %s to %s", filename, chmodArgs[0]) - outputBytes, err = ne.Exec("chmod", chmodArgs).CombinedOutput() + outputBytes, err = writer.ne.Exec("chmod", chmodArgs).CombinedOutput() if err != nil { glog.Errorf("Output from chmod command: %v", string(outputBytes)) return err diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index a12f1397bf5..40778e7c73b 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -71,6 +71,18 @@ go_library( "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ] + select({ + "@io_bazel_rules_go//go/platform:android": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "//pkg/util/nsenter:go_default_library", + ], "@io_bazel_rules_go//go/platform:linux": [ "//pkg/util/file:go_default_library", "//pkg/util/io:go_default_library", @@ -78,8 +90,24 @@ go_library( "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", ], + "@io_bazel_rules_go//go/platform:nacl": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "//pkg/util/nsenter:go_default_library", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "//pkg/util/nsenter:go_default_library", + ], "@io_bazel_rules_go//go/platform:windows": [ "//pkg/util/file:go_default_library", + "//pkg/util/nsenter:go_default_library", ], "//conditions:default": [], }), diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 71660405f5e..19ba068ba39 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -48,15 +48,11 @@ type NsenterMounter struct { } // NewNsenterMounter creates a new mounter for kubelet that runs as a container. -// rootDir is location of /var/lib/kubelet directory (in case it's not on the -// default place). This directory must be available in the container -// on the same place as it's on the host. -func NewNsenterMounter(rootDir string) (*NsenterMounter, error) { - ne, err := nsenter.NewNsenter() - if err != nil { - return nil, err +func NewNsenterMounter(rootDir string, ne *nsenter.Nsenter) *NsenterMounter { + return &NsenterMounter{ + rootDir: rootDir, + ne: ne, } - return &NsenterMounter{ne: ne}, nil } // NsenterMounter implements mount.Interface diff --git a/pkg/util/mount/nsenter_mount_unsupported.go b/pkg/util/mount/nsenter_mount_unsupported.go index ef5cb5a107d..f417ba9bc15 100644 --- a/pkg/util/mount/nsenter_mount_unsupported.go +++ b/pkg/util/mount/nsenter_mount_unsupported.go @@ -21,12 +21,14 @@ package mount import ( "errors" "os" + + "k8s.io/kubernetes/pkg/util/nsenter" ) type NsenterMounter struct{} -func NewNsenterMounter(rootDir string) (*NsenterMounter, error) { - return &NsenterMounter{}, nil +func NewNsenterMounter(rootDir string, ne *nsenter.Nsenter) *NsenterMounter { + return &NsenterMounter{} } var _ = Interface(&NsenterMounter{}) From cb5eb25ec197e6bb97c4044effa5e2489b889651 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 22 May 2018 12:56:26 +0200 Subject: [PATCH 039/416] Nsenter unit tests --- pkg/util/mount/BUILD | 1 + pkg/util/mount/nsenter_mount_test.go | 560 ++++++++++++++++++++++++ pkg/util/nsenter/BUILD | 19 +- pkg/util/nsenter/nsenter.go | 121 ++++- pkg/util/nsenter/nsenter_test.go | 311 +++++++++++++ pkg/util/nsenter/nsenter_unsupported.go | 8 +- 6 files changed, 995 insertions(+), 25 deletions(-) create mode 100644 pkg/util/nsenter/nsenter_test.go diff --git a/pkg/util/mount/BUILD b/pkg/util/mount/BUILD index 40778e7c73b..6f2df9acae8 100644 --- a/pkg/util/mount/BUILD +++ b/pkg/util/mount/BUILD @@ -133,6 +133,7 @@ go_test( "//vendor/k8s.io/utils/exec/testing:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:linux": [ + "//pkg/util/nsenter:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", diff --git a/pkg/util/mount/nsenter_mount_test.go b/pkg/util/mount/nsenter_mount_test.go index 2edb6febf97..c541a4cdf74 100644 --- a/pkg/util/mount/nsenter_mount_test.go +++ b/pkg/util/mount/nsenter_mount_test.go @@ -26,6 +26,7 @@ import ( "testing" "golang.org/x/sys/unix" + "k8s.io/kubernetes/pkg/util/nsenter" ) func TestParseFindMnt(t *testing.T) { @@ -147,3 +148,562 @@ func TestCheckDeviceInode(t *testing.T) { } } } + +func newFakeNsenterMounter(tmpdir string, t *testing.T) (mounter *NsenterMounter, rootfsPath string, varlibPath string, err error) { + rootfsPath = filepath.Join(tmpdir, "rootfs") + if err := os.Mkdir(rootfsPath, 0755); err != nil { + return nil, "", "", err + } + ne, err := nsenter.NewFakeNsenter(rootfsPath) + if err != nil { + return nil, "", "", err + } + + varlibPath = filepath.Join(tmpdir, "/var/lib/kubelet") + if err := os.MkdirAll(varlibPath, 0755); err != nil { + return nil, "", "", err + } + + return NewNsenterMounter(varlibPath, ne), rootfsPath, varlibPath, nil +} + +func TestNsenterExistsFile(t *testing.T) { + tests := []struct { + name string + prepare func(base, rootfs string) (string, error) + expectedOutput bool + expectError bool + }{ + { + name: "simple existing file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/file + path := filepath.Join(base, "file") + if err := ioutil.WriteFile(path, []byte{}, 0644); err != nil { + return "", err + } + // In kubelet: /rootfs/base/file + if _, err := writeRootfsFile(rootfs, path, 0644); err != nil { + return "", err + } + return path, nil + }, + expectedOutput: true, + }, + { + name: "simple non-existing file", + prepare: func(base, rootfs string) (string, error) { + path := filepath.Join(base, "file") + return path, nil + }, + expectedOutput: false, + }, + { + name: "simple non-accessible file", + prepare: func(base, rootfs string) (string, error) { + // On the host: + // create /base/dir/file, then make the dir inaccessible + dir := filepath.Join(base, "dir") + if err := os.MkdirAll(dir, 0755); err != nil { + return "", err + } + path := filepath.Join(dir, "file") + if err := ioutil.WriteFile(path, []byte{}, 0); err != nil { + return "", err + } + if err := os.Chmod(dir, 0644); err != nil { + return "", err + } + + // In kubelet: do the same with /rootfs/base/dir/file + rootfsPath, err := writeRootfsFile(rootfs, path, 0777) + if err != nil { + return "", err + } + rootfsDir := filepath.Dir(rootfsPath) + if err := os.Chmod(rootfsDir, 0644); err != nil { + return "", err + } + + return path, nil + }, + expectedOutput: false, + expectError: true, + }, + { + name: "relative symlink to existing file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/link -> file + file := filepath.Join(base, "file") + if err := ioutil.WriteFile(file, []byte{}, 0); err != nil { + return "", err + } + path := filepath.Join(base, "link") + if err := os.Symlink("file", path); err != nil { + return "", err + } + // In kubelet: /rootfs/base/file + if _, err := writeRootfsFile(rootfs, file, 0644); err != nil { + return "", err + } + return path, nil + }, + expectedOutput: true, + }, + { + name: "absolute symlink to existing file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/link -> /base/file + file := filepath.Join(base, "file") + if err := ioutil.WriteFile(file, []byte{}, 0); err != nil { + return "", err + } + path := filepath.Join(base, "link") + if err := os.Symlink(file, path); err != nil { + return "", err + } + // In kubelet: /rootfs/base/file + if _, err := writeRootfsFile(rootfs, file, 0644); err != nil { + return "", err + } + + return path, nil + }, + expectedOutput: true, + }, + { + name: "relative symlink to non-existing file", + prepare: func(base, rootfs string) (string, error) { + path := filepath.Join(base, "link") + if err := os.Symlink("file", path); err != nil { + return "", err + } + return path, nil + }, + expectedOutput: false, + }, + { + name: "absolute symlink to non-existing file", + prepare: func(base, rootfs string) (string, error) { + file := filepath.Join(base, "file") + path := filepath.Join(base, "link") + if err := os.Symlink(file, path); err != nil { + return "", err + } + return path, nil + }, + expectedOutput: false, + }, + { + name: "symlink loop", + prepare: func(base, rootfs string) (string, error) { + path := filepath.Join(base, "link") + if err := os.Symlink(path, path); err != nil { + return "", err + } + return path, nil + }, + expectedOutput: false, + // TODO: realpath -m is not able to detect symlink loop. Should we care? + expectError: false, + }, + } + + for _, test := range tests { + tmpdir, err := ioutil.TempDir("", "nsenter-exists-file") + if err != nil { + t.Error(err) + continue + } + defer os.RemoveAll(tmpdir) + + testBase := filepath.Join(tmpdir, "base") + if err := os.Mkdir(testBase, 0755); err != nil { + t.Error(err) + continue + } + + mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t) + if err != nil { + t.Error(err) + continue + } + + path, err := test.prepare(testBase, rootfs) + if err != nil { + t.Error(err) + continue + } + + out, err := mounter.ExistsPath(path) + if err != nil && !test.expectError { + t.Errorf("Test %q: unexpected error: %s", test.name, err) + } + if err == nil && test.expectError { + t.Errorf("Test %q: expected error, got none", test.name) + } + + if out != test.expectedOutput { + t.Errorf("Test %q: expected return value %v, got %v", test.name, test.expectedOutput, out) + } + } +} + +func TestNsenterGetMode(t *testing.T) { + tests := []struct { + name string + prepare func(base, rootfs string) (string, error) + expectedMode os.FileMode + expectError bool + }{ + { + name: "simple file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/file + path := filepath.Join(base, "file") + if err := ioutil.WriteFile(path, []byte{}, 0644); err != nil { + return "", err + } + + // Prepare a different file as /rootfs/base/file (="the host + // visible from container") to check that NsEnterMounter calls + // stat on this file and not on /base/file. + // Visible from kubelet: /rootfs/base/file + if _, err := writeRootfsFile(rootfs, path, 0777); err != nil { + return "", err + } + + return path, nil + }, + expectedMode: 0777, + }, + { + name: "non-existing file", + prepare: func(base, rootfs string) (string, error) { + path := filepath.Join(base, "file") + return path, nil + }, + expectedMode: 0, + expectError: true, + }, + { + name: "absolute symlink to existing file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/link -> /base/file + file := filepath.Join(base, "file") + if err := ioutil.WriteFile(file, []byte{}, 0644); err != nil { + return "", err + } + path := filepath.Join(base, "link") + if err := os.Symlink(file, path); err != nil { + return "", err + } + + // Visible from kubelet: + // /rootfs/base/file + if _, err := writeRootfsFile(rootfs, file, 0747); err != nil { + return "", err + } + + return path, nil + }, + expectedMode: 0747, + }, + { + name: "relative symlink to existing file", + prepare: func(base, rootfs string) (string, error) { + // On the host: /base/link -> file + file := filepath.Join(base, "file") + if err := ioutil.WriteFile(file, []byte{}, 0741); err != nil { + return "", err + } + path := filepath.Join(base, "link") + if err := os.Symlink("file", path); err != nil { + return "", err + } + + // Visible from kubelet: + // /rootfs/base/file + if _, err := writeRootfsFile(rootfs, file, 0647); err != nil { + return "", err + } + + return path, nil + }, + expectedMode: 0647, + }, + } + + for _, test := range tests { + tmpdir, err := ioutil.TempDir("", "nsenter-get-mode-") + if err != nil { + t.Error(err) + continue + } + defer os.RemoveAll(tmpdir) + + testBase := filepath.Join(tmpdir, "base") + if err := os.Mkdir(testBase, 0755); err != nil { + t.Error(err) + continue + } + + mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t) + if err != nil { + t.Error(err) + continue + } + + path, err := test.prepare(testBase, rootfs) + if err != nil { + t.Error(err) + continue + } + + mode, err := mounter.GetMode(path) + if err != nil && !test.expectError { + t.Errorf("Test %q: unexpected error: %s", test.name, err) + } + if err == nil && test.expectError { + t.Errorf("Test %q: expected error, got none", test.name) + } + + if mode != test.expectedMode { + t.Errorf("Test %q: expected return value %v, got %v", test.name, test.expectedMode, mode) + } + } +} + +func writeRootfsFile(rootfs, path string, mode os.FileMode) (string, error) { + fullPath := filepath.Join(rootfs, path) + dir := filepath.Dir(fullPath) + if err := os.MkdirAll(dir, 0755); err != nil { + return "", err + } + if err := ioutil.WriteFile(fullPath, []byte{}, mode); err != nil { + return "", err + } + // Use chmod, io.WriteFile is affected by umask + if err := os.Chmod(fullPath, mode); err != nil { + return "", err + } + return fullPath, nil +} + +func TestNsenterSafeMakeDir(t *testing.T) { + tests := []struct { + name string + prepare func(base, rootfs, varlib string) (expectedDir string, err error) + subdir string + expectError bool + // If true, "base" directory for SafeMakeDir will be /var/lib/kubelet + baseIsVarLib bool + }{ + { + name: "simple directory", + // evaluated in base + subdir: "some/subdirectory/structure", + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // expected to be created in /roots/ + expectedDir = filepath.Join(rootfs, base, "some/subdirectory/structure") + return expectedDir, nil + }, + }, + { + name: "simple existing directory", + // evaluated in base + subdir: "some/subdirectory/structure", + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: directory exists + hostPath := filepath.Join(base, "some/subdirectory/structure") + if err := os.MkdirAll(hostPath, 0755); err != nil { + return "", err + } + // In rootfs: directory exists + kubeletPath := filepath.Join(rootfs, hostPath) + if err := os.MkdirAll(kubeletPath, 0755); err != nil { + return "", err + } + // expected to be created in /roots/ + expectedDir = kubeletPath + return expectedDir, nil + }, + }, + { + name: "absolute symlink into safe place", + // evaluated in base + subdir: "some/subdirectory/structure", + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: /base/other/subdirectory exists, /base/some is link to /base/other + hostPath := filepath.Join(base, "other/subdirectory") + if err := os.MkdirAll(hostPath, 0755); err != nil { + return "", err + } + somePath := filepath.Join(base, "some") + otherPath := filepath.Join(base, "other") + if err := os.Symlink(otherPath, somePath); err != nil { + return "", err + } + + // In rootfs: /base/other/subdirectory exists + kubeletPath := filepath.Join(rootfs, hostPath) + if err := os.MkdirAll(kubeletPath, 0755); err != nil { + return "", err + } + // expected 'structure' to be created + expectedDir = filepath.Join(rootfs, hostPath, "structure") + return expectedDir, nil + }, + }, + { + name: "relative symlink into safe place", + // evaluated in base + subdir: "some/subdirectory/structure", + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: /base/other/subdirectory exists, /base/some is link to other + hostPath := filepath.Join(base, "other/subdirectory") + if err := os.MkdirAll(hostPath, 0755); err != nil { + return "", err + } + somePath := filepath.Join(base, "some") + if err := os.Symlink("other", somePath); err != nil { + return "", err + } + + // In rootfs: /base/other/subdirectory exists + kubeletPath := filepath.Join(rootfs, hostPath) + if err := os.MkdirAll(kubeletPath, 0755); err != nil { + return "", err + } + // expected 'structure' to be created + expectedDir = filepath.Join(rootfs, hostPath, "structure") + return expectedDir, nil + }, + }, + { + name: "symlink into unsafe place", + // evaluated in base + subdir: "some/subdirectory/structure", + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: /base/some is link to /bin/other + somePath := filepath.Join(base, "some") + if err := os.Symlink("/bin", somePath); err != nil { + return "", err + } + return "", nil + }, + expectError: true, + }, + { + name: "simple directory in /var/lib/kubelet", + // evaluated in varlib + subdir: "some/subdirectory/structure", + baseIsVarLib: true, + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // expected to be created in /base/var/lib/kubelet, not in /rootfs! + expectedDir = filepath.Join(varlib, "some/subdirectory/structure") + return expectedDir, nil + }, + }, + { + name: "safe symlink in /var/lib/kubelet", + // evaluated in varlib + subdir: "some/subdirectory/structure", + baseIsVarLib: true, + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: /varlib/kubelet/other/subdirectory exists, /varlib/some is link to other + hostPath := filepath.Join(varlib, "other/subdirectory") + if err := os.MkdirAll(hostPath, 0755); err != nil { + return "", err + } + somePath := filepath.Join(varlib, "some") + if err := os.Symlink("other", somePath); err != nil { + return "", err + } + + // expected to be created in /base/var/lib/kubelet, not in /rootfs! + expectedDir = filepath.Join(varlib, "other/subdirectory/structure") + return expectedDir, nil + }, + }, + { + name: "unsafe symlink in /var/lib/kubelet", + // evaluated in varlib + subdir: "some/subdirectory/structure", + baseIsVarLib: true, + prepare: func(base, rootfs, varlib string) (expectedDir string, err error) { + // On the host: /varlib/some is link to /bin + somePath := filepath.Join(varlib, "some") + if err := os.Symlink("/bin", somePath); err != nil { + return "", err + } + + return "", nil + }, + expectError: true, + }, + } + for _, test := range tests { + tmpdir, err := ioutil.TempDir("", "nsenter-get-mode-") + if err != nil { + t.Error(err) + continue + } + defer os.RemoveAll(tmpdir) + + mounter, rootfs, varlib, err := newFakeNsenterMounter(tmpdir, t) + if err != nil { + t.Error(err) + continue + } + // Prepare base directory for the test + testBase := filepath.Join(tmpdir, "base") + if err := os.Mkdir(testBase, 0755); err != nil { + t.Error(err) + continue + } + // Prepare base directory also in /rootfs + rootfsBase := filepath.Join(rootfs, testBase) + if err := os.MkdirAll(rootfsBase, 0755); err != nil { + t.Error(err) + continue + } + + expectedDir := "" + if test.prepare != nil { + expectedDir, err = test.prepare(testBase, rootfs, varlib) + if err != nil { + t.Error(err) + continue + } + } + + if test.baseIsVarLib { + // use /var/lib/kubelet as the test base so we can test creating + // subdirs there directly in /var/lib/kubenet and not in + // /rootfs/var/lib/kubelet + testBase = varlib + } + + err = mounter.SafeMakeDir(test.subdir, testBase, 0755) + if err != nil && !test.expectError { + t.Errorf("Test %q: unexpected error: %s", test.name, err) + } + if test.expectError { + if err == nil { + t.Errorf("Test %q: expected error, got none", test.name) + } else { + if !strings.Contains(err.Error(), "is outside of allowed base") { + t.Errorf("Test %q: expected error to contain \"is outside of allowed base\", got this one instead: %s", test.name, err) + } + } + } + + if expectedDir != "" { + _, err := os.Stat(expectedDir) + if err != nil { + t.Errorf("Test %q: expected %q to exist, got error: %s", test.name, expectedDir, err) + } + } + } +} diff --git a/pkg/util/nsenter/BUILD b/pkg/util/nsenter/BUILD index 988fef01b59..286b8882773 100644 --- a/pkg/util/nsenter/BUILD +++ b/pkg/util/nsenter/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -92,3 +92,20 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:linux": [ + "nsenter_test.go", + ], + "//conditions:default": [], + }), + embed = [":go_default_library"], + deps = select({ + "@io_bazel_rules_go//go/platform:linux": [ + "//vendor/k8s.io/utils/exec:go_default_library", + ], + "//conditions:default": [], + }), +) diff --git a/pkg/util/nsenter/nsenter.go b/pkg/util/nsenter/nsenter.go index 477950476b1..e928a57ac9f 100644 --- a/pkg/util/nsenter/nsenter.go +++ b/pkg/util/nsenter/nsenter.go @@ -19,6 +19,8 @@ limitations under the License. package nsenter import ( + "context" + "errors" "fmt" "os" "path/filepath" @@ -30,9 +32,11 @@ import ( ) const ( - hostRootFsPath = "/rootfs" - // hostProcMountNsPath is the default mount namespace for rootfs - hostProcMountNsPath = "/rootfs/proc/1/ns/mnt" + // DefaultHostRootFsPath is path to host's filesystem mounted into container + // with kubelet. + DefaultHostRootFsPath = "/rootfs" + // mountNsPath is the default mount namespace of the host + mountNsPath = "/proc/1/ns/mnt" // nsenterPath is the default nsenter command nsenterPath = "nsenter" ) @@ -65,30 +69,46 @@ const ( type Nsenter struct { // a map of commands to their paths on the host filesystem paths map[string]string + + // Path to the host filesystem, typically "/rootfs". Used only for testing. + hostRootFsPath string + + // Exec implementation, used only for testing + executor exec.Interface } // NewNsenter constructs a new instance of Nsenter -func NewNsenter() (*Nsenter, error) { +func NewNsenter(hostRootFsPath string, executor exec.Interface) (*Nsenter, error) { ne := &Nsenter{ - paths: map[string]string{ - "mount": "", - "findmnt": "", - "umount": "", - "systemd-run": "", - "stat": "", - "touch": "", - "mkdir": "", - "ls": "", - "sh": "", - "chmod": "", - }, + hostRootFsPath: hostRootFsPath, + executor: executor, + } + if err := ne.initPaths(); err != nil { + return nil, err + } + return ne, nil +} + +func (ne *Nsenter) initPaths() error { + ne.paths = map[string]string{} + binaries := []string{ + "mount", + "findmnt", + "umount", + "systemd-run", + "stat", + "touch", + "mkdir", + "sh", + "chmod", + "realpath", } // search for the required commands in other locations besides /usr/bin - for binary := range ne.paths { + for _, binary := range binaries { // check for binary under the following directories for _, path := range []string{"/", "/bin", "/usr/sbin", "/usr/bin"} { binPath := filepath.Join(path, binary) - if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil { + if _, err := os.Stat(filepath.Join(ne.hostRootFsPath, binPath)); err != nil { continue } ne.paths[binary] = binPath @@ -96,19 +116,19 @@ func NewNsenter() (*Nsenter, error) { } // systemd-run is optional, bailout if we don't find any of the other binaries if ne.paths[binary] == "" && binary != "systemd-run" { - return nil, fmt.Errorf("unable to find %v", binary) + return fmt.Errorf("unable to find %v", binary) } } - return ne, nil + return nil } // Exec executes nsenter commands in hostProcMountNsPath mount namespace func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd { + hostProcMountNsPath := filepath.Join(ne.hostRootFsPath, mountNsPath) fullArgs := append([]string{fmt.Sprintf("--mount=%s", hostProcMountNsPath), "--"}, append([]string{ne.AbsHostPath(cmd)}, args...)...) glog.V(5).Infof("Running nsenter command: %v %v", nsenterPath, fullArgs) - exec := exec.New() - return exec.Command(nsenterPath, fullArgs...) + return ne.executor.Command(nsenterPath, fullArgs...) } // AbsHostPath returns the absolute runnable path for a specified command @@ -136,6 +156,9 @@ func (ne *Nsenter) SupportsSystemd() (string, bool) { // non/existing/directory does not exist // -> It resolves symlinks in /mnt/volume to say /mnt/foo and returns // /mnt/foo/non/existing/directory. +// +// BEWARE! EvalSymlinks is not able to detect symlink looks with mustExist=false! +// If /tmp/link is symlink to /tmp/link, EvalSymlinks(/tmp/link/foo) returns /tmp/link/foo. func (ne *Nsenter) EvalSymlinks(pathname string, mustExist bool) (string, error) { var args []string if mustExist { @@ -157,5 +180,57 @@ func (ne *Nsenter) EvalSymlinks(pathname string, mustExist bool) (string, error) // kubelet. It is recommended to resolve symlinks on the host by EvalSymlinks // before calling this function func (ne *Nsenter) KubeletPath(pathname string) string { - return filepath.Join(hostRootFsPath, pathname) + return filepath.Join(ne.hostRootFsPath, pathname) } + +// NewFakeNsenter returns a Nsenter that does not run "nsenter --mount=... --", +// but runs everything in the same mount namespace as the unit test binary. +// rootfsPath is supposed to be a symlink, e.g. /tmp/xyz/rootfs -> /. +// This fake Nsenter is enough for most operations, e.g. to resolve symlinks, +// but it's not enough to call /bin/mount - unit tests don't run as root. +func NewFakeNsenter(rootfsPath string) (*Nsenter, error) { + executor := &fakeExec{ + rootfsPath: rootfsPath, + } + // prepare /rootfs/bin, usr/bin and usr/sbin + bin := filepath.Join(rootfsPath, "bin") + if err := os.Symlink("/bin", bin); err != nil { + return nil, err + } + + usr := filepath.Join(rootfsPath, "usr") + if err := os.Mkdir(usr, 0755); err != nil { + return nil, err + } + usrbin := filepath.Join(usr, "bin") + if err := os.Symlink("/usr/bin", usrbin); err != nil { + return nil, err + } + usrsbin := filepath.Join(usr, "sbin") + if err := os.Symlink("/usr/sbin", usrsbin); err != nil { + return nil, err + } + + return NewNsenter(rootfsPath, executor) +} + +type fakeExec struct { + rootfsPath string +} + +func (f fakeExec) Command(cmd string, args ...string) exec.Cmd { + // This will intentionaly panic if Nsenter does not provide enough arguments. + realCmd := args[2] + realArgs := args[3:] + return exec.New().Command(realCmd, realArgs...) +} + +func (fakeExec) LookPath(file string) (string, error) { + return "", errors.New("not implemented") +} + +func (fakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd { + return nil +} + +var _ exec.Interface = fakeExec{} diff --git a/pkg/util/nsenter/nsenter_test.go b/pkg/util/nsenter/nsenter_test.go new file mode 100644 index 00000000000..3158a55bbec --- /dev/null +++ b/pkg/util/nsenter/nsenter_test.go @@ -0,0 +1,311 @@ +// +build linux + +/* +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 nsenter + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "k8s.io/utils/exec" +) + +func TestExec(t *testing.T) { + tests := []struct { + name string + command string + args []string + expectedOutput string + expectError bool + }{ + { + name: "simple command", + command: "echo", + args: []string{"hello", "world"}, + expectedOutput: "hello world\n", + }, + { + name: "nozero exit code", + command: "false", + expectError: true, + }, + } + + executor := fakeExec{ + rootfsPath: "/rootfs", + } + for _, test := range tests { + ns := Nsenter{ + hostRootFsPath: "/rootfs", + executor: executor, + } + cmd := ns.Exec(test.command, test.args) + outBytes, err := cmd.CombinedOutput() + out := string(outBytes) + if err != nil && !test.expectError { + t.Errorf("Test %q: unexpected error: %s", test.name, err) + } + if err == nil && test.expectError { + t.Errorf("Test %q: expected error, got none", test.name) + } + if test.expectedOutput != out { + t.Errorf("test %q: expected output %q, got %q", test.name, test.expectedOutput, out) + } + } +} + +func TestKubeletPath(t *testing.T) { + tests := []struct { + rootfs string + hostpath string + expectedKubeletPath string + }{ + { + // simple join + "/rootfs", + "/some/path", + "/rootfs/some/path", + }, + { + // squash slashes + "/rootfs/", + "//some/path", + "/rootfs/some/path", + }, + } + + for _, test := range tests { + ns := Nsenter{ + hostRootFsPath: test.rootfs, + } + out := ns.KubeletPath(test.hostpath) + if out != test.expectedKubeletPath { + t.Errorf("Expected path %q, got %q", test.expectedKubeletPath, out) + } + + } +} + +func TestEvalSymlinks(t *testing.T) { + tests := []struct { + name string + mustExist bool + prepare func(tmpdir string) (src string, expectedDst string, err error) + expectError bool + }{ + { + name: "simple file /src", + mustExist: true, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + src = filepath.Join(tmpdir, "src") + err = ioutil.WriteFile(src, []byte{}, 0644) + return src, src, err + }, + }, + { + name: "non-existing file /src", + mustExist: true, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + src = filepath.Join(tmpdir, "src") + return src, "", nil + }, + expectError: true, + }, + { + name: "non-existing file /src/ with mustExist=false", + mustExist: false, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + src = filepath.Join(tmpdir, "src") + return src, src, nil + }, + }, + { + name: "non-existing file /existing/path/src with mustExist=false with existing directories", + mustExist: false, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + src = filepath.Join(tmpdir, "existing/path") + if err := os.MkdirAll(src, 0755); err != nil { + return "", "", err + } + src = filepath.Join(src, "src") + return src, src, nil + }, + }, + { + name: "simple symlink /src -> /dst", + mustExist: false, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "dst") + if err = ioutil.WriteFile(dst, []byte{}, 0644); err != nil { + return "", "", err + } + src = filepath.Join(tmpdir, "src") + err = os.Symlink(dst, src) + return src, dst, err + }, + }, + { + name: "dangling symlink /src -> /non-existing-path", + mustExist: true, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "non-existing-path") + src = filepath.Join(tmpdir, "src") + err = os.Symlink(dst, src) + return src, "", err + }, + expectError: true, + }, + { + name: "dangling symlink /src -> /non-existing-path with mustExist=false", + mustExist: false, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "non-existing-path") + src = filepath.Join(tmpdir, "src") + err = os.Symlink(dst, src) + return src, dst, err + }, + }, + { + name: "symlink to directory /src/file, where /src is link to /dst", + mustExist: true, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "dst") + if err = os.Mkdir(dst, 0755); err != nil { + return "", "", err + } + dstFile := filepath.Join(dst, "file") + if err = ioutil.WriteFile(dstFile, []byte{}, 0644); err != nil { + return "", "", err + } + + src = filepath.Join(tmpdir, "src") + if err = os.Symlink(dst, src); err != nil { + return "", "", err + } + srcFile := filepath.Join(src, "file") + return srcFile, dstFile, nil + }, + }, + { + name: "symlink to non-existing directory: /src/file, where /src is link to /dst and dst does not exist", + mustExist: true, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "dst") + + src = filepath.Join(tmpdir, "src") + if err = os.Symlink(dst, src); err != nil { + return "", "", err + } + srcFile := filepath.Join(src, "file") + return srcFile, "", nil + }, + expectError: true, + }, + { + name: "symlink to non-existing directory: /src/file, where /src is link to /dst and dst does not exist with mustExist=false", + mustExist: false, + prepare: func(tmpdir string) (src string, expectedDst string, err error) { + dst := filepath.Join(tmpdir, "dst") + dstFile := filepath.Join(dst, "file") + + src = filepath.Join(tmpdir, "src") + if err = os.Symlink(dst, src); err != nil { + return "", "", err + } + srcFile := filepath.Join(src, "file") + return srcFile, dstFile, nil + }, + }, + } + + for _, test := range tests { + ns := Nsenter{ + hostRootFsPath: "/rootfs", + executor: fakeExec{ + rootfsPath: "/rootfs", + }, + } + + tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + src, expectedDst, err := test.prepare(tmpdir) + if err != nil { + t.Error(err) + continue + } + + dst, err := ns.EvalSymlinks(src, test.mustExist) + if err != nil && !test.expectError { + t.Errorf("Test %q: unexpected error: %s", test.name, err) + } + if err == nil && test.expectError { + t.Errorf("Test %q: expected error, got none", test.name) + } + if dst != expectedDst { + t.Errorf("Test %q: expected destination %q, got %q", test.name, expectedDst, dst) + } + } +} + +func TestNewNsenter(t *testing.T) { + // Create a symlink /tmp/xyz/rootfs -> / and use it as rootfs path + // It should resolve all binaries correctly, the test runs on Linux + + tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + rootfs := filepath.Join(tmpdir, "rootfs") + if err = os.Symlink("/", rootfs); err != nil { + t.Fatal(err) + } + + _, err = NewNsenter(rootfs, exec.New()) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestNewNsenterError(t *testing.T) { + // Create empty dir /tmp/xyz/rootfs and use it as rootfs path + // It should resolve all binaries correctly, the test runs on Linux + + tmpdir, err := ioutil.TempDir("", "nsenter-hostpath-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + rootfs := filepath.Join(tmpdir, "rootfs") + if err = os.MkdirAll(rootfs, 0755); err != nil { + t.Fatal(err) + } + + _, err = NewNsenter(rootfs, exec.New()) + if err == nil { + t.Errorf("Expected error, got none") + } +} diff --git a/pkg/util/nsenter/nsenter_unsupported.go b/pkg/util/nsenter/nsenter_unsupported.go index 842cf046731..0618b9da469 100644 --- a/pkg/util/nsenter/nsenter_unsupported.go +++ b/pkg/util/nsenter/nsenter_unsupported.go @@ -22,6 +22,12 @@ import ( "k8s.io/utils/exec" ) +const ( + // DefaultHostRootFsPath is path to host's filesystem mounted into container + // with kubelet. + DefaultHostRootFsPath = "/rootfs" +) + // Nsenter is part of experimental support for running the kubelet // in a container. type Nsenter struct { @@ -30,7 +36,7 @@ type Nsenter struct { } // NewNsenter constructs a new instance of Nsenter -func NewNsenter() (*Nsenter, error) { +func NewNsenter(hostRootFsPath string, executor exec.Interface) (*Nsenter, error) { return &Nsenter{}, nil } From bf48d39f390ba616c4ba55a124664d354f9d1228 Mon Sep 17 00:00:00 2001 From: hui luo Date: Tue, 22 May 2018 08:22:03 -0700 Subject: [PATCH 040/416] add test: verify kubelet.config.Restore only happen once --- pkg/kubelet/config/config_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/kubelet/config/config_test.go b/pkg/kubelet/config/config_test.go index f41542a9a6c..9ebf5a66044 100644 --- a/pkg/kubelet/config/config_test.go +++ b/pkg/kubelet/config/config_test.go @@ -451,4 +451,10 @@ func TestPodRestore(t *testing.T) { t.Fatalf("Restore returned error: %v", err) } expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.RESTORE, kubetypes.ApiserverSource, pod)) + + // Verify Restore only happen once + if err := config.Restore(tmpDir, channel); err != nil { + t.Fatalf("The second restore returned error: %v", err) + } + expectNoPodUpdate(t, ch) } From dca376a03ef43df2714eafd431a76646346c9094 Mon Sep 17 00:00:00 2001 From: Jeff Grafton Date: Wed, 23 May 2018 12:03:27 -0700 Subject: [PATCH 041/416] Add KUBE_CGO_OVERRIDES env var to force enabling CGO --- hack/lib/golang.sh | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index 9f229168a8a..88e0a8f6520 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -213,12 +213,24 @@ readonly KUBE_STATIC_LIBRARIES=( kubectl ) +# KUBE_CGO_OVERRIDES is a space-separated list of binaries which should be built +# with CGO enabled, assuming CGO is supported on the target platform. +# This overrides any entry in KUBE_STATIC_LIBRARIES. +IFS=" " read -ra KUBE_CGO_OVERRIDES <<< "${KUBE_CGO_OVERRIDES:-}" +readonly KUBE_CGO_OVERRIDES +# KUBE_STATIC_OVERRIDES is a space-separated list of binaries which should be +# built with CGO disabled. This is in addition to the list in +# KUBE_STATIC_LIBRARIES. +IFS=" " read -ra KUBE_STATIC_OVERRIDES <<< "${KUBE_STATIC_OVERRIDES:-}" +readonly KUBE_STATIC_OVERRIDES + kube::golang::is_statically_linked_library() { local e + if [[ -n "${KUBE_CGO_OVERRIDES:+x}" ]]; then + for e in "${KUBE_CGO_OVERRIDES[@]}"; do [[ "$1" == *"/$e" ]] && return 1; done; + fi for e in "${KUBE_STATIC_LIBRARIES[@]}"; do [[ "$1" == *"/$e" ]] && return 0; done; - # Allow individual overrides--e.g., so that you can get a static build of - # kubectl for inclusion in a container. - if [ -n "${KUBE_STATIC_OVERRIDES:+x}" ]; then + if [[ -n "${KUBE_STATIC_OVERRIDES:+x}" ]]; then for e in "${KUBE_STATIC_OVERRIDES[@]}"; do [[ "$1" == *"/$e" ]] && return 0; done; fi return 1; From e4ded2b3ecb3084bfb6111e81b2741bead8ccc14 Mon Sep 17 00:00:00 2001 From: Jeff Grafton Date: Wed, 23 May 2018 12:23:00 -0700 Subject: [PATCH 042/416] Explictly enable cgo when building kubectl for darwin from darwin --- hack/lib/golang.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index 88e0a8f6520..c5d463483bf 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -226,6 +226,9 @@ readonly KUBE_STATIC_OVERRIDES kube::golang::is_statically_linked_library() { local e + # Explicitly enable cgo when building kubectl for darwin from darwin. + [[ "$(go env GOHOSTOS)" == "darwin" && "$(go env GOOS)" == "darwin" && + "$1" == *"/kubectl" ]] && return 1 if [[ -n "${KUBE_CGO_OVERRIDES:+x}" ]]; then for e in "${KUBE_CGO_OVERRIDES[@]}"; do [[ "$1" == *"/$e" ]] && return 1; done; fi From 6352230631a593f148a88caac405d0b8d4d72ff1 Mon Sep 17 00:00:00 2001 From: Tim Allclair Date: Wed, 23 May 2018 13:30:14 -0700 Subject: [PATCH 043/416] Update dashboard OWNERS --- cluster/addons/dashboard/OWNERS | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cluster/addons/dashboard/OWNERS b/cluster/addons/dashboard/OWNERS index 222788b954c..04353ada135 100644 --- a/cluster/addons/dashboard/OWNERS +++ b/cluster/addons/dashboard/OWNERS @@ -1,6 +1,12 @@ approvers: -- floreks -- maciaszczykm +- bryk reviewers: +- cheld +- cupofcat +- danielromlein - floreks +- ianlewis +- konryd - maciaszczykm +- mhenc +- rf232 From fd1f19fc423880b2b292d6f9e6fca1e941e87994 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Wed, 23 May 2018 16:12:54 -0700 Subject: [PATCH 044/416] add metadata to kubelet eviction event annotations --- pkg/controller/testutil/test_utils.go | 5 + pkg/kubelet/container/helpers.go | 7 ++ pkg/kubelet/eviction/eviction_manager.go | 13 +-- pkg/kubelet/eviction/helpers.go | 24 ++++- .../k8s.io/client-go/tools/record/event.go | 22 +++-- .../src/k8s.io/client-go/tools/record/fake.go | 4 + test/e2e_node/BUILD | 1 + test/e2e_node/eviction_test.go | 96 ++++++++++++++++--- 8 files changed, 140 insertions(+), 32 deletions(-) diff --git a/pkg/controller/testutil/test_utils.go b/pkg/controller/testutil/test_utils.go index 770df607c41..02119191028 100644 --- a/pkg/controller/testutil/test_utils.go +++ b/pkg/controller/testutil/test_utils.go @@ -365,6 +365,11 @@ func (f *FakeRecorder) Eventf(obj runtime.Object, eventtype, reason, messageFmt func (f *FakeRecorder) PastEventf(obj runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) { } +// AnnotatedEventf emits a fake formatted event to the fake recorder +func (f *FakeRecorder) AnnotatedEventf(obj runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { + f.Eventf(obj, eventtype, reason, messageFmt, args) +} + func (f *FakeRecorder) generateEvent(obj runtime.Object, timestamp metav1.Time, eventtype, reason, message string) { f.Lock() defer f.Unlock() diff --git a/pkg/kubelet/container/helpers.go b/pkg/kubelet/container/helpers.go index 180a3e6df2d..399fa959f45 100644 --- a/pkg/kubelet/container/helpers.go +++ b/pkg/kubelet/container/helpers.go @@ -193,6 +193,13 @@ func (irecorder *innerEventRecorder) PastEventf(object runtime.Object, timestamp } } +func (irecorder *innerEventRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { + if ref, ok := irecorder.shouldRecordEvent(object); ok { + irecorder.recorder.AnnotatedEventf(ref, annotations, eventtype, reason, messageFmt, args...) + } + +} + // Pod must not be nil. func IsHostNetworkPod(pod *v1.Pod) bool { return pod.Spec.HostNetwork diff --git a/pkg/kubelet/eviction/eviction_manager.go b/pkg/kubelet/eviction/eviction_manager.go index b601fe4763d..cadc6afaa6c 100644 --- a/pkg/kubelet/eviction/eviction_manager.go +++ b/pkg/kubelet/eviction/eviction_manager.go @@ -429,7 +429,8 @@ func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc Act if !isHardEvictionThreshold(thresholdToReclaim) { gracePeriodOverride = m.config.MaxPodGracePeriodSeconds } - if m.evictPod(pod, gracePeriodOverride, evictionMessage(resourceToReclaim, pod, statsFunc)) { + message, annotations := evictionMessage(resourceToReclaim, pod, statsFunc) + if m.evictPod(pod, gracePeriodOverride, message, annotations) { return []*v1.Pod{pod} } } @@ -534,7 +535,7 @@ func (m *managerImpl) emptyDirLimitEviction(podStats statsapi.PodStats, pod *v1. used := podVolumeUsed[pod.Spec.Volumes[i].Name] if used != nil && size != nil && size.Sign() == 1 && used.Cmp(*size) > 0 { // the emptyDir usage exceeds the size limit, evict the pod - return m.evictPod(pod, 0, fmt.Sprintf(emptyDirMessage, pod.Spec.Volumes[i].Name, size.String())) + return m.evictPod(pod, 0, fmt.Sprintf(emptyDirMessage, pod.Spec.Volumes[i].Name, size.String()), nil) } } } @@ -566,7 +567,7 @@ func (m *managerImpl) podEphemeralStorageLimitEviction(podStats statsapi.PodStat podEphemeralStorageLimit := podLimits[v1.ResourceEphemeralStorage] if podEphemeralStorageTotalUsage.Cmp(podEphemeralStorageLimit) > 0 { // the total usage of pod exceeds the total size limit of containers, evict the pod - return m.evictPod(pod, 0, fmt.Sprintf(podEphemeralStorageMessage, podEphemeralStorageLimit.String())) + return m.evictPod(pod, 0, fmt.Sprintf(podEphemeralStorageMessage, podEphemeralStorageLimit.String()), nil) } return false } @@ -588,7 +589,7 @@ func (m *managerImpl) containerEphemeralStorageLimitEviction(podStats statsapi.P if ephemeralStorageThreshold, ok := thresholdsMap[containerStat.Name]; ok { if ephemeralStorageThreshold.Cmp(*containerUsed) < 0 { - return m.evictPod(pod, 0, fmt.Sprintf(containerEphemeralStorageMessage, containerStat.Name, ephemeralStorageThreshold.String())) + return m.evictPod(pod, 0, fmt.Sprintf(containerEphemeralStorageMessage, containerStat.Name, ephemeralStorageThreshold.String()), nil) } } @@ -596,7 +597,7 @@ func (m *managerImpl) containerEphemeralStorageLimitEviction(podStats statsapi.P return false } -func (m *managerImpl) evictPod(pod *v1.Pod, gracePeriodOverride int64, evictMsg string) bool { +func (m *managerImpl) evictPod(pod *v1.Pod, gracePeriodOverride int64, evictMsg string, annotations map[string]string) bool { // If the pod is marked as critical and static, and support for critical pod annotations is enabled, // do not evict such pods. Static pods are not re-admitted after evictions. // https://github.com/kubernetes/kubernetes/issues/40573 has more details. @@ -611,7 +612,7 @@ func (m *managerImpl) evictPod(pod *v1.Pod, gracePeriodOverride int64, evictMsg Reason: Reason, } // record that we are evicting the pod - m.recorder.Eventf(pod, v1.EventTypeWarning, Reason, evictMsg) + m.recorder.AnnotatedEventf(pod, annotations, v1.EventTypeWarning, Reason, evictMsg) // this is a blocking call and should only return when the pod and its containers are killed. err := m.killPodFunc(pod, status, &gracePeriodOverride) if err != nil { diff --git a/pkg/kubelet/eviction/helpers.go b/pkg/kubelet/eviction/helpers.go index fd862ea1cef..1c309360a39 100644 --- a/pkg/kubelet/eviction/helpers.go +++ b/pkg/kubelet/eviction/helpers.go @@ -55,6 +55,12 @@ const ( // this prevents constantly updating the memcg notifier if synchronize // is run frequently. notifierRefreshInterval = 10 * time.Second + // OffendingContainersKey is the key in eviction event annotations for the list of container names which exceeded their requests + OffendingContainersKey = "offending_containers" + // OffendingContainersUsageKey is the key in eviction event annotations for the list of usage of containers which exceeded their requests + OffendingContainersUsageKey = "offending_containers_usage" + // StarvedResourceKey is the key for the starved resource in eviction event annotations + StarvedResourceKey = "starved_resource" ) var ( @@ -1053,12 +1059,15 @@ func buildSignalToNodeReclaimFuncs(imageGC ImageGC, containerGC ContainerGC, wit return signalToReclaimFunc } -// evictionMessage constructs a useful message about why an eviction occurred -func evictionMessage(resourceToReclaim v1.ResourceName, pod *v1.Pod, stats statsFunc) string { - message := fmt.Sprintf(message, resourceToReclaim) +// evictionMessage constructs a useful message about why an eviction occurred, and annotations to provide metadata about the eviction +func evictionMessage(resourceToReclaim v1.ResourceName, pod *v1.Pod, stats statsFunc) (message string, annotations map[string]string) { + annotations = make(map[string]string) + message = fmt.Sprintf(message, resourceToReclaim) + containers := []string{} + containerUsage := []string{} podStats, ok := stats(pod) if !ok { - return message + return } for _, containerStats := range podStats.Containers { for _, container := range pod.Spec.Containers { @@ -1077,11 +1086,16 @@ func evictionMessage(resourceToReclaim v1.ResourceName, pod *v1.Pod, stats stats } if usage != nil && usage.Cmp(requests) > 0 { message += fmt.Sprintf(containerMessage, container.Name, usage.String(), requests.String()) + containers = append(containers, container.Name) + containerUsage = append(containerUsage, usage.String()) } } } } - return message + annotations[OffendingContainersKey] = strings.Join(containers, ",") + annotations[OffendingContainersUsageKey] = strings.Join(containerUsage, ",") + annotations[StarvedResourceKey] = string(resourceToReclaim) + return } // thresholdStopCh is a ThresholdStopCh which can only be closed after notifierRefreshInterval time has passed diff --git a/staging/src/k8s.io/client-go/tools/record/event.go b/staging/src/k8s.io/client-go/tools/record/event.go index cc665d74e63..168dfa80c56 100644 --- a/staging/src/k8s.io/client-go/tools/record/event.go +++ b/staging/src/k8s.io/client-go/tools/record/event.go @@ -72,6 +72,9 @@ type EventRecorder interface { // PastEventf is just like Eventf, but with an option to specify the event's 'timestamp' field. PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) + + // AnnotatedEventf is just like eventf, but with annotations attached + AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) } // EventBroadcaster knows how to receive events and send them to any EventSink, watcher, or log. @@ -250,7 +253,7 @@ type recorderImpl struct { clock clock.Clock } -func (recorder *recorderImpl) generateEvent(object runtime.Object, timestamp metav1.Time, eventtype, reason, message string) { +func (recorder *recorderImpl) generateEvent(object runtime.Object, annotations map[string]string, timestamp metav1.Time, eventtype, reason, message string) { ref, err := ref.GetReference(recorder.scheme, object) if err != nil { glog.Errorf("Could not construct reference to: '%#v' due to: '%v'. Will not report event: '%v' '%v' '%v'", object, err, eventtype, reason, message) @@ -262,7 +265,7 @@ func (recorder *recorderImpl) generateEvent(object runtime.Object, timestamp met return } - event := recorder.makeEvent(ref, eventtype, reason, message) + event := recorder.makeEvent(ref, annotations, eventtype, reason, message) event.Source = recorder.source go func() { @@ -281,7 +284,7 @@ func validateEventType(eventtype string) bool { } func (recorder *recorderImpl) Event(object runtime.Object, eventtype, reason, message string) { - recorder.generateEvent(object, metav1.Now(), eventtype, reason, message) + recorder.generateEvent(object, nil, metav1.Now(), eventtype, reason, message) } func (recorder *recorderImpl) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) { @@ -289,10 +292,14 @@ func (recorder *recorderImpl) Eventf(object runtime.Object, eventtype, reason, m } func (recorder *recorderImpl) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) { - recorder.generateEvent(object, timestamp, eventtype, reason, fmt.Sprintf(messageFmt, args...)) + recorder.generateEvent(object, nil, timestamp, eventtype, reason, fmt.Sprintf(messageFmt, args...)) } -func (recorder *recorderImpl) makeEvent(ref *v1.ObjectReference, eventtype, reason, message string) *v1.Event { +func (recorder *recorderImpl) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { + recorder.generateEvent(object, annotations, metav1.Now(), eventtype, reason, fmt.Sprintf(messageFmt, args...)) +} + +func (recorder *recorderImpl) makeEvent(ref *v1.ObjectReference, annotations map[string]string, eventtype, reason, message string) *v1.Event { t := metav1.Time{Time: recorder.clock.Now()} namespace := ref.Namespace if namespace == "" { @@ -300,8 +307,9 @@ func (recorder *recorderImpl) makeEvent(ref *v1.ObjectReference, eventtype, reas } return &v1.Event{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()), - Namespace: namespace, + Name: fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()), + Namespace: namespace, + Annotations: annotations, }, InvolvedObject: *ref, Reason: reason, diff --git a/staging/src/k8s.io/client-go/tools/record/fake.go b/staging/src/k8s.io/client-go/tools/record/fake.go index c0e8eedbb73..6e031daaff8 100644 --- a/staging/src/k8s.io/client-go/tools/record/fake.go +++ b/staging/src/k8s.io/client-go/tools/record/fake.go @@ -45,6 +45,10 @@ func (f *FakeRecorder) Eventf(object runtime.Object, eventtype, reason, messageF func (f *FakeRecorder) PastEventf(object runtime.Object, timestamp metav1.Time, eventtype, reason, messageFmt string, args ...interface{}) { } +func (f *FakeRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { + f.Eventf(object, eventtype, reason, messageFmt, args) +} + // NewFakeRecorder creates new fake event recorder with event channel with // buffer of given size. func NewFakeRecorder(bufferSize int) *FakeRecorder { diff --git a/test/e2e_node/BUILD b/test/e2e_node/BUILD index 9113fa745d9..859fa688bc3 100644 --- a/test/e2e_node/BUILD +++ b/test/e2e_node/BUILD @@ -152,6 +152,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", diff --git a/test/e2e_node/eviction_test.go b/test/e2e_node/eviction_test.go index 21962c62f31..3e3a55d792a 100644 --- a/test/e2e_node/eviction_test.go +++ b/test/e2e_node/eviction_test.go @@ -20,11 +20,13 @@ import ( "fmt" "path/filepath" "strconv" + "strings" "time" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" nodeutil "k8s.io/kubernetes/pkg/api/v1/node" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig" @@ -50,11 +52,13 @@ const ( // pressure conditions often surface after evictions because the kubelet only updates // node conditions periodically. // we wait this period after evictions to make sure that we wait out this delay - pressureDelay = 20 * time.Second - testContextFmt = "when we run containers that should cause %s" - noPressure = v1.NodeConditionType("NoPressure") - lotsOfDisk = 10240 // 10 Gb in Mb - lotsOfFiles = 1000000000 // 1 billion + pressureDelay = 20 * time.Second + testContextFmt = "when we run containers that should cause %s" + noPressure = v1.NodeConditionType("NoPressure") + lotsOfDisk = 10240 // 10 Gb in Mb + lotsOfFiles = 1000000000 // 1 billion + resourceInodes = v1.ResourceName("inodes") + noStarvedResource = v1.ResourceName("none") ) // InodeEviction tests that the node responds to node disk pressure by evicting only responsible pods. @@ -62,6 +66,7 @@ const ( var _ = framework.KubeDescribe("InodeEviction [Slow] [Serial] [Disruptive]", func() { f := framework.NewDefaultFramework("inode-eviction-test") expectedNodeCondition := v1.NodeDiskPressure + expectedStarvedResource := resourceInodes pressureTimeout := 15 * time.Minute inodesConsumed := uint64(200000) Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { @@ -75,7 +80,7 @@ var _ = framework.KubeDescribe("InodeEviction [Slow] [Serial] [Disruptive]", fun initialConfig.EvictionHard = map[string]string{"nodefs.inodesFree": fmt.Sprintf("%d", inodesFree-inodesConsumed)} initialConfig.EvictionMinimumReclaim = map[string]string{} }) - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logInodeMetrics, []podEvictSpec{ + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logInodeMetrics, []podEvictSpec{ { evictionPriority: 1, pod: inodeConsumingPod("container-inode-hog", lotsOfFiles, nil), @@ -98,6 +103,7 @@ var _ = framework.KubeDescribe("ImageGCNoEviction [Slow] [Serial] [Disruptive]", f := framework.NewDefaultFramework("image-gc-eviction-test") pressureTimeout := 10 * time.Minute expectedNodeCondition := v1.NodeDiskPressure + expectedStarvedResource := resourceInodes inodesConsumed := uint64(100000) Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { @@ -112,7 +118,7 @@ var _ = framework.KubeDescribe("ImageGCNoEviction [Slow] [Serial] [Disruptive]", }) // Consume enough inodes to induce disk pressure, // but expect that image garbage collection can reduce it enough to avoid an eviction - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, []podEvictSpec{ + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logDiskMetrics, []podEvictSpec{ { evictionPriority: 0, pod: inodeConsumingPod("container-inode", 110000, nil), @@ -126,6 +132,7 @@ var _ = framework.KubeDescribe("ImageGCNoEviction [Slow] [Serial] [Disruptive]", var _ = framework.KubeDescribe("MemoryAllocatableEviction [Slow] [Serial] [Disruptive]", func() { f := framework.NewDefaultFramework("memory-allocatable-eviction-test") expectedNodeCondition := v1.NodeMemoryPressure + expectedStarvedResource := v1.ResourceMemory pressureTimeout := 10 * time.Minute Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { @@ -140,7 +147,7 @@ var _ = framework.KubeDescribe("MemoryAllocatableEviction [Slow] [Serial] [Disru initialConfig.EnforceNodeAllocatable = []string{kubetypes.NodeAllocatableEnforcementKey} initialConfig.CgroupsPerQOS = true }) - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logMemoryMetrics, []podEvictSpec{ + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logMemoryMetrics, []podEvictSpec{ { evictionPriority: 1, pod: getMemhogPod("memory-hog-pod", "memory-hog", v1.ResourceRequirements{}), @@ -159,6 +166,7 @@ var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive f := framework.NewDefaultFramework("localstorage-eviction-test") pressureTimeout := 10 * time.Minute expectedNodeCondition := v1.NodeDiskPressure + expectedStarvedResource := v1.ResourceEphemeralStorage Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { diskConsumed := resource.MustParse("100Mi") @@ -167,7 +175,7 @@ var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive initialConfig.EvictionHard = map[string]string{"nodefs.available": fmt.Sprintf("%d", availableBytes-uint64(diskConsumed.Value()))} initialConfig.EvictionMinimumReclaim = map[string]string{} }) - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, []podEvictSpec{ + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logDiskMetrics, []podEvictSpec{ { evictionPriority: 1, pod: diskConsumingPod("container-disk-hog", lotsOfDisk, nil, v1.ResourceRequirements{}), @@ -187,6 +195,7 @@ var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disrup f := framework.NewDefaultFramework("localstorage-eviction-test") pressureTimeout := 10 * time.Minute expectedNodeCondition := v1.NodeDiskPressure + expectedStarvedResource := v1.ResourceEphemeralStorage Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { diskConsumed := resource.MustParse("100Mi") @@ -204,7 +213,7 @@ var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disrup // setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty) initialConfig.EvictionHard = map[string]string{"memory.available": "0%"} }) - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, []podEvictSpec{ + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logDiskMetrics, []podEvictSpec{ { evictionPriority: 1, pod: diskConsumingPod("container-disk-hog", lotsOfDisk, nil, v1.ResourceRequirements{}), @@ -232,7 +241,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se useUnderLimit := 99 /* Mb */ containerLimit := v1.ResourceList{v1.ResourceEphemeralStorage: sizeLimit} - runEvictionTest(f, evictionTestTimeout, noPressure, logDiskMetrics, []podEvictSpec{ + runEvictionTest(f, evictionTestTimeout, noPressure, noStarvedResource, logDiskMetrics, []podEvictSpec{ { evictionPriority: 1, // This pod should be evicted because emptyDir (default storage type) usage violation pod: diskConsumingPod("emptydir-disk-sizelimit", useOverLimit, &v1.VolumeSource{ @@ -274,6 +283,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [Disruptive]", func() { f := framework.NewDefaultFramework("priority-memory-eviction-ordering-test") expectedNodeCondition := v1.NodeMemoryPressure + expectedStarvedResource := v1.ResourceMemory pressureTimeout := 10 * time.Minute Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { @@ -310,7 +320,7 @@ var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [ } systemPriority := int32(2147483647) specs[1].pod.Spec.Priority = &systemPriority - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logMemoryMetrics, specs) + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logMemoryMetrics, specs) }) }) @@ -320,6 +330,7 @@ var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [ var _ = framework.KubeDescribe("PriorityLocalStorageEvictionOrdering [Slow] [Serial] [Disruptive]", func() { f := framework.NewDefaultFramework("priority-disk-eviction-ordering-test") expectedNodeCondition := v1.NodeDiskPressure + expectedStarvedResource := v1.ResourceEphemeralStorage pressureTimeout := 10 * time.Minute Context(fmt.Sprintf(testContextFmt, expectedNodeCondition), func() { tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) { @@ -358,7 +369,7 @@ var _ = framework.KubeDescribe("PriorityLocalStorageEvictionOrdering [Slow] [Ser } systemPriority := int32(2147483647) specs[1].pod.Spec.Priority = &systemPriority - runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, specs) + runEvictionTest(f, pressureTimeout, expectedNodeCondition, expectedStarvedResource, logDiskMetrics, specs) }) }) @@ -377,7 +388,7 @@ type podEvictSpec struct { // It ensures that lower evictionPriority pods are always evicted before higher evictionPriority pods (2 evicted before 1, etc.) // It ensures that all pods with non-zero evictionPriority are eventually evicted. // runEvictionTest then cleans up the testing environment by deleting provided pods, and ensures that expectedNodeCondition no longer exists -func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expectedNodeCondition v1.NodeConditionType, logFunc func(), testSpecs []podEvictSpec) { +func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expectedNodeCondition v1.NodeConditionType, expectedStarvedResource v1.ResourceName, logFunc func(), testSpecs []podEvictSpec) { // Place the remainder of the test within a context so that the kubelet config is set before and after the test. Context("", func() { BeforeEach(func() { @@ -442,6 +453,9 @@ func runEvictionTest(f *framework.Framework, pressureTimeout time.Duration, expe logKubeletMetrics(kubeletmetrics.EvictionStatsAgeKey) return verifyEvictionOrdering(f, testSpecs) }, postTestConditionMonitoringPeriod, evictionPollInterval).Should(BeNil()) + + By("checking for correctly formatted eviction events") + verifyEvictionEvents(f, testSpecs, expectedStarvedResource) }) AfterEach(func() { @@ -549,6 +563,60 @@ func verifyEvictionOrdering(f *framework.Framework, testSpecs []podEvictSpec) er return fmt.Errorf("pods that should be evicted are still running") } +func verifyEvictionEvents(f *framework.Framework, testSpecs []podEvictSpec, expectedStarvedResource v1.ResourceName) { + for _, spec := range testSpecs { + pod := spec.pod + if spec.evictionPriority != 0 { + selector := fields.Set{ + "involvedObject.kind": "Pod", + "involvedObject.name": pod.Name, + "involvedObject.namespace": f.Namespace.Name, + "reason": eviction.Reason, + }.AsSelector().String() + podEvictEvents, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).List(metav1.ListOptions{FieldSelector: selector}) + Expect(err).To(BeNil(), "Unexpected error getting events during eviction test: %v", err) + Expect(len(podEvictEvents.Items)).To(Equal(1), "Expected to find 1 eviction event for pod %s, got %d", pod.Name, len(podEvictEvents.Items)) + event := podEvictEvents.Items[0] + + if expectedStarvedResource != noStarvedResource { + // Check the eviction.StarvedResourceKey + starved, found := event.Annotations[eviction.StarvedResourceKey] + Expect(found).To(BeTrue(), "Expected to find an annotation on the eviction event for pod %s containing the starved resource %s, but it was not found", + pod.Name, expectedStarvedResource) + starvedResource := v1.ResourceName(starved) + Expect(starvedResource).To(Equal(expectedStarvedResource), "Expected to the starved_resource annotation on pod %s to contain %s, but got %s instead", + pod.Name, expectedStarvedResource, starvedResource) + + // We only check these keys for memory, because ephemeral storage evictions may be due to volume usage, in which case these values are not present + if expectedStarvedResource == v1.ResourceMemory { + // Check the eviction.OffendingContainersKey + offendersString, found := event.Annotations[eviction.OffendingContainersKey] + Expect(found).To(BeTrue(), "Expected to find an annotation on the eviction event for pod %s containing the offending containers, but it was not found", + pod.Name) + offendingContainers := strings.Split(offendersString, ",") + Expect(len(offendingContainers)).To(Equal(1), "Expected to find the offending container's usage in the %s annotation, but no container was found", + eviction.OffendingContainersKey) + Expect(offendingContainers[0]).To(Equal(pod.Spec.Containers[0].Name), "Expected to find the offending container: %s's usage in the %s annotation, but found %s instead", + pod.Spec.Containers[0].Name, eviction.OffendingContainersKey, offendingContainers[0]) + + // Check the eviction.OffendingContainersUsageKey + offendingUsageString, found := event.Annotations[eviction.OffendingContainersUsageKey] + Expect(found).To(BeTrue(), "Expected to find an annotation on the eviction event for pod %s containing the offending containers' usage, but it was not found", + pod.Name) + offendingContainersUsage := strings.Split(offendingUsageString, ",") + Expect(len(offendingContainersUsage)).To(Equal(1), "Expected to find the offending container's usage in the %s annotation, but found %+v", + eviction.OffendingContainersUsageKey, offendingContainersUsage) + usageQuantity, err := resource.ParseQuantity(offendingContainersUsage[0]) + Expect(err).To(BeNil(), "Expected to be able to parse pod %s's %s annotation as a quantity, but got err: %v", pod.Name, eviction.OffendingContainersUsageKey, err) + request := pod.Spec.Containers[0].Resources.Requests[starvedResource] + Expect(usageQuantity.Cmp(request)).To(Equal(1), "Expected usage of offending container: %s in pod %s to exceed its request %s", + usageQuantity.String(), pod.Name, request.String()) + } + } + } + } +} + // Returns TRUE if the node has the node condition, FALSE otherwise func hasNodeCondition(f *framework.Framework, expectedNodeCondition v1.NodeConditionType) bool { localNodeStatus := getLocalNode(f).Status From 49e762ab3ae2618625439e7b6142cd7039096556 Mon Sep 17 00:00:00 2001 From: Alin-Gheorghe Balutoiu Date: Wed, 23 May 2018 09:52:37 +0200 Subject: [PATCH 045/416] Fix Windows CNI for the sandbox case Windows supports both sandbox and non-sandbox cases. The non-sandbox case is for Windows Server 2016 and for Windows Server version greater than 1709 which use Hyper-V containers. Currently, the CNI on Windows fetches the IP from the containers within the pods regardless of the mode. This should be done only in the non-sandbox mode where the IP of the actual container will be different than the IP of the sandbox container. In the case where the sandbox container is supported, all the containers from the same pod will share the network details of the sandbox container. This patch updates the CNI to fetch the IP from the sandbox container when this mode is supported. Signed-off-by: Alin Balutoiu --- pkg/kubelet/dockershim/docker_sandbox.go | 3 +- pkg/kubelet/dockershim/helpers_linux.go | 2 +- pkg/kubelet/dockershim/helpers_unsupported.go | 2 +- pkg/kubelet/dockershim/helpers_windows.go | 64 +++++++------------ 4 files changed, 28 insertions(+), 43 deletions(-) diff --git a/pkg/kubelet/dockershim/docker_sandbox.go b/pkg/kubelet/dockershim/docker_sandbox.go index 13e9c42366e..6806232e115 100644 --- a/pkg/kubelet/dockershim/docker_sandbox.go +++ b/pkg/kubelet/dockershim/docker_sandbox.go @@ -412,8 +412,9 @@ func (ds *dockerService) PodSandboxStatus(ctx context.Context, req *runtimeapi.P var IP string // TODO: Remove this when sandbox is available on windows + // Currently windows supports both sandbox and non-sandbox cases. // This is a workaround for windows, where sandbox is not in use, and pod IP is determined through containers belonging to the Pod. - if IP = ds.determinePodIPBySandboxID(podSandboxID); IP == "" { + if IP = ds.determinePodIPBySandboxID(podSandboxID, r); IP == "" { IP = ds.getIP(podSandboxID, r) } diff --git a/pkg/kubelet/dockershim/helpers_linux.go b/pkg/kubelet/dockershim/helpers_linux.go index 5d231ea82a2..9b4cd9f02a8 100644 --- a/pkg/kubelet/dockershim/helpers_linux.go +++ b/pkg/kubelet/dockershim/helpers_linux.go @@ -136,7 +136,7 @@ func (ds *dockerService) updateCreateConfig( return nil } -func (ds *dockerService) determinePodIPBySandboxID(uid string) string { +func (ds *dockerService) determinePodIPBySandboxID(uid string, sandbox *dockertypes.ContainerJSON) string { return "" } diff --git a/pkg/kubelet/dockershim/helpers_unsupported.go b/pkg/kubelet/dockershim/helpers_unsupported.go index 2867898f301..0abc271b1d0 100644 --- a/pkg/kubelet/dockershim/helpers_unsupported.go +++ b/pkg/kubelet/dockershim/helpers_unsupported.go @@ -45,7 +45,7 @@ func (ds *dockerService) updateCreateConfig( return nil } -func (ds *dockerService) determinePodIPBySandboxID(uid string) string { +func (ds *dockerService) determinePodIPBySandboxID(uid string, sandbox *dockertypes.ContainerJSON) string { glog.Warningf("determinePodIPBySandboxID is unsupported in this build") return "" } diff --git a/pkg/kubelet/dockershim/helpers_windows.go b/pkg/kubelet/dockershim/helpers_windows.go index 0c54bea03d4..6b3665e876f 100644 --- a/pkg/kubelet/dockershim/helpers_windows.go +++ b/pkg/kubelet/dockershim/helpers_windows.go @@ -83,7 +83,28 @@ func (ds *dockerService) updateCreateConfig( return nil } -func (ds *dockerService) determinePodIPBySandboxID(sandboxID string) string { +func (ds *dockerService) determinePodIPBySandboxID(sandboxID string, sandbox *dockertypes.ContainerJSON) string { + // Versions and feature support + // ============================ + // Windows version >= Windows Server, Version 1709, Supports both sandbox and non-sandbox case + // Windows version == Windows Server 2016 Support only non-sandbox case + // Windows version < Windows Server 2016 is Not Supported + + // Sandbox support in Windows mandates CNI Plugin. + // Presence of CONTAINER_NETWORK flag is considered as non-Sandbox cases here + // Hyper-V isolated containers are also considered as non-Sandbox cases + + // Todo: Add a kernel version check for more validation + + // Hyper-V only supports one container per Pod yet and the container will have a different + // IP address from sandbox. Retrieve the IP from the containers as this is a non-Sandbox case. + // TODO(feiskyer): remove this workaround after Hyper-V supports multiple containers per Pod. + if networkMode := os.Getenv("CONTAINER_NETWORK"); networkMode == "" && sandbox.HostConfig.Isolation != kubeletapis.HypervIsolationValue { + // Sandbox case, fetch the IP from the sandbox container. + return ds.getIP(sandboxID, sandbox) + } + + // Non-Sandbox case, fetch the IP from the containers within the Pod. opts := dockertypes.ContainerListOptions{ All: true, Filters: dockerfilters.NewArgs(), @@ -103,45 +124,8 @@ func (ds *dockerService) determinePodIPBySandboxID(sandboxID string) string { continue } - // Versions and feature support - // ============================ - // Windows version == Windows Server, Version 1709,, Supports both sandbox and non-sandbox case - // Windows version == Windows Server 2016 Support only non-sandbox case - // Windows version < Windows Server 2016 is Not Supported - - // Sandbox support in Windows mandates CNI Plugin. - // Presence of CONTAINER_NETWORK flag is considered as non-Sandbox cases here - - // Todo: Add a kernel version check for more validation - - if networkMode := os.Getenv("CONTAINER_NETWORK"); networkMode == "" { - // On Windows, every container that is created in a Sandbox, needs to invoke CNI plugin again for adding the Network, - // with the shared container name as NetNS info, - // This is passed down to the platform to replicate some necessary information to the new container - - // - // This place is chosen as a hack for now, since ds.getIP would end up calling CNI's addToNetwork - // That is why addToNetwork is required to be idempotent - - // Instead of relying on this call, an explicit call to addToNetwork should be - // done immediately after ContainerCreation, in case of Windows only. TBD Issue # to handle this - - if r.HostConfig.Isolation == kubeletapis.HypervIsolationValue { - // Hyper-V only supports one container per Pod yet and the container will have a different - // IP address from sandbox. Return the first non-sandbox container IP as POD IP. - // TODO(feiskyer): remove this workaround after Hyper-V supports multiple containers per Pod. - if containerIP := ds.getIP(c.ID, r); containerIP != "" { - return containerIP - } - } else { - // Do not return any IP, so that we would continue and get the IP of the Sandbox - ds.getIP(sandboxID, r) - } - } else { - // ds.getIP will call the CNI plugin to fetch the IP - if containerIP := ds.getIP(c.ID, r); containerIP != "" { - return containerIP - } + if containerIP := ds.getIP(c.ID, r); containerIP != "" { + return containerIP } } From 8d8b47596e6e71ae705ed2fd1e946e32a98a42f0 Mon Sep 17 00:00:00 2001 From: Sandeep Rajan Date: Thu, 24 May 2018 08:12:50 -0400 Subject: [PATCH 046/416] update manifest fix test --- cluster/addons/dns/coredns.yaml.base | 8 +++++- cluster/addons/dns/coredns.yaml.in | 8 +++++- cluster/addons/dns/coredns.yaml.sed | 8 +++++- .../app/phases/addons/dns/manifests.go | 6 ++++ cmd/kubeadm/app/phases/addons/dns/versions.go | 6 ++-- .../app/phases/upgrade/compute_test.go | 28 +++++++++---------- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/cluster/addons/dns/coredns.yaml.base b/cluster/addons/dns/coredns.yaml.base index 012ad34dca0..9732029ae92 100644 --- a/cluster/addons/dns/coredns.yaml.base +++ b/cluster/addons/dns/coredns.yaml.base @@ -66,6 +66,7 @@ data: prometheus :9153 proxy . /etc/resolv.conf cache 30 + reload } --- apiVersion: extensions/v1beta1 @@ -103,7 +104,7 @@ spec: operator: "Exists" containers: - name: coredns - image: coredns/coredns:1.0.6 + image: coredns/coredns:1.1.3 imagePullPolicy: IfNotPresent resources: limits: @@ -122,6 +123,9 @@ spec: - containerPort: 53 name: dns-tcp protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP livenessProbe: httpGet: path: /health @@ -145,6 +149,8 @@ kind: Service metadata: name: kube-dns namespace: kube-system + annotations: + prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" diff --git a/cluster/addons/dns/coredns.yaml.in b/cluster/addons/dns/coredns.yaml.in index 5ccf70bd0cd..25d005aac99 100644 --- a/cluster/addons/dns/coredns.yaml.in +++ b/cluster/addons/dns/coredns.yaml.in @@ -66,6 +66,7 @@ data: prometheus :9153 proxy . /etc/resolv.conf cache 30 + reload } --- apiVersion: extensions/v1beta1 @@ -103,7 +104,7 @@ spec: operator: "Exists" containers: - name: coredns - image: coredns/coredns:1.0.6 + image: coredns/coredns:1.1.3 imagePullPolicy: IfNotPresent resources: limits: @@ -122,6 +123,9 @@ spec: - containerPort: 53 name: dns-tcp protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP livenessProbe: httpGet: path: /health @@ -145,6 +149,8 @@ kind: Service metadata: name: kube-dns namespace: kube-system + annotations: + prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" diff --git a/cluster/addons/dns/coredns.yaml.sed b/cluster/addons/dns/coredns.yaml.sed index d635755e770..42105f905d1 100644 --- a/cluster/addons/dns/coredns.yaml.sed +++ b/cluster/addons/dns/coredns.yaml.sed @@ -66,6 +66,7 @@ data: prometheus :9153 proxy . /etc/resolv.conf cache 30 + reload } --- apiVersion: extensions/v1beta1 @@ -103,7 +104,7 @@ spec: operator: "Exists" containers: - name: coredns - image: coredns/coredns:1.0.6 + image: coredns/coredns:1.1.3 imagePullPolicy: IfNotPresent resources: limits: @@ -122,6 +123,9 @@ spec: - containerPort: 53 name: dns-tcp protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP livenessProbe: httpGet: path: /health @@ -145,6 +149,8 @@ kind: Service metadata: name: kube-dns namespace: kube-system + annotations: + prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" diff --git a/cmd/kubeadm/app/phases/addons/dns/manifests.go b/cmd/kubeadm/app/phases/addons/dns/manifests.go index 4184555da68..2a765f634ec 100644 --- a/cmd/kubeadm/app/phases/addons/dns/manifests.go +++ b/cmd/kubeadm/app/phases/addons/dns/manifests.go @@ -196,6 +196,8 @@ metadata: kubernetes.io/name: "KubeDNS" name: kube-dns namespace: kube-system + annotations: + prometheus.io/scrape: "true" # Without this resourceVersion value, an update of the Service between versions will yield: # Service "kube-dns" is invalid: metadata.resourceVersion: Invalid value: "": must be specified for an update resourceVersion: "0" @@ -264,6 +266,9 @@ spec: - containerPort: 53 name: dns-tcp protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP livenessProbe: httpGet: path: /health @@ -303,6 +308,7 @@ data: prometheus :9153 proxy . {{ .UpstreamNameserver }} cache 30 + reload }{{ .StubDomain }} ` // CoreDNSClusterRole is the CoreDNS ClusterRole manifest diff --git a/cmd/kubeadm/app/phases/addons/dns/versions.go b/cmd/kubeadm/app/phases/addons/dns/versions.go index e72eb325fb3..a12d28dfd61 100644 --- a/cmd/kubeadm/app/phases/addons/dns/versions.go +++ b/cmd/kubeadm/app/phases/addons/dns/versions.go @@ -23,13 +23,13 @@ import ( const ( kubeDNSVersion = "1.14.10" - coreDNSVersion = "1.0.6" + coreDNSVersion = "1.1.3" ) // GetDNSVersion returns the right kube-dns version for a specific k8s version func GetDNSVersion(kubeVersion *version.Version, dns string) string { - // v1.9.0+ uses kube-dns 1.14.10 - // v1.9.0+ uses CoreDNS 1.0.6 if feature gate "CoreDNS" is enabled. + // v1.9.0+ uses kube-dns 1.14.10, if feature gate "CoreDNS" is disabled. + // v1.9.0+ uses CoreDNS 1.1.3. // In the future when the version is bumped at HEAD; add conditional logic to return the right versions // Also, the version might be bumped for different k8s releases on the same branch diff --git a/cmd/kubeadm/app/phases/upgrade/compute_test.go b/cmd/kubeadm/app/phases/upgrade/compute_test.go index 7050ed636af..ee17ee76a87 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute_test.go +++ b/cmd/kubeadm/app/phases/upgrade/compute_test.go @@ -168,7 +168,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.10.3", KubeadmVersion: "v1.10.3", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.1.12", }, }, @@ -207,7 +207,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.10.3", KubeadmVersion: "v1.10.3", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.1.12", }, }, @@ -246,7 +246,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0", KubeadmVersion: "v1.11.0", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -285,7 +285,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.10.5", KubeadmVersion: "v1.10.5", // Note: The kubeadm version mustn't be "downgraded" here DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.1.12", }, }, @@ -305,7 +305,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.1", KubeadmVersion: "v1.11.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -364,7 +364,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0-alpha.2", KubeadmVersion: "v1.11.0-alpha.2", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -404,7 +404,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0-alpha.2", KubeadmVersion: "v1.11.0-alpha.2", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -445,7 +445,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0-beta.1", KubeadmVersion: "v1.11.0-beta.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -486,7 +486,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0-rc.1", KubeadmVersion: "v1.11.0-rc.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -527,7 +527,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.6-rc.1", KubeadmVersion: "v1.11.6-rc.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -568,7 +568,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.11.0-rc.1", KubeadmVersion: "v1.11.0-rc.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -588,7 +588,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.12.0-alpha.2", KubeadmVersion: "v1.12.0-alpha.2", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -641,7 +641,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.12.1", KubeadmVersion: "v1.12.1", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, @@ -678,7 +678,7 @@ func TestGetAvailableUpgrades(t *testing.T) { KubeVersion: "v1.12.0", KubeadmVersion: "v1.12.0", DNSType: "coredns", - DNSVersion: "1.0.6", + DNSVersion: "1.1.3", EtcdVersion: "3.2.18", }, }, From ac015892e4e2ab641a7761ab79f8cf986e1fa266 Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Thu, 24 May 2018 09:33:43 -0400 Subject: [PATCH 047/416] add PST to main SECURITY_CONTACTS as formality Add the Product Security Team as the security contacts for the main repository and they can use the OWNERS files in each subsystem/dir to find the correct owners. Signed-off-by: Jess Frazelle --- SECURITY_CONTACTS | 17 +++++++++++++++++ staging/src/k8s.io/api/SECURITY_CONTACTS | 17 +++++++++++++++++ .../apiextensions-apiserver/SECURITY_CONTACTS | 17 +++++++++++++++++ .../src/k8s.io/apimachinery/SECURITY_CONTACTS | 17 +++++++++++++++++ staging/src/k8s.io/apiserver/SECURITY_CONTACTS | 17 +++++++++++++++++ staging/src/k8s.io/client-go/SECURITY_CONTACTS | 17 +++++++++++++++++ .../src/k8s.io/code-generator/SECURITY_CONTACTS | 17 +++++++++++++++++ .../k8s.io/kube-aggregator/SECURITY_CONTACTS | 17 +++++++++++++++++ staging/src/k8s.io/metrics/SECURITY_CONTACTS | 17 +++++++++++++++++ .../k8s.io/sample-apiserver/SECURITY_CONTACTS | 17 +++++++++++++++++ .../k8s.io/sample-controller/SECURITY_CONTACTS | 17 +++++++++++++++++ 11 files changed, 187 insertions(+) create mode 100644 SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/api/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/apiextensions-apiserver/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/apimachinery/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/apiserver/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/client-go/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/code-generator/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/kube-aggregator/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/metrics/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/sample-apiserver/SECURITY_CONTACTS create mode 100644 staging/src/k8s.io/sample-controller/SECURITY_CONTACTS diff --git a/SECURITY_CONTACTS b/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/api/SECURITY_CONTACTS b/staging/src/k8s.io/api/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/api/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/apiextensions-apiserver/SECURITY_CONTACTS b/staging/src/k8s.io/apiextensions-apiserver/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/apimachinery/SECURITY_CONTACTS b/staging/src/k8s.io/apimachinery/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/apiserver/SECURITY_CONTACTS b/staging/src/k8s.io/apiserver/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/apiserver/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/client-go/SECURITY_CONTACTS b/staging/src/k8s.io/client-go/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/client-go/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/code-generator/SECURITY_CONTACTS b/staging/src/k8s.io/code-generator/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/code-generator/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/kube-aggregator/SECURITY_CONTACTS b/staging/src/k8s.io/kube-aggregator/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/kube-aggregator/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/metrics/SECURITY_CONTACTS b/staging/src/k8s.io/metrics/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/metrics/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/sample-apiserver/SECURITY_CONTACTS b/staging/src/k8s.io/sample-apiserver/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/sample-apiserver/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair diff --git a/staging/src/k8s.io/sample-controller/SECURITY_CONTACTS b/staging/src/k8s.io/sample-controller/SECURITY_CONTACTS new file mode 100644 index 00000000000..0648a8ebff7 --- /dev/null +++ b/staging/src/k8s.io/sample-controller/SECURITY_CONTACTS @@ -0,0 +1,17 @@ +# Defined below are the security contacts for this repo. +# +# They are the contact point for the Product Security Team to reach out +# to for triaging and handling of incoming issues. +# +# The below names agree to abide by the +# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy) +# and will be removed and replaced if they violate that agreement. +# +# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE +# INSTRUCTIONS AT https://kubernetes.io/security/ + +cjcullen +jessfraz +liggitt +philips +tallclair From e9c7fe57d33046c60ebddaccc4f604d89e7cecfc Mon Sep 17 00:00:00 2001 From: Zhen Wang Date: Thu, 24 May 2018 11:22:17 -0700 Subject: [PATCH 048/416] Use default seccomp profile for flutend-elasticsearch addon --- cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml | 1 + cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml b/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml index eecd6b4801b..6d8f959b7d2 100644 --- a/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml +++ b/cluster/addons/fluentd-elasticsearch/fluentd-es-ds.yaml @@ -71,6 +71,7 @@ spec: # Note that this does not guarantee admission on the nodes (#40573). annotations: scheduler.alpha.kubernetes.io/critical-pod: '' + seccomp.security.alpha.kubernetes.io/pod: 'docker/default' spec: priorityClassName: system-node-critical serviceAccountName: fluentd-es diff --git a/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml b/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml index 8cd00fe52e5..d6653dd15b4 100644 --- a/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml +++ b/cluster/addons/fluentd-elasticsearch/kibana-deployment.yaml @@ -16,6 +16,8 @@ spec: metadata: labels: k8s-app: kibana-logging + annotations: + seccomp.security.alpha.kubernetes.io/pod: 'docker/default' spec: containers: - name: kibana-logging From ebc254c40f9e4834d34ba6be39fb08f0dc4a265f Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 24 May 2018 19:48:49 +0300 Subject: [PATCH 049/416] kubeadm: rename the `kube-dns` phases addon The command `kubeadm alpha phases addon` has a property called `kube-dns` which would install kube-dns, pre 1.11. In the case of 1.11 this property will install CoreDNS, because the property is also bound to the `CoreDNS` feature gate, which is now `true` by default. Fix that by renaming the property to `coredns`, updating the Cobra info and also updating the unit tests. --- cmd/kubeadm/app/cmd/phases/addons.go | 18 +++++++++--------- cmd/kubeadm/app/cmd/phases/addons_test.go | 2 +- docs/.generated_docs | 4 ++-- ...md => kubeadm_alpha_phase_addon_coredns.md} | 0 ...s.1 => kubeadm-alpha-phase-addon-coredns.1} | 0 5 files changed, 12 insertions(+), 12 deletions(-) rename docs/admin/{kubeadm_alpha_phase_addon_kube-dns.md => kubeadm_alpha_phase_addon_coredns.md} (100%) rename docs/man/man1/{kubeadm-alpha-phase-addon-kube-dns.1 => kubeadm-alpha-phase-addon-coredns.1} (100%) diff --git a/cmd/kubeadm/app/cmd/phases/addons.go b/cmd/kubeadm/app/cmd/phases/addons.go index 4951b233827..26ce873ab9e 100644 --- a/cmd/kubeadm/app/cmd/phases/addons.go +++ b/cmd/kubeadm/app/cmd/phases/addons.go @@ -39,24 +39,24 @@ import ( var ( allAddonsLongDesc = normalizer.LongDesc(` - Installs the kube-dns and the kube-proxys addons components via the API server. + Installs the CoreDNS and the kube-proxys addons components via the API server. Please note that although the DNS server is deployed, it will not be scheduled until CNI is installed. ` + cmdutil.AlphaDisclaimer) allAddonsExample = normalizer.Examples(` - # Installs the kube-dns and the kube-proxys addons components via the API server, + # Installs the CoreDNS and the kube-proxys addons components via the API server, # functionally equivalent to what installed by kubeadm init. kubeadm alpha phase selfhosting from-staticpods `) - kubednsAddonsLongDesc = normalizer.LongDesc(` - Installs the kube-dns addon components via the API server. + corednsAddonsLongDesc = normalizer.LongDesc(` + Installs the CoreDNS addon components via the API server. Please note that although the DNS server is deployed, it will not be scheduled until CNI is installed. ` + cmdutil.AlphaDisclaimer) kubeproxyAddonsLongDesc = normalizer.LongDesc(` - Installs the kube-proxy addon components via the API server. + Installs the kube-proxy addon components via the API server. ` + cmdutil.AlphaDisclaimer) ) @@ -116,9 +116,9 @@ func getAddonsSubCommands() []*cobra.Command { cmdFunc: EnsureAllAddons, }, { - use: "kube-dns", - short: "Installs the kube-dns addon to a Kubernetes cluster", - long: kubednsAddonsLongDesc, + use: "coredns", + short: "Installs the CoreDNS addon to a Kubernetes cluster", + long: corednsAddonsLongDesc, cmdFunc: dnsaddon.EnsureDNSAddon, }, { @@ -151,7 +151,7 @@ func getAddonsSubCommands() []*cobra.Command { cmd.Flags().StringVar(&cfg.Networking.PodSubnet, "pod-network-cidr", cfg.Networking.PodSubnet, `The range of IP addresses used for the Pod network`) } - if properties.use == "all" || properties.use == "kube-dns" { + if properties.use == "all" || properties.use == "coredns" { cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, `Alternative domain for services`) cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, `The range of IP address used for service VIPs`) cmd.Flags().StringVar(&featureGatesString, "feature-gates", featureGatesString, "A set of key=value pairs that describe feature gates for various features."+ diff --git a/cmd/kubeadm/app/cmd/phases/addons_test.go b/cmd/kubeadm/app/cmd/phases/addons_test.go index e2333ec305b..713e7f8dbda 100644 --- a/cmd/kubeadm/app/cmd/phases/addons_test.go +++ b/cmd/kubeadm/app/cmd/phases/addons_test.go @@ -56,7 +56,7 @@ func TestAddonsSubCommandsHasFlags(t *testing.T) { }, }, { - command: "kube-dns", + command: "coredns", additionalFlags: []string{ "service-dns-domain", "service-cidr", diff --git a/docs/.generated_docs b/docs/.generated_docs index 4cbec27e77f..91926436747 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -9,7 +9,7 @@ docs/admin/kubeadm_alpha.md docs/admin/kubeadm_alpha_phase.md docs/admin/kubeadm_alpha_phase_addon.md docs/admin/kubeadm_alpha_phase_addon_all.md -docs/admin/kubeadm_alpha_phase_addon_kube-dns.md +docs/admin/kubeadm_alpha_phase_addon_coredns.md docs/admin/kubeadm_alpha_phase_addon_kube-proxy.md docs/admin/kubeadm_alpha_phase_bootstrap-token.md docs/admin/kubeadm_alpha_phase_bootstrap-token_all.md @@ -86,7 +86,7 @@ docs/man/man1/kube-controller-manager.1 docs/man/man1/kube-proxy.1 docs/man/man1/kube-scheduler.1 docs/man/man1/kubeadm-alpha-phase-addon-all.1 -docs/man/man1/kubeadm-alpha-phase-addon-kube-dns.1 +docs/man/man1/kubeadm-alpha-phase-addon-coredns.1 docs/man/man1/kubeadm-alpha-phase-addon-kube-proxy.1 docs/man/man1/kubeadm-alpha-phase-addon.1 docs/man/man1/kubeadm-alpha-phase-bootstrap-token-all.1 diff --git a/docs/admin/kubeadm_alpha_phase_addon_kube-dns.md b/docs/admin/kubeadm_alpha_phase_addon_coredns.md similarity index 100% rename from docs/admin/kubeadm_alpha_phase_addon_kube-dns.md rename to docs/admin/kubeadm_alpha_phase_addon_coredns.md diff --git a/docs/man/man1/kubeadm-alpha-phase-addon-kube-dns.1 b/docs/man/man1/kubeadm-alpha-phase-addon-coredns.1 similarity index 100% rename from docs/man/man1/kubeadm-alpha-phase-addon-kube-dns.1 rename to docs/man/man1/kubeadm-alpha-phase-addon-coredns.1 From 1f7671b18dbfad7a84835404ddbc9cdaa5ce09d8 Mon Sep 17 00:00:00 2001 From: Andrew Lytvynov Date: Thu, 24 May 2018 15:05:48 -0700 Subject: [PATCH 050/416] Pull gke-exec-auth-plugin binary on Nodes If the plugin URL is set and VM is not master, pull the plugin binary. --- cluster/gce/gci/configure.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cluster/gce/gci/configure.sh b/cluster/gce/gci/configure.sh index c950c045b82..d8bd2baebd1 100644 --- a/cluster/gce/gci/configure.sh +++ b/cluster/gce/gci/configure.sh @@ -264,6 +264,18 @@ runtime-endpoint: ${CONTAINER_RUNTIME_ENDPOINT:-unix:///var/run/dockershim.sock} EOF } +function install-exec-auth-plugin { + if [[ ! ${EXEC_AUTH_PLUGIN_URL:-} ]]; then + return + fi + local -r plugin_url="${EXEC_AUTH_PLUGIN_URL}" + local -r plugin_sha1="${EXEC_AUTH_PLUGIN_SHA1}" + + echo "Downloading gke-exec-auth-plugin binary" + download-or-bust "${plugin_sha1}" "${plugin_url}" + mv "${KUBE_HOME}/gke-exec-auth-plugin" "${KUBE_BIN}" +} + function install-kube-manifests { # Put kube-system pods manifests in ${KUBE_HOME}/kube-manifests/. local dst_dir="${KUBE_HOME}/kube-manifests" @@ -403,6 +415,10 @@ function install-kube-binary-config { # Install crictl on each node. install-crictl + if [[ "${KUBERNETES_MASTER:-}" == "false" ]]; then + install-exec-auth-plugin + fi + # Clean up. rm -rf "${KUBE_HOME}/kubernetes" rm -f "${KUBE_HOME}/${server_binary_tar}" From f2cb23ad6a14169f5e2a82c5d3b1bd6ebc1074a5 Mon Sep 17 00:00:00 2001 From: Guoliang Wang Date: Fri, 25 May 2018 13:59:05 +0800 Subject: [PATCH 051/416] HandleError include the type of the error object --- .../apiserver/pkg/endpoints/handlers/responsewriters/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go index e37a8ea4786..99673077b2b 100755 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go @@ -61,7 +61,7 @@ func ErrorToAPIStatus(err error) *metav1.Status { // by REST storage - these typically indicate programmer // error by not using pkg/api/errors, or unexpected failure // cases. - runtime.HandleError(fmt.Errorf("apiserver received an error that is not an metav1.Status: %v", err)) + runtime.HandleError(fmt.Errorf("apiserver received an error that is not an metav1.Status: %#+v", err)) return &metav1.Status{ TypeMeta: metav1.TypeMeta{ Kind: "Status", From c7dc3334369d7b06fa3e29384e44be53fe0c2f0e Mon Sep 17 00:00:00 2001 From: lovejoy Date: Thu, 24 May 2018 17:17:59 +0800 Subject: [PATCH 052/416] fixChineseTranslation --- translations/kubectl/zh_CN/LC_MESSAGES/k8s.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/kubectl/zh_CN/LC_MESSAGES/k8s.po b/translations/kubectl/zh_CN/LC_MESSAGES/k8s.po index 1db8ac6ea3c..fc91db119f9 100644 --- a/translations/kubectl/zh_CN/LC_MESSAGES/k8s.po +++ b/translations/kubectl/zh_CN/LC_MESSAGES/k8s.po @@ -2390,7 +2390,7 @@ msgstr "显示一个或更多 resources" # https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/config/current_context.go#L48 #: pkg/kubectl/cmd/config/current_context.go:49 msgid "Displays the current-context" -msgstr "显示 current_context" +msgstr "显示当前的 context" # https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/explain.go#L50 #: pkg/kubectl/cmd/explain.go:51 From cc210a4505c3ef4fa8c8e1cf2e3cf468a631caef Mon Sep 17 00:00:00 2001 From: xuzhonghu Date: Fri, 25 May 2018 18:03:22 +0800 Subject: [PATCH 053/416] fix toleration validation invalid error --- pkg/apis/core/validation/validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 6a01b7f6254..81fd33d183f 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -2749,7 +2749,7 @@ func validateTaintEffect(effect *core.TaintEffect, allowEmpty bool, fldPath *fie // TODO: Uncomment this block when implement TaintEffectNoScheduleNoAdmit. // string(core.TaintEffectNoScheduleNoAdmit), } - allErrors = append(allErrors, field.NotSupported(fldPath, effect, validValues)) + allErrors = append(allErrors, field.NotSupported(fldPath, *effect, validValues)) } return allErrors } From 5a099d70c9d2c68afc8f149b0119119d3a928c14 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Fri, 25 May 2018 14:02:59 +0200 Subject: [PATCH 054/416] Move Ceph server secret creation to common code. --- test/e2e/framework/volume_util.go | 25 ++++++++++- test/e2e/storage/volume_io.go | 32 +++----------- test/e2e/storage/volumes.go | 73 +++---------------------------- 3 files changed, 34 insertions(+), 96 deletions(-) diff --git a/test/e2e/framework/volume_util.go b/test/e2e/framework/volume_util.go index 1f0a8e019f7..9eb5269ad62 100644 --- a/test/e2e/framework/volume_util.go +++ b/test/e2e/framework/volume_util.go @@ -186,7 +186,7 @@ func NewISCSIServer(cs clientset.Interface, namespace string) (config VolumeTest } // CephRBD-specific wrapper for CreateStorageServer. -func NewRBDServer(cs clientset.Interface, namespace string) (config VolumeTestConfig, pod *v1.Pod, ip string) { +func NewRBDServer(cs clientset.Interface, namespace string) (config VolumeTestConfig, pod *v1.Pod, secret *v1.Secret, ip string) { config = VolumeTestConfig{ Namespace: namespace, Prefix: "rbd", @@ -205,7 +205,28 @@ func NewRBDServer(cs clientset.Interface, namespace string) (config VolumeTestCo Logf("sleeping a bit to give ceph server time to initialize") time.Sleep(VolumeServerPodStartupSleep) - return config, pod, ip + // create secrets for the server + secret = &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.Prefix + "-secret", + }, + Data: map[string][]byte{ + // from test/images/volumes-tester/rbd/keyring + "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="), + }, + Type: "kubernetes.io/rbd", + } + + secret, err := cs.CoreV1().Secrets(config.Namespace).Create(secret) + if err != nil { + Failf("Failed to create secrets for Ceph RBD: %v", err) + } + + return config, pod, secret, ip } // Wrapper for StartVolumeServer(). A storage server config is passed in, and a pod pointer diff --git a/test/e2e/storage/volume_io.go b/test/e2e/storage/volume_io.go index 04c803b78cd..9ed5e245fff 100644 --- a/test/e2e/storage/volume_io.go +++ b/test/e2e/storage/volume_io.go @@ -385,33 +385,11 @@ var _ = utils.SIGDescribe("Volume plugin streaming [Slow]", func() { Describe("Ceph-RBD [Feature:Volumes]", func() { var ( secret *v1.Secret - name string ) testFile := "ceph-rbd_io_test" BeforeEach(func() { - config, serverPod, serverIP = framework.NewRBDServer(cs, ns) - name = config.Prefix + "-server" - - // create server secret - secret = &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Data: map[string][]byte{ - // from test/images/volumes-tester/rbd/keyring - "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="), - }, - Type: "kubernetes.io/rbd", - } - var err error - secret, err = cs.CoreV1().Secrets(ns).Create(secret) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("BeforeEach: failed to create secret %q for Ceph-RBD: %v", name, err)) - + config, serverPod, secret, serverIP = framework.NewRBDServer(cs, ns) volSource = v1.VolumeSource{ RBD: &v1.RBDVolumeSource{ CephMonitors: []string{serverIP}, @@ -419,7 +397,7 @@ var _ = utils.SIGDescribe("Volume plugin streaming [Slow]", func() { RBDImage: "foo", RadosUser: "admin", SecretRef: &v1.LocalObjectReference{ - Name: name, + Name: secret.Name, }, FSType: "ext2", ReadOnly: false, @@ -428,13 +406,13 @@ var _ = utils.SIGDescribe("Volume plugin streaming [Slow]", func() { }) AfterEach(func() { - framework.Logf("AfterEach: deleting Ceph-RDB server secret %q...", name) - secErr := cs.CoreV1().Secrets(ns).Delete(name, &metav1.DeleteOptions{}) + framework.Logf("AfterEach: deleting Ceph-RDB server secret %q...", secret.Name) + secErr := cs.CoreV1().Secrets(ns).Delete(secret.Name, &metav1.DeleteOptions{}) framework.Logf("AfterEach: deleting Ceph-RDB server pod %q...", serverPod.Name) err := framework.DeletePodWithWait(f, cs, serverPod) if secErr != nil || err != nil { if secErr != nil { - framework.Logf("AfterEach: Ceph-RDB delete secret failed: %v", err) + framework.Logf("AfterEach: Ceph-RDB delete secret failed: %v", secErr) } if err != nil { framework.Logf("AfterEach: Ceph-RDB server pod delete failed: %v", err) diff --git a/test/e2e/storage/volumes.go b/test/e2e/storage/volumes.go index 3be74537258..0a4a2e944a8 100644 --- a/test/e2e/storage/volumes.go +++ b/test/e2e/storage/volumes.go @@ -57,7 +57,6 @@ import ( "k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/storage/utils" vspheretest "k8s.io/kubernetes/test/e2e/storage/vsphere" - imageutils "k8s.io/kubernetes/test/utils/image" ) func DeleteCinderVolume(name string) error { @@ -200,34 +199,9 @@ var _ = utils.SIGDescribe("Volumes", func() { Describe("Ceph RBD [Feature:Volumes]", func() { It("should be mountable", func() { - config, _, serverIP := framework.NewRBDServer(cs, namespace.Name) + config, _, secret, serverIP := framework.NewRBDServer(cs, namespace.Name) defer framework.VolumeTestCleanup(f, config) - - // create secrets for the server - secret := v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.Prefix + "-secret", - }, - Data: map[string][]byte{ - // from test/images/volumes-tester/rbd/keyring - "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="), - }, - Type: "kubernetes.io/rbd", - } - - secClient := cs.CoreV1().Secrets(config.Namespace) - - defer func() { - secClient.Delete(config.Prefix+"-secret", nil) - }() - - if _, err := secClient.Create(&secret); err != nil { - framework.Failf("Failed to create secrets for Ceph RBD: %v", err) - } + defer cs.CoreV1().Secrets(config.Namespace).Delete(secret.Name, nil) tests := []framework.VolumeTest{ { @@ -238,7 +212,7 @@ var _ = utils.SIGDescribe("Volumes", func() { RBDImage: "foo", RadosUser: "admin", SecretRef: &v1.LocalObjectReference{ - Name: config.Prefix + "-secret", + Name: secret.Name, }, FSType: "ext2", }, @@ -258,44 +232,9 @@ var _ = utils.SIGDescribe("Volumes", func() { //////////////////////////////////////////////////////////////////////// Describe("CephFS [Feature:Volumes]", func() { It("should be mountable", func() { - config := framework.VolumeTestConfig{ - Namespace: namespace.Name, - Prefix: "cephfs", - ServerImage: imageutils.GetE2EImage(imageutils.VolumeRBDServer), - ServerPorts: []int{6789}, - } - + config, _, secret, serverIP := framework.NewRBDServer(cs, namespace.Name) defer framework.VolumeTestCleanup(f, config) - _, serverIP := framework.CreateStorageServer(cs, config) - By("sleeping a bit to give ceph server time to initialize") - time.Sleep(framework.VolumeServerPodStartupSleep) - - // create ceph secret - secret := &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: config.Prefix + "-secret", - }, - // from test/images/volumes-tester/rbd/keyring - Data: map[string][]byte{ - "key": []byte("AQDRrKNVbEevChAAEmRC+pW/KBVHxa0w/POILA=="), - }, - Type: "kubernetes.io/cephfs", - } - - defer func() { - if err := cs.CoreV1().Secrets(namespace.Name).Delete(secret.Name, nil); err != nil { - framework.Failf("unable to delete secret %v: %v", secret.Name, err) - } - }() - - var err error - if secret, err = cs.CoreV1().Secrets(namespace.Name).Create(secret); err != nil { - framework.Failf("unable to create test secret %s: %v", secret.Name, err) - } + defer cs.CoreV1().Secrets(config.Namespace).Delete(secret.Name, nil) tests := []framework.VolumeTest{ { @@ -303,7 +242,7 @@ var _ = utils.SIGDescribe("Volumes", func() { CephFS: &v1.CephFSVolumeSource{ Monitors: []string{serverIP + ":6789"}, User: "kube", - SecretRef: &v1.LocalObjectReference{Name: config.Prefix + "-secret"}, + SecretRef: &v1.LocalObjectReference{Name: secret.Name}, ReadOnly: true, }, }, From 2a9d491fb8de037003e9c2bd1f5f76eff0eb31e9 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 25 May 2018 09:05:38 -0400 Subject: [PATCH 055/416] Revert "Change default min-startup-pods value" This reverts commit de0bf05f4637f9f40c0648ef8b341b66726ee46f. --- test/e2e/framework/test_context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go index e3158e8ca4f..b07d02aa282 100644 --- a/test/e2e/framework/test_context.go +++ b/test/e2e/framework/test_context.go @@ -266,7 +266,7 @@ func RegisterClusterFlags() { flag.StringVar(&cloudConfig.ClusterTag, "cluster-tag", "", "Tag used to identify resources. Only required if provider is aws.") flag.StringVar(&cloudConfig.ConfigFile, "cloud-config-file", "", "Cloud config file. Only required if provider is azure.") - flag.IntVar(&TestContext.MinStartupPods, "minStartupPods", 8, "The number of pods which we need to see in 'Running' state with a 'Ready' condition of true, before we try running tests. This is useful in any cluster which needs some base pod-based services running before it can be used.") + flag.IntVar(&TestContext.MinStartupPods, "minStartupPods", 0, "The number of pods which we need to see in 'Running' state with a 'Ready' condition of true, before we try running tests. This is useful in any cluster which needs some base pod-based services running before it can be used.") flag.DurationVar(&TestContext.SystemPodsStartupTimeout, "system-pods-startup-timeout", 10*time.Minute, "Timeout for waiting for all system pods to be running before starting tests.") flag.DurationVar(&TestContext.NodeSchedulableTimeout, "node-schedulable-timeout", 30*time.Minute, "Timeout for waiting for all nodes to be schedulable.") flag.StringVar(&TestContext.UpgradeTarget, "upgrade-target", "ci/latest", "Version to upgrade to (e.g. 'release/stable', 'release/latest', 'ci/latest', '0.19.1', '0.19.1-669-gabac8c8') if doing an upgrade test.") From 7b5f3a1dc58f9accc01c7e90ec5b8373334cff7f Mon Sep 17 00:00:00 2001 From: Daniel Gonzalez Date: Thu, 24 May 2018 14:19:20 +0200 Subject: [PATCH 056/416] Ensure that only IPs are used as node addresses in OpenStack LBs --- .../openstack/openstack_loadbalancer.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go b/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go index e38d2d157bd..72bccb7e35a 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go +++ b/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go @@ -487,21 +487,27 @@ func (lbaas *LbaasV2) GetLoadBalancer(ctx context.Context, clusterName string, s // The LB needs to be configured with instance addresses on the same // subnet as the LB (aka opts.SubnetID). Currently we're just -// guessing that the node's InternalIP is the right address - and that -// should be sufficient for all "normal" cases. +// guessing that the node's InternalIP is the right address. +// In case no InternalIP can be found, ExternalIP is tried. +// If neither InternalIP nor ExternalIP can be found an error is +// returned. func nodeAddressForLB(node *v1.Node) (string, error) { addrs := node.Status.Addresses if len(addrs) == 0 { return "", ErrNoAddressFound } - for _, addr := range addrs { - if addr.Type == v1.NodeInternalIP { - return addr.Address, nil + allowedAddrTypes := []v1.NodeAddressType{v1.NodeInternalIP, v1.NodeExternalIP} + + for _, allowedAddrType := range allowedAddrTypes { + for _, addr := range addrs { + if addr.Type == allowedAddrType { + return addr.Address, nil + } } } - return addrs[0].Address, nil + return "", ErrNoAddressFound } //getStringFromServiceAnnotation searches a given v1.Service for a specific annotationKey and either returns the annotation's value or a specified defaultSetting From 28b6f34107ceffb9b71e600f238100d563eb12b5 Mon Sep 17 00:00:00 2001 From: Yecheng Fu Date: Sat, 26 May 2018 00:09:25 +0800 Subject: [PATCH 057/416] Should use `hostProcMountinfoPath` constant in nsenter_mount.go. --- pkg/util/mount/nsenter_mount.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/util/mount/nsenter_mount.go b/pkg/util/mount/nsenter_mount.go index 9b0464c329a..a5ca17ff5f1 100644 --- a/pkg/util/mount/nsenter_mount.go +++ b/pkg/util/mount/nsenter_mount.go @@ -333,7 +333,7 @@ func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) { if err != nil { return nil, err } - return searchMountPoints(hostpath, procMountInfoPath) + return searchMountPoints(hostpath, hostProcMountinfoPath) } func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { @@ -345,5 +345,5 @@ func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) { } func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) { - return getSELinuxSupport(pathname, procMountInfoPath) + return getSELinuxSupport(pathname, hostProcMountsPath) } From d38afb136793bc719281797016a0cccd4d0efaa1 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Fri, 25 May 2018 19:19:12 +0300 Subject: [PATCH 058/416] remove CrictlChecker from preflight checks CrictlChecker uses InPathCheck to check if crictl presents in the PATH. The same check is done in CRICheck function. CrictlChecker is also called unconditionally, producing messages that can confuse users. CRICheck is called only when needed, i.e. when user specifies CRI socket. --- cmd/kubeadm/app/preflight/checks.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index 8b3c5cb6a98..3aa67e7bfb3 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -959,15 +959,6 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfigura // addCommonChecks is a helper function to deplicate checks that are common between both the // kubeadm init and join commands func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfiguration, checks []Checker) []Checker { - // check if we can use crictl to perform checks via the CRI - glog.V(1).Infoln("checking if we can use crictl to perform checks via the CRI") - criCtlChecker := InPathCheck{ - executable: "crictl", - mandatory: false, - exec: execer, - suggestion: fmt.Sprintf("go get %v", kubeadmconstants.CRICtlPackage), - } - // Check whether or not the CRI socket defined is the default if cfg.GetCRISocket() != kubeadmdefaults.DefaultCRISocket { checks = append(checks, CRICheck{socket: cfg.GetCRISocket(), exec: execer}) @@ -990,7 +981,6 @@ func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfigurat InPathCheck{executable: "socat", mandatory: false, exec: execer}, InPathCheck{executable: "tc", mandatory: false, exec: execer}, InPathCheck{executable: "touch", mandatory: false, exec: execer}, - criCtlChecker, ResolveCheck{}) } checks = append(checks, From 4c3fa4f9baa79ab08a4d39e5d40fb66617af111d Mon Sep 17 00:00:00 2001 From: andrewsykim Date: Fri, 25 May 2018 12:37:56 -0400 Subject: [PATCH 059/416] disable PersistentVolumeLabel admission controller by default --- pkg/kubeapiserver/options/plugins.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/kubeapiserver/options/plugins.go b/pkg/kubeapiserver/options/plugins.go index a17c3584aeb..35f1763027a 100644 --- a/pkg/kubeapiserver/options/plugins.go +++ b/pkg/kubeapiserver/options/plugins.go @@ -131,7 +131,6 @@ func DefaultOffAdmissionPlugins() sets.String { lifecycle.PluginName, //NamespaceLifecycle limitranger.PluginName, //LimitRanger serviceaccount.PluginName, //ServiceAccount - label.PluginName, //PersistentVolumeLabel setdefault.PluginName, //DefaultStorageClass defaulttolerationseconds.PluginName, //DefaultTolerationSeconds mutatingwebhook.PluginName, //MutatingAdmissionWebhook From 25436cdc6a86b9fed05aa100980a4b51243f2e5a Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Fri, 25 May 2018 18:55:35 +0300 Subject: [PATCH 060/416] fix parsing 'crictl pods -q' output Output of crictl pods -q is a list of running pod ids, one id per line. Current code splits this output incorrectly which makes next command 'crictl stopp' fail if there is more than one pod running. Should be fixed by using strings.Fields instead of strings.Split. --- cmd/kubeadm/app/cmd/reset.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kubeadm/app/cmd/reset.go b/cmd/kubeadm/app/cmd/reset.go index 81d2cc83cc0..1c6a84aa100 100644 --- a/cmd/kubeadm/app/cmd/reset.go +++ b/cmd/kubeadm/app/cmd/reset.go @@ -215,7 +215,7 @@ func resetWithCrictl(execer utilsexec.Interface, dockerCheck preflight.Checker, resetWithDocker(execer, dockerCheck) return } - sandboxes := strings.Split(string(output), " ") + sandboxes := strings.Fields(string(output)) glog.V(1).Infoln("[reset] Stopping and removing running containers using crictl") for _, s := range sandboxes { if strings.TrimSpace(s) == "" { From 2f2de31d3d76ea8659a96771eaababdd091c83c2 Mon Sep 17 00:00:00 2001 From: Chuck Ha Date: Mon, 21 May 2018 13:12:07 -0400 Subject: [PATCH 061/416] Prepulls images by default kubeadm now pulls container images before the init step if it cannot find them on the system * This commit also cleans up a dependency cycle Closes #825 --- cmd/kubeadm/app/cmd/config.go | 2 +- cmd/kubeadm/app/cmd/init.go | 3 + cmd/kubeadm/app/images/BUILD | 8 +- cmd/kubeadm/app/images/interface.go | 89 ++++++++ cmd/kubeadm/app/images/interface_test.go | 266 +++++++++++++++++++++++ cmd/kubeadm/app/images/puller.go | 57 ----- cmd/kubeadm/app/images/puller_test.go | 138 ------------ cmd/kubeadm/app/preflight/BUILD | 1 + cmd/kubeadm/app/preflight/checks.go | 44 ++++ cmd/kubeadm/app/preflight/checks_test.go | 30 +++ cmd/kubeadm/app/util/BUILD | 2 - cmd/kubeadm/app/util/error.go | 9 +- cmd/kubeadm/app/util/error_test.go | 8 +- 13 files changed, 450 insertions(+), 207 deletions(-) create mode 100644 cmd/kubeadm/app/images/interface.go create mode 100644 cmd/kubeadm/app/images/interface_test.go delete mode 100644 cmd/kubeadm/app/images/puller.go delete mode 100644 cmd/kubeadm/app/images/puller_test.go diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index a06cc315d37..62f89e92788 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -301,7 +301,7 @@ func NewCmdConfigImagesPull() *cobra.Command { kubeadmutil.CheckErr(err) internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg) kubeadmutil.CheckErr(err) - puller, err := images.NewImagePuller(utilsexec.New(), internalcfg.GetCRISocket()) + puller, err := images.NewCRInterfacer(utilsexec.New(), internalcfg.GetCRISocket()) kubeadmutil.CheckErr(err) imagesPull := NewImagesPull(puller, images.GetAllImages(internalcfg)) kubeadmutil.CheckErr(imagesPull.PullAll()) diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index e763d8fd7c0..50d3beaa62a 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -258,6 +258,9 @@ func NewInit(cfgPath string, externalcfg *kubeadmapiv1alpha2.MasterConfiguration if err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, ignorePreflightErrors); err != nil { return nil, err } + if err := preflight.RunPullImagesCheck(utilsexec.New(), cfg, ignorePreflightErrors); err != nil { + return nil, err + } return &Init{cfg: cfg, skipTokenPrint: skipTokenPrint, dryRun: dryRun, ignorePreflightErrors: ignorePreflightErrors}, nil } diff --git a/cmd/kubeadm/app/images/BUILD b/cmd/kubeadm/app/images/BUILD index 27e970e71d4..86aa6e0781e 100644 --- a/cmd/kubeadm/app/images/BUILD +++ b/cmd/kubeadm/app/images/BUILD @@ -10,12 +10,12 @@ go_library( name = "go_default_library", srcs = [ "images.go", - "puller.go", + "interface.go", ], importpath = "k8s.io/kubernetes/cmd/kubeadm/app/images", deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", - "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", + "//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/phases/addons/dns:go_default_library", @@ -46,10 +46,10 @@ filegroup( go_test( name = "go_default_xtest", - srcs = ["puller_test.go"], + srcs = ["interface_test.go"], deps = [ ":go_default_library", - "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", + "//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], ) diff --git a/cmd/kubeadm/app/images/interface.go b/cmd/kubeadm/app/images/interface.go new file mode 100644 index 00000000000..c9bd6b16fa8 --- /dev/null +++ b/cmd/kubeadm/app/images/interface.go @@ -0,0 +1,89 @@ +/* +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 images + +import ( + "fmt" + + kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" + utilsexec "k8s.io/utils/exec" +) + +// Puller is an interface for pulling images +type Puller interface { + Pull(string) error +} + +// Existence is an interface to determine if an image exists on the system +// A nil error means the image was found +type Existence interface { + Exists(string) error +} + +// Images defines the set of behaviors needed for images relating to the CRI +type Images interface { + Puller + Existence +} + +// CRInterfacer is a struct that interfaces with the container runtime +type CRInterfacer struct { + criSocket string + exec utilsexec.Interface + crictlPath string + dockerPath string +} + +// NewCRInterfacer sets up and returns a CRInterfacer +func NewCRInterfacer(execer utilsexec.Interface, criSocket string) (*CRInterfacer, error) { + var crictlPath, dockerPath string + var err error + if criSocket != kubeadmapiv1alpha2.DefaultCRISocket { + if crictlPath, err = execer.LookPath("crictl"); err != nil { + return nil, fmt.Errorf("crictl is required for non docker container runtimes: %v", err) + } + } else { + // use the dockershim + if dockerPath, err = execer.LookPath("docker"); err != nil { + return nil, fmt.Errorf("`docker` is required when docker is the container runtime and the kubelet is not running: %v", err) + } + } + + return &CRInterfacer{ + exec: execer, + criSocket: criSocket, + crictlPath: crictlPath, + dockerPath: dockerPath, + }, nil +} + +// Pull pulls the actual image using either crictl or docker +func (cri *CRInterfacer) Pull(image string) error { + if cri.criSocket != kubeadmapiv1alpha2.DefaultCRISocket { + return cri.exec.Command(cri.crictlPath, "-r", cri.criSocket, "pull", image).Run() + } + return cri.exec.Command(cri.dockerPath, "pull", image).Run() +} + +// Exists checks to see if the image exists on the system already +// Returns an error if the image is not found. +func (cri *CRInterfacer) Exists(image string) error { + if cri.criSocket != kubeadmapiv1alpha2.DefaultCRISocket { + return cri.exec.Command(cri.crictlPath, "-r", cri.criSocket, "inspecti", image).Run() + } + return cri.exec.Command(cri.dockerPath, "inspect", image).Run() +} diff --git a/cmd/kubeadm/app/images/interface_test.go b/cmd/kubeadm/app/images/interface_test.go new file mode 100644 index 00000000000..54e176aaf6a --- /dev/null +++ b/cmd/kubeadm/app/images/interface_test.go @@ -0,0 +1,266 @@ +/* +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 images_test + +import ( + "context" + "errors" + "io" + "testing" + + kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" + "k8s.io/kubernetes/cmd/kubeadm/app/images" + "k8s.io/utils/exec" +) + +type fakeCmd struct { + err error +} + +func (f *fakeCmd) Run() error { + return f.err +} +func (f *fakeCmd) CombinedOutput() ([]byte, error) { return nil, nil } +func (f *fakeCmd) Output() ([]byte, error) { return nil, nil } +func (f *fakeCmd) SetDir(dir string) {} +func (f *fakeCmd) SetStdin(in io.Reader) {} +func (f *fakeCmd) SetStdout(out io.Writer) {} +func (f *fakeCmd) SetStderr(out io.Writer) {} +func (f *fakeCmd) Stop() {} + +type fakeExecer struct { + cmd exec.Cmd + findCrictl bool + findDocker bool +} + +func (f *fakeExecer) Command(cmd string, args ...string) exec.Cmd { return f.cmd } +func (f *fakeExecer) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd { + return f.cmd +} +func (f *fakeExecer) LookPath(file string) (string, error) { + if file == "crictl" { + if f.findCrictl { + return "/path", nil + } + return "", errors.New("no crictl for you") + } + if file == "docker" { + if f.findDocker { + return "/path", nil + } + return "", errors.New("no docker for you") + } + return "", errors.New("unknown binary") +} + +func TestNewCRInterfacer(t *testing.T) { + testcases := []struct { + name string + criSocket string + findCrictl bool + findDocker bool + expectError bool + }{ + { + name: "need crictl but can only find docker should return an error", + criSocket: "/not/docker", + findCrictl: false, + findDocker: true, + expectError: true, + }, + { + name: "need crictl and cannot find either should return an error", + criSocket: "/not/docker", + findCrictl: false, + findDocker: false, + expectError: true, + }, + { + name: "need crictl and cannot find docker should return no error", + criSocket: "/not/docker", + findCrictl: true, + findDocker: false, + expectError: false, + }, + { + name: "need crictl and can find both should return no error", + criSocket: "/not/docker", + findCrictl: true, + findDocker: true, + expectError: false, + }, + { + name: "need docker and cannot find crictl should return no error", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + findCrictl: false, + findDocker: true, + expectError: false, + }, + { + name: "need docker and cannot find docker should return an error", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + findCrictl: false, + findDocker: false, + expectError: true, + }, + { + name: "need docker and can find both should return no error", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + findCrictl: true, + findDocker: true, + expectError: false, + }, + { + name: "need docker and can only find crictl should return an error", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + findCrictl: true, + findDocker: false, + expectError: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + fe := &fakeExecer{ + findCrictl: tc.findCrictl, + findDocker: tc.findDocker, + } + _, err := images.NewCRInterfacer(fe, tc.criSocket) + if tc.expectError && err == nil { + t.Fatal("expected an error but did not get one") + } + if !tc.expectError && err != nil { + t.Fatalf("did not expedt an error but got an error: %v", err) + } + }) + } +} + +func TestImagePuller(t *testing.T) { + testcases := []struct { + name string + criSocket string + pullFails bool + errorExpected bool + }{ + { + name: "using docker and pull fails", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + pullFails: true, + errorExpected: true, + }, + { + name: "using docker and pull succeeds", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + pullFails: false, + errorExpected: false, + }, + { + name: "using crictl pull fails", + criSocket: "/not/default", + pullFails: true, + errorExpected: true, + }, + { + name: "using crictl and pull succeeds", + criSocket: "/not/default", + pullFails: false, + errorExpected: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + var err error + if tc.pullFails { + err = errors.New("error") + } + + fe := &fakeExecer{ + cmd: &fakeCmd{err}, + findCrictl: true, + findDocker: true, + } + ip, _ := images.NewCRInterfacer(fe, tc.criSocket) + + err = ip.Pull("imageName") + if tc.errorExpected && err == nil { + t.Fatal("expected an error and did not get one") + } + if !tc.errorExpected && err != nil { + t.Fatalf("expected no error but got one: %v", err) + } + }) + } +} + +func TestImageExists(t *testing.T) { + testcases := []struct { + name string + criSocket string + existFails bool + errorExpected bool + }{ + { + name: "using docker and exist fails", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + existFails: true, + errorExpected: true, + }, + { + name: "using docker and exist succeeds", + criSocket: kubeadmapiv1alpha2.DefaultCRISocket, + existFails: false, + errorExpected: false, + }, + { + name: "using crictl exist fails", + criSocket: "/not/default", + existFails: true, + errorExpected: true, + }, + { + name: "using crictl and exist succeeds", + criSocket: "/not/default", + existFails: false, + errorExpected: false, + }, + } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + var err error + if tc.existFails { + err = errors.New("error") + } + + fe := &fakeExecer{ + cmd: &fakeCmd{err}, + findCrictl: true, + findDocker: true, + } + ip, _ := images.NewCRInterfacer(fe, tc.criSocket) + + err = ip.Exists("imageName") + if tc.errorExpected && err == nil { + t.Fatal("expected an error and did not get one") + } + if !tc.errorExpected && err != nil { + t.Fatalf("expected no error but got one: %v", err) + } + }) + } +} diff --git a/cmd/kubeadm/app/images/puller.go b/cmd/kubeadm/app/images/puller.go deleted file mode 100644 index 71db11e481f..00000000000 --- a/cmd/kubeadm/app/images/puller.go +++ /dev/null @@ -1,57 +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 images - -import ( - "fmt" - - kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" - utilsexec "k8s.io/utils/exec" -) - -// Puller is an interface for pulling images -type Puller interface { - Pull(string) error -} - -// ImagePuller is a struct that can pull images and hides the implementation (crictl vs docker) -type ImagePuller struct { - criSocket string - exec utilsexec.Interface - crictlPath string -} - -// NewImagePuller returns a ready to go ImagePuller -func NewImagePuller(execer utilsexec.Interface, criSocket string) (*ImagePuller, error) { - crictlPath, err := execer.LookPath("crictl") - if err != nil && criSocket != kubeadmapiv1alpha1.DefaultCRISocket { - return nil, fmt.Errorf("crictl is required for non docker container runtimes: %v", err) - } - return &ImagePuller{ - exec: execer, - criSocket: criSocket, - crictlPath: crictlPath, - }, nil -} - -// Pull pulls the actual image using either crictl or docker -func (ip *ImagePuller) Pull(image string) error { - if ip.criSocket != kubeadmapiv1alpha1.DefaultCRISocket { - return ip.exec.Command(ip.crictlPath, "-r", ip.criSocket, "pull", image).Run() - } - return ip.exec.Command("sh", "-c", fmt.Sprintf("docker pull %v", image)).Run() -} diff --git a/cmd/kubeadm/app/images/puller_test.go b/cmd/kubeadm/app/images/puller_test.go deleted file mode 100644 index 6a27ec03276..00000000000 --- a/cmd/kubeadm/app/images/puller_test.go +++ /dev/null @@ -1,138 +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 images_test - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "os" - "strings" - "testing" - - kubeadmdefaults "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" - "k8s.io/kubernetes/cmd/kubeadm/app/images" - "k8s.io/utils/exec" -) - -type fakeCmd struct { - cmd string - args []string - out io.Writer -} - -func (f *fakeCmd) Run() error { - fmt.Fprintf(f.out, "%v %v", f.cmd, strings.Join(f.args, " ")) - return nil -} -func (f *fakeCmd) CombinedOutput() ([]byte, error) { return nil, nil } -func (f *fakeCmd) Output() ([]byte, error) { return nil, nil } -func (f *fakeCmd) SetDir(dir string) {} -func (f *fakeCmd) SetStdin(in io.Reader) {} -func (f *fakeCmd) SetStdout(out io.Writer) { - f.out = out -} -func (f *fakeCmd) SetStderr(out io.Writer) {} -func (f *fakeCmd) Stop() {} - -type fakeExecer struct { - cmd exec.Cmd - lookPathSucceeds bool -} - -func (f *fakeExecer) Command(cmd string, args ...string) exec.Cmd { return f.cmd } -func (f *fakeExecer) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd { - return f.cmd -} -func (f *fakeExecer) LookPath(file string) (string, error) { - if f.lookPathSucceeds { - return file, nil - } - return "", &os.PathError{Err: errors.New("does not exist")} -} - -func TestImagePuller(t *testing.T) { - testcases := []struct { - name string - criSocket string - cmd exec.Cmd - findCrictl bool - expected string - errorExpected bool - }{ - { - name: "New succeeds even if crictl is not in path", - criSocket: kubeadmdefaults.DefaultCRISocket, - cmd: &fakeCmd{ - cmd: "hello", - args: []string{"world", "and", "friends"}, - }, - findCrictl: false, - expected: "hello world and friends", - }, - { - name: "New succeeds with crictl in path", - criSocket: "/not/default", - cmd: &fakeCmd{ - cmd: "crictl", - args: []string{"-r", "/some/socket", "imagename"}, - }, - findCrictl: true, - expected: "crictl -r /some/socket imagename", - }, - { - name: "New fails with crictl not in path but is required", - criSocket: "/not/docker", - cmd: &fakeCmd{ - cmd: "crictl", - args: []string{"-r", "/not/docker", "an image"}, - }, - findCrictl: false, - errorExpected: true, - }, - } - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - var b bytes.Buffer - tc.cmd.SetStdout(&b) - fe := &fakeExecer{ - cmd: tc.cmd, - lookPathSucceeds: tc.findCrictl, - } - ip, err := images.NewImagePuller(fe, tc.criSocket) - - if tc.errorExpected { - if err == nil { - t.Fatalf("expected an error but found nil: %v", fe) - } - return - } - - if err != nil { - t.Fatalf("expected nil but found an error: %v", err) - } - if err = ip.Pull("imageName"); err != nil { - t.Fatalf("expected nil pulling an image but found: %v", err) - } - if b.String() != tc.expected { - t.Fatalf("expected %v but got: %v", tc.expected, b.String()) - } - }) - } -} diff --git a/cmd/kubeadm/app/preflight/BUILD b/cmd/kubeadm/app/preflight/BUILD index d6b0d412cdf..a7a26a04e9e 100644 --- a/cmd/kubeadm/app/preflight/BUILD +++ b/cmd/kubeadm/app/preflight/BUILD @@ -52,6 +52,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//cmd/kubeadm/app/images:go_default_library", "//pkg/apis/core/validation:go_default_library", "//pkg/registry/core/service/ipallocator:go_default_library", "//pkg/util/initsystem:go_default_library", diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index 8b3c5cb6a98..922ca00825d 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -46,6 +46,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmdefaults "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/images" "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" "k8s.io/kubernetes/pkg/util/initsystem" @@ -76,10 +77,16 @@ type Error struct { Msg string } +// Error implements the standard error interface func (e *Error) Error() string { return fmt.Sprintf("[preflight] Some fatal errors occurred:\n%s%s", e.Msg, "[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`") } +// Preflight identifies this error as a preflight error +func (e *Error) Preflight() bool { + return true +} + // Checker validates the state of the system to ensure kubeadm will be // successful as often as possible. type Checker interface { @@ -850,6 +857,30 @@ func (ResolveCheck) Check() (warnings, errors []error) { return warnings, errors } +// ImagePullCheck will pull container images used by kubeadm +type ImagePullCheck struct { + Images images.Images + ImageList []string +} + +// Name returns the label for ImagePullCheck +func (ImagePullCheck) Name() string { + return "ImagePull" +} + +// Check pulls images required by kubeadm. This is a mutating check +func (i ImagePullCheck) Check() (warnings, errors []error) { + for _, image := range i.ImageList { + if err := i.Images.Exists(image); err == nil { + continue + } + if err := i.Images.Pull(image); err != nil { + errors = append(errors, fmt.Errorf("failed to pull image [%s]: %v", image, err)) + } + } + return warnings, errors +} + // RunInitMasterChecks executes all individual, applicable to Master node checks. func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, ignorePreflightErrors sets.String) error { // First, check if we're root separately from the other preflight checks and fail fast @@ -1012,6 +1043,19 @@ func RunRootCheckOnly(ignorePreflightErrors sets.String) error { return RunChecks(checks, os.Stderr, ignorePreflightErrors) } +// RunPullImagesCheck will pull images kubeadm needs if the are not found on the system +func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, ignorePreflightErrors sets.String) error { + criInterfacer, err := images.NewCRInterfacer(execer, cfg.GetCRISocket()) + if err != nil { + return err + } + + checks := []Checker{ + ImagePullCheck{Images: criInterfacer, ImageList: images.GetAllImages(cfg)}, + } + return RunChecks(checks, os.Stderr, ignorePreflightErrors) +} + // RunChecks runs each check, displays it's warnings/errors, and once all // are processed will exit if any errors occurred. func RunChecks(checks []Checker, ww io.Writer, ignorePreflightErrors sets.String) error { diff --git a/cmd/kubeadm/app/preflight/checks_test.go b/cmd/kubeadm/app/preflight/checks_test.go index cfea0abc450..f6908c7c3e0 100644 --- a/cmd/kubeadm/app/preflight/checks_test.go +++ b/cmd/kubeadm/app/preflight/checks_test.go @@ -18,6 +18,7 @@ package preflight import ( "bytes" + "errors" "fmt" "io/ioutil" "strings" @@ -696,3 +697,32 @@ func TestSetHasItemOrAll(t *testing.T) { } } } + +type imgs struct{} + +func (i *imgs) Pull(image string) error { + if image == "bad pull" { + return errors.New("pull error") + } + return nil +} +func (i *imgs) Exists(image string) error { + if image == "found" { + return nil + } + return errors.New("error") +} + +func TestImagePullCheck(t *testing.T) { + i := ImagePullCheck{ + Images: &imgs{}, + ImageList: []string{"found", "not found", "bad pull"}, + } + warnings, errors := i.Check() + if len(warnings) != 0 { + t.Fatalf("did not expect any warnings but got %q", warnings) + } + if len(errors) != 1 { + t.Fatalf("expected 1 errors but got %d: %q", len(errors), errors) + } +} diff --git a/cmd/kubeadm/app/util/BUILD b/cmd/kubeadm/app/util/BUILD index 3dfc790b59e..181784908cd 100644 --- a/cmd/kubeadm/app/util/BUILD +++ b/cmd/kubeadm/app/util/BUILD @@ -20,7 +20,6 @@ go_library( importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util", deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", - "//cmd/kubeadm/app/preflight:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", @@ -47,7 +46,6 @@ go_test( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", - "//cmd/kubeadm/app/preflight:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", ], diff --git a/cmd/kubeadm/app/util/error.go b/cmd/kubeadm/app/util/error.go index 61327cf5608..58a892dd191 100644 --- a/cmd/kubeadm/app/util/error.go +++ b/cmd/kubeadm/app/util/error.go @@ -22,7 +22,6 @@ import ( "strings" utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/kubernetes/cmd/kubeadm/app/preflight" ) const ( @@ -60,13 +59,19 @@ func CheckErr(err error) { checkErr("", err, fatal) } +// preflightError allows us to know if the error is a preflight error or not +// defining the interface here avoids an import cycle of pulling in preflight into the util package +type preflightError interface { + Preflight() bool +} + // checkErr formats a given error as a string and calls the passed handleErr // func with that string and an kubectl exit code. func checkErr(prefix string, err error, handleErr func(string, int)) { switch err.(type) { case nil: return - case *preflight.Error: + case preflightError: handleErr(err.Error(), PreFlightExitCode) case utilerrors.Aggregate: handleErr(err.Error(), ValidationExitCode) diff --git a/cmd/kubeadm/app/util/error_test.go b/cmd/kubeadm/app/util/error_test.go index c28a6cc0566..94f131babae 100644 --- a/cmd/kubeadm/app/util/error_test.go +++ b/cmd/kubeadm/app/util/error_test.go @@ -19,10 +19,12 @@ package util import ( "fmt" "testing" - - "k8s.io/kubernetes/cmd/kubeadm/app/preflight" ) +type pferror struct{} + +func (p *pferror) Preflight() bool { return true } +func (p *pferror) Error() string { return "" } func TestCheckErr(t *testing.T) { var codeReturned int errHandle := func(err string, code int) { @@ -35,7 +37,7 @@ func TestCheckErr(t *testing.T) { }{ {nil, 0}, {fmt.Errorf(""), DefaultErrorExitCode}, - {&preflight.Error{}, PreFlightExitCode}, + {&pferror{}, PreFlightExitCode}, } for _, rt := range tokenTest { From 069062365aa79880621058065c882ae0dbf90e0d Mon Sep 17 00:00:00 2001 From: Ashley Gau Date: Fri, 25 May 2018 14:19:00 -0700 Subject: [PATCH 062/416] use fakeGCECloud instead of gce address fakes --- .../providers/gce/cloud/mock/mock.go | 2 +- .../providers/gce/gce_address_manager_test.go | 67 ++--- .../providers/gce/gce_addresses_fakes.go | 239 ------------------ 3 files changed, 39 insertions(+), 269 deletions(-) delete mode 100644 pkg/cloudprovider/providers/gce/gce_addresses_fakes.go diff --git a/pkg/cloudprovider/providers/gce/cloud/mock/mock.go b/pkg/cloudprovider/providers/gce/cloud/mock/mock.go index f0e87cf0ce7..523691c635e 100644 --- a/pkg/cloudprovider/providers/gce/cloud/mock/mock.go +++ b/pkg/cloudprovider/providers/gce/cloud/mock/mock.go @@ -204,7 +204,7 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta. errorCode = http.StatusBadRequest } - return false, &googleapi.Error{Code: errorCode, Message: msg} + return true, &googleapi.Error{Code: errorCode, Message: msg} } } diff --git a/pkg/cloudprovider/providers/gce/gce_address_manager_test.go b/pkg/cloudprovider/providers/gce/gce_address_manager_test.go index 3c7d60dc564..3b7409c6c19 100644 --- a/pkg/cloudprovider/providers/gce/gce_address_manager_test.go +++ b/pkg/cloudprovider/providers/gce/gce_address_manager_test.go @@ -26,95 +26,104 @@ import ( ) const testSvcName = "my-service" -const testRegion = "us-central1" const testSubnet = "/projects/x/testRegions/us-central1/testSubnetworks/customsub" const testLBName = "a111111111111111" +var vals = DefaultTestClusterValues() + // TestAddressManagerNoRequestedIP tests the typical case of passing in no requested IP func TestAddressManagerNoRequestedIP(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) targetIP := "" - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) - testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal)) - testReleaseAddress(t, mgr, svc, testLBName, testRegion) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal)) + testReleaseAddress(t, mgr, svc, testLBName, vals.Region) } // TestAddressManagerBasic tests the typical case of reserving and unreserving an address. func TestAddressManagerBasic(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) targetIP := "1.1.1.1" - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) - testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal)) - testReleaseAddress(t, mgr, svc, testLBName, testRegion) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal)) + testReleaseAddress(t, mgr, svc, testLBName, vals.Region) } // TestAddressManagerOrphaned tests the case where the address exists with the IP being equal // to the requested address (forwarding rule or loadbalancer IP). func TestAddressManagerOrphaned(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) targetIP := "1.1.1.1" addr := &compute.Address{Name: testLBName, Address: targetIP, AddressType: string(cloud.SchemeInternal)} - err := svc.ReserveRegionAddress(addr, testRegion) + err = svc.ReserveRegionAddress(addr, vals.Region) require.NoError(t, err) - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) - testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal)) - testReleaseAddress(t, mgr, svc, testLBName, testRegion) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal)) + testReleaseAddress(t, mgr, svc, testLBName, vals.Region) } // TestAddressManagerOutdatedOrphan tests the case where an address exists but points to // an IP other than the forwarding rule or loadbalancer IP. func TestAddressManagerOutdatedOrphan(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) previousAddress := "1.1.0.0" targetIP := "1.1.1.1" addr := &compute.Address{Name: testLBName, Address: previousAddress, AddressType: string(cloud.SchemeExternal)} - err := svc.ReserveRegionAddress(addr, testRegion) + err = svc.ReserveRegionAddress(addr, vals.Region) require.NoError(t, err) - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) - testHoldAddress(t, mgr, svc, testLBName, testRegion, targetIP, string(cloud.SchemeInternal)) - testReleaseAddress(t, mgr, svc, testLBName, testRegion) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + testHoldAddress(t, mgr, svc, testLBName, vals.Region, targetIP, string(cloud.SchemeInternal)) + testReleaseAddress(t, mgr, svc, testLBName, vals.Region) } // TestAddressManagerExternallyOwned tests the case where the address exists but isn't // owned by the controller. func TestAddressManagerExternallyOwned(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) targetIP := "1.1.1.1" addr := &compute.Address{Name: "my-important-address", Address: targetIP, AddressType: string(cloud.SchemeInternal)} - err := svc.ReserveRegionAddress(addr, testRegion) + err = svc.ReserveRegionAddress(addr, vals.Region) require.NoError(t, err) - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) ipToUse, err := mgr.HoldAddress() require.NoError(t, err) assert.NotEmpty(t, ipToUse) - _, err = svc.GetRegionAddress(testLBName, testRegion) + ad, err := svc.GetRegionAddress(testLBName, vals.Region) assert.True(t, isNotFound(err)) + require.Nil(t, ad) - testReleaseAddress(t, mgr, svc, testLBName, testRegion) + testReleaseAddress(t, mgr, svc, testLBName, vals.Region) } // TestAddressManagerExternallyOwned tests the case where the address exists but isn't // owned by the controller. However, this address has the wrong type. func TestAddressManagerBadExternallyOwned(t *testing.T) { - svc := NewFakeCloudAddressService() + svc, err := fakeGCECloud(vals) + require.NoError(t, err) targetIP := "1.1.1.1" addr := &compute.Address{Name: "my-important-address", Address: targetIP, AddressType: string(cloud.SchemeExternal)} - err := svc.ReserveRegionAddress(addr, testRegion) + err = svc.ReserveRegionAddress(addr, vals.Region) require.NoError(t, err) - mgr := newAddressManager(svc, testSvcName, testRegion, testSubnet, testLBName, targetIP, cloud.SchemeInternal) - _, err = mgr.HoldAddress() - assert.NotNil(t, err) + mgr := newAddressManager(svc, testSvcName, vals.Region, testSubnet, testLBName, targetIP, cloud.SchemeInternal) + ad, err := mgr.HoldAddress() + assert.NotNil(t, err) // FIXME + require.Equal(t, ad, "") } func testHoldAddress(t *testing.T, mgr *addressManager, svc CloudAddressService, name, region, targetIP, scheme string) { diff --git a/pkg/cloudprovider/providers/gce/gce_addresses_fakes.go b/pkg/cloudprovider/providers/gce/gce_addresses_fakes.go deleted file mode 100644 index 75dfa571c9c..00000000000 --- a/pkg/cloudprovider/providers/gce/gce_addresses_fakes.go +++ /dev/null @@ -1,239 +0,0 @@ -/* -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 gce - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - - computealpha "google.golang.org/api/compute/v0.alpha" - computebeta "google.golang.org/api/compute/v0.beta" - compute "google.golang.org/api/compute/v1" - - "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud" -) - -// test - -type FakeCloudAddressService struct { - count int - // reservedAddrs tracks usage of IP addresses - // Key is the IP address as a string - reservedAddrs map[string]bool - // addrsByRegionAndName - // Outer key is for region string; inner key is for address name. - addrsByRegionAndName map[string]map[string]*computealpha.Address -} - -// FakeCloudAddressService Implements CloudAddressService -var _ CloudAddressService = &FakeCloudAddressService{} - -func NewFakeCloudAddressService() *FakeCloudAddressService { - return &FakeCloudAddressService{ - reservedAddrs: make(map[string]bool), - addrsByRegionAndName: make(map[string]map[string]*computealpha.Address), - } -} - -// SetRegionalAddresses sets the addresses of there region. This is used for -// setting the test environment. -func (cas *FakeCloudAddressService) SetRegionalAddresses(region string, addrs []*computealpha.Address) { - // Reset addresses in the region. - cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address) - - for _, addr := range addrs { - cas.reservedAddrs[addr.Address] = true - cas.addrsByRegionAndName[region][addr.Name] = addr - } -} - -func (cas *FakeCloudAddressService) ReserveAlphaRegionAddress(addr *computealpha.Address, region string) error { - if addr.Address == "" { - addr.Address = fmt.Sprintf("1.2.3.%d", cas.count) - cas.count++ - } - - if addr.AddressType == "" { - addr.AddressType = string(cloud.SchemeExternal) - } - - if cas.reservedAddrs[addr.Address] { - msg := "IP in use" - // When the IP is already in use, this call returns an error code based - // on the type (internal vs external) of the address. This is to be - // consistent with actual GCE API. - switch cloud.LbScheme(addr.AddressType) { - case cloud.SchemeExternal: - return makeGoogleAPIError(http.StatusBadRequest, msg) - default: - return makeGoogleAPIError(http.StatusConflict, msg) - } - } - - if _, exists := cas.addrsByRegionAndName[region]; !exists { - cas.addrsByRegionAndName[region] = make(map[string]*computealpha.Address) - } - - if _, exists := cas.addrsByRegionAndName[region][addr.Name]; exists { - return makeGoogleAPIError(http.StatusConflict, "name in use") - } - - cas.addrsByRegionAndName[region][addr.Name] = addr - cas.reservedAddrs[addr.Address] = true - return nil -} - -func (cas *FakeCloudAddressService) ReserveBetaRegionAddress(addr *computebeta.Address, region string) error { - alphaAddr := convertToAlphaAddress(addr) - return cas.ReserveAlphaRegionAddress(alphaAddr, region) -} - -func (cas *FakeCloudAddressService) ReserveRegionAddress(addr *compute.Address, region string) error { - alphaAddr := convertToAlphaAddress(addr) - return cas.ReserveAlphaRegionAddress(alphaAddr, region) -} - -func (cas *FakeCloudAddressService) GetAlphaRegionAddress(name, region string) (*computealpha.Address, error) { - if _, exists := cas.addrsByRegionAndName[region]; !exists { - return nil, makeGoogleAPINotFoundError("") - } - - if addr, exists := cas.addrsByRegionAndName[region][name]; !exists { - return nil, makeGoogleAPINotFoundError("") - } else { - return addr, nil - } -} - -func (cas *FakeCloudAddressService) GetBetaRegionAddress(name, region string) (*computebeta.Address, error) { - addr, err := cas.GetAlphaRegionAddress(name, region) - if addr != nil { - return convertToBetaAddress(addr), err - } - return nil, err -} - -func (cas *FakeCloudAddressService) GetRegionAddress(name, region string) (*compute.Address, error) { - addr, err := cas.GetAlphaRegionAddress(name, region) - if addr != nil { - return convertToV1Address(addr), err - } - return nil, err -} - -func (cas *FakeCloudAddressService) DeleteRegionAddress(name, region string) error { - if _, exists := cas.addrsByRegionAndName[region]; !exists { - return makeGoogleAPINotFoundError("") - } - - addr, exists := cas.addrsByRegionAndName[region][name] - if !exists { - return makeGoogleAPINotFoundError("") - } - - delete(cas.reservedAddrs, addr.Address) - delete(cas.addrsByRegionAndName[region], name) - return nil -} - -func (cas *FakeCloudAddressService) GetAlphaRegionAddressByIP(region, ipAddress string) (*computealpha.Address, error) { - if _, exists := cas.addrsByRegionAndName[region]; !exists { - return nil, makeGoogleAPINotFoundError("") - } - - for _, addr := range cas.addrsByRegionAndName[region] { - if addr.Address == ipAddress { - return addr, nil - } - } - return nil, makeGoogleAPINotFoundError("") -} - -func (cas *FakeCloudAddressService) GetBetaRegionAddressByIP(name, region string) (*computebeta.Address, error) { - addr, err := cas.GetAlphaRegionAddressByIP(name, region) - if addr != nil { - return convertToBetaAddress(addr), nil - } - return nil, err -} - -func (cas *FakeCloudAddressService) GetRegionAddressByIP(name, region string) (*compute.Address, error) { - addr, err := cas.GetAlphaRegionAddressByIP(name, region) - if addr != nil { - return convertToV1Address(addr), nil - } - return nil, err -} - -func (cas *FakeCloudAddressService) getNetworkTierFromAddress(name, region string) (string, error) { - addr, err := cas.GetAlphaRegionAddress(name, region) - if err != nil { - return "", err - } - return addr.NetworkTier, nil -} - -func convertToV1Address(object gceObject) *compute.Address { - enc, err := object.MarshalJSON() - if err != nil { - panic(fmt.Sprintf("Failed to encode to json: %v", err)) - } - var addr compute.Address - if err := json.Unmarshal(enc, &addr); err != nil { - panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 address: %v", object, err)) - } - return &addr -} - -func convertToAlphaAddress(object gceObject) *computealpha.Address { - enc, err := object.MarshalJSON() - if err != nil { - panic(fmt.Sprintf("Failed to encode to json: %v", err)) - } - var addr computealpha.Address - if err := json.Unmarshal(enc, &addr); err != nil { - panic(fmt.Sprintf("Failed to convert GCE apiObject %v to alpha address: %v", object, err)) - } - // Set the default values for the Alpha fields. - addr.NetworkTier = cloud.NetworkTierDefault.ToGCEValue() - return &addr -} - -func convertToBetaAddress(object gceObject) *computebeta.Address { - enc, err := object.MarshalJSON() - if err != nil { - panic(fmt.Sprintf("Failed to encode to json: %v", err)) - } - var addr computebeta.Address - if err := json.Unmarshal(enc, &addr); err != nil { - panic(fmt.Sprintf("Failed to convert GCE apiObject %v to beta address: %v", object, err)) - } - return &addr -} - -func (cas *FakeCloudAddressService) String() string { - var b bytes.Buffer - for region, regAddresses := range cas.addrsByRegionAndName { - b.WriteString(fmt.Sprintf("%v:\n", region)) - for name, addr := range regAddresses { - b.WriteString(fmt.Sprintf(" %v: %v\n", name, addr.Address)) - } - } - return b.String() -} From a96c5f2884739176a545313f670cfb6d57ff1e24 Mon Sep 17 00:00:00 2001 From: Ashley Gau Date: Fri, 25 May 2018 14:33:33 -0700 Subject: [PATCH 063/416] mocks must return true in order to trigger err --- .../providers/gce/cloud/mock/mock.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/cloudprovider/providers/gce/cloud/mock/mock.go b/pkg/cloudprovider/providers/gce/cloud/mock/mock.go index 523691c635e..6a28c9f8e4b 100644 --- a/pkg/cloudprovider/providers/gce/cloud/mock/mock.go +++ b/pkg/cloudprovider/providers/gce/cloud/mock/mock.go @@ -94,7 +94,7 @@ func RemoveInstanceHook(ctx context.Context, key *meta.Key, req *ga.TargetPoolsR func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules map[meta.Key]*cloud.MockForwardingRulesObj, version meta.Version, projectID string) (bool, error) { if !key.Valid() { - return false, fmt.Errorf("invalid GCE key (%+v)", key) + return true, fmt.Errorf("invalid GCE key (%+v)", key) } if _, ok := mRules[*key]; ok { @@ -102,16 +102,16 @@ func convertAndInsertAlphaForwardingRule(key *meta.Key, obj gceObject, mRules ma Code: http.StatusConflict, Message: fmt.Sprintf("MockForwardingRule %v exists", key), } - return false, err + return true, err } enc, err := obj.MarshalJSON() if err != nil { - return false, err + return true, err } var fwdRule alpha.ForwardingRule if err := json.Unmarshal(enc, &fwdRule); err != nil { - return false, err + return true, err } // Set the default values for the Alpha fields. if fwdRule.NetworkTier == "" { @@ -162,7 +162,7 @@ type AddressAttributes struct { func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta.Key]*cloud.MockAddressesObj, version meta.Version, projectID string, addressAttrs AddressAttributes) (bool, error) { if !key.Valid() { - return false, fmt.Errorf("invalid GCE key (%+v)", key) + return true, fmt.Errorf("invalid GCE key (%+v)", key) } if _, ok := mAddrs[*key]; ok { @@ -170,16 +170,16 @@ func convertAndInsertAlphaAddress(key *meta.Key, obj gceObject, mAddrs map[meta. Code: http.StatusConflict, Message: fmt.Sprintf("MockAddresses %v exists", key), } - return false, err + return true, err } enc, err := obj.MarshalJSON() if err != nil { - return false, err + return true, err } var addr alpha.Address if err := json.Unmarshal(enc, &addr); err != nil { - return false, err + return true, err } // Set default address type if not present. From ffeca161018fd6218532786876070a5fcfe96542 Mon Sep 17 00:00:00 2001 From: liz Date: Fri, 25 May 2018 17:48:17 -0400 Subject: [PATCH 064/416] Remove some unnecessarily gendered pronouns in comments --- cmd/kubeadm/app/cmd/reset.go | 2 +- pkg/scheduler/algorithmprovider/defaults/defaults.go | 2 +- pkg/scheduler/core/extender_test.go | 2 +- staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/kubeadm/app/cmd/reset.go b/cmd/kubeadm/app/cmd/reset.go index 81d2cc83cc0..33754e51f4c 100644 --- a/cmd/kubeadm/app/cmd/reset.go +++ b/cmd/kubeadm/app/cmd/reset.go @@ -156,7 +156,7 @@ func (r *Reset) Run(out io.Writer) error { dirsToClean := []string{"/var/lib/kubelet", "/etc/cni/net.d", "/var/lib/dockershim", "/var/run/kubernetes"} // Only clear etcd data when the etcd manifest is found. In case it is not found, we must assume that the user - // provided external etcd endpoints. In that case, it is his own responsibility to reset etcd + // provided external etcd endpoints. In that case, it is their own responsibility to reset etcd etcdManifestPath := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName, "etcd.yaml") glog.V(1).Infof("[reset] checking for etcd manifest") if _, err := os.Stat(etcdManifestPath); err == nil { diff --git a/pkg/scheduler/algorithmprovider/defaults/defaults.go b/pkg/scheduler/algorithmprovider/defaults/defaults.go index 267e5d6ba5f..2995aa9dd28 100644 --- a/pkg/scheduler/algorithmprovider/defaults/defaults.go +++ b/pkg/scheduler/algorithmprovider/defaults/defaults.go @@ -56,7 +56,7 @@ func init() { // For example: // https://github.com/kubernetes/kubernetes/blob/36a218e/plugin/pkg/scheduler/factory/factory.go#L422 - // Registers predicates and priorities that are not enabled by default, but user can pick when creating his + // Registers predicates and priorities that are not enabled by default, but user can pick when creating their // own set of priorities/predicates. // PodFitsPorts has been replaced by PodFitsHostPorts for better user understanding. diff --git a/pkg/scheduler/core/extender_test.go b/pkg/scheduler/core/extender_test.go index b0e7489312d..31bd82017b8 100644 --- a/pkg/scheduler/core/extender_test.go +++ b/pkg/scheduler/core/extender_test.go @@ -135,7 +135,7 @@ func (f *FakeExtender) ProcessPreemption( nodeToVictimsCopy := map[*v1.Node]*schedulerapi.Victims{} // We don't want to change the original nodeToVictims for k, v := range nodeToVictims { - // In real world implementation, extender's user should have his own way to get node object + // In real world implementation, extender's user should have their own way to get node object // by name if needed (e.g. query kube-apiserver etc). // // For test purpose, we just use node from parameters directly. diff --git a/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go b/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go index 56905c36277..78700c33a88 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go +++ b/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go @@ -167,7 +167,7 @@ func WithMaxInFlightLimit( metrics.DroppedRequests.WithLabelValues(metrics.ReadOnlyKind).Inc() } // at this point we're about to return a 429, BUT not all actors should be rate limited. A system:master is so powerful - // that he should always get an answer. It's a super-admin or a loopback connection. + // that they should always get an answer. It's a super-admin or a loopback connection. if currUser, ok := apirequest.UserFrom(ctx); ok { for _, group := range currUser.GetGroups() { if group == user.SystemPrivilegedGroup { From cf393d7a7bc68338a072cc93353c356ed7462966 Mon Sep 17 00:00:00 2001 From: Ashley Gau Date: Fri, 25 May 2018 14:39:27 -0700 Subject: [PATCH 065/416] remove gce_address_fakes.go from BUILD file --- pkg/cloudprovider/providers/gce/BUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cloudprovider/providers/gce/BUILD b/pkg/cloudprovider/providers/gce/BUILD index 86b2c9e6c67..0185082ebca 100644 --- a/pkg/cloudprovider/providers/gce/BUILD +++ b/pkg/cloudprovider/providers/gce/BUILD @@ -13,7 +13,6 @@ go_library( "gce.go", "gce_address_manager.go", "gce_addresses.go", - "gce_addresses_fakes.go", "gce_alpha.go", "gce_annotations.go", "gce_backendservice.go", From c05e89d0e551c753407941abad4c479883f0629b Mon Sep 17 00:00:00 2001 From: Nick Sardo Date: Fri, 25 May 2018 16:09:16 -0700 Subject: [PATCH 066/416] Fix nodeport repair for ESIPP services --- .../core/service/portallocator/controller/repair.go | 5 +++++ .../service/portallocator/controller/repair_test.go | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/registry/core/service/portallocator/controller/repair.go b/pkg/registry/core/service/portallocator/controller/repair.go index 1e4dc685c3d..c1a4f0dadbd 100644 --- a/pkg/registry/core/service/portallocator/controller/repair.go +++ b/pkg/registry/core/service/portallocator/controller/repair.go @@ -204,5 +204,10 @@ func collectServiceNodePorts(service *api.Service) []int { servicePorts = append(servicePorts, int(servicePort.NodePort)) } } + + if service.Spec.HealthCheckNodePort != 0 { + servicePorts = append(servicePorts, int(service.Spec.HealthCheckNodePort)) + } + return servicePorts } diff --git a/pkg/registry/core/service/portallocator/controller/repair_test.go b/pkg/registry/core/service/portallocator/controller/repair_test.go index 151c791cc39..48c41aa989a 100644 --- a/pkg/registry/core/service/portallocator/controller/repair_test.go +++ b/pkg/registry/core/service/portallocator/controller/repair_test.go @@ -164,6 +164,12 @@ func TestRepairWithExisting(t *testing.T) { Ports: []api.ServicePort{{NodePort: 111}}, }, }, + &api.Service{ + ObjectMeta: metav1.ObjectMeta{Namespace: "six", Name: "six"}, + Spec: api.ServiceSpec{ + HealthCheckNodePort: 144, + }, + }, ) registry := &mockRangeRegistry{ @@ -183,10 +189,10 @@ func TestRepairWithExisting(t *testing.T) { if err != nil { t.Fatal(err) } - if !after.Has(111) || !after.Has(122) || !after.Has(133) { + if !after.Has(111) || !after.Has(122) || !after.Has(133) || !after.Has(144) { t.Errorf("unexpected portallocator state: %#v", after) } - if free := after.Free(); free != 98 { + if free := after.Free(); free != 97 { t.Errorf("unexpected portallocator state: %d free", free) } } From 350d2c2402c9596d67a262c9c30b5c30c0c84bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Osipiuk?= Date: Thu, 24 May 2018 13:24:58 +0200 Subject: [PATCH 067/416] Run cluster-autoscaler+GPU e2e tests for all gpu types --- .../autoscaling/cluster_size_autoscaling.go | 143 +++++++++--------- 1 file changed, 74 insertions(+), 69 deletions(-) diff --git a/test/e2e/autoscaling/cluster_size_autoscaling.go b/test/e2e/autoscaling/cluster_size_autoscaling.go index 51015b95239..c08a9fa4e7b 100644 --- a/test/e2e/autoscaling/cluster_size_autoscaling.go +++ b/test/e2e/autoscaling/cluster_size_autoscaling.go @@ -207,101 +207,106 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() { It("should increase cluster size if pending pods are small [Feature:ClusterSizeAutoscalingScaleUp]", func() { simpleScaleUpTest(0) }) - It("Should scale up GPU pool from 0 [Feature:ClusterSizeAutoscalingGpu]", func() { - framework.SkipUnlessProviderIs("gke") + supportedGpuTypes := []string{"nvidia-tesla-k80", "nvidia-tesla-v100", "nvidia-tesla-p100"} + for _, gpuType := range supportedGpuTypes { - const gpuPoolName = "gpu-pool" - addGpuNodePool(gpuPoolName, "nvidia-tesla-k80", 1, 0) - defer deleteNodePool(gpuPoolName) + It(fmt.Sprintf("Should scale up GPU pool from 0 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() { + framework.SkipUnlessProviderIs("gke") - installNvidiaDriversDaemonSet() + const gpuPoolName = "gpu-pool" + addGpuNodePool(gpuPoolName, gpuType, 1, 0) + defer deleteNodePool(gpuPoolName) - By("Enable autoscaler") - framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) - defer disableAutoscaler(gpuPoolName, 0, 1) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) + installNvidiaDriversDaemonSet() - By("Schedule a pod which requires GPU") - framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) + By("Enable autoscaler") + framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) + defer disableAutoscaler(gpuPoolName, 0, 1) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) - framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, - func(size int) bool { return size == nodeCount+1 }, scaleUpTimeout)) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) - }) + By("Schedule a pod which requires GPU") + framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) - It("Should scale up GPU pool from 1 [Feature:ClusterSizeAutoscalingGpu]", func() { - framework.SkipUnlessProviderIs("gke") + framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, + func(size int) bool { return size == nodeCount+1 }, scaleUpTimeout)) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) + }) - const gpuPoolName = "gpu-pool" - addGpuNodePool(gpuPoolName, "nvidia-tesla-k80", 1, 1) - defer deleteNodePool(gpuPoolName) + It(fmt.Sprintf("Should scale up GPU pool from 1 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() { + framework.SkipUnlessProviderIs("gke") - installNvidiaDriversDaemonSet() + const gpuPoolName = "gpu-pool" + addGpuNodePool(gpuPoolName, gpuType, 1, 1) + defer deleteNodePool(gpuPoolName) - By("Schedule a single pod which requires GPU") - framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) + installNvidiaDriversDaemonSet() - By("Enable autoscaler") - framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 2)) - defer disableAutoscaler(gpuPoolName, 0, 2) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) + By("Schedule a single pod which requires GPU") + framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) - framework.ScaleRC(f.ClientSet, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc", 2, false) + By("Enable autoscaler") + framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 2)) + defer disableAutoscaler(gpuPoolName, 0, 2) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) - framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, - func(size int) bool { return size == nodeCount+2 }, scaleUpTimeout)) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(2)) - }) + By("Scale GPU deployment") + framework.ScaleRC(f.ClientSet, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc", 2, false) - It("Should not scale GPU pool up if pod does not require GPUs [Feature:ClusterSizeAutoscalingGpu]", func() { - framework.SkipUnlessProviderIs("gke") + framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, + func(size int) bool { return size == nodeCount+2 }, scaleUpTimeout)) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(2)) + }) - const gpuPoolName = "gpu-pool" - addGpuNodePool(gpuPoolName, "nvidia-tesla-k80", 1, 0) - defer deleteNodePool(gpuPoolName) + It(fmt.Sprintf("Should not scale GPU pool up if pod does not require GPUs [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() { + framework.SkipUnlessProviderIs("gke") - installNvidiaDriversDaemonSet() + const gpuPoolName = "gpu-pool" + addGpuNodePool(gpuPoolName, gpuType, 1, 0) + defer deleteNodePool(gpuPoolName) - By("Enable autoscaler") - framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) - defer disableAutoscaler(gpuPoolName, 0, 1) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) + installNvidiaDriversDaemonSet() - By("Schedule bunch of pods beyond point of filling default pool but do not request any GPUs") - ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second) - defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation") + By("Enable autoscaler") + framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) + defer disableAutoscaler(gpuPoolName, 0, 1) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) - // Verify that cluster size is increased - framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, - func(size int) bool { return size >= nodeCount+1 }, scaleUpTimeout)) + By("Schedule bunch of pods beyond point of filling default pool but do not request any GPUs") + ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second) + defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation") + // Verify that cluster size is increased + framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, + func(size int) bool { return size >= nodeCount+1 }, scaleUpTimeout)) - // Expect gpu pool to stay intact - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) - }) + // Expect gpu pool to stay intact + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) + }) - It("Should scale down GPU pool from 1 [Feature:ClusterSizeAutoscalingGpu]", func() { - framework.SkipUnlessProviderIs("gke") + It(fmt.Sprintf("Should scale down GPU pool from 1 [GpuType:%s] [Feature:ClusterSizeAutoscalingGpu]", gpuType), func() { + framework.SkipUnlessProviderIs("gke") - const gpuPoolName = "gpu-pool" - addGpuNodePool(gpuPoolName, "nvidia-tesla-k80", 1, 1) - defer deleteNodePool(gpuPoolName) + const gpuPoolName = "gpu-pool" + addGpuNodePool(gpuPoolName, gpuType, 1, 1) + defer deleteNodePool(gpuPoolName) - installNvidiaDriversDaemonSet() + installNvidiaDriversDaemonSet() - By("Schedule a single pod which requires GPU") - framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) + By("Schedule a single pod which requires GPU") + framework.ExpectNoError(scheduleGpuPod(f, "gpu-pod-rc")) - By("Enable autoscaler") - framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) - defer disableAutoscaler(gpuPoolName, 0, 1) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) + By("Enable autoscaler") + framework.ExpectNoError(enableAutoscaler(gpuPoolName, 0, 1)) + defer disableAutoscaler(gpuPoolName, 0, 1) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) - framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc") + By("Remove the only POD requiring GPU") + framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc") - framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, - func(size int) bool { return size == nodeCount }, scaleDownTimeout)) - Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) - }) + framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, + func(size int) bool { return size == nodeCount }, scaleDownTimeout)) + Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(0)) + }) + } It("should increase cluster size if pending pods are small and one node is broken [Feature:ClusterSizeAutoscalingScaleUp]", func() { From 3c8bd9ae24296b0cf9625416330bed47fd8ae027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Osipiuk?= Date: Fri, 25 May 2018 17:05:46 +0200 Subject: [PATCH 068/416] Wait for PODs ready after scale up --- test/e2e/autoscaling/cluster_size_autoscaling.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/autoscaling/cluster_size_autoscaling.go b/test/e2e/autoscaling/cluster_size_autoscaling.go index c08a9fa4e7b..5a35f0ee441 100644 --- a/test/e2e/autoscaling/cluster_size_autoscaling.go +++ b/test/e2e/autoscaling/cluster_size_autoscaling.go @@ -250,7 +250,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() { Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) By("Scale GPU deployment") - framework.ScaleRC(f.ClientSet, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc", 2, false) + framework.ScaleRC(f.ClientSet, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc", 2, true) framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, func(size int) bool { return size == nodeCount+2 }, scaleUpTimeout)) From 6d95bb3fa00b91962d1d21c756f5cd5490df94ca Mon Sep 17 00:00:00 2001 From: Xianglin Gao Date: Sun, 27 May 2018 00:25:10 +0800 Subject: [PATCH 069/416] Improve the help of kubeadm completion --- cmd/kubeadm/app/cmd/completion.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/kubeadm/app/cmd/completion.go b/cmd/kubeadm/app/cmd/completion.go index 33d5526f9a8..9e7722b7fbb 100644 --- a/cmd/kubeadm/app/cmd/completion.go +++ b/cmd/kubeadm/app/cmd/completion.go @@ -51,17 +51,18 @@ var ( The shell code must be evaluated to provide interactive completion of kubeadm commands. This can be done by sourcing it from the .bash_profile. + + Note: this requires the bash-completion framework. - Note: this requires the bash-completion framework, which is not installed - by default on Mac. This can be installed by using homebrew: - + To install it on Mac use homebrew: $ brew install bash-completion - Once installed, bash_completion must be evaluated. This can be done by adding the following line to the .bash_profile - $ source $(brew --prefix)/etc/bash_completion + If bash-completion is not installed on Linux, please install the 'bash-completion' package + via your distribution's package manager. + Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`) completionExample = dedent.Dedent(` From 8d84ef63adb3d5f7eca0414a4c074be6992c953c Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Tue, 22 May 2018 22:41:28 +0300 Subject: [PATCH 070/416] kubeadm: do not use --admission-control for the API server The API server argument --admission-control is deprecated. Use the following arguments instead: --enable-admission-plugins=NodeRestriction --disable-admission-plugins=PersistentVolumeLabel Add comment that PersistentVolumeLabel should be removed at some point in 1.11. --- .../app/phases/controlplane/manifests.go | 13 +++++--- .../app/phases/controlplane/manifests_test.go | 33 ++++++++++++------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cmd/kubeadm/app/phases/controlplane/manifests.go b/cmd/kubeadm/app/phases/controlplane/manifests.go index 17ff9de4390..0a604f5a330 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests.go @@ -39,8 +39,6 @@ import ( "k8s.io/kubernetes/pkg/util/version" ) -const defaultAdmissionControl = "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" - // CreateInitStaticPodManifestFiles will write all static pod manifest files needed to bring up the control plane. func CreateInitStaticPodManifestFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguration) error { glog.V(1).Infoln("[controlplane] creating static pod files") @@ -141,9 +139,14 @@ func createStaticPodFiles(manifestDir string, cfg *kubeadmapi.MasterConfiguratio // getAPIServerCommand builds the right API server command from the given config object and version func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration) []string { defaultArguments := map[string]string{ - "advertise-address": cfg.API.AdvertiseAddress, - "insecure-port": "0", - "admission-control": defaultAdmissionControl, + "advertise-address": cfg.API.AdvertiseAddress, + "insecure-port": "0", + "enable-admission-plugins": "NodeRestriction", + // TODO: remove `PersistentVolumeLabel` in kubeadm v1.11, as it's automatically disabled in v1.11. + // ref: https://github.com/kubernetes/kubernetes/pull/64326 + // we can't skip it now as we support v1.10 clusters still. + // remove it from the unit tests too. + "disable-admission-plugins": "PersistentVolumeLabel", "service-cluster-ip-range": cfg.Networking.ServiceSubnet, "service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName), "client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName), diff --git a/cmd/kubeadm/app/phases/controlplane/manifests_test.go b/cmd/kubeadm/app/phases/controlplane/manifests_test.go index 3e1a7325d33..6da80ae160d 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests_test.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests_test.go @@ -154,7 +154,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -196,7 +197,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -233,7 +235,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -279,7 +282,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -322,7 +326,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -360,7 +365,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -402,7 +408,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -447,7 +454,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=baz", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -491,7 +499,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -531,7 +540,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=1234", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", @@ -571,7 +581,8 @@ func TestGetAPIServerCommand(t *testing.T) { expected: []string{ "kube-apiserver", "--insecure-port=0", - "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota", + "--enable-admission-plugins=NodeRestriction", + "--disable-admission-plugins=PersistentVolumeLabel", "--service-cluster-ip-range=bar", "--service-account-key-file=" + testCertsDir + "/sa.pub", "--client-ca-file=" + testCertsDir + "/ca.crt", From 3988331c6ccdcdd0ab385c41906f4871a23fab49 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Sat, 26 May 2018 21:54:15 -0700 Subject: [PATCH 071/416] Restore InstanceNotFound comment & logic Otherwise node registration is broken on AWS. --- pkg/cloudprovider/cloud.go | 1 + pkg/cloudprovider/providers/aws/aws.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index 530475799df..0358890bcd8 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -131,6 +131,7 @@ type Instances interface { // services cannot be used in this method to obtain nodeaddresses NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) // InstanceID returns the cloud provider ID of the node with the specified NodeName. + // Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound) InstanceID(ctx context.Context, nodeName types.NodeName) (string, error) // InstanceType returns the type of the specified instance. InstanceType(ctx context.Context, name types.NodeName) (string, error) diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index 6ea14ef58ef..e95ad50d892 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -1363,6 +1363,10 @@ func (c *Cloud) InstanceID(ctx context.Context, nodeName types.NodeName) (string } inst, err := c.getInstanceByNodeName(nodeName) if err != nil { + if err == cloudprovider.InstanceNotFound { + // The Instances interface requires that we return InstanceNotFound (without wrapping) + return "", err + } return "", fmt.Errorf("getInstanceByNodeName failed for %q with %q", nodeName, err) } return "/" + aws.StringValue(inst.Placement.AvailabilityZone) + "/" + aws.StringValue(inst.InstanceId), nil From ec12fe1e8427980aed3de8a1f48c63957119ff3a Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Sat, 26 May 2018 22:29:36 -0700 Subject: [PATCH 072/416] ccm: recognize InstanceNotFound from InstanceID Otherwise we won't actually delete Nodes in this code path. --- pkg/controller/cloud/node_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/controller/cloud/node_controller.go b/pkg/controller/cloud/node_controller.go index 0867ee8bbbc..cf38936f366 100644 --- a/pkg/controller/cloud/node_controller.go +++ b/pkg/controller/cloud/node_controller.go @@ -445,6 +445,9 @@ func ensureNodeExistsByProviderID(instances cloudprovider.Instances, node *v1.No var err error providerID, err = instances.InstanceID(context.TODO(), types.NodeName(node.Name)) if err != nil { + if err == cloudprovider.InstanceNotFound { + return false, nil + } return false, err } From 3895887f5e92bb83f8be33e785a53dd2840f554e Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Thu, 24 May 2018 15:57:19 -0400 Subject: [PATCH 073/416] move scaleClient from factory --- pkg/kubectl/cmd/apply.go | 2 +- pkg/kubectl/cmd/rollingupdate.go | 3 +-- pkg/kubectl/cmd/scale.go | 2 +- pkg/kubectl/cmd/util/factory.go | 4 ---- pkg/kubectl/cmd/util/helpers.go | 34 ++++++++++++++++++++++++++++++++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index fc8f67626dc..18819353753 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -220,7 +220,7 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - o.Scaler, err = f.ScaleClient() + o.Scaler, err = cmdutil.ScaleClientFn(f) if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index 24cde12b467..a82ef567ef1 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -214,7 +214,7 @@ func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a return err } - o.ScaleClient, err = f.ScaleClient() + o.ScaleClient, err = cmdutil.ScaleClientFn(f) if err != nil { return err } @@ -242,7 +242,6 @@ func (o *RollingUpdateOptions) Validate(cmd *cobra.Command, args []string) error } func (o *RollingUpdateOptions) Run() error { - filename := "" if len(o.FilenameOptions.Filenames) > 0 { filename = o.FilenameOptions.Filenames[0] diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 6b4ffb1b5ff..d873a1ae2a7 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -289,7 +289,7 @@ func ScaleJob(info *resource.Info, jobsClient batchclient.JobsGetter, count uint } func scaler(f cmdutil.Factory) (kubectl.Scaler, error) { - scalesGetter, err := f.ScaleClient() + scalesGetter, err := cmdutil.ScaleClientFn(f) if err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 0e7f0724797..2718111ae5c 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -25,7 +25,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" - scaleclient "k8s.io/client-go/scale" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" @@ -81,9 +80,6 @@ type Factory interface { Validator(validate bool) (validation.Schema, error) // OpenAPISchema returns the schema openapi schema definition OpenAPISchema() (openapi.Resources, error) - - // ScaleClient gives you back scale getter - ScaleClient() (scaleclient.ScalesGetter, error) } func makePortsString(ports []api.ServicePort, useNodePort bool) string { diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index b4640f67b25..e942b682b14 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -38,6 +38,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "k8s.io/client-go/scale" "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" @@ -673,3 +675,35 @@ func genericDescriber(restClientGetter genericclioptions.RESTClientGetter, mappi eventsClient := clientSet.Core() return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil } + +// ScaleClientFunc provides a ScalesGetter +type ScaleClientFunc func(genericclioptions.RESTClientGetter) (scale.ScalesGetter, error) + +// ScaleClientFn gives a way to easily override the function for unit testing if needed. +var ScaleClientFn ScaleClientFunc = scaleClient + +// scaleClient gives you back scale getter +func scaleClient(restClientGetter genericclioptions.RESTClientGetter) (scale.ScalesGetter, error) { + discoveryClient, err := restClientGetter.ToDiscoveryClient() + if err != nil { + return nil, err + } + + clientConfig, err := restClientGetter.ToRESTConfig() + if err != nil { + return nil, err + } + + setKubernetesDefaults(clientConfig) + restClient, err := rest.RESTClientFor(clientConfig) + if err != nil { + return nil, err + } + resolver := scale.NewDiscoveryScaleKindResolver(discoveryClient) + mapper, err := restClientGetter.ToRESTMapper() + if err != nil { + return nil, err + } + + return scale.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil +} From 7495ab52293423dcc6f4d83a86c21b4e440dce8e Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Fri, 25 May 2018 17:33:22 +0200 Subject: [PATCH 074/416] Remove Generators from Factory --- pkg/kubectl/cmd/expose.go | 2 +- pkg/kubectl/cmd/run.go | 4 +- pkg/kubectl/cmd/util/BUILD | 1 + pkg/kubectl/cmd/util/factory.go | 3 - pkg/kubectl/cmd/util/factory_client_access.go | 222 ---------------- pkg/kubectl/cmd/util/generator.go | 238 ++++++++++++++++++ 6 files changed, 242 insertions(+), 228 deletions(-) create mode 100644 pkg/kubectl/cmd/util/generator.go diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index d36d47d736d..944af7f770e 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -188,7 +188,7 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e return err } - o.Generators = f.Generators + o.Generators = cmdutil.GeneratorFn o.Builder = f.NewBuilder() o.CanBeExposed = polymorphichelpers.CanBeExposedFn o.ClientForMapping = f.ClientForMapping diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index de450080765..a239b34e5a7 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -321,7 +321,7 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e } } - generators := f.Generators("run") + generators := cmdutil.GeneratorFn("run") generator, found := generators[generatorName] if !found { return cmdutil.UsageErrorf(cmd, "generator %q not found", generatorName) @@ -573,7 +573,7 @@ func verifyImagePullPolicy(cmd *cobra.Command) error { } func (o *RunOptions) generateService(f cmdutil.Factory, cmd *cobra.Command, serviceGenerator string, paramsIn map[string]interface{}, namespace string) (*RunObject, error) { - generators := f.Generators("expose") + generators := cmdutil.GeneratorFn("expose") generator, found := generators[serviceGenerator] if !found { return nil, fmt.Errorf("missing service generator: %s", serviceGenerator) diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index fa44ccc6917..026f03db50c 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -6,6 +6,7 @@ go_library( "conversion.go", "factory.go", "factory_client_access.go", + "generator.go", "helpers.go", "kubectl_match_version.go", "printing.go", diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 0e7f0724797..c3303c6727c 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -28,7 +28,6 @@ import ( scaleclient "k8s.io/client-go/scale" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" @@ -68,8 +67,6 @@ type Factory interface { // other namespace is specified and whether the namespace was // overridden. DefaultNamespace() (string, bool, error) - // Generators returns the generators for the provided command - Generators(cmdName string) map[string]kubectl.Generator // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index c9cbda6bdde..8b806097680 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -19,22 +19,11 @@ limitations under the License. package util import ( - "fmt" - "io" "sync" - appsv1 "k8s.io/api/apps/v1" - appsv1beta1 "k8s.io/api/apps/v1beta1" - batchv1 "k8s.io/api/batch/v1" - batchv1beta1 "k8s.io/api/batch/v1beta1" - batchv2alpha1 "k8s.io/api/batch/v2alpha1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -44,7 +33,6 @@ import ( "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" @@ -223,216 +211,6 @@ func (f *factoryImpl) ScaleClient() (scaleclient.ScalesGetter, error) { return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil } -const ( - // TODO(sig-cli): Enforce consistent naming for generators here. - // See discussion in https://github.com/kubernetes/kubernetes/issues/46237 - // before you add any more. - RunV1GeneratorName = "run/v1" - RunPodV1GeneratorName = "run-pod/v1" - ServiceV1GeneratorName = "service/v1" - ServiceV2GeneratorName = "service/v2" - ServiceNodePortGeneratorV1Name = "service-nodeport/v1" - ServiceClusterIPGeneratorV1Name = "service-clusterip/v1" - ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1" - ServiceExternalNameGeneratorV1Name = "service-externalname/v1" - ServiceAccountV1GeneratorName = "serviceaccount/v1" - HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1" - DeploymentV1Beta1GeneratorName = "deployment/v1beta1" - DeploymentAppsV1Beta1GeneratorName = "deployment/apps.v1beta1" - DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1" - DeploymentBasicAppsV1Beta1GeneratorName = "deployment-basic/apps.v1beta1" - DeploymentBasicAppsV1GeneratorName = "deployment-basic/apps.v1" - JobV1GeneratorName = "job/v1" - CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1" - CronJobV1Beta1GeneratorName = "cronjob/v1beta1" - NamespaceV1GeneratorName = "namespace/v1" - ResourceQuotaV1GeneratorName = "resourcequotas/v1" - SecretV1GeneratorName = "secret/v1" - SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1" - SecretForTLSV1GeneratorName = "secret-for-tls/v1" - ConfigMapV1GeneratorName = "configmap/v1" - ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1" - RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1" - ClusterV1Beta1GeneratorName = "cluster/v1beta1" - PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1" - PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2" - PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1" -) - -// DefaultGenerators returns the set of default generators for use in Factory instances -func DefaultGenerators(cmdName string) map[string]kubectl.Generator { - var generator map[string]kubectl.Generator - switch cmdName { - case "expose": - generator = map[string]kubectl.Generator{ - ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{}, - ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{}, - } - case "service-clusterip": - generator = map[string]kubectl.Generator{ - ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{}, - } - case "service-nodeport": - generator = map[string]kubectl.Generator{ - ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{}, - } - case "service-loadbalancer": - generator = map[string]kubectl.Generator{ - ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{}, - } - case "deployment": - // Create Deployment has only StructuredGenerators and no - // param-based Generators. - // The StructuredGenerators are as follows (as of 2018-03-16): - // DeploymentBasicV1Beta1GeneratorName -> kubectl.DeploymentBasicGeneratorV1 - // DeploymentBasicAppsV1Beta1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1Beta1 - // DeploymentBasicAppsV1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1 - generator = map[string]kubectl.Generator{} - case "run": - generator = map[string]kubectl.Generator{ - RunV1GeneratorName: kubectl.BasicReplicationController{}, - RunPodV1GeneratorName: kubectl.BasicPod{}, - DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{}, - DeploymentAppsV1Beta1GeneratorName: kubectl.DeploymentAppsV1Beta1{}, - JobV1GeneratorName: kubectl.JobV1{}, - CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{}, - CronJobV1Beta1GeneratorName: kubectl.CronJobV1Beta1{}, - } - case "namespace": - generator = map[string]kubectl.Generator{ - NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{}, - } - case "quota": - generator = map[string]kubectl.Generator{ - ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{}, - } - case "secret": - generator = map[string]kubectl.Generator{ - SecretV1GeneratorName: kubectl.SecretGeneratorV1{}, - } - case "secret-for-docker-registry": - generator = map[string]kubectl.Generator{ - SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{}, - } - case "secret-for-tls": - generator = map[string]kubectl.Generator{ - SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{}, - } - } - - return generator -} - -// fallbackGeneratorNameIfNecessary returns the name of the old generator -// if server does not support new generator. Otherwise, the -// generator string is returned unchanged. -// -// If the generator name is changed, print a warning message to let the user -// know. -func FallbackGeneratorNameIfNecessary( - generatorName string, - discoveryClient discovery.DiscoveryInterface, - cmdErr io.Writer, -) (string, error) { - switch generatorName { - case DeploymentAppsV1Beta1GeneratorName: - hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) - if err != nil { - return "", err - } - if !hasResource { - return FallbackGeneratorNameIfNecessary(DeploymentV1Beta1GeneratorName, discoveryClient, cmdErr) - } - case DeploymentV1Beta1GeneratorName: - hasResource, err := HasResource(discoveryClient, extensionsv1beta1.SchemeGroupVersion.WithResource("deployments")) - if err != nil { - return "", err - } - if !hasResource { - return RunV1GeneratorName, nil - } - case DeploymentBasicAppsV1GeneratorName: - hasResource, err := HasResource(discoveryClient, appsv1.SchemeGroupVersion.WithResource("deployments")) - if err != nil { - return "", err - } - if !hasResource { - return FallbackGeneratorNameIfNecessary(DeploymentBasicAppsV1Beta1GeneratorName, discoveryClient, cmdErr) - } - case DeploymentBasicAppsV1Beta1GeneratorName: - hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) - if err != nil { - return "", err - } - if !hasResource { - return DeploymentBasicV1Beta1GeneratorName, nil - } - case JobV1GeneratorName: - hasResource, err := HasResource(discoveryClient, batchv1.SchemeGroupVersion.WithResource("jobs")) - if err != nil { - return "", err - } - if !hasResource { - return RunPodV1GeneratorName, nil - } - case CronJobV1Beta1GeneratorName: - hasResource, err := HasResource(discoveryClient, batchv1beta1.SchemeGroupVersion.WithResource("cronjobs")) - if err != nil { - return "", err - } - if !hasResource { - return FallbackGeneratorNameIfNecessary(CronJobV2Alpha1GeneratorName, discoveryClient, cmdErr) - } - case CronJobV2Alpha1GeneratorName: - hasResource, err := HasResource(discoveryClient, batchv2alpha1.SchemeGroupVersion.WithResource("cronjobs")) - if err != nil { - return "", err - } - if !hasResource { - return JobV1GeneratorName, nil - } - } - return generatorName, nil -} - -func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) { - fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+ - "but it isn't available. "+ - "Falling back to %q.\n", - newGeneratorName, - oldGeneratorName, - ) -} - -func HasResource(client discovery.DiscoveryInterface, resource schema.GroupVersionResource) (bool, error) { - resources, err := client.ServerResourcesForGroupVersion(resource.GroupVersion().String()) - if apierrors.IsNotFound(err) { - // entire group is missing - return false, nil - } - if err != nil { - // other errors error - return false, fmt.Errorf("failed to discover supported resources: %v", err) - } - for _, serverResource := range resources.APIResources { - if serverResource.Name == resource.Resource { - return true, nil - } - } - return false, nil -} - -func Contains(resourcesList []*metav1.APIResourceList, resource schema.GroupVersionResource) bool { - resources := discovery.FilteredBy(discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { - return resource.GroupVersion().String() == gv && resource.Resource == r.Name - }), resourcesList) - return len(resources) != 0 -} - -func (f *factoryImpl) Generators(cmdName string) map[string]kubectl.Generator { - return DefaultGenerators(cmdName) -} - // this method exists to help us find the points still relying on internal types. func InternalVersionDecoder() runtime.Decoder { return legacyscheme.Codecs.UniversalDecoder() diff --git a/pkg/kubectl/cmd/util/generator.go b/pkg/kubectl/cmd/util/generator.go new file mode 100644 index 00000000000..551f9e94c10 --- /dev/null +++ b/pkg/kubectl/cmd/util/generator.go @@ -0,0 +1,238 @@ +/* +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 util + +import ( + "fmt" + "io" + + appsv1 "k8s.io/api/apps/v1" + appsv1beta1 "k8s.io/api/apps/v1beta1" + batchv1 "k8s.io/api/batch/v1" + batchv1beta1 "k8s.io/api/batch/v1beta1" + batchv2alpha1 "k8s.io/api/batch/v2alpha1" + extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/kubernetes/pkg/kubectl" +) + +const ( + // TODO(sig-cli): Enforce consistent naming for generators here. + // See discussion in https://github.com/kubernetes/kubernetes/issues/46237 + // before you add any more. + RunV1GeneratorName = "run/v1" + RunPodV1GeneratorName = "run-pod/v1" + ServiceV1GeneratorName = "service/v1" + ServiceV2GeneratorName = "service/v2" + ServiceNodePortGeneratorV1Name = "service-nodeport/v1" + ServiceClusterIPGeneratorV1Name = "service-clusterip/v1" + ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1" + ServiceExternalNameGeneratorV1Name = "service-externalname/v1" + ServiceAccountV1GeneratorName = "serviceaccount/v1" + HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1" + DeploymentV1Beta1GeneratorName = "deployment/v1beta1" + DeploymentAppsV1Beta1GeneratorName = "deployment/apps.v1beta1" + DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1" + DeploymentBasicAppsV1Beta1GeneratorName = "deployment-basic/apps.v1beta1" + DeploymentBasicAppsV1GeneratorName = "deployment-basic/apps.v1" + JobV1GeneratorName = "job/v1" + CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1" + CronJobV1Beta1GeneratorName = "cronjob/v1beta1" + NamespaceV1GeneratorName = "namespace/v1" + ResourceQuotaV1GeneratorName = "resourcequotas/v1" + SecretV1GeneratorName = "secret/v1" + SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1" + SecretForTLSV1GeneratorName = "secret-for-tls/v1" + ConfigMapV1GeneratorName = "configmap/v1" + ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1" + RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1" + ClusterV1Beta1GeneratorName = "cluster/v1beta1" + PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1" + PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2" + PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1" +) + +// GeneratorFunc returns the generators for the provided command +type GeneratorFunc func(cmdName string) map[string]kubectl.Generator + +// GeneratorFn gives a way to easily override the function for unit testing if needed +var GeneratorFn GeneratorFunc = defaultGenerators + +// defaultGenerators returns the set of default generators for use in Factory instances +func defaultGenerators(cmdName string) map[string]kubectl.Generator { + var generator map[string]kubectl.Generator + switch cmdName { + case "expose": + generator = map[string]kubectl.Generator{ + ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{}, + ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{}, + } + case "service-clusterip": + generator = map[string]kubectl.Generator{ + ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{}, + } + case "service-nodeport": + generator = map[string]kubectl.Generator{ + ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{}, + } + case "service-loadbalancer": + generator = map[string]kubectl.Generator{ + ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{}, + } + case "deployment": + // Create Deployment has only StructuredGenerators and no + // param-based Generators. + // The StructuredGenerators are as follows (as of 2018-03-16): + // DeploymentBasicV1Beta1GeneratorName -> kubectl.DeploymentBasicGeneratorV1 + // DeploymentBasicAppsV1Beta1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1Beta1 + // DeploymentBasicAppsV1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1 + generator = map[string]kubectl.Generator{} + case "run": + generator = map[string]kubectl.Generator{ + RunV1GeneratorName: kubectl.BasicReplicationController{}, + RunPodV1GeneratorName: kubectl.BasicPod{}, + DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{}, + DeploymentAppsV1Beta1GeneratorName: kubectl.DeploymentAppsV1Beta1{}, + JobV1GeneratorName: kubectl.JobV1{}, + CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{}, + CronJobV1Beta1GeneratorName: kubectl.CronJobV1Beta1{}, + } + case "namespace": + generator = map[string]kubectl.Generator{ + NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{}, + } + case "quota": + generator = map[string]kubectl.Generator{ + ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{}, + } + case "secret": + generator = map[string]kubectl.Generator{ + SecretV1GeneratorName: kubectl.SecretGeneratorV1{}, + } + case "secret-for-docker-registry": + generator = map[string]kubectl.Generator{ + SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{}, + } + case "secret-for-tls": + generator = map[string]kubectl.Generator{ + SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{}, + } + } + + return generator +} + +// FallbackGeneratorNameIfNecessary returns the name of the old generator +// if server does not support new generator. Otherwise, the +// generator string is returned unchanged. +// +// If the generator name is changed, print a warning message to let the user +// know. +func FallbackGeneratorNameIfNecessary( + generatorName string, + discoveryClient discovery.DiscoveryInterface, + cmdErr io.Writer, +) (string, error) { + switch generatorName { + case DeploymentAppsV1Beta1GeneratorName: + hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) + if err != nil { + return "", err + } + if !hasResource { + return FallbackGeneratorNameIfNecessary(DeploymentV1Beta1GeneratorName, discoveryClient, cmdErr) + } + case DeploymentV1Beta1GeneratorName: + hasResource, err := HasResource(discoveryClient, extensionsv1beta1.SchemeGroupVersion.WithResource("deployments")) + if err != nil { + return "", err + } + if !hasResource { + return RunV1GeneratorName, nil + } + case DeploymentBasicAppsV1GeneratorName: + hasResource, err := HasResource(discoveryClient, appsv1.SchemeGroupVersion.WithResource("deployments")) + if err != nil { + return "", err + } + if !hasResource { + return FallbackGeneratorNameIfNecessary(DeploymentBasicAppsV1Beta1GeneratorName, discoveryClient, cmdErr) + } + case DeploymentBasicAppsV1Beta1GeneratorName: + hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) + if err != nil { + return "", err + } + if !hasResource { + return DeploymentBasicV1Beta1GeneratorName, nil + } + case JobV1GeneratorName: + hasResource, err := HasResource(discoveryClient, batchv1.SchemeGroupVersion.WithResource("jobs")) + if err != nil { + return "", err + } + if !hasResource { + return RunPodV1GeneratorName, nil + } + case CronJobV1Beta1GeneratorName: + hasResource, err := HasResource(discoveryClient, batchv1beta1.SchemeGroupVersion.WithResource("cronjobs")) + if err != nil { + return "", err + } + if !hasResource { + return FallbackGeneratorNameIfNecessary(CronJobV2Alpha1GeneratorName, discoveryClient, cmdErr) + } + case CronJobV2Alpha1GeneratorName: + hasResource, err := HasResource(discoveryClient, batchv2alpha1.SchemeGroupVersion.WithResource("cronjobs")) + if err != nil { + return "", err + } + if !hasResource { + return JobV1GeneratorName, nil + } + } + return generatorName, nil +} + +func HasResource(client discovery.DiscoveryInterface, resource schema.GroupVersionResource) (bool, error) { + resources, err := client.ServerResourcesForGroupVersion(resource.GroupVersion().String()) + if apierrors.IsNotFound(err) { + // entire group is missing + return false, nil + } + if err != nil { + // other errors error + return false, fmt.Errorf("failed to discover supported resources: %v", err) + } + for _, serverResource := range resources.APIResources { + if serverResource.Name == resource.Resource { + return true, nil + } + } + return false, nil +} + +func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) { + fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+ + "but it isn't available. "+ + "Falling back to %q.\n", + newGeneratorName, + oldGeneratorName, + ) +} From e1e0591364563fe062ede8639531537c293331fc Mon Sep 17 00:00:00 2001 From: Ward Loos Date: Sun, 27 May 2018 20:26:37 +0200 Subject: [PATCH 075/416] Don't reset global timeout on each for loop iteration --- pkg/kubectl/cmd/drain.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index 8a6383f9a2e..0258e7a18f8 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -610,6 +610,7 @@ func (o *DrainOptions) evictPods(pods []corev1.Pod, policyGroupVersion string, g } else { globalTimeout = o.Timeout } + globalTimeoutCh := time.After(globalTimeout) for { select { case err := <-errCh: @@ -619,7 +620,7 @@ func (o *DrainOptions) evictPods(pods []corev1.Pod, policyGroupVersion string, g if doneCount == len(pods) { return nil } - case <-time.After(globalTimeout): + case <-globalTimeoutCh: return fmt.Errorf("Drain did not complete within %v", globalTimeout) } } From e330741d6d72e97346f3c989960829c247950546 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Mon, 28 May 2018 02:46:18 +0000 Subject: [PATCH 076/416] fix azure file size grow issue --- pkg/volume/azure_file/azure_file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/volume/azure_file/azure_file.go b/pkg/volume/azure_file/azure_file.go index b7efb940d0b..f36b47cb3ee 100644 --- a/pkg/volume/azure_file/azure_file.go +++ b/pkg/volume/azure_file/azure_file.go @@ -150,7 +150,7 @@ func (plugin *azureFilePlugin) ExpandVolumeDevice( newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) { - if spec.PersistentVolume != nil || spec.PersistentVolume.Spec.AzureFile == nil { + if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.AzureFile == nil { return oldSize, fmt.Errorf("invalid PV spec") } shareName := spec.PersistentVolume.Spec.AzureFile.ShareName From ecdc1638f6557d8d10d72ebc821e182ead2f0cdc Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 9 Mar 2018 18:47:53 +0100 Subject: [PATCH 077/416] apiextensions-apiserver: add columns to CRD spec --- .../pkg/apis/apiextensions/fuzzer/fuzzer.go | 8 + .../pkg/apis/apiextensions/types.go | 24 +++ .../apis/apiextensions/v1beta1/defaults.go | 8 + .../pkg/apis/apiextensions/v1beta1/types.go | 24 +++ .../apiextensions/validation/validation.go | 39 ++++ .../pkg/apiserver/customresource_handler.go | 5 +- .../pkg/registry/customresource/etcd_test.go | 102 +++++++++- .../tableconvertor/tableconvertor.go | 136 +++++++++---- .../tableconvertor/tableconvertor_test.go | 68 +++++++ .../test/integration/table_test.go | 185 ++++++++++++++++++ .../apimachinery/pkg/api/meta/table/table.go | 6 +- .../pkg/registry/rest/resttest/resttest.go | 5 +- 12 files changed, 556 insertions(+), 54 deletions(-) create mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor_test.go create mode 100644 staging/src/k8s.io/apiextensions-apiserver/test/integration/table_test.go diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go index 0fe919ab64e..ff8cc033469 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go @@ -23,9 +23,12 @@ import ( "github.com/google/gofuzz" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" ) +var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() + // Funcs returns the fuzzer functions for the apiextensions apis. func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { return []interface{}{ @@ -53,6 +56,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { } else if len(obj.Versions) != 0 { obj.Version = obj.Versions[0].Name } + if len(obj.AdditionalPrinterColumns) == 0 { + obj.AdditionalPrinterColumns = []apiextensions.CustomResourceColumnDefinition{ + {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"}, + } + } }, func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) { c.FuzzNoCustom(obj) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go index debe74a5b0c..6fc75154fac 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go @@ -49,6 +49,8 @@ type CustomResourceDefinitionSpec struct { // major version, then minor version. An example sorted list of versions: // v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10. Versions []CustomResourceDefinitionVersion + // AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. + AdditionalPrinterColumns []CustomResourceColumnDefinition } type CustomResourceDefinitionVersion struct { @@ -61,6 +63,28 @@ type CustomResourceDefinitionVersion struct { Storage bool } +// CustomResourceColumnDefinition specifies a column for server side printing. +type CustomResourceColumnDefinition struct { + // name is a human readable name for the column. + Name string + // type is an OpenAPI type definition for this column. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + Type string + // format is an optional OpenAPI type definition for this column. The 'name' format is applied + // to the primary identifier column to assist in clients identifying column is the resource name. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + Format string + // description is a human readable description of this column. + Description string + // priority is an integer defining the relative importance of this column compared to others. Lower + // numbers are considered higher priority. Columns that may be omitted in limited space scenarios + // should be given a higher priority. + Priority int32 + + // JSONPath is a simple JSON path, i.e. without array notation. + JSONPath string +} + // CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition type CustomResourceDefinitionNames struct { // Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go index 1984e229778..e3235e8702c 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go @@ -19,9 +19,12 @@ package v1beta1 import ( "strings" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) +var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() + func addDefaultingFuncs(scheme *runtime.Scheme) error { scheme.AddTypeDefaultingFunc(&CustomResourceDefinition{}, func(obj interface{}) { SetDefaults_CustomResourceDefinition(obj.(*CustomResourceDefinition)) }) // TODO figure out why I can't seem to get my defaulter generated @@ -63,4 +66,9 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec) if len(obj.Version) == 0 && len(obj.Versions) != 0 { obj.Version = obj.Versions[0].Name } + if len(obj.AdditionalPrinterColumns) == 0 { + obj.AdditionalPrinterColumns = []CustomResourceColumnDefinition{ + {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"}, + } + } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go index 9d8d1cd80d1..2080cc8217e 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go @@ -52,6 +52,8 @@ type CustomResourceDefinitionSpec struct { // major version, then minor version. An example sorted list of versions: // v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10. Versions []CustomResourceDefinitionVersion `json:"versions,omitempty" protobuf:"bytes,7,rep,name=versions"` + // AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. + AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,8,rep,name=additionalPrinterColumns"` } type CustomResourceDefinitionVersion struct { @@ -64,6 +66,28 @@ type CustomResourceDefinitionVersion struct { Storage bool `json:"storage" protobuf:"varint,3,opt,name=storage"` } +// CustomResourceColumnDefinition specifies a column for server side printing. +type CustomResourceColumnDefinition struct { + // name is a human readable name for the column. + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + // type is an OpenAPI type definition for this column. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + Type string `json:"type" protobuf:"bytes,2,opt,name=type"` + // format is an optional OpenAPI type definition for this column. The 'name' format is applied + // to the primary identifier column to assist in clients identifying column is the resource name. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + Format string `json:"format,omitempty" protobuf:"bytes,3,opt,name=format"` + // description is a human readable description of this column. + Description string `json:"description,omitempty" protobuf:"bytes,4,opt,name=description"` + // priority is an integer defining the relative importance of this column compared to others. Lower + // numbers are considered higher priority. Columns that may be omitted in limited space scenarios + // should be given a higher priority. + Priority int32 `json:"priority,omitempty" protobuf:"bytes,5,opt,name=priority"` + + // JSONPath is a simple JSON path, i.e. with array notation. + JSONPath string `json:"JSONPath" protobuf:"bytes,6,opt,name=JSONPath"` +} + // CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition type CustomResourceDefinitionNames struct { // Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go index eb9acc79e8e..40baf42351a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go @@ -22,6 +22,7 @@ import ( "strings" genericvalidation "k8s.io/apimachinery/pkg/api/validation" + "k8s.io/apimachinery/pkg/util/sets" validationutil "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -31,6 +32,11 @@ import ( apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" ) +var ( + printerColumnDatatypes = sets.NewString("integer", "number", "string", "boolean", "date") + customResourceColumnDefinitionFormats = sets.NewString("int32", "int64", "float", "double", "byte", "date", "date-time", "password") +) + // ValidateCustomResourceDefinition statically validates func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinition) field.ErrorList { nameValidationFn := func(name string, prefix bool) []string { @@ -175,6 +181,12 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi allErrs = append(allErrs, field.Forbidden(fldPath.Child("subresources"), "disabled by feature-gate CustomResourceSubresources")) } + for i := range spec.AdditionalPrinterColumns { + if errs := ValidateCustomResourceColumnDefinition(&spec.AdditionalPrinterColumns[i], fldPath.Child("columns").Index(i)); len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + } + return allErrs } @@ -238,6 +250,33 @@ func ValidateCustomResourceDefinitionNames(names *apiextensions.CustomResourceDe return allErrs } +// ValidateCustomResourceColumnDefinition statically validates a printer column. +func ValidateCustomResourceColumnDefinition(col *apiextensions.CustomResourceColumnDefinition, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if len(col.Name) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("header"), "")) + } + + if len(col.Type) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("type"), fmt.Sprintf("must be one of %s", strings.Join(printerColumnDatatypes.List(), ",")))) + } else if !printerColumnDatatypes.Has(col.Type) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), col.Type, fmt.Sprintf("must be one of %s", strings.Join(printerColumnDatatypes.List(), ",")))) + } + + if len(col.Format) > 0 && !customResourceColumnDefinitionFormats.Has(col.Format) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("format"), col.Format, fmt.Sprintf("must be one of %s", strings.Join(customResourceColumnDefinitionFormats.List(), ",")))) + } + + if len(col.JSONPath) == 0 { + allErrs = append(allErrs, field.Required(fldPath.Child("path"), "")) + } else if errs := validateSimpleJSONPath(col.JSONPath, fldPath.Child("path")); len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + + return allErrs +} + // specStandardValidator applies validations for different OpenAPI specification versions. type specStandardValidator interface { validate(spec *apiextensions.JSONSchemaProps, fldPath *field.Path) field.ErrorList diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go index 20231bdf866..231bffdbf6d 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go @@ -370,6 +370,8 @@ func (r *crdHandler) GetCustomResourceListerCollectionDeleter(crd *apiextensions return info.storages[info.storageVersion].CustomResource, nil } +var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() + func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResourceDefinition) (*crdInfo, error) { storageMap := r.customStorage.Load().(crdStorageMap) if ret, ok := storageMap[crd.UID]; ok { @@ -439,8 +441,7 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource scaleSpec = crd.Spec.Subresources.Scale } - // TODO: identify how to pass printer specification from the CRD - table, err := tableconvertor.New(nil) + table, err := tableconvertor.New(crd.Spec.AdditionalPrinterColumns) if err != nil { glog.V(2).Infof("The CRD for %v has an invalid printer specification, falling back to default printing: %v", kind, err) } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go index 9a2c8320505..15a242e4493 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd_test.go @@ -21,12 +21,15 @@ import ( "reflect" "strings" "testing" + "time" autoscalingv1 "k8s.io/api/autoscaling/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" + metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/diff" @@ -72,8 +75,19 @@ func newStorage(t *testing.T) (customresource.CustomResourceStorage, *etcdtestin status := &apiextensions.CustomResourceSubresourceStatus{} - // TODO: identify how to pass printer specification from the CRD - table, _ := tableconvertor.New(nil) + headers := []apiextensions.CustomResourceColumnDefinition{ + {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, + {Name: "Replicas", Type: "integer", JSONPath: ".spec.replicas"}, + {Name: "Missing", Type: "string", JSONPath: ".spec.missing"}, + {Name: "Invalid", Type: "integer", JSONPath: ".spec.string"}, + {Name: "String", Type: "string", JSONPath: ".spec.string"}, + {Name: "StringFloat64", Type: "string", JSONPath: ".spec.float64"}, + {Name: "StringInt64", Type: "string", JSONPath: ".spec.replicas"}, + {Name: "StringBool", Type: "string", JSONPath: ".spec.bool"}, + {Name: "Float64", Type: "number", JSONPath: ".spec.float64"}, + {Name: "Bool", Type: "boolean", JSONPath: ".spec.bool"}, + } + table, _ := tableconvertor.New(headers) storage := customresource.NewStorage( schema.GroupResource{Group: "mygroup.example.com", Resource: "noxus"}, @@ -112,11 +126,18 @@ func validNewCustomResource() *unstructured.Unstructured { "apiVersion": "mygroup.example.com/v1beta1", "kind": "Noxu", "metadata": map[string]interface{}{ - "namespace": "default", - "name": "foo", + "namespace": "default", + "name": "foo", + "creationTimestamp": time.Now().Add(-time.Hour*12 - 30*time.Minute).UTC().Format(time.RFC3339), }, "spec": map[string]interface{}{ - "replicas": int64(7), + "replicas": int64(7), + "string": "string", + "float64": float64(3.1415926), + "bool": true, + "stringList": []interface{}{"foo", "bar"}, + "mixedList": []interface{}{"foo", int64(42)}, + "nonPrimitiveList": []interface{}{"foo", []interface{}{int64(1), int64(2)}}, }, }, } @@ -225,6 +246,77 @@ func TestCategories(t *testing.T) { } } +func TestColumns(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.CustomResource.Store.DestroyFunc() + + ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault) + key := "/noxus/" + metav1.NamespaceDefault + "/foo" + validCustomResource := validNewCustomResource() + if err := storage.CustomResource.Storage.Create(ctx, key, validCustomResource, nil, 0); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + gottenList, err := storage.CustomResource.List(ctx, &metainternal.ListOptions{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + tbl, err := storage.CustomResource.ConvertToTable(ctx, gottenList, &metav1beta1.TableOptions{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expectedColumns := []struct { + Name, Type string + }{ + {"Name", "string"}, + {"Age", "date"}, + {"Replicas", "integer"}, + {"Missing", "string"}, + {"Invalid", "integer"}, + {"String", "string"}, + {"StringFloat64", "string"}, + {"StringInt64", "string"}, + {"StringBool", "string"}, + {"Float64", "number"}, + {"Bool", "boolean"}, + } + if len(tbl.ColumnDefinitions) != len(expectedColumns) { + t.Fatalf("got %d columns, expected %d. Got: %+v", len(tbl.ColumnDefinitions), len(expectedColumns), tbl.ColumnDefinitions) + } + for i, d := range tbl.ColumnDefinitions { + if d.Name != expectedColumns[i].Name { + t.Errorf("got column %d name %q, expected %q", i, d.Name, expectedColumns[i].Name) + } + if d.Type != expectedColumns[i].Type { + t.Errorf("got column %d type %q, expected %q", i, d.Type, expectedColumns[i].Type) + } + } + + expectedRows := [][]interface{}{ + { + "foo", + "12h", + int64(7), + nil, + nil, + "string", + "3.1415926", + "7", + "true", + float64(3.1415926), + true, + }, + } + for i, r := range tbl.Rows { + if !reflect.DeepEqual(r.Cells, expectedRows[i]) { + t.Errorf("got row %d with cells %#v, expected %#v", i, r.Cells, expectedRows[i]) + } + } +} + func TestStatusUpdate(t *testing.T) { storage, server := newStorage(t) defer server.Terminate(t) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go index 8dbc0e77265..e1bed809d99 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor.go @@ -19,11 +19,11 @@ package tableconvertor import ( "bytes" "context" + "encoding/json" "fmt" - "strings" - - "github.com/go-openapi/spec" + "reflect" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apimachinery/pkg/api/meta" metatable "k8s.io/apimachinery/pkg/api/meta/table" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,56 +33,46 @@ import ( "k8s.io/client-go/util/jsonpath" ) -const printColumnsKey = "x-kubernetes-print-columns" - var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc() -// New creates a new table convertor for the provided OpenAPI schema. If the printer definition cannot be parsed, +// New creates a new table convertor for the provided CRD column definition. If the printer definition cannot be parsed, // error will be returned along with a default table convertor. -func New(extensions spec.Extensions) (rest.TableConvertor, error) { +func New(crdColumns []apiextensions.CustomResourceColumnDefinition) (rest.TableConvertor, error) { headers := []metav1beta1.TableColumnDefinition{ {Name: "Name", Type: "string", Format: "name", Description: swaggerMetadataDescriptions["name"]}, - {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"]}, } c := &convertor{ headers: headers, } - format, ok := extensions.GetString(printColumnsKey) - if !ok { - return c, nil - } - // "x-kubernetes-print-columns": "custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion" - parts := strings.SplitN(format, "=", 2) - if len(parts) != 2 || parts[0] != "custom-columns" { - return c, fmt.Errorf("unrecognized column definition in 'x-kubernetes-print-columns', only support 'custom-columns=NAME=JSONPATH[,NAME=JSONPATH]'") - } - columnSpecs := strings.Split(parts[1], ",") - var columns []*jsonpath.JSONPath - for _, spec := range columnSpecs { - parts := strings.SplitN(spec, ":", 2) - if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 { - return c, fmt.Errorf("unrecognized column definition in 'x-kubernetes-print-columns', must specify NAME=JSONPATH: %s", spec) - } - path := jsonpath.New(parts[0]) - if err := path.Parse(parts[1]); err != nil { - return c, fmt.Errorf("unrecognized column definition in 'x-kubernetes-print-columns': %v", spec) + + for _, col := range crdColumns { + path := jsonpath.New(col.Name) + if err := path.Parse(fmt.Sprintf("{%s}", col.JSONPath)); err != nil { + return c, fmt.Errorf("unrecognized column definition %q", col.JSONPath) } path.AllowMissingKeys(true) - columns = append(columns, path) - headers = append(headers, metav1beta1.TableColumnDefinition{ - Name: parts[0], - Type: "string", - Description: fmt.Sprintf("Custom resource definition column from OpenAPI (in JSONPath format): %s", parts[1]), + + desc := fmt.Sprintf("Custom resource definition column (in JSONPath format): %s", col.JSONPath) + if len(col.Description) > 0 { + desc = col.Description + } + + c.additionalColumns = append(c.additionalColumns, path) + c.headers = append(c.headers, metav1beta1.TableColumnDefinition{ + Name: col.Name, + Type: col.Type, + Format: col.Format, + Description: desc, + Priority: col.Priority, }) } - c.columns = columns - c.headers = headers + return c, nil } type convertor struct { - headers []metav1beta1.TableColumnDefinition - columns []*jsonpath.JSONPath + headers []metav1beta1.TableColumnDefinition + additionalColumns []*jsonpath.JSONPath } func (c *convertor) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) { @@ -103,18 +93,80 @@ func (c *convertor) ConvertToTable(ctx context.Context, obj runtime.Object, tabl var err error buf := &bytes.Buffer{} table.Rows, err = metatable.MetaToTableRow(obj, func(obj runtime.Object, m metav1.Object, name, age string) ([]interface{}, error) { - cells := make([]interface{}, 2, 2+len(c.columns)) + cells := make([]interface{}, 1, 1+len(c.additionalColumns)) cells[0] = name - cells[1] = age - for _, column := range c.columns { - if err := column.Execute(buf, obj); err != nil { + customHeaders := c.headers[1:] + for i, column := range c.additionalColumns { + results, err := column.FindResults(obj.(runtime.Unstructured).UnstructuredContent()) + if err != nil || len(results) == 0 || len(results[0]) == 0 { cells = append(cells, nil) continue } - cells = append(cells, buf.String()) - buf.Reset() + + // as we only support simple JSON path, we can assume to have only one result (or none, filtered out above) + value := results[0][0].Interface() + if customHeaders[i].Type == "string" { + if err := column.PrintResults(buf, []reflect.Value{reflect.ValueOf(value)}); err == nil { + cells = append(cells, buf.String()) + buf.Reset() + } else { + cells = append(cells, nil) + } + } else { + cells = append(cells, cellForJSONValue(customHeaders[i].Type, value)) + } } return cells, nil }) return table, err } + +func cellForJSONValue(headerType string, value interface{}) interface{} { + if value == nil { + return nil + } + + switch headerType { + case "integer": + switch typed := value.(type) { + case int64: + return typed + case float64: + return int64(typed) + case json.Number: + if i64, err := typed.Int64(); err == nil { + return i64 + } + } + case "number": + switch typed := value.(type) { + case int64: + return float64(typed) + case float64: + return typed + case json.Number: + if f, err := typed.Float64(); err == nil { + return f + } + } + case "boolean": + if b, ok := value.(bool); ok { + return b + } + case "string": + if s, ok := value.(string); ok { + return s + } + case "date": + if typed, ok := value.(string); ok { + var timestamp metav1.Time + err := timestamp.UnmarshalQueryParameter(typed) + if err != nil { + return "" + } + return metatable.ConvertToHumanReadableDateType(timestamp) + } + } + + return nil +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor_test.go new file mode 100644 index 00000000000..179aabb8ab7 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/tableconvertor_test.go @@ -0,0 +1,68 @@ +/* +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 tableconvertor + +import ( + "fmt" + "reflect" + "testing" + "time" +) + +func Test_cellForJSONValue(t *testing.T) { + tests := []struct { + headerType string + value interface{} + want interface{} + }{ + {"integer", int64(42), int64(42)}, + {"integer", float64(3.14), int64(3)}, + {"integer", true, nil}, + {"integer", "foo", nil}, + + {"number", int64(42), float64(42)}, + {"number", float64(3.14), float64(3.14)}, + {"number", true, nil}, + {"number", "foo", nil}, + + {"boolean", int64(42), nil}, + {"boolean", float64(3.14), nil}, + {"boolean", true, true}, + {"boolean", "foo", nil}, + + {"string", int64(42), nil}, + {"string", float64(3.14), nil}, + {"string", true, nil}, + {"string", "foo", "foo"}, + + {"date", int64(42), nil}, + {"date", float64(3.14), nil}, + {"date", true, nil}, + {"date", time.Now().Add(-time.Hour*12 - 30*time.Minute).UTC().Format(time.RFC3339), "12h"}, + {"date", time.Now().Add(+time.Hour*12 + 30*time.Minute).UTC().Format(time.RFC3339), ""}, + {"date", "", ""}, + + {"unknown", "foo", nil}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%#v of type %s", tt.value, tt.headerType), func(t *testing.T) { + if got := cellForJSONValue(tt.headerType, tt.value); !reflect.DeepEqual(got, tt.want) { + t.Errorf("cellForJSONValue() = %#v, want %#v", got, tt.want) + } + }) + } +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/table_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/table_test.go new file mode 100644 index 00000000000..0c3ac609a9c --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/table_test.go @@ -0,0 +1,185 @@ +/* +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 integration + +import ( + "fmt" + "testing" + + "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apiextensions-apiserver/test/integration/testserver" +) + +func newTableCRD() *apiextensionsv1beta1.CustomResourceDefinition { + return &apiextensionsv1beta1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{Name: "tables.mygroup.example.com"}, + Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ + Group: "mygroup.example.com", + Version: "v1beta1", + Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ + Plural: "tables", + Singular: "table", + Kind: "Table", + ListKind: "TablemList", + }, + Scope: apiextensionsv1beta1.ClusterScoped, + AdditionalPrinterColumns: []apiextensionsv1beta1.CustomResourceColumnDefinition{ + {Name: "Age", Type: "date", JSONPath: ".metadata.creationTimestamp"}, + {Name: "Alpha", Type: "string", JSONPath: ".spec.alpha"}, + {Name: "Beta", Type: "integer", Description: "the beta field", Format: "int64", Priority: 42, JSONPath: ".spec.beta"}, + {Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values", JSONPath: ".spec.gamma"}, + }, + }, + } +} + +func newTableInstance(name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "mygroup.example.com/v1beta1", + "kind": "Table", + "metadata": map[string]interface{}{ + "name": name, + }, + "spec": map[string]interface{}{ + "alpha": "foo_123", + "beta": 10, + "gamma": "bar", + "delta": "hello", + }, + }, + } +} + +func TestTableGet(t *testing.T) { + stopCh, config, err := testserver.StartDefaultServer() + if err != nil { + t.Fatal(err) + } + defer close(stopCh) + + apiExtensionClient, err := clientset.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + + crd := newTableCRD() + crd, err = testserver.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient) + if err != nil { + t.Fatal(err) + } + + crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + t.Logf("table crd created: %#v", crd) + + crClient := newNamespacedCustomResourceClient("", dynamicClient, crd) + foo, err := crClient.Create(newTableInstance("foo")) + if err != nil { + t.Fatalf("unable to create noxu instance: %v", err) + } + t.Logf("foo created: %#v", foo.UnstructuredContent()) + + gv := schema.GroupVersion{Group: crd.Spec.Group, Version: crd.Spec.Version} + gvk := gv.WithKind(crd.Spec.Names.Kind) + + scheme := runtime.NewScheme() + codecs := serializer.NewCodecFactory(scheme) + parameterCodec := runtime.NewParameterCodec(scheme) + metav1.AddToGroupVersion(scheme, gv) + scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{}) + scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{}) + + crConfig := *config + crConfig.GroupVersion = &gv + crConfig.APIPath = "/apis" + crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs} + crRestClient, err := rest.RESTClientFor(&crConfig) + if err != nil { + t.Fatal(err) + } + + ret, err := crRestClient.Get(). + Resource(crd.Spec.Names.Plural). + SetHeader("Accept", fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)). + VersionedParams(&metav1beta1.TableOptions{}, parameterCodec). + Do(). + Get() + if err != nil { + t.Fatalf("failed to list %v resources: %v", gvk, err) + } + + tbl, ok := ret.(*metav1beta1.Table) + if !ok { + t.Fatalf("expected metav1beta1.Table, got %T", ret) + } + t.Logf("%v table list: %#v", gvk, tbl) + + if got, expected := len(tbl.ColumnDefinitions), 5; got != expected { + t.Errorf("expected %d headers, got %d", expected, got) + } else { + alpha := metav1beta1.TableColumnDefinition{Name: "Alpha", Type: "string", Format: "", Description: "Custom resource definition column (in JSONPath format): .spec.alpha", Priority: 0} + if got, expected := tbl.ColumnDefinitions[2], alpha; got != expected { + t.Errorf("expected column definition %#v, got %#v", expected, got) + } + + beta := metav1beta1.TableColumnDefinition{Name: "Beta", Type: "integer", Format: "int64", Description: "the beta field", Priority: 42} + if got, expected := tbl.ColumnDefinitions[3], beta; got != expected { + t.Errorf("expected column definition %#v, got %#v", expected, got) + } + + gamma := metav1beta1.TableColumnDefinition{Name: "Gamma", Type: "integer", Description: "a column with wrongly typed values"} + if got, expected := tbl.ColumnDefinitions[4], gamma; got != expected { + t.Errorf("expected column definition %#v, got %#v", expected, got) + } + } + if got, expected := len(tbl.Rows), 1; got != expected { + t.Errorf("expected %d rows, got %d", expected, got) + } else if got, expected := len(tbl.Rows[0].Cells), 5; got != expected { + t.Errorf("expected %d cells, got %d", expected, got) + } else { + if got, expected := tbl.Rows[0].Cells[0], "foo"; got != expected { + t.Errorf("expected cell[0] to equal %q, got %q", expected, got) + } + if got, expected := tbl.Rows[0].Cells[2], "foo_123"; got != expected { + t.Errorf("expected cell[2] to equal %q, got %q", expected, got) + } + if got, expected := tbl.Rows[0].Cells[3], int64(10); got != expected { + t.Errorf("expected cell[3] to equal %#v, got %#v", expected, got) + } + if got, expected := tbl.Rows[0].Cells[4], interface{}(nil); got != expected { + t.Errorf("expected cell[3] to equal %#v although the type does not match the column, got %#v", expected, got) + } + } +} diff --git a/staging/src/k8s.io/apimachinery/pkg/api/meta/table/table.go b/staging/src/k8s.io/apimachinery/pkg/api/meta/table/table.go index a0097a4e26d..2144a77cb19 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/meta/table/table.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/meta/table/table.go @@ -53,7 +53,7 @@ func MetaToTableRow(obj runtime.Object, rowFn func(obj runtime.Object, m metav1. row := metav1beta1.TableRow{ Object: runtime.RawExtension{Object: obj}, } - row.Cells, err = rowFn(obj, m, m.GetName(), translateTimestamp(m.GetCreationTimestamp())) + row.Cells, err = rowFn(obj, m, m.GetName(), ConvertToHumanReadableDateType(m.GetCreationTimestamp())) if err != nil { return nil, err } @@ -61,9 +61,9 @@ func MetaToTableRow(obj runtime.Object, rowFn func(obj runtime.Object, m metav1. return rows, nil } -// translateTimestamp returns the elapsed time since timestamp in +// ConvertToHumanReadableDateType returns the elapsed time since timestamp in // human-readable approximation. -func translateTimestamp(timestamp metav1.Time) string { +func ConvertToHumanReadableDateType(timestamp metav1.Time) string { if timestamp.IsZero() { return "" } diff --git a/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go b/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go index 64d792a607a..647b9401b1e 100644 --- a/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go +++ b/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go @@ -1320,7 +1320,7 @@ func (t *Tester) testListTableConversion(obj runtime.Object, assignFn AssignFunc t.Errorf("column %d has no name", j) } switch column.Type { - case "string", "date", "integer": + case "string", "date", "integer", "number", "boolean": default: t.Errorf("column %d has unexpected type: %q", j, column.Type) } @@ -1342,13 +1342,14 @@ func (t *Tester) testListTableConversion(obj runtime.Object, assignFn AssignFunc } for i, row := range table.Rows { if len(row.Cells) != len(table.ColumnDefinitions) { - t.Errorf("row %d did not have the correct number of cells: %d in %v", i, len(table.ColumnDefinitions), row.Cells) + t.Errorf("row %d did not have the correct number of cells: %d in %v, expected %d", i, len(row.Cells), row.Cells, len(table.ColumnDefinitions)) } for j, cell := range row.Cells { // do not add to this test without discussion - may break clients switch cell.(type) { case float64, int64, int32, int, string, bool: case []interface{}: + case nil: default: t.Errorf("row %d, cell %d has an unrecognized type, only JSON serialization safe types are allowed: %T ", i, j, cell) } From 96475ce20988df126d016baa8ffaa937682933bb Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 9 Mar 2018 18:50:55 +0100 Subject: [PATCH 078/416] Update generated files --- api/openapi-spec/swagger.json | 42 ++ .../pkg/apis/apiextensions/fuzzer/BUILD | 1 + .../apiextensions/v1beta1/generated.pb.go | 664 ++++++++++++++---- .../apiextensions/v1beta1/generated.proto | 29 + .../v1beta1/zz_generated.conversion.go | 34 + .../v1beta1/zz_generated.deepcopy.go | 21 + .../pkg/apis/apiextensions/validation/BUILD | 1 + .../apiextensions/zz_generated.deepcopy.go | 21 + .../pkg/registry/customresource/BUILD | 2 + .../customresource/tableconvertor/BUILD | 10 +- .../test/integration/BUILD | 4 + 11 files changed, 671 insertions(+), 158 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 390902a04e3..a62df308c16 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -85067,6 +85067,41 @@ } } }, + "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition": { + "description": "CustomResourceColumnDefinition specifies a column for server side printing.", + "required": [ + "name", + "type", + "JSONPath" + ], + "properties": { + "JSONPath": { + "description": "JSONPath is a simple JSON path, i.e. with array notation.", + "type": "string" + }, + "description": { + "description": "description is a human readable description of this column.", + "type": "string" + }, + "format": { + "description": "format is an optional OpenAPI type definition for this column. The 'name' format is applied to the primary identifier column to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + "type": "string" + }, + "name": { + "description": "name is a human readable name for the column.", + "type": "string" + }, + "priority": { + "description": "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a higher priority.", + "type": "integer", + "format": "int32" + }, + "type": { + "description": "type is an OpenAPI type definition for this column. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + "type": "string" + } + } + }, "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition": { "description": "CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format \u003c.spec.name\u003e.\u003c.spec.group\u003e.", "properties": { @@ -85207,6 +85242,13 @@ "scope" ], "properties": { + "additionalPrinterColumns": { + "description": "AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.", + "type": "array", + "items": { + "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition" + } + }, "group": { "description": "Group is the group this resource belongs in", "type": "string" diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/BUILD index a6e5ed9df8c..983bb2bbcb6 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/BUILD @@ -12,6 +12,7 @@ go_library( deps = [ "//vendor/github.com/google/gofuzz:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.pb.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.pb.go index b5a8c0e0002..8e30403c8e3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.pb.go @@ -25,6 +25,7 @@ limitations under the License. k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.proto It has these top-level messages: + CustomResourceColumnDefinition CustomResourceDefinition CustomResourceDefinitionCondition CustomResourceDefinitionList @@ -67,99 +68,106 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +func (m *CustomResourceColumnDefinition) Reset() { *m = CustomResourceColumnDefinition{} } +func (*CustomResourceColumnDefinition) ProtoMessage() {} +func (*CustomResourceColumnDefinition) Descriptor() ([]byte, []int) { + return fileDescriptorGenerated, []int{0} +} + func (m *CustomResourceDefinition) Reset() { *m = CustomResourceDefinition{} } func (*CustomResourceDefinition) ProtoMessage() {} func (*CustomResourceDefinition) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{0} + return fileDescriptorGenerated, []int{1} } func (m *CustomResourceDefinitionCondition) Reset() { *m = CustomResourceDefinitionCondition{} } func (*CustomResourceDefinitionCondition) ProtoMessage() {} func (*CustomResourceDefinitionCondition) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{1} + return fileDescriptorGenerated, []int{2} } func (m *CustomResourceDefinitionList) Reset() { *m = CustomResourceDefinitionList{} } func (*CustomResourceDefinitionList) ProtoMessage() {} func (*CustomResourceDefinitionList) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{2} + return fileDescriptorGenerated, []int{3} } func (m *CustomResourceDefinitionNames) Reset() { *m = CustomResourceDefinitionNames{} } func (*CustomResourceDefinitionNames) ProtoMessage() {} func (*CustomResourceDefinitionNames) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{3} + return fileDescriptorGenerated, []int{4} } func (m *CustomResourceDefinitionSpec) Reset() { *m = CustomResourceDefinitionSpec{} } func (*CustomResourceDefinitionSpec) ProtoMessage() {} func (*CustomResourceDefinitionSpec) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{4} + return fileDescriptorGenerated, []int{5} } func (m *CustomResourceDefinitionStatus) Reset() { *m = CustomResourceDefinitionStatus{} } func (*CustomResourceDefinitionStatus) ProtoMessage() {} func (*CustomResourceDefinitionStatus) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{5} + return fileDescriptorGenerated, []int{6} } func (m *CustomResourceDefinitionVersion) Reset() { *m = CustomResourceDefinitionVersion{} } func (*CustomResourceDefinitionVersion) ProtoMessage() {} func (*CustomResourceDefinitionVersion) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{6} + return fileDescriptorGenerated, []int{7} } func (m *CustomResourceSubresourceScale) Reset() { *m = CustomResourceSubresourceScale{} } func (*CustomResourceSubresourceScale) ProtoMessage() {} func (*CustomResourceSubresourceScale) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{7} + return fileDescriptorGenerated, []int{8} } func (m *CustomResourceSubresourceStatus) Reset() { *m = CustomResourceSubresourceStatus{} } func (*CustomResourceSubresourceStatus) ProtoMessage() {} func (*CustomResourceSubresourceStatus) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{8} + return fileDescriptorGenerated, []int{9} } func (m *CustomResourceSubresources) Reset() { *m = CustomResourceSubresources{} } func (*CustomResourceSubresources) ProtoMessage() {} func (*CustomResourceSubresources) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{9} + return fileDescriptorGenerated, []int{10} } func (m *CustomResourceValidation) Reset() { *m = CustomResourceValidation{} } func (*CustomResourceValidation) ProtoMessage() {} func (*CustomResourceValidation) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{10} + return fileDescriptorGenerated, []int{11} } func (m *ExternalDocumentation) Reset() { *m = ExternalDocumentation{} } func (*ExternalDocumentation) ProtoMessage() {} -func (*ExternalDocumentation) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{11} } +func (*ExternalDocumentation) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } func (m *JSON) Reset() { *m = JSON{} } func (*JSON) ProtoMessage() {} -func (*JSON) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } +func (*JSON) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } func (m *JSONSchemaProps) Reset() { *m = JSONSchemaProps{} } func (*JSONSchemaProps) ProtoMessage() {} -func (*JSONSchemaProps) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } +func (*JSONSchemaProps) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } func (m *JSONSchemaPropsOrArray) Reset() { *m = JSONSchemaPropsOrArray{} } func (*JSONSchemaPropsOrArray) ProtoMessage() {} -func (*JSONSchemaPropsOrArray) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } +func (*JSONSchemaPropsOrArray) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } func (m *JSONSchemaPropsOrBool) Reset() { *m = JSONSchemaPropsOrBool{} } func (*JSONSchemaPropsOrBool) ProtoMessage() {} -func (*JSONSchemaPropsOrBool) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } +func (*JSONSchemaPropsOrBool) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} } func (m *JSONSchemaPropsOrStringArray) Reset() { *m = JSONSchemaPropsOrStringArray{} } func (*JSONSchemaPropsOrStringArray) ProtoMessage() {} func (*JSONSchemaPropsOrStringArray) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{16} + return fileDescriptorGenerated, []int{17} } func init() { + proto.RegisterType((*CustomResourceColumnDefinition)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceColumnDefinition") proto.RegisterType((*CustomResourceDefinition)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinition") proto.RegisterType((*CustomResourceDefinitionCondition)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition") proto.RegisterType((*CustomResourceDefinitionList)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionList") @@ -178,6 +186,47 @@ func init() { proto.RegisterType((*JSONSchemaPropsOrBool)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrBool") proto.RegisterType((*JSONSchemaPropsOrStringArray)(nil), "k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaPropsOrStringArray") } +func (m *CustomResourceColumnDefinition) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CustomResourceColumnDefinition) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + dAtA[i] = 0x1a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Format))) + i += copy(dAtA[i:], m.Format) + dAtA[i] = 0x22 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Description))) + i += copy(dAtA[i:], m.Description) + dAtA[i] = 0x28 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.Priority)) + dAtA[i] = 0x32 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.JSONPath))) + i += copy(dAtA[i:], m.JSONPath) + return i, nil +} + func (m *CustomResourceDefinition) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -431,6 +480,18 @@ func (m *CustomResourceDefinitionSpec) MarshalTo(dAtA []byte) (int, error) { i += n } } + if len(m.AdditionalPrinterColumns) > 0 { + for _, msg := range m.AdditionalPrinterColumns { + dAtA[i] = 0x42 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } return i, nil } @@ -1257,6 +1318,23 @@ func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return offset + 1 } +func (m *CustomResourceColumnDefinition) Size() (n int) { + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Type) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Format) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Description) + n += 1 + l + sovGenerated(uint64(l)) + n += 1 + sovGenerated(uint64(m.Priority)) + l = len(m.JSONPath) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *CustomResourceDefinition) Size() (n int) { var l int _ = l @@ -1350,6 +1428,12 @@ func (m *CustomResourceDefinitionSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if len(m.AdditionalPrinterColumns) > 0 { + for _, e := range m.AdditionalPrinterColumns { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } return n } @@ -1651,6 +1735,21 @@ func sovGenerated(x uint64) (n int) { func sozGenerated(x uint64) (n int) { return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (this *CustomResourceColumnDefinition) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CustomResourceColumnDefinition{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Format:` + fmt.Sprintf("%v", this.Format) + `,`, + `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `Priority:` + fmt.Sprintf("%v", this.Priority) + `,`, + `JSONPath:` + fmt.Sprintf("%v", this.JSONPath) + `,`, + `}`, + }, "") + return s +} func (this *CustomResourceDefinition) String() string { if this == nil { return "nil" @@ -1715,6 +1814,7 @@ func (this *CustomResourceDefinitionSpec) String() string { `Validation:` + strings.Replace(fmt.Sprintf("%v", this.Validation), "CustomResourceValidation", "CustomResourceValidation", 1) + `,`, `Subresources:` + strings.Replace(fmt.Sprintf("%v", this.Subresources), "CustomResourceSubresources", "CustomResourceSubresources", 1) + `,`, `Versions:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Versions), "CustomResourceDefinitionVersion", "CustomResourceDefinitionVersion", 1), `&`, ``, 1) + `,`, + `AdditionalPrinterColumns:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.AdditionalPrinterColumns), "CustomResourceColumnDefinition", "CustomResourceColumnDefinition", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -1932,6 +2032,220 @@ func valueToStringGenerated(v interface{}) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("*%v", pv) } +func (m *CustomResourceColumnDefinition) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CustomResourceColumnDefinition: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CustomResourceColumnDefinition: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Format", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Format = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) + } + m.Priority = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Priority |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JSONPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JSONPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *CustomResourceDefinition) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2846,6 +3160,37 @@ func (m *CustomResourceDefinitionSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdditionalPrinterColumns", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdditionalPrinterColumns = append(m.AdditionalPrinterColumns, CustomResourceColumnDefinition{}) + if err := m.AdditionalPrinterColumns[len(m.AdditionalPrinterColumns)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -5529,143 +5874,150 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 2200 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xcd, 0x6f, 0x1c, 0x49, - 0x15, 0x77, 0xcf, 0x78, 0xfc, 0x51, 0xb6, 0x63, 0xbb, 0x12, 0x87, 0x8e, 0x49, 0x66, 0xec, 0x59, - 0x76, 0x65, 0x60, 0x33, 0x43, 0xf6, 0x83, 0x5d, 0x56, 0xe2, 0xe0, 0xb1, 0x0d, 0xca, 0x62, 0xc7, - 0x56, 0x4d, 0x12, 0x04, 0xfb, 0x59, 0xee, 0xae, 0x19, 0x77, 0xdc, 0x5f, 0xe9, 0xaa, 0x9e, 0xd8, - 0x12, 0x20, 0x3e, 0xb4, 0x42, 0x42, 0xc0, 0x22, 0x88, 0x90, 0x90, 0xb8, 0x80, 0xc4, 0x05, 0x21, - 0x38, 0xc0, 0x91, 0x3f, 0x20, 0xc7, 0x95, 0xb8, 0xec, 0x69, 0x44, 0x86, 0x7f, 0x01, 0x09, 0xc9, - 0x27, 0x54, 0x1f, 0x5d, 0xdd, 0x3d, 0xe3, 0xd9, 0x44, 0xda, 0x99, 0xcd, 0xcd, 0xfd, 0xde, 0xab, - 0xf7, 0xfb, 0xd5, 0xab, 0x57, 0xaf, 0xde, 0x1b, 0x83, 0xd6, 0xf1, 0xeb, 0xb4, 0xe6, 0x04, 0xf5, - 0xe3, 0xf8, 0x90, 0x44, 0x3e, 0x61, 0x84, 0xd6, 0x3b, 0xc4, 0xb7, 0x83, 0xa8, 0xae, 0x14, 0x38, - 0x74, 0xc8, 0x09, 0x23, 0x3e, 0x75, 0x02, 0x9f, 0x5e, 0xc7, 0xa1, 0x43, 0x49, 0xd4, 0x21, 0x51, - 0x3d, 0x3c, 0x6e, 0x73, 0x1d, 0xcd, 0x1b, 0xd4, 0x3b, 0x37, 0x0e, 0x09, 0xc3, 0x37, 0xea, 0x6d, - 0xe2, 0x93, 0x08, 0x33, 0x62, 0xd7, 0xc2, 0x28, 0x60, 0x01, 0xfc, 0xba, 0x74, 0x57, 0xcb, 0x59, - 0xbf, 0xa7, 0xdd, 0xd5, 0xc2, 0xe3, 0x36, 0xd7, 0xd1, 0xbc, 0x41, 0x4d, 0xb9, 0x5b, 0xbd, 0xde, - 0x76, 0xd8, 0x51, 0x7c, 0x58, 0xb3, 0x02, 0xaf, 0xde, 0x0e, 0xda, 0x41, 0x5d, 0x78, 0x3d, 0x8c, - 0x5b, 0xe2, 0x4b, 0x7c, 0x88, 0xbf, 0x24, 0xda, 0xea, 0x2b, 0x29, 0x79, 0x0f, 0x5b, 0x47, 0x8e, - 0x4f, 0xa2, 0xd3, 0x94, 0xb1, 0x47, 0x18, 0xae, 0x77, 0x06, 0x38, 0xae, 0xd6, 0x87, 0xad, 0x8a, - 0x62, 0x9f, 0x39, 0x1e, 0x19, 0x58, 0xf0, 0xd5, 0x27, 0x2d, 0xa0, 0xd6, 0x11, 0xf1, 0xf0, 0xc0, - 0xba, 0x97, 0x87, 0xad, 0x8b, 0x99, 0xe3, 0xd6, 0x1d, 0x9f, 0x51, 0x16, 0xf5, 0x2f, 0xaa, 0xfe, - 0xa4, 0x08, 0xcc, 0xad, 0x98, 0xb2, 0xc0, 0x43, 0x84, 0x06, 0x71, 0x64, 0x91, 0x6d, 0xd2, 0x72, - 0x7c, 0x87, 0x39, 0x81, 0x0f, 0xdf, 0x07, 0x33, 0x7c, 0x57, 0x36, 0x66, 0xd8, 0x34, 0xd6, 0x8c, - 0x8d, 0xb9, 0x97, 0xbe, 0x52, 0x4b, 0x23, 0xae, 0x41, 0xd2, 0x30, 0x73, 0xeb, 0x5a, 0xe7, 0x46, - 0x6d, 0xff, 0xf0, 0x1e, 0xb1, 0xd8, 0x1e, 0x61, 0xb8, 0x01, 0x1f, 0x75, 0x2b, 0x13, 0xbd, 0x6e, - 0x05, 0xa4, 0x32, 0xa4, 0xbd, 0xc2, 0xef, 0x83, 0x49, 0x1a, 0x12, 0xcb, 0x2c, 0x08, 0xef, 0x6f, - 0xd5, 0x3e, 0xd5, 0x79, 0xd6, 0x86, 0x6d, 0xa4, 0x19, 0x12, 0xab, 0x31, 0xaf, 0x88, 0x4c, 0xf2, - 0x2f, 0x24, 0x60, 0xe1, 0x07, 0x06, 0x98, 0xa2, 0x0c, 0xb3, 0x98, 0x9a, 0x45, 0xc1, 0xe0, 0x9d, - 0x71, 0x31, 0x10, 0x20, 0x8d, 0x0b, 0x8a, 0xc3, 0x94, 0xfc, 0x46, 0x0a, 0xbc, 0xfa, 0xdf, 0x02, - 0x58, 0x1f, 0xb6, 0x74, 0x2b, 0xf0, 0x6d, 0x79, 0x1c, 0x37, 0xc1, 0x24, 0x3b, 0x0d, 0x89, 0x38, - 0x8a, 0xd9, 0xc6, 0xab, 0xc9, 0x7e, 0x6e, 0x9f, 0x86, 0xe4, 0xac, 0x5b, 0x79, 0xfe, 0x89, 0x0e, - 0xb8, 0x21, 0x12, 0x2e, 0xe0, 0xd7, 0xf4, 0xbe, 0x0b, 0xc2, 0xd9, 0x7a, 0x9e, 0xd8, 0x59, 0xb7, - 0xb2, 0xa8, 0x97, 0xe5, 0xb9, 0xc2, 0x0e, 0x80, 0x2e, 0xa6, 0xec, 0x76, 0x84, 0x7d, 0x2a, 0xdd, - 0x3a, 0x1e, 0x51, 0xe1, 0xfb, 0xd2, 0xd3, 0xa5, 0x07, 0x5f, 0xd1, 0x58, 0x55, 0x90, 0x70, 0x77, - 0xc0, 0x1b, 0x3a, 0x07, 0x01, 0xbe, 0x00, 0xa6, 0x22, 0x82, 0x69, 0xe0, 0x9b, 0x93, 0x82, 0xb2, - 0x8e, 0x25, 0x12, 0x52, 0xa4, 0xb4, 0xf0, 0x8b, 0x60, 0xda, 0x23, 0x94, 0xe2, 0x36, 0x31, 0x4b, - 0xc2, 0x70, 0x51, 0x19, 0x4e, 0xef, 0x49, 0x31, 0x4a, 0xf4, 0xd5, 0x33, 0x03, 0x5c, 0x1d, 0x16, - 0xb5, 0x5d, 0x87, 0x32, 0xf8, 0xf6, 0xc0, 0x05, 0xa8, 0x3d, 0xdd, 0x0e, 0xf9, 0x6a, 0x91, 0xfe, - 0x4b, 0x0a, 0x7c, 0x26, 0x91, 0x64, 0x92, 0xff, 0x7b, 0xa0, 0xe4, 0x30, 0xe2, 0xf1, 0x33, 0x28, - 0x6e, 0xcc, 0xbd, 0xf4, 0xed, 0x31, 0xe5, 0x5e, 0x63, 0x41, 0x71, 0x28, 0xdd, 0xe4, 0x68, 0x48, - 0x82, 0x56, 0xff, 0x54, 0x00, 0xd7, 0x86, 0x2d, 0xb9, 0x85, 0x3d, 0x42, 0x79, 0xc4, 0x43, 0x37, - 0x8e, 0xb0, 0xab, 0x32, 0x4e, 0x47, 0xfc, 0x40, 0x48, 0x91, 0xd2, 0xc2, 0x17, 0xc1, 0x0c, 0x75, - 0xfc, 0x76, 0xec, 0xe2, 0x48, 0xa5, 0x93, 0xde, 0x75, 0x53, 0xc9, 0x91, 0xb6, 0x80, 0x35, 0x00, - 0xe8, 0x51, 0x10, 0x31, 0x81, 0x61, 0x16, 0xd7, 0x8a, 0xdc, 0x33, 0x2f, 0x10, 0x4d, 0x2d, 0x45, - 0x19, 0x0b, 0xb8, 0x06, 0x26, 0x8f, 0x1d, 0xdf, 0x56, 0xa7, 0xae, 0x6f, 0xf1, 0xb7, 0x1c, 0xdf, - 0x46, 0x42, 0xc3, 0xf1, 0x5d, 0x87, 0x32, 0x2e, 0x51, 0x47, 0x9e, 0x8b, 0xba, 0xb0, 0xd4, 0x16, - 0x1c, 0xdf, 0xc2, 0x8c, 0xb4, 0x83, 0xc8, 0x21, 0xd4, 0x9c, 0x4a, 0xf1, 0xb7, 0xb4, 0x14, 0x65, - 0x2c, 0xaa, 0xff, 0x2a, 0x0d, 0x4f, 0x12, 0x5e, 0x4a, 0xe0, 0x73, 0xa0, 0xd4, 0x8e, 0x82, 0x38, - 0x54, 0x51, 0xd2, 0xd1, 0xfe, 0x26, 0x17, 0x22, 0xa9, 0xe3, 0x59, 0xd9, 0x21, 0x11, 0x3f, 0x30, - 0x15, 0x22, 0x9d, 0x95, 0x77, 0xa5, 0x18, 0x25, 0x7a, 0xf8, 0x23, 0x03, 0x94, 0x7c, 0x15, 0x1c, - 0x9e, 0x72, 0x6f, 0x8f, 0x29, 0x2f, 0x44, 0x78, 0x53, 0xba, 0x32, 0xf2, 0x12, 0x19, 0xbe, 0x02, - 0x4a, 0xd4, 0x0a, 0x42, 0xa2, 0xa2, 0x5e, 0x4e, 0x8c, 0x9a, 0x5c, 0x78, 0xd6, 0xad, 0x2c, 0x24, - 0xee, 0x84, 0x00, 0x49, 0x63, 0xf8, 0x53, 0x03, 0x80, 0x0e, 0x76, 0x1d, 0x1b, 0x73, 0xff, 0xe2, - 0x2c, 0x46, 0x9d, 0xd6, 0x77, 0xb5, 0x7b, 0x79, 0x68, 0xe9, 0x37, 0xca, 0x40, 0xc3, 0x0f, 0x0d, - 0x30, 0x4f, 0xe3, 0xc3, 0x48, 0xad, 0xe2, 0xe7, 0xcc, 0xb9, 0x7c, 0x67, 0xa4, 0x5c, 0x9a, 0x19, - 0x80, 0xc6, 0x52, 0xaf, 0x5b, 0x99, 0xcf, 0x4a, 0x50, 0x8e, 0x00, 0xfc, 0xb9, 0x01, 0x66, 0xd4, - 0x09, 0x53, 0x73, 0x5a, 0x5c, 0xf8, 0x77, 0xc7, 0x74, 0xb0, 0x2a, 0xa3, 0xd2, 0x5b, 0xa0, 0x04, - 0x14, 0x69, 0x06, 0xd5, 0x0f, 0x8b, 0xa0, 0xfc, 0xc9, 0x8f, 0x15, 0x7c, 0x68, 0x00, 0x60, 0x25, - 0x8f, 0x00, 0x35, 0x0d, 0xc1, 0xf9, 0xfd, 0x31, 0x71, 0xd6, 0xaf, 0x4d, 0xda, 0x30, 0x68, 0x11, - 0xbf, 0x8f, 0xfa, 0x6f, 0xf8, 0x3b, 0x03, 0x2c, 0x60, 0xcb, 0x22, 0x21, 0x23, 0xb6, 0xac, 0x21, - 0x85, 0xcf, 0xe0, 0x9a, 0xac, 0x28, 0x56, 0x0b, 0x9b, 0x59, 0x68, 0x94, 0x67, 0x02, 0xdf, 0x00, - 0x17, 0x28, 0x0b, 0x22, 0x62, 0x27, 0x11, 0x57, 0xf5, 0x0d, 0xf6, 0xba, 0x95, 0x0b, 0xcd, 0x9c, - 0x06, 0xf5, 0x59, 0x56, 0x7f, 0x6b, 0x80, 0xca, 0x13, 0x4e, 0x94, 0xd7, 0x42, 0x7e, 0x3f, 0x55, - 0xa5, 0xd1, 0xb5, 0x90, 0x83, 0x23, 0xa1, 0xe1, 0x35, 0x5b, 0x6c, 0xd7, 0x16, 0x51, 0x99, 0xc9, - 0x74, 0x1c, 0x42, 0x8a, 0x94, 0x96, 0xd7, 0x23, 0x8e, 0xcf, 0x5f, 0xc9, 0xa2, 0x30, 0xd4, 0xf5, - 0xa8, 0x29, 0xc5, 0x28, 0xd1, 0x57, 0xff, 0x67, 0xf4, 0xa7, 0x4a, 0x26, 0xcd, 0x9b, 0x16, 0x76, - 0x09, 0xdc, 0x06, 0x4b, 0xbc, 0x9f, 0x42, 0x24, 0x74, 0x1d, 0x0b, 0xd3, 0x03, 0xcc, 0x8e, 0x14, - 0x47, 0x53, 0xb9, 0x5d, 0x6a, 0xf6, 0xe9, 0xd1, 0xc0, 0x0a, 0xf8, 0x26, 0x80, 0xb2, 0xc7, 0xc8, - 0xf9, 0x91, 0xe5, 0x52, 0x77, 0x0b, 0xcd, 0x01, 0x0b, 0x74, 0xce, 0x2a, 0xb8, 0x05, 0x96, 0x5d, - 0x7c, 0x48, 0xdc, 0x26, 0x71, 0x89, 0xc5, 0x82, 0x48, 0xb8, 0x2a, 0x0a, 0x57, 0x2b, 0xbd, 0x6e, - 0x65, 0x79, 0xb7, 0x5f, 0x89, 0x06, 0xed, 0xab, 0xeb, 0xfd, 0x27, 0x92, 0xdd, 0xb8, 0xec, 0xdc, - 0xfe, 0x50, 0x00, 0xab, 0xc3, 0xab, 0x02, 0xfc, 0x71, 0xda, 0x60, 0xca, 0xfe, 0xe1, 0xdd, 0x71, - 0x55, 0x20, 0xd5, 0x61, 0x82, 0xc1, 0xee, 0x12, 0xfe, 0x80, 0x17, 0x73, 0xec, 0x12, 0x75, 0x51, - 0xde, 0x19, 0x1b, 0x05, 0x0e, 0xd2, 0x98, 0x95, 0xef, 0x04, 0x76, 0xc5, 0xb3, 0x80, 0x5d, 0x52, - 0xfd, 0xb3, 0xd1, 0x3f, 0x63, 0xa4, 0x55, 0x1b, 0xfe, 0xc2, 0x00, 0x8b, 0x41, 0x48, 0xfc, 0xcd, - 0x83, 0x9b, 0x77, 0x5f, 0x6e, 0x8a, 0xc9, 0x46, 0x85, 0xea, 0xd6, 0xa7, 0xe4, 0xf9, 0x66, 0x73, - 0xff, 0x96, 0x74, 0x78, 0x10, 0x05, 0x21, 0x6d, 0x5c, 0xec, 0x75, 0x2b, 0x8b, 0xfb, 0x79, 0x28, - 0xd4, 0x8f, 0x5d, 0xf5, 0xc0, 0xca, 0xce, 0x09, 0x23, 0x91, 0x8f, 0xdd, 0xed, 0xc0, 0x8a, 0x3d, - 0xe2, 0x33, 0x49, 0xf4, 0x55, 0x30, 0x67, 0x13, 0x6a, 0x45, 0x4e, 0x28, 0x1e, 0x37, 0x99, 0xde, - 0x17, 0x55, 0x5a, 0xce, 0x6d, 0xa7, 0x2a, 0x94, 0xb5, 0x83, 0xd7, 0x40, 0x31, 0x8e, 0x5c, 0x95, - 0xc5, 0x73, 0xca, 0xbc, 0x78, 0x07, 0xed, 0x22, 0x2e, 0xaf, 0xae, 0x83, 0x49, 0xce, 0x13, 0x5e, - 0x01, 0xc5, 0x08, 0x3f, 0x10, 0x5e, 0xe7, 0x1b, 0xd3, 0xdc, 0x04, 0xe1, 0x07, 0x88, 0xcb, 0xaa, - 0x7f, 0xb9, 0x0a, 0x16, 0xfb, 0xf6, 0x02, 0x57, 0x41, 0xc1, 0xb1, 0x15, 0x07, 0xa0, 0x9c, 0x16, - 0x6e, 0x6e, 0xa3, 0x82, 0x63, 0xc3, 0xd7, 0xc0, 0x94, 0x9c, 0x10, 0x15, 0x68, 0x45, 0x97, 0x00, - 0x21, 0xe5, 0xaf, 0x77, 0xea, 0x8e, 0x13, 0x51, 0xe6, 0x82, 0x03, 0x69, 0xa9, 0x5b, 0x22, 0x39, - 0x90, 0x16, 0xe2, 0xb2, 0xfe, 0xcd, 0x4f, 0x3e, 0xe5, 0xe6, 0xd7, 0xd4, 0xc4, 0x52, 0xca, 0xd7, - 0xab, 0xcc, 0x20, 0xf2, 0x02, 0x98, 0x6a, 0x05, 0x91, 0x87, 0x99, 0x78, 0xa1, 0x33, 0x3d, 0xe6, - 0x37, 0x84, 0x14, 0x29, 0x2d, 0x6f, 0xb2, 0x98, 0xc3, 0x5c, 0x62, 0x4e, 0xe7, 0x9b, 0xac, 0xdb, - 0x5c, 0x88, 0xa4, 0x0e, 0xde, 0x03, 0xd3, 0x36, 0x69, 0xe1, 0xd8, 0x65, 0xe6, 0x8c, 0x48, 0xa1, - 0xad, 0x11, 0xa4, 0x50, 0x63, 0x8e, 0x57, 0xc5, 0x6d, 0xe9, 0x17, 0x25, 0x00, 0xf0, 0x79, 0x30, - 0xed, 0xe1, 0x13, 0xc7, 0x8b, 0x3d, 0x73, 0x76, 0xcd, 0xd8, 0x30, 0xa4, 0xd9, 0x9e, 0x14, 0xa1, - 0x44, 0xc7, 0x2b, 0x23, 0x39, 0xb1, 0xdc, 0x98, 0x3a, 0x1d, 0xa2, 0x94, 0x26, 0x10, 0x05, 0x57, - 0x57, 0xc6, 0x9d, 0x3e, 0x3d, 0x1a, 0x58, 0x21, 0xc0, 0x1c, 0x5f, 0x2c, 0x9e, 0xcb, 0x80, 0x49, - 0x11, 0x4a, 0x74, 0x79, 0x30, 0x65, 0x3f, 0x3f, 0x0c, 0x4c, 0x2d, 0x1e, 0x58, 0x01, 0xbf, 0x0c, - 0x66, 0x3d, 0x7c, 0xb2, 0x4b, 0xfc, 0x36, 0x3b, 0x32, 0x17, 0xd6, 0x8c, 0x8d, 0x62, 0x63, 0xa1, - 0xd7, 0xad, 0xcc, 0xee, 0x25, 0x42, 0x94, 0xea, 0x85, 0xb1, 0xe3, 0x2b, 0xe3, 0x0b, 0x19, 0xe3, - 0x44, 0x88, 0x52, 0x3d, 0x7f, 0x74, 0x42, 0xcc, 0xf8, 0xe5, 0x32, 0x17, 0xf3, 0x4d, 0xf0, 0x81, - 0x14, 0xa3, 0x44, 0x0f, 0x37, 0xc0, 0x8c, 0x87, 0x4f, 0xc4, 0xc0, 0x62, 0x2e, 0x09, 0xb7, 0xf3, - 0xbc, 0x93, 0xd9, 0x53, 0x32, 0xa4, 0xb5, 0xc2, 0xd2, 0xf1, 0xa5, 0xe5, 0x72, 0xc6, 0x52, 0xc9, - 0x90, 0xd6, 0xf2, 0x24, 0x8e, 0x7d, 0xe7, 0x7e, 0x4c, 0xa4, 0x31, 0x14, 0x91, 0xd1, 0x49, 0x7c, - 0x27, 0x55, 0xa1, 0xac, 0x1d, 0x1f, 0x18, 0xbc, 0xd8, 0x65, 0x4e, 0xe8, 0x92, 0xfd, 0x96, 0x79, - 0x51, 0xc4, 0x5f, 0xf4, 0x9e, 0x7b, 0x5a, 0x8a, 0x32, 0x16, 0x90, 0x80, 0x49, 0xe2, 0xc7, 0x9e, - 0x79, 0x49, 0x34, 0x4c, 0x23, 0x49, 0x41, 0x7d, 0x73, 0x76, 0xfc, 0xd8, 0x43, 0xc2, 0x3d, 0x7c, - 0x0d, 0x2c, 0x78, 0xf8, 0x84, 0x97, 0x03, 0x12, 0x31, 0x3e, 0xca, 0xac, 0x88, 0xcd, 0x2f, 0xf3, - 0x26, 0x65, 0x2f, 0xab, 0x40, 0x79, 0x3b, 0xb1, 0xd0, 0xf1, 0x33, 0x0b, 0x2f, 0x67, 0x16, 0x66, - 0x15, 0x28, 0x6f, 0xc7, 0x23, 0x1d, 0x91, 0xfb, 0xb1, 0x13, 0x11, 0xdb, 0xfc, 0x9c, 0xe8, 0x6b, - 0x44, 0xa4, 0x91, 0x92, 0x21, 0xad, 0x85, 0x9d, 0x64, 0xb2, 0x35, 0xc5, 0x35, 0xbc, 0x33, 0xda, - 0x4a, 0xbe, 0x1f, 0x6d, 0x46, 0x11, 0x3e, 0x95, 0x2f, 0x4d, 0x76, 0xa6, 0x85, 0x14, 0x94, 0xb0, - 0xeb, 0xee, 0xb7, 0xcc, 0x2b, 0x22, 0xf6, 0xa3, 0x7e, 0x41, 0x74, 0xd5, 0xd9, 0xe4, 0x20, 0x48, - 0x62, 0x71, 0xd0, 0xc0, 0xe7, 0xa9, 0xb1, 0x3a, 0x5e, 0xd0, 0x7d, 0x0e, 0x82, 0x24, 0x96, 0xd8, - 0xa9, 0x7f, 0xba, 0xdf, 0x32, 0x3f, 0x3f, 0xe6, 0x9d, 0x72, 0x10, 0x24, 0xb1, 0xa0, 0x03, 0x8a, - 0x7e, 0xc0, 0xcc, 0xab, 0x63, 0x79, 0x9e, 0xc5, 0x83, 0x73, 0x2b, 0x60, 0x88, 0x63, 0xc0, 0x5f, - 0x1b, 0x00, 0x84, 0x69, 0x8a, 0x5e, 0x1b, 0xc9, 0xc0, 0xd4, 0x07, 0x59, 0x4b, 0x73, 0x7b, 0xc7, - 0x67, 0xd1, 0x69, 0x3a, 0x7a, 0x64, 0xee, 0x40, 0x86, 0x05, 0xfc, 0xa3, 0x01, 0x2e, 0x61, 0x5b, - 0x0e, 0x22, 0xd8, 0xcd, 0xdc, 0xa0, 0xb2, 0x88, 0xc8, 0xed, 0x51, 0xa7, 0x79, 0x23, 0x08, 0xdc, - 0x86, 0xd9, 0xeb, 0x56, 0x2e, 0x6d, 0x9e, 0x83, 0x8a, 0xce, 0xe5, 0x02, 0xff, 0x6a, 0x80, 0x65, - 0x55, 0x45, 0x33, 0x0c, 0x2b, 0x22, 0x80, 0x64, 0xd4, 0x01, 0xec, 0xc7, 0x91, 0x71, 0xbc, 0xa2, - 0xe2, 0xb8, 0x3c, 0xa0, 0x47, 0x83, 0xd4, 0xe0, 0x3f, 0x0c, 0x30, 0x6f, 0x93, 0x90, 0xf8, 0x36, - 0xf1, 0x2d, 0xce, 0x75, 0x6d, 0x24, 0x93, 0x66, 0x3f, 0xd7, 0xed, 0x0c, 0x84, 0xa4, 0x59, 0x53, - 0x34, 0xe7, 0xb3, 0xaa, 0xb3, 0x6e, 0xe5, 0x72, 0xba, 0x34, 0xab, 0x41, 0x39, 0x96, 0xf0, 0x37, - 0x06, 0x58, 0x4c, 0x0f, 0x40, 0x3e, 0x29, 0xeb, 0x63, 0xcc, 0x03, 0xd1, 0xbe, 0x6e, 0xe6, 0x01, - 0x51, 0x3f, 0x03, 0xf8, 0x37, 0x83, 0x77, 0x6a, 0xc9, 0xdc, 0x48, 0xcd, 0xaa, 0x88, 0xe5, 0x7b, - 0x23, 0x8f, 0xa5, 0x46, 0x90, 0xa1, 0x7c, 0x31, 0x6d, 0x05, 0xb5, 0xe6, 0xac, 0x5b, 0x59, 0xc9, - 0x46, 0x52, 0x2b, 0x50, 0x96, 0x21, 0xfc, 0x99, 0x01, 0xe6, 0x49, 0xda, 0x71, 0x53, 0xf3, 0xb9, - 0x91, 0x04, 0xf1, 0xdc, 0x26, 0x5e, 0xfe, 0x4a, 0x93, 0x51, 0x51, 0x94, 0xc3, 0xe6, 0x1d, 0x24, - 0x39, 0xc1, 0x5e, 0xe8, 0x12, 0xf3, 0x0b, 0x23, 0xee, 0x20, 0x77, 0xa4, 0x5f, 0x94, 0x00, 0xac, - 0xf2, 0xc9, 0xa7, 0xef, 0xe6, 0xc0, 0x25, 0x50, 0x3c, 0x26, 0xa7, 0xb2, 0xb1, 0x47, 0xfc, 0x4f, - 0x68, 0x83, 0x52, 0x07, 0xbb, 0x71, 0x32, 0xbc, 0x8d, 0xb8, 0xea, 0x22, 0xe9, 0xfc, 0x8d, 0xc2, - 0xeb, 0xc6, 0xea, 0x43, 0x03, 0x5c, 0x3e, 0xff, 0x42, 0x3f, 0x53, 0x5a, 0xbf, 0x37, 0xc0, 0xf2, - 0xc0, 0xdd, 0x3d, 0x87, 0xd1, 0xfd, 0x3c, 0xa3, 0xb7, 0x46, 0x7d, 0x09, 0x9b, 0x2c, 0x72, 0xfc, - 0xb6, 0xe8, 0x3c, 0xb2, 0xf4, 0x7e, 0x69, 0x80, 0xa5, 0xfe, 0xeb, 0xf0, 0x2c, 0xe3, 0x55, 0x7d, - 0x58, 0x00, 0x97, 0xcf, 0x6f, 0x98, 0x60, 0xa4, 0x27, 0xc3, 0xf1, 0x4c, 0xd8, 0x20, 0x9d, 0x32, - 0xf5, 0x50, 0xf9, 0x81, 0x01, 0xe6, 0xee, 0x69, 0xbb, 0xe4, 0x7f, 0x1d, 0x23, 0x9f, 0xed, 0x93, - 0xfa, 0x93, 0x2a, 0x28, 0xca, 0xe2, 0x56, 0xff, 0x6e, 0x80, 0x95, 0x73, 0x0b, 0x2b, 0x1f, 0x41, - 0xb1, 0xeb, 0x06, 0x0f, 0xe4, 0x4f, 0x34, 0x99, 0x9f, 0xcc, 0x36, 0x85, 0x14, 0x29, 0x6d, 0x26, - 0x7a, 0x85, 0xcf, 0x2a, 0x7a, 0xd5, 0x7f, 0x1a, 0xe0, 0xea, 0x27, 0x65, 0xe2, 0x33, 0x39, 0xd2, - 0x0d, 0x30, 0xa3, 0x9a, 0xa2, 0x53, 0x71, 0x9c, 0x6a, 0x0e, 0x50, 0x45, 0xe3, 0x14, 0x69, 0x6d, - 0xe3, 0xfa, 0xa3, 0xc7, 0xe5, 0x89, 0x8f, 0x1e, 0x97, 0x27, 0x3e, 0x7e, 0x5c, 0x9e, 0xf8, 0x61, - 0xaf, 0x6c, 0x3c, 0xea, 0x95, 0x8d, 0x8f, 0x7a, 0x65, 0xe3, 0xe3, 0x5e, 0xd9, 0xf8, 0x77, 0xaf, - 0x6c, 0xfc, 0xea, 0x3f, 0xe5, 0x89, 0xef, 0x4e, 0x2b, 0xf0, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, - 0x8c, 0x3a, 0x89, 0x32, 0x37, 0x20, 0x00, 0x00, + // 2306 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xdd, 0x6f, 0x5b, 0x49, + 0x15, 0xef, 0xb5, 0xe3, 0xc4, 0x19, 0x27, 0x4d, 0x32, 0x6d, 0xca, 0x6d, 0x68, 0xed, 0xd4, 0x65, + 0x57, 0x01, 0xb6, 0x36, 0xed, 0xee, 0xb2, 0xcb, 0x4a, 0x3c, 0xc4, 0x49, 0x41, 0x5d, 0x9a, 0x26, + 0x1a, 0xb7, 0x45, 0xb0, 0x9f, 0x13, 0x7b, 0xe2, 0xdc, 0xe6, 0x7e, 0x75, 0x66, 0xae, 0x9b, 0x48, + 0x80, 0xf8, 0xd0, 0x0a, 0x09, 0x01, 0x0b, 0x6c, 0x85, 0x84, 0xc4, 0x0b, 0x48, 0xbc, 0x20, 0x04, + 0x0f, 0xf0, 0x06, 0x7f, 0x40, 0x1f, 0xf7, 0x71, 0x9f, 0x2c, 0x6a, 0xfe, 0x05, 0x24, 0xa4, 0x3c, + 0xa1, 0xf9, 0xb8, 0x73, 0xef, 0xb5, 0xe3, 0x6d, 0xb5, 0x6b, 0x6f, 0xdf, 0x7c, 0xcf, 0x39, 0x73, + 0x7e, 0xbf, 0x39, 0x73, 0xe6, 0xcc, 0x39, 0x09, 0xd8, 0x3b, 0x78, 0x95, 0xd5, 0x9c, 0xa0, 0x7e, + 0x10, 0xed, 0x12, 0xea, 0x13, 0x4e, 0x58, 0xbd, 0x4b, 0xfc, 0x76, 0x40, 0xeb, 0x5a, 0x81, 0x43, + 0x87, 0x1c, 0x72, 0xe2, 0x33, 0x27, 0xf0, 0xd9, 0x15, 0x1c, 0x3a, 0x8c, 0xd0, 0x2e, 0xa1, 0xf5, + 0xf0, 0xa0, 0x23, 0x74, 0x2c, 0x6b, 0x50, 0xef, 0x5e, 0xdd, 0x25, 0x1c, 0x5f, 0xad, 0x77, 0x88, + 0x4f, 0x28, 0xe6, 0xa4, 0x5d, 0x0b, 0x69, 0xc0, 0x03, 0xf8, 0x75, 0xe5, 0xae, 0x96, 0xb1, 0x7e, + 0xc7, 0xb8, 0xab, 0x85, 0x07, 0x1d, 0xa1, 0x63, 0x59, 0x83, 0x9a, 0x76, 0xb7, 0x72, 0xa5, 0xe3, + 0xf0, 0xfd, 0x68, 0xb7, 0xd6, 0x0a, 0xbc, 0x7a, 0x27, 0xe8, 0x04, 0x75, 0xe9, 0x75, 0x37, 0xda, + 0x93, 0x5f, 0xf2, 0x43, 0xfe, 0x52, 0x68, 0x2b, 0x2f, 0x25, 0xe4, 0x3d, 0xdc, 0xda, 0x77, 0x7c, + 0x42, 0x8f, 0x12, 0xc6, 0x1e, 0xe1, 0xb8, 0xde, 0x1d, 0xe2, 0xb8, 0x52, 0x1f, 0xb5, 0x8a, 0x46, + 0x3e, 0x77, 0x3c, 0x32, 0xb4, 0xe0, 0xab, 0x4f, 0x5a, 0xc0, 0x5a, 0xfb, 0xc4, 0xc3, 0x43, 0xeb, + 0x5e, 0x1c, 0xb5, 0x2e, 0xe2, 0x8e, 0x5b, 0x77, 0x7c, 0xce, 0x38, 0x1d, 0x5c, 0x54, 0xfd, 0x20, + 0x07, 0xca, 0x1b, 0x11, 0xe3, 0x81, 0x87, 0x08, 0x0b, 0x22, 0xda, 0x22, 0x1b, 0x81, 0x1b, 0x79, + 0xfe, 0x26, 0xd9, 0x73, 0x7c, 0x87, 0x3b, 0x81, 0x0f, 0x57, 0xc1, 0x94, 0x8f, 0x3d, 0x62, 0x5b, + 0xab, 0xd6, 0xda, 0x6c, 0x63, 0xee, 0x51, 0xaf, 0x72, 0xaa, 0xdf, 0xab, 0x4c, 0xdd, 0xc2, 0x1e, + 0x41, 0x52, 0x23, 0x2c, 0xf8, 0x51, 0x48, 0xec, 0x5c, 0xd6, 0xe2, 0xf6, 0x51, 0x48, 0x90, 0xd4, + 0xc0, 0xe7, 0xc1, 0xf4, 0x5e, 0x40, 0x3d, 0xcc, 0xed, 0xbc, 0xb4, 0x39, 0xad, 0x6d, 0xa6, 0xbf, + 0x21, 0xa5, 0x48, 0x6b, 0xe1, 0xcb, 0xa0, 0xd4, 0x26, 0xac, 0x45, 0x9d, 0x50, 0x40, 0xdb, 0x53, + 0xd2, 0xf8, 0x8c, 0x36, 0x2e, 0x6d, 0x26, 0x2a, 0x94, 0xb6, 0x83, 0x2f, 0x80, 0x62, 0x48, 0x9d, + 0x80, 0x3a, 0xfc, 0xc8, 0x2e, 0xac, 0x5a, 0x6b, 0x85, 0xc6, 0xa2, 0x5e, 0x53, 0xdc, 0xd1, 0x72, + 0x64, 0x2c, 0xe0, 0x2a, 0x28, 0xbe, 0xde, 0xdc, 0xbe, 0xb5, 0x83, 0xf9, 0xbe, 0x3d, 0x2d, 0x11, + 0xa6, 0x84, 0x35, 0x2a, 0xde, 0xd3, 0xd2, 0xea, 0x4f, 0xf2, 0xc0, 0xce, 0x46, 0x25, 0x15, 0x8f, + 0x77, 0x41, 0x51, 0x9c, 0x75, 0x1b, 0x73, 0x2c, 0x63, 0x52, 0xba, 0xf6, 0x95, 0x5a, 0x92, 0x87, + 0x26, 0xf4, 0x49, 0xf2, 0x09, 0xeb, 0x5a, 0xf7, 0x6a, 0x6d, 0x7b, 0xf7, 0x1e, 0x69, 0xf1, 0x2d, + 0xc2, 0x71, 0x03, 0x6a, 0x7a, 0x20, 0x91, 0x21, 0xe3, 0x15, 0x7e, 0x1f, 0x4c, 0xb1, 0x90, 0xb4, + 0x64, 0x3c, 0x4b, 0xd7, 0xde, 0xa8, 0x7d, 0xaa, 0x2c, 0xaf, 0x8d, 0xda, 0x48, 0x33, 0x24, 0xad, + 0xe4, 0xb0, 0xc4, 0x17, 0x92, 0xb0, 0xf0, 0x3d, 0x0b, 0x4c, 0x33, 0x8e, 0x79, 0xc4, 0xe4, 0x69, + 0x95, 0xae, 0xbd, 0x35, 0x29, 0x06, 0x12, 0x24, 0x49, 0x06, 0xf5, 0x8d, 0x34, 0x78, 0xf5, 0xbf, + 0x39, 0x70, 0x69, 0xd4, 0xd2, 0x8d, 0xc0, 0x6f, 0xab, 0xe3, 0xb8, 0xa1, 0x93, 0x4f, 0xa5, 0xe7, + 0xcb, 0xe9, 0xe4, 0x3b, 0xee, 0x55, 0x9e, 0x7b, 0xa2, 0x83, 0x54, 0x96, 0x7e, 0xcd, 0xec, 0x5b, + 0x65, 0xf2, 0xa5, 0x2c, 0xb1, 0xe3, 0x5e, 0x65, 0xc1, 0x2c, 0xcb, 0x72, 0x85, 0x5d, 0x00, 0x5d, + 0xcc, 0xf8, 0x6d, 0x8a, 0x7d, 0xa6, 0xdc, 0x3a, 0x1e, 0xd1, 0xe1, 0xfb, 0xd2, 0xd3, 0xa5, 0x87, + 0x58, 0xd1, 0x58, 0xd1, 0x90, 0xf0, 0xe6, 0x90, 0x37, 0x74, 0x02, 0x82, 0xb8, 0x58, 0x94, 0x60, + 0x66, 0xee, 0x8a, 0x89, 0x25, 0x92, 0x52, 0xa4, 0xb5, 0xf0, 0x8b, 0x60, 0xc6, 0x23, 0x8c, 0xe1, + 0x0e, 0x91, 0x17, 0x64, 0xb6, 0xb1, 0xa0, 0x0d, 0x67, 0xb6, 0x94, 0x18, 0xc5, 0xfa, 0xea, 0xb1, + 0x05, 0x2e, 0x8c, 0x8a, 0xda, 0x4d, 0x87, 0x71, 0xf8, 0xe6, 0xd0, 0x05, 0xa8, 0x3d, 0xdd, 0x0e, + 0xc5, 0x6a, 0x99, 0xfe, 0xe6, 0x76, 0xc6, 0x92, 0x54, 0xf2, 0x7f, 0x0f, 0x14, 0x1c, 0x4e, 0x3c, + 0x71, 0x06, 0xf9, 0xb5, 0xd2, 0xb5, 0x6f, 0x4f, 0x28, 0xf7, 0x1a, 0xf3, 0x9a, 0x43, 0xe1, 0x86, + 0x40, 0x43, 0x0a, 0xb4, 0xfa, 0xa7, 0x1c, 0xb8, 0x38, 0x6a, 0x89, 0xa8, 0x78, 0x4c, 0x44, 0x3c, + 0x74, 0x23, 0x8a, 0x5d, 0x9d, 0x71, 0x26, 0xe2, 0x3b, 0x52, 0x8a, 0xb4, 0x56, 0xd4, 0x24, 0xe6, + 0xf8, 0x9d, 0xc8, 0xc5, 0x54, 0xa7, 0x93, 0xd9, 0x75, 0x53, 0xcb, 0x91, 0xb1, 0x80, 0x35, 0x00, + 0xd8, 0x7e, 0x40, 0xb9, 0xc4, 0xb0, 0xf3, 0xab, 0x79, 0xe1, 0x59, 0x14, 0x88, 0xa6, 0x91, 0xa2, + 0x94, 0x85, 0x28, 0xb9, 0x07, 0x8e, 0xdf, 0xd6, 0xa7, 0x6e, 0x6e, 0xf1, 0xb7, 0x1c, 0xbf, 0x8d, + 0xa4, 0x46, 0xe0, 0xbb, 0x0e, 0xe3, 0x42, 0xa2, 0x8f, 0x3c, 0x13, 0x75, 0x69, 0x69, 0x2c, 0x04, + 0x7e, 0x0b, 0x73, 0xd2, 0x09, 0xa8, 0x43, 0x98, 0x3d, 0x9d, 0xe0, 0x6f, 0x18, 0x29, 0x4a, 0x59, + 0x54, 0x7f, 0x3d, 0x33, 0x3a, 0x49, 0x44, 0x29, 0x81, 0x97, 0x41, 0xa1, 0x43, 0x83, 0x28, 0xd4, + 0x51, 0x32, 0xd1, 0xfe, 0xa6, 0x10, 0x22, 0xa5, 0x13, 0x59, 0xd9, 0x25, 0x54, 0x1c, 0x98, 0x0e, + 0x91, 0xc9, 0xca, 0xbb, 0x4a, 0x8c, 0x62, 0x3d, 0xfc, 0x91, 0x05, 0x0a, 0xbe, 0x0e, 0x8e, 0x48, + 0xb9, 0x37, 0x27, 0x94, 0x17, 0x32, 0xbc, 0x09, 0x5d, 0x15, 0x79, 0x85, 0x0c, 0x5f, 0x02, 0x05, + 0xd6, 0x0a, 0x42, 0xa2, 0xa3, 0x5e, 0x8e, 0x8d, 0x9a, 0x42, 0x78, 0xdc, 0xab, 0xcc, 0xc7, 0xee, + 0xa4, 0x00, 0x29, 0x63, 0xf8, 0x53, 0x0b, 0x80, 0x2e, 0x76, 0x9d, 0x36, 0x96, 0x6f, 0x5a, 0x41, + 0xd2, 0x1f, 0x6f, 0x5a, 0xdf, 0x35, 0xee, 0xd5, 0xa1, 0x25, 0xdf, 0x28, 0x05, 0x0d, 0xdf, 0xb7, + 0xc0, 0x1c, 0x8b, 0x76, 0xa9, 0x5e, 0xc5, 0xe4, 0xeb, 0x57, 0xba, 0xf6, 0x9d, 0xb1, 0x72, 0x69, + 0xa6, 0x00, 0x1a, 0x8b, 0xfd, 0x5e, 0x65, 0x2e, 0x2d, 0x41, 0x19, 0x02, 0xf0, 0xe7, 0x16, 0x28, + 0xea, 0x13, 0x66, 0xf6, 0x8c, 0xbc, 0xf0, 0x6f, 0x4f, 0xe8, 0x60, 0x75, 0x46, 0x25, 0xb7, 0x40, + 0x0b, 0x18, 0x32, 0x0c, 0xe0, 0x3f, 0x2d, 0x60, 0xe3, 0xb6, 0x2a, 0xf0, 0xd8, 0xdd, 0xa1, 0x8e, + 0xcf, 0x09, 0x55, 0x0d, 0x11, 0xb3, 0x8b, 0x92, 0xde, 0x78, 0xdf, 0xc2, 0xc1, 0x66, 0xab, 0xb1, + 0xaa, 0xd9, 0xd9, 0xeb, 0x23, 0x68, 0xa0, 0x91, 0x04, 0xab, 0xef, 0xe7, 0x07, 0x7b, 0xb9, 0xc1, + 0xa7, 0x16, 0x3e, 0xb4, 0x00, 0x68, 0xc5, 0x4f, 0x18, 0xb3, 0x2d, 0xb9, 0xa5, 0x77, 0x27, 0x14, + 0x71, 0xf3, 0x56, 0x26, 0xed, 0x8e, 0x11, 0x89, 0x6a, 0x62, 0x7e, 0xc3, 0xdf, 0x59, 0x60, 0x1e, + 0xb7, 0x5a, 0x24, 0xe4, 0xa4, 0xad, 0x2a, 0x60, 0xee, 0x33, 0xb8, 0xe4, 0xcb, 0x9a, 0xd5, 0xfc, + 0x7a, 0x1a, 0x1a, 0x65, 0x99, 0xc0, 0xd7, 0xc0, 0x69, 0xc6, 0x03, 0x4a, 0xda, 0x71, 0xbe, 0xe8, + 0xea, 0x0c, 0xfb, 0xbd, 0xca, 0xe9, 0x66, 0x46, 0x83, 0x06, 0x2c, 0xab, 0xbf, 0xb5, 0x40, 0xe5, + 0x09, 0xf9, 0xf8, 0x14, 0xed, 0xf5, 0xf3, 0x60, 0x5a, 0x6e, 0xb7, 0x2d, 0xa3, 0x52, 0x4c, 0xf5, + 0x4b, 0x52, 0x8a, 0xb4, 0x56, 0x54, 0x53, 0x81, 0x2f, 0xde, 0xf8, 0xbc, 0x34, 0x34, 0xd5, 0xb4, + 0xa9, 0xc4, 0x28, 0xd6, 0x57, 0xff, 0x67, 0x0d, 0xa6, 0x4a, 0xea, 0x92, 0x36, 0x5b, 0xd8, 0x25, + 0x70, 0x13, 0x2c, 0x8a, 0x6e, 0x10, 0x91, 0xd0, 0x75, 0x5a, 0x98, 0xc9, 0x6e, 0x59, 0x71, 0xb4, + 0xb5, 0xdb, 0xc5, 0xe6, 0x80, 0x1e, 0x0d, 0xad, 0x80, 0xaf, 0x03, 0xa8, 0x3a, 0xa4, 0x8c, 0x1f, + 0x55, 0xec, 0x4d, 0xaf, 0xd3, 0x1c, 0xb2, 0x40, 0x27, 0xac, 0x82, 0x1b, 0x60, 0xc9, 0xc5, 0xbb, + 0xc4, 0x6d, 0x12, 0x97, 0xb4, 0x78, 0x40, 0xa5, 0x2b, 0x35, 0x4f, 0x2c, 0xf7, 0x7b, 0x95, 0xa5, + 0x9b, 0x83, 0x4a, 0x34, 0x6c, 0x5f, 0xbd, 0x34, 0x78, 0x22, 0xe9, 0x8d, 0xab, 0xbe, 0xf3, 0x0f, + 0x39, 0xb0, 0x32, 0xba, 0xa6, 0xc1, 0x1f, 0x27, 0xed, 0xb1, 0xea, 0x7e, 0xde, 0x9e, 0x54, 0xfd, + 0xd4, 0xfd, 0x31, 0x18, 0xee, 0x8d, 0xe1, 0x0f, 0xc4, 0x53, 0x84, 0x5d, 0xa2, 0x2f, 0xca, 0x5b, + 0x13, 0xa3, 0x20, 0x40, 0x1a, 0xb3, 0xea, 0x95, 0xc3, 0xae, 0x7c, 0xd4, 0xb0, 0x4b, 0xaa, 0x7f, + 0xb6, 0x06, 0x27, 0xa4, 0xe4, 0xcd, 0x81, 0xbf, 0xb0, 0xc0, 0x42, 0x10, 0x12, 0x7f, 0x7d, 0xe7, + 0xc6, 0xdd, 0x17, 0x9b, 0x72, 0x5a, 0xd5, 0xa1, 0xba, 0xf5, 0x29, 0x79, 0x8a, 0xb9, 0x4d, 0x39, + 0xdc, 0xa1, 0x41, 0xc8, 0x1a, 0x67, 0xfa, 0xbd, 0xca, 0xc2, 0x76, 0x16, 0x0a, 0x0d, 0x62, 0x57, + 0x3d, 0xb0, 0x7c, 0xfd, 0x90, 0x13, 0xea, 0x63, 0x77, 0x33, 0x68, 0x45, 0x1e, 0xf1, 0xb9, 0x22, + 0x3a, 0x30, 0x6e, 0x5a, 0x4f, 0x39, 0x6e, 0x5e, 0x04, 0xf9, 0x88, 0xba, 0x3a, 0x8b, 0x4b, 0xda, + 0x3c, 0x7f, 0x07, 0xdd, 0x44, 0x42, 0x5e, 0xbd, 0x04, 0xa6, 0x04, 0x4f, 0x78, 0x1e, 0xe4, 0x29, + 0x7e, 0x20, 0xbd, 0xce, 0x35, 0x66, 0x84, 0x09, 0xc2, 0x0f, 0x90, 0x90, 0x55, 0xff, 0x72, 0x01, + 0x2c, 0x0c, 0xec, 0x05, 0xae, 0x80, 0x9c, 0xd3, 0xd6, 0x1c, 0x80, 0x76, 0x9a, 0xbb, 0xb1, 0x89, + 0x72, 0x4e, 0x1b, 0xbe, 0x02, 0xa6, 0xd5, 0xd4, 0xaf, 0x41, 0x2b, 0xa6, 0x04, 0x48, 0xa9, 0xe8, + 0x3d, 0x12, 0x77, 0x82, 0x88, 0x36, 0x97, 0x1c, 0xc8, 0x9e, 0xbe, 0x25, 0x8a, 0x03, 0xd9, 0x43, + 0x42, 0xf6, 0x49, 0x67, 0xed, 0x78, 0xd8, 0x2f, 0x3c, 0xc5, 0xb0, 0x3f, 0xfd, 0xb1, 0xc3, 0xfe, + 0x65, 0x50, 0xe0, 0x0e, 0x77, 0x89, 0x3d, 0x93, 0x6d, 0x11, 0x6f, 0x0b, 0x21, 0x52, 0x3a, 0x78, + 0x0f, 0xcc, 0xb4, 0xc9, 0x1e, 0x8e, 0x5c, 0x6e, 0x17, 0x65, 0x0a, 0x6d, 0x8c, 0x21, 0x85, 0x1a, + 0x25, 0x51, 0x15, 0x37, 0x95, 0x5f, 0x14, 0x03, 0xc0, 0xe7, 0xc0, 0x8c, 0x87, 0x0f, 0x1d, 0x2f, + 0xf2, 0xec, 0xd9, 0x55, 0x6b, 0xcd, 0x52, 0x66, 0x5b, 0x4a, 0x84, 0x62, 0x9d, 0xa8, 0x8c, 0xe4, + 0xb0, 0xe5, 0x46, 0xcc, 0xe9, 0x12, 0xad, 0xb4, 0x81, 0x2c, 0xb8, 0xa6, 0x32, 0x5e, 0x1f, 0xd0, + 0xa3, 0xa1, 0x15, 0x12, 0xcc, 0xf1, 0xe5, 0xe2, 0x52, 0x0a, 0x4c, 0x89, 0x50, 0xac, 0xcb, 0x82, + 0x69, 0xfb, 0xb9, 0x51, 0x60, 0x7a, 0xf1, 0xd0, 0x0a, 0xf8, 0x65, 0x30, 0xeb, 0xe1, 0xc3, 0x9b, + 0xc4, 0xef, 0xf0, 0x7d, 0x7b, 0x7e, 0xd5, 0x5a, 0xcb, 0x37, 0xe6, 0xfb, 0xbd, 0xca, 0xec, 0x56, + 0x2c, 0x44, 0x89, 0x5e, 0x1a, 0x3b, 0xbe, 0x36, 0x3e, 0x9d, 0x32, 0x8e, 0x85, 0x28, 0xd1, 0x8b, + 0x47, 0x27, 0xc4, 0x5c, 0x5c, 0x2e, 0x7b, 0x21, 0xdb, 0xc2, 0xef, 0x28, 0x31, 0x8a, 0xf5, 0x70, + 0x0d, 0x14, 0x3d, 0x7c, 0x28, 0xc7, 0x2d, 0x7b, 0x51, 0xba, 0x9d, 0x13, 0x7d, 0xd8, 0x96, 0x96, + 0x21, 0xa3, 0x95, 0x96, 0x8e, 0xaf, 0x2c, 0x97, 0x52, 0x96, 0x5a, 0x86, 0x8c, 0x56, 0x24, 0x71, + 0xe4, 0x3b, 0xf7, 0x23, 0xa2, 0x8c, 0xa1, 0x8c, 0x8c, 0x49, 0xe2, 0x3b, 0x89, 0x0a, 0xa5, 0xed, + 0xc4, 0xb8, 0xe3, 0x45, 0x2e, 0x77, 0x42, 0x97, 0x6c, 0xef, 0xd9, 0x67, 0x64, 0xfc, 0x65, 0xe7, + 0xbc, 0x65, 0xa4, 0x28, 0x65, 0x01, 0x09, 0x98, 0x22, 0x7e, 0xe4, 0xd9, 0x67, 0x65, 0xc3, 0x34, + 0x96, 0x14, 0x34, 0x37, 0xe7, 0xba, 0x1f, 0x79, 0x48, 0xba, 0x87, 0xaf, 0x80, 0x79, 0x0f, 0x1f, + 0x8a, 0x72, 0x40, 0x28, 0x17, 0x83, 0xd8, 0xb2, 0xdc, 0xfc, 0x92, 0x68, 0x52, 0xb6, 0xd2, 0x0a, + 0x94, 0xb5, 0x93, 0x0b, 0x1d, 0x3f, 0xb5, 0xf0, 0x5c, 0x6a, 0x61, 0x5a, 0x81, 0xb2, 0x76, 0x22, + 0xd2, 0x94, 0xdc, 0x8f, 0x1c, 0x4a, 0xda, 0xf6, 0xe7, 0x64, 0x5f, 0x23, 0x23, 0x8d, 0xb4, 0x0c, + 0x19, 0x2d, 0xec, 0xc6, 0x73, 0xb9, 0x2d, 0xaf, 0xe1, 0x9d, 0xf1, 0x56, 0xf2, 0x6d, 0xba, 0x4e, + 0x29, 0x3e, 0x52, 0x2f, 0x4d, 0x7a, 0x22, 0x87, 0x0c, 0x14, 0xb0, 0xeb, 0x6e, 0xef, 0xd9, 0xe7, + 0x65, 0xec, 0xc7, 0xfd, 0x82, 0x98, 0xaa, 0xb3, 0x2e, 0x40, 0x90, 0xc2, 0x12, 0xa0, 0x81, 0x2f, + 0x52, 0x63, 0x65, 0xb2, 0xa0, 0xdb, 0x02, 0x04, 0x29, 0x2c, 0xb9, 0x53, 0xff, 0x68, 0x7b, 0xcf, + 0xfe, 0xfc, 0x84, 0x77, 0x2a, 0x40, 0x90, 0xc2, 0x82, 0x0e, 0xc8, 0xfb, 0x01, 0xb7, 0x2f, 0x4c, + 0xe4, 0x79, 0x96, 0x0f, 0xce, 0xad, 0x80, 0x23, 0x81, 0x01, 0x7f, 0x63, 0x01, 0x10, 0x26, 0x29, + 0x7a, 0x71, 0x2c, 0xe3, 0xde, 0x00, 0x64, 0x2d, 0xc9, 0xed, 0xeb, 0x3e, 0xa7, 0x47, 0xc9, 0xe8, + 0x91, 0xba, 0x03, 0x29, 0x16, 0xf0, 0x8f, 0x16, 0x38, 0x9b, 0x9e, 0xa8, 0x0c, 0xbd, 0xb2, 0x8c, + 0xc8, 0xed, 0x71, 0xa7, 0x79, 0x23, 0x08, 0xdc, 0x86, 0xdd, 0xef, 0x55, 0xce, 0xae, 0x9f, 0x80, + 0x8a, 0x4e, 0xe4, 0x02, 0xff, 0x6a, 0x81, 0x25, 0x5d, 0x45, 0x53, 0x0c, 0x2b, 0x32, 0x80, 0x64, + 0xdc, 0x01, 0x1c, 0xc4, 0x51, 0x71, 0x3c, 0xaf, 0xe3, 0xb8, 0x34, 0xa4, 0x47, 0xc3, 0xd4, 0xe0, + 0x3f, 0x2c, 0x30, 0xd7, 0x26, 0x21, 0xf1, 0xdb, 0xc4, 0x6f, 0x09, 0xae, 0xab, 0x63, 0x99, 0x34, + 0x07, 0xb9, 0x6e, 0xa6, 0x20, 0x14, 0xcd, 0x9a, 0xa6, 0x39, 0x97, 0x56, 0x1d, 0xf7, 0x2a, 0xe7, + 0x92, 0xa5, 0x69, 0x0d, 0xca, 0xb0, 0x84, 0x1f, 0x58, 0x60, 0x21, 0x39, 0x00, 0xf5, 0xa4, 0x5c, + 0x9a, 0x60, 0x1e, 0xc8, 0xf6, 0x75, 0x3d, 0x0b, 0x88, 0x06, 0x19, 0xc0, 0xbf, 0x59, 0xa2, 0x53, + 0x8b, 0xe7, 0x46, 0x66, 0x57, 0x65, 0x2c, 0xdf, 0x19, 0x7b, 0x2c, 0x0d, 0x82, 0x0a, 0xe5, 0x0b, + 0x49, 0x2b, 0x68, 0x34, 0xc7, 0xbd, 0xca, 0x72, 0x3a, 0x92, 0x46, 0x81, 0xd2, 0x0c, 0xe1, 0xcf, + 0x2c, 0x30, 0x47, 0x92, 0x8e, 0x9b, 0xd9, 0x97, 0xc7, 0x12, 0xc4, 0x13, 0x9b, 0x78, 0xf5, 0x37, + 0xa6, 0x94, 0x8a, 0xa1, 0x0c, 0xb6, 0xe8, 0x20, 0xc9, 0x21, 0xf6, 0x42, 0x97, 0xd8, 0x5f, 0x18, + 0x73, 0x07, 0x79, 0x5d, 0xf9, 0x45, 0x31, 0xc0, 0x8a, 0x98, 0x7c, 0x06, 0x6e, 0x0e, 0x5c, 0x04, + 0xf9, 0x03, 0x72, 0xa4, 0x1a, 0x7b, 0x24, 0x7e, 0xc2, 0x36, 0x28, 0x74, 0xb1, 0x1b, 0xc5, 0xc3, + 0xdb, 0x98, 0xab, 0x2e, 0x52, 0xce, 0x5f, 0xcb, 0xbd, 0x6a, 0xad, 0x3c, 0xb4, 0xc0, 0xb9, 0x93, + 0x2f, 0xf4, 0x33, 0xa5, 0xf5, 0x7b, 0x0b, 0x2c, 0x0d, 0xdd, 0xdd, 0x13, 0x18, 0xdd, 0xcf, 0x32, + 0x7a, 0x63, 0xdc, 0x97, 0xb0, 0xc9, 0xa9, 0xe3, 0x77, 0x64, 0xe7, 0x91, 0xa6, 0xf7, 0x4b, 0x0b, + 0x2c, 0x0e, 0x5e, 0x87, 0x67, 0x19, 0xaf, 0xea, 0xc3, 0x1c, 0x38, 0x77, 0x72, 0xc3, 0x04, 0xa9, + 0x99, 0x0c, 0x27, 0x33, 0x61, 0x83, 0x64, 0xca, 0x34, 0x43, 0xe5, 0x7b, 0x16, 0x28, 0xdd, 0x33, + 0x76, 0xf1, 0x7f, 0x6a, 0xc6, 0x3e, 0xdb, 0xc7, 0xf5, 0x27, 0x51, 0x30, 0x94, 0xc6, 0xad, 0xfe, + 0xdd, 0x02, 0xcb, 0x27, 0x16, 0x56, 0x31, 0x82, 0x62, 0xd7, 0x0d, 0x1e, 0xa8, 0x3f, 0xd1, 0xa4, + 0xfe, 0x64, 0xb6, 0x2e, 0xa5, 0x48, 0x6b, 0x53, 0xd1, 0xcb, 0x7d, 0x56, 0xd1, 0xab, 0xfe, 0xcb, + 0x02, 0x17, 0x3e, 0x2e, 0x13, 0x9f, 0xc9, 0x91, 0xae, 0x81, 0xa2, 0x6e, 0x8a, 0x8e, 0xe4, 0x71, + 0xea, 0x39, 0x40, 0x17, 0x0d, 0xf9, 0xdf, 0x73, 0xf5, 0xab, 0x71, 0xe5, 0xd1, 0xe3, 0xf2, 0xa9, + 0x0f, 0x1f, 0x97, 0x4f, 0x7d, 0xf4, 0xb8, 0x7c, 0xea, 0x87, 0xfd, 0xb2, 0xf5, 0xa8, 0x5f, 0xb6, + 0x3e, 0xec, 0x97, 0xad, 0x8f, 0xfa, 0x65, 0xeb, 0xdf, 0xfd, 0xb2, 0xf5, 0xab, 0xff, 0x94, 0x4f, + 0x7d, 0x77, 0x46, 0x83, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x11, 0xe8, 0x41, 0x0b, 0x22, + 0x00, 0x00, } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.proto b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.proto index 0d494317838..2a75484ffd9 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.proto +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/generated.proto @@ -29,6 +29,32 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto"; // Package-wide variables from generator "generated". option go_package = "v1beta1"; +// CustomResourceColumnDefinition specifies a column for server side printing. +message CustomResourceColumnDefinition { + // name is a human readable name for the column. + optional string name = 1; + + // type is an OpenAPI type definition for this column. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + optional string type = 2; + + // format is an optional OpenAPI type definition for this column. The 'name' format is applied + // to the primary identifier column to assist in clients identifying column is the resource name. + // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more. + optional string format = 3; + + // description is a human readable description of this column. + optional string description = 4; + + // priority is an integer defining the relative importance of this column compared to others. Lower + // numbers are considered higher priority. Columns that may be omitted in limited space scenarios + // should be given a higher priority. + optional int32 priority = 5; + + // JSONPath is a simple JSON path, i.e. with array notation. + optional string JSONPath = 6; +} + // CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format // <.spec.name>.<.spec.group>. message CustomResourceDefinition { @@ -132,6 +158,9 @@ message CustomResourceDefinitionSpec { // major version, then minor version. An example sorted list of versions: // v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10. repeated CustomResourceDefinitionVersion versions = 7; + + // AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column. + repeated CustomResourceColumnDefinition additionalPrinterColumns = 8; } // CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.conversion.go index db2340e7333..bcb2527c81b 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.conversion.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.conversion.go @@ -36,6 +36,8 @@ func init() { // Public to allow building arbitrary schemes. func RegisterConversions(scheme *runtime.Scheme) error { return scheme.AddGeneratedConversionFuncs( + Convert_v1beta1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition, + Convert_apiextensions_CustomResourceColumnDefinition_To_v1beta1_CustomResourceColumnDefinition, Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition, Convert_apiextensions_CustomResourceDefinition_To_v1beta1_CustomResourceDefinition, Convert_v1beta1_CustomResourceDefinitionCondition_To_apiextensions_CustomResourceDefinitionCondition, @@ -73,6 +75,36 @@ func RegisterConversions(scheme *runtime.Scheme) error { ) } +func autoConvert_v1beta1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition(in *CustomResourceColumnDefinition, out *apiextensions.CustomResourceColumnDefinition, s conversion.Scope) error { + out.Name = in.Name + out.Type = in.Type + out.Format = in.Format + out.Description = in.Description + out.Priority = in.Priority + out.JSONPath = in.JSONPath + return nil +} + +// Convert_v1beta1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition is an autogenerated conversion function. +func Convert_v1beta1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition(in *CustomResourceColumnDefinition, out *apiextensions.CustomResourceColumnDefinition, s conversion.Scope) error { + return autoConvert_v1beta1_CustomResourceColumnDefinition_To_apiextensions_CustomResourceColumnDefinition(in, out, s) +} + +func autoConvert_apiextensions_CustomResourceColumnDefinition_To_v1beta1_CustomResourceColumnDefinition(in *apiextensions.CustomResourceColumnDefinition, out *CustomResourceColumnDefinition, s conversion.Scope) error { + out.Name = in.Name + out.Type = in.Type + out.Format = in.Format + out.Description = in.Description + out.Priority = in.Priority + out.JSONPath = in.JSONPath + return nil +} + +// Convert_apiextensions_CustomResourceColumnDefinition_To_v1beta1_CustomResourceColumnDefinition is an autogenerated conversion function. +func Convert_apiextensions_CustomResourceColumnDefinition_To_v1beta1_CustomResourceColumnDefinition(in *apiextensions.CustomResourceColumnDefinition, out *CustomResourceColumnDefinition, s conversion.Scope) error { + return autoConvert_apiextensions_CustomResourceColumnDefinition_To_v1beta1_CustomResourceColumnDefinition(in, out, s) +} + func autoConvert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(in *CustomResourceDefinition, out *apiextensions.CustomResourceDefinition, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_CustomResourceDefinitionSpec_To_apiextensions_CustomResourceDefinitionSpec(&in.Spec, &out.Spec, s); err != nil { @@ -223,6 +255,7 @@ func autoConvert_v1beta1_CustomResourceDefinitionSpec_To_apiextensions_CustomRes } out.Subresources = (*apiextensions.CustomResourceSubresources)(unsafe.Pointer(in.Subresources)) out.Versions = *(*[]apiextensions.CustomResourceDefinitionVersion)(unsafe.Pointer(&in.Versions)) + out.AdditionalPrinterColumns = *(*[]apiextensions.CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns)) return nil } @@ -249,6 +282,7 @@ func autoConvert_apiextensions_CustomResourceDefinitionSpec_To_v1beta1_CustomRes } out.Subresources = (*CustomResourceSubresources)(unsafe.Pointer(in.Subresources)) out.Versions = *(*[]CustomResourceDefinitionVersion)(unsafe.Pointer(&in.Versions)) + out.AdditionalPrinterColumns = *(*[]CustomResourceColumnDefinition)(unsafe.Pointer(&in.AdditionalPrinterColumns)) return nil } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.deepcopy.go index c990055d03a..d2c1cebf232 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/zz_generated.deepcopy.go @@ -24,6 +24,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResourceColumnDefinition) DeepCopyInto(out *CustomResourceColumnDefinition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceColumnDefinition. +func (in *CustomResourceColumnDefinition) DeepCopy() *CustomResourceColumnDefinition { + if in == nil { + return nil + } + out := new(CustomResourceColumnDefinition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) { *out = *in @@ -155,6 +171,11 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti *out = make([]CustomResourceDefinitionVersion, len(*in)) copy(*out, *in) } + if in.AdditionalPrinterColumns != nil { + in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns + *out = make([]CustomResourceColumnDefinition, len(*in)) + copy(*out, *in) + } return } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/BUILD index b641b609370..c43365fe032 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/BUILD @@ -15,6 +15,7 @@ go_library( "//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/zz_generated.deepcopy.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/zz_generated.deepcopy.go index 9284bbc81c4..145b3c5d715 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/zz_generated.deepcopy.go @@ -24,6 +24,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResourceColumnDefinition) DeepCopyInto(out *CustomResourceColumnDefinition) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceColumnDefinition. +func (in *CustomResourceColumnDefinition) DeepCopy() *CustomResourceColumnDefinition { + if in == nil { + return nil + } + out := new(CustomResourceColumnDefinition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) { *out = *in @@ -155,6 +171,11 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti *out = make([]CustomResourceDefinitionVersion, len(*in)) copy(*out, *in) } + if in.AdditionalPrinterColumns != nil { + in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns + *out = make([]CustomResourceColumnDefinition, len(*in)) + copy(*out, *in) + } return } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD index bbef70ca780..cebbd87bfa4 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/BUILD @@ -71,8 +71,10 @@ go_test( "//vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/BUILD index c9c5e70b772..9f8a352d3d9 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -6,7 +6,7 @@ go_library( importpath = "k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor", visibility = ["//visibility:public"], deps = [ - "//vendor/github.com/go-openapi/spec:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta/table:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -30,3 +30,9 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["tableconvertor_test.go"], + embed = [":go_default_library"], +) diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/BUILD b/staging/src/k8s.io/apiextensions-apiserver/test/integration/BUILD index 6c5adbefbb4..867dffe6089 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/BUILD @@ -13,6 +13,7 @@ go_test( "finalization_test.go", "registration_test.go", "subresources_test.go", + "table_test.go", "validation_test.go", "versioning_test.go", "yaml_test.go", @@ -32,12 +33,15 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", ], ) From d463bbddb1e53627a50b8e00a1f5a5b71703eebe Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Thu, 24 May 2018 13:36:30 -0400 Subject: [PATCH 079/416] move resource builder flags to genericclioptions --- hack/import-restrictions.yaml | 1 + pkg/kubectl/cmd/delete.go | 2 +- pkg/kubectl/cmd/wait/BUILD | 7 +- pkg/kubectl/cmd/wait/wait.go | 6 +- pkg/kubectl/cmd/wait/wait_test.go | 4 +- pkg/kubectl/genericclioptions/BUILD | 3 + .../builder_flags.go} | 65 ++++++++++++++----- .../builder_flags_fake.go} | 2 +- 8 files changed, 62 insertions(+), 28 deletions(-) rename pkg/kubectl/{cmd/wait/flags.go => genericclioptions/builder_flags.go} (66%) rename pkg/kubectl/{cmd/wait/fakeresourcefinder.go => genericclioptions/builder_flags_fake.go} (98%) diff --git a/hack/import-restrictions.yaml b/hack/import-restrictions.yaml index c053eb45251..19e12ac1e94 100644 --- a/hack/import-restrictions.yaml +++ b/hack/import-restrictions.yaml @@ -24,6 +24,7 @@ # TODO this one should be tightened. We depend on it for testing, but we should instead create our own scheme - k8s.io/api/core/v1 - k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers + - k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource - baseImportPath: "./vendor/k8s.io/apimachinery/" allowedImports: diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 2f2bd91508a..e98f0451a98 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -275,7 +275,7 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error { effectiveTimeout = 168 * time.Hour } waitOptions := kubectlwait.WaitOptions{ - ResourceFinder: kubectlwait.ResourceFinderForResult(o.Result), + ResourceFinder: genericclioptions.ResourceFinderForResult(o.Result), DynamicClient: o.DynamicClient, Timeout: effectiveTimeout, diff --git a/pkg/kubectl/cmd/wait/BUILD b/pkg/kubectl/cmd/wait/BUILD index ce1253f61eb..0f13739b86c 100644 --- a/pkg/kubectl/cmd/wait/BUILD +++ b/pkg/kubectl/cmd/wait/BUILD @@ -2,11 +2,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "fakeresourcefinder.go", - "flags.go", - "wait.go", - ], + srcs = ["wait.go"], importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/wait", visibility = ["//visibility:public"], deps = [ @@ -15,7 +11,6 @@ go_library( "//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", - "//vendor/github.com/spf13/pflag:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", diff --git a/pkg/kubectl/cmd/wait/wait.go b/pkg/kubectl/cmd/wait/wait.go index e75d5f418dc..9ccf3cb735e 100644 --- a/pkg/kubectl/cmd/wait/wait.go +++ b/pkg/kubectl/cmd/wait/wait.go @@ -42,7 +42,7 @@ import ( type WaitFlags struct { RESTClientGetter genericclioptions.RESTClientGetter PrintFlags *genericclioptions.PrintFlags - ResourceBuilderFlags *ResourceBuilderFlags + ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags Timeout time.Duration ForCondition string @@ -55,7 +55,7 @@ func NewWaitFlags(restClientGetter genericclioptions.RESTClientGetter, streams g return &WaitFlags{ RESTClientGetter: restClientGetter, PrintFlags: genericclioptions.NewPrintFlags("condition met"), - ResourceBuilderFlags: NewResourceBuilderFlags(), + ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags(), Timeout: 30 * time.Second, @@ -151,7 +151,7 @@ func conditionFuncFor(condition string) (ConditionFunc, error) { // WaitOptions is a set of options that allows you to wait. This is the object reflects the runtime needs of a wait // command, making the logic itself easy to unit test with our existing mocks. type WaitOptions struct { - ResourceFinder ResourceFinder + ResourceFinder genericclioptions.ResourceFinder DynamicClient dynamic.Interface Timeout time.Duration diff --git a/pkg/kubectl/cmd/wait/wait_test.go b/pkg/kubectl/cmd/wait/wait_test.go index 6ef63357bd5..77d98e8d459 100644 --- a/pkg/kubectl/cmd/wait/wait_test.go +++ b/pkg/kubectl/cmd/wait/wait_test.go @@ -219,7 +219,7 @@ func TestWaitForDeletion(t *testing.T) { t.Run(test.name, func(t *testing.T) { fakeClient := test.fakeClient() o := &WaitOptions{ - ResourceFinder: NewSimpleResourceFinder(test.info), + ResourceFinder: genericclioptions.NewSimpleResourceFinder(test.info), DynamicClient: fakeClient, Timeout: test.timeout, @@ -451,7 +451,7 @@ func TestWaitForCondition(t *testing.T) { t.Run(test.name, func(t *testing.T) { fakeClient := test.fakeClient() o := &WaitOptions{ - ResourceFinder: NewSimpleResourceFinder(test.info), + ResourceFinder: genericclioptions.NewSimpleResourceFinder(test.info), DynamicClient: fakeClient, Timeout: test.timeout, diff --git a/pkg/kubectl/genericclioptions/BUILD b/pkg/kubectl/genericclioptions/BUILD index 66867c87ec5..24f95b3373c 100644 --- a/pkg/kubectl/genericclioptions/BUILD +++ b/pkg/kubectl/genericclioptions/BUILD @@ -3,6 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "builder_flags.go", + "builder_flags_fake.go", "config_flags.go", "config_flags_fake.go", "doc.go", @@ -16,6 +18,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/kubectl/genericclioptions/printers:go_default_library", + "//pkg/kubectl/genericclioptions/resource:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library", diff --git a/pkg/kubectl/cmd/wait/flags.go b/pkg/kubectl/genericclioptions/builder_flags.go similarity index 66% rename from pkg/kubectl/cmd/wait/flags.go rename to pkg/kubectl/genericclioptions/builder_flags.go index 824a8af8db6..2fc1dcf5fae 100644 --- a/pkg/kubectl/cmd/wait/flags.go +++ b/pkg/kubectl/genericclioptions/builder_flags.go @@ -14,14 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package wait +package genericclioptions import ( "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" - "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" ) @@ -29,15 +28,15 @@ import ( type ResourceBuilderFlags struct { FilenameOptions resource.FilenameOptions - LabelSelector string - FieldSelector string - AllNamespaces bool Namespace string ExplicitNamespace bool - // TODO add conditional support. These are false for now. - All bool - Local bool + LabelSelector *string + FieldSelector *string + AllNamespaces *bool + + All *bool + Local *bool } // NewResourceBuilderFlags returns a default ResourceBuilderFlags @@ -46,6 +45,13 @@ func NewResourceBuilderFlags() *ResourceBuilderFlags { FilenameOptions: resource.FilenameOptions{ Recursive: true, }, + + LabelSelector: str_ptr(""), + FieldSelector: str_ptr(""), + AllNamespaces: bool_ptr(false), + + All: bool_ptr(false), + Local: bool_ptr(false), } } @@ -59,23 +65,44 @@ func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { flagset.SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) flagset.BoolVar(&o.FilenameOptions.Recursive, "recursive", o.FilenameOptions.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") - flagset.StringVarP(&o.LabelSelector, "selector", "l", o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") - flagset.StringVar(&o.FieldSelector, "field-selector", o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.") - flagset.BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") + if o.LabelSelector != nil { + flagset.StringVarP(o.LabelSelector, "selector", "l", *o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + } + if o.FieldSelector != nil { + flagset.StringVar(o.FieldSelector, "field-selector", *o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.") + } + if o.AllNamespaces != nil { + flagset.BoolVar(o.AllNamespaces, "all-namespaces", *o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") + } } // ToBuilder gives you back a resource finder to visit resources that are located -func (o *ResourceBuilderFlags) ToBuilder(restClientGetter genericclioptions.RESTClientGetter, resources []string) ResourceFinder { +func (o *ResourceBuilderFlags) ToBuilder(restClientGetter RESTClientGetter, resources []string) ResourceFinder { namespace, enforceNamespace, namespaceErr := restClientGetter.ToRawKubeConfigLoader().Namespace() + labelSelector := "" + if o.LabelSelector != nil { + labelSelector = *o.LabelSelector + } + + fieldSelector := "" + if o.FieldSelector != nil { + fieldSelector = *o.FieldSelector + } + + allResources := false + if o.All != nil { + allResources = *o.All + } + return &ResourceFindBuilderWrapper{ builder: resource.NewBuilder(restClientGetter). Unstructured(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). - LabelSelectorParam(o.LabelSelector). - FieldSelectorParam(o.FieldSelector). - ResourceTypeOrNameArgs(o.All, resources...). + LabelSelectorParam(labelSelector). + FieldSelectorParam(fieldSelector). + ResourceTypeOrNameArgs(allResources, resources...). Latest(). Flatten(). AddError(namespaceErr), @@ -112,3 +139,11 @@ func ResourceFinderForResult(result resource.Visitor) ResourceFinder { return result }) } + +func str_ptr(val string) *string { + return &val +} + +func bool_ptr(val bool) *bool { + return &val +} diff --git a/pkg/kubectl/cmd/wait/fakeresourcefinder.go b/pkg/kubectl/genericclioptions/builder_flags_fake.go similarity index 98% rename from pkg/kubectl/cmd/wait/fakeresourcefinder.go rename to pkg/kubectl/genericclioptions/builder_flags_fake.go index 591dea27ef4..15137c9e797 100644 --- a/pkg/kubectl/cmd/wait/fakeresourcefinder.go +++ b/pkg/kubectl/genericclioptions/builder_flags_fake.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package wait +package genericclioptions import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" From b4af3a4ffb2273fdbf2a260b3a87452700009279 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Thu, 24 May 2018 15:05:16 -0400 Subject: [PATCH 080/416] move filename flags to genericclioptions --- pkg/kubectl/BUILD | 2 - pkg/kubectl/bash_comp_utils.go | 36 --------- pkg/kubectl/cmd/apply_set_last_applied.go | 2 +- pkg/kubectl/cmd/delete_flags.go | 39 +--------- pkg/kubectl/cmd/rollingupdate.go | 2 +- pkg/kubectl/cmd/util/helpers.go | 13 +++- pkg/kubectl/genericclioptions/BUILD | 1 + .../genericclioptions/builder_flags.go | 73 ++++++++----------- .../genericclioptions/filename_flags.go | 71 ++++++++++++++++++ 9 files changed, 119 insertions(+), 120 deletions(-) delete mode 100644 pkg/kubectl/bash_comp_utils.go create mode 100644 pkg/kubectl/genericclioptions/filename_flags.go diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 8cc818887be..9701a1ad2f9 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -82,7 +82,6 @@ go_library( srcs = [ "apply.go", "autoscale.go", - "bash_comp_utils.go", "clusterrolebinding.go", "conditions.go", "configmap.go", @@ -125,7 +124,6 @@ go_library( "//pkg/controller/deployment/util:go_default_library", "//pkg/credentialprovider:go_default_library", "//pkg/kubectl/apps:go_default_library", - "//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/util:go_default_library", "//pkg/kubectl/util/hash:go_default_library", "//pkg/kubectl/util/slice:go_default_library", diff --git a/pkg/kubectl/bash_comp_utils.go b/pkg/kubectl/bash_comp_utils.go deleted file mode 100644 index 94df450a4a9..00000000000 --- a/pkg/kubectl/bash_comp_utils.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2015 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. -*/ - -// A set of common functions needed by cmd/kubectl and pkg/kubectl packages. - -package kubectl - -import ( - "strings" - - "github.com/spf13/cobra" - - "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" -) - -func AddJsonFilenameFlag(cmd *cobra.Command, value *[]string, usage string) { - cmd.Flags().StringSliceVarP(value, "filename", "f", *value, usage) - annotations := make([]string, 0, len(resource.FileExtensions)) - for _, ext := range resource.FileExtensions { - annotations = append(annotations, strings.TrimLeft(ext, ".")) - } - cmd.Flags().SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) -} diff --git a/pkg/kubectl/cmd/apply_set_last_applied.go b/pkg/kubectl/cmd/apply_set_last_applied.go index 8b39bfd2b6f..3d92e6b8bce 100644 --- a/pkg/kubectl/cmd/apply_set_last_applied.go +++ b/pkg/kubectl/cmd/apply_set_last_applied.go @@ -107,7 +107,7 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IO cmdutil.AddDryRunFlag(cmd) cmd.Flags().BoolVar(&o.CreateAnnotation, "create-annotation", o.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one") - kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations") + cmdutil.AddJsonFilenameFlag(cmd.Flags(), &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations") return cmd } diff --git a/pkg/kubectl/cmd/delete_flags.go b/pkg/kubectl/cmd/delete_flags.go index 1a5a6db3e2c..fc0580c688d 100644 --- a/pkg/kubectl/cmd/delete_flags.go +++ b/pkg/kubectl/cmd/delete_flags.go @@ -22,44 +22,13 @@ import ( "github.com/spf13/cobra" "k8s.io/client-go/dynamic" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" - "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" ) -type FileNameFlags struct { - Usage string - - Filenames *[]string - Recursive *bool -} - -func (o *FileNameFlags) ToOptions() resource.FilenameOptions { - options := resource.FilenameOptions{} - - if o.Recursive != nil { - options.Recursive = *o.Recursive - } - if o.Filenames != nil { - options.Filenames = *o.Filenames - } - - return options -} - -func (o *FileNameFlags) AddFlags(cmd *cobra.Command) { - if o.Recursive != nil { - cmd.Flags().BoolVarP(o.Recursive, "recursive", "R", *o.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") - } - if o.Filenames != nil { - kubectl.AddJsonFilenameFlag(cmd, o.Filenames, "Filename, directory, or URL to files "+o.Usage) - } -} - // PrintFlags composes common printer flag structs // used for commands requiring deletion logic. type DeleteFlags struct { - FileNameFlags *FileNameFlags + FileNameFlags *genericclioptions.FileNameFlags LabelSelector *string FieldSelector *string @@ -121,7 +90,7 @@ func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams generic } func (f *DeleteFlags) AddFlags(cmd *cobra.Command) { - f.FileNameFlags.AddFlags(cmd) + f.FileNameFlags.AddFlags(cmd.Flags()) if f.LabelSelector != nil { cmd.Flags().StringVarP(f.LabelSelector, "selector", "l", *f.LabelSelector, "Selector (label query) to filter on, not including uninitialized ones.") } @@ -175,7 +144,7 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags { recursive := false return &DeleteFlags{ - FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, + FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, LabelSelector: &labelSelector, FieldSelector: &fieldSelector, @@ -203,7 +172,7 @@ func NewDeleteFlags(usage string) *DeleteFlags { recursive := false return &DeleteFlags{ - FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, + FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive}, Cascade: &cascade, GracePeriod: &gracePeriod, diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index a82ef567ef1..de3e289f4a4 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -150,7 +150,7 @@ func NewCmdRollingUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStream cmd.Flags().DurationVar(&o.Interval, "poll-interval", o.Interval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) usage := "Filename or URL to file to use to create the new replication controller." - kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.Filenames, usage) + cmdutil.AddJsonFilenameFlag(cmd.Flags(), &o.FilenameOptions.Filenames, usage) cmd.Flags().StringVar(&o.Image, "image", o.Image, i18n.T("Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f")) cmd.Flags().StringVar(&o.DeploymentKey, "deployment-label-key", o.DeploymentKey, i18n.T("The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise")) cmd.Flags().StringVar(&o.Container, "container", o.Container, i18n.T("Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod")) diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index e942b682b14..d9b16d19152 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -29,6 +29,7 @@ import ( "github.com/evanphx/json-patch" "github.com/golang/glog" "github.com/spf13/cobra" + "github.com/spf13/pflag" kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -42,7 +43,6 @@ import ( "k8s.io/client-go/scale" "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/printers" @@ -405,10 +405,19 @@ func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) { } func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) { - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage) + AddJsonFilenameFlag(cmd.Flags(), &options.Filenames, "Filename, directory, or URL to files "+usage) cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") } +func AddJsonFilenameFlag(flags *pflag.FlagSet, value *[]string, usage string) { + flags.StringSliceVarP(value, "filename", "f", *value, usage) + annotations := make([]string, 0, len(resource.FileExtensions)) + for _, ext := range resource.FileExtensions { + annotations = append(annotations, strings.TrimLeft(ext, ".")) + } + flags.SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) +} + // AddDryRunFlag adds dry-run flag to a command. Usually used by mutations. func AddDryRunFlag(cmd *cobra.Command) { cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") diff --git a/pkg/kubectl/genericclioptions/BUILD b/pkg/kubectl/genericclioptions/BUILD index 24f95b3373c..88c5662b716 100644 --- a/pkg/kubectl/genericclioptions/BUILD +++ b/pkg/kubectl/genericclioptions/BUILD @@ -8,6 +8,7 @@ go_library( "config_flags.go", "config_flags_fake.go", "doc.go", + "filename_flags.go", "io_options.go", "json_yaml_flags.go", "name_flags.go", diff --git a/pkg/kubectl/genericclioptions/builder_flags.go b/pkg/kubectl/genericclioptions/builder_flags.go index 2fc1dcf5fae..4648751c315 100644 --- a/pkg/kubectl/genericclioptions/builder_flags.go +++ b/pkg/kubectl/genericclioptions/builder_flags.go @@ -17,53 +17,46 @@ limitations under the License. package genericclioptions import ( - "strings" - - "github.com/spf13/cobra" "github.com/spf13/pflag" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" ) // ResourceBuilderFlags are flags for finding resources +// TODO(juanvallejo): wire --local flag from commands through type ResourceBuilderFlags struct { - FilenameOptions resource.FilenameOptions - - Namespace string - ExplicitNamespace bool + FileNameFlags *FileNameFlags LabelSelector *string FieldSelector *string AllNamespaces *bool - All *bool - Local *bool + All bool } // NewResourceBuilderFlags returns a default ResourceBuilderFlags func NewResourceBuilderFlags() *ResourceBuilderFlags { + filenames := []string{} + return &ResourceBuilderFlags{ - FilenameOptions: resource.FilenameOptions{ - Recursive: true, + FileNameFlags: &FileNameFlags{ + Usage: "identifying the resource.", + Filenames: &filenames, + Recursive: boolPtr(true), }, - LabelSelector: str_ptr(""), - FieldSelector: str_ptr(""), - AllNamespaces: bool_ptr(false), - - All: bool_ptr(false), - Local: bool_ptr(false), + LabelSelector: strPtr(""), + AllNamespaces: boolPtr(false), } } +func (o *ResourceBuilderFlags) WithFieldSelector(selector string) *ResourceBuilderFlags { + o.FieldSelector = &selector + return o +} + // AddFlags registers flags for finding resources func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { - flagset.StringSliceVarP(&o.FilenameOptions.Filenames, "filename", "f", o.FilenameOptions.Filenames, "Filename, directory, or URL to files identifying the resource.") - annotations := make([]string, 0, len(resource.FileExtensions)) - for _, ext := range resource.FileExtensions { - annotations = append(annotations, strings.TrimLeft(ext, ".")) - } - flagset.SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) - flagset.BoolVar(&o.FilenameOptions.Recursive, "recursive", o.FilenameOptions.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") + o.FileNameFlags.AddFlags(flagset) if o.LabelSelector != nil { flagset.StringVarP(o.LabelSelector, "selector", "l", *o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") @@ -80,29 +73,23 @@ func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { func (o *ResourceBuilderFlags) ToBuilder(restClientGetter RESTClientGetter, resources []string) ResourceFinder { namespace, enforceNamespace, namespaceErr := restClientGetter.ToRawKubeConfigLoader().Namespace() - labelSelector := "" + builder := resource.NewBuilder(restClientGetter). + Unstructured(). + NamespaceParam(namespace).DefaultNamespace(). + ResourceTypeOrNameArgs(o.All, resources...) + if o.FileNameFlags != nil { + opts := o.FileNameFlags.ToOptions() + builder = builder.FilenameParam(enforceNamespace, &opts) + } if o.LabelSelector != nil { - labelSelector = *o.LabelSelector + builder = builder.LabelSelectorParam(*o.LabelSelector) } - - fieldSelector := "" if o.FieldSelector != nil { - fieldSelector = *o.FieldSelector - } - - allResources := false - if o.All != nil { - allResources = *o.All + builder = builder.FieldSelectorParam(*o.FieldSelector) } return &ResourceFindBuilderWrapper{ - builder: resource.NewBuilder(restClientGetter). - Unstructured(). - NamespaceParam(namespace).DefaultNamespace(). - FilenameParam(enforceNamespace, &o.FilenameOptions). - LabelSelectorParam(labelSelector). - FieldSelectorParam(fieldSelector). - ResourceTypeOrNameArgs(allResources, resources...). + builder: builder. Latest(). Flatten(). AddError(namespaceErr), @@ -140,10 +127,10 @@ func ResourceFinderForResult(result resource.Visitor) ResourceFinder { }) } -func str_ptr(val string) *string { +func strPtr(val string) *string { return &val } -func bool_ptr(val bool) *bool { +func boolPtr(val bool) *bool { return &val } diff --git a/pkg/kubectl/genericclioptions/filename_flags.go b/pkg/kubectl/genericclioptions/filename_flags.go new file mode 100644 index 00000000000..9fc0b60709c --- /dev/null +++ b/pkg/kubectl/genericclioptions/filename_flags.go @@ -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 genericclioptions + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" +) + +// Usage of this struct by itself is discouraged. +// These flags are composed by ResourceBuilderFlags +// which should be used instead. +type FileNameFlags struct { + Usage string + + Filenames *[]string + Recursive *bool +} + +func (o *FileNameFlags) ToOptions() resource.FilenameOptions { + options := resource.FilenameOptions{} + + if o == nil { + return options + } + + if o.Recursive != nil { + options.Recursive = *o.Recursive + } + if o.Filenames != nil { + options.Filenames = *o.Filenames + } + + return options +} + +func (o *FileNameFlags) AddFlags(flags *pflag.FlagSet) { + if o == nil { + return + } + + if o.Recursive != nil { + flags.BoolVarP(o.Recursive, "recursive", "R", *o.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") + } + if o.Filenames != nil { + flags.StringSliceVarP(o.Filenames, "filename", "f", *o.Filenames, o.Usage) + annotations := make([]string, 0, len(resource.FileExtensions)) + for _, ext := range resource.FileExtensions { + annotations = append(annotations, strings.TrimLeft(ext, ".")) + } + flags.SetAnnotation("filename", cobra.BashCompFilenameExt, annotations) + } +} From cc2a5954acf570b7f919a9a7e274661cc830d3a3 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 28 May 2018 11:44:20 +0200 Subject: [PATCH 081/416] e2e/storage: central argument handling Putting the command line argument handling into the central test context seems like the better solution, in particular considering that argument handling might get changed in the future to use Viper. --- test/e2e/framework/test_context.go | 16 ++++++++++++++++ test/e2e/storage/csi_objects.go | 14 +++----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go index c13e8e769f0..a4028e0688b 100644 --- a/test/e2e/framework/test_context.go +++ b/test/e2e/framework/test_context.go @@ -110,6 +110,8 @@ type TestContextType struct { FeatureGates map[string]bool // Node e2e specific test context NodeTestContextType + // Storage e2e specific test context + StorageTestContextType // Monitoring solution that is used in current cluster. ClusterMonitoringMode string // Separate Prometheus monitoring deployed in cluster @@ -157,6 +159,14 @@ type NodeTestContextType struct { SystemSpecName string } +// StorageConfig contains the shared settings for storage 2e2 tests. +type StorageTestContextType struct { + // CSIImageVersion overrides the builtin stable version numbers if set. + CSIImageVersion string + // CSIImageRegistry defines the image registry hosting the CSI container images. + CSIImageRegistry string +} + type CloudConfig struct { ApiEndpoint string ProjectID string @@ -290,6 +300,11 @@ func RegisterNodeFlags() { flag.StringVar(&TestContext.SystemSpecName, "system-spec-name", "", "The name of the system spec (e.g., gke) that's used in the node e2e test. The system specs are in test/e2e_node/system/specs/. This is used by the test framework to determine which tests to run for validating the system requirements.") } +func RegisterStorageFlags() { + flag.StringVar(&TestContext.CSIImageVersion, "csiImageVersion", "", "overrides the default tag used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") + flag.StringVar(&TestContext.CSIImageRegistry, "csiImageRegistry", "quay.io/k8scsi", "overrides the default repository used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") +} + // ViperizeFlags sets up all flag and config processing. Future configuration info should be added to viper, not to flags. func ViperizeFlags() { @@ -298,6 +313,7 @@ func ViperizeFlags() { // since go test 'flag's are sort of incompatible w/ flag, glog, etc. RegisterCommonFlags() RegisterClusterFlags() + RegisterStorageFlags() flag.Parse() // Part 2: Set Viper provided flags. diff --git a/test/e2e/storage/csi_objects.go b/test/e2e/storage/csi_objects.go index 2836f11d1c6..15fd7cf3282 100644 --- a/test/e2e/storage/csi_objects.go +++ b/test/e2e/storage/csi_objects.go @@ -20,7 +20,6 @@ limitations under the License. package storage import ( - "flag" "fmt" "time" @@ -44,19 +43,12 @@ var csiImageVersions = map[string]string{ "csi-provisioner": "v0.2.1", "driver-registrar": "v0.2.0", } -var csiImageVersion string -var csiImageRegistry string - -func init() { - flag.StringVar(&csiImageVersion, "csiImageVersion", "", "overrides the default tag used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") - flag.StringVar(&csiImageRegistry, "csiImageRegistry", "quay.io/k8scsi", "overrides the default repository used for hostpathplugin/csi-attacher/csi-provisioner/driver-registrar images") -} func csiContainerImage(image string) string { var fullName string - fullName += csiImageRegistry + "/" + image + ":" - if csiImageVersion != "" { - fullName += csiImageVersion + fullName += framework.TestContext.CSIImageRegistry + "/" + image + ":" + if framework.TestContext.CSIImageVersion != "" { + fullName += framework.TestContext.CSIImageVersion } else { fullName += csiImageVersions[image] } From 9ea33c604c49c8ab03615d7ad85544614673911f Mon Sep 17 00:00:00 2001 From: Shyam Jeedigunta Date: Mon, 28 May 2018 13:23:37 +0200 Subject: [PATCH 082/416] Fix bug with scheduler throughput variable pass-by-value --- test/e2e/scalability/density.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/scalability/density.go b/test/e2e/scalability/density.go index 1ef125d8744..b461e458882 100644 --- a/test/e2e/scalability/density.go +++ b/test/e2e/scalability/density.go @@ -227,7 +227,7 @@ func logPodStartupStatus( expectedPods int, observedLabels map[string]string, period time.Duration, - scheduleThroughputs []float64, + scheduleThroughputs *[]float64, stopCh chan struct{}) { label := labels.SelectorFromSet(labels.Set(observedLabels)) @@ -250,14 +250,14 @@ func logPodStartupStatus( framework.Logf(startupStatus.String("Density")) // Compute scheduling throughput for the latest time period. throughput := float64(startupStatus.Scheduled-lastScheduledCount) / float64(period/time.Second) - scheduleThroughputs = append(scheduleThroughputs, throughput) + *scheduleThroughputs = append(*scheduleThroughputs, throughput) lastScheduledCount = startupStatus.Scheduled } } // runDensityTest will perform a density test and return the time it took for // all pods to start -func runDensityTest(dtc DensityTestConfig, testPhaseDurations *timer.TestPhaseTimer, scheduleThroughputs []float64) time.Duration { +func runDensityTest(dtc DensityTestConfig, testPhaseDurations *timer.TestPhaseTimer, scheduleThroughputs *[]float64) time.Duration { defer GinkgoRecover() // Create all secrets, configmaps and daemons. @@ -647,7 +647,7 @@ var _ = SIGDescribe("Density", func() { LogFunc: framework.Logf, }) } - e2eStartupTime = runDensityTest(dConfig, testPhaseDurations, scheduleThroughputs) + e2eStartupTime = runDensityTest(dConfig, testPhaseDurations, &scheduleThroughputs) if itArg.runLatencyTest { By("Scheduling additional Pods to measure startup latencies") From c798dfc88df9c015f6c274399413c5cab1315e94 Mon Sep 17 00:00:00 2001 From: Alexander Kanevskiy Date: Thu, 24 May 2018 18:33:40 +0300 Subject: [PATCH 083/416] UX improvement for preflight check for external etcd client certificates By using same preflight check label for all etcd client certificates, it will allow user to use single identifier, and as result shorter command line arguments to kubeadm, in case multiple issues found with those files. Fixes: kubernetes/kubeadm#834 --- cmd/kubeadm/app/preflight/checks.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index 8b3c5cb6a98..1b107e36649 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -892,13 +892,13 @@ func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfi if cfg.Etcd.External != nil { // Only check etcd version when external endpoints are specified if cfg.Etcd.External.CAFile != "" { - checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.CAFile}) + checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.CAFile, Label: "ExternalEtcdClientCertificates"}) } if cfg.Etcd.External.CertFile != "" { - checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.CertFile}) + checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.CertFile, Label: "ExternalEtcdClientCertificates"}) } if cfg.Etcd.External.KeyFile != "" { - checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.KeyFile}) + checks = append(checks, FileExistingCheck{Path: cfg.Etcd.External.KeyFile, Label: "ExternalEtcdClientCertificates"}) } checks = append(checks, ExternalEtcdVersionCheck{Etcd: cfg.Etcd}) } From a4e0659815ad69da425a66f362919112568d93d4 Mon Sep 17 00:00:00 2001 From: "Rostislav M. Georgiev" Date: Mon, 28 May 2018 15:20:03 +0300 Subject: [PATCH 084/416] kubeadm: Use loadPodSpecFromFile instead of LoadPodFromFile Implement and use loadPodSpecFromFile which loads and returns PodSpec object from YAML or JSON file. Use this function in the places where LoadPodFromFile is used to load Pod object and then return the PodSpec portion of it. This also removes the dependency on //pkg/volume/util and its dependencies (thus, making kubeadm more lean). Signed-off-by: Rostislav M. Georgiev --- cmd/kubeadm/app/phases/selfhosting/BUILD | 4 +-- .../app/phases/selfhosting/selfhosting.go | 29 ++++++++++++++++--- .../phases/selfhosting/selfhosting_test.go | 18 ++++++++---- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/cmd/kubeadm/app/phases/selfhosting/BUILD b/cmd/kubeadm/app/phases/selfhosting/BUILD index 8ca10bd1bb3..4503f0c71cc 100644 --- a/cmd/kubeadm/app/phases/selfhosting/BUILD +++ b/cmd/kubeadm/app/phases/selfhosting/BUILD @@ -17,7 +17,6 @@ go_test( deps = [ "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/util:go_default_library", - "//pkg/volume/util:go_default_library", "//vendor/k8s.io/api/apps/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", ], @@ -37,12 +36,13 @@ go_library( "//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/apiclient:go_default_library", - "//pkg/volume/util:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/apps/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", ], ) diff --git a/cmd/kubeadm/app/phases/selfhosting/selfhosting.go b/cmd/kubeadm/app/phases/selfhosting/selfhosting.go index 4993ba0a74c..3d20c959dd8 100644 --- a/cmd/kubeadm/app/phases/selfhosting/selfhosting.go +++ b/cmd/kubeadm/app/phases/selfhosting/selfhosting.go @@ -18,6 +18,7 @@ package selfhosting import ( "fmt" + "io/ioutil" "os" "time" @@ -26,12 +27,13 @@ import ( apps "k8s.io/api/apps/v1" "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" clientset "k8s.io/client-go/kubernetes" + clientscheme "k8s.io/client-go/kubernetes/scheme" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" - volumeutil "k8s.io/kubernetes/pkg/volume/util" ) const ( @@ -85,12 +87,11 @@ func CreateSelfHostedControlPlane(manifestsDir, kubeConfigDir string, cfg *kubea continue } - // Load the Static Pod file in order to be able to create a self-hosted variant of that file - pod, err := volumeutil.LoadPodFromFile(manifestPath) + // Load the Static Pod spec in order to be able to create a self-hosted variant of that file + podSpec, err := loadPodSpecFromFile(manifestPath) if err != nil { return err } - podSpec := &pod.Spec // Build a DaemonSet object from the loaded PodSpec ds := BuildDaemonSet(componentName, podSpec, mutators) @@ -174,3 +175,23 @@ func BuildSelfhostedComponentLabels(component string) map[string]string { func BuildSelfHostedComponentLabelQuery(componentName string) string { return fmt.Sprintf("k8s-app=%s", kubeadmconstants.AddSelfHostedPrefix(componentName)) } + +func loadPodSpecFromFile(filePath string) (*v1.PodSpec, error) { + podDef, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("failed to read file path %s: %+v", filePath, err) + } + + if len(podDef) == 0 { + return nil, fmt.Errorf("file was empty: %s", filePath) + } + + codec := clientscheme.Codecs.UniversalDecoder() + pod := &v1.Pod{} + + if err = runtime.DecodeInto(codec, podDef, pod); err != nil { + return nil, fmt.Errorf("failed decoding pod: %v", err) + } + + return &pod.Spec, nil +} diff --git a/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go b/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go index 5b30899d77b..8d7e757f675 100644 --- a/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go +++ b/cmd/kubeadm/app/phases/selfhosting/selfhosting_test.go @@ -26,7 +26,6 @@ import ( apps "k8s.io/api/apps/v1" "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/util" - volumeutil "k8s.io/kubernetes/pkg/volume/util" ) const ( @@ -494,11 +493,10 @@ func TestBuildDaemonSet(t *testing.T) { } defer os.Remove(tempFile) - pod, err := volumeutil.LoadPodFromFile(tempFile) + podSpec, err := loadPodSpecFromFile(tempFile) if err != nil { - t.Fatalf("couldn't load the specified Pod") + t.Fatalf("couldn't load the specified Pod Spec") } - podSpec := &pod.Spec ds := BuildDaemonSet(rt.component, podSpec, GetDefaultMutators()) dsBytes, err := util.MarshalToYaml(ds, apps.SchemeGroupVersion) @@ -517,6 +515,11 @@ func TestLoadPodSpecFromFile(t *testing.T) { content string expectError bool }{ + { + // No content + content: "", + expectError: true, + }, { // Good YAML content: ` @@ -570,11 +573,16 @@ spec: } defer os.Remove(tempFile) - _, err = volumeutil.LoadPodFromFile(tempFile) + _, err = loadPodSpecFromFile(tempFile) if (err != nil) != rt.expectError { t.Errorf("failed TestLoadPodSpecFromFile:\nexpected error:\n%t\nsaw:\n%v", rt.expectError, err) } } + + _, err := loadPodSpecFromFile("") + if err == nil { + t.Error("unexpected success: loadPodSpecFromFile should return error when no file is given") + } } func createTempFileWithContent(content []byte) (string, error) { From 23bf2246fe0b356fea5650da4c056153bd4a4486 Mon Sep 17 00:00:00 2001 From: wojtekt Date: Mon, 28 May 2018 14:27:31 +0200 Subject: [PATCH 085/416] Fix GKE Regional Clusters upgrade tests --- test/e2e/framework/nodes_util.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/e2e/framework/nodes_util.go b/test/e2e/framework/nodes_util.go index 4212899e74f..a58a6e62859 100644 --- a/test/e2e/framework/nodes_util.go +++ b/test/e2e/framework/nodes_util.go @@ -205,16 +205,7 @@ func NodeUpgrade(f *Framework, v string, img string) error { if err != nil { return err } - - // Wait for it to complete and validate nodes are healthy. - // - // TODO(ihmccreery) We shouldn't have to wait for nodes to be ready in - // GKE; the operation shouldn't return until they all are. - Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout) - if _, err := CheckNodesReady(f.ClientSet, TestContext.CloudConfig.NumNodes, RestartNodeReadyAgainTimeout); err != nil { - return err - } - return nil + return waitForNodesReadyAfterUpgrade(f) } // TODO(mrhohn): Remove this function when kube-proxy is run as a DaemonSet by default. @@ -223,9 +214,20 @@ func NodeUpgradeGCEWithKubeProxyDaemonSet(f *Framework, v string, img string, en if err := nodeUpgradeGCE(v, img, enableKubeProxyDaemonSet); err != nil { return err } + return waitForNodesReadyAfterUpgrade(f) +} + +func waitForNodesReadyAfterUpgrade(f *Framework) error { // Wait for it to complete and validate nodes are healthy. - Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout) - if _, err := CheckNodesReady(f.ClientSet, TestContext.CloudConfig.NumNodes, RestartNodeReadyAgainTimeout); err != nil { + // + // TODO(ihmccreery) We shouldn't have to wait for nodes to be ready in + // GKE; the operation shouldn't return until they all are. + numNodes, err := NumberOfRegisteredNodes(f.ClientSet) + if err != nil { + return fmt.Errorf("couldn't detect number of nodes") + } + Logf("Waiting up to %v for all %d nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout, numNodes) + if _, err := CheckNodesReady(f.ClientSet, numNodes, RestartNodeReadyAgainTimeout); err != nil { return err } return nil From c85e69aeb96801a16ee8b60dc75b8ca31054204f Mon Sep 17 00:00:00 2001 From: David Eads Date: Thu, 24 May 2018 09:33:36 -0400 Subject: [PATCH 086/416] remove unnecessary factory delegation for RESTClientGetter method --- pkg/kubectl/cmd/annotate.go | 2 +- pkg/kubectl/cmd/annotate_test.go | 15 ++-- pkg/kubectl/cmd/apply.go | 2 +- pkg/kubectl/cmd/apply_set_last_applied.go | 2 +- pkg/kubectl/cmd/apply_test.go | 42 ++++------- pkg/kubectl/cmd/apply_view_last_applied.go | 2 +- pkg/kubectl/cmd/attach.go | 2 +- pkg/kubectl/cmd/attach_test.go | 9 +-- pkg/kubectl/cmd/auth/cani.go | 2 +- pkg/kubectl/cmd/auth/cani_test.go | 3 +- pkg/kubectl/cmd/auth/reconcile.go | 2 +- pkg/kubectl/cmd/autoscale.go | 2 +- pkg/kubectl/cmd/clusterinfo_dump.go | 2 +- pkg/kubectl/cmd/convert.go | 2 +- pkg/kubectl/cmd/convert_test.go | 3 +- pkg/kubectl/cmd/cp.go | 2 +- pkg/kubectl/cmd/cp_test.go | 3 +- pkg/kubectl/cmd/create/create.go | 4 +- .../cmd/create/create_clusterrole_test.go | 7 +- .../create/create_clusterrolebinding_test.go | 3 +- .../cmd/create/create_configmap_test.go | 3 +- .../cmd/create/create_deployment_test.go | 6 +- pkg/kubectl/cmd/create/create_job.go | 2 +- pkg/kubectl/cmd/create/create_pdb_test.go | 3 +- pkg/kubectl/cmd/create/create_quota_test.go | 4 +- pkg/kubectl/cmd/create/create_role.go | 2 +- pkg/kubectl/cmd/create/create_role_test.go | 10 +-- .../cmd/create/create_rolebinding_test.go | 3 +- pkg/kubectl/cmd/create/create_secret_test.go | 6 +- pkg/kubectl/cmd/create/create_service_test.go | 9 +-- .../cmd/create/create_serviceaccount_test.go | 3 +- pkg/kubectl/cmd/create/create_test.go | 9 +-- pkg/kubectl/cmd/delete.go | 2 +- pkg/kubectl/cmd/delete_test.go | 45 ++++-------- pkg/kubectl/cmd/describe.go | 2 +- pkg/kubectl/cmd/describe_test.go | 17 ++--- pkg/kubectl/cmd/diff.go | 4 +- pkg/kubectl/cmd/drain.go | 2 +- pkg/kubectl/cmd/edit_test.go | 6 +- pkg/kubectl/cmd/exec.go | 2 +- pkg/kubectl/cmd/exec_test.go | 6 +- pkg/kubectl/cmd/expose.go | 2 +- pkg/kubectl/cmd/expose_test.go | 3 +- pkg/kubectl/cmd/get/get.go | 2 +- pkg/kubectl/cmd/get/get_test.go | 69 +++++++------------ pkg/kubectl/cmd/label.go | 2 +- pkg/kubectl/cmd/label_test.go | 12 ++-- pkg/kubectl/cmd/logs.go | 2 +- pkg/kubectl/cmd/logs_test.go | 4 +- pkg/kubectl/cmd/patch.go | 2 +- pkg/kubectl/cmd/patch_test.go | 12 ++-- pkg/kubectl/cmd/plugin.go | 2 +- pkg/kubectl/cmd/portforward.go | 2 +- pkg/kubectl/cmd/portforward_test.go | 3 +- pkg/kubectl/cmd/replace.go | 2 +- pkg/kubectl/cmd/replace_test.go | 12 ++-- pkg/kubectl/cmd/rollingupdate.go | 2 +- pkg/kubectl/cmd/rollout/rollout_history.go | 2 +- pkg/kubectl/cmd/rollout/rollout_pause.go | 2 +- pkg/kubectl/cmd/rollout/rollout_pause_test.go | 3 +- pkg/kubectl/cmd/rollout/rollout_resume.go | 2 +- pkg/kubectl/cmd/rollout/rollout_status.go | 2 +- pkg/kubectl/cmd/rollout/rollout_undo.go | 2 +- pkg/kubectl/cmd/run.go | 2 +- pkg/kubectl/cmd/run_test.go | 6 +- pkg/kubectl/cmd/scale.go | 2 +- pkg/kubectl/cmd/set/set_env.go | 2 +- pkg/kubectl/cmd/set/set_env_test.go | 14 ++-- pkg/kubectl/cmd/set/set_image.go | 2 +- pkg/kubectl/cmd/set/set_image_test.go | 11 ++- pkg/kubectl/cmd/set/set_resources.go | 2 +- pkg/kubectl/cmd/set/set_resources_test.go | 11 ++- pkg/kubectl/cmd/set/set_selector.go | 2 +- pkg/kubectl/cmd/set/set_selector_test.go | 3 +- pkg/kubectl/cmd/set/set_serviceaccount.go | 2 +- .../cmd/set/set_serviceaccount_test.go | 14 ++-- pkg/kubectl/cmd/set/set_subject.go | 2 +- pkg/kubectl/cmd/set/set_subject_test.go | 4 +- pkg/kubectl/cmd/taint.go | 2 +- pkg/kubectl/cmd/testing/BUILD | 1 - pkg/kubectl/cmd/testing/fake.go | 28 +++----- pkg/kubectl/cmd/top_node_test.go | 21 ++---- pkg/kubectl/cmd/top_pod.go | 2 +- pkg/kubectl/cmd/top_pod_test.go | 9 +-- pkg/kubectl/cmd/util/editor/editoptions.go | 2 +- pkg/kubectl/cmd/util/factory.go | 5 -- pkg/kubectl/cmd/util/factory_client_access.go | 4 -- pkg/kubectl/genericclioptions/BUILD | 1 + .../genericclioptions/config_flags_fake.go | 31 +++++++++ 89 files changed, 228 insertions(+), 360 deletions(-) diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 20e12bd2b3f..7df91f9b74a 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -177,7 +177,7 @@ func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [ return printer.PrintObj(obj, out) } - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/annotate_test.go b/pkg/kubectl/cmd/annotate_test.go index f54fcf0ff11..172eb23309c 100644 --- a/pkg/kubectl/cmd/annotate_test.go +++ b/pkg/kubectl/cmd/annotate_test.go @@ -418,10 +418,9 @@ func TestAnnotateErrors(t *testing.T) { for k, testCase := range testCases { t.Run(k, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() iostreams, _, bufOut, bufErr := genericclioptions.NewTestIOStreams() @@ -453,7 +452,7 @@ func TestAnnotateErrors(t *testing.T) { func TestAnnotateObject(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -485,7 +484,6 @@ func TestAnnotateObject(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() iostreams, _, bufOut, _ := genericclioptions.NewTestIOStreams() @@ -507,7 +505,7 @@ func TestAnnotateObject(t *testing.T) { func TestAnnotateObjectFromFile(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -539,7 +537,6 @@ func TestAnnotateObjectFromFile(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() iostreams, _, bufOut, _ := genericclioptions.NewTestIOStreams() @@ -560,7 +557,7 @@ func TestAnnotateObjectFromFile(t *testing.T) { } func TestAnnotateLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -571,7 +568,6 @@ func TestAnnotateLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() iostreams, _, _, _ := genericclioptions.NewTestIOStreams() @@ -594,7 +590,7 @@ func TestAnnotateLocal(t *testing.T) { func TestAnnotateMultipleObjects(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -627,7 +623,6 @@ func TestAnnotateMultipleObjects(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() iostreams, _, _, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index 18819353753..650451120ed 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -230,7 +230,7 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/apply_set_last_applied.go b/pkg/kubectl/cmd/apply_set_last_applied.go index 3d92e6b8bce..809515d2442 100644 --- a/pkg/kubectl/cmd/apply_set_last_applied.go +++ b/pkg/kubectl/cmd/apply_set_last_applied.go @@ -118,7 +118,7 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) o.shortOutput = o.output == "name" var err error - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/apply_test.go b/pkg/kubectl/cmd/apply_test.go index abc4df764c0..81f7464219d 100644 --- a/pkg/kubectl/cmd/apply_test.go +++ b/pkg/kubectl/cmd/apply_test.go @@ -273,7 +273,7 @@ func TestRunApplyPrintsValidObjectList(t *testing.T) { cmBytes := readConfigMapList(t, filenameCM) pathCM := "/namespaces/test/configmaps" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -292,7 +292,6 @@ func TestRunApplyPrintsValidObjectList(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -393,7 +392,7 @@ func TestRunApplyViewLastApplied(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -419,7 +418,6 @@ func TestRunApplyViewLastApplied(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() cmdutil.BehaviorOnFatal(func(str string, code int) { @@ -453,7 +451,7 @@ func TestApplyObjectWithoutAnnotation(t *testing.T) { nameRC, rcBytes := readReplicationController(t, filenameRC) pathRC := "/namespaces/test/replicationcontrollers/" + nameRC - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -472,7 +470,6 @@ func TestApplyObjectWithoutAnnotation(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -499,7 +496,7 @@ func TestApplyObject(t *testing.T) { for _, fn := range testingOpenAPISchemaFns { t.Run("test apply when a local object is specified", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -520,7 +517,6 @@ func TestApplyObject(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -564,7 +560,7 @@ func TestApplyObjectOutput(t *testing.T) { for _, fn := range testingOpenAPISchemaFns { t.Run("test apply returns correct output", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -585,7 +581,6 @@ func TestApplyObjectOutput(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -617,7 +612,7 @@ func TestApplyRetry(t *testing.T) { firstPatch := true retry := false getCount := 0 - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -647,7 +642,6 @@ func TestApplyRetry(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -677,7 +671,7 @@ func TestApplyNonExistObject(t *testing.T) { pathRC := "/namespaces/test/replicationcontrollers" pathNameRC := pathRC + "/" + nameRC - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -697,7 +691,6 @@ func TestApplyNonExistObject(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -723,7 +716,7 @@ func TestApplyEmptyPatch(t *testing.T) { var body []byte - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -750,7 +743,6 @@ func TestApplyEmptyPatch(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() // 1. apply non exist object @@ -797,7 +789,7 @@ func testApplyMultipleObjects(t *testing.T, asList bool) { for _, fn := range testingOpenAPISchemaFns { t.Run("test apply on multiple objects", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -825,7 +817,6 @@ func testApplyMultipleObjects(t *testing.T, asList bool) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -884,7 +875,7 @@ func TestApplyNULLPreservation(t *testing.T) { for _, fn := range testingOpenAPISchemaFns { t.Run("test apply preserves NULL fields", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -926,7 +917,6 @@ func TestApplyNULLPreservation(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -960,7 +950,7 @@ func TestUnstructuredApply(t *testing.T) { for _, fn := range testingOpenAPISchemaFns { t.Run("test apply works correctly with unstructured objects", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -993,7 +983,6 @@ func TestUnstructuredApply(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -1029,7 +1018,7 @@ func TestUnstructuredIdempotentApply(t *testing.T) { for _, fn := range testingOpenAPISchemaFns { t.Run("test repeated apply operations on an unstructured object", func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -1059,7 +1048,6 @@ func TestUnstructuredIdempotentApply(t *testing.T) { }), } tf.OpenAPISchemaFunc = fn - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() @@ -1131,7 +1119,7 @@ func TestRunApplySetLastApplied(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1161,7 +1149,6 @@ func TestRunApplySetLastApplied(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() cmdutil.BehaviorOnFatal(func(str string, code int) { @@ -1227,7 +1214,7 @@ func TestForceApply(t *testing.T) { deleted := false isScaledDownToZero := false counts := map[string]int{} - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.ClientConfigVal = defaultClientConfig() @@ -1312,7 +1299,6 @@ func TestForceApply(t *testing.T) { tf.OpenAPISchemaFunc = fn tf.Client = tf.UnstructuredClient tf.ClientConfigVal = &restclient.Config{} - tf.Namespace = "test" ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() cmd := NewCmdApply("kubectl", tf, ioStreams) diff --git a/pkg/kubectl/cmd/apply_view_last_applied.go b/pkg/kubectl/cmd/apply_view_last_applied.go index ce5c7722e19..5fa5cd12c2a 100644 --- a/pkg/kubectl/cmd/apply_view_last_applied.go +++ b/pkg/kubectl/cmd/apply_view_last_applied.go @@ -91,7 +91,7 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I } func (o *ViewLastAppliedOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/attach.go b/pkg/kubectl/cmd/attach.go index 2195eee1ced..88ff4f7d415 100644 --- a/pkg/kubectl/cmd/attach.go +++ b/pkg/kubectl/cmd/attach.go @@ -135,7 +135,7 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [ return cmdutil.UsageErrorf(cmd, "expected POD, TYPE/NAME, or TYPE NAME, (at most 2 arguments) saw %d: %v", len(argsIn), argsIn) } - namespace, _, err := f.DefaultNamespace() + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/attach_test.go b/pkg/kubectl/cmd/attach_test.go index 55e0aad17ed..007df0dccdf 100644 --- a/pkg/kubectl/cmd/attach_test.go +++ b/pkg/kubectl/cmd/attach_test.go @@ -140,7 +140,7 @@ func TestPodAndContainerAttach(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -156,7 +156,6 @@ func TestPodAndContainerAttach(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() cmd := &cobra.Command{} @@ -224,7 +223,7 @@ func TestAttach(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -247,7 +246,6 @@ func TestAttach(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}} remoteAttach := &fakeRemoteAttach{} if test.remoteAttachErr { @@ -312,7 +310,7 @@ func TestAttachWarnings(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -335,7 +333,6 @@ func TestAttachWarnings(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs, GroupVersion: &schema.GroupVersion{Version: test.version}}} streams, _, _, bufErr := genericclioptions.NewTestIOStreams() ex := &fakeRemoteAttach{} diff --git a/pkg/kubectl/cmd/auth/cani.go b/pkg/kubectl/cmd/auth/cani.go index 3d771af8703..f7e5dac5df5 100644 --- a/pkg/kubectl/cmd/auth/cani.go +++ b/pkg/kubectl/cmd/auth/cani.go @@ -146,7 +146,7 @@ func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error { o.Namespace = "" if !o.AllNamespaces { - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/auth/cani_test.go b/pkg/kubectl/cmd/auth/cani_test.go index 84dc9826868..8ce36cdd680 100644 --- a/pkg/kubectl/cmd/auth/cani_test.go +++ b/pkg/kubectl/cmd/auth/cani_test.go @@ -121,7 +121,7 @@ func TestRunAccessCheck(t *testing.T) { test.o.Out = ioutil.Discard test.o.ErrOut = ioutil.Discard - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -157,7 +157,6 @@ func TestRunAccessCheck(t *testing.T) { test.serverErr }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}} if err := test.o.Complete(tf, test.args); err != nil { diff --git a/pkg/kubectl/cmd/auth/reconcile.go b/pkg/kubectl/cmd/auth/reconcile.go index 555244ca61f..6237c1ba7dd 100644 --- a/pkg/kubectl/cmd/auth/reconcile.go +++ b/pkg/kubectl/cmd/auth/reconcile.go @@ -97,7 +97,7 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args return errors.New("no arguments are allowed") } - namespace, enforceNamespace, err := f.DefaultNamespace() + namespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index 7c1875cf4d2..e2fc589982f 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -165,7 +165,7 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args } } - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/clusterinfo_dump.go b/pkg/kubectl/cmd/clusterinfo_dump.go index 0c3af8d4974..9e55ca45d77 100644 --- a/pkg/kubectl/cmd/clusterinfo_dump.go +++ b/pkg/kubectl/cmd/clusterinfo_dump.go @@ -134,7 +134,7 @@ func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) if err != nil { return err } - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index ec29cdca15a..c22c2990dad 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -139,7 +139,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err er o.builder.Schema(schema) } - cmdNamespace, _, err := f.DefaultNamespace() + cmdNamespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/convert_test.go b/pkg/kubectl/cmd/convert_test.go index 72ff0755ee5..8f3e8cf85e4 100644 --- a/pkg/kubectl/cmd/convert_test.go +++ b/pkg/kubectl/cmd/convert_test.go @@ -95,7 +95,7 @@ func TestConvertObject(t *testing.T) { for _, tc := range testcases { for _, field := range tc.fields { t.Run(fmt.Sprintf("%s %s", tc.name, field), func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -104,7 +104,6 @@ func TestConvertObject(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" buf := bytes.NewBuffer([]byte{}) cmd := NewCmdConvert(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf}) diff --git a/pkg/kubectl/cmd/cp.go b/pkg/kubectl/cmd/cp.go index ff486853baa..b7242f5abd5 100644 --- a/pkg/kubectl/cmd/cp.go +++ b/pkg/kubectl/cmd/cp.go @@ -144,7 +144,7 @@ func extractFileSpec(arg string) (fileSpec, error) { func (o *CopyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { var err error - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/cp_test.go b/pkg/kubectl/cmd/cp_test.go index 925383a7260..656c53ea97a 100644 --- a/pkg/kubectl/cmd/cp_test.go +++ b/pkg/kubectl/cmd/cp_test.go @@ -514,8 +514,7 @@ func TestClean(t *testing.T) { } func TestCopyToPod(t *testing.T) { - tf := cmdtesting.NewTestFactory() - tf.Namespace = "test" + tf := cmdtesting.NewTestFactory().WithNamespace("test") ns := legacyscheme.Codecs codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) diff --git a/pkg/kubectl/cmd/create/create.go b/pkg/kubectl/cmd/create/create.go index 1be7fc47562..556e6d15afa 100644 --- a/pkg/kubectl/cmd/create/create.go +++ b/pkg/kubectl/cmd/create/create.go @@ -216,7 +216,7 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error { return err } - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } @@ -386,7 +386,7 @@ func (o *CreateSubcommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command return printer.PrintObj(obj, out) } - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/create/create_clusterrole_test.go b/pkg/kubectl/cmd/create/create_clusterrole_test.go index 4dc876f9a9e..647c0865751 100644 --- a/pkg/kubectl/cmd/create/create_clusterrole_test.go +++ b/pkg/kubectl/cmd/create/create_clusterrole_test.go @@ -36,10 +36,9 @@ import ( func TestCreateClusterRole(t *testing.T) { clusterRoleName := "my-cluster-role" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{} tf.ClientConfigVal = defaultClientConfig() @@ -178,11 +177,9 @@ func TestCreateClusterRole(t *testing.T) { } func TestClusterRoleValidate(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" - tests := map[string]struct { clusterRoleOptions *CreateClusterRoleOptions expectErr bool diff --git a/pkg/kubectl/cmd/create/create_clusterrolebinding_test.go b/pkg/kubectl/cmd/create/create_clusterrolebinding_test.go index 206d6da73b2..69f1559aac9 100644 --- a/pkg/kubectl/cmd/create/create_clusterrolebinding_test.go +++ b/pkg/kubectl/cmd/create/create_clusterrolebinding_test.go @@ -68,7 +68,7 @@ func TestCreateClusterRoleBinding(t *testing.T) { }, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -77,7 +77,6 @@ func TestCreateClusterRoleBinding(t *testing.T) { encoder := ns.EncoderForVersion(info.Serializer, groupVersion) decoder := ns.DecoderToVersion(info.Serializer, groupVersion) - tf.Namespace = "test" tf.Client = &ClusterRoleBindingRESTClient{ RESTClient: &fake.RESTClient{ NegotiatedSerializer: ns, diff --git a/pkg/kubectl/cmd/create/create_configmap_test.go b/pkg/kubectl/cmd/create/create_configmap_test.go index 65aac513988..8ee9501bcbf 100644 --- a/pkg/kubectl/cmd/create/create_configmap_test.go +++ b/pkg/kubectl/cmd/create/create_configmap_test.go @@ -36,7 +36,7 @@ import ( func TestCreateConfigMap(t *testing.T) { configMap := &v1.ConfigMap{} configMap.Name = "my-configmap" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -55,7 +55,6 @@ func TestCreateConfigMap(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateConfigMap(tf, ioStreams) cmd.Flags().Set("output", "name") diff --git a/pkg/kubectl/cmd/create/create_deployment_test.go b/pkg/kubectl/cmd/create/create_deployment_test.go index af1e48f081e..bd42fcfedd0 100644 --- a/pkg/kubectl/cmd/create/create_deployment_test.go +++ b/pkg/kubectl/cmd/create/create_deployment_test.go @@ -89,7 +89,7 @@ func Test_generatorFromName(t *testing.T) { func TestCreateDeployment(t *testing.T) { depName := "jonny-dep" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -104,7 +104,6 @@ func TestCreateDeployment(t *testing.T) { }), } tf.ClientConfigVal = &restclient.Config{} - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateDeployment(tf, ioStreams) @@ -120,7 +119,7 @@ func TestCreateDeployment(t *testing.T) { func TestCreateDeploymentNoImage(t *testing.T) { depName := "jonny-dep" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -135,7 +134,6 @@ func TestCreateDeploymentNoImage(t *testing.T) { }), } tf.ClientConfigVal = &restclient.Config{} - tf.Namespace = "test" ioStreams := genericclioptions.NewTestIOStreamsDiscard() cmd := NewCmdCreateDeployment(tf, ioStreams) diff --git a/pkg/kubectl/cmd/create/create_job.go b/pkg/kubectl/cmd/create/create_job.go index 9980fa2866d..d4f024e470b 100644 --- a/pkg/kubectl/cmd/create/create_job.go +++ b/pkg/kubectl/cmd/create/create_job.go @@ -100,7 +100,7 @@ func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args } o.Name = args[0] - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/create/create_pdb_test.go b/pkg/kubectl/cmd/create/create_pdb_test.go index 06a7fd80c80..01d6ee99e8d 100644 --- a/pkg/kubectl/cmd/create/create_pdb_test.go +++ b/pkg/kubectl/cmd/create/create_pdb_test.go @@ -32,7 +32,7 @@ import ( func TestCreatePdb(t *testing.T) { pdbName := "my-pdb" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -48,7 +48,6 @@ func TestCreatePdb(t *testing.T) { }), } tf.ClientConfigVal = &restclient.Config{} - tf.Namespace = "test" outputFormat := "name" diff --git a/pkg/kubectl/cmd/create/create_quota_test.go b/pkg/kubectl/cmd/create/create_quota_test.go index c9bf1333eb5..c780beafefd 100644 --- a/pkg/kubectl/cmd/create/create_quota_test.go +++ b/pkg/kubectl/cmd/create/create_quota_test.go @@ -51,11 +51,9 @@ func TestCreateQuota(t *testing.T) { } for name, test := range tests { t.Run(name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" - ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateQuota(tf, ioStreams) cmd.Flags().Parse(test.flags) diff --git a/pkg/kubectl/cmd/create/create_role.go b/pkg/kubectl/cmd/create/create_role.go index 53243761dc0..d01014b45f1 100644 --- a/pkg/kubectl/cmd/create/create_role.go +++ b/pkg/kubectl/cmd/create/create_role.go @@ -226,7 +226,7 @@ func (o *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args return printer.PrintObj(obj, o.Out) } - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/create/create_role_test.go b/pkg/kubectl/cmd/create/create_role_test.go index c2284c3ec8a..4551bea8541 100644 --- a/pkg/kubectl/cmd/create/create_role_test.go +++ b/pkg/kubectl/cmd/create/create_role_test.go @@ -34,10 +34,9 @@ import ( func TestCreateRole(t *testing.T) { roleName := "my-role" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{} tf.ClientConfigVal = defaultClientConfig() @@ -152,11 +151,9 @@ func TestCreateRole(t *testing.T) { } func TestValidate(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" - tests := map[string]struct { roleOptions *CreateRoleOptions expectErr bool @@ -357,10 +354,9 @@ func TestValidate(t *testing.T) { func TestComplete(t *testing.T) { roleName := "my-role" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{} tf.ClientConfigVal = defaultClientConfig() diff --git a/pkg/kubectl/cmd/create/create_rolebinding_test.go b/pkg/kubectl/cmd/create/create_rolebinding_test.go index f57b2351204..f9cb82518f7 100644 --- a/pkg/kubectl/cmd/create/create_rolebinding_test.go +++ b/pkg/kubectl/cmd/create/create_rolebinding_test.go @@ -70,7 +70,7 @@ func TestCreateRoleBinding(t *testing.T) { }, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -79,7 +79,6 @@ func TestCreateRoleBinding(t *testing.T) { encoder := ns.EncoderForVersion(info.Serializer, groupVersion) decoder := ns.DecoderToVersion(info.Serializer, groupVersion) - tf.Namespace = "test" tf.Client = &RoleBindingRESTClient{ RESTClient: &fake.RESTClient{ NegotiatedSerializer: ns, diff --git a/pkg/kubectl/cmd/create/create_secret_test.go b/pkg/kubectl/cmd/create/create_secret_test.go index 21b41a8520c..6bdf4475966 100644 --- a/pkg/kubectl/cmd/create/create_secret_test.go +++ b/pkg/kubectl/cmd/create/create_secret_test.go @@ -37,7 +37,7 @@ func TestCreateSecretGeneric(t *testing.T) { }, } secretObject.Name = "my-secret" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -56,7 +56,6 @@ func TestCreateSecretGeneric(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateSecretGeneric(tf, ioStreams) cmd.Flags().Set("output", "name") @@ -72,7 +71,7 @@ func TestCreateSecretGeneric(t *testing.T) { func TestCreateSecretDockerRegistry(t *testing.T) { secretObject := &v1.Secret{} secretObject.Name = "my-secret" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) ns := legacyscheme.Codecs @@ -89,7 +88,6 @@ func TestCreateSecretDockerRegistry(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateSecretDockerRegistry(tf, ioStreams) cmd.Flags().Set("docker-username", "test-user") diff --git a/pkg/kubectl/cmd/create/create_service_test.go b/pkg/kubectl/cmd/create/create_service_test.go index 9fd018f1852..eb622922214 100644 --- a/pkg/kubectl/cmd/create/create_service_test.go +++ b/pkg/kubectl/cmd/create/create_service_test.go @@ -32,7 +32,7 @@ import ( func TestCreateService(t *testing.T) { service := &v1.Service{} service.Name = "my-service" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -51,7 +51,6 @@ func TestCreateService(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateServiceClusterIP(tf, ioStreams) cmd.Flags().Set("output", "name") @@ -66,7 +65,7 @@ func TestCreateService(t *testing.T) { func TestCreateServiceNodePort(t *testing.T) { service := &v1.Service{} service.Name = "my-node-port-service" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -85,7 +84,6 @@ func TestCreateServiceNodePort(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateServiceNodePort(tf, ioStreams) cmd.Flags().Set("output", "name") @@ -100,7 +98,7 @@ func TestCreateServiceNodePort(t *testing.T) { func TestCreateServiceExternalName(t *testing.T) { service := &v1.Service{} service.Name = "my-external-name-service" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -119,7 +117,6 @@ func TestCreateServiceExternalName(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateServiceExternalName(tf, ioStreams) cmd.Flags().Set("output", "name") diff --git a/pkg/kubectl/cmd/create/create_serviceaccount_test.go b/pkg/kubectl/cmd/create/create_serviceaccount_test.go index 2f62c05fd3c..06f9e35642a 100644 --- a/pkg/kubectl/cmd/create/create_serviceaccount_test.go +++ b/pkg/kubectl/cmd/create/create_serviceaccount_test.go @@ -32,7 +32,7 @@ import ( func TestCreateServiceAccount(t *testing.T) { serviceAccountObject := &v1.ServiceAccount{} serviceAccountObject.Name = "my-service-account" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -51,7 +51,6 @@ func TestCreateServiceAccount(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreateServiceAccount(tf, ioStreams) cmd.Flags().Set("output", "name") diff --git a/pkg/kubectl/cmd/create/create_test.go b/pkg/kubectl/cmd/create/create_test.go index 607a3bfbd3f..dba513d8b93 100644 --- a/pkg/kubectl/cmd/create/create_test.go +++ b/pkg/kubectl/cmd/create/create_test.go @@ -51,7 +51,7 @@ func TestCreateObject(t *testing.T) { _, _, rc := testData() rc.Items[0].Name = "redis-master-controller" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -69,7 +69,6 @@ func TestCreateObject(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreate(tf, ioStreams) @@ -87,7 +86,7 @@ func TestCreateMultipleObject(t *testing.T) { initTestErrorHandler(t) _, svc, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -107,7 +106,6 @@ func TestCreateMultipleObject(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreate(tf, ioStreams) @@ -127,7 +125,7 @@ func TestCreateDirectory(t *testing.T) { _, _, rc := testData() rc.Items[0].Name = "name" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -145,7 +143,6 @@ func TestCreateDirectory(t *testing.T) { } }), } - tf.Namespace = "test" ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdCreate(tf, ioStreams) diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index e98f0451a98..6446e2a354f 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -146,7 +146,7 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra } func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index 51fb4a887c7..71e4405f3a2 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -52,7 +52,7 @@ func TestDeleteObjectByTuple(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -77,7 +77,6 @@ func TestDeleteObjectByTuple(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -118,7 +117,7 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -137,7 +136,6 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) { } }), } - tf.Namespace = "test" // DeleteOptions.PropagationPolicy should be Foreground, when cascade is true (default). foregroundPolicy := metav1.DeletePropagationForeground @@ -170,7 +168,7 @@ func TestDeleteNamedObject(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -195,7 +193,6 @@ func TestDeleteNamedObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -223,7 +220,7 @@ func TestDeleteObject(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -240,7 +237,6 @@ func TestDeleteObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -260,7 +256,7 @@ func TestDeleteObjectGraceZero(t *testing.T) { pods, _, _ := testData() count := 0 - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -288,7 +284,6 @@ func TestDeleteObjectGraceZero(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, errBuf := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -307,7 +302,7 @@ func TestDeleteObjectGraceZero(t *testing.T) { func TestDeleteObjectNotFound(t *testing.T) { initTestErrorHandler(t) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -322,7 +317,6 @@ func TestDeleteObjectNotFound(t *testing.T) { } }), } - tf.Namespace = "test" options := &DeleteOptions{ FilenameOptions: resource.FilenameOptions{ @@ -345,7 +339,7 @@ func TestDeleteObjectNotFound(t *testing.T) { func TestDeleteObjectIgnoreNotFound(t *testing.T) { initTestErrorHandler(t) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -360,7 +354,6 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -382,7 +375,7 @@ func TestDeleteAllNotFound(t *testing.T) { svc.Items = append(svc.Items, api.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) notFoundError := &errors.NewNotFound(api.Resource("services"), "foo").ErrStatus - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -403,7 +396,6 @@ func TestDeleteAllNotFound(t *testing.T) { } }), } - tf.Namespace = "test" // Make sure we can explicitly choose to fail on NotFound errors, even with --all options := &DeleteOptions{ @@ -429,7 +421,7 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) { initTestErrorHandler(t) _, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -454,7 +446,6 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -472,7 +463,7 @@ func TestDeleteMultipleObject(t *testing.T) { initTestErrorHandler(t) _, svc, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -491,7 +482,6 @@ func TestDeleteMultipleObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -510,7 +500,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { initTestErrorHandler(t) _, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -529,7 +519,6 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() options := &DeleteOptions{ @@ -558,7 +547,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) { initTestErrorHandler(t) _, svc, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -582,7 +571,6 @@ func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -599,7 +587,7 @@ func TestDeleteDirectory(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -616,7 +604,6 @@ func TestDeleteDirectory(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -634,7 +621,7 @@ func TestDeleteMultipleSelector(t *testing.T) { initTestErrorHandler(t) pods, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -663,7 +650,6 @@ func TestDeleteMultipleSelector(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdDelete(tf, streams) @@ -703,10 +689,9 @@ func TestResourceErrors(t *testing.T) { for k, testCase := range testCases { t.Run(k, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 848f82100f8..3a0feafdd8f 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -122,7 +122,7 @@ func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions. func (o *DescribeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { var err error - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/describe_test.go b/pkg/kubectl/cmd/describe_test.go index ecc0fc8c4ed..daac115ed6f 100644 --- a/pkg/kubectl/cmd/describe_test.go +++ b/pkg/kubectl/cmd/describe_test.go @@ -41,7 +41,7 @@ func TestDescribeUnknownSchemaObject(t *testing.T) { }() cmdutil.DescriberFn = d.describerFor - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("non-default") defer tf.Cleanup() _, _, codec := cmdtesting.NewExternalScheme() @@ -52,7 +52,6 @@ func TestDescribeUnknownSchemaObject(t *testing.T) { streams, _, buf, _ := genericclioptions.NewTestIOStreams() - tf.Namespace = "non-default" cmd := NewCmdDescribe("kubectl", tf, streams) cmd.Run(cmd, []string{"type", "foo"}) @@ -82,7 +81,7 @@ func TestDescribeUnknownNamespacedSchemaObject(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalNamespacedType("", "", "foo", "non-default"))}, } - tf.Namespace = "non-default" + tf.WithNamespace("non-default") streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -107,7 +106,7 @@ func TestDescribeObject(t *testing.T) { cmdutil.DescriberFn = d.describerFor _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -123,7 +122,6 @@ func TestDescribeObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -149,7 +147,7 @@ func TestDescribeListObjects(t *testing.T) { cmdutil.DescriberFn = d.describerFor pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -160,7 +158,6 @@ func TestDescribeListObjects(t *testing.T) { streams, _, buf, _ := genericclioptions.NewTestIOStreams() - tf.Namespace = "test" cmd := NewCmdDescribe("kubectl", tf, streams) cmd.Run(cmd, []string{"pods"}) if buf.String() != fmt.Sprintf("%s\n\n%s", d.Output, d.Output) { @@ -177,7 +174,7 @@ func TestDescribeObjectShowEvents(t *testing.T) { cmdutil.DescriberFn = d.describerFor pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -186,7 +183,6 @@ func TestDescribeObjectShowEvents(t *testing.T) { Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } - tf.Namespace = "test" cmd := NewCmdDescribe("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard()) cmd.Flags().Set("show-events", "true") cmd.Run(cmd, []string{"pods"}) @@ -204,7 +200,7 @@ func TestDescribeObjectSkipEvents(t *testing.T) { cmdutil.DescriberFn = d.describerFor pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -213,7 +209,6 @@ func TestDescribeObjectSkipEvents(t *testing.T) { Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } - tf.Namespace = "test" cmd := NewCmdDescribe("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard()) cmd.Flags().Set("show-events", "false") cmd.Run(cmd, []string{"pods"}) diff --git a/pkg/kubectl/cmd/diff.go b/pkg/kubectl/cmd/diff.go index d513c7ab762..d4cda1261b4 100644 --- a/pkg/kubectl/cmd/diff.go +++ b/pkg/kubectl/cmd/diff.go @@ -407,7 +407,7 @@ func NewDownloader(f cmdutil.Factory) (*Downloader, error) { if err != nil { return nil, err } - d.ns, _, _ = f.DefaultNamespace() + d.ns, _, _ = f.ToRawKubeConfigLoader().Namespace() return &d, nil } @@ -451,7 +451,7 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions, from, t printer := Printer{} - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index 8a6383f9a2e..8c0baf5b5e9 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -263,7 +263,7 @@ func (o *DrainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st o.nodeInfos = []*resource.Info{} - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/edit_test.go b/pkg/kubectl/cmd/edit_test.go index fe5fd28ae44..6820d64e7ad 100644 --- a/pkg/kubectl/cmd/edit_test.go +++ b/pkg/kubectl/cmd/edit_test.go @@ -224,12 +224,8 @@ func TestEdit(t *testing.T) { Client: fake.CreateHTTPClient(reqResp), }, nil } - - if len(testcase.Namespace) > 0 { - tf.Namespace = testcase.Namespace - } + tf.WithNamespace(testcase.Namespace) tf.ClientConfigVal = defaultClientConfig() - tf.CommandVal = "edit test cmd invocation" ioStreams, _, buf, errBuf := genericclioptions.NewTestIOStreams() var cmd *cobra.Command diff --git a/pkg/kubectl/cmd/exec.go b/pkg/kubectl/cmd/exec.go index 1866da37b42..5290b0fd22c 100644 --- a/pkg/kubectl/cmd/exec.go +++ b/pkg/kubectl/cmd/exec.go @@ -166,7 +166,7 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s } } - namespace, _, err := f.DefaultNamespace() + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/exec_test.go b/pkg/kubectl/cmd/exec_test.go index 90c0e4be2ee..187c6ff8f8d 100644 --- a/pkg/kubectl/cmd/exec_test.go +++ b/pkg/kubectl/cmd/exec_test.go @@ -132,7 +132,7 @@ func TestPodAndContainer(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() ns := legacyscheme.Codecs @@ -141,7 +141,6 @@ func TestPodAndContainer(t *testing.T) { NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() cmd := &cobra.Command{} @@ -193,7 +192,7 @@ func TestExec(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -212,7 +211,6 @@ func TestExec(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ex := &fakeRemoteExecutor{} if test.execErr { diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 944af7f770e..f0e309eb74f 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -201,7 +201,7 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e return err } - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/expose_test.go b/pkg/kubectl/cmd/expose_test.go index 548a44ac5cb..7c4fa01e93c 100644 --- a/pkg/kubectl/cmd/expose_test.go +++ b/pkg/kubectl/cmd/expose_test.go @@ -467,7 +467,7 @@ func TestRunExposeService(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace(test.ns) defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -486,7 +486,6 @@ func TestRunExposeService(t *testing.T) { } }), } - tf.Namespace = test.ns ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdExposeService(tf, ioStreams) diff --git a/pkg/kubectl/cmd/get/get.go b/pkg/kubectl/cmd/get/get.go index ad6554c0ef8..ceb2e2de901 100644 --- a/pkg/kubectl/cmd/get/get.go +++ b/pkg/kubectl/cmd/get/get.go @@ -189,7 +189,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri } var err error - o.Namespace, o.ExplicitNamespace, err = f.DefaultNamespace() + o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/get/get_test.go b/pkg/kubectl/cmd/get/get_test.go index bdea31c0671..a6ab8990423 100644 --- a/pkg/kubectl/cmd/get/get_test.go +++ b/pkg/kubectl/cmd/get/get_test.go @@ -172,7 +172,7 @@ func testComponentStatusData() *api.ComponentStatusList { // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. func TestGetUnknownSchemaObject(t *testing.T) { t.Skip("This test is completely broken. The first thing it does is add the object to the scheme!") - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() _, _, codec := cmdtesting.NewExternalScheme() tf.OpenAPISchemaFunc = openapitesting.CreateOpenAPISchemaFunc(openapiSchemaPath) @@ -190,7 +190,6 @@ func TestGetUnknownSchemaObject(t *testing.T) { Body: objBody(codec, obj), }, } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -225,7 +224,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. func TestGetSchemaObject(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}) t.Logf("%v", string(runtime.EncodeOrDie(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}))) @@ -234,7 +233,6 @@ func TestGetSchemaObject(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})}, } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -249,7 +247,7 @@ func TestGetSchemaObject(t *testing.T) { func TestGetObjectsWithOpenAPIOutputFormatPresent(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -260,7 +258,6 @@ func TestGetObjectsWithOpenAPIOutputFormatPresent(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -306,7 +303,7 @@ func testOpenAPISchemaData() (openapi.Resources, error) { func TestGetObjects(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -314,7 +311,6 @@ func TestGetObjects(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -332,7 +328,7 @@ foo 0/0 0 func TestGetObjectsShowKind(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -340,7 +336,6 @@ func TestGetObjectsShowKind(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -359,7 +354,7 @@ pod/foo 0/0 0 func TestGetObjectsShowLabels(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -367,7 +362,6 @@ func TestGetObjectsShowLabels(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -398,7 +392,7 @@ func TestGetObjectIgnoreNotFound(t *testing.T) { }, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -416,7 +410,6 @@ func TestGetObjectIgnoreNotFound(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -451,7 +444,7 @@ func TestGetSortedObjects(t *testing.T) { }, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -459,7 +452,6 @@ func TestGetSortedObjects(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: "v1"}}} streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -483,7 +475,7 @@ c 0/0 0 func TestGetObjectsIdentifiedByFile(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -491,7 +483,6 @@ func TestGetObjectsIdentifiedByFile(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -510,7 +501,7 @@ foo 0/0 0 func TestGetListObjects(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -518,7 +509,6 @@ func TestGetListObjects(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -537,7 +527,7 @@ bar 0/0 0 func TestGetListComponentStatus(t *testing.T) { statuses := testComponentStatusData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -545,7 +535,6 @@ func TestGetListComponentStatus(t *testing.T) { NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, statuses)}, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -578,7 +567,7 @@ func TestGetMixedGenericObjects(t *testing.T) { Code: 0, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -594,7 +583,6 @@ func TestGetMixedGenericObjects(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -628,7 +616,7 @@ func TestGetMixedGenericObjects(t *testing.T) { func TestGetMultipleTypeObjects(t *testing.T) { pods, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -646,7 +634,6 @@ func TestGetMultipleTypeObjects(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -667,7 +654,7 @@ service/baz ClusterIP func TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -685,7 +672,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -767,7 +753,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { func TestGetMultipleTypeObjectsWithLabelSelector(t *testing.T) { pods, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -788,7 +774,6 @@ func TestGetMultipleTypeObjectsWithLabelSelector(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -811,7 +796,7 @@ service/baz ClusterIP func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) { pods, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -832,7 +817,6 @@ func TestGetMultipleTypeObjectsWithFieldSelector(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -860,7 +844,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { }, } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -878,7 +862,6 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -969,7 +952,7 @@ func watchTestData() ([]api.Pod, []watch.Event) { func TestWatchLabelSelector(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -997,7 +980,6 @@ func TestWatchLabelSelector(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -1021,7 +1003,7 @@ foo 0/0 0 func TestWatchFieldSelector(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1049,7 +1031,6 @@ func TestWatchFieldSelector(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -1073,7 +1054,7 @@ foo 0/0 0 func TestWatchResource(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1095,7 +1076,6 @@ func TestWatchResource(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -1117,7 +1097,7 @@ foo 0/0 0 func TestWatchResourceIdentifiedByFile(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1139,7 +1119,6 @@ func TestWatchResourceIdentifiedByFile(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -1162,7 +1141,7 @@ foo 0/0 0 func TestWatchOnlyResource(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1184,7 +1163,6 @@ func TestWatchOnlyResource(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) @@ -1205,7 +1183,7 @@ foo 0/0 0 func TestWatchOnlyList(t *testing.T) { pods, events := watchTestData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -1230,7 +1208,6 @@ func TestWatchOnlyList(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdGet("kubectl", tf, streams) diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index ce22d4af0bf..f89024770d6 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -188,7 +188,7 @@ func (o *LabelOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st return fmt.Errorf("--list and --output may not be specified together") } - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index 4bb226fa81d..f0264645e8d 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -322,10 +322,9 @@ func TestLabelErrors(t *testing.T) { for k, testCase := range testCases { t.Run(k, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, _, _ := genericclioptions.NewTestIOStreams() @@ -357,7 +356,7 @@ func TestLabelErrors(t *testing.T) { func TestLabelForResourceFromFile(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -388,7 +387,6 @@ func TestLabelForResourceFromFile(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -411,7 +409,7 @@ func TestLabelForResourceFromFile(t *testing.T) { } func TestLabelLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ @@ -421,7 +419,6 @@ func TestLabelLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -446,7 +443,7 @@ func TestLabelLocal(t *testing.T) { func TestLabelMultipleObjects(t *testing.T) { pods, _, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -479,7 +476,6 @@ func TestLabelMultipleObjects(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/logs.go b/pkg/kubectl/cmd/logs.go index 71eb390c6a9..9b5e55cf5ab 100644 --- a/pkg/kubectl/cmd/logs.go +++ b/pkg/kubectl/cmd/logs.go @@ -156,7 +156,7 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []str return cmdutil.UsageErrorf(cmd, "%s", logsUsageStr) } var err error - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/logs_test.go b/pkg/kubectl/cmd/logs_test.go index e95e67a01fc..7b82432532e 100644 --- a/pkg/kubectl/cmd/logs_test.go +++ b/pkg/kubectl/cmd/logs_test.go @@ -58,7 +58,7 @@ func TestLog(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { logContent := "test log content" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -80,7 +80,6 @@ func TestLog(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() oldLogFn := polymorphichelpers.LogsForObjectFn defer func() { @@ -123,6 +122,7 @@ func testPod() *api.Pod { func TestValidateLogFlags(t *testing.T) { f := cmdtesting.NewTestFactory() defer f.Cleanup() + f.WithNamespace("") tests := []struct { name string diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index f773632e18c..14fa2c6a510 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -152,7 +152,7 @@ func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st return o.PrintFlags.ToPrinter() } - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/patch_test.go b/pkg/kubectl/cmd/patch_test.go index 1311f188439..9a6ee38cae2 100644 --- a/pkg/kubectl/cmd/patch_test.go +++ b/pkg/kubectl/cmd/patch_test.go @@ -31,7 +31,7 @@ import ( func TestPatchObject(t *testing.T) { _, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -55,7 +55,6 @@ func TestPatchObject(t *testing.T) { } }), } - tf.Namespace = "test" stream, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdPatch(tf, stream) @@ -73,7 +72,7 @@ func TestPatchObject(t *testing.T) { func TestPatchObjectFromFile(t *testing.T) { _, svc, _ := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -90,7 +89,6 @@ func TestPatchObjectFromFile(t *testing.T) { } }), } - tf.Namespace = "test" stream, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdPatch(tf, stream) @@ -111,7 +109,7 @@ func TestPatchNoop(t *testing.T) { getObject := &svc.Items[0] patchObject := &svc.Items[0] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -130,7 +128,6 @@ func TestPatchNoop(t *testing.T) { } }), } - tf.Namespace = "test" // Patched { @@ -159,7 +156,7 @@ func TestPatchObjectFromFileOutput(t *testing.T) { } svcCopy.Labels["post-patch"] = "post-patch-value" - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -178,7 +175,6 @@ func TestPatchObjectFromFileOutput(t *testing.T) { } }), } - tf.Namespace = "test" stream, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdPatch(tf, stream) diff --git a/pkg/kubectl/cmd/plugin.go b/pkg/kubectl/cmd/plugin.go index 35c4662a59b..feb35253146 100644 --- a/pkg/kubectl/cmd/plugin.go +++ b/pkg/kubectl/cmd/plugin.go @@ -158,7 +158,7 @@ type factoryAttrsPluginEnvProvider struct { } func (p *factoryAttrsPluginEnvProvider) Env() (plugins.EnvList, error) { - cmdNamespace, _, err := p.factory.DefaultNamespace() + cmdNamespace, _, err := p.factory.ToRawKubeConfigLoader().Namespace() if err != nil { return plugins.EnvList{}, err } diff --git a/pkg/kubectl/cmd/portforward.go b/pkg/kubectl/cmd/portforward.go index b6f73348e26..3baa3bee0b1 100644 --- a/pkg/kubectl/cmd/portforward.go +++ b/pkg/kubectl/cmd/portforward.go @@ -174,7 +174,7 @@ func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg return cmdutil.UsageErrorf(cmd, "TYPE/NAME and list of ports are required for port-forward") } - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/portforward_test.go b/pkg/kubectl/cmd/portforward_test.go index 87642e1c853..c2ddde7fa98 100644 --- a/pkg/kubectl/cmd/portforward_test.go +++ b/pkg/kubectl/cmd/portforward_test.go @@ -73,7 +73,7 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { var err error - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -94,7 +94,6 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() ff := &fakePortForwarder{} if test.pfErr { diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index 3f210c6a57a..7c94ddf4973 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -179,7 +179,7 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [] o.Builder = f.NewBuilder o.BuilderArgs = args - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/replace_test.go b/pkg/kubectl/cmd/replace_test.go index e40bde5148c..13e69d907c6 100644 --- a/pkg/kubectl/cmd/replace_test.go +++ b/pkg/kubectl/cmd/replace_test.go @@ -32,7 +32,7 @@ import ( func TestReplaceObject(t *testing.T) { _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -62,7 +62,6 @@ func TestReplaceObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdReplace(tf, streams) @@ -89,7 +88,7 @@ func TestReplaceObject(t *testing.T) { func TestReplaceMultipleObject(t *testing.T) { _, svc, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -133,7 +132,6 @@ func TestReplaceMultipleObject(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdReplace(tf, streams) @@ -160,7 +158,7 @@ func TestReplaceMultipleObject(t *testing.T) { func TestReplaceDirectory(t *testing.T) { _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -191,7 +189,6 @@ func TestReplaceDirectory(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdReplace(tf, streams) @@ -218,7 +215,7 @@ func TestReplaceDirectory(t *testing.T) { func TestForceReplaceObjectNotFound(t *testing.T) { _, _, rc := testData() - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -238,7 +235,6 @@ func TestForceReplaceObjectNotFound(t *testing.T) { } }), } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdReplace(tf, streams) diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index de3e289f4a4..c304fdcaa1f 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -209,7 +209,7 @@ func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a } var err error - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollout/rollout_history.go b/pkg/kubectl/cmd/rollout/rollout_history.go index 5ace22c993f..b438bc30d1d 100644 --- a/pkg/kubectl/cmd/rollout/rollout_history.go +++ b/pkg/kubectl/cmd/rollout/rollout_history.go @@ -74,7 +74,7 @@ func RunHistory(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str return fmt.Errorf("revision must be a positive integer: %v", revision) } - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 51d8ea3faac..7ab8a851934 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -104,7 +104,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args o.Pauser = polymorphichelpers.ObjectPauserFn - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollout/rollout_pause_test.go b/pkg/kubectl/cmd/rollout/rollout_pause_test.go index a40e0327305..965a52163be 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause_test.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause_test.go @@ -40,7 +40,7 @@ var rolloutPauseGroupVersionDecoder = schema.GroupVersion{Group: "extensions", V func TestRolloutPause(t *testing.T) { deploymentName := "deployment/nginx-deployment" ns := legacyscheme.Codecs - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON) encoder := ns.EncoderForVersion(info.Serializer, rolloutPauseGroupVersionEncoder) @@ -62,7 +62,6 @@ func TestRolloutPause(t *testing.T) { }, } - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdRolloutPause(tf, streams) diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index 8ca44d8a762..aca0534f8eb 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -102,7 +102,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg o.Resumer = polymorphichelpers.ObjectResumerFn - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollout/rollout_status.go b/pkg/kubectl/cmd/rollout/rollout_status.go index 3182c4a8f37..e705bd8d163 100644 --- a/pkg/kubectl/cmd/rollout/rollout_status.go +++ b/pkg/kubectl/cmd/rollout/rollout_status.go @@ -104,7 +104,7 @@ func (o *RolloutStatusOptions) Complete(f cmdutil.Factory, args []string) error o.Builder = f.NewBuilder() var err error - o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/rollout/rollout_undo.go b/pkg/kubectl/cmd/rollout/rollout_undo.go index 46a66a0dd7b..f2840eebeb9 100644 --- a/pkg/kubectl/cmd/rollout/rollout_undo.go +++ b/pkg/kubectl/cmd/rollout/rollout_undo.go @@ -109,7 +109,7 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io o.Out = out o.DryRun = cmdutil.GetDryRunFlag(cmd) - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index a239b34e5a7..7157b72c8bd 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -265,7 +265,7 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e return cmdutil.UsageErrorf(cmd, "--port must be set when exposing a service") } - namespace, _, err := f.DefaultNamespace() + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/run_test.go b/pkg/kubectl/cmd/run_test.go index b4a6f6850e6..3ffd79f1c5e 100644 --- a/pkg/kubectl/cmd/run_test.go +++ b/pkg/kubectl/cmd/run_test.go @@ -170,7 +170,7 @@ func TestRunArgsFollowDashRules(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -190,7 +190,6 @@ func TestRunArgsFollowDashRules(t *testing.T) { }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{} cmd := NewCmdRun(tf, genericclioptions.NewTestIOStreamsDiscard()) @@ -505,7 +504,7 @@ func TestRunValidations(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() _, _, codec := cmdtesting.NewExternalScheme() @@ -513,7 +512,6 @@ func TestRunValidations(t *testing.T) { NegotiatedSerializer: scheme.Codecs, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", ""))}, } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, _, bufErr := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index d873a1ae2a7..1f8c4f2577c 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -156,7 +156,7 @@ func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st } o.PrintObj = printer.PrintObj - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_env.go b/pkg/kubectl/cmd/set/set_env.go index d2ec28e1a7f..10800c5b716 100644 --- a/pkg/kubectl/cmd/set/set_env.go +++ b/pkg/kubectl/cmd/set/set_env.go @@ -233,7 +233,7 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri if err != nil { return err } - o.namespace, o.enforceNamespace, err = f.DefaultNamespace() + o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_env_test.go b/pkg/kubectl/cmd/set/set_env_test.go index d5d73a5163a..f3d7c3338c0 100644 --- a/pkg/kubectl/cmd/set/set_env_test.go +++ b/pkg/kubectl/cmd/set/set_env_test.go @@ -45,7 +45,7 @@ import ( ) func TestSetEnvLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -56,7 +56,6 @@ func TestSetEnvLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -83,7 +82,7 @@ func TestSetEnvLocal(t *testing.T) { } func TestSetMultiResourcesEnvLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -94,7 +93,6 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -447,16 +445,15 @@ func TestSetEnvRemote(t *testing.T) { t.Run(input.name, func(t *testing.T) { groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion} testapi.Default = testapi.Groups[input.testAPIGroup] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{ GroupVersion: groupVersion, NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) + resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", "test", input.args[1]) switch p, m := req.URL.Path, req.Method; { case p == resourcePath && m == http.MethodGet: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(input.object)}, nil @@ -585,10 +582,9 @@ func TestSetEnvFromResource(t *testing.T) { }, } t.Run(input.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.Client = &fake.RESTClient{ GroupVersion: schema.GroupVersion{Group: "", Version: "v1"}, diff --git a/pkg/kubectl/cmd/set/set_image.go b/pkg/kubectl/cmd/set/set_image.go index b1de0b7b71a..7efa5f2d1fd 100644 --- a/pkg/kubectl/cmd/set/set_image.go +++ b/pkg/kubectl/cmd/set/set_image.go @@ -150,7 +150,7 @@ func (o *SetImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [ o.PrintObj = printer.PrintObj - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_image_test.go b/pkg/kubectl/cmd/set/set_image_test.go index fb383be767d..bf731f066b2 100644 --- a/pkg/kubectl/cmd/set/set_image_test.go +++ b/pkg/kubectl/cmd/set/set_image_test.go @@ -46,7 +46,7 @@ import ( ) func TestImageLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -57,7 +57,6 @@ func TestImageLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -159,7 +158,7 @@ func TestSetImageValidation(t *testing.T) { } func TestSetMultiResourcesImageLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -170,7 +169,6 @@ func TestSetMultiResourcesImageLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -532,15 +530,14 @@ func TestSetImageRemote(t *testing.T) { t.Run(input.name, func(t *testing.T) { groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion} testapi.Default = testapi.Groups[input.testAPIGroup] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{ GroupVersion: groupVersion, NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) + resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", "test", input.args[1]) switch p, m := req.URL.Path, req.Method; { case p == resourcePath && m == http.MethodGet: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(input.object)}, nil diff --git a/pkg/kubectl/cmd/set/set_resources.go b/pkg/kubectl/cmd/set/set_resources.go index 055068dd203..e773bb8137e 100644 --- a/pkg/kubectl/cmd/set/set_resources.go +++ b/pkg/kubectl/cmd/set/set_resources.go @@ -160,7 +160,7 @@ func (o *SetResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, ar } o.PrintObj = printer.PrintObj - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_resources_test.go b/pkg/kubectl/cmd/set/set_resources_test.go index fe630782649..9d286278f01 100644 --- a/pkg/kubectl/cmd/set/set_resources_test.go +++ b/pkg/kubectl/cmd/set/set_resources_test.go @@ -45,7 +45,7 @@ import ( ) func TestResourcesLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -56,7 +56,6 @@ func TestResourcesLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -94,7 +93,7 @@ func TestResourcesLocal(t *testing.T) { } func TestSetMultiResourcesLimitsLocal(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -105,7 +104,6 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -455,15 +453,14 @@ func TestSetResourcesRemote(t *testing.T) { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion} testapi.Default = testapi.Groups[input.testAPIGroup] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{ GroupVersion: groupVersion, NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) + resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", "test", input.args[1]) switch p, m := req.URL.Path, req.Method; { case p == resourcePath && m == http.MethodGet: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(input.object)}, nil diff --git a/pkg/kubectl/cmd/set/set_selector.go b/pkg/kubectl/cmd/set/set_selector.go index 54cfbacc1f7..3f29dd07c05 100644 --- a/pkg/kubectl/cmd/set/set_selector.go +++ b/pkg/kubectl/cmd/set/set_selector.go @@ -132,7 +132,7 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg o.dryrun = cmdutil.GetDryRunFlag(cmd) o.output = cmdutil.GetFlagString(cmd, "output") - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_selector_test.go b/pkg/kubectl/cmd/set/set_selector_test.go index 9c014f0c32d..351e6d98d35 100644 --- a/pkg/kubectl/cmd/set/set_selector_test.go +++ b/pkg/kubectl/cmd/set/set_selector_test.go @@ -317,7 +317,7 @@ func TestGetResourcesAndSelector(t *testing.T) { } func TestSelectorTest(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -328,7 +328,6 @@ func TestSelectorTest(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} streams, _, buf, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/set/set_serviceaccount.go b/pkg/kubectl/cmd/set/set_serviceaccount.go index ab4e843a619..6faf727801a 100644 --- a/pkg/kubectl/cmd/set/set_serviceaccount.go +++ b/pkg/kubectl/cmd/set/set_serviceaccount.go @@ -141,7 +141,7 @@ func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Comman } o.PrintObj = printer.PrintObj - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_serviceaccount_test.go b/pkg/kubectl/cmd/set/set_serviceaccount_test.go index f2e06bd7bb6..7239bf1c587 100644 --- a/pkg/kubectl/cmd/set/set_serviceaccount_test.go +++ b/pkg/kubectl/cmd/set/set_serviceaccount_test.go @@ -68,7 +68,7 @@ func TestSetServiceAccountLocal(t *testing.T) { for i, input := range inputs { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -81,7 +81,6 @@ func TestSetServiceAccountLocal(t *testing.T) { outputFormat := "yaml" - tf.Namespace = "test" streams, _, buf, _ := genericclioptions.NewTestIOStreams() cmd := NewCmdServiceAccount(tf, streams) cmd.Flags().Set("output", outputFormat) @@ -105,7 +104,7 @@ func TestSetServiceAccountLocal(t *testing.T) { func TestSetServiceAccountMultiLocal(t *testing.T) { testapi.Default = testapi.Groups[""] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -116,7 +115,6 @@ func TestSetServiceAccountMultiLocal(t *testing.T) { return nil, nil }), } - tf.Namespace = "test" tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} outputFormat := "name" @@ -326,15 +324,14 @@ func TestSetServiceAccountRemote(t *testing.T) { t.Run(input.apiPrefix, func(t *testing.T) { groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion} testapi.Default = testapi.Groups[input.testAPIGroup] - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" tf.Client = &fake.RESTClient{ GroupVersion: groupVersion, NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) + resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", "test", input.args[1]) switch p, m := req.URL.Path, req.Method; { case p == resourcePath && m == http.MethodGet: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(input.object)}, nil @@ -387,7 +384,7 @@ func TestServiceAccountValidation(t *testing.T) { } for _, input := range inputs { t.Run(input.name, func(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() tf.Client = &fake.RESTClient{ @@ -400,7 +397,6 @@ func TestServiceAccountValidation(t *testing.T) { outputFormat := "" - tf.Namespace = "test" streams := genericclioptions.NewTestIOStreamsDiscard() cmd := NewCmdServiceAccount(tf, streams) diff --git a/pkg/kubectl/cmd/set/set_subject.go b/pkg/kubectl/cmd/set/set_subject.go index c745abfc3a4..4b395b31d3c 100644 --- a/pkg/kubectl/cmd/set/set_subject.go +++ b/pkg/kubectl/cmd/set/set_subject.go @@ -131,7 +131,7 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [] o.PrintObj = printer.PrintObj var enforceNamespace bool - o.namespace, enforceNamespace, err = f.DefaultNamespace() + o.namespace, enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/set_subject_test.go b/pkg/kubectl/cmd/set/set_subject_test.go index 05c82082c83..33fd75cd739 100644 --- a/pkg/kubectl/cmd/set/set_subject_test.go +++ b/pkg/kubectl/cmd/set/set_subject_test.go @@ -28,11 +28,9 @@ import ( ) func TestValidate(t *testing.T) { - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() - tf.Namespace = "test" - tests := map[string]struct { options *SubjectOptions expectErr bool diff --git a/pkg/kubectl/cmd/taint.go b/pkg/kubectl/cmd/taint.go index e4c68dd4340..45b6ab0e7bf 100644 --- a/pkg/kubectl/cmd/taint.go +++ b/pkg/kubectl/cmd/taint.go @@ -124,7 +124,7 @@ func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra. // Complete adapts from the command line args and factory to the data required. func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) (err error) { - namespace, _, err := f.DefaultNamespace() + namespace, _, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/testing/BUILD b/pkg/kubectl/cmd/testing/BUILD index 8c615c2cf7f..e6b59fa7f0e 100644 --- a/pkg/kubectl/cmd/testing/BUILD +++ b/pkg/kubectl/cmd/testing/BUILD @@ -18,7 +18,6 @@ go_library( "//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/validation:go_default_library", - "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta/testrestmapper:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 839ee29dbea..12d876626d7 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -25,8 +25,6 @@ import ( "path/filepath" "time" - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta/testrestmapper" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -234,12 +232,12 @@ func (d *fakeCachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList type TestFactory struct { cmdutil.Factory + kubeConfigFlags *genericclioptions.TestConfigFlags + Client kubectl.RESTClient ScaleGetter scaleclient.ScalesGetter UnstructuredClient kubectl.RESTClient - Namespace string ClientConfigVal *restclient.Config - CommandVal string FakeDynamicClient *fakedynamic.FakeDynamicClient tempConfigFile *os.File @@ -276,6 +274,7 @@ func NewTestFactory() *TestFactory { return &TestFactory{ Factory: cmdutil.NewFactory(configFlags), + kubeConfigFlags: configFlags, FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(legacyscheme.Scheme), tempConfigFile: tmpFile, @@ -283,6 +282,11 @@ func NewTestFactory() *TestFactory { } } +func (f *TestFactory) WithNamespace(ns string) *TestFactory { + f.kubeConfigFlags.WithNamespace(ns) + return f +} + func (f *TestFactory) Cleanup() { if f.tempConfigFile == nil { return @@ -295,10 +299,6 @@ func (f *TestFactory) ToRESTConfig() (*restclient.Config, error) { return f.ClientConfigVal, nil } -func (f *TestFactory) BareClientConfig() (*restclient.Config, error) { - return f.ClientConfigVal, nil -} - func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) { return f.Client, nil } @@ -314,10 +314,6 @@ func (f *TestFactory) Validator(validate bool) (validation.Schema, error) { return validation.NullSchema{}, nil } -func (f *TestFactory) DefaultNamespace() (string, bool, error) { - return f.Namespace, false, nil -} - func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) { if f.OpenAPISchemaFunc != nil { return f.OpenAPISchemaFunc() @@ -325,10 +321,6 @@ func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) { return openapitesting.EmptyResources{}, nil } -func (f *TestFactory) Command(*cobra.Command, bool) string { - return f.CommandVal -} - func (f *TestFactory) NewBuilder() *resource.Builder { mapper, err := f.ToRESTMapper() @@ -425,10 +417,6 @@ func (f *TestFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, err return cachedClient, nil } -func (f *TestFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error) { - return f.ClientSet() -} - func testRESTMapper() meta.RESTMapper { groupResources := testDynamicResources() mapper := restmapper.NewDiscoveryRESTMapper(groupResources) diff --git a/pkg/kubectl/cmd/top_node_test.go b/pkg/kubectl/cmd/top_node_test.go index 6857355fb1a..ab387fbfc83 100644 --- a/pkg/kubectl/cmd/top_node_test.go +++ b/pkg/kubectl/cmd/top_node_test.go @@ -50,7 +50,7 @@ func TestTopNodeAllMetrics(t *testing.T) { expectedMetricsPath := fmt.Sprintf("%s/%s/nodes", baseMetricsAddress, metricsApiVersion) expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -78,7 +78,6 @@ func TestTopNodeAllMetrics(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -103,7 +102,7 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) { expectedMetricsPath := fmt.Sprintf("%s/%s/nodes", customBaseMetricsAddress, metricsApiVersion) expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -131,7 +130,6 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -167,7 +165,7 @@ func TestTopNodeWithNameMetrics(t *testing.T) { expectedPath := fmt.Sprintf("%s/%s/nodes/%s", baseMetricsAddress, metricsApiVersion, expectedMetrics.Name) expectedNodePath := fmt.Sprintf("/%s/%s/nodes/%s", apiPrefix, apiVersion, expectedMetrics.Name) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -195,7 +193,6 @@ func TestTopNodeWithNameMetrics(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -234,7 +231,7 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) { expectedQuery := fmt.Sprintf("labelSelector=%s", url.QueryEscape(label)) expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -262,7 +259,6 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) { } }), } - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -289,7 +285,7 @@ func TestTopNodeAllMetricsFromMetricsServer(t *testing.T) { expectedMetrics, nodes := testNodeV1beta1MetricsData() expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -315,7 +311,6 @@ func TestTopNodeAllMetricsFromMetricsServer(t *testing.T) { fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) { return true, expectedMetrics, nil }) - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -357,7 +352,7 @@ func TestTopNodeWithNameMetricsFromMetricsServer(t *testing.T) { } expectedNodePath := fmt.Sprintf("/%s/%s/nodes/%s", apiPrefix, apiVersion, expectedMetrics.Name) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -383,7 +378,6 @@ func TestTopNodeWithNameMetricsFromMetricsServer(t *testing.T) { fakemetricsClientset.AddReactor("get", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) { return true, &expectedMetrics, nil }) - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -435,7 +429,7 @@ func TestTopNodeWithLabelSelectorMetricsFromMetricsServer(t *testing.T) { label := "key=value" expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion) - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("test") defer tf.Cleanup() codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) @@ -462,7 +456,6 @@ func TestTopNodeWithLabelSelectorMetricsFromMetricsServer(t *testing.T) { fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) { return true, expectedMetrics, nil }) - tf.Namespace = "test" tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/top_pod.go b/pkg/kubectl/cmd/top_pod.go index e90ac689882..b63dde44ebf 100644 --- a/pkg/kubectl/cmd/top_pod.go +++ b/pkg/kubectl/cmd/top_pod.go @@ -121,7 +121,7 @@ func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []s return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } - o.Namespace, _, err = f.DefaultNamespace() + o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/top_pod_test.go b/pkg/kubectl/cmd/top_pod_test.go index 494e08efa6c..1cba8d692eb 100644 --- a/pkg/kubectl/cmd/top_pod_test.go +++ b/pkg/kubectl/cmd/top_pod_test.go @@ -164,7 +164,7 @@ func TestTopPod(t *testing.T) { } } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace(testNS) defer tf.Cleanup() ns := legacyscheme.Codecs @@ -190,7 +190,6 @@ func TestTopPod(t *testing.T) { } }), } - tf.Namespace = testNS tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -309,7 +308,7 @@ func TestTopPodWithMetricsServer(t *testing.T) { }) } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace(testNS) defer tf.Cleanup() ns := legacyscheme.Codecs @@ -329,7 +328,6 @@ func TestTopPodWithMetricsServer(t *testing.T) { } }), } - tf.Namespace = testNS tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() @@ -508,7 +506,7 @@ func TestTopPodCustomDefaults(t *testing.T) { } } - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace(testNS) defer tf.Cleanup() ns := legacyscheme.Codecs @@ -534,7 +532,6 @@ func TestTopPodCustomDefaults(t *testing.T) { } }), } - tf.Namespace = testNS tf.ClientConfigVal = defaultClientConfig() streams, _, buf, _ := genericclioptions.NewTestIOStreams() diff --git a/pkg/kubectl/cmd/util/editor/editoptions.go b/pkg/kubectl/cmd/util/editor/editoptions.go index abf1e7ef2a3..49417e54ad2 100644 --- a/pkg/kubectl/cmd/util/editor/editoptions.go +++ b/pkg/kubectl/cmd/util/editor/editoptions.go @@ -129,7 +129,7 @@ func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Comm return fmt.Errorf("the edit mode doesn't support output the patch") } - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() if err != nil { return err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 24544f67213..8dc6df3fa56 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -62,11 +62,6 @@ type Factory interface { // and which implements the common patterns for CLI interactions with generic resources. NewBuilder() *resource.Builder - // Returns the default namespace to use in cases where no - // other namespace is specified and whether the namespace was - // overridden. - DefaultNamespace() (string, bool, error) - // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 8b806097680..e01e649f4f8 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -118,10 +118,6 @@ func (f *factoryImpl) RESTClient() (*restclient.RESTClient, error) { return restclient.RESTClientFor(clientConfig) } -func (f *factoryImpl) DefaultNamespace() (string, bool, error) { - return f.clientGetter.ToRawKubeConfigLoader().Namespace() -} - func (f *factoryImpl) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) { cfg, err := f.clientGetter.ToRESTConfig() if err != nil { diff --git a/pkg/kubectl/genericclioptions/BUILD b/pkg/kubectl/genericclioptions/BUILD index 88c5662b716..0a04b54eeac 100644 --- a/pkg/kubectl/genericclioptions/BUILD +++ b/pkg/kubectl/genericclioptions/BUILD @@ -30,6 +30,7 @@ go_library( "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/restmapper:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", + "//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library", "//vendor/k8s.io/client-go/util/homedir:go_default_library", ], ) diff --git a/pkg/kubectl/genericclioptions/config_flags_fake.go b/pkg/kubectl/genericclioptions/config_flags_fake.go index d019b8c93a2..64e9a688330 100644 --- a/pkg/kubectl/genericclioptions/config_flags_fake.go +++ b/pkg/kubectl/genericclioptions/config_flags_fake.go @@ -24,6 +24,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" ) type TestConfigFlags struct { @@ -74,6 +75,36 @@ func (f *TestConfigFlags) WithDiscoveryClient(c discovery.CachedDiscoveryInterfa return f } +func (f *TestConfigFlags) WithNamespace(ns string) *TestConfigFlags { + if f.clientConfig == nil { + panic("attempt to obtain a test RawKubeConfigLoader with no clientConfig specified") + } + f.clientConfig = &namespacedClientConfig{ + delegate: f.clientConfig, + namespace: ns, + } + return f +} + func NewTestConfigFlags() *TestConfigFlags { return &TestConfigFlags{} } + +type namespacedClientConfig struct { + delegate clientcmd.ClientConfig + namespace string +} + +func (c *namespacedClientConfig) Namespace() (string, bool, error) { + return c.namespace, false, nil +} + +func (c *namespacedClientConfig) RawConfig() (clientcmdapi.Config, error) { + return c.delegate.RawConfig() +} +func (c *namespacedClientConfig) ClientConfig() (*rest.Config, error) { + return c.delegate.ClientConfig() +} +func (c *namespacedClientConfig) ConfigAccess() clientcmd.ConfigAccess { + return c.delegate.ConfigAccess() +} From d1abc5c8247dbb62b3e8df5c66a891f3bea38298 Mon Sep 17 00:00:00 2001 From: John Calabrese Date: Thu, 10 May 2018 07:07:26 -0400 Subject: [PATCH 087/416] use subtest for table units employ consitent table var naming - https://github.com/kubernetes/kubernetes/pull/63659#discussion_r187736533 --- .../priorities/util/non_zero_test.go | 12 ++++++---- .../priorities/util/topologies_test.go | 24 ++++++++++++------- .../algorithm/priorities/util/util_test.go | 14 ++++++----- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/pkg/scheduler/algorithm/priorities/util/non_zero_test.go b/pkg/scheduler/algorithm/priorities/util/non_zero_test.go index 8be7e4ac94b..001b65ca195 100644 --- a/pkg/scheduler/algorithm/priorities/util/non_zero_test.go +++ b/pkg/scheduler/algorithm/priorities/util/non_zero_test.go @@ -26,7 +26,7 @@ import ( ) func TestGetNonzeroRequests(t *testing.T) { - tds := []struct { + tests := []struct { name string requests v1.ResourceList expectedCPU int64 @@ -65,9 +65,11 @@ func TestGetNonzeroRequests(t *testing.T) { }, } - for _, td := range tds { - realCPU, realMemory := GetNonzeroRequests(&td.requests) - assert.EqualValuesf(t, td.expectedCPU, realCPU, "Failed to test: %s", td.name) - assert.EqualValuesf(t, td.expectedMemory, realMemory, "Failed to test: %s", td.name) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + realCPU, realMemory := GetNonzeroRequests(&test.requests) + assert.EqualValuesf(t, test.expectedCPU, realCPU, "Failed to test: %s", test.name) + assert.EqualValuesf(t, test.expectedMemory, realMemory, "Failed to test: %s", test.name) + }) } } diff --git a/pkg/scheduler/algorithm/priorities/util/topologies_test.go b/pkg/scheduler/algorithm/priorities/util/topologies_test.go index 25e24299f87..6aee723e468 100644 --- a/pkg/scheduler/algorithm/priorities/util/topologies_test.go +++ b/pkg/scheduler/algorithm/priorities/util/topologies_test.go @@ -59,8 +59,10 @@ func TestGetNamespacesFromPodAffinityTerm(t *testing.T) { } for _, test := range tests { - realValue := GetNamespacesFromPodAffinityTerm(fakePod(), test.podAffinityTerm) - assert.EqualValuesf(t, test.expectedValue, realValue, "Failed to test: %s", test.name) + t.Run(test.name, func(t *testing.T) { + realValue := GetNamespacesFromPodAffinityTerm(fakePod(), test.podAffinityTerm) + assert.EqualValuesf(t, test.expectedValue, realValue, "Failed to test: %s", test.name) + }) } } @@ -96,12 +98,14 @@ func TestPodMatchesTermsNamespaceAndSelector(t *testing.T) { } for _, test := range tests { - fakeTestPod := fakePod() - fakeTestPod.Namespace = test.podNamespaces - fakeTestPod.Labels = test.podLabels + t.Run(test.name, func(t *testing.T) { + fakeTestPod := fakePod() + fakeTestPod.Namespace = test.podNamespaces + fakeTestPod.Labels = test.podLabels - realValue := PodMatchesTermsNamespaceAndSelector(fakeTestPod, fakeNamespaces, fakeSelector) - assert.EqualValuesf(t, test.expectedResult, realValue, "Faild to test: %s", test.name) + realValue := PodMatchesTermsNamespaceAndSelector(fakeTestPod, fakeNamespaces, fakeSelector) + assert.EqualValuesf(t, test.expectedResult, realValue, "Faild to test: %s", test.name) + }) } } @@ -248,7 +252,9 @@ func TestNodesHaveSameTopologyKey(t *testing.T) { } for _, test := range tests { - got := NodesHaveSameTopologyKey(test.nodeA, test.nodeB, test.topologyKey) - assert.Equalf(t, test.expected, got, "Failed to test: %s", test.name) + t.Run(test.name, func(t *testing.T) { + got := NodesHaveSameTopologyKey(test.nodeA, test.nodeB, test.topologyKey) + assert.Equalf(t, test.expected, got, "Failed to test: %s", test.name) + }) } } diff --git a/pkg/scheduler/algorithm/priorities/util/util_test.go b/pkg/scheduler/algorithm/priorities/util/util_test.go index fc79ebbfc4f..b4f0fc0aa52 100644 --- a/pkg/scheduler/algorithm/priorities/util/util_test.go +++ b/pkg/scheduler/algorithm/priorities/util/util_test.go @@ -109,11 +109,13 @@ func TestGetControllerRef(t *testing.T) { } for _, td := range tds { - realOR := GetControllerRef(&td.pod) - if td.expectedNil { - assert.Nilf(t, realOR, "Failed to test: %s", td.name) - } else { - assert.Equalf(t, &td.expectedOR, realOR, "Failed to test: %s", td.name) - } + t.Run(td.name, func(t *testing.T) { + realOR := GetControllerRef(&td.pod) + if td.expectedNil { + assert.Nilf(t, realOR, "Failed to test: %s", td.name) + } else { + assert.Equalf(t, &td.expectedOR, realOR, "Failed to test: %s", td.name) + } + }) } } From a82d11bbbab22a1fb6e89bf29f7b703db8a4b811 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Mon, 28 May 2018 15:46:18 +0200 Subject: [PATCH 088/416] Increase the timeout when waiting for the job to be gone --- test/e2e/kubectl/kubectl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/kubectl/kubectl.go b/test/e2e/kubectl/kubectl.go index 254391188d5..3132d2c11a9 100644 --- a/test/e2e/kubectl/kubectl.go +++ b/test/e2e/kubectl/kubectl.go @@ -1545,7 +1545,7 @@ metadata: Expect(runOutput).To(ContainSubstring("abcd1234")) Expect(runOutput).To(ContainSubstring("stdin closed")) - err := framework.WaitForJobGone(c, ns, jobName, 10*time.Second) + err := framework.WaitForJobGone(c, ns, jobName, wait.ForeverTestTimeout) Expect(err).NotTo(HaveOccurred()) By("verifying the job " + jobName + " was deleted") From 8ff0fff06511cadb3850ab8b96098e946926999f Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Mon, 28 May 2018 16:24:19 +0200 Subject: [PATCH 089/416] Allow AWS EBS volumes to be attached as ReadOnly. --- pkg/cloudprovider/providers/aws/aws.go | 10 ++-------- pkg/volume/aws_ebs/attacher.go | 4 ++-- pkg/volume/aws_ebs/attacher_test.go | 17 +++++------------ .../persistentvolume/label/admission_test.go | 2 +- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index 6ea14ef58ef..c2dc6add563 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -429,7 +429,7 @@ type Volumes interface { // Attach the disk to the node with the specified NodeName // nodeName can be empty to mean "the instance on which we are running" // Returns the device (e.g. /dev/xvdf) where we attached the volume - AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) + AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName) (string, error) // Detach the disk from the node with the specified NodeName // nodeName can be empty to mean "the instance on which we are running" // Returns the device where the volume was attached @@ -1956,7 +1956,7 @@ func wrapAttachError(err error, disk *awsDisk, instance string) error { } // AttachDisk implements Volumes.AttachDisk -func (c *Cloud) AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) { +func (c *Cloud) AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName) (string, error) { disk, err := newAWSDisk(c, diskName) if err != nil { return "", err @@ -1967,12 +1967,6 @@ func (c *Cloud) AttachDisk(diskName KubernetesVolumeID, nodeName types.NodeName, return "", fmt.Errorf("error finding instance %s: %q", nodeName, err) } - if readOnly { - // TODO: We could enforce this when we mount the volume (?) - // TODO: We could also snapshot the volume and attach copies of it - return "", errors.New("AWS volumes cannot be mounted read-only") - } - // mountDevice will hold the device where we should try to attach the disk var mountDevice mountDevice // alreadyAttached is true if we have already called AttachVolume on this disk diff --git a/pkg/volume/aws_ebs/attacher.go b/pkg/volume/aws_ebs/attacher.go index 059431e1661..ac716ed1b0d 100644 --- a/pkg/volume/aws_ebs/attacher.go +++ b/pkg/volume/aws_ebs/attacher.go @@ -59,7 +59,7 @@ func (plugin *awsElasticBlockStorePlugin) GetDeviceMountRefs(deviceMountPath str } func (attacher *awsElasticBlockStoreAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) { - volumeSource, readOnly, err := getVolumeSource(spec) + volumeSource, _, err := getVolumeSource(spec) if err != nil { return "", err } @@ -68,7 +68,7 @@ func (attacher *awsElasticBlockStoreAttacher) Attach(spec *volume.Spec, nodeName // awsCloud.AttachDisk checks if disk is already attached to node and // succeeds in that case, so no need to do that separately. - devicePath, err := attacher.awsVolumes.AttachDisk(volumeID, nodeName, readOnly) + devicePath, err := attacher.awsVolumes.AttachDisk(volumeID, nodeName) if err != nil { glog.Errorf("Error attaching volume %q to node %q: %+v", volumeID, nodeName, err) return "", err diff --git a/pkg/volume/aws_ebs/attacher_test.go b/pkg/volume/aws_ebs/attacher_test.go index 1076d06910e..36ed854d1a4 100644 --- a/pkg/volume/aws_ebs/attacher_test.go +++ b/pkg/volume/aws_ebs/attacher_test.go @@ -76,15 +76,14 @@ type testcase struct { func TestAttachDetach(t *testing.T) { diskName := aws.KubernetesVolumeID("disk") nodeName := types.NodeName("instance") - readOnly := false - spec := createVolSpec(diskName, readOnly) + spec := createVolSpec(diskName, false) attachError := errors.New("Fake attach error") detachError := errors.New("Fake detach error") tests := []testcase{ // Successful Attach call { name: "Attach_Positive", - attach: attachCall{diskName, nodeName, readOnly, "/dev/sda", nil}, + attach: attachCall{diskName, nodeName, "/dev/sda", nil}, test: func(testcase *testcase) (string, error) { attacher := newAttacher(testcase) return attacher.Attach(spec, nodeName) @@ -95,7 +94,7 @@ func TestAttachDetach(t *testing.T) { // Attach call fails { name: "Attach_Negative", - attach: attachCall{diskName, nodeName, readOnly, "", attachError}, + attach: attachCall{diskName, nodeName, "", attachError}, test: func(testcase *testcase) (string, error) { attacher := newAttacher(testcase) return attacher.Attach(spec, nodeName) @@ -195,7 +194,6 @@ func createPVSpec(name aws.KubernetesVolumeID, readOnly bool) *volume.Spec { type attachCall struct { diskName aws.KubernetesVolumeID nodeName types.NodeName - readOnly bool retDeviceName string ret error } @@ -214,7 +212,7 @@ type diskIsAttachedCall struct { ret error } -func (testcase *testcase) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) { +func (testcase *testcase) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName) (string, error) { expected := &testcase.attach if expected.diskName == "" && expected.nodeName == "" { @@ -234,12 +232,7 @@ func (testcase *testcase) AttachDisk(diskName aws.KubernetesVolumeID, nodeName t return "", errors.New("Unexpected AttachDisk call: wrong nodeName") } - if expected.readOnly != readOnly { - testcase.t.Errorf("Unexpected AttachDisk call: expected readOnly %v, got %v", expected.readOnly, readOnly) - return "", errors.New("Unexpected AttachDisk call: wrong readOnly") - } - - glog.V(4).Infof("AttachDisk call: %s, %s, %v, returning %q, %v", diskName, nodeName, readOnly, expected.retDeviceName, expected.ret) + glog.V(4).Infof("AttachDisk call: %s, %s, returning %q, %v", diskName, nodeName, expected.retDeviceName, expected.ret) return expected.retDeviceName, expected.ret } diff --git a/plugin/pkg/admission/storage/persistentvolume/label/admission_test.go b/plugin/pkg/admission/storage/persistentvolume/label/admission_test.go index c8cbc7ff4f4..f04939152e2 100644 --- a/plugin/pkg/admission/storage/persistentvolume/label/admission_test.go +++ b/plugin/pkg/admission/storage/persistentvolume/label/admission_test.go @@ -36,7 +36,7 @@ type mockVolumes struct { var _ aws.Volumes = &mockVolumes{} -func (v *mockVolumes) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName, readOnly bool) (string, error) { +func (v *mockVolumes) AttachDisk(diskName aws.KubernetesVolumeID, nodeName types.NodeName) (string, error) { return "", fmt.Errorf("not implemented") } From a3e841871ce35c4bd99e529a2d12dc84a2339588 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Mon, 28 May 2018 17:50:36 +0200 Subject: [PATCH 090/416] Add daemonset when to getReplicasFromRuntimeObject when cleaning objects in e2e --- test/e2e/framework/util.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 5dc45815b6f..ad85097d340 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -3030,6 +3030,8 @@ func getReplicasFromRuntimeObject(obj runtime.Object) (int32, error) { return *typed.Spec.Replicas, nil } return 0, nil + case *extensions.DaemonSet: + return 0, nil case *batch.Job: // TODO: currently we use pause pods so that's OK. When we'll want to switch to Pods // that actually finish we need a better way to do this. From a2a3a98e1db80702e9adec77598db31265d240db Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Mon, 28 May 2018 17:14:53 +0200 Subject: [PATCH 091/416] DaemonSet internals are still in extensions --- test/e2e/scheduling/BUILD | 2 +- test/e2e/scheduling/nvidia-gpus.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/scheduling/BUILD b/test/e2e/scheduling/BUILD index 33a4eda78ed..1e1cda3a2c4 100644 --- a/test/e2e/scheduling/BUILD +++ b/test/e2e/scheduling/BUILD @@ -20,8 +20,8 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/api/v1/pod:go_default_library", - "//pkg/apis/apps:go_default_library", "//pkg/apis/core:go_default_library", + "//pkg/apis/extensions:go_default_library", "//pkg/apis/scheduling:go_default_library", "//pkg/kubelet/apis:go_default_library", "//pkg/quota/evaluator/core:go_default_library", diff --git a/test/e2e/scheduling/nvidia-gpus.go b/test/e2e/scheduling/nvidia-gpus.go index 5e08e339c5f..b0b4c62e7c9 100644 --- a/test/e2e/scheduling/nvidia-gpus.go +++ b/test/e2e/scheduling/nvidia-gpus.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/kubernetes/pkg/apis/apps" + extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/test/e2e/framework" imageutils "k8s.io/kubernetes/test/utils/image" @@ -137,10 +137,10 @@ func SetupNVIDIAGPUNode(f *framework.Framework, setupResourceGatherer bool) *fra framework.ExpectNoError(err, "failed to create nvidia-driver-installer daemonset") framework.Logf("Successfully created daemonset to install Nvidia drivers.") - pods, err := framework.WaitForControlledPods(f.ClientSet, ds.Namespace, ds.Name, apps.Kind("DaemonSet")) + pods, err := framework.WaitForControlledPods(f.ClientSet, ds.Namespace, ds.Name, extensionsinternal.Kind("DaemonSet")) framework.ExpectNoError(err, "failed to get pods controlled by the nvidia-driver-installer daemonset") - devicepluginPods, err := framework.WaitForControlledPods(f.ClientSet, "kube-system", "nvidia-gpu-device-plugin", apps.Kind("DaemonSet")) + devicepluginPods, err := framework.WaitForControlledPods(f.ClientSet, "kube-system", "nvidia-gpu-device-plugin", extensionsinternal.Kind("DaemonSet")) if err == nil { framework.Logf("Adding deviceplugin addon pod.") pods.Items = append(pods.Items, devicepluginPods.Items...) From 6f1b178ed762202a83200c9523f095794b1c5bca Mon Sep 17 00:00:00 2001 From: Nail Islamov Date: Sun, 27 May 2018 23:40:52 +1000 Subject: [PATCH 092/416] Declare wait flag in way consistent with other deletion flags --- pkg/kubectl/cmd/delete.go | 14 ++++++-------- pkg/kubectl/cmd/delete_flags.go | 13 +++++++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 6446e2a354f..10c93998f05 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -139,8 +139,6 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra deleteFlags.AddFlags(cmd) - cmd.Flags().Bool("wait", true, `If true, wait for resources to be gone before returning. This waits for finalizers.`) - cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -165,14 +163,9 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Co } if o.GracePeriod == 0 && !o.ForceDeletion { // To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0 - // into --grace-period=1 and wait until the object is successfully deleted. Users may provide --force - // to bypass this wait. - o.WaitForDeletion = true + // into --grace-period=1. Users may provide --force to bypass this conversion. o.GracePeriod = 1 } - if b, err := cmd.Flags().GetBool("wait"); err == nil { - o.WaitForDeletion = b - } includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) r := f.NewBuilder(). @@ -218,6 +211,11 @@ func (o *DeleteOptions) Validate(cmd *cobra.Command) error { return fmt.Errorf("cannot set --all and --field-selector at the same time") } + if o.GracePeriod == 0 && !o.ForceDeletion && !o.WaitForDeletion { + // With the explicit --wait flag we need extra validation for backward compatibility + return fmt.Errorf("--grace-period=0 must have either --force specified, or --wait to be set to true") + } + switch { case o.GracePeriod == 0 && o.ForceDeletion: fmt.Fprintf(o.ErrOut, "warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.\n") diff --git a/pkg/kubectl/cmd/delete_flags.go b/pkg/kubectl/cmd/delete_flags.go index fc0580c688d..c98d1fce0f5 100644 --- a/pkg/kubectl/cmd/delete_flags.go +++ b/pkg/kubectl/cmd/delete_flags.go @@ -39,6 +39,7 @@ type DeleteFlags struct { IgnoreNotFound *bool Now *bool Timeout *time.Duration + Wait *bool Output *string } @@ -85,6 +86,9 @@ func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams generic if f.Timeout != nil { options.Timeout = *f.Timeout } + if f.Wait != nil { + options.WaitForDeletion = *f.Wait + } return options } @@ -118,11 +122,12 @@ func (f *DeleteFlags) AddFlags(cmd *cobra.Command) { if f.IgnoreNotFound != nil { cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") } - + if f.Wait != nil { + cmd.Flags().BoolVar(f.Wait, "wait", *f.Wait, "If true, wait for resources to be gone before returning. This waits for finalizers.") + } if f.Output != nil { cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).") } - } // NewDeleteCommandFlags provides default flags and values for use with the "delete" command @@ -139,6 +144,7 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags { labelSelector := "" fieldSelector := "" timeout := time.Duration(0) + wait := true filenames := []string{} recursive := false @@ -156,6 +162,7 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags { IgnoreNotFound: &ignoreNotFound, Now: &now, Timeout: &timeout, + Wait: &wait, Output: &output, } } @@ -167,6 +174,7 @@ func NewDeleteFlags(usage string) *DeleteFlags { force := false timeout := time.Duration(0) + wait := false filenames := []string{} recursive := false @@ -180,5 +188,6 @@ func NewDeleteFlags(usage string) *DeleteFlags { // add non-defaults Force: &force, Timeout: &timeout, + Wait: &wait, } } From f6d4244c30060bcf6ebe4ad80ecec47e836aca30 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Tue, 29 May 2018 09:16:25 +0200 Subject: [PATCH 093/416] client-go: document README exception in .github/PULL_REQUEST_TEMPLATE.md --- .../src/k8s.io/client-go/.github/PULL_REQUEST_TEMPLATE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/staging/src/k8s.io/client-go/.github/PULL_REQUEST_TEMPLATE.md b/staging/src/k8s.io/client-go/.github/PULL_REQUEST_TEMPLATE.md index e559c074bb5..6aed9889cf1 100644 --- a/staging/src/k8s.io/client-go/.github/PULL_REQUEST_TEMPLATE.md +++ b/staging/src/k8s.io/client-go/.github/PULL_REQUEST_TEMPLATE.md @@ -1,2 +1,3 @@ -Sorry, we do not accept changes directly against this repository. Please see -CONTRIBUTING.md for information on where and how to contribute instead. +Sorry, we do not accept changes directly against this repository, unless the +change is to the `README.md` itself. Please see +`CONTRIBUTING.md` for information on where and how to contribute instead. From 39263a3f5d12fe2f5ca1dae3100b5faa32932aa8 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 29 May 2018 09:54:20 +0200 Subject: [PATCH 094/416] Add reliable wait for volume server startup. Remove sleep(20) and check for readiness of volume servers by checking logs. --- test/e2e/framework/volume_util.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/test/e2e/framework/volume_util.go b/test/e2e/framework/volume_util.go index 9eb5269ad62..ce97bcf2dcf 100644 --- a/test/e2e/framework/volume_util.go +++ b/test/e2e/framework/volume_util.go @@ -66,7 +66,7 @@ const ( TiB int64 = 1024 * GiB // Waiting period for volume server (Ceph, ...) to initialize itself. - VolumeServerPodStartupSleep = 20 * time.Second + VolumeServerPodStartupTimeout = 1 * time.Minute // Waiting period for pod to be cleaned up and unmount its volumes so we // don't tear down containers with NFS/Ceph/Gluster server too early. @@ -92,6 +92,8 @@ type VolumeTestConfig struct { // map -> // if is empty, mount a tmpfs emptydir ServerVolumes map[string]string + // Message to wait for before starting clients + ServerReadyMessage string // Wait for the pod to terminate successfully // False indicates that the pod is long running WaitForCompletion bool @@ -114,11 +116,12 @@ type VolumeTest struct { // NFS-specific wrapper for CreateStorageServer. func NewNFSServer(cs clientset.Interface, namespace string, args []string) (config VolumeTestConfig, pod *v1.Pod, ip string) { config = VolumeTestConfig{ - Namespace: namespace, - Prefix: "nfs", - ServerImage: imageutils.GetE2EImage(imageutils.VolumeNFSServer), - ServerPorts: []int{2049}, - ServerVolumes: map[string]string{"": "/exports"}, + Namespace: namespace, + Prefix: "nfs", + ServerImage: imageutils.GetE2EImage(imageutils.VolumeNFSServer), + ServerPorts: []int{2049}, + ServerVolumes: map[string]string{"": "/exports"}, + ServerReadyMessage: "NFS started", } if len(args) > 0 { config.ServerArgs = args @@ -180,6 +183,7 @@ func NewISCSIServer(cs clientset.Interface, namespace string) (config VolumeTest // iSCSI container needs to insert modules from the host "/lib/modules": "/lib/modules", }, + ServerReadyMessage: "Configuration restored from /etc/target/saveconfig.json", } pod, ip = CreateStorageServer(cs, config) return config, pod, ip @@ -195,16 +199,9 @@ func NewRBDServer(cs clientset.Interface, namespace string) (config VolumeTestCo ServerVolumes: map[string]string{ "/lib/modules": "/lib/modules", }, + ServerReadyMessage: "Ceph is ready", } pod, ip = CreateStorageServer(cs, config) - - // Ceph server container needs some time to start. Tests continue working if - // this sleep is removed, however kubelet logs (and kubectl describe - // ) would be cluttered with error messages about non-existing - // image. - Logf("sleeping a bit to give ceph server time to initialize") - time.Sleep(VolumeServerPodStartupSleep) - // create secrets for the server secret = &v1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -350,6 +347,10 @@ func StartVolumeServer(client clientset.Interface, config VolumeTestConfig) *v1. ExpectNoError(err, "Cannot locate the server pod %q: %v", serverPodName, err) } } + if config.ServerReadyMessage != "" { + _, err := LookForStringInLog(pod.Namespace, pod.Name, serverPodName, config.ServerReadyMessage, VolumeServerPodStartupTimeout) + ExpectNoError(err, "Failed to find %q in pod logs: %s", config.ServerReadyMessage, err) + } return pod } From 08564f203edb1e37343f0b4b21cddf048075b340 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Tue, 29 May 2018 12:02:40 +0200 Subject: [PATCH 095/416] Add block volume support to internal provisioners. --- pkg/volume/aws_ebs/BUILD | 2 ++ pkg/volume/aws_ebs/aws_ebs.go | 6 ++++++ pkg/volume/azure_dd/BUILD | 2 ++ pkg/volume/azure_dd/azure_provision.go | 7 +++++++ pkg/volume/azure_file/azure_provision.go | 3 +++ pkg/volume/cinder/cinder.go | 4 ++++ pkg/volume/flocker/flocker_volume.go | 4 ++++ pkg/volume/gce_pd/gce_pd.go | 6 ++++++ pkg/volume/glusterfs/glusterfs.go | 5 +++++ pkg/volume/host_path/host_path.go | 4 ++++ pkg/volume/photon_pd/photon_pd.go | 4 ++++ pkg/volume/portworx/portworx.go | 4 ++++ pkg/volume/quobyte/quobyte.go | 4 ++++ pkg/volume/rbd/BUILD | 2 ++ pkg/volume/rbd/rbd.go | 7 +++++++ pkg/volume/scaleio/sio_volume.go | 4 ++++ pkg/volume/storageos/storageos.go | 3 +++ pkg/volume/util/util.go | 6 ++++++ pkg/volume/vsphere_volume/vsphere_volume.go | 3 +++ 19 files changed, 80 insertions(+) diff --git a/pkg/volume/aws_ebs/BUILD b/pkg/volume/aws_ebs/BUILD index 5354b9e31b1..506240f6764 100644 --- a/pkg/volume/aws_ebs/BUILD +++ b/pkg/volume/aws_ebs/BUILD @@ -19,6 +19,7 @@ go_library( deps = [ "//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider/providers/aws:go_default_library", + "//pkg/features:go_default_library", "//pkg/util/mount:go_default_library", "//pkg/util/strings:go_default_library", "//pkg/volume:go_default_library", @@ -29,6 +30,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", ], ) diff --git a/pkg/volume/aws_ebs/aws_ebs.go b/pkg/volume/aws_ebs/aws_ebs.go index 71531245371..2dedb9aa4af 100644 --- a/pkg/volume/aws_ebs/aws_ebs.go +++ b/pkg/volume/aws_ebs/aws_ebs.go @@ -28,7 +28,9 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/cloudprovider/providers/aws" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" kstrings "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" @@ -507,5 +509,9 @@ func (c *awsElasticBlockStoreProvisioner) Provision() (*v1.PersistentVolume, err } } + if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) { + pv.Spec.VolumeMode = c.options.PVC.Spec.VolumeMode + } + return pv, nil } diff --git a/pkg/volume/azure_dd/BUILD b/pkg/volume/azure_dd/BUILD index c4913bfba6e..cb1ccc3bf09 100644 --- a/pkg/volume/azure_dd/BUILD +++ b/pkg/volume/azure_dd/BUILD @@ -56,6 +56,7 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider/providers/azure:go_default_library", + "//pkg/features:go_default_library", "//pkg/util/keymutex:go_default_library", "//pkg/util/mount:go_default_library", "//pkg/util/strings:go_default_library", @@ -71,6 +72,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", ], ) diff --git a/pkg/volume/azure_dd/azure_provision.go b/pkg/volume/azure_dd/azure_provision.go index bf7eae37db4..2beac4b11eb 100644 --- a/pkg/volume/azure_dd/azure_provision.go +++ b/pkg/volume/azure_dd/azure_provision.go @@ -23,6 +23,8 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume/util" ) @@ -187,5 +189,10 @@ func (p *azureDiskProvisioner) Provision() (*v1.PersistentVolume, error) { MountOptions: p.options.MountOptions, }, } + + if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) { + pv.Spec.VolumeMode = p.options.PVC.Spec.VolumeMode + } + return pv, nil } diff --git a/pkg/volume/azure_file/azure_provision.go b/pkg/volume/azure_file/azure_provision.go index dc155334ff9..ca344c7061e 100644 --- a/pkg/volume/azure_file/azure_provision.go +++ b/pkg/volume/azure_file/azure_provision.go @@ -135,6 +135,9 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) { if !util.AccessModesContainedInAll(a.plugin.GetAccessModes(), a.options.PVC.Spec.AccessModes) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", a.options.PVC.Spec.AccessModes, a.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(a.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", a.plugin.GetPluginName()) + } var sku, location, account string diff --git a/pkg/volume/cinder/cinder.go b/pkg/volume/cinder/cinder.go index 121972c4613..5216c8dee91 100644 --- a/pkg/volume/cinder/cinder.go +++ b/pkg/volume/cinder/cinder.go @@ -508,6 +508,10 @@ func (c *cinderVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName()) + } + volumeID, sizeGB, labels, fstype, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/flocker/flocker_volume.go b/pkg/volume/flocker/flocker_volume.go index d7f245d15a2..ef81dd7cc26 100644 --- a/pkg/volume/flocker/flocker_volume.go +++ b/pkg/volume/flocker/flocker_volume.go @@ -67,6 +67,10 @@ func (c *flockerVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { return nil, fmt.Errorf("Provisioning failed: Specified unsupported selector") } + if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName()) + } + datasetUUID, sizeGB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/gce_pd/gce_pd.go b/pkg/volume/gce_pd/gce_pd.go index 8c78c6754af..9c2be1c8133 100644 --- a/pkg/volume/gce_pd/gce_pd.go +++ b/pkg/volume/gce_pd/gce_pd.go @@ -27,6 +27,8 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" kstrings "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" @@ -448,5 +450,9 @@ func (c *gcePersistentDiskProvisioner) Provision() (*v1.PersistentVolume, error) } } + if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) { + pv.Spec.VolumeMode = c.options.PVC.Spec.VolumeMode + } + return pv, nil } diff --git a/pkg/volume/glusterfs/glusterfs.go b/pkg/volume/glusterfs/glusterfs.go index 28a7104aa1d..573d93d359c 100644 --- a/pkg/volume/glusterfs/glusterfs.go +++ b/pkg/volume/glusterfs/glusterfs.go @@ -673,6 +673,11 @@ func (p *glusterfsVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { glog.V(4).Infof("not able to parse your claim Selector") return nil, fmt.Errorf("not able to parse your claim Selector") } + + if volutil.CheckPersistentVolumeClaimModeBlock(p.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", p.plugin.GetPluginName()) + } + glog.V(4).Infof("Provision VolumeOptions %v", p.options) scName := v1helper.GetPersistentVolumeClaimClass(p.options.PVC) cfg, err := parseClassParameters(p.options.Parameters, p.plugin.host.GetKubeClient()) diff --git a/pkg/volume/host_path/host_path.go b/pkg/volume/host_path/host_path.go index 4a3cb76b6af..178a8248905 100644 --- a/pkg/volume/host_path/host_path.go +++ b/pkg/volume/host_path/host_path.go @@ -266,6 +266,10 @@ type hostPathProvisioner struct { // Create for hostPath simply creates a local /tmp/hostpath_pv/%s directory as a new PersistentVolume. // This Provisioner is meant for development and testing only and WILL NOT WORK in a multi-node cluster. func (r *hostPathProvisioner) Provision() (*v1.PersistentVolume, error) { + if util.CheckPersistentVolumeClaimModeBlock(r.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", r.plugin.GetPluginName()) + } + fullpath := fmt.Sprintf("/tmp/hostpath_pv/%s", uuid.NewUUID()) capacity := r.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] diff --git a/pkg/volume/photon_pd/photon_pd.go b/pkg/volume/photon_pd/photon_pd.go index 25ca23928d0..3f615ed7f2f 100644 --- a/pkg/volume/photon_pd/photon_pd.go +++ b/pkg/volume/photon_pd/photon_pd.go @@ -345,6 +345,10 @@ func (p *photonPersistentDiskProvisioner) Provision() (*v1.PersistentVolume, err return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(p.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", p.plugin.GetPluginName()) + } + pdID, sizeGB, fstype, err := p.manager.CreateVolume(p) if err != nil { return nil, err diff --git a/pkg/volume/portworx/portworx.go b/pkg/volume/portworx/portworx.go index c0cde9fde9e..2378bb32fbc 100644 --- a/pkg/volume/portworx/portworx.go +++ b/pkg/volume/portworx/portworx.go @@ -383,6 +383,10 @@ func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName()) + } + volumeID, sizeGiB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/quobyte/quobyte.go b/pkg/volume/quobyte/quobyte.go index 0a5990b2900..f919166a322 100644 --- a/pkg/volume/quobyte/quobyte.go +++ b/pkg/volume/quobyte/quobyte.go @@ -359,6 +359,10 @@ func (provisioner *quobyteVolumeProvisioner) Provision() (*v1.PersistentVolume, return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", provisioner.options.PVC.Spec.AccessModes, provisioner.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(provisioner.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", provisioner.plugin.GetPluginName()) + } + if provisioner.options.PVC.Spec.Selector != nil { return nil, fmt.Errorf("claim Selector is not supported") } diff --git a/pkg/volume/rbd/BUILD b/pkg/volume/rbd/BUILD index 65042418cd4..e6b7fe8aead 100644 --- a/pkg/volume/rbd/BUILD +++ b/pkg/volume/rbd/BUILD @@ -17,6 +17,7 @@ go_library( ], importpath = "k8s.io/kubernetes/pkg/volume/rbd", deps = [ + "//pkg/features:go_default_library", "//pkg/util/file:go_default_library", "//pkg/util/mount:go_default_library", "//pkg/util/node:go_default_library", @@ -33,6 +34,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", ], ) diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index 615b89554ab..283403f273c 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -30,7 +30,9 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/uuid" + utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/strings" "k8s.io/kubernetes/pkg/volume" @@ -698,6 +700,11 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", sizeMB)), } pv.Spec.MountOptions = r.options.MountOptions + + if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) { + pv.Spec.VolumeMode = r.options.PVC.Spec.VolumeMode + } + return pv, nil } diff --git a/pkg/volume/scaleio/sio_volume.go b/pkg/volume/scaleio/sio_volume.go index b7f02701118..3c1cb2da8b5 100644 --- a/pkg/volume/scaleio/sio_volume.go +++ b/pkg/volume/scaleio/sio_volume.go @@ -259,6 +259,10 @@ func (v *sioVolume) Provision() (*api.PersistentVolume, error) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(v.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", v.plugin.GetPluginName()) + } + // setup volume attrributes genName := v.generateName("k8svol", 11) var oneGig int64 = 1024 * 1024 * 1024 diff --git a/pkg/volume/storageos/storageos.go b/pkg/volume/storageos/storageos.go index a814c438dd1..42a5df7f050 100644 --- a/pkg/volume/storageos/storageos.go +++ b/pkg/volume/storageos/storageos.go @@ -564,6 +564,9 @@ func (c *storageosProvisioner) Provision() (*v1.PersistentVolume, error) { if !util.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(c.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", c.plugin.GetPluginName()) + } var adminSecretName, adminSecretNamespace string diff --git a/pkg/volume/util/util.go b/pkg/volume/util/util.go index 663d8d62946..18956ef89b8 100644 --- a/pkg/volume/util/util.go +++ b/pkg/volume/util/util.go @@ -718,6 +718,12 @@ func CheckVolumeModeFilesystem(volumeSpec *volume.Spec) (bool, error) { return true, nil } +// CheckPersistentVolumeClaimModeBlock checks VolumeMode. +// If the mode is Block, return true otherwise return false. +func CheckPersistentVolumeClaimModeBlock(pvc *v1.PersistentVolumeClaim) bool { + return utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && pvc.Spec.VolumeMode != nil && *pvc.Spec.VolumeMode == v1.PersistentVolumeBlock +} + // MakeAbsolutePath convert path to absolute path according to GOOS func MakeAbsolutePath(goos, path string) string { if goos != "windows" { diff --git a/pkg/volume/vsphere_volume/vsphere_volume.go b/pkg/volume/vsphere_volume/vsphere_volume.go index 7b1e611df54..377622c7a3a 100644 --- a/pkg/volume/vsphere_volume/vsphere_volume.go +++ b/pkg/volume/vsphere_volume/vsphere_volume.go @@ -352,6 +352,9 @@ func (v *vsphereVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { if !util.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes()) } + if util.CheckPersistentVolumeClaimModeBlock(v.options.PVC) { + return nil, fmt.Errorf("%s does not support block volume provisioning", v.plugin.GetPluginName()) + } volSpec, err := v.manager.CreateVolume(v) if err != nil { From 1b1c2d1264516c51222d9d9e762885322da29d77 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 29 May 2018 18:07:41 +0800 Subject: [PATCH 096/416] include rollout object name in cli message --- pkg/kubectl/rollout_status.go | 10 +++++----- pkg/kubectl/rollout_status_test.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/kubectl/rollout_status.go b/pkg/kubectl/rollout_status.go index b65b91fd4eb..714d364fd83 100644 --- a/pkg/kubectl/rollout_status.go +++ b/pkg/kubectl/rollout_status.go @@ -83,13 +83,13 @@ func (s *DeploymentStatusViewer) Status(namespace, name string, revision int64) return "", false, fmt.Errorf("deployment %q exceeded its progress deadline", name) } if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas { - return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas), false, nil + return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d out of %d new replicas have been updated...\n", name, deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas), false, nil } if deployment.Status.Replicas > deployment.Status.UpdatedReplicas { - return fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...\n", deployment.Status.Replicas-deployment.Status.UpdatedReplicas), false, nil + return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d old replicas are pending termination...\n", name, deployment.Status.Replicas-deployment.Status.UpdatedReplicas), false, nil } if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas { - return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil + return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d of %d updated replicas are available...\n", name, deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil } return fmt.Sprintf("deployment %q successfully rolled out\n", name), true, nil } @@ -109,10 +109,10 @@ func (s *DaemonSetStatusViewer) Status(namespace, name string, revision int64) ( } if daemon.Generation <= daemon.Status.ObservedGeneration { if daemon.Status.UpdatedNumberScheduled < daemon.Status.DesiredNumberScheduled { - return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new pods have been updated...\n", daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), false, nil + return fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d out of %d new pods have been updated...\n", name, daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), false, nil } if daemon.Status.NumberAvailable < daemon.Status.DesiredNumberScheduled { - return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated pods are available...\n", daemon.Status.NumberAvailable, daemon.Status.DesiredNumberScheduled), false, nil + return fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d of %d updated pods are available...\n", name, daemon.Status.NumberAvailable, daemon.Status.DesiredNumberScheduled), false, nil } return fmt.Sprintf("daemon set %q successfully rolled out\n", name), true, nil } diff --git a/pkg/kubectl/rollout_status_test.go b/pkg/kubectl/rollout_status_test.go index 729709a7af3..ae346f116bc 100644 --- a/pkg/kubectl/rollout_status_test.go +++ b/pkg/kubectl/rollout_status_test.go @@ -45,7 +45,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) { UnavailableReplicas: 0, }, - msg: "Waiting for rollout to finish: 0 out of 1 new replicas have been updated...\n", + msg: "Waiting for deployment \"foo\" rollout to finish: 0 out of 1 new replicas have been updated...\n", done: false, }, { @@ -59,7 +59,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) { UnavailableReplicas: 0, }, - msg: "Waiting for rollout to finish: 1 old replicas are pending termination...\n", + msg: "Waiting for deployment \"foo\" rollout to finish: 1 old replicas are pending termination...\n", done: false, }, { @@ -73,7 +73,7 @@ func TestDeploymentStatusViewerStatus(t *testing.T) { UnavailableReplicas: 1, }, - msg: "Waiting for rollout to finish: 1 of 2 updated replicas are available...\n", + msg: "Waiting for deployment \"foo\" rollout to finish: 1 of 2 updated replicas are available...\n", done: false, }, { @@ -155,7 +155,7 @@ func TestDaemonSetStatusViewerStatus(t *testing.T) { NumberAvailable: 0, }, - msg: "Waiting for rollout to finish: 0 out of 1 new pods have been updated...\n", + msg: "Waiting for daemon set \"foo\" rollout to finish: 0 out of 1 new pods have been updated...\n", done: false, }, { @@ -167,7 +167,7 @@ func TestDaemonSetStatusViewerStatus(t *testing.T) { NumberAvailable: 1, }, - msg: "Waiting for rollout to finish: 1 of 2 updated pods are available...\n", + msg: "Waiting for daemon set \"foo\" rollout to finish: 1 of 2 updated pods are available...\n", done: false, }, { From 2bf66c377d60276e017e78ab9dbc0490f26baa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mudrini=C4=87?= Date: Tue, 17 Apr 2018 22:22:37 +0200 Subject: [PATCH 097/416] apiextensions-apiserver: add establishing controller to avoid race between established and CRs actually served --- cmd/kube-apiserver/app/apiextensions.go | 2 + cmd/kube-apiserver/app/server.go | 2 +- .../src/k8s.io/apiextensions-apiserver/BUILD | 1 + .../pkg/apiserver/BUILD | 1 + .../pkg/apiserver/apiserver.go | 10 +- .../pkg/apiserver/customresource_handler.go | 33 +++- .../pkg/controller/establish/BUILD | 34 +++++ .../establish/establishing_controller.go | 142 ++++++++++++++++++ .../pkg/controller/status/BUILD | 1 + .../controller/status/naming_controller.go | 18 ++- .../status/naming_controller_test.go | 63 ++++---- 11 files changed, 270 insertions(+), 37 deletions(-) create mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/BUILD create mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/establishing_controller.go diff --git a/cmd/kube-apiserver/app/apiextensions.go b/cmd/kube-apiserver/app/apiextensions.go index 375372a162e..b94b5f311b0 100644 --- a/cmd/kube-apiserver/app/apiextensions.go +++ b/cmd/kube-apiserver/app/apiextensions.go @@ -35,6 +35,7 @@ func createAPIExtensionsConfig( externalInformers kubeexternalinformers.SharedInformerFactory, pluginInitializers []admission.PluginInitializer, commandOptions *options.ServerRunOptions, + masterCount int, ) (*apiextensionsapiserver.Config, error) { // make a shallow copy to let us twiddle a few things // most of the config actually remains the same. We only need to mess with a couple items related to the particulars of the apiextensions @@ -69,6 +70,7 @@ func createAPIExtensionsConfig( }, ExtraConfig: apiextensionsapiserver.ExtraConfig{ CRDRESTOptionsGetter: apiextensionscmd.NewCRDRESTOptionsGetter(etcdOptions), + MasterCount: masterCount, }, } diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 3fb5ed71bb1..b936c9ece2a 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -165,7 +165,7 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan } // If additional API servers are added, they should be gated. - apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, versionedInformers, pluginInitializer, completedOptions.ServerRunOptions) + apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, versionedInformers, pluginInitializer, completedOptions.ServerRunOptions, completedOptions.MasterCount) if err != nil { return nil, err } diff --git a/staging/src/k8s.io/apiextensions-apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/BUILD index 8092fcb8216..31d0de0ae36 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/BUILD @@ -44,6 +44,7 @@ filegroup( "//staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server:all-srcs", + "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:all-srcs", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD index 4d5e2a73ec0..f9c4a706d91 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD @@ -32,6 +32,7 @@ go_library( "//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/controller/establish:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/controller/finalizer:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/controller/status:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go index f2c73601135..f1fc89ba96c 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go @@ -37,11 +37,11 @@ import ( "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset" internalinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion" + "k8s.io/apiextensions-apiserver/pkg/controller/establish" "k8s.io/apiextensions-apiserver/pkg/controller/finalizer" "k8s.io/apiextensions-apiserver/pkg/controller/status" "k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition" - // make sure the generated client works _ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" _ "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions" _ "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion" @@ -74,6 +74,10 @@ func init() { type ExtraConfig struct { CRDRESTOptionsGetter genericregistry.RESTOptionsGetter + + // MasterCount is used to detect whether cluster is HA, and if it is + // the CRD Establishing will be hold by 5 seconds. + MasterCount int } type Config struct { @@ -162,6 +166,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) discovery: map[string]*discovery.APIGroupHandler{}, delegate: delegateHandler, } + establishingController := establish.NewEstablishingController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions()) crdHandler := NewCustomResourceDefinitionHandler( versionDiscoveryHandler, groupDiscoveryHandler, @@ -169,6 +174,8 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) delegateHandler, c.ExtraConfig.CRDRESTOptionsGetter, c.GenericConfig.AdmissionControl, + establishingController, + c.ExtraConfig.MasterCount, ) s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler) s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler) @@ -188,6 +195,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error { go crdController.Run(context.StopCh) go namingController.Run(context.StopCh) + go establishingController.Run(context.StopCh) go finalizingController.Run(5, context.StopCh) return nil }) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go index 231bffdbf6d..e93fad14336 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go @@ -62,6 +62,7 @@ import ( apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation" informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion" listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion" + "k8s.io/apiextensions-apiserver/pkg/controller/establish" "k8s.io/apiextensions-apiserver/pkg/controller/finalizer" apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" "k8s.io/apiextensions-apiserver/pkg/registry/customresource" @@ -86,6 +87,12 @@ type crdHandler struct { delegate http.Handler restOptionsGetter generic.RESTOptionsGetter admission admission.Interface + + establishingController *establish.EstablishingController + + // MasterCount is used to implement sleep to improve + // CRD establishing process for HA clusters. + masterCount int } // crdInfo stores enough information to serve the storage for the custom resource @@ -120,7 +127,9 @@ func NewCustomResourceDefinitionHandler( crdInformer informers.CustomResourceDefinitionInformer, delegate http.Handler, restOptionsGetter generic.RESTOptionsGetter, - admission admission.Interface) *crdHandler { + admission admission.Interface, + establishingController *establish.EstablishingController, + masterCount int) *crdHandler { ret := &crdHandler{ versionDiscoveryHandler: versionDiscoveryHandler, groupDiscoveryHandler: groupDiscoveryHandler, @@ -129,6 +138,8 @@ func NewCustomResourceDefinitionHandler( delegate: delegate, restOptionsGetter: restOptionsGetter, admission: admission, + establishingController: establishingController, + masterCount: masterCount, } crdInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ UpdateFunc: ret.updateCustomResourceDefinition, @@ -181,7 +192,12 @@ func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { r.delegate.ServeHTTP(w, req) return } - if !apiextensions.IsCRDConditionTrue(crd, apiextensions.Established) { + // There is a small chance that a CRD is being served because NamesAccepted condition is true, + // but it becomes "unserved" because another names update leads to a conflict + // and EstablishingController wasn't fast enough to put the CRD into the Established condition. + // We accept this as the problem is small and self-healing. + if !apiextensions.IsCRDConditionTrue(crd, apiextensions.NamesAccepted) && + !apiextensions.IsCRDConditionTrue(crd, apiextensions.Established) { r.delegate.ServeHTTP(w, req) return } @@ -299,6 +315,19 @@ func (r *crdHandler) updateCustomResourceDefinition(oldObj, newObj interface{}) r.customStorageLock.Lock() defer r.customStorageLock.Unlock() + // Add CRD to the establishing controller queue. + // For HA clusters, we want to prevent race conditions when changing status to Established, + // so we want to be sure that CRD is Installing at least for 5 seconds before Establishing it. + // TODO: find a real HA safe checkpointing mechanism instead of an arbitrary wait. + if !apiextensions.IsCRDConditionTrue(newCRD, apiextensions.Established) && + apiextensions.IsCRDConditionTrue(newCRD, apiextensions.NamesAccepted) { + if r.masterCount > 1 { + r.establishingController.QueueCRD(newCRD.Name, 5*time.Second) + } else { + r.establishingController.QueueCRD(newCRD.Name, 0) + } + } + storageMap := r.customStorage.Load().(crdStorageMap) oldInfo, found := storageMap[newCRD.UID] if !found { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/BUILD new file mode 100644 index 00000000000..fa3e5dfa1bb --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/BUILD @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["establishing_controller.go"], + importpath = "k8s.io/apiextensions-apiserver/pkg/controller/establish", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library", + "//vendor/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/client-go/tools/cache:go_default_library", + "//vendor/k8s.io/client-go/util/workqueue:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/establishing_controller.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/establishing_controller.go new file mode 100644 index 00000000000..5c2ebbcaad8 --- /dev/null +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/establishing_controller.go @@ -0,0 +1,142 @@ +/* +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 establish + +import ( + "fmt" + "time" + + "github.com/golang/glog" + apierrors "k8s.io/apimachinery/pkg/api/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + client "k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion" + informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion" + listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion" +) + +// EstablishingController controls how and when CRD is established. +type EstablishingController struct { + crdClient client.CustomResourceDefinitionsGetter + crdLister listers.CustomResourceDefinitionLister + crdSynced cache.InformerSynced + + // To allow injection for testing. + syncFn func(key string) error + + queue workqueue.RateLimitingInterface +} + +// NewEstablishingController creates new EstablishingController. +func NewEstablishingController(crdInformer informers.CustomResourceDefinitionInformer, + crdClient client.CustomResourceDefinitionsGetter) *EstablishingController { + ec := &EstablishingController{ + crdClient: crdClient, + crdLister: crdInformer.Lister(), + crdSynced: crdInformer.Informer().HasSynced, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "crdEstablishing"), + } + + ec.syncFn = ec.sync + + return ec +} + +// QueueCRD adds CRD into the establishing queue. +func (ec *EstablishingController) QueueCRD(key string, timeout time.Duration) { + ec.queue.AddAfter(key, timeout) +} + +// Run starts the EstablishingController. +func (ec *EstablishingController) Run(stopCh <-chan struct{}) { + defer utilruntime.HandleCrash() + defer ec.queue.ShutDown() + + glog.Infof("Starting EstablishingController") + defer glog.Infof("Shutting down EstablishingController") + + if !cache.WaitForCacheSync(stopCh, ec.crdSynced) { + return + } + + // only start one worker thread since its a slow moving API + go wait.Until(ec.runWorker, time.Second, stopCh) + + <-stopCh +} + +func (ec *EstablishingController) runWorker() { + for ec.processNextWorkItem() { + } +} + +// processNextWorkItem deals with one key off the queue. +// It returns false when it's time to quit. +func (ec *EstablishingController) processNextWorkItem() bool { + key, quit := ec.queue.Get() + if quit { + return false + } + defer ec.queue.Done(key) + + err := ec.syncFn(key.(string)) + if err == nil { + return true + } + + utilruntime.HandleError(fmt.Errorf("%v failed with: %v", key, err)) + ec.queue.AddRateLimited(key) + + return true +} + +// sync is used to turn CRDs into the Established state. +func (ec *EstablishingController) sync(key string) error { + cachedCRD, err := ec.crdLister.Get(key) + if apierrors.IsNotFound(err) { + return nil + } + if err != nil { + return err + } + + if !apiextensions.IsCRDConditionTrue(cachedCRD, apiextensions.NamesAccepted) || + apiextensions.IsCRDConditionTrue(cachedCRD, apiextensions.Established) { + return nil + } + + crd := cachedCRD.DeepCopy() + establishedCondition := apiextensions.CustomResourceDefinitionCondition{ + Type: apiextensions.Established, + Status: apiextensions.ConditionTrue, + Reason: "InitialNamesAccepted", + Message: "the initial names have been accepted", + } + apiextensions.SetCRDCondition(crd, establishedCondition) + + // Update server with new CRD condition. + _, err = ec.crdClient.CustomResourceDefinitions().UpdateStatus(crd) + if err != nil { + return err + } + + return nil +} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/BUILD index c408cf4874e..0489e9fccd7 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/BUILD @@ -28,6 +28,7 @@ go_library( "//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go index 16016e493a5..f00def4b124 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go @@ -24,6 +24,7 @@ import ( "github.com/golang/glog" + "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/labels" utilerrors "k8s.io/apimachinery/pkg/util/errors" @@ -191,7 +192,10 @@ func (c *NamingConditionController) calculateNamesAndConditions(in *apiextension namesAcceptedCondition.Message = "no conflicts found" } - // set EstablishedCondition to true if all names are accepted. Never set it back to false. + // set EstablishedCondition initially to false, then set it to true in establishing controller. + // The Establishing Controller will see the NamesAccepted condition when it arrives through the shared informer. + // At that time the API endpoint handler will serve the endpoint, avoiding a race + // which we had if we set Established to true here. establishedCondition := apiextensions.CustomResourceDefinitionCondition{ Type: apiextensions.Established, Status: apiextensions.ConditionFalse, @@ -204,8 +208,8 @@ func (c *NamingConditionController) calculateNamesAndConditions(in *apiextension if establishedCondition.Status != apiextensions.ConditionTrue && namesAcceptedCondition.Status == apiextensions.ConditionTrue { establishedCondition = apiextensions.CustomResourceDefinitionCondition{ Type: apiextensions.Established, - Status: apiextensions.ConditionTrue, - Reason: "InitialNamesAccepted", + Status: apiextensions.ConditionFalse, + Reason: "Installing", Message: "the initial names have been accepted", } } @@ -238,12 +242,16 @@ func (c *NamingConditionController) sync(key string) error { return err } + // Skip checking names if Spec and Status names are same. + if equality.Semantic.DeepEqual(inCustomResourceDefinition.Spec.Names, inCustomResourceDefinition.Status.AcceptedNames) { + return nil + } + acceptedNames, namingCondition, establishedCondition := c.calculateNamesAndConditions(inCustomResourceDefinition) // nothing to do if accepted names and NamesAccepted condition didn't change if reflect.DeepEqual(inCustomResourceDefinition.Status.AcceptedNames, acceptedNames) && - apiextensions.IsCRDConditionEquivalent(&namingCondition, apiextensions.FindCRDCondition(inCustomResourceDefinition, apiextensions.NamesAccepted)) && - apiextensions.IsCRDConditionEquivalent(&establishedCondition, apiextensions.FindCRDCondition(inCustomResourceDefinition, apiextensions.Established)) { + apiextensions.IsCRDConditionEquivalent(&namingCondition, apiextensions.FindCRDCondition(inCustomResourceDefinition, apiextensions.NamesAccepted)) { return nil } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller_test.go index 615c5dd85ff..717e5288484 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller_test.go @@ -95,19 +95,17 @@ var acceptedCondition = apiextensions.CustomResourceDefinitionCondition{ Message: "no conflicts found", } -func nameConflictCondition(reason, message string) apiextensions.CustomResourceDefinitionCondition { - return apiextensions.CustomResourceDefinitionCondition{ - Type: apiextensions.NamesAccepted, - Status: apiextensions.ConditionFalse, - Reason: reason, - Message: message, - } +var notAcceptedCondition = apiextensions.CustomResourceDefinitionCondition{ + Type: apiextensions.NamesAccepted, + Status: apiextensions.ConditionFalse, + Reason: "NotAccepted", + Message: "not all names are accepted", } -var establishedCondition = apiextensions.CustomResourceDefinitionCondition{ +var installingCondition = apiextensions.CustomResourceDefinitionCondition{ Type: apiextensions.Established, - Status: apiextensions.ConditionTrue, - Reason: "InitialNamesAccepted", + Status: apiextensions.ConditionFalse, + Reason: "Installing", Message: "the initial names have been accepted", } @@ -118,6 +116,15 @@ var notEstablishedCondition = apiextensions.CustomResourceDefinitionCondition{ Message: "not all names are accepted", } +func nameConflictCondition(reason, message string) apiextensions.CustomResourceDefinitionCondition { + return apiextensions.CustomResourceDefinitionCondition{ + Type: apiextensions.NamesAccepted, + Status: apiextensions.ConditionFalse, + Reason: reason, + Message: message, + } +} + func TestSync(t *testing.T) { tests := []struct { name string @@ -136,7 +143,7 @@ func TestSync(t *testing.T) { Plural: "alfa", }, expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { name: "different groups", @@ -146,7 +153,7 @@ func TestSync(t *testing.T) { }, expectedNames: names("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"), expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { name: "conflict plural to singular", @@ -206,7 +213,7 @@ func TestSync(t *testing.T) { }, expectedNames: names("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"), expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { name: "merge on conflicts", @@ -248,7 +255,7 @@ func TestSync(t *testing.T) { }, expectedNames: names("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"), expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { name: "no conflicts on self, remove shortname", @@ -264,44 +271,44 @@ func TestSync(t *testing.T) { }, expectedNames: names("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1"), expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { - name: "established before with true condition", - in: newCRD("alfa.bravo.com").Condition(establishedCondition).NewOrDie(), + name: "installing before with true condition", + in: newCRD("alfa.bravo.com").Condition(acceptedCondition).NewOrDie(), existing: []*apiextensions.CustomResourceDefinition{}, expectedNames: apiextensions.CustomResourceDefinitionNames{ Plural: "alfa", }, expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { - name: "not established before with false condition", - in: newCRD("alfa.bravo.com").Condition(notEstablishedCondition).NewOrDie(), + name: "not installing before with false condition", + in: newCRD("alfa.bravo.com").Condition(notAcceptedCondition).NewOrDie(), existing: []*apiextensions.CustomResourceDefinition{}, expectedNames: apiextensions.CustomResourceDefinitionNames{ Plural: "alfa", }, expectedNameConflictCondition: acceptedCondition, - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: installingCondition, }, { - name: "conflicting, established before with true condition", + name: "conflicting, installing before with true condition", in: newCRD("alfa.bravo.com").SpecNames("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"). - Condition(establishedCondition). + Condition(acceptedCondition). NewOrDie(), existing: []*apiextensions.CustomResourceDefinition{ newCRD("india.bravo.com").StatusNames("india", "alfa", "", "").NewOrDie(), }, expectedNames: names("", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"), expectedNameConflictCondition: nameConflictCondition("PluralConflict", `"alfa" is already in use`), - expectedEstablishedCondition: establishedCondition, + expectedEstablishedCondition: notEstablishedCondition, }, { - name: "conflicting, not established before with false condition", + name: "conflicting, not installing before with false condition", in: newCRD("alfa.bravo.com").SpecNames("alfa", "delta-singular", "echo-kind", "foxtrot-listkind", "golf-shortname-1", "hotel-shortname-2"). - Condition(notEstablishedCondition). + Condition(notAcceptedCondition). NewOrDie(), existing: []*apiextensions.CustomResourceDefinition{ newCRD("india.bravo.com").StatusNames("india", "alfa", "", "").NewOrDie(), @@ -322,7 +329,7 @@ func TestSync(t *testing.T) { crdLister: listers.NewCustomResourceDefinitionLister(crdIndexer), crdMutationCache: cache.NewIntegerResourceVersionMutationCache(crdIndexer, crdIndexer, 60*time.Second, false), } - actualNames, actualNameConflictCondition, actualEstablishedCondition := c.calculateNamesAndConditions(tc.in) + actualNames, actualNameConflictCondition, establishedCondition := c.calculateNamesAndConditions(tc.in) if e, a := tc.expectedNames, actualNames; !reflect.DeepEqual(e, a) { t.Errorf("%v expected %v, got %#v", tc.name, e, a) @@ -330,7 +337,7 @@ func TestSync(t *testing.T) { if e, a := tc.expectedNameConflictCondition, actualNameConflictCondition; !apiextensions.IsCRDConditionEquivalent(&e, &a) { t.Errorf("%v expected %v, got %v", tc.name, e, a) } - if e, a := tc.expectedEstablishedCondition, actualEstablishedCondition; !apiextensions.IsCRDConditionEquivalent(&e, &a) { + if e, a := tc.expectedEstablishedCondition, establishedCondition; !apiextensions.IsCRDConditionEquivalent(&e, &a) { t.Errorf("%v expected %v, got %v", tc.name, e, a) } } From 14a03dd646e992c06a3fdfb9bd60f58ef542066e Mon Sep 17 00:00:00 2001 From: Jacob Tanenbaum Date: Tue, 22 May 2018 11:03:47 -0400 Subject: [PATCH 098/416] Modify LoopbackHostPort() so it returns an IPv6 Loopback address when given [::] address Currently when LoopbackHostPort() is called with 0.0.0.0 and [::] it returns the first loopback address returned from net.InterfaceAddrs() which is typically 127.0.0.1 (golang does not specify an order that interfaces are returned). It would be more appropriate if when calling LoopbackHostPort() with [::] that an IPv6 loopback address is returned, this prevents some cert. generation failures. --- staging/src/k8s.io/apiserver/pkg/server/config_selfclient.go | 4 +++- .../k8s.io/apiserver/pkg/server/config_selfclient_test.go | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/staging/src/k8s.io/apiserver/pkg/server/config_selfclient.go b/staging/src/k8s.io/apiserver/pkg/server/config_selfclient.go index bf09161c9fc..eb11dc701b2 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/config_selfclient.go +++ b/staging/src/k8s.io/apiserver/pkg/server/config_selfclient.go @@ -63,6 +63,8 @@ func LoopbackHostPort(bindAddress string) (string, string, error) { return "", "", fmt.Errorf("invalid server bind address: %q", bindAddress) } + isIPv6 := net.ParseIP(host).To4() == nil + // Value is expected to be an IP or DNS name, not "0.0.0.0". if host == "0.0.0.0" || host == "::" { host = "localhost" @@ -72,7 +74,7 @@ func LoopbackHostPort(bindAddress string) (string, string, error) { addrs, err := net.InterfaceAddrs() if err == nil { for _, address := range addrs { - if ipnet, ok := address.(*net.IPNet); ok && ipnet.IP.IsLoopback() { + if ipnet, ok := address.(*net.IPNet); ok && ipnet.IP.IsLoopback() && isIPv6 == (ipnet.IP.To4() == nil) { host = ipnet.IP.String() break } diff --git a/staging/src/k8s.io/apiserver/pkg/server/config_selfclient_test.go b/staging/src/k8s.io/apiserver/pkg/server/config_selfclient_test.go index fcf126b232b..9374403ff9d 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/config_selfclient_test.go +++ b/staging/src/k8s.io/apiserver/pkg/server/config_selfclient_test.go @@ -59,9 +59,10 @@ func TestLoopbackHostPort(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if ip := net.ParseIP(host); ip == nil || !ip.IsLoopback() { - t.Fatalf("expected host to be loopback, got %q", host) + if ip := net.ParseIP(host); ip == nil || !ip.IsLoopback() || ip.To4() != nil { + t.Fatalf("expected IPv6 host to be loopback, got %q", host) } + if port != "443" { t.Fatalf("expected 443 as port, got %q", port) } From b3ce7a9935e5c71225493cbad744bd9a3b2f4875 Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 29 May 2018 08:28:41 -0400 Subject: [PATCH 099/416] services must listen on port 443 --- .../status/available_controller.go | 16 +++++++++++++ .../status/available_controller_test.go | 24 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go index a9bbe08a1fd..367b60dbeb8 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go @@ -176,6 +176,22 @@ func (c *AvailableConditionController) sync(key string) error { } if service.Spec.Type == v1.ServiceTypeClusterIP { + // if we have a cluster IP service, it must be listening on 443 and we can check that + foundPort := false + for _, port := range service.Spec.Ports { + if port.Port == 443 { + foundPort = true + } + } + if !foundPort { + availableCondition.Status = apiregistration.ConditionFalse + availableCondition.Reason = "ServicePortError" + availableCondition.Message = fmt.Sprintf("service/%s in %q is not listening on port 443", apiService.Spec.Service.Name, apiService.Spec.Service.Namespace) + apiregistration.SetAPIServiceCondition(apiService, availableCondition) + _, err := c.apiServiceClient.APIServices().UpdateStatus(apiService) + return err + } + endpoints, err := c.endpointsLister.Endpoints(apiService.Spec.Service.Namespace).Get(apiService.Spec.Service.Name) if apierrors.IsNotFound(err) { availableCondition.Status = apiregistration.ConditionFalse diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go index d0dbff24776..d82a0a0bd39 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller_test.go @@ -55,6 +55,9 @@ func newService(namespace, name string) *v1.Service { ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeClusterIP, + Ports: []v1.ServicePort{ + {Port: 443}, + }, }, } } @@ -110,6 +113,27 @@ func TestSync(t *testing.T) { Message: `service/bar in "foo" is not present`, }, }, + { + name: "service on bad port", + apiServiceName: "remote.group", + apiServices: []*apiregistration.APIService{newRemoteAPIService("remote.group")}, + services: []*v1.Service{{ + ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}, + Spec: v1.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + Ports: []v1.ServicePort{ + {Port: 6443}, + }, + }, + }}, + endpoints: []*v1.Endpoints{newEndpointsWithAddress("foo", "bar")}, + expectedAvailability: apiregistration.APIServiceCondition{ + Type: apiregistration.Available, + Status: apiregistration.ConditionFalse, + Reason: "ServicePortError", + Message: `service/bar in "foo" is not listening on port 443`, + }, + }, { name: "no endpoints", apiServiceName: "remote.group", From cb09607536b178cddc831c6513bdba27b41c61f0 Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 29 May 2018 08:34:47 -0400 Subject: [PATCH 100/416] fix the delete result being used --- pkg/kubectl/cmd/delete.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 10c93998f05..cbe8abd0a28 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -273,7 +273,7 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error { effectiveTimeout = 168 * time.Hour } waitOptions := kubectlwait.WaitOptions{ - ResourceFinder: genericclioptions.ResourceFinderForResult(o.Result), + ResourceFinder: genericclioptions.ResourceFinderForResult(r), DynamicClient: o.DynamicClient, Timeout: effectiveTimeout, From 11f65b2a30be2bfc014c563dc7a8e98ec3ccc1ff Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 25 May 2018 15:49:44 +0200 Subject: [PATCH 101/416] client-go: start fresh with owner file --- staging/src/k8s.io/client-go/OWNERS | 40 ++++------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/staging/src/k8s.io/client-go/OWNERS b/staging/src/k8s.io/client-go/OWNERS index 13af74c628a..b3991682993 100644 --- a/staging/src/k8s.io/client-go/OWNERS +++ b/staging/src/k8s.io/client-go/OWNERS @@ -1,45 +1,15 @@ approvers: - caesarxuchao - deads2k -- krousey - lavalamp +- liggitt - smarterclayton - sttts -- liggitt reviewers: -- thockin -- lavalamp -- smarterclayton -- wojtek-t -- deads2k -- yujuhong -- derekwaynecarr - caesarxuchao -- vishh -- mikedanese +- deads2k +- lavalamp - liggitt -- nikhiljindal -- gmarek -- erictune -- davidopp -- pmorie -- sttts -- dchen1107 -- saad-ali -- zmerlynn -- luxas -- janetkuo -- justinsb -- roberthbailey -- ncdc -- tallclair -- yifan-gu -- eparis -- mwielgus -- timothysc -- feiskyer -- jlowdermilk - soltysh -- piosz -- jsafrane -- awly +- sttts +- yliaog From 9c5bdd4b5c67024412a84e5fb09033cd38d21e9d Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 29 May 2018 10:46:54 -0400 Subject: [PATCH 102/416] add resource builder flags --- pkg/kubectl/cmd/wait/wait.go | 9 +- pkg/kubectl/cmd/wait/wait_test.go | 4 +- .../genericclioptions/builder_flags.go | 98 ++++++++++++++++--- .../genericclioptions/builder_flags_fake.go | 2 +- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/pkg/kubectl/cmd/wait/wait.go b/pkg/kubectl/cmd/wait/wait.go index 9ccf3cb735e..37b5a4b66d1 100644 --- a/pkg/kubectl/cmd/wait/wait.go +++ b/pkg/kubectl/cmd/wait/wait.go @@ -53,9 +53,12 @@ type WaitFlags struct { // NewWaitFlags returns a default WaitFlags func NewWaitFlags(restClientGetter genericclioptions.RESTClientGetter, streams genericclioptions.IOStreams) *WaitFlags { return &WaitFlags{ - RESTClientGetter: restClientGetter, - PrintFlags: genericclioptions.NewPrintFlags("condition met"), - ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags(), + RESTClientGetter: restClientGetter, + PrintFlags: genericclioptions.NewPrintFlags("condition met"), + ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags(). + WithLabelSelector(""). + WithAllNamespaces(false). + WithLatest(), Timeout: 30 * time.Second, diff --git a/pkg/kubectl/cmd/wait/wait_test.go b/pkg/kubectl/cmd/wait/wait_test.go index 77d98e8d459..27446e840f6 100644 --- a/pkg/kubectl/cmd/wait/wait_test.go +++ b/pkg/kubectl/cmd/wait/wait_test.go @@ -219,7 +219,7 @@ func TestWaitForDeletion(t *testing.T) { t.Run(test.name, func(t *testing.T) { fakeClient := test.fakeClient() o := &WaitOptions{ - ResourceFinder: genericclioptions.NewSimpleResourceFinder(test.info), + ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.info), DynamicClient: fakeClient, Timeout: test.timeout, @@ -451,7 +451,7 @@ func TestWaitForCondition(t *testing.T) { t.Run(test.name, func(t *testing.T) { fakeClient := test.fakeClient() o := &WaitOptions{ - ResourceFinder: genericclioptions.NewSimpleResourceFinder(test.info), + ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(test.info), DynamicClient: fakeClient, Timeout: test.timeout, diff --git a/pkg/kubectl/genericclioptions/builder_flags.go b/pkg/kubectl/genericclioptions/builder_flags.go index 4648751c315..ad0ec7a6376 100644 --- a/pkg/kubectl/genericclioptions/builder_flags.go +++ b/pkg/kubectl/genericclioptions/builder_flags.go @@ -18,6 +18,7 @@ package genericclioptions import ( "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" ) @@ -29,8 +30,11 @@ type ResourceBuilderFlags struct { LabelSelector *string FieldSelector *string AllNamespaces *bool + All *bool + Local *bool - All bool + Scheme *runtime.Scheme + Latest bool } // NewResourceBuilderFlags returns a default ResourceBuilderFlags @@ -43,17 +47,54 @@ func NewResourceBuilderFlags() *ResourceBuilderFlags { Filenames: &filenames, Recursive: boolPtr(true), }, - - LabelSelector: strPtr(""), - AllNamespaces: boolPtr(false), } } +func (o *ResourceBuilderFlags) WithFile(recurse bool, files ...string) *ResourceBuilderFlags { + o.FileNameFlags = &FileNameFlags{ + Usage: "identifying the resource.", + Filenames: &files, + Recursive: boolPtr(recurse), + } + + return o +} + +func (o *ResourceBuilderFlags) WithLabelSelector(selector string) *ResourceBuilderFlags { + o.LabelSelector = &selector + return o +} + func (o *ResourceBuilderFlags) WithFieldSelector(selector string) *ResourceBuilderFlags { o.FieldSelector = &selector return o } +func (o *ResourceBuilderFlags) WithAllNamespaces(defaultVal bool) *ResourceBuilderFlags { + o.AllNamespaces = &defaultVal + return o +} + +func (o *ResourceBuilderFlags) WithAll(defaultVal bool) *ResourceBuilderFlags { + o.All = &defaultVal + return o +} + +func (o *ResourceBuilderFlags) WithLocal(defaultVal bool) *ResourceBuilderFlags { + o.Local = &defaultVal + return o +} + +func (o *ResourceBuilderFlags) WithScheme(scheme *runtime.Scheme) *ResourceBuilderFlags { + o.Scheme = scheme + return o +} + +func (o *ResourceBuilderFlags) WithLatest() *ResourceBuilderFlags { + o.Latest = true + return o +} + // AddFlags registers flags for finding resources func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { o.FileNameFlags.AddFlags(flagset) @@ -67,6 +108,12 @@ func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { if o.AllNamespaces != nil { flagset.BoolVar(o.AllNamespaces, "all-namespaces", *o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") } + if o.All != nil { + flagset.BoolVar(o.All, "all", *o.All, "Select all resources in the namespace of the specified resource types") + } + if o.Local != nil { + flagset.BoolVar(o.Local, "local", *o.Local, "If true, annotation will NOT contact api-server but run locally.") + } } // ToBuilder gives you back a resource finder to visit resources that are located @@ -74,24 +121,45 @@ func (o *ResourceBuilderFlags) ToBuilder(restClientGetter RESTClientGetter, reso namespace, enforceNamespace, namespaceErr := restClientGetter.ToRawKubeConfigLoader().Namespace() builder := resource.NewBuilder(restClientGetter). - Unstructured(). - NamespaceParam(namespace).DefaultNamespace(). - ResourceTypeOrNameArgs(o.All, resources...) + NamespaceParam(namespace).DefaultNamespace() + + if o.Scheme != nil { + builder.WithScheme(o.Scheme, o.Scheme.PrioritizedVersionsAllGroups()...) + } else { + builder.Unstructured() + } + if o.FileNameFlags != nil { opts := o.FileNameFlags.ToOptions() - builder = builder.FilenameParam(enforceNamespace, &opts) + builder.FilenameParam(enforceNamespace, &opts) } - if o.LabelSelector != nil { - builder = builder.LabelSelectorParam(*o.LabelSelector) - } - if o.FieldSelector != nil { - builder = builder.FieldSelectorParam(*o.FieldSelector) + + if o.Local == nil || !*o.Local { + // resource type/name tuples only work non-local + if o.All != nil { + builder.ResourceTypeOrNameArgs(*o.All, resources...) + } else { + builder.ResourceTypeOrNameArgs(false, resources...) + } + // label selectors only work non-local (for now) + if o.LabelSelector != nil { + builder.LabelSelectorParam(*o.LabelSelector) + } + // field selectors only work non-local (forever) + if o.FieldSelector != nil { + builder.FieldSelectorParam(*o.FieldSelector) + } + // latest only works non-local (forever) + if o.Latest { + builder.Latest() + } + } else { + builder.Local() } return &ResourceFindBuilderWrapper{ builder: builder. - Latest(). - Flatten(). + Flatten(). // I think we're going to recommend this everywhere AddError(namespaceErr), } } diff --git a/pkg/kubectl/genericclioptions/builder_flags_fake.go b/pkg/kubectl/genericclioptions/builder_flags_fake.go index 15137c9e797..de968d8e5d7 100644 --- a/pkg/kubectl/genericclioptions/builder_flags_fake.go +++ b/pkg/kubectl/genericclioptions/builder_flags_fake.go @@ -21,7 +21,7 @@ import ( ) // NewSimpleResourceFinder builds a super simple ResourceFinder that just iterates over the objects you provided -func NewSimpleResourceFinder(infos ...*resource.Info) ResourceFinder { +func NewSimpleFakeResourceFinder(infos ...*resource.Info) ResourceFinder { return &fakeResourceFinder{ Infos: infos, } From b48f23b786f026edb407c27866c667df2809fe4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 29 May 2018 17:51:39 +0300 Subject: [PATCH 103/416] kubeadm: Move .NodeName and .CRISocket to a common sub-struct --- cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go | 18 +++- cmd/kubeadm/app/apis/kubeadm/types.go | 51 +++++---- .../app/apis/kubeadm/v1alpha1/conversion.go | 63 +++++++++++ .../app/apis/kubeadm/v1alpha2/defaults.go | 16 +-- .../app/apis/kubeadm/v1alpha2/types.go | 45 +++++--- .../app/apis/kubeadm/validation/validation.go | 20 +++- cmd/kubeadm/app/cmd/config.go | 2 +- cmd/kubeadm/app/cmd/init.go | 18 ++-- cmd/kubeadm/app/cmd/join.go | 14 ++- cmd/kubeadm/app/cmd/phases/kubeconfig.go | 2 +- cmd/kubeadm/app/cmd/phases/kubelet.go | 5 +- cmd/kubeadm/app/cmd/phases/markmaster.go | 4 +- cmd/kubeadm/app/constants/constants.go | 7 +- .../app/phases/certs/pkiutil/pki_helpers.go | 4 +- .../app/phases/kubeconfig/kubeconfig.go | 2 +- cmd/kubeadm/app/phases/kubelet/config.go | 17 +-- cmd/kubeadm/app/phases/kubelet/dynamic.go | 71 ++++-------- cmd/kubeadm/app/phases/kubelet/flags.go | 23 ++-- .../app/phases/markmaster/markmaster.go | 101 +++--------------- .../app/phases/selfhosting/selfhosting.go | 2 +- cmd/kubeadm/app/phases/upgrade/staticpods.go | 6 +- cmd/kubeadm/app/preflight/checks.go | 2 +- cmd/kubeadm/app/util/apiclient/idempotency.go | 52 +++++++++ cmd/kubeadm/app/util/config/masterconfig.go | 8 +- cmd/kubeadm/app/util/config/nodeconfig.go | 2 +- cmd/kubeadm/test/util.go | 7 +- 26 files changed, 309 insertions(+), 253 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 2ede4cb22ba..b640d6e533e 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -21,6 +21,7 @@ import ( fuzz "github.com/google/gofuzz" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -43,7 +44,6 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.APIServerCertSANs = []string{"foo"} obj.Token = "foo" - obj.CRISocket = "foo" obj.TokenTTL = &metav1.Duration{Duration: 1 * time.Hour} obj.TokenUsages = []string{"foo"} obj.TokenGroups = []string{"foo"} @@ -59,6 +59,9 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { MountPath: "foo", Writable: false, }} + // Note: We don't set values here for obj.Etcd.External, as these are mutually exlusive. + // And to make sure the fuzzer doesn't set a random value for obj.Etcd.External, we let + // kubeadmapi.Etcd implement fuzz.Interface (we handle that ourselves) obj.Etcd.Local = &kubeadm.LocalEtcd{ Image: "foo", DataDir: "foo", @@ -66,9 +69,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { PeerCertSANs: []string{"foo"}, ExtraArgs: map[string]string{"foo": "foo"}, } - // Note: We don't set values here for obj.Etcd.External, as these are mutually exlusive. - // And to make sure the fuzzer doesn't set a random value for obj.Etcd.External, we let - // kubeadmapi.Etcd implement fuzz.Interface (we handle that ourselves) + obj.NodeRegistration = kubeadm.NodeRegistrationOptions{ + CRISocket: "foo", + Name: "foo", + Taints: []v1.Taint{}, + } obj.KubeletConfiguration = kubeadm.KubeletConfiguration{ BaseConfig: &kubeletconfigv1beta1.KubeletConfiguration{ StaticPodPath: "foo", @@ -139,8 +144,11 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.DiscoveryTimeout = &metav1.Duration{Duration: 1} obj.TLSBootstrapToken = "foo" obj.Token = "foo" - obj.CRISocket = "foo" obj.ClusterName = "foo" + obj.NodeRegistration = kubeadm.NodeRegistrationOptions{ + CRISocket: "foo", + Name: "foo", + } }, } } diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index ea062c62184..f83d63e9bd6 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -44,13 +44,9 @@ type MasterConfiguration struct { Networking Networking // KubernetesVersion is the target version of the control plane. KubernetesVersion string - // NodeName is the name of the node that will host the k8s control plane. - // Defaults to the hostname if not provided. - NodeName string - // NoTaintMaster will, if set, suppress the tainting of the - // master node allowing workloads to be run on it (e.g. in - // single node configurations). - NoTaintMaster bool + + // NodeRegistration holds fields that relate to registering the new master node to the cluster + NodeRegistration NodeRegistrationOptions // Token is used for establishing bidirectional trust between nodes and masters. // Used for joining nodes in the cluster. @@ -62,9 +58,6 @@ type MasterConfiguration struct { // Extra groups that this token will authenticate as when used for authentication TokenGroups []string - // CRISocket is used to retrieve container runtime info. - CRISocket string - // APIServerExtraArgs is a set of extra flags to pass to the API Server or override // default ones in form of =. // TODO: This is temporary and ideally we would like to switch all components to @@ -138,6 +131,28 @@ type API struct { BindPort int32 } +// NodeRegistrationOptions holds fields that relate to registering a new master or node to the cluster, either via "kubeadm init" or "kubeadm join" +type NodeRegistrationOptions struct { + + // Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm joiń` operation. + // This field is also used in the CommonName field of the kubelet's client certificate to the API server. + // Defaults to the hostname of the node if not provided. + Name string + + // CRISocket is used to retrieve container runtime info. This information will be annotated to the Node API object, for later re-use + CRISocket string + + // Taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, in the `kubeadm init` process + // it will be defaulted to []v1.Taint{'node-role.kubernetes.io/master=""'}. If you don't want to taint your master node, set this field to an + // empty slice, i.e. `taints: {}` in the YAML file. This field is solely used for Node registration. + Taints []v1.Taint + + // ExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file + // kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap + // Flags have higher higher priority when parsing. These values are local and specific to the node kubeadm is executing on. + ExtraArgs map[string]string +} + // TokenDiscovery contains elements needed for token discovery. type TokenDiscovery struct { // ID is the first part of a bootstrap token. Considered public information. @@ -223,6 +238,9 @@ type ExternalEtcd struct { type NodeConfiguration struct { metav1.TypeMeta + // NodeRegistration holds fields that relate to registering the new master node to the cluster + NodeRegistration NodeRegistrationOptions + // CACertPath is the path to the SSL certificate authority used to // secure comunications between node and master. // Defaults to "/etc/kubernetes/pki/ca.crt". @@ -239,16 +257,11 @@ type NodeConfiguration struct { DiscoveryTokenAPIServers []string // DiscoveryTimeout modifies the discovery timeout DiscoveryTimeout *metav1.Duration - // NodeName is the name of the node to join the cluster. Defaults - // to the name of the host. - NodeName string // TLSBootstrapToken is a token used for TLS bootstrapping. // Defaults to Token. TLSBootstrapToken string // Token is used for both discovery and TLS bootstrapping. Token string - // CRISocket is used to retrieve container runtime info. - CRISocket string // The cluster name ClusterName string @@ -332,13 +345,13 @@ type CommonConfiguration interface { // GetCRISocket will return the CRISocket that is defined for the MasterConfiguration. // This is used internally to deduplicate the kubeadm preflight checks. func (cfg *MasterConfiguration) GetCRISocket() string { - return cfg.CRISocket + return cfg.NodeRegistration.CRISocket } // GetNodeName will return the NodeName that is defined for the MasterConfiguration. // This is used internally to deduplicate the kubeadm preflight checks. func (cfg *MasterConfiguration) GetNodeName() string { - return cfg.NodeName + return cfg.NodeRegistration.Name } // GetKubernetesVersion will return the KubernetesVersion that is defined for the MasterConfiguration. @@ -350,13 +363,13 @@ func (cfg *MasterConfiguration) GetKubernetesVersion() string { // GetCRISocket will return the CRISocket that is defined for the NodeConfiguration. // This is used internally to deduplicate the kubeadm preflight checks. func (cfg *NodeConfiguration) GetCRISocket() string { - return cfg.CRISocket + return cfg.NodeRegistration.CRISocket } // GetNodeName will return the NodeName that is defined for the NodeConfiguration. // This is used internally to deduplicate the kubeadm preflight checks. func (cfg *NodeConfiguration) GetNodeName() string { - return cfg.NodeName + return cfg.NodeRegistration.Name } // GetKubernetesVersion will return an empty string since KubernetesVersion is not a diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/conversion.go index d5492c3333a..54a598af1c9 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/conversion.go @@ -20,15 +20,20 @@ import ( "reflect" "strings" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + "k8s.io/kubernetes/cmd/kubeadm/app/constants" ) func addConversionFuncs(scheme *runtime.Scheme) error { // Add non-generated conversion functions err := scheme.AddConversionFuncs( Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration, + Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration, + Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration, + Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration, Convert_v1alpha1_Etcd_To_kubeadm_Etcd, Convert_kubeadm_Etcd_To_v1alpha1_Etcd, ) @@ -39,6 +44,8 @@ func addConversionFuncs(scheme *runtime.Scheme) error { return nil } +// Upgrades below + func Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in *MasterConfiguration, out *kubeadm.MasterConfiguration, s conversion.Scope) error { if err := autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in, out, s); err != nil { return err @@ -46,12 +53,26 @@ func Convert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in *Mas UpgradeCloudProvider(in, out) UpgradeAuthorizationModes(in, out) + UpgradeNodeRegistrationOptionsForMaster(in, out) // We don't support migrating information from the .PrivilegedPods field which was removed in v1alpha2 // We don't support migrating information from the .ImagePullPolicy field which was removed in v1alpha2 return nil } +func Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeConfiguration, out *kubeadm.NodeConfiguration, s conversion.Scope) error { + if err := autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in, out, s); err != nil { + return err + } + + // .NodeName has moved to .NodeRegistration.Name + out.NodeRegistration.Name = in.NodeName + // .CRISocket has moved to .NodeRegistration.CRISocket + out.NodeRegistration.CRISocket = in.CRISocket + + return nil +} + func Convert_v1alpha1_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s conversion.Scope) error { if err := autoConvert_v1alpha1_Etcd_To_kubeadm_Etcd(in, out, s); err != nil { return err @@ -123,3 +144,45 @@ func UpgradeAuthorizationModes(in *MasterConfiguration, out *kubeadm.MasterConfi out.APIServerExtraArgs["authorization-mode"] = strings.Join(in.AuthorizationModes, ",") } } + +func UpgradeNodeRegistrationOptionsForMaster(in *MasterConfiguration, out *kubeadm.MasterConfiguration) { + // .NodeName has moved to .NodeRegistration.Name + out.NodeRegistration.Name = in.NodeName + + // .CRISocket has moved to .NodeRegistration.CRISocket + out.NodeRegistration.CRISocket = in.CRISocket + + // Transfer the information from .NoTaintMaster to the new layout + if in.NoTaintMaster { + out.NodeRegistration.Taints = []v1.Taint{} + } else { + out.NodeRegistration.Taints = []v1.Taint{constants.MasterTaint} + } +} + +// Downgrades below + +func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error { + if err := autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in, out, s); err != nil { + return err + } + + // Converting from newer API version to an older API version isn't supported. This is here only for the roundtrip tests meanwhile. + out.NodeName = in.NodeRegistration.Name + out.CRISocket = in.NodeRegistration.CRISocket + out.NoTaintMaster = in.NodeRegistration.Taints != nil && len(in.NodeRegistration.Taints) == 0 + + return nil +} + +func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error { + if err := autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in, out, s); err != nil { + return err + } + + // Converting from newer API version to an older API version isn't supported. This is here only for the roundtrip tests meanwhile. + out.NodeName = in.NodeRegistration.Name + out.CRISocket = in.NodeRegistration.CRISocket + + return nil +} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/defaults.go index f9c4000198f..d9467e27f87 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/defaults.go @@ -103,10 +103,6 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) { } } - if obj.CRISocket == "" { - obj.CRISocket = DefaultCRISocket - } - if len(obj.TokenUsages) == 0 { obj.TokenUsages = constants.DefaultTokenUsages } @@ -123,6 +119,7 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) { obj.ClusterName = DefaultClusterName } + SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration) SetDefaults_KubeletConfiguration(obj) SetDefaults_Etcd(obj) SetDefaults_ProxyConfiguration(obj) @@ -168,9 +165,6 @@ func SetDefaults_NodeConfiguration(obj *NodeConfiguration) { if len(obj.DiscoveryToken) == 0 && len(obj.DiscoveryFile) == 0 { obj.DiscoveryToken = obj.Token } - if obj.CRISocket == "" { - obj.CRISocket = DefaultCRISocket - } // Make sure file URLs become paths if len(obj.DiscoveryFile) != 0 { u, err := url.Parse(obj.DiscoveryFile) @@ -186,6 +180,8 @@ func SetDefaults_NodeConfiguration(obj *NodeConfiguration) { if obj.ClusterName == "" { obj.ClusterName = DefaultClusterName } + + SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration) } // SetDefaults_KubeletConfiguration assigns default values to kubelet @@ -237,6 +233,12 @@ func SetDefaults_KubeletConfiguration(obj *MasterConfiguration) { } } +func SetDefaults_NodeRegistrationOptions(obj *NodeRegistrationOptions) { + if obj.CRISocket == "" { + obj.CRISocket = DefaultCRISocket + } +} + // SetDefaults_AuditPolicyConfiguration sets default values for the AuditPolicyConfiguration func SetDefaults_AuditPolicyConfiguration(obj *MasterConfiguration) { if obj.AuditPolicyConfiguration.LogDir == "" { diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/types.go index dd48f2b9277..f1ec6f51079 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/types.go @@ -40,15 +40,12 @@ type MasterConfiguration struct { KubeletConfiguration KubeletConfiguration `json:"kubeletConfiguration"` // Networking holds configuration for the networking topology of the cluster. Networking Networking `json:"networking"` + + // NodeRegistration holds fields that relate to registering the new master node to the cluster + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration"` + // KubernetesVersion is the target version of the control plane. KubernetesVersion string `json:"kubernetesVersion"` - // NodeName is the name of the node that will host the k8s control plane. - // Defaults to the hostname if not provided. - NodeName string `json:"nodeName"` - // NoTaintMaster will, if set, suppress the tainting of the - // master node allowing workloads to be run on it (e.g. in - // single node configurations). - NoTaintMaster bool `json:"noTaintMaster,omitempty"` // Token is used for establishing bidirectional trust between nodes and masters. // Used for joining nodes in the cluster. @@ -60,9 +57,6 @@ type MasterConfiguration struct { // Extra groups that this token will authenticate as when used for authentication TokenGroups []string `json:"tokenGroups,omitempty"` - // CRISocket is used to retrieve container runtime info. - CRISocket string `json:"criSocket,omitempty"` - // APIServerExtraArgs is a set of extra flags to pass to the API Server or override // default ones in form of =. // TODO: This is temporary and ideally we would like to switch all components to @@ -129,6 +123,28 @@ type API struct { BindPort int32 `json:"bindPort"` } +// NodeRegistrationOptions holds fields that relate to registering a new master or node to the cluster, either via "kubeadm init" or "kubeadm join" +type NodeRegistrationOptions struct { + + // Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm joiń` operation. + // This field is also used in the CommonName field of the kubelet's client certificate to the API server. + // Defaults to the hostname of the node if not provided. + Name string `json:"name"` + + // CRISocket is used to retrieve container runtime info. This information will be annotated to the Node API object, for later re-use + CRISocket string `json:"criSocket"` + + // Taints specifies the taints the Node API object should be registered with. If this field is unset, i.e. nil, in the `kubeadm init` process + // it will be defaulted to []v1.Taint{'node-role.kubernetes.io/master=""'}. If you don't want to taint your master node, set this field to an + // empty slice, i.e. `taints: {}` in the YAML file. This field is solely used for Node registration. + Taints []v1.Taint `json:"taints,omitempty"` + + // ExtraArgs passes through extra arguments to the kubelet. The arguments here are passed to the kubelet command line via the environment file + // kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap + // Flags have higher higher priority when parsing. These values are local and specific to the node kubeadm is executing on. + ExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` +} + // TokenDiscovery contains elements needed for token discovery. type TokenDiscovery struct { // ID is the first part of a bootstrap token. Considered public information. @@ -206,6 +222,9 @@ type ExternalEtcd struct { type NodeConfiguration struct { metav1.TypeMeta `json:",inline"` + // NodeRegistration holds fields that relate to registering the new master node to the cluster + NodeRegistration NodeRegistrationOptions `json:"nodeRegistration"` + // CACertPath is the path to the SSL certificate authority used to // secure comunications between node and master. // Defaults to "/etc/kubernetes/pki/ca.crt". @@ -222,16 +241,12 @@ type NodeConfiguration struct { DiscoveryTokenAPIServers []string `json:"discoveryTokenAPIServers,omitempty"` // DiscoveryTimeout modifies the discovery timeout DiscoveryTimeout *metav1.Duration `json:"discoveryTimeout,omitempty"` - // NodeName is the name of the node to join the cluster. Defaults - // to the name of the host. - NodeName string `json:"nodeName"` // TLSBootstrapToken is a token used for TLS bootstrapping. // Defaults to Token. TLSBootstrapToken string `json:"tlsBootstrapToken"` // Token is used for both discovery and TLS bootstrapping. Token string `json:"token"` - // CRISocket is used to retrieve container runtime info. - CRISocket string `json:"criSocket,omitempty"` + // ClusterName is the name for the cluster in kubeconfig. ClusterName string `json:"clusterName,omitempty"` diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 116bfce6c8d..f51f539ac8f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -54,7 +54,7 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList allErrs = append(allErrs, ValidateNetworking(&c.Networking, field.NewPath("networking"))...) allErrs = append(allErrs, ValidateCertSANs(c.APIServerCertSANs, field.NewPath("apiServerCertSANs"))...) allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificatesDir"))...) - allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("nodeName"))...) + allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...) allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...) allErrs = append(allErrs, ValidateTokenUsages(c.TokenUsages, field.NewPath("tokenUsages"))...) allErrs = append(allErrs, ValidateTokenGroups(c.TokenUsages, c.TokenGroups, field.NewPath("tokenGroups"))...) @@ -62,9 +62,7 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList allErrs = append(allErrs, ValidateAPIEndpoint(&c.API, field.NewPath("api"))...) allErrs = append(allErrs, ValidateProxy(c.KubeProxy.Config, field.NewPath("kubeProxy").Child("config"))...) allErrs = append(allErrs, ValidateEtcd(&c.Etcd, field.NewPath("etcd"))...) - if features.Enabled(c.FeatureGates, features.DynamicKubeletConfig) { - allErrs = append(allErrs, ValidateKubeletConfiguration(&c.KubeletConfiguration, field.NewPath("kubeletConfiguration"))...) - } + allErrs = append(allErrs, ValidateKubeletConfiguration(&c.KubeletConfiguration, field.NewPath("kubeletConfiguration"))...) return allErrs } @@ -86,6 +84,7 @@ func ValidateProxy(c *kubeproxyconfigv1alpha1.KubeProxyConfiguration, fldPath *f func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, ValidateDiscovery(c)...) + allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...) if !filepath.IsAbs(c.CACertPath) || !strings.HasSuffix(c.CACertPath, ".crt") { allErrs = append(allErrs, field.Invalid(field.NewPath("caCertPath"), c.CACertPath, "the ca certificate path must be an absolute path")) @@ -93,6 +92,15 @@ func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList { return allErrs } +// ValidateNodeRegistrationOptions validates the NodeRegistrationOptions object +func ValidateNodeRegistrationOptions(nro *kubeadm.NodeRegistrationOptions, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, ValidateNodeName(nro.Name, fldPath.Child("name"))...) + allErrs = append(allErrs, ValidateAbsolutePath(nro.CRISocket, fldPath.Child("criSocket"))...) + // TODO: Maybe validate .Taints as well in the future using something like validateNodeTaints() in pkg/apis/core/validation + return allErrs +} + // ValidateDiscovery validates discovery related configuration and collects all encountered errors func ValidateDiscovery(c *kubeadm.NodeConfiguration) field.ErrorList { allErrs := field.ErrorList{} @@ -422,6 +430,10 @@ func ValidateIgnorePreflightErrors(ignorePreflightErrors []string, skipPreflight func ValidateKubeletConfiguration(c *kubeadm.KubeletConfiguration, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} + if c.BaseConfig == nil { + return allErrs + } + scheme, _, err := kubeletscheme.NewSchemeAndCodecs() if err != nil { allErrs = append(allErrs, field.Invalid(fldPath, "kubeletConfiguration", err.Error())) diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index 62f89e92788..8b45f8407fe 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -401,5 +401,5 @@ func AddImagesCommonConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.M // AddImagesPullFlags adds flags related to the `kubeadm config images pull` command func AddImagesPullFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.MasterConfiguration) { - flagSet.StringVar(&cfg.CRISocket, "cri-socket-path", cfg.CRISocket, "Path to the CRI socket.") + flagSet.StringVar(&cfg.NodeRegistration.CRISocket, "cri-socket-path", cfg.NodeRegistration.CRISocket, "Path to the CRI socket.") } diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 50d3beaa62a..68df174b7b9 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -189,7 +189,7 @@ func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.MasterCon `Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names.`, ) flagSet.StringVar( - &cfg.NodeName, "node-name", cfg.NodeName, + &cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, `Specify the node name.`, ) flagSet.StringVar( @@ -201,7 +201,7 @@ func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.MasterCon "The duration before the bootstrap token is automatically deleted. If set to '0', the token will never expire.", ) flagSet.StringVar( - &cfg.CRISocket, "cri-socket", cfg.CRISocket, + &cfg.NodeRegistration.CRISocket, "cri-socket", cfg.NodeRegistration.CRISocket, `Specify the CRI socket to connect to.`, ) flagSet.StringVar(featureGatesString, "feature-gates", *featureGatesString, "A set of key=value pairs that describe feature gates for various features. "+ @@ -276,8 +276,10 @@ type Init struct { // Run executes master node provisioning, including certificates, needed static pod manifests, etc. func (i *Init) Run(out io.Writer) error { - // Write env file with flags for the kubelet to use - if err := kubeletphase.WriteKubeletDynamicEnvFile(i.cfg); err != nil { + // Write env file with flags for the kubelet to use. We do not need to write the --register-with-taints for the master, + // as we handle that ourselves in the markmaster phase + // TODO: Maybe we want to do that some time in the future, in order to remove some logic from the markmaster phase? + if err := kubeletphase.WriteKubeletDynamicEnvFile(&i.cfg.NodeRegistration, false); err != nil { return err } @@ -362,7 +364,7 @@ func (i *Init) Run(out io.Writer) error { } // Write the kubelet configuration to disk. - if err := kubeletphase.WriteConfigToDisk(i.cfg.KubeletConfiguration.BaseConfig, kubeletVersion); err != nil { + if err := kubeletphase.WriteConfigToDisk(i.cfg.KubeletConfiguration.BaseConfig); err != nil { return fmt.Errorf("error writing kubelet configuration to disk: %v", err) } @@ -411,7 +413,7 @@ func (i *Init) Run(out io.Writer) error { // PHASE 4: Mark the master with the right label/taint glog.V(1).Infof("[init] marking the master with right label") - if err := markmasterphase.MarkMaster(client, i.cfg.NodeName, !i.cfg.NoTaintMaster); err != nil { + if err := markmasterphase.MarkMaster(client, i.cfg.NodeRegistration.Name, i.cfg.NodeRegistration.Taints); err != nil { return fmt.Errorf("error marking master: %v", err) } @@ -419,7 +421,7 @@ func (i *Init) Run(out io.Writer) error { // This feature is disabled by default, as it is alpha still if features.Enabled(i.cfg.FeatureGates, features.DynamicKubeletConfig) { // Enable dynamic kubelet configuration for the node. - if err := kubeletphase.EnableDynamicConfigForNode(client, i.cfg.NodeName, kubeletVersion); err != nil { + if err := kubeletphase.EnableDynamicConfigForNode(client, i.cfg.NodeRegistration.Name, kubeletVersion); err != nil { return fmt.Errorf("error enabling dynamic kubelet configuration: %v", err) } } @@ -507,7 +509,7 @@ func (i *Init) Run(out io.Writer) error { func createClient(cfg *kubeadmapi.MasterConfiguration, dryRun bool) (clientset.Interface, error) { if dryRun { // If we're dry-running; we should create a faked client that answers some GETs in order to be able to do the full init flow and just logs the rest of requests - dryRunGetter := apiclient.NewInitDryRunGetter(cfg.NodeName, cfg.Networking.ServiceSubnet) + dryRunGetter := apiclient.NewInitDryRunGetter(cfg.NodeRegistration.Name, cfg.Networking.ServiceSubnet) return apiclient.NewDryRunClient(dryRunGetter, os.Stdout), nil } diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 77fcb6697ca..0a3239fe4e0 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -155,7 +155,7 @@ func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.NodeConfi &cfg.DiscoveryToken, "discovery-token", "", "A token used to validate cluster information fetched from the master.") flagSet.StringVar( - &cfg.NodeName, "node-name", "", + &cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, "Specify the node name.") flagSet.StringVar( &cfg.TLSBootstrapToken, "tls-bootstrap-token", "", @@ -174,7 +174,7 @@ func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1alpha2.NodeConfi "A set of key=value pairs that describe feature gates for various features. "+ "Options are:\n"+strings.Join(features.KnownFeatures(&features.InitFeatureGates), "\n")) flagSet.StringVar( - &cfg.CRISocket, "cri-socket", cfg.CRISocket, + &cfg.NodeRegistration.CRISocket, "cri-socket", cfg.NodeRegistration.CRISocket, `Specify the CRI socket to connect to.`, ) } @@ -204,7 +204,7 @@ type Join struct { // NewJoin instantiates Join struct with given arguments func NewJoin(cfgPath string, args []string, defaultcfg *kubeadmapiv1alpha2.NodeConfiguration, ignorePreflightErrors sets.String) (*Join, error) { - if defaultcfg.NodeName == "" { + if defaultcfg.NodeRegistration.Name == "" { glog.V(1).Infoln("[join] found NodeName empty") glog.V(1).Infoln("[join] considered OS hostname as NodeName") } @@ -231,6 +231,12 @@ func NewJoin(cfgPath string, args []string, defaultcfg *kubeadmapiv1alpha2.NodeC // Run executes worker node provisioning and tries to join an existing cluster. func (j *Join) Run(out io.Writer) error { + + // Write env file with flags for the kubelet to use. Also register taints + if err := kubeletphase.WriteKubeletDynamicEnvFile(&j.cfg.NodeRegistration, true); err != nil { + return err + } + glog.V(1).Infoln("[join] retrieving KubeConfig objects") cfg, err := discovery.For(j.cfg) if err != nil { @@ -273,7 +279,7 @@ func (j *Join) Run(out io.Writer) error { return err } - if err := kubeletphase.EnableDynamicConfigForNode(client, j.cfg.NodeName, kubeletVersion); err != nil { + if err := kubeletphase.EnableDynamicConfigForNode(client, j.cfg.NodeRegistration.Name, kubeletVersion); err != nil { return fmt.Errorf("error consuming base kubelet configuration: %v", err) } } diff --git a/cmd/kubeadm/app/cmd/phases/kubeconfig.go b/cmd/kubeadm/app/cmd/phases/kubeconfig.go index 3f3e185299d..7a0dcb0ce5b 100644 --- a/cmd/kubeadm/app/cmd/phases/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/phases/kubeconfig.go @@ -184,7 +184,7 @@ func getKubeConfigSubCommands(out io.Writer, outDir, defaultKubernetesVersion st cmd.Flags().Int32Var(&cfg.API.BindPort, "apiserver-bind-port", cfg.API.BindPort, "The port the API server is accessible on") cmd.Flags().StringVar(&outDir, "kubeconfig-dir", outDir, "The path where to save the kubeconfig file") if properties.use == "all" || properties.use == "kubelet" { - cmd.Flags().StringVar(&cfg.NodeName, "node-name", cfg.NodeName, `The node name that should be used for the kubelet client certificate`) + cmd.Flags().StringVar(&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, `The node name that should be used for the kubelet client certificate`) } if properties.use == "user" { cmd.Flags().StringVar(&token, "token", token, "The token that should be used as the authentication mechanism for this kubeconfig, instead of client certificates") diff --git a/cmd/kubeadm/app/cmd/phases/kubelet.go b/cmd/kubeadm/app/cmd/phases/kubelet.go index c07257d9b7f..bd979d60492 100644 --- a/cmd/kubeadm/app/cmd/phases/kubelet.go +++ b/cmd/kubeadm/app/cmd/phases/kubelet.go @@ -134,9 +134,6 @@ func NewCmdKubeletWriteConfigToDisk(kubeConfigFile *string) *cobra.Command { kubeadmutil.CheckErr(fmt.Errorf("The --kubelet-version argument is required")) } - kubeletVersion, err := version.ParseSemantic(kubeletVersionStr) - kubeadmutil.CheckErr(err) - client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile) kubeadmutil.CheckErr(err) @@ -144,7 +141,7 @@ func NewCmdKubeletWriteConfigToDisk(kubeConfigFile *string) *cobra.Command { internalcfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "kubelet", cfgPath) kubeadmutil.CheckErr(err) - err = kubeletphase.WriteConfigToDisk(internalcfg.KubeletConfiguration.BaseConfig, kubeletVersion) + err = kubeletphase.WriteConfigToDisk(internalcfg.KubeletConfiguration.BaseConfig) kubeadmutil.CheckErr(err) }, } diff --git a/cmd/kubeadm/app/cmd/phases/markmaster.go b/cmd/kubeadm/app/cmd/phases/markmaster.go index 5a2f11ddbb5..13ec0a314ce 100644 --- a/cmd/kubeadm/app/cmd/phases/markmaster.go +++ b/cmd/kubeadm/app/cmd/phases/markmaster.go @@ -75,14 +75,14 @@ func NewCmdMarkMaster() *cobra.Command { client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile) kubeadmutil.CheckErr(err) - err = markmasterphase.MarkMaster(client, internalcfg.NodeName, !internalcfg.NoTaintMaster) + err = markmasterphase.MarkMaster(client, internalcfg.NodeRegistration.Name, internalcfg.NodeRegistration.Taints) kubeadmutil.CheckErr(err) }, } cmd.Flags().StringVar(&kubeConfigFile, "kubeconfig", "/etc/kubernetes/admin.conf", "The KubeConfig file to use when talking to the cluster") cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file. WARNING: Usage of a configuration file is experimental") - cmd.Flags().StringVar(&cfg.NodeName, "node-name", cfg.NodeName, `The node name to which label and taints should apply`) + cmd.Flags().StringVar(&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, `The node name to which label and taints should apply`) return cmd } diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index afd5ce5ba5c..3a6f4233f4f 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -165,8 +165,8 @@ const ( APICallRetryInterval = 500 * time.Millisecond // DiscoveryRetryInterval specifies how long kubeadm should wait before retrying to connect to the master when doing discovery DiscoveryRetryInterval = 5 * time.Second - // MarkMasterTimeout specifies how long kubeadm should wait for applying the label and taint on the master before timing out - MarkMasterTimeout = 2 * time.Minute + // PatchNodeTimeout specifies how long kubeadm should wait for applying the label and taint on the master before timing out + PatchNodeTimeout = 2 * time.Minute // UpdateNodeTimeout specifies how long kubeadm should wait for updating node with the initial remote configuration of kubelet before timing out UpdateNodeTimeout = 2 * time.Minute @@ -295,9 +295,6 @@ var ( // MinimumKubeletVersion specifies the minimum version of kubelet which kubeadm supports MinimumKubeletVersion = version.MustParseSemantic("v1.10.0") - // MinimumKubeletConfigVersion specifies the minimum version of Kubernetes where kubeadm supports specifying --config to the kubelet - MinimumKubeletConfigVersion = version.MustParseSemantic("v1.11.0-alpha.1") - // SupportedEtcdVersion lists officially supported etcd versions with corresponding kubernetes releases SupportedEtcdVersion = map[uint8]string{ 10: "3.1.12", diff --git a/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go b/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go index 39ca139cba7..337656afd73 100644 --- a/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go +++ b/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go @@ -275,7 +275,7 @@ func GetAPIServerAltNames(cfg *kubeadmapi.MasterConfiguration) (*certutil.AltNam // create AltNames with defaults DNSNames/IPs altNames := &certutil.AltNames{ DNSNames: []string{ - cfg.NodeName, + cfg.NodeRegistration.Name, "kubernetes", "kubernetes.default", "kubernetes.default.svc", @@ -336,7 +336,7 @@ func GetEtcdPeerAltNames(cfg *kubeadmapi.MasterConfiguration) (*certutil.AltName // create AltNames with defaults DNSNames/IPs altNames := &certutil.AltNames{ - DNSNames: []string{cfg.NodeName}, + DNSNames: []string{cfg.NodeRegistration.Name}, IPs: []net.IP{advertiseAddress}, } diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index 02194885a97..6a21c8f772d 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -160,7 +160,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.MasterConfiguration) (map[string]*kubeCo kubeadmconstants.KubeletKubeConfigFileName: { CACert: caCert, APIServer: masterEndpoint, - ClientName: fmt.Sprintf("system:node:%s", cfg.NodeName), + ClientName: fmt.Sprintf("system:node:%s", cfg.NodeRegistration.Name), ClientCertAuth: &clientCertAuth{ CAKey: caKey, Organizations: []string{kubeadmconstants.NodesGroup}, diff --git a/cmd/kubeadm/app/phases/kubelet/config.go b/cmd/kubeadm/app/phases/kubelet/config.go index 5503a16a7fc..e515d649860 100644 --- a/cmd/kubeadm/app/phases/kubelet/config.go +++ b/cmd/kubeadm/app/phases/kubelet/config.go @@ -37,12 +37,7 @@ import ( // WriteConfigToDisk writes the kubelet config object down to a file // Used at "kubeadm init" and "kubeadm upgrade" time -func WriteConfigToDisk(kubeletConfig *kubeletconfigv1beta1.KubeletConfiguration, kubeletVersion *version.Version) error { - - // If the kubelet version is v1.10.x, exit - if kubeletVersion.LessThan(kubeadmconstants.MinimumKubeletConfigVersion) { - return nil - } +func WriteConfigToDisk(kubeletConfig *kubeletconfigv1beta1.KubeletConfiguration) error { kubeletBytes, err := getConfigBytes(kubeletConfig) if err != nil { @@ -60,11 +55,6 @@ func CreateConfigMap(cfg *kubeadmapi.MasterConfiguration, client clientset.Inter return err } - // If Kubernetes version is v1.10.x, exit - if k8sVersion.LessThan(kubeadmconstants.MinimumKubeletConfigVersion) { - return nil - } - configMapName := configMapName(k8sVersion) fmt.Printf("[kubelet] Creating a ConfigMap %q in namespace %s with the configuration for the kubelets in the cluster\n", configMapName, metav1.NamespaceSystem) @@ -132,11 +122,6 @@ func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Ve // Used at "kubeadm join" time func DownloadConfig(kubeletKubeConfig string, kubeletVersion *version.Version) error { - // If the kubelet version is v1.10.x, exit - if kubeletVersion.LessThan(kubeadmconstants.MinimumKubeletConfigVersion) { - return nil - } - // Download the ConfigMap from the cluster based on what version the kubelet is configMapName := configMapName(kubeletVersion) diff --git a/cmd/kubeadm/app/phases/kubelet/dynamic.go b/cmd/kubeadm/app/phases/kubelet/dynamic.go index b65d7460370..2f49209182a 100644 --- a/cmd/kubeadm/app/phases/kubelet/dynamic.go +++ b/cmd/kubeadm/app/phases/kubelet/dynamic.go @@ -17,19 +17,17 @@ limitations under the License. package kubelet import ( - "encoding/json" "fmt" "os" "path/filepath" "k8s.io/api/core/v1" - apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" "k8s.io/kubernetes/pkg/util/version" ) @@ -39,64 +37,33 @@ import ( // This func is ONLY run if the user enables the `DynamicKubeletConfig` feature gate, which is by default off func EnableDynamicConfigForNode(client clientset.Interface, nodeName string, kubeletVersion *version.Version) error { - // If the kubelet version is v1.10.x, exit - if kubeletVersion.LessThan(kubeadmconstants.MinimumKubeletConfigVersion) { - return nil - } - configMapName := configMapName(kubeletVersion) fmt.Printf("[kubelet] Enabling Dynamic Kubelet Config for Node %q; config sourced from ConfigMap %q in namespace %s\n", nodeName, configMapName, metav1.NamespaceSystem) fmt.Println("[kubelet] WARNING: The Dynamic Kubelet Config feature is alpha and off by default. It hasn't been well-tested yet at this stage, use with caution.") + kubeletConfigMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(configMapName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("couldn't get the kubelet configuration ConfigMap: %v", err) + } + // Loop on every falsy return. Return with an error if raised. Exit successfully if true is returned. - return wait.Poll(kubeadmconstants.APICallRetryInterval, kubeadmconstants.UpdateNodeTimeout, func() (bool, error) { - node, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{}) - if err != nil { - return false, nil - } - - oldData, err := json.Marshal(node) - if err != nil { - return false, err - } - - kubeletCfg, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(configMapName, metav1.GetOptions{}) - if err != nil { - return false, nil - } - - node.Spec.ConfigSource = &v1.NodeConfigSource{ - ConfigMap: &v1.ConfigMapNodeConfigSource{ - Name: configMapName, - Namespace: metav1.NamespaceSystem, - UID: kubeletCfg.UID, - KubeletConfigKey: kubeadmconstants.KubeletBaseConfigurationConfigMapKey, - }, - } - - newData, err := json.Marshal(node) - if err != nil { - return false, err - } - - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{}) - if err != nil { - return false, err - } - - if _, err := client.CoreV1().Nodes().Patch(node.Name, types.StrategicMergePatchType, patchBytes); err != nil { - if apierrs.IsConflict(err) { - fmt.Println("Temporarily unable to update node metadata due to conflict (will retry)") - return false, nil - } - return false, err - } - - return true, nil + return apiclient.PatchNode(client, nodeName, func(n *v1.Node) { + patchNodeForDynamicConfig(n, configMapName, kubeletConfigMap.UID) }) } +func patchNodeForDynamicConfig(n *v1.Node, configMapName string, configMapUID types.UID) { + n.Spec.ConfigSource = &v1.NodeConfigSource{ + ConfigMap: &v1.ConfigMapNodeConfigSource{ + Name: configMapName, + Namespace: metav1.NamespaceSystem, + UID: configMapUID, + KubeletConfigKey: kubeadmconstants.KubeletBaseConfigurationConfigMapKey, + }, + } +} + // GetLocalNodeTLSBootstrappedClient waits for the kubelet to perform the TLS bootstrap // and then creates a client from config file /etc/kubernetes/kubelet.conf func GetLocalNodeTLSBootstrappedClient() (clientset.Interface, error) { diff --git a/cmd/kubeadm/app/phases/kubelet/flags.go b/cmd/kubeadm/app/phases/kubelet/flags.go index 167966793aa..926c4b5022e 100644 --- a/cmd/kubeadm/app/phases/kubelet/flags.go +++ b/cmd/kubeadm/app/phases/kubelet/flags.go @@ -31,10 +31,9 @@ import ( // WriteKubeletDynamicEnvFile writes a environment file with dynamic flags to the kubelet. // Used at "kubeadm init" and "kubeadm join" time. -func WriteKubeletDynamicEnvFile(cfg *kubeadmapi.MasterConfiguration) error { +func WriteKubeletDynamicEnvFile(nodeRegOpts *kubeadmapi.NodeRegistrationOptions, registerTaintsUsingFlags bool) error { - // TODO: Pass through extra arguments from the config file here in the future - argList := kubeadmutil.BuildArgumentListFromMap(buildKubeletArgMap(cfg), map[string]string{}) + argList := kubeadmutil.BuildArgumentListFromMap(buildKubeletArgMap(nodeRegOpts, registerTaintsUsingFlags), nodeRegOpts.ExtraArgs) envFileContent := fmt.Sprintf("%s=%s\n", constants.KubeletEnvFileVariableName, strings.Join(argList, " ")) return writeKubeletFlagBytesToDisk([]byte(envFileContent)) @@ -42,20 +41,28 @@ func WriteKubeletDynamicEnvFile(cfg *kubeadmapi.MasterConfiguration) error { // buildKubeletArgMap takes a MasterConfiguration object and builds based on that a string-string map with flags // that should be given to the local kubelet daemon. -func buildKubeletArgMap(cfg *kubeadmapi.MasterConfiguration) map[string]string { +func buildKubeletArgMap(nodeRegOpts *kubeadmapi.NodeRegistrationOptions, registerTaintsUsingFlags bool) map[string]string { kubeletFlags := map[string]string{} - if cfg.CRISocket == kubeadmapiv1alpha2.DefaultCRISocket { + if nodeRegOpts.CRISocket == kubeadmapiv1alpha2.DefaultCRISocket { // These flags should only be set when running docker kubeletFlags["network-plugin"] = "cni" kubeletFlags["cni-conf-dir"] = "/etc/cni/net.d" kubeletFlags["cni-bin-dir"] = "/opt/cni/bin" } else { kubeletFlags["container-runtime"] = "remote" - kubeletFlags["container-runtime-endpoint"] = cfg.CRISocket + kubeletFlags["container-runtime-endpoint"] = nodeRegOpts.CRISocket } - // TODO: Add support for registering custom Taints and Labels - // TODO: Add support for overriding flags with ExtraArgs + + if registerTaintsUsingFlags && nodeRegOpts.Taints != nil && len(nodeRegOpts.Taints) > 0 { + taintStrs := []string{} + for _, taint := range nodeRegOpts.Taints { + taintStrs = append(taintStrs, taint.ToString()) + } + + kubeletFlags["register-with-taints"] = strings.Join(taintStrs, ",") + } + // TODO: Pass through --hostname-override if a custom name is used? // TODO: Check if `systemd-resolved` is running, and set `--resolv-conf` based on that // TODO: Conditionally set `--cgroup-driver` to either `systemd` or `cgroupfs` diff --git a/cmd/kubeadm/app/phases/markmaster/markmaster.go b/cmd/kubeadm/app/phases/markmaster/markmaster.go index 9f8f52237d9..39ee9174da5 100644 --- a/cmd/kubeadm/app/phases/markmaster/markmaster.go +++ b/cmd/kubeadm/app/phases/markmaster/markmaster.go @@ -17,107 +17,30 @@ limitations under the License. package markmaster import ( - "encoding/json" - "fmt" - "github.com/golang/glog" "k8s.io/api/core/v1" - apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" - kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" - kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" + "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" ) // MarkMaster taints the master and sets the master label -func MarkMaster(client clientset.Interface, masterName string, taint bool) error { +func MarkMaster(client clientset.Interface, masterName string, taints []v1.Taint) error { - if taint { - glog.Infof("[markmaster] will mark node %s as master by adding a label and a taint\n", masterName) - } else { - glog.Infof("[markmaster] will mark node %s as master by adding a label\n", masterName) + glog.Infof("[markmaster] Marking the node %s as master by adding the label \"%s=''\"\n", masterName, constants.LabelNodeRoleMaster) + + if taints != nil && len(taints) > 0 { + glog.Infof("[markmaster] Marking the node %s as master by adding the taints %v\n", masterName, taints) } - // Loop on every falsy return. Return with an error if raised. Exit successfully if true is returned. - return wait.Poll(kubeadmconstants.APICallRetryInterval, kubeadmconstants.MarkMasterTimeout, func() (bool, error) { - // First get the node object - n, err := client.CoreV1().Nodes().Get(masterName, metav1.GetOptions{}) - if err != nil { - return false, nil - } - - // The node may appear to have no labels at first, - // so we wait for it to get hostname label. - if _, found := n.ObjectMeta.Labels[kubeletapis.LabelHostname]; !found { - return false, nil - } - - oldData, err := json.Marshal(n) - if err != nil { - return false, err - } - - // The master node should be tainted and labelled accordingly - markMasterNode(n, taint) - - newData, err := json.Marshal(n) - if err != nil { - return false, err - } - - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{}) - if err != nil { - return false, err - } - - if _, err := client.CoreV1().Nodes().Patch(n.Name, types.StrategicMergePatchType, patchBytes); err != nil { - if apierrs.IsConflict(err) { - fmt.Println("[markmaster] Temporarily unable to update master node metadata due to conflict (will retry)") - return false, nil - } - return false, err - } - - if taint { - fmt.Printf("[markmaster] Master %s tainted and labelled with key/value: %s=%q\n", masterName, kubeadmconstants.LabelNodeRoleMaster, "") - } else { - fmt.Printf("[markmaster] Master %s labelled with key/value: %s=%q\n", masterName, kubeadmconstants.LabelNodeRoleMaster, "") - } - - return true, nil + return apiclient.PatchNode(client, masterName, func(n *v1.Node) { + markMasterNode(n, taints) }) } -func markMasterNode(n *v1.Node, taint bool) { - n.ObjectMeta.Labels[kubeadmconstants.LabelNodeRoleMaster] = "" - if taint { - addTaintIfNotExists(n, kubeadmconstants.MasterTaint) - } else { - delTaintIfExists(n, kubeadmconstants.MasterTaint) - } -} - -func addTaintIfNotExists(n *v1.Node, t v1.Taint) { - for _, taint := range n.Spec.Taints { - if taint == t { - return - } - } - - n.Spec.Taints = append(n.Spec.Taints, t) -} - -func delTaintIfExists(n *v1.Node, t v1.Taint) { - var taints []v1.Taint - for _, taint := range n.Spec.Taints { - if taint == t { - continue - } - taints = append(taints, t) - } +func markMasterNode(n *v1.Node, taints []v1.Taint) { + n.ObjectMeta.Labels[constants.LabelNodeRoleMaster] = "" + // TODO: Append taints, don't override? n.Spec.Taints = taints } diff --git a/cmd/kubeadm/app/phases/selfhosting/selfhosting.go b/cmd/kubeadm/app/phases/selfhosting/selfhosting.go index 3d20c959dd8..5bd90886e4e 100644 --- a/cmd/kubeadm/app/phases/selfhosting/selfhosting.go +++ b/cmd/kubeadm/app/phases/selfhosting/selfhosting.go @@ -118,7 +118,7 @@ func CreateSelfHostedControlPlane(manifestsDir, kubeConfigDir string, cfg *kubea // Wait for the mirror Pod hash to be removed; otherwise we'll run into race conditions here when the kubelet hasn't had time to // remove the Static Pod (or the mirror Pod respectively). This implicitly also tests that the API server endpoint is healthy, // because this blocks until the API server returns a 404 Not Found when getting the Static Pod - staticPodName := fmt.Sprintf("%s-%s", componentName, cfg.NodeName) + staticPodName := fmt.Sprintf("%s-%s", componentName, cfg.NodeRegistration.Name) if err := waiter.WaitForPodToDisappear(staticPodName); err != nil { return err } diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods.go b/cmd/kubeadm/app/phases/upgrade/staticpods.go index a041c9669cf..77056dcadda 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods.go @@ -206,7 +206,7 @@ func upgradeComponent(component string, waiter apiclient.Waiter, pathMgr StaticP // notice the removal of the Static Pod, leading to a false positive below where we check that the API endpoint is healthy // If we don't do this, there is a case where we remove the Static Pod manifest, kubelet is slow to react, kubeadm checks the // API endpoint below of the OLD Static Pod component and proceeds quickly enough, which might lead to unexpected results. - if err := waiter.WaitForStaticPodHashChange(cfg.NodeName, component, beforePodHash); err != nil { + if err := waiter.WaitForStaticPodHashChange(cfg.NodeRegistration.Name, component, beforePodHash); err != nil { return rollbackOldManifests(recoverManifests, err, pathMgr, recoverEtcd) } @@ -266,7 +266,7 @@ func performEtcdStaticPodUpgrade(waiter apiclient.Waiter, pathMgr StaticPodPathM return false, nil } - beforeEtcdPodHash, err := waiter.WaitForStaticPodSingleHash(cfg.NodeName, constants.Etcd) + beforeEtcdPodHash, err := waiter.WaitForStaticPodSingleHash(cfg.NodeRegistration.Name, constants.Etcd) if err != nil { return true, fmt.Errorf("failed to get etcd pod's hash: %v", err) } @@ -376,7 +376,7 @@ func StaticPodControlPlane(waiter apiclient.Waiter, pathMgr StaticPodPathManager var isTLSUpgrade bool var isExternalEtcd bool - beforePodHashMap, err := waiter.WaitForStaticPodControlPlaneHashes(cfg.NodeName) + beforePodHashMap, err := waiter.WaitForStaticPodControlPlaneHashes(cfg.NodeRegistration.Name) if err != nil { return err } diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index 922ca00825d..6f0cdf4c876 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -906,7 +906,7 @@ func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfi checks = addCommonChecks(execer, cfg, checks) // Check ipvs required kernel module once we use ipvs kube-proxy mode - if cfg.KubeProxy.Config.Mode == ipvsutil.IPVSProxyMode { + if cfg.KubeProxy.Config != nil && cfg.KubeProxy.Config.Mode == ipvsutil.IPVSProxyMode { checks = append(checks, ipvsutil.RequiredIPVSKernelModulesAvailableCheck{Executor: execer}, ) diff --git a/cmd/kubeadm/app/util/apiclient/idempotency.go b/cmd/kubeadm/app/util/apiclient/idempotency.go index ffe42df077a..bc36f65ebdd 100644 --- a/cmd/kubeadm/app/util/apiclient/idempotency.go +++ b/cmd/kubeadm/app/util/apiclient/idempotency.go @@ -17,6 +17,7 @@ limitations under the License. package apiclient import ( + "encoding/json" "fmt" apps "k8s.io/api/apps/v1" @@ -24,7 +25,12 @@ import ( rbac "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/cmd/kubeadm/app/constants" + kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" ) // TODO: We should invent a dynamic mechanism for this using the dynamic client instead of hard-coding these functions per-type @@ -186,3 +192,49 @@ func CreateOrUpdateClusterRoleBinding(client clientset.Interface, clusterRoleBin } return nil } + +// PatchNode tries to patch a node using the following client, executing patchFn for the actual mutating logic +func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Node)) error { + // Loop on every false return. Return with an error if raised. Exit successfully if true is returned. + return wait.Poll(constants.APICallRetryInterval, constants.PatchNodeTimeout, func() (bool, error) { + // First get the node object + n, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + + // The node may appear to have no labels at first, + // so we wait for it to get hostname label. + if _, found := n.ObjectMeta.Labels[kubeletapis.LabelHostname]; !found { + return false, nil + } + + oldData, err := json.Marshal(n) + if err != nil { + return false, err + } + + // Execute the mutating function + patchFn(n) + + newData, err := json.Marshal(n) + if err != nil { + return false, err + } + + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{}) + if err != nil { + return false, err + } + + if _, err := client.CoreV1().Nodes().Patch(n.Name, types.StrategicMergePatchType, patchBytes); err != nil { + if apierrors.IsConflict(err) { + fmt.Println("[patchnode] Temporarily unable to update node metadata due to conflict (will retry)") + return false, nil + } + return false, err + } + + return true, nil + }) +} diff --git a/cmd/kubeadm/app/util/config/masterconfig.go b/cmd/kubeadm/app/util/config/masterconfig.go index ec2f84dec46..a78149e211c 100644 --- a/cmd/kubeadm/app/util/config/masterconfig.go +++ b/cmd/kubeadm/app/util/config/masterconfig.go @@ -23,6 +23,7 @@ import ( "github.com/golang/glog" + "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" netutil "k8s.io/apimachinery/pkg/util/net" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -72,7 +73,12 @@ func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error { } } - cfg.NodeName = node.GetHostname(cfg.NodeName) + cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name) + + // Only if the slice is nil, we should append the master taint. This allows the user to specify an empty slice for no default master taint + if cfg.NodeRegistration.Taints == nil { + cfg.NodeRegistration.Taints = []v1.Taint{kubeadmconstants.MasterTaint} + } return nil } diff --git a/cmd/kubeadm/app/util/config/nodeconfig.go b/cmd/kubeadm/app/util/config/nodeconfig.go index b8ffc64d932..77bcf8b32f6 100644 --- a/cmd/kubeadm/app/util/config/nodeconfig.go +++ b/cmd/kubeadm/app/util/config/nodeconfig.go @@ -33,7 +33,7 @@ import ( // SetJoinDynamicDefaults checks and sets configuration values for the NodeConfiguration object func SetJoinDynamicDefaults(cfg *kubeadmapi.NodeConfiguration) error { - cfg.NodeName = node.GetHostname(cfg.NodeName) + cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name) return nil } diff --git a/cmd/kubeadm/test/util.go b/cmd/kubeadm/test/util.go index fdfdae5592b..cb7ae225a68 100644 --- a/cmd/kubeadm/test/util.go +++ b/cmd/kubeadm/test/util.go @@ -57,9 +57,10 @@ func SetupMasterConfigurationFile(t *testing.T, tmpdir string, cfg *kubeadmapi.M kind: MasterConfiguration certificatesDir: {{.CertificatesDir}} api: - advertiseAddress: {{.API.AdvertiseAddress}} - bindPort: {{.API.BindPort}} - nodeName: {{.NodeName}} + advertiseAddress: {{.API.AdvertiseAddress}} + bindPort: {{.API.BindPort}} + nodeRegistration: + name: {{.NodeRegistration.Name}} `))) f, err := os.Create(cfgPath) From fd47f8b20c9ff9acb167915959ac03c0d7232d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 29 May 2018 17:52:10 +0300 Subject: [PATCH 104/416] Update unit tests to use the new NodeRegistration object --- .../kubeadm/validation/validation_test.go | 28 +++++++-------- cmd/kubeadm/app/cmd/phases/certs_test.go | 6 ++-- cmd/kubeadm/app/cmd/phases/kubeconfig_test.go | 6 ++-- cmd/kubeadm/app/cmd/upgrade/common_test.go | 8 +++-- cmd/kubeadm/app/phases/certs/certs_test.go | 36 +++++++++---------- .../phases/certs/pkiutil/pki_helpers_test.go | 8 ++--- .../app/phases/controlplane/manifests_test.go | 4 +-- .../app/phases/kubeconfig/kubeconfig_test.go | 32 ++++++++--------- cmd/kubeadm/app/phases/kubelet/config_test.go | 2 +- .../app/phases/kubelet/dynamic_test.go | 4 ++- .../app/phases/markmaster/markmaster_test.go | 35 +++++++++--------- .../app/phases/upgrade/postupgrade_test.go | 6 ++-- .../app/phases/upgrade/staticpods_test.go | 4 ++- .../app/util/config/nodeconfig_test.go | 22 ++++-------- 14 files changed, 100 insertions(+), 101 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index b0dd24a683b..6876bd702de 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -119,7 +119,7 @@ func TestValidateNodeName(t *testing.T) { actual := ValidateNodeName(rt.s, rt.f) if (len(actual) == 0) != rt.expected { t.Errorf( - "failed ValidateNodeName:\n\texpected: %t\n\t actual: %t", + "failed ValidateNodeRegistration: kubeadm.NodeRegistrationOptions{Name:\n\texpected: %t\n\t actual: %t", rt.expected, (len(actual) == 0), ) @@ -407,8 +407,8 @@ func TestValidateMasterConfiguration(t *testing.T) { ServiceSubnet: "10.96.0.1/12", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/cert/dir", - NodeName: nodename, + CertificatesDir: "/some/cert/dir", + NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, false}, {"invalid missing token with IPv6 service subnet", &kubeadm.MasterConfiguration{ @@ -420,8 +420,8 @@ func TestValidateMasterConfiguration(t *testing.T) { ServiceSubnet: "2001:db8::1/98", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/cert/dir", - NodeName: nodename, + CertificatesDir: "/some/cert/dir", + NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, false}, {"invalid missing node name", &kubeadm.MasterConfiguration{ @@ -447,9 +447,9 @@ func TestValidateMasterConfiguration(t *testing.T) { DNSDomain: "cluster.local", PodSubnet: "10.0.1.15", }, - CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", - NodeName: nodename, + CertificatesDir: "/some/other/cert/dir", + Token: "abcdef.0123456789abcdef", + NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, false}, {"valid master configuration with IPv4 service subnet", &kubeadm.MasterConfiguration{ @@ -493,9 +493,9 @@ func TestValidateMasterConfiguration(t *testing.T) { DNSDomain: "cluster.local", PodSubnet: "10.0.1.15/16", }, - CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", - NodeName: nodename, + CertificatesDir: "/some/other/cert/dir", + Token: "abcdef.0123456789abcdef", + NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, true}, {"valid master configuration using IPv6 service subnet", &kubeadm.MasterConfiguration{ @@ -538,9 +538,9 @@ func TestValidateMasterConfiguration(t *testing.T) { ServiceSubnet: "2001:db8::1/98", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/other/cert/dir", - Token: "abcdef.0123456789abcdef", - NodeName: nodename, + CertificatesDir: "/some/other/cert/dir", + Token: "abcdef.0123456789abcdef", + NodeRegistration: kubeadm.NodeRegistrationOptions{Name: nodename, CRISocket: "/some/path"}, }, true}, } for _, rt := range tests { diff --git a/cmd/kubeadm/app/cmd/phases/certs_test.go b/cmd/kubeadm/app/cmd/phases/certs_test.go index 0b35c435650..32b90027479 100644 --- a/cmd/kubeadm/app/cmd/phases/certs_test.go +++ b/cmd/kubeadm/app/cmd/phases/certs_test.go @@ -254,9 +254,9 @@ func TestSubCmdCertsCreateFilesWithConfigFile(t *testing.T) { certdir := tmpdir cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, - CertificatesDir: certdir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, + CertificatesDir: certdir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, } configPath := testutil.SetupMasterConfigurationFile(t, tmpdir, cfg) diff --git a/cmd/kubeadm/app/cmd/phases/kubeconfig_test.go b/cmd/kubeadm/app/cmd/phases/kubeconfig_test.go index 309a5922a4a..f74538a6aab 100644 --- a/cmd/kubeadm/app/cmd/phases/kubeconfig_test.go +++ b/cmd/kubeadm/app/cmd/phases/kubeconfig_test.go @@ -277,9 +277,9 @@ func TestKubeConfigSubCommandsThatCreateFilesWithConfigFile(t *testing.T) { // Adds a master configuration file cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, } cfgPath := testutil.SetupMasterConfigurationFile(t, tmpdir, cfg) diff --git a/cmd/kubeadm/app/cmd/upgrade/common_test.go b/cmd/kubeadm/app/cmd/upgrade/common_test.go index ac170b04bb6..8745378b60f 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common_test.go +++ b/cmd/kubeadm/app/cmd/upgrade/common_test.go @@ -65,7 +65,9 @@ func TestPrintConfiguration(t *testing.T) { dnsDomain: "" podSubnet: "" serviceSubnet: "" - nodeName: "" + nodeRegistration: + criSocket: "" + name: "" token: "" unifiedControlPlaneImage: "" `), @@ -108,7 +110,9 @@ func TestPrintConfiguration(t *testing.T) { dnsDomain: "" podSubnet: "" serviceSubnet: 10.96.0.1/12 - nodeName: "" + nodeRegistration: + criSocket: "" + name: "" token: "" unifiedControlPlaneImage: "" `), diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go index c6fbdcca840..2e46abf1680 100644 --- a/cmd/kubeadm/app/phases/certs/certs_test.go +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -273,9 +273,9 @@ func TestNewAPIServerCertAndKey(t *testing.T) { advertiseAddresses := []string{"1.2.3.4", "1:2:3::4"} for _, addr := range advertiseAddresses { cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: addr}, - Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: hostname, + API: kubeadmapi.API{AdvertiseAddress: addr}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: hostname}, } caCert, caKey, err := NewCACertAndKey() if err != nil { @@ -357,8 +357,8 @@ func TestNewEtcdPeerCertAndKey(t *testing.T) { advertiseAddresses := []string{"1.2.3.4", "1:2:3::4"} for _, addr := range advertiseAddresses { cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: addr}, - NodeName: hostname, + API: kubeadmapi.API{AdvertiseAddress: addr}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: hostname}, Etcd: kubeadmapi.Etcd{ Local: &kubeadmapi.LocalEtcd{ PeerCertSANs: []string{ @@ -481,10 +481,10 @@ func TestUsingExternalCA(t *testing.T) { defer os.RemoveAll(dir) cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, - Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", - CertificatesDir: dir, + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, + CertificatesDir: dir, } for _, f := range test.setupFuncs { @@ -564,10 +564,10 @@ func TestValidateMethods(t *testing.T) { test.loc.pkiDir = dir cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, - Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", - CertificatesDir: dir, + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, + CertificatesDir: dir, } fmt.Println("Testing", test.name) @@ -696,11 +696,11 @@ func TestCreateCertificateFilesMethods(t *testing.T) { defer os.RemoveAll(tmpdir) cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, - Etcd: kubeadmapi.Etcd{Local: &kubeadmapi.LocalEtcd{}}, - Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", - CertificatesDir: tmpdir, + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, + Etcd: kubeadmapi.Etcd{Local: &kubeadmapi.LocalEtcd{}}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, + CertificatesDir: tmpdir, } if test.externalEtcd { diff --git a/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers_test.go b/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers_test.go index 9ff34b0b20a..5bd203c1d50 100644 --- a/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers_test.go +++ b/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers_test.go @@ -448,7 +448,7 @@ func TestGetAPIServerAltNames(t *testing.T) { cfg: &kubeadmapi.MasterConfiguration{ API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io:6443"}, Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, APIServerCertSANs: []string{"10.1.245.94", "10.1.245.95", "1.2.3.L", "invalid,commas,in,DNS"}, }, expectedDNSNames: []string{"valid-hostname", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local", "api.k8s.io"}, @@ -459,7 +459,7 @@ func TestGetAPIServerAltNames(t *testing.T) { cfg: &kubeadmapi.MasterConfiguration{ API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "4.5.6.7:6443"}, Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, APIServerCertSANs: []string{"10.1.245.94", "10.1.245.95", "1.2.3.L", "invalid,commas,in,DNS"}, }, expectedDNSNames: []string{"valid-hostname", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local"}, @@ -561,8 +561,8 @@ func TestGetEtcdPeerAltNames(t *testing.T) { proxyIP := "10.10.10.100" advertiseIP := "1.2.3.4" cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: advertiseIP}, - NodeName: hostname, + API: kubeadmapi.API{AdvertiseAddress: advertiseIP}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: hostname}, Etcd: kubeadmapi.Etcd{ Local: &kubeadmapi.LocalEtcd{ PeerCertSANs: []string{ diff --git a/cmd/kubeadm/app/phases/controlplane/manifests_test.go b/cmd/kubeadm/app/phases/controlplane/manifests_test.go index 6da80ae160d..58b81636874 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests_test.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests_test.go @@ -838,7 +838,7 @@ func TestGetControllerManagerCommandExternalCA(t *testing.T) { KubernetesVersion: "v1.7.0", API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, }, caKeyPresent: false, expectedArgFunc: func(tmpdir string) []string { @@ -862,7 +862,7 @@ func TestGetControllerManagerCommandExternalCA(t *testing.T) { KubernetesVersion: "v1.7.0", API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "valid-hostname", + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"}, }, caKeyPresent: true, expectedArgFunc: func(tmpdir string) []string { diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index 3b47f4e627f..ced29c30e3f 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -67,29 +67,29 @@ func TestGetKubeConfigSpecs(t *testing.T) { // Creates Master Configurations pointing to the pkidir folder cfgs := []*kubeadmapi.MasterConfiguration{ { - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, }, { - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, }, { - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io:4321", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io:4321", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, }, { - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, }, { - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io:4321", BindPort: 1234}, - CertificatesDir: pkidir, - NodeName: "valid-node-name", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4", ControlPlaneEndpoint: "api.k8s.io:4321", BindPort: 1234}, + CertificatesDir: pkidir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"}, }, } @@ -106,7 +106,7 @@ func TestGetKubeConfigSpecs(t *testing.T) { }, { kubeConfigFile: kubeadmconstants.KubeletKubeConfigFileName, - clientName: fmt.Sprintf("system:node:%s", cfg.NodeName), + clientName: fmt.Sprintf("system:node:%s", cfg.NodeRegistration.Name), organizations: []string{kubeadmconstants.NodesGroup}, }, { diff --git a/cmd/kubeadm/app/phases/kubelet/config_test.go b/cmd/kubeadm/app/phases/kubelet/config_test.go index 0e6d5b68c39..dc21da453d4 100644 --- a/cmd/kubeadm/app/phases/kubelet/config_test.go +++ b/cmd/kubeadm/app/phases/kubelet/config_test.go @@ -33,7 +33,7 @@ func TestCreateConfigMap(t *testing.T) { nodeName := "fake-node" client := fake.NewSimpleClientset() cfg := &kubeadmapi.MasterConfiguration{ - NodeName: nodeName, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodeName}, KubernetesVersion: "v1.11.0", KubeletConfiguration: kubeadmapi.KubeletConfiguration{ BaseConfig: &kubeletconfigv1beta1.KubeletConfiguration{}, diff --git a/cmd/kubeadm/app/phases/kubelet/dynamic_test.go b/cmd/kubeadm/app/phases/kubelet/dynamic_test.go index fe0ba35f41c..150eeda405b 100644 --- a/cmd/kubeadm/app/phases/kubelet/dynamic_test.go +++ b/cmd/kubeadm/app/phases/kubelet/dynamic_test.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" core "k8s.io/client-go/testing" + kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" "k8s.io/kubernetes/pkg/util/version" ) @@ -33,7 +34,8 @@ func TestEnableDynamicConfigForNode(t *testing.T) { client.PrependReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) { return true, &v1.Node{ ObjectMeta: metav1.ObjectMeta{ - Name: nodeName, + Name: nodeName, + Labels: map[string]string{kubeletapis.LabelHostname: nodeName}, }, Spec: v1.NodeSpec{ ConfigSource: &v1.NodeConfigSource{ diff --git a/cmd/kubeadm/app/phases/markmaster/markmaster_test.go b/cmd/kubeadm/app/phases/markmaster/markmaster_test.go index d7d5a934501..48b421cdbf7 100644 --- a/cmd/kubeadm/app/phases/markmaster/markmaster_test.go +++ b/cmd/kubeadm/app/phases/markmaster/markmaster_test.go @@ -40,52 +40,52 @@ func TestMarkMaster(t *testing.T) { // will need to change if strategicpatch's behavior changes in the // future. tests := []struct { - name string - existingLabel string - existingTaint *v1.Taint - wantTaint bool - expectedPatch string + name string + existingLabel string + existingTaints []v1.Taint + newTaints []v1.Taint + expectedPatch string }{ { "master label and taint missing", "", nil, - true, + []v1.Taint{kubeadmconstants.MasterTaint}, "{\"metadata\":{\"labels\":{\"node-role.kubernetes.io/master\":\"\"}},\"spec\":{\"taints\":[{\"effect\":\"NoSchedule\",\"key\":\"node-role.kubernetes.io/master\"}]}}", }, { "master label and taint missing but taint not wanted", "", nil, - false, + nil, "{\"metadata\":{\"labels\":{\"node-role.kubernetes.io/master\":\"\"}}}", }, { "master label missing", "", - &kubeadmconstants.MasterTaint, - true, + []v1.Taint{kubeadmconstants.MasterTaint}, + []v1.Taint{kubeadmconstants.MasterTaint}, "{\"metadata\":{\"labels\":{\"node-role.kubernetes.io/master\":\"\"}}}", }, { "master taint missing", kubeadmconstants.LabelNodeRoleMaster, nil, - true, + []v1.Taint{kubeadmconstants.MasterTaint}, "{\"spec\":{\"taints\":[{\"effect\":\"NoSchedule\",\"key\":\"node-role.kubernetes.io/master\"}]}}", }, { "nothing missing", kubeadmconstants.LabelNodeRoleMaster, - &kubeadmconstants.MasterTaint, - true, + []v1.Taint{kubeadmconstants.MasterTaint}, + []v1.Taint{kubeadmconstants.MasterTaint}, "{}", }, { "nothing missing but taint unwanted", kubeadmconstants.LabelNodeRoleMaster, - &kubeadmconstants.MasterTaint, - false, + []v1.Taint{kubeadmconstants.MasterTaint}, + nil, "{\"spec\":{\"taints\":null}}", }, } @@ -105,8 +105,8 @@ func TestMarkMaster(t *testing.T) { masterNode.ObjectMeta.Labels[tc.existingLabel] = "" } - if tc.existingTaint != nil { - masterNode.Spec.Taints = append(masterNode.Spec.Taints, *tc.existingTaint) + if tc.existingTaints != nil { + masterNode.Spec.Taints = tc.existingTaints } jsonNode, err := json.Marshal(masterNode) @@ -144,8 +144,7 @@ func TestMarkMaster(t *testing.T) { t.Fatalf("MarkMaster(%s): unexpected error building clientset: %v", tc.name, err) } - err = MarkMaster(cs, hostname, tc.wantTaint) - if err != nil { + if err := MarkMaster(cs, hostname, tc.newTaints); err != nil { t.Errorf("MarkMaster(%s) returned unexpected error: %v", tc.name, err) } diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go index 652dee2e40b..c25387e8eca 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go @@ -131,9 +131,9 @@ func TestRollbackFiles(t *testing.T) { func TestShouldBackupAPIServerCertAndKey(t *testing.T) { cfg := &kubeadmapi.MasterConfiguration{ - API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, - Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, - NodeName: "test-node", + API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"}, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "test-node"}, } for desc, test := range map[string]struct { diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods_test.go b/cmd/kubeadm/app/phases/upgrade/staticpods_test.go index 44a299b5a3a..1ba9042b77b 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods_test.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods_test.go @@ -66,7 +66,9 @@ networking: dnsDomain: cluster.local podSubnet: "" serviceSubnet: 10.96.0.0/12 -nodeName: thegopher +nodeRegistration: + name: foo + criSocket: "" schedulerExtraArgs: null token: ce3aa5.5ec8455bb76b379f tokenTTL: 24h diff --git a/cmd/kubeadm/app/util/config/nodeconfig_test.go b/cmd/kubeadm/app/util/config/nodeconfig_test.go index dd26b79dd32..dcf679815a0 100644 --- a/cmd/kubeadm/app/util/config/nodeconfig_test.go +++ b/cmd/kubeadm/app/util/config/nodeconfig_test.go @@ -24,19 +24,17 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" ) const ( - node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml" - node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml" - node_internalYAML = "testdata/conversion/node/internal.yaml" - node_incompleteYAML = "testdata/defaulting/node/incomplete.yaml" - node_defaultedv1alpha1YAML = "testdata/defaulting/node/defaulted_v1alpha1.yaml" - node_defaultedv1alpha2YAML = "testdata/defaulting/node/defaulted_v1alpha2.yaml" - node_invalidYAML = "testdata/validation/invalid_nodecfg.yaml" + node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml" + node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml" + node_internalYAML = "testdata/conversion/node/internal.yaml" + node_incompleteYAML = "testdata/defaulting/node/incomplete.yaml" + node_defaultedYAML = "testdata/defaulting/node/defaulted.yaml" + node_invalidYAML = "testdata/validation/invalid_nodecfg.yaml" ) func TestNodeConfigFileAndDefaultsToInternalConfig(t *testing.T) { @@ -67,16 +65,10 @@ func TestNodeConfigFileAndDefaultsToInternalConfig(t *testing.T) { }, // These tests are reading one file that has only a subset of the fields populated, loading it using NodeConfigFileAndDefaultsToInternalConfig, // and then marshals the internal object to the expected groupVersion - { // v1alpha1 -> default -> validate -> internal -> v1alpha1 - name: "incompleteYAMLToDefaulted", - in: node_incompleteYAML, - out: node_defaultedv1alpha1YAML, - groupVersion: v1alpha1.SchemeGroupVersion, - }, { // v1alpha1 -> default -> validate -> internal -> v1alpha2 name: "incompleteYAMLToDefaulted", in: node_incompleteYAML, - out: node_defaultedv1alpha2YAML, + out: node_defaultedYAML, groupVersion: v1alpha2.SchemeGroupVersion, }, { // v1alpha1 (faulty) -> validation should fail From 8bcbc1e9bdf67f253d749db95251f1c9234ed913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 29 May 2018 17:52:22 +0300 Subject: [PATCH 105/416] autogenerated --- cmd/kubeadm/app/apis/kubeadm/fuzzer/BUILD | 1 + .../v1alpha1/zz_generated.conversion.go | 32 +++--------- .../v1alpha2/zz_generated.conversion.go | 50 +++++++++++++++---- .../kubeadm/v1alpha2/zz_generated.deepcopy.go | 33 ++++++++++++ .../kubeadm/v1alpha2/zz_generated.defaults.go | 2 + .../app/apis/kubeadm/zz_generated.deepcopy.go | 33 ++++++++++++ cmd/kubeadm/app/phases/kubelet/BUILD | 3 +- cmd/kubeadm/app/phases/markmaster/BUILD | 7 +-- cmd/kubeadm/app/util/apiclient/BUILD | 3 ++ cmd/kubeadm/app/util/config/BUILD | 1 + .../testdata/conversion/master/internal.yaml | 10 ++-- .../testdata/conversion/master/v1alpha2.yaml | 8 ++- .../testdata/conversion/node/internal.yaml | 7 ++- .../testdata/conversion/node/v1alpha2.yaml | 5 +- .../testdata/defaulting/master/defaulted.yaml | 8 ++- ...defaulted_v1alpha2.yaml => defaulted.yaml} | 5 +- .../defaulting/node/defaulted_v1alpha1.yaml | 14 ------ 17 files changed, 152 insertions(+), 70 deletions(-) rename cmd/kubeadm/app/util/config/testdata/defaulting/node/{defaulted_v1alpha2.yaml => defaulted.yaml} (83%) delete mode 100644 cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha1.yaml diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/BUILD b/cmd/kubeadm/app/apis/kubeadm/fuzzer/BUILD index cea19499314..f78847d71b9 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/BUILD @@ -16,6 +16,7 @@ go_library( "//pkg/proxy/apis/kubeproxyconfig/v1alpha1:go_default_library", "//pkg/util/pointer:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", ], diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go index e602d3aaafb..6a979a82eae 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go @@ -217,15 +217,15 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in } out.KubernetesVersion = in.KubernetesVersion // WARNING: in.CloudProvider requires manual conversion: does not exist in peer-type - out.NodeName = in.NodeName + // WARNING: in.NodeName requires manual conversion: does not exist in peer-type // WARNING: in.AuthorizationModes requires manual conversion: does not exist in peer-type - out.NoTaintMaster = in.NoTaintMaster + // WARNING: in.NoTaintMaster requires manual conversion: does not exist in peer-type // WARNING: in.PrivilegedPods requires manual conversion: does not exist in peer-type out.Token = in.Token out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL)) out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) - out.CRISocket = in.CRISocket + // WARNING: in.CRISocket requires manual conversion: does not exist in peer-type out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs)) @@ -262,13 +262,11 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in return err } out.KubernetesVersion = in.KubernetesVersion - out.NodeName = in.NodeName - out.NoTaintMaster = in.NoTaintMaster + // WARNING: in.NodeRegistration requires manual conversion: does not exist in peer-type out.Token = in.Token out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL)) out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) - out.CRISocket = in.CRISocket out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs)) @@ -288,11 +286,6 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in return nil } -// Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration is an autogenerated conversion function. -func Convert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in *kubeadm.MasterConfiguration, out *MasterConfiguration, s conversion.Scope) error { - return autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in, out, s) -} - func autoConvert_v1alpha1_Networking_To_kubeadm_Networking(in *Networking, out *kubeadm.Networking, s conversion.Scope) error { out.ServiceSubnet = in.ServiceSubnet out.PodSubnet = in.PodSubnet @@ -323,10 +316,10 @@ func autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *Nod out.DiscoveryToken = in.DiscoveryToken out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers)) out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout)) - out.NodeName = in.NodeName + // WARNING: in.NodeName requires manual conversion: does not exist in peer-type out.TLSBootstrapToken = in.TLSBootstrapToken out.Token = in.Token - out.CRISocket = in.CRISocket + // WARNING: in.CRISocket requires manual conversion: does not exist in peer-type out.ClusterName = in.ClusterName out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes)) out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification @@ -334,21 +327,15 @@ func autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *Nod return nil } -// Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration is an autogenerated conversion function. -func Convert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeConfiguration, out *kubeadm.NodeConfiguration, s conversion.Scope) error { - return autoConvert_v1alpha1_NodeConfiguration_To_kubeadm_NodeConfiguration(in, out, s) -} - func autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error { + // WARNING: in.NodeRegistration requires manual conversion: does not exist in peer-type out.CACertPath = in.CACertPath out.DiscoveryFile = in.DiscoveryFile out.DiscoveryToken = in.DiscoveryToken out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers)) out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout)) - out.NodeName = in.NodeName out.TLSBootstrapToken = in.TLSBootstrapToken out.Token = in.Token - out.CRISocket = in.CRISocket out.ClusterName = in.ClusterName out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes)) out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification @@ -356,11 +343,6 @@ func autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kub return nil } -// Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration is an autogenerated conversion function. -func Convert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error { - return autoConvert_kubeadm_NodeConfiguration_To_v1alpha1_NodeConfiguration(in, out, s) -} - func autoConvert_v1alpha1_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error { out.ID = in.ID out.Secret = in.Secret diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.conversion.go index dd661e08773..888517af55d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.conversion.go @@ -62,6 +62,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_kubeadm_Networking_To_v1alpha2_Networking, Convert_v1alpha2_NodeConfiguration_To_kubeadm_NodeConfiguration, Convert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration, + Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions, + Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions, Convert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery, Convert_kubeadm_TokenDiscovery_To_v1alpha2_TokenDiscovery, ) @@ -275,14 +277,14 @@ func autoConvert_v1alpha2_MasterConfiguration_To_kubeadm_MasterConfiguration(in if err := Convert_v1alpha2_Networking_To_kubeadm_Networking(&in.Networking, &out.Networking, s); err != nil { return err } + if err := Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } out.KubernetesVersion = in.KubernetesVersion - out.NodeName = in.NodeName - out.NoTaintMaster = in.NoTaintMaster out.Token = in.Token out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL)) out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) - out.CRISocket = in.CRISocket out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs)) @@ -323,13 +325,13 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha2_MasterConfiguration(in return err } out.KubernetesVersion = in.KubernetesVersion - out.NodeName = in.NodeName - out.NoTaintMaster = in.NoTaintMaster + if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } out.Token = in.Token out.TokenTTL = (*meta_v1.Duration)(unsafe.Pointer(in.TokenTTL)) out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) - out.CRISocket = in.CRISocket out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) out.SchedulerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.SchedulerExtraArgs)) @@ -379,15 +381,16 @@ func Convert_kubeadm_Networking_To_v1alpha2_Networking(in *kubeadm.Networking, o } func autoConvert_v1alpha2_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeConfiguration, out *kubeadm.NodeConfiguration, s conversion.Scope) error { + if err := Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } out.CACertPath = in.CACertPath out.DiscoveryFile = in.DiscoveryFile out.DiscoveryToken = in.DiscoveryToken out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers)) out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout)) - out.NodeName = in.NodeName out.TLSBootstrapToken = in.TLSBootstrapToken out.Token = in.Token - out.CRISocket = in.CRISocket out.ClusterName = in.ClusterName out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes)) out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification @@ -401,15 +404,16 @@ func Convert_v1alpha2_NodeConfiguration_To_kubeadm_NodeConfiguration(in *NodeCon } func autoConvert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration(in *kubeadm.NodeConfiguration, out *NodeConfiguration, s conversion.Scope) error { + if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { + return err + } out.CACertPath = in.CACertPath out.DiscoveryFile = in.DiscoveryFile out.DiscoveryToken = in.DiscoveryToken out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers)) out.DiscoveryTimeout = (*meta_v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout)) - out.NodeName = in.NodeName out.TLSBootstrapToken = in.TLSBootstrapToken out.Token = in.Token - out.CRISocket = in.CRISocket out.ClusterName = in.ClusterName out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes)) out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification @@ -422,6 +426,32 @@ func Convert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration(in *kubeadm return autoConvert_kubeadm_NodeConfiguration_To_v1alpha2_NodeConfiguration(in, out, s) } +func autoConvert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error { + out.Name = in.Name + out.CRISocket = in.CRISocket + out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints)) + out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + return nil +} + +// Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions is an autogenerated conversion function. +func Convert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error { + return autoConvert_v1alpha2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in, out, s) +} + +func autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { + out.Name = in.Name + out.CRISocket = in.CRISocket + out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints)) + out.ExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)) + return nil +} + +// Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions is an autogenerated conversion function. +func Convert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error { + return autoConvert_kubeadm_NodeRegistrationOptions_To_v1alpha2_NodeRegistrationOptions(in, out, s) +} + func autoConvert_v1alpha2_TokenDiscovery_To_kubeadm_TokenDiscovery(in *TokenDiscovery, out *kubeadm.TokenDiscovery, s conversion.Scope) error { out.ID = in.ID out.Secret = in.Secret diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.deepcopy.go index af11b89b973..9d5fac577c0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha2 import ( + core_v1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" v1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1" @@ -231,6 +232,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { in.Etcd.DeepCopyInto(&out.Etcd) in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration) out.Networking = in.Networking + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) if in.TokenTTL != nil { in, out := &in.TokenTTL, &out.TokenTTL if *in == nil { @@ -340,6 +342,7 @@ func (in *Networking) DeepCopy() *Networking { func (in *NodeConfiguration) DeepCopyInto(out *NodeConfiguration) { *out = *in out.TypeMeta = in.TypeMeta + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) if in.DiscoveryTokenAPIServers != nil { in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers *out = make([]string, len(*in)) @@ -387,6 +390,36 @@ func (in *NodeConfiguration) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { + *out = *in + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]core_v1.Taint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeRegistrationOptions. +func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions { + if in == nil { + return nil + } + out := new(NodeRegistrationOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TokenDiscovery) DeepCopyInto(out *TokenDiscovery) { *out = *in diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.defaults.go index 7218ab95912..62ab2c8e971 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha2/zz_generated.defaults.go @@ -43,8 +43,10 @@ func SetObjectDefaults_MasterConfiguration(in *MasterConfiguration) { if in.KubeletConfiguration.BaseConfig != nil { v1beta1.SetDefaults_KubeletConfiguration(in.KubeletConfiguration.BaseConfig) } + SetDefaults_NodeRegistrationOptions(&in.NodeRegistration) } func SetObjectDefaults_NodeConfiguration(in *NodeConfiguration) { SetDefaults_NodeConfiguration(in) + SetDefaults_NodeRegistrationOptions(&in.NodeRegistration) } diff --git a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go index ce680355710..c7923fb1480 100644 --- a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package kubeadm import ( + core_v1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" v1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1" @@ -231,6 +232,7 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { in.Etcd.DeepCopyInto(&out.Etcd) in.KubeletConfiguration.DeepCopyInto(&out.KubeletConfiguration) out.Networking = in.Networking + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) if in.TokenTTL != nil { in, out := &in.TokenTTL, &out.TokenTTL if *in == nil { @@ -340,6 +342,7 @@ func (in *Networking) DeepCopy() *Networking { func (in *NodeConfiguration) DeepCopyInto(out *NodeConfiguration) { *out = *in out.TypeMeta = in.TypeMeta + in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) if in.DiscoveryTokenAPIServers != nil { in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers *out = make([]string, len(*in)) @@ -387,6 +390,36 @@ func (in *NodeConfiguration) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { + *out = *in + if in.Taints != nil { + in, out := &in.Taints, &out.Taints + *out = make([]core_v1.Taint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ExtraArgs != nil { + in, out := &in.ExtraArgs, &out.ExtraArgs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeRegistrationOptions. +func (in *NodeRegistrationOptions) DeepCopy() *NodeRegistrationOptions { + if in == nil { + return nil + } + out := new(NodeRegistrationOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TokenDiscovery) DeepCopyInto(out *TokenDiscovery) { *out = *in diff --git a/cmd/kubeadm/app/phases/kubelet/BUILD b/cmd/kubeadm/app/phases/kubelet/BUILD index 0dc4429f902..fecacf2a1f9 100644 --- a/cmd/kubeadm/app/phases/kubelet/BUILD +++ b/cmd/kubeadm/app/phases/kubelet/BUILD @@ -22,10 +22,8 @@ go_library( "//pkg/util/version:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/rbac/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", ], @@ -40,6 +38,7 @@ go_test( embed = [":go_default_library"], deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis/kubeletconfig/v1beta1:go_default_library", "//pkg/util/version:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", diff --git a/cmd/kubeadm/app/phases/markmaster/BUILD b/cmd/kubeadm/app/phases/markmaster/BUILD index efb2903f412..8d2dd295588 100644 --- a/cmd/kubeadm/app/phases/markmaster/BUILD +++ b/cmd/kubeadm/app/phases/markmaster/BUILD @@ -27,14 +27,9 @@ go_library( importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster", deps = [ "//cmd/kubeadm/app/constants:go_default_library", - "//pkg/kubelet/apis:go_default_library", + "//cmd/kubeadm/app/util/apiclient:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", ], ) diff --git a/cmd/kubeadm/app/util/apiclient/BUILD b/cmd/kubeadm/app/util/apiclient/BUILD index 5704d1228a1..d702b7328c9 100644 --- a/cmd/kubeadm/app/util/apiclient/BUILD +++ b/cmd/kubeadm/app/util/apiclient/BUILD @@ -19,6 +19,7 @@ go_library( deps = [ "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/util:go_default_library", + "//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/types:go_default_library", "//pkg/registry/core/service/ipallocator:go_default_library", "//vendor/k8s.io/api/apps/v1:go_default_library", @@ -28,8 +29,10 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", diff --git a/cmd/kubeadm/app/util/config/BUILD b/cmd/kubeadm/app/util/config/BUILD index 02eacd1243f..2bb14a8ded6 100644 --- a/cmd/kubeadm/app/util/config/BUILD +++ b/cmd/kubeadm/app/util/config/BUILD @@ -26,6 +26,7 @@ go_library( "//pkg/util/node:go_default_library", "//pkg/util/version:go_default_library", "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/cmd/kubeadm/app/util/config/testdata/conversion/master/internal.yaml b/cmd/kubeadm/app/util/config/testdata/conversion/master/internal.yaml index e6bcefe3baa..5267a7bce5c 100644 --- a/cmd/kubeadm/app/util/config/testdata/conversion/master/internal.yaml +++ b/cmd/kubeadm/app/util/config/testdata/conversion/master/internal.yaml @@ -11,7 +11,6 @@ AuditPolicyConfiguration: LogMaxAge: 2 Path: "" CIImageRepository: "" -CRISocket: /var/run/dockershim.sock CertificatesDir: /etc/kubernetes/pki ClusterName: kubernetes ControllerManagerExtraArgs: null @@ -140,8 +139,13 @@ Networking: DNSDomain: cluster.local PodSubnet: "" ServiceSubnet: 10.96.0.0/12 -NoTaintMaster: false -NodeName: master-1 +NodeRegistration: + CRISocket: /var/run/dockershim.sock + ExtraArgs: null + Name: master-1 + Taints: + - effect: NoSchedule + key: node-role.kubernetes.io/master SchedulerExtraArgs: null SchedulerExtraVolumes: null Token: s73ybu.6tw6wnqgp5z0wb77 diff --git a/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha2.yaml b/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha2.yaml index d5f30a60566..b6594689dc8 100644 --- a/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha2.yaml +++ b/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha2.yaml @@ -11,7 +11,6 @@ auditPolicy: path: "" certificatesDir: /etc/kubernetes/pki clusterName: kubernetes -criSocket: /var/run/dockershim.sock etcd: local: dataDir: /var/lib/etcd @@ -132,7 +131,12 @@ networking: dnsDomain: cluster.local podSubnet: "" serviceSubnet: 10.96.0.0/12 -nodeName: master-1 +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: master-1 + taints: + - effect: NoSchedule + key: node-role.kubernetes.io/master token: s73ybu.6tw6wnqgp5z0wb77 tokenGroups: - system:bootstrappers:kubeadm:default-node-token diff --git a/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml b/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml index dce87719dd4..517a1def2ca 100644 --- a/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml +++ b/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml @@ -1,5 +1,4 @@ CACertPath: /etc/kubernetes/pki/ca.crt -CRISocket: /var/run/dockershim.sock ClusterName: kubernetes DiscoveryFile: "" DiscoveryTimeout: 5m0s @@ -9,6 +8,10 @@ DiscoveryTokenAPIServers: DiscoveryTokenCACertHashes: null DiscoveryTokenUnsafeSkipCAVerification: true FeatureGates: null -NodeName: master-1 +NodeRegistration: + CRISocket: /var/run/dockershim.sock + ExtraArgs: null + Name: master-1 + Taints: null TLSBootstrapToken: abcdef.0123456789abcdef Token: abcdef.0123456789abcdef diff --git a/cmd/kubeadm/app/util/config/testdata/conversion/node/v1alpha2.yaml b/cmd/kubeadm/app/util/config/testdata/conversion/node/v1alpha2.yaml index 8cf55e5da27..40754a5888b 100644 --- a/cmd/kubeadm/app/util/config/testdata/conversion/node/v1alpha2.yaml +++ b/cmd/kubeadm/app/util/config/testdata/conversion/node/v1alpha2.yaml @@ -1,7 +1,6 @@ apiVersion: kubeadm.k8s.io/v1alpha2 caCertPath: /etc/kubernetes/pki/ca.crt clusterName: kubernetes -criSocket: /var/run/dockershim.sock discoveryFile: "" discoveryTimeout: 5m0s discoveryToken: abcdef.0123456789abcdef @@ -9,6 +8,8 @@ discoveryTokenAPIServers: - kube-apiserver:6443 discoveryTokenUnsafeSkipCAVerification: true kind: NodeConfiguration -nodeName: master-1 +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: master-1 tlsBootstrapToken: abcdef.0123456789abcdef token: abcdef.0123456789abcdef diff --git a/cmd/kubeadm/app/util/config/testdata/defaulting/master/defaulted.yaml b/cmd/kubeadm/app/util/config/testdata/defaulting/master/defaulted.yaml index 6cea70575d7..42647648181 100644 --- a/cmd/kubeadm/app/util/config/testdata/defaulting/master/defaulted.yaml +++ b/cmd/kubeadm/app/util/config/testdata/defaulting/master/defaulted.yaml @@ -9,7 +9,6 @@ auditPolicy: path: "" certificatesDir: /var/lib/kubernetes/pki clusterName: kubernetes -criSocket: /var/run/criruntime.sock etcd: local: dataDir: /var/lib/etcd @@ -127,7 +126,12 @@ networking: dnsDomain: cluster.global podSubnet: "" serviceSubnet: 10.196.0.0/12 -nodeName: master-1 +nodeRegistration: + criSocket: /var/run/criruntime.sock + name: master-1 + taints: + - effect: NoSchedule + key: node-role.kubernetes.io/master token: s73ybu.6tw6wnqgp5z0wb77 tokenGroups: - system:bootstrappers:kubeadm:default-node-token diff --git a/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha2.yaml b/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted.yaml similarity index 83% rename from cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha2.yaml rename to cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted.yaml index 4be5386539d..85c6b9b481c 100644 --- a/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha2.yaml +++ b/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted.yaml @@ -1,7 +1,6 @@ apiVersion: kubeadm.k8s.io/v1alpha2 caCertPath: /etc/kubernetes/pki/ca.crt clusterName: kubernetes -criSocket: /var/run/dockershim.sock discoveryFile: "" discoveryTimeout: 5m0s discoveryToken: abcdef.0123456789abcdef @@ -9,6 +8,8 @@ discoveryTokenAPIServers: - kube-apiserver:6443 discoveryTokenUnsafeSkipCAVerification: true kind: NodeConfiguration -nodeName: thegopher +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: thegopher tlsBootstrapToken: abcdef.0123456789abcdef token: abcdef.0123456789abcdef diff --git a/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha1.yaml b/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha1.yaml deleted file mode 100644 index 5ffc2205fdf..00000000000 --- a/cmd/kubeadm/app/util/config/testdata/defaulting/node/defaulted_v1alpha1.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kubeadm.k8s.io/v1alpha1 -caCertPath: /etc/kubernetes/pki/ca.crt -clusterName: kubernetes -criSocket: /var/run/dockershim.sock -discoveryFile: "" -discoveryTimeout: 5m0s -discoveryToken: abcdef.0123456789abcdef -discoveryTokenAPIServers: -- kube-apiserver:6443 -discoveryTokenUnsafeSkipCAVerification: true -kind: NodeConfiguration -nodeName: thegopher -tlsBootstrapToken: abcdef.0123456789abcdef -token: abcdef.0123456789abcdef From 753632d85b7639ffadb05eed3e49dbfbbd5360b6 Mon Sep 17 00:00:00 2001 From: Sandeep Rajan Date: Fri, 23 Mar 2018 14:57:36 -0400 Subject: [PATCH 106/416] create coredns and kube-dns folders --- cluster/addons/dns/{ => coredns}/Makefile | 2 +- .../dns/{ => coredns}/coredns.yaml.base | 0 .../addons/dns/{ => coredns}/coredns.yaml.in | 0 .../addons/dns/{ => coredns}/coredns.yaml.sed | 0 .../dns/{ => coredns}/transforms2salt.sed | 0 .../dns/{ => coredns}/transforms2sed.sed | 0 cluster/addons/dns/kube-dns/Makefile | 34 +++++++++++++++++++ cluster/addons/dns/{ => kube-dns}/README.md | 0 .../dns/{ => kube-dns}/kube-dns.yaml.base | 0 .../dns/{ => kube-dns}/kube-dns.yaml.in | 0 .../dns/{ => kube-dns}/kube-dns.yaml.sed | 0 .../addons/dns/kube-dns/transforms2salt.sed | 4 +++ .../addons/dns/kube-dns/transforms2sed.sed | 4 +++ cluster/centos/deployAddons.sh | 2 +- cluster/gce/gci/configure-helper.sh | 11 +++--- hack/local-up-cluster.sh | 2 +- 16 files changed, 51 insertions(+), 8 deletions(-) rename cluster/addons/dns/{ => coredns}/Makefile (92%) rename cluster/addons/dns/{ => coredns}/coredns.yaml.base (100%) rename cluster/addons/dns/{ => coredns}/coredns.yaml.in (100%) rename cluster/addons/dns/{ => coredns}/coredns.yaml.sed (100%) rename cluster/addons/dns/{ => coredns}/transforms2salt.sed (100%) rename cluster/addons/dns/{ => coredns}/transforms2sed.sed (100%) create mode 100644 cluster/addons/dns/kube-dns/Makefile rename cluster/addons/dns/{ => kube-dns}/README.md (100%) rename cluster/addons/dns/{ => kube-dns}/kube-dns.yaml.base (100%) rename cluster/addons/dns/{ => kube-dns}/kube-dns.yaml.in (100%) rename cluster/addons/dns/{ => kube-dns}/kube-dns.yaml.sed (100%) create mode 100644 cluster/addons/dns/kube-dns/transforms2salt.sed create mode 100644 cluster/addons/dns/kube-dns/transforms2sed.sed diff --git a/cluster/addons/dns/Makefile b/cluster/addons/dns/coredns/Makefile similarity index 92% rename from cluster/addons/dns/Makefile rename to cluster/addons/dns/coredns/Makefile index 01f45e4ba51..d3455dd853f 100644 --- a/cluster/addons/dns/Makefile +++ b/cluster/addons/dns/coredns/Makefile @@ -29,6 +29,6 @@ all: transform %.sed: %.base sed -f transforms2sed.sed $< | sed s/__SOURCE_FILENAME__/$ $@ -transform: kube-dns.yaml.in kube-dns.yaml.sed coredns.yaml.in coredns.yaml.sed +transform: coredns.yaml.in coredns.yaml.sed .PHONY: transform diff --git a/cluster/addons/dns/coredns.yaml.base b/cluster/addons/dns/coredns/coredns.yaml.base similarity index 100% rename from cluster/addons/dns/coredns.yaml.base rename to cluster/addons/dns/coredns/coredns.yaml.base diff --git a/cluster/addons/dns/coredns.yaml.in b/cluster/addons/dns/coredns/coredns.yaml.in similarity index 100% rename from cluster/addons/dns/coredns.yaml.in rename to cluster/addons/dns/coredns/coredns.yaml.in diff --git a/cluster/addons/dns/coredns.yaml.sed b/cluster/addons/dns/coredns/coredns.yaml.sed similarity index 100% rename from cluster/addons/dns/coredns.yaml.sed rename to cluster/addons/dns/coredns/coredns.yaml.sed diff --git a/cluster/addons/dns/transforms2salt.sed b/cluster/addons/dns/coredns/transforms2salt.sed similarity index 100% rename from cluster/addons/dns/transforms2salt.sed rename to cluster/addons/dns/coredns/transforms2salt.sed diff --git a/cluster/addons/dns/transforms2sed.sed b/cluster/addons/dns/coredns/transforms2sed.sed similarity index 100% rename from cluster/addons/dns/transforms2sed.sed rename to cluster/addons/dns/coredns/transforms2sed.sed diff --git a/cluster/addons/dns/kube-dns/Makefile b/cluster/addons/dns/kube-dns/Makefile new file mode 100644 index 00000000000..50419ff3247 --- /dev/null +++ b/cluster/addons/dns/kube-dns/Makefile @@ -0,0 +1,34 @@ +# 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. + +# Makefile for the kubedns underscore templates to Salt/Pillar and other formats. + +# If you update the *.base templates, please run this Makefile before pushing. +# +# Usage: +# make + +all: transform + +# .base -> .in pattern rule +%.in: %.base + sed -f transforms2salt.sed $< | sed s/__SOURCE_FILENAME__/$ $@ + +# .base -> .sed pattern rule +%.sed: %.base + sed -f transforms2sed.sed $< | sed s/__SOURCE_FILENAME__/$ $@ + +transform: kube-dns.yaml.in kube-dns.yaml.sed + +.PHONY: transform diff --git a/cluster/addons/dns/README.md b/cluster/addons/dns/kube-dns/README.md similarity index 100% rename from cluster/addons/dns/README.md rename to cluster/addons/dns/kube-dns/README.md diff --git a/cluster/addons/dns/kube-dns.yaml.base b/cluster/addons/dns/kube-dns/kube-dns.yaml.base similarity index 100% rename from cluster/addons/dns/kube-dns.yaml.base rename to cluster/addons/dns/kube-dns/kube-dns.yaml.base diff --git a/cluster/addons/dns/kube-dns.yaml.in b/cluster/addons/dns/kube-dns/kube-dns.yaml.in similarity index 100% rename from cluster/addons/dns/kube-dns.yaml.in rename to cluster/addons/dns/kube-dns/kube-dns.yaml.in diff --git a/cluster/addons/dns/kube-dns.yaml.sed b/cluster/addons/dns/kube-dns/kube-dns.yaml.sed similarity index 100% rename from cluster/addons/dns/kube-dns.yaml.sed rename to cluster/addons/dns/kube-dns/kube-dns.yaml.sed diff --git a/cluster/addons/dns/kube-dns/transforms2salt.sed b/cluster/addons/dns/kube-dns/transforms2salt.sed new file mode 100644 index 00000000000..0a0778b9292 --- /dev/null +++ b/cluster/addons/dns/kube-dns/transforms2salt.sed @@ -0,0 +1,4 @@ +s/__PILLAR__DNS__SERVER__/{{ pillar['dns_server'] }}/g +s/__PILLAR__DNS__DOMAIN__/{{ pillar['dns_domain'] }}/g +s/__PILLAR__CLUSTER_CIDR__/{{ pillar['service_cluster_ip_range'] }}/g +s/__MACHINE_GENERATED_WARNING__/Warning: This is a file generated from the base underscore template file: __SOURCE_FILENAME__/g diff --git a/cluster/addons/dns/kube-dns/transforms2sed.sed b/cluster/addons/dns/kube-dns/transforms2sed.sed new file mode 100644 index 00000000000..7d64f8e0b51 --- /dev/null +++ b/cluster/addons/dns/kube-dns/transforms2sed.sed @@ -0,0 +1,4 @@ +s/__PILLAR__DNS__SERVER__/$DNS_SERVER_IP/g +s/__PILLAR__DNS__DOMAIN__/$DNS_DOMAIN/g +s/__PILLAR__CLUSTER_CIDR__/$SERVICE_CLUSTER_IP_RANGE/g +s/__MACHINE_GENERATED_WARNING__/Warning: This is a file generated from the base underscore template file: __SOURCE_FILENAME__/g diff --git a/cluster/centos/deployAddons.sh b/cluster/centos/deployAddons.sh index 349b308ddbd..b19d1cd1ef7 100755 --- a/cluster/centos/deployAddons.sh +++ b/cluster/centos/deployAddons.sh @@ -26,7 +26,7 @@ export KUBE_CONFIG_FILE=${KUBE_CONFIG_FILE:-${KUBE_ROOT}/cluster/centos/config-d function deploy_dns { echo "Deploying DNS on Kubernetes" - cp "${KUBE_ROOT}/cluster/addons/dns/kube-dns.yaml.sed" kube-dns.yaml + cp "${KUBE_ROOT}/cluster/addons/dns/kube-dns/kube-dns.yaml.sed" kube-dns.yaml sed -i -e "s/\\\$DNS_DOMAIN/${DNS_DOMAIN}/g" kube-dns.yaml sed -i -e "s/\\\$DNS_SERVER_IP/${DNS_SERVER_IP}/g" kube-dns.yaml diff --git a/cluster/gce/gci/configure-helper.sh b/cluster/gce/gci/configure-helper.sh index 0935fc37f2c..9cab03492e2 100644 --- a/cluster/gce/gci/configure-helper.sh +++ b/cluster/gce/gci/configure-helper.sh @@ -2175,8 +2175,8 @@ function update-dashboard-controller { # Sets up the manifests of coreDNS for k8s addons. function setup-coredns-manifest { - local -r coredns_file="${dst_dir}/dns/coredns.yaml" - mv "${dst_dir}/dns/coredns.yaml.in" "${coredns_file}" + local -r coredns_file="${dst_dir}/dns/coredns/coredns.yaml" + mv "${dst_dir}/dns/coredns/coredns.yaml.in" "${coredns_file}" # Replace the salt configurations with variable values. sed -i -e "s@{{ *pillar\['dns_domain'\] *}}@${DNS_DOMAIN}@g" "${coredns_file}" sed -i -e "s@{{ *pillar\['dns_server'\] *}}@${DNS_SERVER_IP}@g" "${coredns_file}" @@ -2215,8 +2215,8 @@ function setup-fluentd { # Sets up the manifests of kube-dns for k8s addons. function setup-kube-dns-manifest { - local -r kubedns_file="${dst_dir}/dns/kube-dns.yaml" - mv "${dst_dir}/dns/kube-dns.yaml.in" "${kubedns_file}" + local -r kubedns_file="${dst_dir}/dns/kube-dns/kube-dns.yaml" + mv "${dst_dir}/dns/kube-dns/kube-dns.yaml.in" "${kubedns_file}" if [ -n "${CUSTOM_KUBE_DNS_YAML:-}" ]; then # Replace with custom GKE kube-dns deployment. cat > "${kubedns_file}" < Date: Tue, 1 May 2018 02:12:29 -0400 Subject: [PATCH 107/416] Auto-generated files --- .../pluginregistration/v1alpha1/api.pb.go | 1027 +++++++++++++++++ .../example_plugin_apis/v1beta1/api.pb.go | 632 ++++++++++ .../example_plugin_apis/v1beta2/api.pb.go | 633 ++++++++++ 3 files changed, 2292 insertions(+) create mode 100644 pkg/kubelet/apis/pluginregistration/v1alpha1/api.pb.go create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.pb.go create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.pb.go diff --git a/pkg/kubelet/apis/pluginregistration/v1alpha1/api.pb.go b/pkg/kubelet/apis/pluginregistration/v1alpha1/api.pb.go new file mode 100644 index 00000000000..96e1d571dba --- /dev/null +++ b/pkg/kubelet/apis/pluginregistration/v1alpha1/api.pb.go @@ -0,0 +1,1027 @@ +/* +Copyright 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. +*/ + +// Code generated by protoc-gen-gogo. +// source: api.proto +// DO NOT EDIT! + +/* + Package pluginregistration is a generated protocol buffer package. + + It is generated from these files: + api.proto + + It has these top-level messages: + PluginInfo + RegistrationStatus + RegistrationStatusResponse + InfoRequest +*/ +package pluginregistration + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// PluginInfo is the message sent from a plugin to the Kubelet pluginwatcher for plugin registration +type PluginInfo struct { + // Type of the Plugin. CSIPlugin or DevicePlugin + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // Plugin name that uniquely identifies the plugin for the given plugin type. + // For DevicePlugin, this is the resource name that the plugin manages and + // should follow the extended resource name convention. + // For CSI, this is the CSI driver registrar name. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Optional endpoint location. If found set by Kubelet component, + // Kubelet component will use this endpoint for specific requests. + // This allows the plugin to register using one endpoint and possibly use + // a different socket for control operations. CSI uses this model to delegate + // its registration external from the plugin. + Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + // Plugin service API versions the plugin supports. + // For DevicePlugin, this maps to the deviceplugin API versions the + // plugin supports at the given socket. + // The Kubelet component communicating with the plugin should be able + // to choose any preferred version from this list, or returns an error + // if none of the listed versions is supported. + SupportedVersions []string `protobuf:"bytes,4,rep,name=supported_versions,json=supportedVersions" json:"supported_versions,omitempty"` +} + +func (m *PluginInfo) Reset() { *m = PluginInfo{} } +func (*PluginInfo) ProtoMessage() {} +func (*PluginInfo) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } + +func (m *PluginInfo) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *PluginInfo) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PluginInfo) GetEndpoint() string { + if m != nil { + return m.Endpoint + } + return "" +} + +func (m *PluginInfo) GetSupportedVersions() []string { + if m != nil { + return m.SupportedVersions + } + return nil +} + +// RegistrationStatus is the message sent from Kubelet pluginwatcher to the plugin for notification on registration status +type RegistrationStatus struct { + // True if plugin gets registered successfully at Kubelet + PluginRegistered bool `protobuf:"varint,1,opt,name=plugin_registered,json=pluginRegistered,proto3" json:"plugin_registered,omitempty"` + // Error message in case plugin fails to register, empty string otherwise + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *RegistrationStatus) Reset() { *m = RegistrationStatus{} } +func (*RegistrationStatus) ProtoMessage() {} +func (*RegistrationStatus) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{1} } + +func (m *RegistrationStatus) GetPluginRegistered() bool { + if m != nil { + return m.PluginRegistered + } + return false +} + +func (m *RegistrationStatus) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +// RegistrationStatusResponse is sent by plugin to kubelet in response to RegistrationStatus RPC +type RegistrationStatusResponse struct { +} + +func (m *RegistrationStatusResponse) Reset() { *m = RegistrationStatusResponse{} } +func (*RegistrationStatusResponse) ProtoMessage() {} +func (*RegistrationStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{2} } + +// InfoRequest is the empty request message from Kubelet +type InfoRequest struct { +} + +func (m *InfoRequest) Reset() { *m = InfoRequest{} } +func (*InfoRequest) ProtoMessage() {} +func (*InfoRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{3} } + +func init() { + proto.RegisterType((*PluginInfo)(nil), "pluginregistration.PluginInfo") + proto.RegisterType((*RegistrationStatus)(nil), "pluginregistration.RegistrationStatus") + proto.RegisterType((*RegistrationStatusResponse)(nil), "pluginregistration.RegistrationStatusResponse") + proto.RegisterType((*InfoRequest)(nil), "pluginregistration.InfoRequest") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Registration service + +type RegistrationClient interface { + GetInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*PluginInfo, error) + NotifyRegistrationStatus(ctx context.Context, in *RegistrationStatus, opts ...grpc.CallOption) (*RegistrationStatusResponse, error) +} + +type registrationClient struct { + cc *grpc.ClientConn +} + +func NewRegistrationClient(cc *grpc.ClientConn) RegistrationClient { + return ®istrationClient{cc} +} + +func (c *registrationClient) GetInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*PluginInfo, error) { + out := new(PluginInfo) + err := grpc.Invoke(ctx, "/pluginregistration.Registration/GetInfo", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registrationClient) NotifyRegistrationStatus(ctx context.Context, in *RegistrationStatus, opts ...grpc.CallOption) (*RegistrationStatusResponse, error) { + out := new(RegistrationStatusResponse) + err := grpc.Invoke(ctx, "/pluginregistration.Registration/NotifyRegistrationStatus", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Registration service + +type RegistrationServer interface { + GetInfo(context.Context, *InfoRequest) (*PluginInfo, error) + NotifyRegistrationStatus(context.Context, *RegistrationStatus) (*RegistrationStatusResponse, error) +} + +func RegisterRegistrationServer(s *grpc.Server, srv RegistrationServer) { + s.RegisterService(&_Registration_serviceDesc, srv) +} + +func _Registration_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistrationServer).GetInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pluginregistration.Registration/GetInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistrationServer).GetInfo(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Registration_NotifyRegistrationStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegistrationStatus) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistrationServer).NotifyRegistrationStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pluginregistration.Registration/NotifyRegistrationStatus", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistrationServer).NotifyRegistrationStatus(ctx, req.(*RegistrationStatus)) + } + return interceptor(ctx, in, info, handler) +} + +var _Registration_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pluginregistration.Registration", + HandlerType: (*RegistrationServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetInfo", + Handler: _Registration_GetInfo_Handler, + }, + { + MethodName: "NotifyRegistrationStatus", + Handler: _Registration_NotifyRegistrationStatus_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func (m *PluginInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PluginInfo) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Type) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + } + if len(m.Name) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Endpoint) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Endpoint))) + i += copy(dAtA[i:], m.Endpoint) + } + if len(m.SupportedVersions) > 0 { + for _, s := range m.SupportedVersions { + dAtA[i] = 0x22 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func (m *RegistrationStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistrationStatus) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.PluginRegistered { + dAtA[i] = 0x8 + i++ + if m.PluginRegistered { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.Error) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Error))) + i += copy(dAtA[i:], m.Error) + } + return i, nil +} + +func (m *RegistrationStatusResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RegistrationStatusResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func (m *InfoRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *InfoRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func encodeFixed64Api(dAtA []byte, offset int, v uint64) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + dAtA[offset+4] = uint8(v >> 32) + dAtA[offset+5] = uint8(v >> 40) + dAtA[offset+6] = uint8(v >> 48) + dAtA[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Api(dAtA []byte, offset int, v uint32) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *PluginInfo) Size() (n int) { + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.Endpoint) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.SupportedVersions) > 0 { + for _, s := range m.SupportedVersions { + l = len(s) + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *RegistrationStatus) Size() (n int) { + var l int + _ = l + if m.PluginRegistered { + n += 2 + } + l = len(m.Error) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *RegistrationStatusResponse) Size() (n int) { + var l int + _ = l + return n +} + +func (m *InfoRequest) Size() (n int) { + var l int + _ = l + return n +} + +func sovApi(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *PluginInfo) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PluginInfo{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Endpoint:` + fmt.Sprintf("%v", this.Endpoint) + `,`, + `SupportedVersions:` + fmt.Sprintf("%v", this.SupportedVersions) + `,`, + `}`, + }, "") + return s +} +func (this *RegistrationStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RegistrationStatus{`, + `PluginRegistered:` + fmt.Sprintf("%v", this.PluginRegistered) + `,`, + `Error:` + fmt.Sprintf("%v", this.Error) + `,`, + `}`, + }, "") + return s +} +func (this *RegistrationStatusResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RegistrationStatusResponse{`, + `}`, + }, "") + return s +} +func (this *InfoRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&InfoRequest{`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *PluginInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PluginInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Endpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SupportedVersions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SupportedVersions = append(m.SupportedVersions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistrationStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistrationStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistrationStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PluginRegistered", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.PluginRegistered = bool(v != 0) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RegistrationStatusResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RegistrationStatusResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RegistrationStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *InfoRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: InfoRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InfoRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthApi + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipApi(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } + +var fileDescriptorApi = []byte{ + // 337 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x41, 0x4b, 0x33, 0x31, + 0x14, 0xdc, 0x7c, 0xed, 0xa7, 0xed, 0x53, 0xc1, 0x06, 0x0f, 0xcb, 0x52, 0x62, 0xd9, 0x83, 0x14, + 0xa4, 0x5b, 0xd0, 0x7f, 0xe0, 0x45, 0x04, 0x11, 0x89, 0xa0, 0xc7, 0xb2, 0xb5, 0xaf, 0x6b, 0xc0, + 0x26, 0x31, 0xc9, 0x0a, 0x3d, 0xe9, 0x4f, 0xf0, 0x67, 0xf5, 0x28, 0x9e, 0x3c, 0xda, 0xf5, 0x8f, + 0x48, 0xb3, 0x65, 0x2d, 0xb4, 0x07, 0x6f, 0x6f, 0xe6, 0x4d, 0x1e, 0x33, 0x43, 0xa0, 0x99, 0x6a, + 0x91, 0x68, 0xa3, 0x9c, 0xa2, 0x54, 0x3f, 0xe6, 0x99, 0x90, 0x06, 0x33, 0x61, 0x9d, 0x49, 0x9d, + 0x50, 0x32, 0xea, 0x65, 0xc2, 0x3d, 0xe4, 0xc3, 0xe4, 0x5e, 0x4d, 0xfa, 0x99, 0xca, 0x54, 0xdf, + 0x4b, 0x87, 0xf9, 0xd8, 0x23, 0x0f, 0xfc, 0x54, 0x9e, 0x88, 0x5f, 0x00, 0xae, 0xfd, 0x91, 0x0b, + 0x39, 0x56, 0x94, 0x42, 0xdd, 0x4d, 0x35, 0x86, 0xa4, 0x43, 0xba, 0x4d, 0xee, 0xe7, 0x05, 0x27, + 0xd3, 0x09, 0x86, 0xff, 0x4a, 0x6e, 0x31, 0xd3, 0x08, 0x1a, 0x28, 0x47, 0x5a, 0x09, 0xe9, 0xc2, + 0x9a, 0xe7, 0x2b, 0x4c, 0x7b, 0x40, 0x6d, 0xae, 0xb5, 0x32, 0x0e, 0x47, 0x83, 0x67, 0x34, 0x56, + 0x28, 0x69, 0xc3, 0x7a, 0xa7, 0xd6, 0x6d, 0xf2, 0x56, 0xb5, 0xb9, 0x5d, 0x2e, 0xe2, 0x3b, 0xa0, + 0x7c, 0xc5, 0xff, 0x8d, 0x4b, 0x5d, 0x6e, 0xe9, 0x31, 0xb4, 0xca, 0x6c, 0x83, 0x32, 0x1c, 0x1a, + 0x1c, 0x79, 0x57, 0x0d, 0xbe, 0x5f, 0x2e, 0x78, 0xc5, 0xd3, 0x03, 0xf8, 0x8f, 0xc6, 0x28, 0xb3, + 0xb4, 0x58, 0x82, 0xb8, 0x0d, 0xd1, 0xfa, 0x61, 0x8e, 0x56, 0x2b, 0x69, 0x31, 0xde, 0x83, 0x9d, + 0x45, 0x62, 0x8e, 0x4f, 0x39, 0x5a, 0x77, 0xf2, 0x41, 0x60, 0x77, 0x55, 0x4d, 0x2f, 0x61, 0xfb, + 0x1c, 0x9d, 0x2f, 0xe5, 0x30, 0x59, 0xaf, 0x39, 0x59, 0x79, 0x1c, 0xb1, 0x4d, 0x82, 0xdf, 0x56, + 0xe3, 0x80, 0x3a, 0x08, 0xaf, 0x94, 0x13, 0xe3, 0xe9, 0x86, 0xa8, 0x47, 0x9b, 0x5e, 0xaf, 0xeb, + 0xa2, 0xe4, 0x6f, 0xba, 0x2a, 0x61, 0x70, 0xd6, 0x9e, 0xcd, 0x19, 0xf9, 0x9c, 0xb3, 0xe0, 0xb5, + 0x60, 0x64, 0x56, 0x30, 0xf2, 0x5e, 0x30, 0xf2, 0x55, 0x30, 0xf2, 0xf6, 0xcd, 0x82, 0xe1, 0x96, + 0xff, 0x00, 0xa7, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe4, 0xc0, 0xe3, 0x42, 0x50, 0x02, 0x00, + 0x00, +} diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.pb.go b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.pb.go new file mode 100644 index 00000000000..671e3df493b --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.pb.go @@ -0,0 +1,632 @@ +/* +Copyright 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. +*/ + +// Code generated by protoc-gen-gogo. +// source: api.proto +// DO NOT EDIT! + +/* + Package v1beta1 is a generated protocol buffer package. + + It is generated from these files: + api.proto + + It has these top-level messages: + ExampleRequest + ExampleResponse +*/ +package v1beta1 + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type ExampleRequest struct { + Request string `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` + V1Beta1Field string `protobuf:"bytes,2,opt,name=v1beta1_field,json=v1beta1Field,proto3" json:"v1beta1_field,omitempty"` +} + +func (m *ExampleRequest) Reset() { *m = ExampleRequest{} } +func (*ExampleRequest) ProtoMessage() {} +func (*ExampleRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } + +func (m *ExampleRequest) GetRequest() string { + if m != nil { + return m.Request + } + return "" +} + +func (m *ExampleRequest) GetV1Beta1Field() string { + if m != nil { + return m.V1Beta1Field + } + return "" +} + +type ExampleResponse struct { + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *ExampleResponse) Reset() { *m = ExampleResponse{} } +func (*ExampleResponse) ProtoMessage() {} +func (*ExampleResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{1} } + +func (m *ExampleResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func init() { + proto.RegisterType((*ExampleRequest)(nil), "v1beta1.ExampleRequest") + proto.RegisterType((*ExampleResponse)(nil), "v1beta1.ExampleResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Example service + +type ExampleClient interface { + GetExampleInfo(ctx context.Context, in *ExampleRequest, opts ...grpc.CallOption) (*ExampleResponse, error) +} + +type exampleClient struct { + cc *grpc.ClientConn +} + +func NewExampleClient(cc *grpc.ClientConn) ExampleClient { + return &exampleClient{cc} +} + +func (c *exampleClient) GetExampleInfo(ctx context.Context, in *ExampleRequest, opts ...grpc.CallOption) (*ExampleResponse, error) { + out := new(ExampleResponse) + err := grpc.Invoke(ctx, "/v1beta1.Example/GetExampleInfo", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Example service + +type ExampleServer interface { + GetExampleInfo(context.Context, *ExampleRequest) (*ExampleResponse, error) +} + +func RegisterExampleServer(s *grpc.Server, srv ExampleServer) { + s.RegisterService(&_Example_serviceDesc, srv) +} + +func _Example_GetExampleInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExampleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExampleServer).GetExampleInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1beta1.Example/GetExampleInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExampleServer).GetExampleInfo(ctx, req.(*ExampleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Example_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1beta1.Example", + HandlerType: (*ExampleServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetExampleInfo", + Handler: _Example_GetExampleInfo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func (m *ExampleRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExampleRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Request) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Request))) + i += copy(dAtA[i:], m.Request) + } + if len(m.V1Beta1Field) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.V1Beta1Field))) + i += copy(dAtA[i:], m.V1Beta1Field) + } + return i, nil +} + +func (m *ExampleResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExampleResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Error) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Error))) + i += copy(dAtA[i:], m.Error) + } + return i, nil +} + +func encodeFixed64Api(dAtA []byte, offset int, v uint64) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + dAtA[offset+4] = uint8(v >> 32) + dAtA[offset+5] = uint8(v >> 40) + dAtA[offset+6] = uint8(v >> 48) + dAtA[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Api(dAtA []byte, offset int, v uint32) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *ExampleRequest) Size() (n int) { + var l int + _ = l + l = len(m.Request) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.V1Beta1Field) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *ExampleResponse) Size() (n int) { + var l int + _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func sovApi(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ExampleRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExampleRequest{`, + `Request:` + fmt.Sprintf("%v", this.Request) + `,`, + `V1Beta1Field:` + fmt.Sprintf("%v", this.V1Beta1Field) + `,`, + `}`, + }, "") + return s +} +func (this *ExampleResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExampleResponse{`, + `Error:` + fmt.Sprintf("%v", this.Error) + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *ExampleRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExampleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExampleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Request = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field V1Beta1Field", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.V1Beta1Field = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExampleResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExampleResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExampleResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthApi + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipApi(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } + +var fileDescriptorApi = []byte{ + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0x94, 0xd2, + 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, 0xd7, + 0x07, 0xcb, 0x27, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, 0x98, 0x05, 0xd1, 0xa7, 0xe4, 0xcf, 0xc5, + 0xe7, 0x5a, 0x91, 0x98, 0x5b, 0x90, 0x93, 0x1a, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x24, + 0xc1, 0xc5, 0x5e, 0x04, 0x61, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x42, 0xca, + 0x5c, 0xbc, 0x50, 0x5b, 0xe2, 0xd3, 0x32, 0x53, 0x73, 0x52, 0x24, 0x98, 0xc0, 0xf2, 0x3c, 0x50, + 0x41, 0x37, 0x90, 0x98, 0x92, 0x3a, 0x17, 0x3f, 0xdc, 0xc0, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, + 0x21, 0x11, 0x2e, 0xd6, 0xd4, 0xa2, 0xa2, 0xfc, 0x22, 0xa8, 0x79, 0x10, 0x8e, 0x51, 0x00, 0x17, + 0x3b, 0x54, 0xa1, 0x90, 0x2b, 0x17, 0x9f, 0x7b, 0x6a, 0x09, 0x94, 0xe7, 0x99, 0x97, 0x96, 0x2f, + 0x24, 0xae, 0x07, 0x35, 0x54, 0x0f, 0xd5, 0x75, 0x52, 0x12, 0x98, 0x12, 0x10, 0x5b, 0x94, 0x18, + 0x9c, 0x64, 0x4e, 0x3c, 0x94, 0x63, 0xbc, 0xf1, 0x50, 0x8e, 0xa1, 0xe1, 0x91, 0x1c, 0xe3, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x43, + 0x12, 0x1b, 0xd8, 0xc3, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0x62, 0xd1, 0x9c, 0x35, + 0x01, 0x00, 0x00, +} diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.pb.go b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.pb.go new file mode 100644 index 00000000000..0c63b31429f --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.pb.go @@ -0,0 +1,633 @@ +/* +Copyright 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. +*/ + +// Code generated by protoc-gen-gogo. +// source: api.proto +// DO NOT EDIT! + +/* + Package v1beta2 is a generated protocol buffer package. + + It is generated from these files: + api.proto + + It has these top-level messages: + ExampleRequest + ExampleResponse +*/ +package v1beta2 + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// Renames a field from v1beta1 ExampleRequest. +type ExampleRequest struct { + Request string `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` + V1Beta2Field string `protobuf:"bytes,2,opt,name=v1beta2_field,json=v1beta2Field,proto3" json:"v1beta2_field,omitempty"` +} + +func (m *ExampleRequest) Reset() { *m = ExampleRequest{} } +func (*ExampleRequest) ProtoMessage() {} +func (*ExampleRequest) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } + +func (m *ExampleRequest) GetRequest() string { + if m != nil { + return m.Request + } + return "" +} + +func (m *ExampleRequest) GetV1Beta2Field() string { + if m != nil { + return m.V1Beta2Field + } + return "" +} + +type ExampleResponse struct { + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *ExampleResponse) Reset() { *m = ExampleResponse{} } +func (*ExampleResponse) ProtoMessage() {} +func (*ExampleResponse) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{1} } + +func (m *ExampleResponse) GetError() string { + if m != nil { + return m.Error + } + return "" +} + +func init() { + proto.RegisterType((*ExampleRequest)(nil), "v1beta2.ExampleRequest") + proto.RegisterType((*ExampleResponse)(nil), "v1beta2.ExampleResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for Example service + +type ExampleClient interface { + GetExampleInfo(ctx context.Context, in *ExampleRequest, opts ...grpc.CallOption) (*ExampleResponse, error) +} + +type exampleClient struct { + cc *grpc.ClientConn +} + +func NewExampleClient(cc *grpc.ClientConn) ExampleClient { + return &exampleClient{cc} +} + +func (c *exampleClient) GetExampleInfo(ctx context.Context, in *ExampleRequest, opts ...grpc.CallOption) (*ExampleResponse, error) { + out := new(ExampleResponse) + err := grpc.Invoke(ctx, "/v1beta2.Example/GetExampleInfo", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Example service + +type ExampleServer interface { + GetExampleInfo(context.Context, *ExampleRequest) (*ExampleResponse, error) +} + +func RegisterExampleServer(s *grpc.Server, srv ExampleServer) { + s.RegisterService(&_Example_serviceDesc, srv) +} + +func _Example_GetExampleInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ExampleRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExampleServer).GetExampleInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1beta2.Example/GetExampleInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExampleServer).GetExampleInfo(ctx, req.(*ExampleRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Example_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1beta2.Example", + HandlerType: (*ExampleServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetExampleInfo", + Handler: _Example_GetExampleInfo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func (m *ExampleRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExampleRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Request) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Request))) + i += copy(dAtA[i:], m.Request) + } + if len(m.V1Beta2Field) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.V1Beta2Field))) + i += copy(dAtA[i:], m.V1Beta2Field) + } + return i, nil +} + +func (m *ExampleResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExampleResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Error) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.Error))) + i += copy(dAtA[i:], m.Error) + } + return i, nil +} + +func encodeFixed64Api(dAtA []byte, offset int, v uint64) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + dAtA[offset+4] = uint8(v >> 32) + dAtA[offset+5] = uint8(v >> 40) + dAtA[offset+6] = uint8(v >> 48) + dAtA[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Api(dAtA []byte, offset int, v uint32) int { + dAtA[offset] = uint8(v) + dAtA[offset+1] = uint8(v >> 8) + dAtA[offset+2] = uint8(v >> 16) + dAtA[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *ExampleRequest) Size() (n int) { + var l int + _ = l + l = len(m.Request) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.V1Beta2Field) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *ExampleResponse) Size() (n int) { + var l int + _ = l + l = len(m.Error) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func sovApi(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ExampleRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExampleRequest{`, + `Request:` + fmt.Sprintf("%v", this.Request) + `,`, + `V1Beta2Field:` + fmt.Sprintf("%v", this.V1Beta2Field) + `,`, + `}`, + }, "") + return s +} +func (this *ExampleResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExampleResponse{`, + `Error:` + fmt.Sprintf("%v", this.Error) + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *ExampleRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExampleRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExampleRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Request = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field V1Beta2Field", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.V1Beta2Field = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExampleResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExampleResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExampleResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Error = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthApi + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipApi(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } + +var fileDescriptorApi = []byte{ + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0x92, 0xd2, + 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, 0xd7, + 0x07, 0xcb, 0x27, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, 0x98, 0x05, 0xd1, 0xa7, 0xe4, 0xcf, 0xc5, + 0xe7, 0x5a, 0x91, 0x98, 0x5b, 0x90, 0x93, 0x1a, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x24, + 0xc1, 0xc5, 0x5e, 0x04, 0x61, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x42, 0xca, + 0x5c, 0xbc, 0x50, 0x5b, 0xe2, 0xd3, 0x32, 0x53, 0x73, 0x52, 0x24, 0x98, 0xc0, 0xf2, 0x3c, 0x50, + 0x41, 0x37, 0x90, 0x98, 0x92, 0x3a, 0x17, 0x3f, 0xdc, 0xc0, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, + 0x21, 0x11, 0x2e, 0xd6, 0xd4, 0xa2, 0xa2, 0xfc, 0x22, 0xa8, 0x79, 0x10, 0x8e, 0x51, 0x00, 0x17, + 0x3b, 0x54, 0xa1, 0x90, 0x2b, 0x17, 0x9f, 0x7b, 0x6a, 0x09, 0x94, 0xe7, 0x99, 0x97, 0x96, 0x2f, + 0x24, 0xae, 0x07, 0x35, 0x54, 0x0f, 0xd5, 0x75, 0x52, 0x12, 0x98, 0x12, 0x10, 0x5b, 0x94, 0x18, + 0x9c, 0x64, 0x4e, 0x3c, 0x94, 0x63, 0xbc, 0xf1, 0x50, 0x8e, 0xa1, 0xe1, 0x91, 0x1c, 0xe3, 0x89, + 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x43, + 0x12, 0x1b, 0xd8, 0xc3, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x79, 0x17, 0x13, 0x35, + 0x01, 0x00, 0x00, +} From 3a2e3bcc70ef9810b871db8bcf0599c4712f6bc2 Mon Sep 17 00:00:00 2001 From: vikaschoudhary16 Date: Tue, 1 May 2018 02:15:06 -0400 Subject: [PATCH 108/416] Add probe based mechanism for kubelet plugin discovery --- hack/.golint_failures | 4 + ...-kubelet-plugin-registration-dockerized.sh | 29 ++ ...e-generated-kubelet-plugin-registration.sh | 27 ++ ...y-generated-kubelet-plugin-registration.sh | 39 +++ pkg/kubelet/BUILD | 1 + pkg/kubelet/apis/BUILD | 1 + .../apis/pluginregistration/v1alpha1/BUILD | 40 +++ .../pluginregistration/v1alpha1/api.proto | 60 ++++ .../pluginregistration/v1alpha1/constants.go | 22 ++ pkg/kubelet/kubelet.go | 12 + pkg/kubelet/util/BUILD | 2 + pkg/kubelet/util/pluginwatcher/BUILD | 58 ++++ pkg/kubelet/util/pluginwatcher/README | 29 ++ .../util/pluginwatcher/example_plugin.go | 150 ++++++++++ .../example_plugin_apis/v1beta1/BUILD | 34 +++ .../example_plugin_apis/v1beta1/api.proto | 28 ++ .../example_plugin_apis/v1beta2/BUILD | 34 +++ .../example_plugin_apis/v1beta2/api.proto | 29 ++ .../util/pluginwatcher/plugin_watcher.go | 260 ++++++++++++++++++ .../util/pluginwatcher/plugin_watcher_test.go | 220 +++++++++++++++ 20 files changed, 1079 insertions(+) create mode 100755 hack/update-generated-kubelet-plugin-registration-dockerized.sh create mode 100755 hack/update-generated-kubelet-plugin-registration.sh create mode 100755 hack/verify-generated-kubelet-plugin-registration.sh create mode 100644 pkg/kubelet/apis/pluginregistration/v1alpha1/BUILD create mode 100644 pkg/kubelet/apis/pluginregistration/v1alpha1/api.proto create mode 100644 pkg/kubelet/apis/pluginregistration/v1alpha1/constants.go create mode 100644 pkg/kubelet/util/pluginwatcher/BUILD create mode 100644 pkg/kubelet/util/pluginwatcher/README create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin.go create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/BUILD create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.proto create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/BUILD create mode 100644 pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.proto create mode 100644 pkg/kubelet/util/pluginwatcher/plugin_watcher.go create mode 100644 pkg/kubelet/util/pluginwatcher/plugin_watcher_test.go diff --git a/hack/.golint_failures b/hack/.golint_failures index 2b2798e46a7..0853347106b 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -166,6 +166,7 @@ pkg/kubelet/apis/deviceplugin/v1alpha pkg/kubelet/apis/deviceplugin/v1beta1 pkg/kubelet/apis/kubeletconfig pkg/kubelet/apis/kubeletconfig/v1beta1 +pkg/kubelet/apis/pluginregistration/v1alpha1 pkg/kubelet/cadvisor pkg/kubelet/cadvisor/testing pkg/kubelet/checkpoint @@ -217,6 +218,9 @@ pkg/kubelet/sysctl pkg/kubelet/types pkg/kubelet/util pkg/kubelet/util/cache +pkg/kubelet/util/pluginwatcher +pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1 +pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2 pkg/kubelet/util/queue pkg/kubelet/util/sliceutils pkg/kubemark diff --git a/hack/update-generated-kubelet-plugin-registration-dockerized.sh b/hack/update-generated-kubelet-plugin-registration-dockerized.sh new file mode 100755 index 00000000000..daf5abbd36a --- /dev/null +++ b/hack/update-generated-kubelet-plugin-registration-dockerized.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT="$(cd "$(dirname "${BASH_SOURCE}")/../" && pwd -P)" +KUBELET_PLUGIN_REGISTRATION_ROOT="${KUBE_ROOT}/pkg/kubelet/apis/pluginregistration/v1alpha1/" +KUBELET_EXAMPLE_PLUGIN_V1BETA1="${KUBE_ROOT}/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/" +KUBELET_EXAMPLE_PLUGIN_V1BETA2="${KUBE_ROOT}/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/" + +source "${KUBE_ROOT}/hack/lib/protoc.sh" +kube::protoc::generate_proto ${KUBELET_PLUGIN_REGISTRATION_ROOT} +kube::protoc::generate_proto ${KUBELET_EXAMPLE_PLUGIN_V1BETA1} +kube::protoc::generate_proto ${KUBELET_EXAMPLE_PLUGIN_V1BETA2} diff --git a/hack/update-generated-kubelet-plugin-registration.sh b/hack/update-generated-kubelet-plugin-registration.sh new file mode 100755 index 00000000000..308733c0246 --- /dev/null +++ b/hack/update-generated-kubelet-plugin-registration.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. + +# NOTE: All output from this script needs to be copied back to the calling +# source tree. This is managed in kube::build::copy_output in build/common.sh. +# If the output set is changed update that function. + +${KUBE_ROOT}/build/run.sh hack/update-generated-kubelet-plugin-registration-dockerized.sh "$@" diff --git a/hack/verify-generated-kubelet-plugin-registration.sh b/hack/verify-generated-kubelet-plugin-registration.sh new file mode 100755 index 00000000000..3dfffa8dcc0 --- /dev/null +++ b/hack/verify-generated-kubelet-plugin-registration.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. +ERROR="Kubelet Plugin Registration api is out of date. Please run hack/update-generated-kubelet-plugin-registration.sh" +KUBELET_PLUGIN_REGISTRATION_ROOT="${KUBE_ROOT}/pkg/kubelet/apis/pluginregistration/v1alpha1/" + +source "${KUBE_ROOT}/hack/lib/protoc.sh" +kube::golang::setup_env + +function cleanup { + rm -rf ${KUBELET_PLUGIN_REGISTRATION_ROOT}/_tmp/ +} + +trap cleanup EXIT + +mkdir -p ${KUBELET_PLUGIN_REGISTRATION_ROOT}/_tmp +cp ${KUBELET_PLUGIN_REGISTRATION_ROOT}/api.pb.go ${KUBELET_PLUGIN_REGISTRATION_ROOT}/_tmp/ + +KUBE_VERBOSE=3 "${KUBE_ROOT}/hack/update-generated-kubelet-plugin-registration.sh" +kube::protoc::diff "${KUBELET_PLUGIN_REGISTRATION_ROOT}/api.pb.go" "${KUBELET_PLUGIN_REGISTRATION_ROOT}/_tmp/api.pb.go" ${ERROR} +echo "Generated Kubelet Plugin Registration api is up to date." diff --git a/pkg/kubelet/BUILD b/pkg/kubelet/BUILD index b4d6d166fa9..710af32b7a3 100644 --- a/pkg/kubelet/BUILD +++ b/pkg/kubelet/BUILD @@ -84,6 +84,7 @@ go_library( "//pkg/kubelet/util:go_default_library", "//pkg/kubelet/util/format:go_default_library", "//pkg/kubelet/util/manager:go_default_library", + "//pkg/kubelet/util/pluginwatcher:go_default_library", "//pkg/kubelet/util/queue:go_default_library", "//pkg/kubelet/util/sliceutils:go_default_library", "//pkg/kubelet/volumemanager:go_default_library", diff --git a/pkg/kubelet/apis/BUILD b/pkg/kubelet/apis/BUILD index 47cb8184ccb..2a22e48121a 100644 --- a/pkg/kubelet/apis/BUILD +++ b/pkg/kubelet/apis/BUILD @@ -41,6 +41,7 @@ filegroup( "//pkg/kubelet/apis/deviceplugin/v1alpha:all-srcs", "//pkg/kubelet/apis/deviceplugin/v1beta1:all-srcs", "//pkg/kubelet/apis/kubeletconfig:all-srcs", + "//pkg/kubelet/apis/pluginregistration/v1alpha1:all-srcs", "//pkg/kubelet/apis/stats/v1alpha1:all-srcs", ], tags = ["automanaged"], diff --git a/pkg/kubelet/apis/pluginregistration/v1alpha1/BUILD b/pkg/kubelet/apis/pluginregistration/v1alpha1/BUILD new file mode 100644 index 00000000000..f51668500bb --- /dev/null +++ b/pkg/kubelet/apis/pluginregistration/v1alpha1/BUILD @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = [ + "api.pb.go", + "constants.go", + ], + importpath = "k8s.io/kubernetes/pkg/kubelet/apis/pluginregistration/v1alpha1", + deps = [ + "//vendor/github.com/gogo/protobuf/gogoproto:go_default_library", + "//vendor/github.com/gogo/protobuf/proto:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) + +filegroup( + name = "go_default_library_protos", + srcs = ["api.proto"], + visibility = ["//visibility:public"], +) diff --git a/pkg/kubelet/apis/pluginregistration/v1alpha1/api.proto b/pkg/kubelet/apis/pluginregistration/v1alpha1/api.proto new file mode 100644 index 00000000000..319b3f19fb7 --- /dev/null +++ b/pkg/kubelet/apis/pluginregistration/v1alpha1/api.proto @@ -0,0 +1,60 @@ +// To regenerate api.pb.go run hack/update-generated-kubelet-plugin-registration.sh +syntax = 'proto3'; + +package pluginregistration; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.goproto_getters_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_unrecognized_all) = false; + +// PluginInfo is the message sent from a plugin to the Kubelet pluginwatcher for plugin registration +message PluginInfo { + // Type of the Plugin. CSIPlugin or DevicePlugin + string type = 1; + // Plugin name that uniquely identifies the plugin for the given plugin type. + // For DevicePlugin, this is the resource name that the plugin manages and + // should follow the extended resource name convention. + // For CSI, this is the CSI driver registrar name. + string name = 2; + // Optional endpoint location. If found set by Kubelet component, + // Kubelet component will use this endpoint for specific requests. + // This allows the plugin to register using one endpoint and possibly use + // a different socket for control operations. CSI uses this model to delegate + // its registration external from the plugin. + string endpoint = 3; + // Plugin service API versions the plugin supports. + // For DevicePlugin, this maps to the deviceplugin API versions the + // plugin supports at the given socket. + // The Kubelet component communicating with the plugin should be able + // to choose any preferred version from this list, or returns an error + // if none of the listed versions is supported. + repeated string supported_versions = 4; +} + +// RegistrationStatus is the message sent from Kubelet pluginwatcher to the plugin for notification on registration status +message RegistrationStatus { + // True if plugin gets registered successfully at Kubelet + bool plugin_registered = 1; + // Error message in case plugin fails to register, empty string otherwise + string error = 2; +} + +// RegistrationStatusResponse is sent by plugin to kubelet in response to RegistrationStatus RPC +message RegistrationStatusResponse { +} + +// InfoRequest is the empty request message from Kubelet +message InfoRequest { +} + +// Registration is the service advertised by the Plugins. +service Registration { + rpc GetInfo(InfoRequest) returns (PluginInfo) {} + rpc NotifyRegistrationStatus(RegistrationStatus) returns (RegistrationStatusResponse) {} +} diff --git a/pkg/kubelet/apis/pluginregistration/v1alpha1/constants.go b/pkg/kubelet/apis/pluginregistration/v1alpha1/constants.go new file mode 100644 index 00000000000..cfc1b7c6d7c --- /dev/null +++ b/pkg/kubelet/apis/pluginregistration/v1alpha1/constants.go @@ -0,0 +1,22 @@ +/* +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 pluginregistration + +const ( + CSIPlugin = "CSIPlugin" + DevicePlugin = "DevicePlugin" +) diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 421c0c98bea..9194c1bbb9c 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -93,6 +93,7 @@ import ( kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/util/format" "k8s.io/kubernetes/pkg/kubelet/util/manager" + "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher" "k8s.io/kubernetes/pkg/kubelet/util/queue" "k8s.io/kubernetes/pkg/kubelet/util/sliceutils" "k8s.io/kubernetes/pkg/kubelet/volumemanager" @@ -775,6 +776,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, if err != nil { return nil, err } + klet.pluginWatcher = pluginwatcher.NewWatcher(klet.getPluginsDir()) // If the experimentalMounterPathFlag is set, we do not want to // check node capabilities since the mount path is not the default @@ -1150,6 +1152,11 @@ type Kubelet struct { // This flag, if set, instructs the kubelet to keep volumes from terminated pods mounted to the node. // This can be useful for debugging volume related issues. keepTerminatedPodVolumes bool // DEPRECATED + + // pluginwatcher is a utility for Kubelet to register different types of node-level plugins + // such as device plugins or CSI plugins. It discovers plugins by monitoring inotify events under the + // directory returned by kubelet.getPluginsDir() + pluginWatcher pluginwatcher.Watcher } func allGlobalUnicastIPs() ([]net.IP, error) { @@ -1264,6 +1271,11 @@ func (kl *Kubelet) initializeModules() error { } } + // Start the plugin watcher + if err := kl.pluginWatcher.Start(); err != nil { + return fmt.Errorf("failed to start Plugin Watcher. err: %v", err) + } + // Start the image manager. kl.imageManager.Start() diff --git a/pkg/kubelet/util/BUILD b/pkg/kubelet/util/BUILD index df02ebdcd25..ff1755ebd11 100644 --- a/pkg/kubelet/util/BUILD +++ b/pkg/kubelet/util/BUILD @@ -93,9 +93,11 @@ filegroup( "//pkg/kubelet/util/format:all-srcs", "//pkg/kubelet/util/ioutils:all-srcs", "//pkg/kubelet/util/manager:all-srcs", + "//pkg/kubelet/util/pluginwatcher:all-srcs", "//pkg/kubelet/util/queue:all-srcs", "//pkg/kubelet/util/sliceutils:all-srcs", "//pkg/kubelet/util/store:all-srcs", ], tags = ["automanaged"], + visibility = ["//visibility:public"], ) diff --git a/pkg/kubelet/util/pluginwatcher/BUILD b/pkg/kubelet/util/pluginwatcher/BUILD new file mode 100644 index 00000000000..b4173ab5e1c --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/BUILD @@ -0,0 +1,58 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = [ + "example_plugin.go", + "plugin_watcher.go", + ], + importpath = "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher", + deps = [ + "//pkg/kubelet/apis/pluginregistration/v1alpha1:go_default_library", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1:go_default_library", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2:go_default_library", + "//pkg/util/filesystem:go_default_library", + "//vendor/github.com/fsnotify/fsnotify:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1:all-srcs", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["plugin_watcher_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/kubelet/apis/pluginregistration/v1alpha1:go_default_library", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1:go_default_library", + "//pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2:go_default_library", + "//vendor/github.com/stretchr/testify/require:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + ], +) diff --git a/pkg/kubelet/util/pluginwatcher/README b/pkg/kubelet/util/pluginwatcher/README new file mode 100644 index 00000000000..9654b2cf62a --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/README @@ -0,0 +1,29 @@ +This folder contains a utility, pluginwatcher, for Kubelet to register +different types of node-level plugins such as device plugins or CSI plugins. +It discovers plugins by monitoring inotify events under the directory returned by +kubelet.getPluginsDir(). Lets refer this directory as PluginsSockDir. +For any discovered plugin, pluginwatcher issues Registration.GetInfo grpc call +to get plugin type, name and supported service API versions. For any registered plugin type, +pluginwatcher calls the registered callback function with the received plugin +name, supported service API versions, and the full socket path. The Kubelet +component that receives this callback can acknowledge or reject the plugin +according to its own logic, and use the socket path to establish its service +communication with any API version supported by the plugin. + +Here are the general rules that Kubelet plugin developers should follow: +- Run as 'root' user. Currently creating socket under PluginsSockDir, a root owned directory, requires + plugin process to be running as 'root'. +- Implements the Registration service specified in + pkg/kubelet/apis/pluginregistration/v*/api.proto. +- The plugin name sent during Registration.GetInfo grpc should be unique + for the given plugin type (CSIPlugin or DevicePlugin). +- The socket path needs to be unique and doesn't conflict with the path chosen + by any other potential plugins. Currently we only support flat fs namespace + under PluginsSockDir but will soon support recursive inotify watch for + hierarchical socket paths. +- A plugin should clean up its own socket upon exiting or when a new instance + comes up. A plugin should NOT remove any sockets belonging to other plugins. +- A plugin should make sure it has service ready for any supported service API + version listed in the PluginInfo. +- For an example plugin implementation, take a look at example_plugin.go + included in this directory. diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin.go b/pkg/kubelet/util/pluginwatcher/example_plugin.go new file mode 100644 index 00000000000..fbca43acad5 --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin.go @@ -0,0 +1,150 @@ +/* +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 pluginwatcher + +import ( + "fmt" + "net" + "sync" + "time" + + "github.com/golang/glog" + "golang.org/x/net/context" + "google.golang.org/grpc" + + registerapi "k8s.io/kubernetes/pkg/kubelet/apis/pluginregistration/v1alpha1" + v1beta1 "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1" + v1beta2 "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2" +) + +const ( + PluginName = "example-plugin" + PluginType = "example-plugin-type" +) + +// examplePlugin is a sample plugin to work with plugin watcher +type examplePlugin struct { + grpcServer *grpc.Server + wg sync.WaitGroup + registrationStatus chan registerapi.RegistrationStatus // for testing + endpoint string // for testing +} + +type pluginServiceV1Beta1 struct { + server *examplePlugin +} + +func (s *pluginServiceV1Beta1) GetExampleInfo(ctx context.Context, rqt *v1beta1.ExampleRequest) (*v1beta1.ExampleResponse, error) { + glog.Infof("GetExampleInfo v1beta1field: %s", rqt.V1Beta1Field) + return &v1beta1.ExampleResponse{}, nil +} + +func (s *pluginServiceV1Beta1) RegisterService() { + v1beta1.RegisterExampleServer(s.server.grpcServer, s) +} + +type pluginServiceV1Beta2 struct { + server *examplePlugin +} + +func (s *pluginServiceV1Beta2) GetExampleInfo(ctx context.Context, rqt *v1beta2.ExampleRequest) (*v1beta2.ExampleResponse, error) { + glog.Infof("GetExampleInfo v1beta2_field: %s", rqt.V1Beta2Field) + return &v1beta2.ExampleResponse{}, nil +} + +func (s *pluginServiceV1Beta2) RegisterService() { + v1beta2.RegisterExampleServer(s.server.grpcServer, s) +} + +// NewExamplePlugin returns an initialized examplePlugin instance +func NewExamplePlugin() *examplePlugin { + return &examplePlugin{} +} + +// NewTestExamplePlugin returns an initialized examplePlugin instance for testing +func NewTestExamplePlugin(endpoint string) *examplePlugin { + return &examplePlugin{ + registrationStatus: make(chan registerapi.RegistrationStatus), + endpoint: endpoint, + } +} + +// GetInfo is the RPC invoked by plugin watcher +func (e *examplePlugin) GetInfo(ctx context.Context, req *registerapi.InfoRequest) (*registerapi.PluginInfo, error) { + return ®isterapi.PluginInfo{ + Type: PluginType, + Name: PluginName, + Endpoint: e.endpoint, + SupportedVersions: []string{"v1beta1", "v1beta2"}, + }, nil +} + +func (e *examplePlugin) NotifyRegistrationStatus(ctx context.Context, status *registerapi.RegistrationStatus) (*registerapi.RegistrationStatusResponse, error) { + if e.registrationStatus != nil { + e.registrationStatus <- *status + } + if !status.PluginRegistered { + glog.Errorf("Registration failed: %s\n", status.Error) + } + return ®isterapi.RegistrationStatusResponse{}, nil +} + +// Serve starts example plugin grpc server +func (e *examplePlugin) Serve(socketPath string) error { + glog.Infof("starting example server at: %s\n", socketPath) + lis, err := net.Listen("unix", socketPath) + if err != nil { + return err + } + glog.Infof("example server started at: %s\n", socketPath) + e.grpcServer = grpc.NewServer() + // Registers kubelet plugin watcher api. + registerapi.RegisterRegistrationServer(e.grpcServer, e) + // Registers services for both v1beta1 and v1beta2 versions. + v1beta1 := &pluginServiceV1Beta1{server: e} + v1beta1.RegisterService() + v1beta2 := &pluginServiceV1Beta2{server: e} + v1beta2.RegisterService() + + // Starts service + e.wg.Add(1) + go func() { + defer e.wg.Done() + // Blocking call to accept incoming connections. + if err := e.grpcServer.Serve(lis); err != nil { + glog.Errorf("example server stopped serving: %v", err) + } + }() + return nil +} + +func (e *examplePlugin) Stop() error { + glog.Infof("Stopping example server\n") + e.grpcServer.Stop() + c := make(chan struct{}) + go func() { + defer close(c) + e.wg.Wait() + }() + select { + case <-c: + return nil + case <-time.After(time.Second): + glog.Errorf("Timed out on waiting for stop completion") + return fmt.Errorf("Timed out on waiting for stop completion") + } +} diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/BUILD b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/BUILD new file mode 100644 index 00000000000..affbd0aee4a --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/BUILD @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +filegroup( + name = "go_default_library_protos", + srcs = ["api.proto"], + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["api.pb.go"], + importpath = "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gogo/protobuf/gogoproto:go_default_library", + "//vendor/github.com/gogo/protobuf/proto:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.proto b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.proto new file mode 100644 index 00000000000..14aa7df2c4d --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1/api.proto @@ -0,0 +1,28 @@ +syntax = 'proto3'; + +package v1beta1; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.goproto_getters_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_unrecognized_all) = false; + +message ExampleRequest { + string request = 1; + string v1beta1_field = 2; +} + +message ExampleResponse { + string error = 1; +} + +// Example is a simple example service for general reference on the recommended +// kubelet plugin model and plugin watcher testing. +service Example { + rpc GetExampleInfo(ExampleRequest) returns (ExampleResponse) {} +} diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/BUILD b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/BUILD new file mode 100644 index 00000000000..f2b53898d38 --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/BUILD @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +filegroup( + name = "go_default_library_protos", + srcs = ["api.proto"], + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["api.pb.go"], + importpath = "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gogo/protobuf/gogoproto:go_default_library", + "//vendor/github.com/gogo/protobuf/proto:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.proto b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.proto new file mode 100644 index 00000000000..e34697f3a66 --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2/api.proto @@ -0,0 +1,29 @@ +syntax = 'proto3'; + +package v1beta2; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.goproto_getters_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_unrecognized_all) = false; + +// Renames a field from v1beta1 ExampleRequest. +message ExampleRequest { + string request = 1; + string v1beta2_field = 2; +} + +message ExampleResponse { + string error = 1; +} + +// Example is a simple example service for general reference on the recommended +// kubelet plugin model and plugin watcher testing. +service Example { + rpc GetExampleInfo(ExampleRequest) returns (ExampleResponse) {} +} diff --git a/pkg/kubelet/util/pluginwatcher/plugin_watcher.go b/pkg/kubelet/util/pluginwatcher/plugin_watcher.go new file mode 100644 index 00000000000..9a5241cb2e5 --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/plugin_watcher.go @@ -0,0 +1,260 @@ +/* +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 pluginwatcher + +import ( + "fmt" + "net" + "os" + "path" + "path/filepath" + "sync" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/golang/glog" + "golang.org/x/net/context" + "google.golang.org/grpc" + registerapi "k8s.io/kubernetes/pkg/kubelet/apis/pluginregistration/v1alpha1" + utilfs "k8s.io/kubernetes/pkg/util/filesystem" +) + +// RegisterCallbackFn is the type of the callback function that handlers will provide +type RegisterCallbackFn func(pluginName string, endpoint string, versions []string, socketPath string) (error, chan bool) + +// Watcher is the plugin watcher +type Watcher struct { + path string + handlers map[string]RegisterCallbackFn + stopCh chan interface{} + fs utilfs.Filesystem + watcher *fsnotify.Watcher + wg sync.WaitGroup + mutex sync.Mutex +} + +// NewWatcher provides a new watcher +func NewWatcher(sockDir string) Watcher { + return Watcher{ + path: sockDir, + handlers: make(map[string]RegisterCallbackFn), + fs: &utilfs.DefaultFs{}, + } +} + +// AddHandler registers a callback to be invoked for a particular type of plugin +func (w *Watcher) AddHandler(handlerType string, handlerCbkFn RegisterCallbackFn) { + w.mutex.Lock() + defer w.mutex.Unlock() + w.handlers[handlerType] = handlerCbkFn +} + +// Creates the plugin directory, if it doesn't already exist. +func (w *Watcher) createPluginDir() error { + glog.V(4).Infof("Ensuring Plugin directory at %s ", w.path) + if err := w.fs.MkdirAll(w.path, 0755); err != nil { + return fmt.Errorf("error (re-)creating driver directory: %s", err) + } + return nil +} + +// Walks through the plugin directory to discover any existing plugin sockets. +func (w *Watcher) traversePluginDir() error { + files, err := w.fs.ReadDir(w.path) + if err != nil { + return fmt.Errorf("error reading the plugin directory: %v", err) + } + for _, f := range files { + // Currently only supports flat fs namespace under the plugin directory. + // TODO: adds support for hierarchical fs namespace. + if !f.IsDir() && filepath.Base(f.Name())[0] != '.' { + go func(sockName string) { + w.watcher.Events <- fsnotify.Event{ + Name: sockName, + Op: fsnotify.Op(uint32(1)), + } + }(path.Join(w.path, f.Name())) + } + } + return nil +} + +func (w *Watcher) init() error { + if err := w.createPluginDir(); err != nil { + return err + } + return nil +} + +func (w *Watcher) registerPlugin(socketPath string) error { + //TODO: Implement rate limiting to mitigate any DOS kind of attacks. + glog.V(4).Infof("registerPlugin called for socketPath: %s", socketPath) + client, conn, err := dial(socketPath) + if err != nil { + return fmt.Errorf("dial failed at socket %s, err: %v", socketPath, err) + } + defer conn.Close() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + infoResp, err := client.GetInfo(ctx, ®isterapi.InfoRequest{}) + if err != nil { + return fmt.Errorf("failed to get plugin info using RPC GetInfo at socket %s, err: %v", socketPath, err) + } + if err := w.invokeRegistrationCallbackAtHandler(ctx, client, infoResp, socketPath); err != nil { + return fmt.Errorf("failed to register plugin. Callback handler returned err: %v", err) + } + glog.V(4).Infof("Successfully registered plugin for plugin type: %s, name: %s, socket: %s", infoResp.Type, infoResp.Name, socketPath) + return nil +} + +func (w *Watcher) invokeRegistrationCallbackAtHandler(ctx context.Context, client registerapi.RegistrationClient, infoResp *registerapi.PluginInfo, socketPath string) error { + var handlerCbkFn RegisterCallbackFn + var ok bool + handlerCbkFn, ok = w.handlers[infoResp.Type] + if !ok { + if _, err := client.NotifyRegistrationStatus(ctx, ®isterapi.RegistrationStatus{ + PluginRegistered: false, + Error: fmt.Sprintf("No handler found registered for plugin type: %s, socket: %s", infoResp.Type, socketPath), + }); err != nil { + glog.Errorf("Failed to send registration status at socket %s, err: %v", socketPath, err) + } + return fmt.Errorf("no handler found registered for plugin type: %s, socket: %s", infoResp.Type, socketPath) + } + + var versions []string + for _, version := range infoResp.SupportedVersions { + versions = append(versions, version) + } + // calls handler callback to verify registration request + err, chanForAckOfNotification := handlerCbkFn(infoResp.Name, infoResp.Endpoint, versions, socketPath) + if err != nil { + if _, err := client.NotifyRegistrationStatus(ctx, ®isterapi.RegistrationStatus{ + PluginRegistered: false, + Error: fmt.Sprintf("Plugin registration failed with err: %v", err), + }); err != nil { + glog.Errorf("Failed to send registration status at socket %s, err: %v", socketPath, err) + } + chanForAckOfNotification <- false + return fmt.Errorf("plugin registration failed with err: %v", err) + } + + if _, err := client.NotifyRegistrationStatus(ctx, ®isterapi.RegistrationStatus{ + PluginRegistered: true, + }); err != nil { + return fmt.Errorf("failed to send registration status at socket %s, err: %v", socketPath, err) + } + chanForAckOfNotification <- true + return nil +} + +// Start watches for the creation of plugin sockets at the path +func (w *Watcher) Start() error { + glog.V(2).Infof("Plugin Watcher Start at %s", w.path) + w.stopCh = make(chan interface{}) + + // Creating the directory to be watched if it doesn't exist yet, + // and walks through the directory to discover the existing plugins. + if err := w.init(); err != nil { + return err + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + return fmt.Errorf("failed to start plugin watcher, err: %v", err) + } + + if err := watcher.Add(w.path); err != nil { + watcher.Close() + return fmt.Errorf("failed to start plugin watcher, err: %v", err) + } + + w.watcher = watcher + + if err := w.traversePluginDir(); err != nil { + watcher.Close() + return fmt.Errorf("failed to traverse plugin socket path, err: %v", err) + } + + w.wg.Add(1) + go func(watcher *fsnotify.Watcher) { + defer w.wg.Done() + for { + select { + case event := <-watcher.Events: + if event.Op&fsnotify.Create == fsnotify.Create { + go func(eventName string) { + err := w.registerPlugin(eventName) + if err != nil { + glog.Errorf("Plugin %s registration failed with error: %v", eventName, err) + } + }(event.Name) + } + continue + case err := <-watcher.Errors: + //TODO: Handle errors by taking corrective measures + if err != nil { + glog.Errorf("Watcher received error: %v", err) + } + continue + + case <-w.stopCh: + watcher.Close() + break + } + break + } + }(watcher) + return nil +} + +// Stop stops probing the creation of plugin sockets at the path +func (w *Watcher) Stop() error { + close(w.stopCh) + c := make(chan struct{}) + go func() { + defer close(c) + w.wg.Wait() + }() + select { + case <-c: + case <-time.After(10 * time.Second): + return fmt.Errorf("timeout on stopping watcher") + } + return nil +} + +// Cleanup cleans the path by removing sockets +func (w *Watcher) Cleanup() error { + return os.RemoveAll(w.path) +} + +// Dial establishes the gRPC communication with the picked up plugin socket. https://godoc.org/google.golang.org/grpc#Dial +func dial(unixSocketPath string) (registerapi.RegistrationClient, *grpc.ClientConn, error) { + c, err := grpc.Dial(unixSocketPath, grpc.WithInsecure(), grpc.WithBlock(), + grpc.WithTimeout(10*time.Second), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", addr, timeout) + }), + ) + + if err != nil { + return nil, nil, fmt.Errorf("failed to dial socket %s, err: %v", unixSocketPath, err) + } + + return registerapi.NewRegistrationClient(c), c, nil +} diff --git a/pkg/kubelet/util/pluginwatcher/plugin_watcher_test.go b/pkg/kubelet/util/pluginwatcher/plugin_watcher_test.go new file mode 100644 index 00000000000..44bccf9a6f3 --- /dev/null +++ b/pkg/kubelet/util/pluginwatcher/plugin_watcher_test.go @@ -0,0 +1,220 @@ +/* +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 pluginwatcher + +import ( + "fmt" + "io/ioutil" + "strconv" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + + "k8s.io/apimachinery/pkg/util/sets" + registerapi "k8s.io/kubernetes/pkg/kubelet/apis/pluginregistration/v1alpha1" + v1beta1 "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta1" + v1beta2 "k8s.io/kubernetes/pkg/kubelet/util/pluginwatcher/example_plugin_apis/v1beta2" +) + +func TestExamplePlugin(t *testing.T) { + socketDir, err := ioutil.TempDir("", "plugin_test") + require.NoError(t, err) + socketPath := socketDir + "/plugin.sock" + w := NewWatcher(socketDir) + + testCases := []struct { + description string + expectedEndpoint string + returnErr error + }{ + { + description: "Successfully register plugin through inotify", + expectedEndpoint: "", + returnErr: nil, + }, + { + description: "Successfully register plugin through inotify and got expected optional endpoint", + expectedEndpoint: "dummyEndpoint", + returnErr: nil, + }, + { + description: "Fails registration because endpoint is expected to be non-empty", + expectedEndpoint: "dummyEndpoint", + returnErr: fmt.Errorf("empty endpoint received"), + }, + { + description: "Successfully register plugin through inotify after plugin restarts", + expectedEndpoint: "", + returnErr: nil, + }, + { + description: "Fails registration with conflicting plugin name", + expectedEndpoint: "", + returnErr: fmt.Errorf("conflicting plugin name"), + }, + { + description: "Successfully register plugin during initial traverse after plugin watcher restarts", + expectedEndpoint: "", + returnErr: nil, + }, + { + description: "Fails registration with conflicting plugin name during initial traverse after plugin watcher restarts", + expectedEndpoint: "", + returnErr: fmt.Errorf("conflicting plugin name"), + }, + } + + callbackCount := struct { + mutex sync.Mutex + count int32 + }{} + w.AddHandler(PluginType, func(name string, endpoint string, versions []string, sockPath string) (error, chan bool) { + callbackCount.mutex.Lock() + localCount := callbackCount.count + callbackCount.count = callbackCount.count + 1 + callbackCount.mutex.Unlock() + + require.True(t, localCount <= int32((len(testCases)-1))) + require.Equal(t, PluginName, name, "Plugin name mismatched!!") + retError := testCases[localCount].returnErr + if retError == nil || retError.Error() != "empty endpoint received" { + require.Equal(t, testCases[localCount].expectedEndpoint, endpoint, "Unexpected endpoint") + } else { + require.NotEqual(t, testCases[localCount].expectedEndpoint, endpoint, "Unexpected endpoint") + } + + require.Equal(t, []string{"v1beta1", "v1beta2"}, versions, "Plugin version mismatched!!") + // Verifies the grpcServer is ready to serve services. + _, conn, err := dial(sockPath) + require.Nil(t, err) + defer conn.Close() + + // The plugin handler should be able to use any listed service API version. + v1beta1Client := v1beta1.NewExampleClient(conn) + v1beta2Client := v1beta2.NewExampleClient(conn) + + // Tests v1beta1 GetExampleInfo + _, err = v1beta1Client.GetExampleInfo(context.Background(), &v1beta1.ExampleRequest{}) + require.Nil(t, err) + + // Tests v1beta1 GetExampleInfo + _, err = v1beta2Client.GetExampleInfo(context.Background(), &v1beta2.ExampleRequest{}) + //atomic.AddInt32(&callbackCount, 1) + chanForAckOfNotification := make(chan bool) + + go func() { + select { + case <-chanForAckOfNotification: + close(chanForAckOfNotification) + case <-time.After(time.Second): + t.Fatalf("Timed out while waiting for notification ack") + } + }() + return retError, chanForAckOfNotification + }) + require.NoError(t, w.Start()) + + p := NewTestExamplePlugin("") + require.NoError(t, p.Serve(socketPath)) + require.True(t, waitForPluginRegistrationStatus(t, p.registrationStatus)) + + require.NoError(t, p.Stop()) + + p = NewTestExamplePlugin("dummyEndpoint") + require.NoError(t, p.Serve(socketPath)) + require.True(t, waitForPluginRegistrationStatus(t, p.registrationStatus)) + + require.NoError(t, p.Stop()) + + p = NewTestExamplePlugin("") + require.NoError(t, p.Serve(socketPath)) + require.False(t, waitForPluginRegistrationStatus(t, p.registrationStatus)) + + // Trying to start a plugin service at the same socket path should fail + // with "bind: address already in use" + require.NotNil(t, p.Serve(socketPath)) + + // grpcServer.Stop() will remove the socket and starting plugin service + // at the same path again should succeeds and trigger another callback. + require.NoError(t, p.Stop()) + p = NewTestExamplePlugin("") + go func() { + require.Nil(t, p.Serve(socketPath)) + }() + require.True(t, waitForPluginRegistrationStatus(t, p.registrationStatus)) + + // Starting another plugin with the same name got verification error. + p2 := NewTestExamplePlugin("") + socketPath2 := socketDir + "/plugin2.sock" + go func() { + require.NoError(t, p2.Serve(socketPath2)) + }() + require.False(t, waitForPluginRegistrationStatus(t, p2.registrationStatus)) + + // Restarts plugin watcher should traverse the socket directory and issues a + // callback for every existing socket. + require.NoError(t, w.Stop()) + errCh := make(chan error) + go func() { + errCh <- w.Start() + }() + + var wg sync.WaitGroup + wg.Add(2) + var pStatus string + var p2Status string + go func() { + pStatus = strconv.FormatBool(waitForPluginRegistrationStatus(t, p.registrationStatus)) + wg.Done() + }() + go func() { + p2Status = strconv.FormatBool(waitForPluginRegistrationStatus(t, p2.registrationStatus)) + wg.Done() + }() + wg.Wait() + expectedSet := sets.NewString() + expectedSet.Insert("true", "false") + actualSet := sets.NewString() + actualSet.Insert(pStatus, p2Status) + + require.Equal(t, expectedSet, actualSet) + + select { + case err = <-errCh: + require.NoError(t, err) + case <-time.After(time.Second): + t.Fatalf("Timed out while waiting for watcher start") + + } + + require.NoError(t, w.Stop()) + err = w.Cleanup() + require.NoError(t, err) +} + +func waitForPluginRegistrationStatus(t *testing.T, statusCh chan registerapi.RegistrationStatus) bool { + select { + case status := <-statusCh: + return status.PluginRegistered + case <-time.After(10 * time.Second): + t.Fatalf("Timed out while waiting for registration status") + } + return false +} From b2d4426f093f092153030ab520e82a5c5e4ec642 Mon Sep 17 00:00:00 2001 From: Kevin Taylor Date: Fri, 21 Jul 2017 14:42:03 +0000 Subject: [PATCH 109/416] Add dynamic environment variable substitution to subpaths --- pkg/features/kube_features.go | 8 + pkg/kubelet/container/helpers.go | 5 + pkg/kubelet/container/helpers_test.go | 108 +++++++++++++ pkg/kubelet/kubelet_pods.go | 22 ++- pkg/kubelet/kubelet_pods_test.go | 6 +- pkg/kubelet/kubelet_pods_windows_test.go | 2 +- test/e2e/common/expansion.go | 189 +++++++++++++++++++++++ 7 files changed, 328 insertions(+), 12 deletions(-) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index e95a0fcc155..c8d77e5e927 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -286,6 +286,13 @@ const ( // // Extend the default scheduler to be aware of volume topology and handle PV provisioning DynamicProvisioningScheduling utilfeature.Feature = "DynamicProvisioningScheduling" + + // owner: @kevtaylor + // alpha: v1.11 + // + // Allow subpath environment variable substitution + // Only applicable if the VolumeSubpath feature is also enabled + VolumeSubpathEnvExpansion utilfeature.Feature = "VolumeSubpathEnvExpansion" ) func init() { @@ -335,6 +342,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS VolumeSubpath: {Default: true, PreRelease: utilfeature.GA}, BalanceAttachedNodeVolumes: {Default: false, PreRelease: utilfeature.Alpha}, DynamicProvisioningScheduling: {Default: false, PreRelease: utilfeature.Alpha}, + VolumeSubpathEnvExpansion: {Default: false, PreRelease: utilfeature.Alpha}, // inherited features from generic apiserver, relisted here to get a conflict if it is changed // unintentionally on either side: diff --git a/pkg/kubelet/container/helpers.go b/pkg/kubelet/container/helpers.go index 180a3e6df2d..fbf108c2a74 100644 --- a/pkg/kubelet/container/helpers.go +++ b/pkg/kubelet/container/helpers.go @@ -133,6 +133,11 @@ func ExpandContainerCommandOnlyStatic(containerCommand []string, envs []v1.EnvVa return command } +func ExpandContainerVolumeMounts(mount v1.VolumeMount, envs []EnvVar) (expandedSubpath string) { + mapping := expansion.MappingFuncFor(EnvVarsToMap(envs)) + return expansion.Expand(mount.SubPath, mapping) +} + func ExpandContainerCommandAndArgs(container *v1.Container, envs []EnvVar) (command []string, args []string) { mapping := expansion.MappingFuncFor(EnvVarsToMap(envs)) diff --git a/pkg/kubelet/container/helpers_test.go b/pkg/kubelet/container/helpers_test.go index 14d9d6e6c8c..d6c2792c60b 100644 --- a/pkg/kubelet/container/helpers_test.go +++ b/pkg/kubelet/container/helpers_test.go @@ -138,6 +138,114 @@ func TestExpandCommandAndArgs(t *testing.T) { } } +func TestExpandVolumeMountsWithSubpath(t *testing.T) { + cases := []struct { + name string + container *v1.Container + envs []EnvVar + expectedSubPath string + expectedMountPath string + }{ + { + name: "subpath with no expansion", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{SubPath: "foo"}}, + }, + expectedSubPath: "foo", + expectedMountPath: "", + }, + { + name: "volumes with expanded subpath", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{SubPath: "foo/$(POD_NAME)"}}, + }, + envs: []EnvVar{ + { + Name: "POD_NAME", + Value: "bar", + }, + }, + expectedSubPath: "foo/bar", + expectedMountPath: "", + }, + { + name: "volumes expanded with empty subpath", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{SubPath: ""}}, + }, + envs: []EnvVar{ + { + Name: "POD_NAME", + Value: "bar", + }, + }, + expectedSubPath: "", + expectedMountPath: "", + }, + { + name: "volumes expanded with no envs subpath", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{SubPath: "/foo/$(POD_NAME)"}}, + }, + expectedSubPath: "/foo/$(POD_NAME)", + expectedMountPath: "", + }, + { + name: "volumes expanded with leading environment variable", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{SubPath: "$(POD_NAME)/bar"}}, + }, + envs: []EnvVar{ + { + Name: "POD_NAME", + Value: "foo", + }, + }, + expectedSubPath: "foo/bar", + expectedMountPath: "", + }, + { + name: "volumes with volume and subpath", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{MountPath: "/foo", SubPath: "$(POD_NAME)/bar"}}, + }, + envs: []EnvVar{ + { + Name: "POD_NAME", + Value: "foo", + }, + }, + expectedSubPath: "foo/bar", + expectedMountPath: "/foo", + }, + { + name: "volumes with volume and no subpath", + container: &v1.Container{ + VolumeMounts: []v1.VolumeMount{{MountPath: "/foo"}}, + }, + envs: []EnvVar{ + { + Name: "POD_NAME", + Value: "foo", + }, + }, + expectedSubPath: "", + expectedMountPath: "/foo", + }, + } + + for _, tc := range cases { + actualSubPath := ExpandContainerVolumeMounts(tc.container.VolumeMounts[0], tc.envs) + if e, a := tc.expectedSubPath, actualSubPath; !reflect.DeepEqual(e, a) { + t.Errorf("%v: unexpected subpath; expected %v, got %v", tc.name, e, a) + } + if e, a := tc.expectedMountPath, tc.container.VolumeMounts[0].MountPath; !reflect.DeepEqual(e, a) { + t.Errorf("%v: unexpected mountpath; expected %v, got %v", tc.name, e, a) + } + } + +} + func TestShouldContainerBeRestarted(t *testing.T) { pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 46a17a01a58..50c28c0ebec 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -129,7 +129,8 @@ func (kl *Kubelet) makeBlockVolumes(pod *v1.Pod, container *v1.Container, podVol } // makeMounts determines the mount points for the given container. -func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap, mounter mountutil.Interface) ([]kubecontainer.Mount, func(), error) { +func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap, mounter mountutil.Interface, expandEnvs []kubecontainer.EnvVar) ([]kubecontainer.Mount, func(), error) { + // Kubernetes only mounts on /etc/hosts if: // - container is not an infrastructure (pause) container // - container is not already mounting on /etc/hosts @@ -166,6 +167,11 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h return nil, cleanupAction, fmt.Errorf("volume subpaths are disabled") } + // Expand subpath variables + if utilfeature.DefaultFeatureGate.Enabled(features.VolumeSubpathEnvExpansion) { + mount.SubPath = kubecontainer.ExpandContainerVolumeMounts(mount, expandEnvs) + } + if filepath.IsAbs(mount.SubPath) { return nil, cleanupAction, fmt.Errorf("error SubPath `%s` must not be an absolute path", mount.SubPath) } @@ -454,18 +460,18 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *v1.Pod, container *v1.Contai opts.Devices = append(opts.Devices, blkVolumes...) } - mounts, cleanupAction, err := makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes, kl.mounter) + envs, err := kl.makeEnvironmentVariables(pod, container, podIP) + if err != nil { + return nil, nil, err + } + opts.Envs = append(opts.Envs, envs...) + + mounts, cleanupAction, err := makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes, kl.mounter, opts.Envs) if err != nil { return nil, cleanupAction, err } opts.Mounts = append(opts.Mounts, mounts...) - envs, err := kl.makeEnvironmentVariables(pod, container, podIP) - if err != nil { - return nil, cleanupAction, err - } - opts.Envs = append(opts.Envs, envs...) - // Disabling adding TerminationMessagePath on Windows as these files would be mounted as docker volume and // Docker for Windows has a bug where only directories can be mounted if len(container.TerminationMessagePath) != 0 && runtime.GOOS != "windows" { diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 9d2c813848b..6a26e20550e 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -273,7 +273,7 @@ func TestMakeMounts(t *testing.T) { return } - mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fm) + mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fm, nil) // validate only the error if we expect an error if tc.expectErr { @@ -296,7 +296,7 @@ func TestMakeMounts(t *testing.T) { t.Errorf("Failed to enable feature gate for MountPropagation: %v", err) return } - mounts, _, err = makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fm) + mounts, _, err = makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", "", tc.podVolumes, fm, nil) if !tc.expectErr { expectedPrivateMounts := []kubecontainer.Mount{} for _, mount := range tc.expectedMounts { @@ -357,7 +357,7 @@ func TestDisabledSubpath(t *testing.T) { defer utilfeature.DefaultFeatureGate.Set("VolumeSubpath=true") for name, test := range cases { - _, _, err := makeMounts(&pod, "/pod", &test.container, "fakepodname", "", "", podVolumes, fm) + _, _, err := makeMounts(&pod, "/pod", &test.container, "fakepodname", "", "", podVolumes, fm, nil) if err != nil && !test.expectError { t.Errorf("test %v failed: %v", name, err) } diff --git a/pkg/kubelet/kubelet_pods_windows_test.go b/pkg/kubelet/kubelet_pods_windows_test.go index cc16b358fb0..628c2ecdd7a 100644 --- a/pkg/kubelet/kubelet_pods_windows_test.go +++ b/pkg/kubelet/kubelet_pods_windows_test.go @@ -66,7 +66,7 @@ func TestMakeMountsWindows(t *testing.T) { } fm := &mount.FakeMounter{} - mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes, fm) + mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes, fm, nil) expectedMounts := []kubecontainer.Mount{ { diff --git a/test/e2e/common/expansion.go b/test/e2e/common/expansion.go index eadb324fecf..17123dd8c87 100644 --- a/test/e2e/common/expansion.go +++ b/test/e2e/common/expansion.go @@ -19,8 +19,13 @@ package common import ( "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/test/e2e/framework" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "time" ) // These tests exercise the Kubernetes expansion syntax $(VAR). @@ -144,4 +149,188 @@ var _ = framework.KubeDescribe("Variable Expansion", func() { "test-value", }) }) + + /* + Testname: var-expansion-subpath + Description: Make sure a container's subpath can be set using an + expansion of environment variables. + */ + It("should allow substituting values in a volume subpath [Feature:VolumeSubpathEnvExpansion][NodeAlphaFeature:VolumeSubpathEnvExpansion]", func() { + podName := "var-expansion-" + string(uuid.NewUUID()) + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Labels: map[string]string{"name": podName}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "dapi-container", + Image: busyboxImage, + Command: []string{"sh", "-c", "test -d /testcontainer/" + podName + ";echo $?"}, + Env: []v1.EnvVar{ + { + Name: "POD_NAME", + Value: podName, + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "workdir1", + MountPath: "/logscontainer", + SubPath: "$(POD_NAME)", + }, + { + Name: "workdir2", + MountPath: "/testcontainer", + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + Volumes: []v1.Volume{ + { + Name: "workdir1", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{Path: "/tmp"}, + }, + }, + { + Name: "workdir2", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{Path: "/tmp"}, + }, + }, + }, + }, + } + + f.TestContainerOutput("substitution in volume subpath", pod, 0, []string{ + "0", + }) + }) + + /* + Testname: var-expansion-subpath-with-backticks + Description: Make sure a container's subpath can not be set using an + expansion of environment variables when backticks are supplied. + */ + It("should fail substituting values in a volume subpath with backticks [Feature:VolumeSubpathEnvExpansion][NodeAlphaFeature:VolumeSubpathEnvExpansion][Slow]", func() { + + podName := "var-expansion-" + string(uuid.NewUUID()) + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Labels: map[string]string{"name": podName}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "dapi-container", + Image: busyboxImage, + Env: []v1.EnvVar{ + { + Name: "POD_NAME", + Value: "..", + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "workdir1", + MountPath: "/logscontainer", + SubPath: "$(POD_NAME)", + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + Volumes: []v1.Volume{ + { + Name: "workdir1", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + } + + // Pod should fail + testPodFailSubpath(f, pod, "SubPath `..`: must not contain '..'") + }) + + /* + Testname: var-expansion-subpath-with-absolute-path + Description: Make sure a container's subpath can not be set using an + expansion of environment variables when absoluete path is supplied. + */ + It("should fail substituting values in a volume subpath with absolute path [Feature:VolumeSubpathEnvExpansion][NodeAlphaFeature:VolumeSubpathEnvExpansion][Slow]", func() { + + podName := "var-expansion-" + string(uuid.NewUUID()) + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: podName, + Labels: map[string]string{"name": podName}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "dapi-container", + Image: busyboxImage, + Env: []v1.EnvVar{ + { + Name: "POD_NAME", + Value: "/tmp", + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "workdir1", + MountPath: "/logscontainer", + SubPath: "$(POD_NAME)", + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + Volumes: []v1.Volume{ + { + Name: "workdir1", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + } + + // Pod should fail + testPodFailSubpath(f, pod, "SubPath `/tmp` must not be an absolute path") + }) }) + +func testPodFailSubpath(f *framework.Framework, pod *v1.Pod, errorText string) { + + pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(pod) + Expect(err).ToNot(HaveOccurred(), "while creating pod") + + defer func() { + framework.DeletePodWithWait(f, f.ClientSet, pod) + }() + + err = framework.WaitTimeoutForPodRunningInNamespace(f.ClientSet, pod.Name, pod.Namespace, 30*time.Second) + Expect(err).To(HaveOccurred(), "while waiting for pod to be running") + + selector := fields.Set{ + "involvedObject.kind": "Pod", + "involvedObject.name": pod.Name, + "involvedObject.namespace": f.Namespace.Name, + "reason": "Failed", + }.AsSelector().String() + + options := metav1.ListOptions{FieldSelector: selector} + events, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).List(options) + Expect(err).NotTo(HaveOccurred(), "while getting pod events") + Expect(len(events.Items)).NotTo(Equal(0), "no events found") + Expect(events.Items[0].Message).To(ContainSubstring(errorText), "subpath error not found") +} From 631124cde4989ec7233d9b885a9d140a0ed0bb89 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 7 May 2018 14:14:23 -0400 Subject: [PATCH 110/416] Correctly apply request transforms with flattened resource builder This change moves the NewClientWithOptions call into Builder.getClient. Since getClient is the only way for Builder and its visitors to create a RESTClient, we can reasonably guarantee that the request transforms will be honored. Previously, it was possible for a call to NewFlattenListVisitor to return resource Info objects whose Client field did not honor the request transforms. Signed-off-by: Monis Khan --- .../genericclioptions/resource/builder.go | 26 ++++---- .../resource/builder_test.go | 60 ++++++++++++++----- .../genericclioptions/resource/interfaces.go | 3 + 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/pkg/kubectl/genericclioptions/resource/builder.go b/pkg/kubectl/genericclioptions/resource/builder.go index 9f4e02d02b2..09e21827f7e 100644 --- a/pkg/kubectl/genericclioptions/resource/builder.go +++ b/pkg/kubectl/genericclioptions/resource/builder.go @@ -49,9 +49,7 @@ type Builder struct { categoryExpander restmapper.CategoryExpander // mapper is set explicitly by resource builders - mapper *mapper - internal *mapper - unstructured *mapper + mapper *mapper // clientConfigFn is a function to produce a client, *if* you need one clientConfigFn ClientConfigFunc @@ -829,7 +827,6 @@ func (b *Builder) visitBySelector() *Result { result.err = err return result } - client = NewClientWithOptions(client, b.requestTransforms...) selectorNamespace := b.namespace if mapping.Scope.Name() != meta.RESTScopeNameNamespace { selectorNamespace = "" @@ -846,15 +843,25 @@ func (b *Builder) visitBySelector() *Result { } func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) { - if b.fakeClientFn != nil { - return b.fakeClientFn(gv) + var ( + client RESTClient + err error + ) + + switch { + case b.fakeClientFn != nil: + client, err = b.fakeClientFn(gv) + case b.negotiatedSerializer != nil: + client, err = b.clientConfigFn.clientForGroupVersion(gv, b.negotiatedSerializer) + default: + client, err = b.clientConfigFn.unstructuredClientForGroupVersion(gv) } - if b.negotiatedSerializer != nil { - return b.clientConfigFn.clientForGroupVersion(gv, b.negotiatedSerializer) + if err != nil { + return nil, err } - return b.clientConfigFn.unstructuredClientForGroupVersion(gv) + return NewClientWithOptions(client, b.requestTransforms...), nil } func (b *Builder) visitByResource() *Result { @@ -891,7 +898,6 @@ func (b *Builder) visitByResource() *Result { result.err = err return result } - client = NewClientWithOptions(client, b.requestTransforms...) clients[s] = client } diff --git a/pkg/kubectl/genericclioptions/resource/builder_test.go b/pkg/kubectl/genericclioptions/resource/builder_test.go index e6308a56687..ecf12529a19 100644 --- a/pkg/kubectl/genericclioptions/resource/builder_test.go +++ b/pkg/kubectl/genericclioptions/resource/builder_test.go @@ -655,22 +655,50 @@ func TestMultipleResourceByTheSameName(t *testing.T) { } func TestRequestModifier(t *testing.T) { - var got *rest.Request - b := newDefaultBuilderWith(fakeClientWith("test", t, nil)). - NamespaceParam("foo"). - TransformRequests(func(req *rest.Request) { - got = req - }). - ResourceNames("", "services/baz"). - RequireObject(false) - - i, err := b.Do().Infos() - if err != nil { - t.Fatal(err) - } - req := i[0].Client.Get() - if got != req { - t.Fatalf("request was not received by modifier: %#v", req) + for _, tc := range []struct { + name string + f func(t *testing.T, got **rest.Request) *Builder + }{ + { + name: "simple", + f: func(t *testing.T, got **rest.Request) *Builder { + return newDefaultBuilderWith(fakeClientWith(t.Name(), t, nil)). + NamespaceParam("foo"). + TransformRequests(func(req *rest.Request) { + *got = req + }). + ResourceNames("", "services/baz"). + RequireObject(false) + }, + }, + { + name: "flatten", + f: func(t *testing.T, got **rest.Request) *Builder { + pods, _ := testData() + return newDefaultBuilderWith(fakeClientWith(t.Name(), t, map[string]string{ + "/namespaces/foo/pods": runtime.EncodeOrDie(corev1Codec, pods), + })). + NamespaceParam("foo"). + TransformRequests(func(req *rest.Request) { + *got = req + }). + ResourceTypeOrNameArgs(true, "pods"). + Flatten() + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + var got *rest.Request + b := tc.f(t, &got) + i, err := b.Do().Infos() + if err != nil { + t.Fatal(err) + } + req := i[0].Client.Get() + if got != req { + t.Fatalf("request was not received by modifier: %#v", req) + } + }) } } diff --git a/pkg/kubectl/genericclioptions/resource/interfaces.go b/pkg/kubectl/genericclioptions/resource/interfaces.go index 6179481a5d8..508d4d6b5e4 100644 --- a/pkg/kubectl/genericclioptions/resource/interfaces.go +++ b/pkg/kubectl/genericclioptions/resource/interfaces.go @@ -47,6 +47,9 @@ type RequestTransform func(*rest.Request) // NewClientWithOptions wraps the provided RESTClient and invokes each transform on each // newly created request. func NewClientWithOptions(c RESTClient, transforms ...RequestTransform) RESTClient { + if len(transforms) == 0 { + return c + } return &clientOptions{c: c, transforms: transforms} } From 2d97f8ea3a5e64ca1a1575c5900f567e98c0da59 Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Tue, 29 May 2018 09:15:29 -0700 Subject: [PATCH 111/416] node e2e: fix the missing square brackets Also tag the inode eviction tests with [NodeFeature:Eviction]. --- test/e2e_node/eviction_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/e2e_node/eviction_test.go b/test/e2e_node/eviction_test.go index dcd29e43982..5131f53e771 100644 --- a/test/e2e_node/eviction_test.go +++ b/test/e2e_node/eviction_test.go @@ -59,7 +59,7 @@ const ( // InodeEviction tests that the node responds to node disk pressure by evicting only responsible pods. // Node disk pressure is induced by consuming all inodes on the node. -var _ = framework.KubeDescribe("InodeEviction [Slow] [Serial] [Disruptive]", func() { +var _ = framework.KubeDescribe("InodeEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("inode-eviction-test") expectedNodeCondition := v1.NodeDiskPressure pressureTimeout := 15 * time.Minute @@ -123,7 +123,7 @@ var _ = framework.KubeDescribe("ImageGCNoEviction [Slow] [Serial] [Disruptive][N // MemoryAllocatableEviction tests that the node responds to node memory pressure by evicting only responsible pods. // Node memory pressure is only encountered because we reserve the majority of the node's capacity via kube-reserved. -var _ = framework.KubeDescribe("MemoryAllocatableEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction", func() { +var _ = framework.KubeDescribe("MemoryAllocatableEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("memory-allocatable-eviction-test") expectedNodeCondition := v1.NodeMemoryPressure pressureTimeout := 10 * time.Minute @@ -155,7 +155,7 @@ var _ = framework.KubeDescribe("MemoryAllocatableEviction [Slow] [Serial] [Disru // LocalStorageEviction tests that the node responds to node disk pressure by evicting only responsible pods // Disk pressure is induced by running pods which consume disk space. -var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction", func() { +var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("localstorage-eviction-test") pressureTimeout := 10 * time.Minute expectedNodeCondition := v1.NodeDiskPressure @@ -183,7 +183,7 @@ var _ = framework.KubeDescribe("LocalStorageEviction [Slow] [Serial] [Disruptive // LocalStorageEviction tests that the node responds to node disk pressure by evicting only responsible pods // Disk pressure is induced by running pods which consume disk space, which exceed the soft eviction threshold. // Note: This test's purpose is to test Soft Evictions. Local storage was chosen since it is the least costly to run. -var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction", func() { +var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("localstorage-eviction-test") pressureTimeout := 10 * time.Minute expectedNodeCondition := v1.NodeDiskPressure @@ -271,7 +271,7 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se // PriorityMemoryEvictionOrdering tests that the node responds to node memory pressure by evicting pods. // This test tests that the guaranteed pod is never evicted, and that the lower-priority pod is evicted before // the higher priority pod. -var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [Disruptive][NodeFeature:Eviction", func() { +var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("priority-memory-eviction-ordering-test") expectedNodeCondition := v1.NodeMemoryPressure pressureTimeout := 10 * time.Minute @@ -317,7 +317,7 @@ var _ = framework.KubeDescribe("PriorityMemoryEvictionOrdering [Slow] [Serial] [ // PriorityLocalStorageEvictionOrdering tests that the node responds to node disk pressure by evicting pods. // This test tests that the guaranteed pod is never evicted, and that the lower-priority pod is evicted before // the higher priority pod. -var _ = framework.KubeDescribe("PriorityLocalStorageEvictionOrdering [Slow] [Serial] [Disruptive][NodeFeature:Eviction", func() { +var _ = framework.KubeDescribe("PriorityLocalStorageEvictionOrdering [Slow] [Serial] [Disruptive][NodeFeature:Eviction]", func() { f := framework.NewDefaultFramework("priority-disk-eviction-ordering-test") expectedNodeCondition := v1.NodeDiskPressure pressureTimeout := 10 * time.Minute From 668e127a1e033c01e91924b70051c3c7a59e8859 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Tue, 29 May 2018 09:34:40 -0700 Subject: [PATCH 112/416] fix dynamic kubelet config tests --- test/e2e_node/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e_node/util.go b/test/e2e_node/util.go index 918622ed4a0..a33ae7cc699 100644 --- a/test/e2e_node/util.go +++ b/test/e2e_node/util.go @@ -138,7 +138,7 @@ func isKubeletConfigEnabled(f *framework.Framework) (bool, error) { } v, ok := cfgz.FeatureGates[string(features.DynamicKubeletConfig)] if !ok { - return false, nil + return true, nil } return v, nil } From 207e9d1d90c47b2a40c3cd4d43563060f27f8ff7 Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 29 May 2018 09:57:15 -0400 Subject: [PATCH 113/416] cleanup some dead kubectl code and narrow scope of helpers --- hack/.golint_failures | 2 - pkg/kubectl/cmd/apply.go | 10 - pkg/kubectl/cmd/set/BUILD | 7 +- pkg/kubectl/cmd/{util => set}/env/BUILD | 2 +- pkg/kubectl/cmd/{util => set}/env/doc.go | 4 +- .../cmd/{util => set}/env/env_parse.go | 19 -- .../cmd/{util => set}/env/env_parse_test.go | 27 --- .../cmd/{util => set}/env/env_resolve.go | 0 pkg/kubectl/cmd/set/set_env.go | 2 +- pkg/kubectl/cmd/util/BUILD | 7 +- pkg/kubectl/cmd/util/editor/BUILD | 7 +- .../{util => cmd/util/editor}/crlf/BUILD | 2 +- .../{util => cmd/util/editor}/crlf/crlf.go | 2 +- pkg/kubectl/cmd/util/editor/editoptions.go | 2 +- pkg/kubectl/cmd/util/factory.go | 28 --- pkg/kubectl/cmd/util/factory_client_access.go | 19 -- pkg/kubectl/cmd/util/factory_test.go | 79 ------- pkg/kubectl/cmd/util/generator.go | 1 - pkg/kubectl/cmd/util/helpers.go | 19 -- pkg/kubectl/cmd/util/jsonmerge/BUILD | 32 --- pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go | 193 ------------------ pkg/kubectl/util/BUILD | 1 - 22 files changed, 18 insertions(+), 447 deletions(-) rename pkg/kubectl/cmd/{util => set}/env/BUILD (94%) rename pkg/kubectl/cmd/{util => set}/env/doc.go (86%) rename pkg/kubectl/cmd/{util => set}/env/env_parse.go (91%) rename pkg/kubectl/cmd/{util => set}/env/env_parse_test.go (76%) rename pkg/kubectl/cmd/{util => set}/env/env_resolve.go (100%) rename pkg/kubectl/{util => cmd/util/editor}/crlf/BUILD (85%) rename pkg/kubectl/{util => cmd/util/editor}/crlf/crlf.go (98%) delete mode 100644 pkg/kubectl/cmd/util/factory_test.go delete mode 100644 pkg/kubectl/cmd/util/jsonmerge/BUILD delete mode 100644 pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go diff --git a/hack/.golint_failures b/hack/.golint_failures index 2b2798e46a7..e59d33138e6 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -148,7 +148,6 @@ pkg/kubectl/cmd/templates pkg/kubectl/cmd/testing pkg/kubectl/cmd/util pkg/kubectl/cmd/util/editor -pkg/kubectl/cmd/util/jsonmerge pkg/kubectl/cmd/util/sanity pkg/kubectl/cmd/wait pkg/kubectl/genericclioptions @@ -156,7 +155,6 @@ pkg/kubectl/genericclioptions/printers pkg/kubectl/genericclioptions/resource pkg/kubectl/metricsutil pkg/kubectl/util -pkg/kubectl/util/crlf pkg/kubectl/util/slice pkg/kubelet pkg/kubelet/apis diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index 650451120ed..354f9a7b675 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -40,7 +40,6 @@ import ( "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/dynamic" - scaleclient "k8s.io/client-go/scale" oapi "k8s.io/kube-openapi/pkg/util/proto" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" @@ -79,7 +78,6 @@ type ApplyOptions struct { Validator validation.Schema Builder *resource.Builder Mapper meta.RESTMapper - Scaler scaleclient.ScalesGetter DynamicClient dynamic.Interface OpenAPISchema openapi.Resources @@ -220,11 +218,6 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - o.Scaler, err = cmdutil.ScaleClientFn(f) - if err != nil { - return err - } - o.DynamicClient, err = f.DynamicClient() if err != nil { return err @@ -493,7 +486,6 @@ func (o *ApplyOptions) Run() error { cascade: o.DeleteOptions.Cascade, dryRun: o.DryRun, gracePeriod: o.DeleteOptions.GracePeriod, - scaler: o.Scaler, toPrinter: o.ToPrinter, @@ -583,8 +575,6 @@ type pruner struct { dryRun bool gracePeriod int - scaler scaleclient.ScalesGetter - toPrinter func(string) (printers.ResourcePrinter, error) out io.Writer diff --git a/pkg/kubectl/cmd/set/BUILD b/pkg/kubectl/cmd/set/BUILD index aba25208706..023d91e9910 100644 --- a/pkg/kubectl/cmd/set/BUILD +++ b/pkg/kubectl/cmd/set/BUILD @@ -21,9 +21,9 @@ go_library( deps = [ "//pkg/apis/rbac:go_default_library", "//pkg/kubectl:go_default_library", + "//pkg/kubectl/cmd/set/env:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", - "//pkg/kubectl/cmd/util/env:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library", @@ -96,7 +96,10 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//pkg/kubectl/cmd/set/env:all-srcs", + ], tags = ["automanaged"], visibility = [ "//build/visible_to:pkg_kubectl_cmd_set_CONSUMERS", diff --git a/pkg/kubectl/cmd/util/env/BUILD b/pkg/kubectl/cmd/set/env/BUILD similarity index 94% rename from pkg/kubectl/cmd/util/env/BUILD rename to pkg/kubectl/cmd/set/env/BUILD index ecdf11acb37..97fb91a2072 100644 --- a/pkg/kubectl/cmd/util/env/BUILD +++ b/pkg/kubectl/cmd/set/env/BUILD @@ -7,7 +7,7 @@ go_library( "env_parse.go", "env_resolve.go", ], - importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/env", + importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/set/env", visibility = ["//visibility:public"], deps = [ "//pkg/api/v1/resource:go_default_library", diff --git a/pkg/kubectl/cmd/util/env/doc.go b/pkg/kubectl/cmd/set/env/doc.go similarity index 86% rename from pkg/kubectl/cmd/util/env/doc.go rename to pkg/kubectl/cmd/set/env/doc.go index 39adb0adf0c..25e4c04a70f 100644 --- a/pkg/kubectl/cmd/util/env/doc.go +++ b/pkg/kubectl/cmd/set/env/doc.go @@ -14,5 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package env provides functions to incorporate environment variables into kubectl commands. -package env // import "k8s.io/kubernetes/pkg/kubectl/cmd/util/env" +// Package env provides functions to incorporate environment variables into set env. +package env diff --git a/pkg/kubectl/cmd/util/env/env_parse.go b/pkg/kubectl/cmd/set/env/env_parse.go similarity index 91% rename from pkg/kubectl/cmd/util/env/env_parse.go rename to pkg/kubectl/cmd/set/env/env_parse.go index 1d1686af647..5e2e529088d 100644 --- a/pkg/kubectl/cmd/util/env/env_parse.go +++ b/pkg/kubectl/cmd/set/env/env_parse.go @@ -20,7 +20,6 @@ import ( "bufio" "fmt" "io" - "os" "regexp" "strings" @@ -28,24 +27,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" ) -// Env returns an environment variable if not nil, or a default value. -func Env(key string, defaultValue string) string { - val := os.Getenv(key) - if len(val) == 0 { - return defaultValue - } - return val -} - -// GetEnv returns an environment value if not nil, and an ok boolean. -func GetEnv(key string) (string, bool) { - val := os.Getenv(key) - if len(val) == 0 { - return "", false - } - return val, true -} - var argumentEnvironment = regexp.MustCompile("(?ms)^(.+)\\=(.*)$") var validArgumentEnvironment = regexp.MustCompile("(?ms)^(\\w+)\\=(.*)$") diff --git a/pkg/kubectl/cmd/util/env/env_parse_test.go b/pkg/kubectl/cmd/set/env/env_parse_test.go similarity index 76% rename from pkg/kubectl/cmd/util/env/env_parse_test.go rename to pkg/kubectl/cmd/set/env/env_parse_test.go index 32be9833c76..5cff84a1849 100644 --- a/pkg/kubectl/cmd/util/env/env_parse_test.go +++ b/pkg/kubectl/cmd/set/env/env_parse_test.go @@ -19,36 +19,9 @@ package env import ( "fmt" "io" - "os" "strings" ) -func ExampleEnv_defaultValue() { - fmt.Println(Env("TESTENVVAR", "default")) - // Output: default -} - -func ExampleEnv_variableExists() { - os.Setenv("TESTENVVAR", "test value") - defer os.Unsetenv("TESTENVVAR") - fmt.Println(Env("TESTENVVAR", "default")) - // Output: test value -} - -func ExampleGetEnv_variableExists() { - os.Setenv("THISVAREXISTS", "value") - defer os.Unsetenv("THISVAREXISTS") - fmt.Println(GetEnv("THISVAREXISTS")) - // Output: - // value true -} - -func ExampleGetEnv_variableDoesNotExist() { - fmt.Println(GetEnv("THISVARDOESNOTEXIST")) - // Output: - // false -} - func ExampleIsEnvironmentArgument_true() { test := "returns=true" fmt.Println(IsEnvironmentArgument(test)) diff --git a/pkg/kubectl/cmd/util/env/env_resolve.go b/pkg/kubectl/cmd/set/env/env_resolve.go similarity index 100% rename from pkg/kubectl/cmd/util/env/env_resolve.go rename to pkg/kubectl/cmd/set/env/env_resolve.go diff --git a/pkg/kubectl/cmd/set/set_env.go b/pkg/kubectl/cmd/set/set_env.go index 10800c5b716..f1e6c0cc65d 100644 --- a/pkg/kubectl/cmd/set/set_env.go +++ b/pkg/kubectl/cmd/set/set_env.go @@ -31,9 +31,9 @@ import ( "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/kubernetes" + envutil "k8s.io/kubernetes/pkg/kubectl/cmd/set/env" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index 026f03db50c..7ed95e5050b 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -58,10 +58,7 @@ go_library( go_test( name = "go_default_test", - srcs = [ - "factory_test.go", - "helpers_test.go", - ], + srcs = ["helpers_test.go"], embed = [":go_default_library"], deps = [ "//pkg/api/testapi:go_default_library", @@ -90,8 +87,6 @@ filegroup( srcs = [ ":package-srcs", "//pkg/kubectl/cmd/util/editor:all-srcs", - "//pkg/kubectl/cmd/util/env:all-srcs", - "//pkg/kubectl/cmd/util/jsonmerge:all-srcs", "//pkg/kubectl/cmd/util/openapi:all-srcs", "//pkg/kubectl/cmd/util/sanity:all-srcs", ], diff --git a/pkg/kubectl/cmd/util/editor/BUILD b/pkg/kubectl/cmd/util/editor/BUILD index 06cf3472f53..379bdcc5c95 100644 --- a/pkg/kubectl/cmd/util/editor/BUILD +++ b/pkg/kubectl/cmd/util/editor/BUILD @@ -18,11 +18,11 @@ go_library( "//pkg/apis/core:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", + "//pkg/kubectl/cmd/util/editor/crlf:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/scheme:go_default_library", - "//pkg/kubectl/util/crlf:go_default_library", "//pkg/kubectl/util/term:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/golang/glog:go_default_library", @@ -57,7 +57,10 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//pkg/kubectl/cmd/util/editor/crlf:all-srcs", + ], tags = ["automanaged"], visibility = [ "//build/visible_to:pkg_kubectl_cmd_util_editor_CONSUMERS", diff --git a/pkg/kubectl/util/crlf/BUILD b/pkg/kubectl/cmd/util/editor/crlf/BUILD similarity index 85% rename from pkg/kubectl/util/crlf/BUILD rename to pkg/kubectl/cmd/util/editor/crlf/BUILD index fad33187461..ebf46c7e204 100644 --- a/pkg/kubectl/util/crlf/BUILD +++ b/pkg/kubectl/cmd/util/editor/crlf/BUILD @@ -8,7 +8,7 @@ load( go_library( name = "go_default_library", srcs = ["crlf.go"], - importpath = "k8s.io/kubernetes/pkg/kubectl/util/crlf", + importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/crlf", ) filegroup( diff --git a/pkg/kubectl/util/crlf/crlf.go b/pkg/kubectl/cmd/util/editor/crlf/crlf.go similarity index 98% rename from pkg/kubectl/util/crlf/crlf.go rename to pkg/kubectl/cmd/util/editor/crlf/crlf.go index c08fe6f3108..524a81f3e73 100644 --- a/pkg/kubectl/util/crlf/crlf.go +++ b/pkg/kubectl/cmd/util/editor/crlf/crlf.go @@ -51,7 +51,7 @@ func (w crlfWriter) Write(b []byte) (n int, err error) { } return written + n, err } - written += 1 + written++ i = next + 1 } } diff --git a/pkg/kubectl/cmd/util/editor/editoptions.go b/pkg/kubectl/cmd/util/editor/editoptions.go index 49417e54ad2..f2d2920b98a 100644 --- a/pkg/kubectl/cmd/util/editor/editoptions.go +++ b/pkg/kubectl/cmd/util/editor/editoptions.go @@ -45,11 +45,11 @@ import ( api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor/crlf" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/scheme" - "k8s.io/kubernetes/pkg/kubectl/util/crlf" ) // EditOptions contains all the options for running edit cli command. diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 8dc6df3fa56..3a0fc5d97cf 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -17,15 +17,10 @@ limitations under the License. package util import ( - "fmt" - "strconv" - "strings" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" - api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" @@ -73,26 +68,3 @@ type Factory interface { // OpenAPISchema returns the schema openapi schema definition OpenAPISchema() (openapi.Resources, error) } - -func makePortsString(ports []api.ServicePort, useNodePort bool) string { - pieces := make([]string, len(ports)) - for ix := range ports { - var port int32 - if useNodePort { - port = ports[ix].NodePort - } else { - port = ports[ix].Port - } - pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), port) - } - return strings.Join(pieces, ",") -} - -// Extracts the protocols exposed by a service from the given service spec. -func getServiceProtocols(spec api.ServiceSpec) map[string]string { - result := make(map[string]string) - for _, servicePort := range spec.Ports { - result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol) - } - return result -} diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index e01e649f4f8..43b4a1a9b08 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -28,7 +28,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" - scaleclient "k8s.io/client-go/scale" "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" @@ -189,24 +188,6 @@ func (f *factoryImpl) OpenAPISchema() (openapi.Resources, error) { return f.openAPIGetter.getter.Get() } -func (f *factoryImpl) ScaleClient() (scaleclient.ScalesGetter, error) { - discoClient, err := f.clientGetter.ToDiscoveryClient() - if err != nil { - return nil, err - } - restClient, err := f.RESTClient() - if err != nil { - return nil, err - } - resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient) - mapper, err := f.clientGetter.ToRESTMapper() - if err != nil { - return nil, err - } - - return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil -} - // this method exists to help us find the points still relying on internal types. func InternalVersionDecoder() runtime.Decoder { return legacyscheme.Codecs.UniversalDecoder() diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go deleted file mode 100644 index 06d9ac87062..00000000000 --- a/pkg/kubectl/cmd/util/factory_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2014 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 util - -import ( - "testing" - - api "k8s.io/kubernetes/pkg/apis/core" -) - -func TestMakePortsString(t *testing.T) { - tests := []struct { - ports []api.ServicePort - useNodePort bool - expectedOutput string - }{ - {ports: nil, expectedOutput: ""}, - {ports: []api.ServicePort{}, expectedOutput: ""}, - {ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - }, - expectedOutput: "tcp:80", - }, - {ports: []api.ServicePort{ - { - Port: 80, - Protocol: "TCP", - }, - { - Port: 8080, - Protocol: "UDP", - }, - { - Port: 9000, - Protocol: "TCP", - }, - }, - expectedOutput: "tcp:80,udp:8080,tcp:9000", - }, - {ports: []api.ServicePort{ - { - Port: 80, - NodePort: 9090, - Protocol: "TCP", - }, - { - Port: 8080, - NodePort: 80, - Protocol: "UDP", - }, - }, - useNodePort: true, - expectedOutput: "tcp:9090,udp:80", - }, - } - for _, test := range tests { - output := makePortsString(test.ports, test.useNodePort) - if output != test.expectedOutput { - t.Errorf("expected: %s, saw: %s.", test.expectedOutput, output) - } - } -} diff --git a/pkg/kubectl/cmd/util/generator.go b/pkg/kubectl/cmd/util/generator.go index 551f9e94c10..7d02669bee8 100644 --- a/pkg/kubectl/cmd/util/generator.go +++ b/pkg/kubectl/cmd/util/generator.go @@ -62,7 +62,6 @@ const ( ConfigMapV1GeneratorName = "configmap/v1" ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1" RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1" - ClusterV1Beta1GeneratorName = "cluster/v1beta1" PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1" PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2" PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1" diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index d9b16d19152..aa2224652b8 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -300,16 +300,6 @@ func IsFilenameSliceEmpty(filenames []string) bool { return len(filenames) == 0 } -// Whether this cmd need watching objects. -func isWatch(cmd *cobra.Command) bool { - if w, err := cmd.Flags().GetBool("watch"); err == nil && w { - return true - } - - wo, err := cmd.Flags().GetBool("watch-only") - return err == nil && wo -} - func GetFlagString(cmd *cobra.Command, flag string) string { s, err := cmd.Flags().GetString(flag) if err != nil { @@ -336,15 +326,6 @@ func GetFlagStringArray(cmd *cobra.Command, flag string) []string { return s } -// GetWideFlag is used to determine if "-o wide" is used -func GetWideFlag(cmd *cobra.Command) bool { - f := cmd.Flags().Lookup("output") - if f != nil && f.Value != nil && f.Value.String() == "wide" { - return true - } - return false -} - func GetFlagBool(cmd *cobra.Command, flag string) bool { b, err := cmd.Flags().GetBool(flag) if err != nil { diff --git a/pkg/kubectl/cmd/util/jsonmerge/BUILD b/pkg/kubectl/cmd/util/jsonmerge/BUILD deleted file mode 100644 index 08551294bbd..00000000000 --- a/pkg/kubectl/cmd/util/jsonmerge/BUILD +++ /dev/null @@ -1,32 +0,0 @@ -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["jsonmerge.go"], - importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge", - visibility = ["//visibility:public"], - deps = [ - "//vendor/github.com/evanphx/json-patch:go_default_library", - "//vendor/github.com/golang/glog:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/mergepatch:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], - visibility = [ - "//build/visible_to:pkg_kubectl_cmd_util_jsonmerge_CONSUMERS", - ], -) diff --git a/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go b/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go deleted file mode 100644 index beebc7f052a..00000000000 --- a/pkg/kubectl/cmd/util/jsonmerge/jsonmerge.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright 2015 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 jsonmerge - -import ( - "encoding/json" - "fmt" - - "github.com/evanphx/json-patch" - "github.com/golang/glog" - - "k8s.io/apimachinery/pkg/util/mergepatch" - "k8s.io/apimachinery/pkg/util/yaml" -) - -// Delta represents a change between two JSON documents. -type Delta struct { - original []byte - edit []byte - - preconditions []PreconditionFunc -} - -// PreconditionFunc is a test to verify that an incompatible change -// has occurred before an Apply can be successful. -type PreconditionFunc func(interface{}) (hold bool, message string) - -// AddPreconditions adds precondition checks to a change which must -// be satisfied before an Apply is considered successful. If a -// precondition returns false, the Apply is failed with -// ErrPreconditionFailed. -func (d *Delta) AddPreconditions(fns ...PreconditionFunc) { - d.preconditions = append(d.preconditions, fns...) -} - -// RequireKeyUnchanged creates a precondition function that fails -// if the provided key is present in the diff (indicating its value -// has changed). -func RequireKeyUnchanged(key string) PreconditionFunc { - return func(diff interface{}) (bool, string) { - m, ok := diff.(map[string]interface{}) - if !ok { - return true, "" - } - // the presence of key in a diff means that its value has been changed, therefore - // we should fail the precondition. - _, ok = m[key] - if ok { - return false, key + " should not be changed\n" - } else { - return true, "" - } - } -} - -// RequireMetadataKeyUnchanged creates a precondition function that fails -// if the metadata.key is present in the diff (indicating its value -// has changed). -func RequireMetadataKeyUnchanged(key string) PreconditionFunc { - return func(diff interface{}) (bool, string) { - m, ok := diff.(map[string]interface{}) - if !ok { - return true, "" - } - m1, ok := m["metadata"] - if !ok { - return true, "" - } - m2, ok := m1.(map[string]interface{}) - if !ok { - return true, "" - } - _, ok = m2[key] - if ok { - return false, "metadata." + key + " should not be changed\n" - } else { - return true, "" - } - } -} - -// TestPreconditions test if preconditions hold given the edit -func TestPreconditionsHold(edit []byte, preconditions []PreconditionFunc) (bool, string) { - diff := make(map[string]interface{}) - if err := json.Unmarshal(edit, &diff); err != nil { - return false, err.Error() - } - for _, fn := range preconditions { - if hold, msg := fn(diff); !hold { - return false, msg - } - } - return true, "" -} - -// NewDelta accepts two JSON or YAML documents and calculates the difference -// between them. It returns a Delta object which can be used to resolve -// conflicts against a third version with a common parent, or an error -// if either document is in error. -func NewDelta(from, to []byte) (*Delta, error) { - d := &Delta{} - before, err := yaml.ToJSON(from) - if err != nil { - return nil, err - } - after, err := yaml.ToJSON(to) - if err != nil { - return nil, err - } - diff, err := jsonpatch.CreateMergePatch(before, after) - if err != nil { - return nil, err - } - glog.V(6).Infof("Patch created from:\n%s\n%s\n%s", string(before), string(after), string(diff)) - d.original = before - d.edit = diff - return d, nil -} - -// Apply attempts to apply the changes described by Delta onto latest, -// returning an error if the changes cannot be applied cleanly. -// IsConflicting will be true if the changes overlap, otherwise a -// generic error will be returned. -func (d *Delta) Apply(latest []byte) ([]byte, error) { - base, err := yaml.ToJSON(latest) - if err != nil { - return nil, err - } - changes, err := jsonpatch.CreateMergePatch(d.original, base) - if err != nil { - return nil, err - } - diff1 := make(map[string]interface{}) - if err := json.Unmarshal(d.edit, &diff1); err != nil { - return nil, err - } - diff2 := make(map[string]interface{}) - if err := json.Unmarshal(changes, &diff2); err != nil { - return nil, err - } - for _, fn := range d.preconditions { - hold1, _ := fn(diff1) - hold2, _ := fn(diff2) - if !hold1 || !hold2 { - return nil, ErrPreconditionFailed - } - } - - glog.V(6).Infof("Testing for conflict between:\n%s\n%s", string(d.edit), string(changes)) - hasConflicts, err := mergepatch.HasConflicts(diff1, diff2) - if err != nil { - return nil, err - } - if hasConflicts { - return nil, ErrConflict - } - - return jsonpatch.MergePatch(base, d.edit) -} - -// IsConflicting returns true if the provided error indicates a -// conflict exists between the original changes and the applied -// changes. -func IsConflicting(err error) bool { - return err == ErrConflict -} - -// IsPreconditionFailed returns true if the provided error indicates -// a Delta precondition did not succeed. -func IsPreconditionFailed(err error) bool { - return err == ErrPreconditionFailed -} - -var ErrPreconditionFailed = fmt.Errorf("a precondition failed") -var ErrConflict = fmt.Errorf("changes are in conflict") - -func (d *Delta) Edit() []byte { - return d.edit -} diff --git a/pkg/kubectl/util/BUILD b/pkg/kubectl/util/BUILD index ad3c1b73130..73dfd3a1932 100644 --- a/pkg/kubectl/util/BUILD +++ b/pkg/kubectl/util/BUILD @@ -98,7 +98,6 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", - "//pkg/kubectl/util/crlf:all-srcs", "//pkg/kubectl/util/hash:all-srcs", "//pkg/kubectl/util/i18n:all-srcs", "//pkg/kubectl/util/logs:all-srcs", From 4b836d77d5f046c6b9e124975b79af6badfa51f4 Mon Sep 17 00:00:00 2001 From: David Eads Date: Tue, 29 May 2018 11:18:50 -0400 Subject: [PATCH 114/416] update set selector to use resource builder flags --- pkg/kubectl/cmd/set/BUILD | 1 + pkg/kubectl/cmd/set/set_selector.go | 84 +++++-------------- pkg/kubectl/cmd/set/set_selector_test.go | 46 +++++----- .../genericclioptions/builder_flags.go | 43 ++++++++-- 4 files changed, 82 insertions(+), 92 deletions(-) diff --git a/pkg/kubectl/cmd/set/BUILD b/pkg/kubectl/cmd/set/BUILD index aba25208706..51dd84ba7a9 100644 --- a/pkg/kubectl/cmd/set/BUILD +++ b/pkg/kubectl/cmd/set/BUILD @@ -68,6 +68,7 @@ go_test( "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", + "//pkg/kubectl/genericclioptions/printers:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/scheme:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", diff --git a/pkg/kubectl/cmd/set/set_selector.go b/pkg/kubectl/cmd/set/set_selector.go index 3f29dd07c05..70199c37679 100644 --- a/pkg/kubectl/cmd/set/set_selector.go +++ b/pkg/kubectl/cmd/set/set_selector.go @@ -23,7 +23,6 @@ import ( "github.com/spf13/cobra" "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -40,26 +39,23 @@ import ( // SelectorOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of // referencing the cmd.Flags() type SetSelectorOptions struct { - fileOptions resource.FilenameOptions - - PrintFlags *genericclioptions.PrintFlags - RecordFlags *genericclioptions.RecordFlags - - local bool - dryrun bool - all bool - output string + // Bound + ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags + PrintFlags *genericclioptions.PrintFlags + RecordFlags *genericclioptions.RecordFlags + dryrun bool + // set by args resources []string selector *metav1.LabelSelector - ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) - - PrintObj printers.ResourcePrinterFunc - Recorder genericclioptions.Recorder - - builder *resource.Builder + // computed + WriteToServer bool + PrintObj printers.ResourcePrinterFunc + Recorder genericclioptions.Recorder + ResourceFinder genericclioptions.ResourceFinder + // set at initialization genericclioptions.IOStreams } @@ -79,6 +75,12 @@ var ( func NewSelectorOptions(streams genericclioptions.IOStreams) *SetSelectorOptions { return &SetSelectorOptions{ + ResourceBuilderFlags: genericclioptions.NewResourceBuilderFlags(). + WithScheme(scheme.Scheme). + WithAll(false). + WithLocal(false). + WithUninitialized(false). + WithLatest(), PrintFlags: genericclioptions.NewPrintFlags("selector updated").WithTypeSetter(scheme.Scheme), RecordFlags: genericclioptions.NewRecordFlags(), @@ -105,16 +107,12 @@ func NewCmdSelector(f cmdutil.Factory, streams genericclioptions.IOStreams) *cob }, } + o.ResourceBuilderFlags.AddFlags(cmd.Flags()) o.PrintFlags.AddFlags(cmd) o.RecordFlags.AddFlags(cmd) - cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") - cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, set selector will NOT contact api-server but run locally.") cmd.Flags().String("resource-version", "", "If non-empty, the selectors update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.") - usage := "the resource to update the selectors" - cmdutil.AddFilenameOptionFlags(cmd, &o.fileOptions, usage) cmdutil.AddDryRunFlag(cmd) - cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -130,40 +128,14 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg } o.dryrun = cmdutil.GetDryRunFlag(cmd) - o.output = cmdutil.GetFlagString(cmd, "output") - - cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } o.resources, o.selector, err = getResourcesAndSelector(args) if err != nil { return err } - includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) - o.builder = f.NewBuilder(). - WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). - LocalParam(o.local). - ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, &o.fileOptions). - IncludeUninitialized(includeUninitialized). - Flatten() - - if !o.local { - o.builder. - ResourceTypeOrNameArgs(o.all, o.resources...). - Latest() - } else { - // if a --local flag was provided, and a resource was specified in the form - // /, fail immediately as --local cannot query the api server - // for the specified resource. - if len(o.resources) > 0 { - return resource.LocalResourceError - } - } + o.ResourceFinder = o.ResourceBuilderFlags.ToBuilder(f, o.resources) + o.WriteToServer = !(*o.ResourceBuilderFlags.Local || o.dryrun) if o.dryrun { o.PrintFlags.Complete("%s (dry run)") @@ -174,17 +146,11 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg } o.PrintObj = printer.PrintObj - o.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return f.ClientForMapping(mapping) - } return err } // Validate basic inputs func (o *SetSelectorOptions) Validate() error { - if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.fileOptions.Filenames) { - return fmt.Errorf("one or more resources must be specified as or /") - } if o.selector == nil { return fmt.Errorf("one selector is required") } @@ -193,11 +159,7 @@ func (o *SetSelectorOptions) Validate() error { // RunSelector executes the command. func (o *SetSelectorOptions) RunSelector() error { - r := o.builder.Do() - err := r.Err() - if err != nil { - return err - } + r := o.ResourceFinder.Do() return r.Visit(func(info *resource.Info, err error) error { patch := &Patch{Info: info} @@ -218,7 +180,7 @@ func (o *SetSelectorOptions) RunSelector() error { if patch.Err != nil { return patch.Err } - if o.local || o.dryrun { + if !o.WriteToServer { return o.PrintObj(info.Object, o.Out) } diff --git a/pkg/kubectl/cmd/set/set_selector_test.go b/pkg/kubectl/cmd/set/set_selector_test.go index 351e6d98d35..e1924f32c56 100644 --- a/pkg/kubectl/cmd/set/set_selector_test.go +++ b/pkg/kubectl/cmd/set/set_selector_test.go @@ -17,7 +17,6 @@ limitations under the License. package set import ( - "net/http" "reflect" "strings" "testing" @@ -28,13 +27,9 @@ import ( extensionsv1beta1 "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - restclient "k8s.io/client-go/rest" - "k8s.io/client-go/rest/fake" - cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" - "k8s.io/kubernetes/pkg/kubectl/scheme" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" ) func TestUpdateSelectorForObjectTypes(t *testing.T) { @@ -317,27 +312,30 @@ func TestGetResourcesAndSelector(t *testing.T) { } func TestSelectorTest(t *testing.T) { - tf := cmdtesting.NewTestFactory().WithNamespace("test") - defer tf.Cleanup() - - tf.Client = &fake.RESTClient{ - GroupVersion: schema.GroupVersion{Version: ""}, - NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) - return nil, nil - }), + info := &resource.Info{ + Object: &v1.Service{ + TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Service"}, + ObjectMeta: metav1.ObjectMeta{Namespace: "some-ns", Name: "cassandra"}, + }, } - tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} - streams, _, buf, _ := genericclioptions.NewTestIOStreams() - cmd := NewCmdSelector(tf, streams) - cmd.Flags().Set("output", "name") - cmd.Flags().Set("local", "true") - cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/statefulset/cassandra/service.yaml") + labelToSet, err := metav1.ParseToLabelSelector("environment=qa") + if err != nil { + t.Fatal(err) + } - cmd.Run(cmd, []string{"environment=qa"}) + iostreams, _, buf, _ := genericclioptions.NewTestIOStreams() + o := &SetSelectorOptions{ + selector: labelToSet, + ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(info), + Recorder: genericclioptions.NoopRecorder{}, + PrintObj: (&printers.NamePrinter{}).PrintObj, + IOStreams: iostreams, + } + if err := o.RunSelector(); err != nil { + t.Fatal(err) + } if !strings.Contains(buf.String(), "service/cassandra") { t.Errorf("did not set selector: %s", buf.String()) } diff --git a/pkg/kubectl/genericclioptions/builder_flags.go b/pkg/kubectl/genericclioptions/builder_flags.go index ad0ec7a6376..43ca43b3b6e 100644 --- a/pkg/kubectl/genericclioptions/builder_flags.go +++ b/pkg/kubectl/genericclioptions/builder_flags.go @@ -27,14 +27,16 @@ import ( type ResourceBuilderFlags struct { FileNameFlags *FileNameFlags - LabelSelector *string - FieldSelector *string - AllNamespaces *bool - All *bool - Local *bool + LabelSelector *string + FieldSelector *string + AllNamespaces *bool + All *bool + Local *bool + IncludeUninitialized *bool - Scheme *runtime.Scheme - Latest bool + Scheme *runtime.Scheme + Latest bool + StopOnFirstError bool } // NewResourceBuilderFlags returns a default ResourceBuilderFlags @@ -85,6 +87,12 @@ func (o *ResourceBuilderFlags) WithLocal(defaultVal bool) *ResourceBuilderFlags return o } +// WithUninitialized is using an alpha feature and may be dropped +func (o *ResourceBuilderFlags) WithUninitialized(defaultVal bool) *ResourceBuilderFlags { + o.IncludeUninitialized = &defaultVal + return o +} + func (o *ResourceBuilderFlags) WithScheme(scheme *runtime.Scheme) *ResourceBuilderFlags { o.Scheme = scheme return o @@ -95,6 +103,11 @@ func (o *ResourceBuilderFlags) WithLatest() *ResourceBuilderFlags { return o } +func (o *ResourceBuilderFlags) StopOnError() *ResourceBuilderFlags { + o.StopOnFirstError = true + return o +} + // AddFlags registers flags for finding resources func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { o.FileNameFlags.AddFlags(flagset) @@ -114,6 +127,9 @@ func (o *ResourceBuilderFlags) AddFlags(flagset *pflag.FlagSet) { if o.Local != nil { flagset.BoolVar(o.Local, "local", *o.Local, "If true, annotation will NOT contact api-server but run locally.") } + if o.IncludeUninitialized != nil { + flagset.BoolVar(o.IncludeUninitialized, "include-uninitialized", *o.IncludeUninitialized, `If true, the kubectl command applies to uninitialized objects. If explicitly set to false, this flag overrides other flags that make the kubectl commands apply to uninitialized objects, e.g., "--all". Objects with empty metadata.initializers are regarded as initialized.`) + } } // ToBuilder gives you back a resource finder to visit resources that are located @@ -153,8 +169,21 @@ func (o *ResourceBuilderFlags) ToBuilder(restClientGetter RESTClientGetter, reso if o.Latest { builder.Latest() } + } else { builder.Local() + + if len(resources) > 0 { + builder.AddError(resource.LocalResourceError) + } + } + + if o.IncludeUninitialized != nil { + builder.IncludeUninitialized(*o.IncludeUninitialized) + } + + if !o.StopOnFirstError { + builder.ContinueOnError() } return &ResourceFindBuilderWrapper{ From 9b068706209c5133e65e9c64bb791a86d6507cbc Mon Sep 17 00:00:00 2001 From: Jonathan Basseri Date: Tue, 8 May 2018 15:10:40 -0700 Subject: [PATCH 115/416] Clean up names and comments in equivalence cache. --- pkg/scheduler/core/equivalence_cache.go | 111 +++++++++++-------- pkg/scheduler/core/equivalence_cache_test.go | 26 ++--- pkg/scheduler/core/generic_scheduler.go | 6 +- 3 files changed, 78 insertions(+), 65 deletions(-) diff --git a/pkg/scheduler/core/equivalence_cache.go b/pkg/scheduler/core/equivalence_cache.go index da50d2a81f7..6e080a256fe 100644 --- a/pkg/scheduler/core/equivalence_cache.go +++ b/pkg/scheduler/core/equivalence_cache.go @@ -31,22 +31,30 @@ import ( "github.com/golang/glog" ) -// EquivalenceCache holds: -// 1. a map of AlgorithmCache with node name as key -// 2. function to get equivalence pod +// EquivalenceCache saves and reuses the output of predicate functions. Use +// RunPredicate to get or update the cached results. An appropriate Invalidate* +// function should be called when some predicate results are no longer valid. +// +// Internally, results are keyed by node name, predicate name, and "equivalence +// class". (Equivalence class is defined in the `EquivalenceClassInfo` type.) +// Saved results will be reused until an appropriate invalidation function is +// called. type EquivalenceCache struct { - mu sync.RWMutex - algorithmCache map[string]AlgorithmCache + mu sync.RWMutex + cache nodeMap } -// The AlgorithmCache stores PredicateMap with predicate name as key, PredicateMap as value. -type AlgorithmCache map[string]PredicateMap +// nodeMap stores PredicateCaches with node name as the key. +type nodeMap map[string]predicateMap -// PredicateMap stores HostPrediacte with equivalence hash as key -type PredicateMap map[uint64]HostPredicate +// predicateMap stores resultMaps with predicate name as the key. +type predicateMap map[string]resultMap -// HostPredicate is the cached predicate result -type HostPredicate struct { +// resultMap stores PredicateResult with pod equivalence hash as the key. +type resultMap map[uint64]predicateResult + +// predicateResult stores the output of a FitPredicate. +type predicateResult struct { Fit bool FailReasons []algorithm.PredicateFailureReason } @@ -55,12 +63,12 @@ type HostPredicate struct { // result from previous scheduling. func NewEquivalenceCache() *EquivalenceCache { return &EquivalenceCache{ - algorithmCache: make(map[string]AlgorithmCache), + cache: make(nodeMap), } } -// RunPredicate will return a cached predicate result. In case of a cache miss, the predicate will -// be run and its results cached for the next call. +// RunPredicate returns a cached predicate result. In case of a cache miss, the predicate will be +// run and its results cached for the next call. // // NOTE: RunPredicate will not update the equivalence cache if the given NodeInfo is stale. func (ec *EquivalenceCache) RunPredicate( @@ -69,7 +77,7 @@ func (ec *EquivalenceCache) RunPredicate( pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *schedulercache.NodeInfo, - equivClassInfo *equivalenceClassInfo, + equivClassInfo *EquivalenceClassInfo, cache schedulercache.Cache, ) (bool, []algorithm.PredicateFailureReason, error) { if nodeInfo == nil || nodeInfo.Node() == nil { @@ -111,20 +119,20 @@ func (ec *EquivalenceCache) updateResult( return } nodeName := nodeInfo.Node().GetName() - if _, exist := ec.algorithmCache[nodeName]; !exist { - ec.algorithmCache[nodeName] = AlgorithmCache{} + if _, exist := ec.cache[nodeName]; !exist { + ec.cache[nodeName] = make(predicateMap) } - predicateItem := HostPredicate{ + predicateItem := predicateResult{ Fit: fit, FailReasons: reasons, } // if cached predicate map already exists, just update the predicate by key - if predicateMap, ok := ec.algorithmCache[nodeName][predicateKey]; ok { + if predicates, ok := ec.cache[nodeName][predicateKey]; ok { // maps in golang are references, no need to add them back - predicateMap[equivalenceHash] = predicateItem + predicates[equivalenceHash] = predicateItem } else { - ec.algorithmCache[nodeName][predicateKey] = - PredicateMap{ + ec.cache[nodeName][predicateKey] = + resultMap{ equivalenceHash: predicateItem, } } @@ -143,11 +151,11 @@ func (ec *EquivalenceCache) lookupResult( defer ec.mu.RUnlock() glog.V(5).Infof("Begin to calculate predicate: %v for pod: %s on node: %s based on equivalence cache", predicateKey, podName, nodeName) - if hostPredicate, exist := ec.algorithmCache[nodeName][predicateKey][equivalenceHash]; exist { - if hostPredicate.Fit { + if result, exist := ec.cache[nodeName][predicateKey][equivalenceHash]; exist { + if result.Fit { return true, []algorithm.PredicateFailureReason{}, false } - return false, hostPredicate.FailReasons, false + return false, result.FailReasons, false } // is invalid return false, []algorithm.PredicateFailureReason{}, true @@ -161,40 +169,43 @@ func (ec *EquivalenceCache) InvalidateCachedPredicateItem(nodeName string, predi ec.mu.Lock() defer ec.mu.Unlock() for predicateKey := range predicateKeys { - delete(ec.algorithmCache[nodeName], predicateKey) + delete(ec.cache[nodeName], predicateKey) } glog.V(5).Infof("Done invalidating cached predicates: %v on node: %s", predicateKeys, nodeName) } -// InvalidateCachedPredicateItemOfAllNodes marks all items of given predicateKeys, of all pods, on all node as invalid +// InvalidateCachedPredicateItemOfAllNodes marks all items of given +// predicateKeys, of all pods, on all node as invalid func (ec *EquivalenceCache) InvalidateCachedPredicateItemOfAllNodes(predicateKeys sets.String) { if len(predicateKeys) == 0 { return } ec.mu.Lock() defer ec.mu.Unlock() - // algorithmCache uses nodeName as key, so we just iterate it and invalid given predicates - for _, algorithmCache := range ec.algorithmCache { + // ec.cache uses nodeName as key, so we just iterate it and invalid given predicates + for _, predicates := range ec.cache { for predicateKey := range predicateKeys { - delete(algorithmCache, predicateKey) + delete(predicates, predicateKey) } } glog.V(5).Infof("Done invalidating cached predicates: %v on all node", predicateKeys) } -// InvalidateAllCachedPredicateItemOfNode marks all cached items on given node as invalid +// InvalidateAllCachedPredicateItemOfNode marks all cached items on given node +// as invalid func (ec *EquivalenceCache) InvalidateAllCachedPredicateItemOfNode(nodeName string) { ec.mu.Lock() defer ec.mu.Unlock() - delete(ec.algorithmCache, nodeName) + delete(ec.cache, nodeName) glog.V(5).Infof("Done invalidating all cached predicates on node: %s", nodeName) } -// InvalidateCachedPredicateItemForPodAdd is a wrapper of InvalidateCachedPredicateItem for pod add case +// InvalidateCachedPredicateItemForPodAdd is a wrapper of +// InvalidateCachedPredicateItem for pod add case func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, nodeName string) { // MatchInterPodAffinity: we assume scheduler can make sure newly bound pod - // will not break the existing inter pod affinity. So we does not need to invalidate - // MatchInterPodAffinity when pod added. + // will not break the existing inter pod affinity. So we does not need to + // invalidate MatchInterPodAffinity when pod added. // // But when a pod is deleted, existing inter pod affinity may become invalid. // (e.g. this pod was preferred by some else, or vice versa) @@ -227,22 +238,23 @@ func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, ec.InvalidateCachedPredicateItem(nodeName, invalidPredicates) } -// equivalenceClassInfo holds equivalence hash which is used for checking equivalence cache. -// We will pass this to podFitsOnNode to ensure equivalence hash is only calculated per schedule. -type equivalenceClassInfo struct { +// EquivalenceClassInfo holds equivalence hash which is used for checking +// equivalence cache. We will pass this to podFitsOnNode to ensure equivalence +// hash is only calculated per schedule. +type EquivalenceClassInfo struct { // Equivalence hash. hash uint64 } -// getEquivalenceClassInfo returns a hash of the given pod. -// The hashing function returns the same value for any two pods that are -// equivalent from the perspective of scheduling. -func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceClassInfo { - equivalencePod := getEquivalenceHash(pod) +// GetEquivalenceClassInfo returns a hash of the given pod. The hashing function +// returns the same value for any two pods that are equivalent from the +// perspective of scheduling. +func (ec *EquivalenceCache) GetEquivalenceClassInfo(pod *v1.Pod) *EquivalenceClassInfo { + equivalencePod := getEquivalencePod(pod) if equivalencePod != nil { hash := fnv.New32a() hashutil.DeepHashObject(hash, equivalencePod) - return &equivalenceClassInfo{ + return &EquivalenceClassInfo{ hash: uint64(hash.Sum32()), } } @@ -254,9 +266,9 @@ func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceCla // include any Pod field which is used by a FitPredicate. // // NOTE: For equivalence hash to be formally correct, lists and maps in the -// equivalencePod should be normalized. (e.g. by sorting them) However, the -// vast majority of equivalent pod classes are expected to be created from a -// single pod template, so they will all have the same ordering. +// equivalencePod should be normalized. (e.g. by sorting them) However, the vast +// majority of equivalent pod classes are expected to be created from a single +// pod template, so they will all have the same ordering. type equivalencePod struct { Namespace *string Labels map[string]string @@ -269,8 +281,9 @@ type equivalencePod struct { Volumes []v1.Volume // See note about ordering } -// getEquivalenceHash returns the equivalencePod for a Pod. -func getEquivalenceHash(pod *v1.Pod) *equivalencePod { +// getEquivalencePod returns a normalized representation of a pod so that two +// "equivalent" pods will hash to the same value. +func getEquivalencePod(pod *v1.Pod) *equivalencePod { ep := &equivalencePod{ Namespace: &pod.Namespace, Labels: pod.Labels, diff --git a/pkg/scheduler/core/equivalence_cache_test.go b/pkg/scheduler/core/equivalence_cache_test.go index 412e258510b..efbcf43e9a6 100644 --- a/pkg/scheduler/core/equivalence_cache_test.go +++ b/pkg/scheduler/core/equivalence_cache_test.go @@ -251,7 +251,7 @@ func TestRunPredicate(t *testing.T) { meta := algorithm.EmptyPredicateMetadataProducer(nil, nil) ecache := NewEquivalenceCache() - equivClass := ecache.getEquivalenceClassInfo(pod) + equivClass := ecache.GetEquivalenceClassInfo(pod) if test.expectCacheHit { ecache.updateResult(pod.Name, "testPredicate", test.expectFit, test.expectedReasons, equivClass.hash, test.cache, node) } @@ -311,7 +311,7 @@ func TestUpdateResult(t *testing.T) { reasons []algorithm.PredicateFailureReason equivalenceHash uint64 expectPredicateMap bool - expectCacheItem HostPredicate + expectCacheItem predicateResult cache schedulercache.Cache }{ { @@ -322,7 +322,7 @@ func TestUpdateResult(t *testing.T) { fit: true, equivalenceHash: 123, expectPredicateMap: false, - expectCacheItem: HostPredicate{ + expectCacheItem: predicateResult{ Fit: true, }, cache: &upToDateCache{}, @@ -335,7 +335,7 @@ func TestUpdateResult(t *testing.T) { fit: false, equivalenceHash: 123, expectPredicateMap: true, - expectCacheItem: HostPredicate{ + expectCacheItem: predicateResult{ Fit: false, }, cache: &upToDateCache{}, @@ -344,12 +344,12 @@ func TestUpdateResult(t *testing.T) { for _, test := range tests { ecache := NewEquivalenceCache() if test.expectPredicateMap { - ecache.algorithmCache[test.nodeName] = AlgorithmCache{} - predicateItem := HostPredicate{ + ecache.cache[test.nodeName] = make(predicateMap) + predicateItem := predicateResult{ Fit: true, } - ecache.algorithmCache[test.nodeName][test.predicateKey] = - PredicateMap{ + ecache.cache[test.nodeName][test.predicateKey] = + resultMap{ test.equivalenceHash: predicateItem, } } @@ -366,7 +366,7 @@ func TestUpdateResult(t *testing.T) { node, ) - cachedMapItem, ok := ecache.algorithmCache[test.nodeName][test.predicateKey] + cachedMapItem, ok := ecache.cache[test.nodeName][test.predicateKey] if !ok { t.Errorf("Failed: %s, can't find expected cache item: %v", test.name, test.expectCacheItem) @@ -618,7 +618,7 @@ func TestGetEquivalenceHash(t *testing.T) { t.Run(test.name, func(t *testing.T) { for i, podInfo := range test.podInfoList { testPod := podInfo.pod - eclassInfo := ecache.getEquivalenceClassInfo(testPod) + eclassInfo := ecache.GetEquivalenceClassInfo(testPod) if eclassInfo == nil && podInfo.hashIsValid { t.Errorf("Failed: pod %v is expected to have valid hash", testPod) } @@ -708,7 +708,7 @@ func TestInvalidateCachedPredicateItemOfAllNodes(t *testing.T) { // there should be no cached predicate any more for _, test := range tests { - if algorithmCache, exist := ecache.algorithmCache[test.nodeName]; exist { + if algorithmCache, exist := ecache.cache[test.nodeName]; exist { if _, exist := algorithmCache[testPredicate]; exist { t.Errorf("Failed: cached item for predicate key: %v on node: %v should be invalidated", testPredicate, test.nodeName) @@ -778,7 +778,7 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) { for _, test := range tests { // invalidate cached predicate for all nodes ecache.InvalidateAllCachedPredicateItemOfNode(test.nodeName) - if _, exist := ecache.algorithmCache[test.nodeName]; exist { + if _, exist := ecache.cache[test.nodeName]; exist { t.Errorf("Failed: cached item for node: %v should be invalidated", test.nodeName) break } @@ -788,7 +788,7 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) { func BenchmarkEquivalenceHash(b *testing.B) { pod := makeBasicPod("test") for i := 0; i < b.N; i++ { - getEquivalenceHash(pod) + getEquivalencePod(pod) } } diff --git a/pkg/scheduler/core/generic_scheduler.go b/pkg/scheduler/core/generic_scheduler.go index 960054b0d14..83c21f45c31 100644 --- a/pkg/scheduler/core/generic_scheduler.go +++ b/pkg/scheduler/core/generic_scheduler.go @@ -342,10 +342,10 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v // We can use the same metadata producer for all nodes. meta := g.predicateMetaProducer(pod, g.cachedNodeInfoMap) - var equivCacheInfo *equivalenceClassInfo + var equivCacheInfo *EquivalenceClassInfo if g.equivalenceCache != nil { // getEquivalenceClassInfo will return immediately if no equivalence pod found - equivCacheInfo = g.equivalenceCache.getEquivalenceClassInfo(pod) + equivCacheInfo = g.equivalenceCache.GetEquivalenceClassInfo(pod) } checkNode := func(i int) { @@ -462,7 +462,7 @@ func podFitsOnNode( ecache *EquivalenceCache, queue SchedulingQueue, alwaysCheckAllPredicates bool, - equivCacheInfo *equivalenceClassInfo, + equivCacheInfo *EquivalenceClassInfo, ) (bool, []algorithm.PredicateFailureReason, error) { var ( eCacheAvailable bool From 5d13798e5c33ce02834823cdaf58a7c319cb2e1a Mon Sep 17 00:00:00 2001 From: Jonathan Basseri Date: Tue, 8 May 2018 16:00:37 -0700 Subject: [PATCH 116/416] Change the return of EquivalenceClass.lookupResult. This makes the lookup behave like a normal map lookup, so it is easier for readers to follow the logic. It also inverts the "invalid" bool to an "ok" bool because `!invalid` is a double negative. --- pkg/scheduler/core/equivalence_cache.go | 24 ++++------- pkg/scheduler/core/equivalence_cache_test.go | 45 +++++++++++--------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/pkg/scheduler/core/equivalence_cache.go b/pkg/scheduler/core/equivalence_cache.go index 6e080a256fe..b2d56ff2a03 100644 --- a/pkg/scheduler/core/equivalence_cache.go +++ b/pkg/scheduler/core/equivalence_cache.go @@ -85,9 +85,9 @@ func (ec *EquivalenceCache) RunPredicate( return false, []algorithm.PredicateFailureReason{}, fmt.Errorf("nodeInfo is nil or node is invalid") } - fit, reasons, invalid := ec.lookupResult(pod.GetName(), nodeInfo.Node().GetName(), predicateKey, equivClassInfo.hash) - if !invalid { - return fit, reasons, nil + result, ok := ec.lookupResult(pod.GetName(), nodeInfo.Node().GetName(), predicateKey, equivClassInfo.hash) + if ok { + return result.Fit, result.FailReasons, nil } fit, reasons, err := pred(pod, meta, nodeInfo) if err != nil { @@ -139,26 +139,18 @@ func (ec *EquivalenceCache) updateResult( glog.V(5).Infof("Updated cached predicate: %v for pod: %v on node: %s, with item %v", predicateKey, podName, nodeName, predicateItem) } -// lookupResult returns cached predicate results: -// 1. if pod fit -// 2. reasons if pod did not fit -// 3. if cache item is not found +// lookupResult returns cached predicate results and a bool saying whether a +// cache entry was found. func (ec *EquivalenceCache) lookupResult( podName, nodeName, predicateKey string, equivalenceHash uint64, -) (bool, []algorithm.PredicateFailureReason, bool) { +) (value predicateResult, ok bool) { ec.mu.RLock() defer ec.mu.RUnlock() glog.V(5).Infof("Begin to calculate predicate: %v for pod: %s on node: %s based on equivalence cache", predicateKey, podName, nodeName) - if result, exist := ec.cache[nodeName][predicateKey][equivalenceHash]; exist { - if result.Fit { - return true, []algorithm.PredicateFailureReason{}, false - } - return false, result.FailReasons, false - } - // is invalid - return false, []algorithm.PredicateFailureReason{}, true + value, ok = ec.cache[nodeName][predicateKey][equivalenceHash] + return value, ok } // InvalidateCachedPredicateItem marks all items of given predicateKeys, of all pods, on the given node as invalid diff --git a/pkg/scheduler/core/equivalence_cache_test.go b/pkg/scheduler/core/equivalence_cache_test.go index efbcf43e9a6..b745f1c81aa 100644 --- a/pkg/scheduler/core/equivalence_cache_test.go +++ b/pkg/scheduler/core/equivalence_cache_test.go @@ -287,14 +287,14 @@ func TestRunPredicate(t *testing.T) { if !test.expectCacheHit && test.pred.callCount == 0 { t.Errorf("Predicate should be called") } - _, _, invalid := ecache.lookupResult(pod.Name, node.Node().Name, "testPredicate", equivClass.hash) - if invalid && test.expectCacheWrite { + _, ok := ecache.lookupResult(pod.Name, node.Node().Name, "testPredicate", equivClass.hash) + if !ok && test.expectCacheWrite { t.Errorf("Cache write should happen") } - if !test.expectCacheHit && test.expectCacheWrite && invalid { + if !test.expectCacheHit && test.expectCacheWrite && !ok { t.Errorf("Cache write should happen") } - if !test.expectCacheHit && !test.expectCacheWrite && !invalid { + if !test.expectCacheHit && !test.expectCacheWrite && ok { t.Errorf("Cache write should not happen") } }) @@ -396,8 +396,8 @@ func TestLookupResult(t *testing.T) { equivalenceHashForUpdatePredicate uint64 equivalenceHashForCalPredicate uint64 cachedItem predicateItemType - expectedInvalidPredicateKey bool - expectedInvalidEquivalenceHash bool + expectedPredicateKeyMiss bool + expectedEquivalenceHashMiss bool expectedPredicateItem predicateItemType cache schedulercache.Cache }{ @@ -412,7 +412,7 @@ func TestLookupResult(t *testing.T) { fit: false, reasons: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}, }, - expectedInvalidPredicateKey: true, + expectedPredicateKeyMiss: true, expectedPredicateItem: predicateItemType{ fit: false, reasons: []algorithm.PredicateFailureReason{}, @@ -429,7 +429,7 @@ func TestLookupResult(t *testing.T) { cachedItem: predicateItemType{ fit: true, }, - expectedInvalidPredicateKey: false, + expectedPredicateKeyMiss: false, expectedPredicateItem: predicateItemType{ fit: true, reasons: []algorithm.PredicateFailureReason{}, @@ -447,7 +447,7 @@ func TestLookupResult(t *testing.T) { fit: false, reasons: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}, }, - expectedInvalidPredicateKey: false, + expectedPredicateKeyMiss: false, expectedPredicateItem: predicateItemType{ fit: false, reasons: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}, @@ -465,8 +465,8 @@ func TestLookupResult(t *testing.T) { fit: false, reasons: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}, }, - expectedInvalidPredicateKey: false, - expectedInvalidEquivalenceHash: true, + expectedPredicateKeyMiss: false, + expectedEquivalenceHashMiss: true, expectedPredicateItem: predicateItemType{ fit: false, reasons: []algorithm.PredicateFailureReason{}, @@ -490,27 +490,32 @@ func TestLookupResult(t *testing.T) { node, ) // if we want to do invalid, invalid the cached item - if test.expectedInvalidPredicateKey { + if test.expectedPredicateKeyMiss { predicateKeys := sets.NewString() predicateKeys.Insert(test.predicateKey) ecache.InvalidateCachedPredicateItem(test.nodeName, predicateKeys) } // calculate predicate with equivalence cache - fit, reasons, invalid := ecache.lookupResult(test.podName, + result, ok := ecache.lookupResult(test.podName, test.nodeName, test.predicateKey, test.equivalenceHashForCalPredicate, ) - // returned invalid should match expectedInvalidPredicateKey or expectedInvalidEquivalenceHash + fit, reasons := result.Fit, result.FailReasons + // returned invalid should match expectedPredicateKeyMiss or expectedEquivalenceHashMiss if test.equivalenceHashForUpdatePredicate != test.equivalenceHashForCalPredicate { - if invalid != test.expectedInvalidEquivalenceHash { - t.Errorf("Failed: %s, expected invalid: %v, but got: %v", - test.name, test.expectedInvalidEquivalenceHash, invalid) + if ok && test.expectedEquivalenceHashMiss { + t.Errorf("Failed: %s, expected (equivalence hash) cache miss", test.name) + } + if !ok && !test.expectedEquivalenceHashMiss { + t.Errorf("Failed: %s, expected (equivalence hash) cache hit", test.name) } } else { - if invalid != test.expectedInvalidPredicateKey { - t.Errorf("Failed: %s, expected invalid: %v, but got: %v", - test.name, test.expectedInvalidPredicateKey, invalid) + if ok && test.expectedPredicateKeyMiss { + t.Errorf("Failed: %s, expected (predicate key) cache miss", test.name) + } + if !ok && !test.expectedPredicateKeyMiss { + t.Errorf("Failed: %s, expected (predicate key) cache hit", test.name) } } // returned predicate result should match expected predicate item From ba08b05e286e7102dbc4a14638f440e8f42a171f Mon Sep 17 00:00:00 2001 From: Jonathan Basseri Date: Wed, 23 May 2018 11:41:57 -0700 Subject: [PATCH 117/416] Rename equiv. class invalidation functions. Change the invalidation functions to have cleaner and more consistent names. --- pkg/scheduler/core/equivalence_cache.go | 39 ++++++++++---------- pkg/scheduler/core/equivalence_cache_test.go | 8 ++-- pkg/scheduler/factory/factory.go | 28 +++++++------- pkg/scheduler/scheduler.go | 2 +- 4 files changed, 38 insertions(+), 39 deletions(-) diff --git a/pkg/scheduler/core/equivalence_cache.go b/pkg/scheduler/core/equivalence_cache.go index b2d56ff2a03..032b53e4a12 100644 --- a/pkg/scheduler/core/equivalence_cache.go +++ b/pkg/scheduler/core/equivalence_cache.go @@ -153,22 +153,8 @@ func (ec *EquivalenceCache) lookupResult( return value, ok } -// InvalidateCachedPredicateItem marks all items of given predicateKeys, of all pods, on the given node as invalid -func (ec *EquivalenceCache) InvalidateCachedPredicateItem(nodeName string, predicateKeys sets.String) { - if len(predicateKeys) == 0 { - return - } - ec.mu.Lock() - defer ec.mu.Unlock() - for predicateKey := range predicateKeys { - delete(ec.cache[nodeName], predicateKey) - } - glog.V(5).Infof("Done invalidating cached predicates: %v on node: %s", predicateKeys, nodeName) -} - -// InvalidateCachedPredicateItemOfAllNodes marks all items of given -// predicateKeys, of all pods, on all node as invalid -func (ec *EquivalenceCache) InvalidateCachedPredicateItemOfAllNodes(predicateKeys sets.String) { +// InvalidatePredicates clears all cached results for the given predicates. +func (ec *EquivalenceCache) InvalidatePredicates(predicateKeys sets.String) { if len(predicateKeys) == 0 { return } @@ -183,9 +169,21 @@ func (ec *EquivalenceCache) InvalidateCachedPredicateItemOfAllNodes(predicateKey glog.V(5).Infof("Done invalidating cached predicates: %v on all node", predicateKeys) } -// InvalidateAllCachedPredicateItemOfNode marks all cached items on given node -// as invalid -func (ec *EquivalenceCache) InvalidateAllCachedPredicateItemOfNode(nodeName string) { +// InvalidatePredicatesOnNode clears cached results for the given predicates on one node. +func (ec *EquivalenceCache) InvalidatePredicatesOnNode(nodeName string, predicateKeys sets.String) { + if len(predicateKeys) == 0 { + return + } + ec.mu.Lock() + defer ec.mu.Unlock() + for predicateKey := range predicateKeys { + delete(ec.cache[nodeName], predicateKey) + } + glog.V(5).Infof("Done invalidating cached predicates: %v on node: %s", predicateKeys, nodeName) +} + +// InvalidateAllPredicatesOnNode clears all cached results for one node. +func (ec *EquivalenceCache) InvalidateAllPredicatesOnNode(nodeName string) { ec.mu.Lock() defer ec.mu.Unlock() delete(ec.cache, nodeName) @@ -194,6 +192,7 @@ func (ec *EquivalenceCache) InvalidateAllCachedPredicateItemOfNode(nodeName stri // InvalidateCachedPredicateItemForPodAdd is a wrapper of // InvalidateCachedPredicateItem for pod add case +// TODO: This logic does not belong with the equivalence cache implementation. func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, nodeName string) { // MatchInterPodAffinity: we assume scheduler can make sure newly bound pod // will not break the existing inter pod affinity. So we does not need to @@ -227,7 +226,7 @@ func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, } } } - ec.InvalidateCachedPredicateItem(nodeName, invalidPredicates) + ec.InvalidatePredicatesOnNode(nodeName, invalidPredicates) } // EquivalenceClassInfo holds equivalence hash which is used for checking diff --git a/pkg/scheduler/core/equivalence_cache_test.go b/pkg/scheduler/core/equivalence_cache_test.go index b745f1c81aa..a1f3c8221cf 100644 --- a/pkg/scheduler/core/equivalence_cache_test.go +++ b/pkg/scheduler/core/equivalence_cache_test.go @@ -493,7 +493,7 @@ func TestLookupResult(t *testing.T) { if test.expectedPredicateKeyMiss { predicateKeys := sets.NewString() predicateKeys.Insert(test.predicateKey) - ecache.InvalidateCachedPredicateItem(test.nodeName, predicateKeys) + ecache.InvalidatePredicatesOnNode(test.nodeName, predicateKeys) } // calculate predicate with equivalence cache result, ok := ecache.lookupResult(test.podName, @@ -709,7 +709,7 @@ func TestInvalidateCachedPredicateItemOfAllNodes(t *testing.T) { } // invalidate cached predicate for all nodes - ecache.InvalidateCachedPredicateItemOfAllNodes(sets.NewString(testPredicate)) + ecache.InvalidatePredicates(sets.NewString(testPredicate)) // there should be no cached predicate any more for _, test := range tests { @@ -782,7 +782,7 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) { for _, test := range tests { // invalidate cached predicate for all nodes - ecache.InvalidateAllCachedPredicateItemOfNode(test.nodeName) + ecache.InvalidateAllPredicatesOnNode(test.nodeName) if _, exist := ecache.cache[test.nodeName]; exist { t.Errorf("Failed: cached item for node: %v should be invalidated", test.nodeName) break @@ -857,7 +857,7 @@ func TestEquivalenceCacheInvalidationRace(t *testing.T) { if err := cache.AddPod(pod); err != nil { t.Errorf("Could not add pod to cache: %v", err) } - eCache.InvalidateAllCachedPredicateItemOfNode("machine1") + eCache.InvalidateAllPredicatesOnNode("machine1") mockCache.cacheInvalidated <- struct{}{} }() diff --git a/pkg/scheduler/factory/factory.go b/pkg/scheduler/factory/factory.go index fb5cc22fcbe..340cd9f6ffc 100644 --- a/pkg/scheduler/factory/factory.go +++ b/pkg/scheduler/factory/factory.go @@ -410,7 +410,7 @@ func (c *configFactory) invalidatePredicatesForPvUpdate(oldPV, newPV *v1.Persist break } } - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) + c.equivalencePodCache.InvalidatePredicates(invalidPredicates) } // isZoneRegionLabel check if given key of label is zone or region label. @@ -468,7 +468,7 @@ func (c *configFactory) invalidatePredicatesForPv(pv *v1.PersistentVolume) { invalidPredicates.Insert(predicates.CheckVolumeBindingPred) } - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) + c.equivalencePodCache.InvalidatePredicates(invalidPredicates) } func (c *configFactory) onPvcAdd(obj interface{}) { @@ -538,7 +538,7 @@ func (c *configFactory) invalidatePredicatesForPvc(pvc *v1.PersistentVolumeClaim // Add/delete impacts the available PVs to choose from invalidPredicates.Insert(predicates.CheckVolumeBindingPred) } - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) + c.equivalencePodCache.InvalidatePredicates(invalidPredicates) } func (c *configFactory) invalidatePredicatesForPvcUpdate(old, new *v1.PersistentVolumeClaim) { @@ -553,12 +553,12 @@ func (c *configFactory) invalidatePredicatesForPvcUpdate(old, new *v1.Persistent invalidPredicates.Insert(maxPDVolumeCountPredicateKeys...) } - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) + c.equivalencePodCache.InvalidatePredicates(invalidPredicates) } func (c *configFactory) onServiceAdd(obj interface{}) { if c.enableEquivalenceClassCache { - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) + c.equivalencePodCache.InvalidatePredicates(serviceAffinitySet) } c.podQueue.MoveAllToActiveQueue() } @@ -569,7 +569,7 @@ func (c *configFactory) onServiceUpdate(oldObj interface{}, newObj interface{}) oldService := oldObj.(*v1.Service) newService := newObj.(*v1.Service) if !reflect.DeepEqual(oldService.Spec.Selector, newService.Spec.Selector) { - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) + c.equivalencePodCache.InvalidatePredicates(serviceAffinitySet) } } c.podQueue.MoveAllToActiveQueue() @@ -577,7 +577,7 @@ func (c *configFactory) onServiceUpdate(oldObj interface{}, newObj interface{}) func (c *configFactory) onServiceDelete(obj interface{}) { if c.enableEquivalenceClassCache { - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes(serviceAffinitySet) + c.equivalencePodCache.InvalidatePredicates(serviceAffinitySet) } c.podQueue.MoveAllToActiveQueue() } @@ -694,13 +694,13 @@ func (c *configFactory) invalidateCachedPredicatesOnUpdatePod(newPod *v1.Pod, ol if !reflect.DeepEqual(oldPod.GetLabels(), newPod.GetLabels()) { // MatchInterPodAffinity need to be reconsidered for this node, // as well as all nodes in its same failure domain. - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes( + c.equivalencePodCache.InvalidatePredicates( matchInterPodAffinitySet) } // if requested container resource changed, invalidate GeneralPredicates of this node if !reflect.DeepEqual(predicates.GetResourceRequest(newPod), predicates.GetResourceRequest(oldPod)) { - c.equivalencePodCache.InvalidateCachedPredicateItem( + c.equivalencePodCache.InvalidatePredicatesOnNode( newPod.Spec.NodeName, generalPredicatesSets) } } @@ -741,14 +741,14 @@ func (c *configFactory) invalidateCachedPredicatesOnDeletePod(pod *v1.Pod) { // MatchInterPodAffinity need to be reconsidered for this node, // as well as all nodes in its same failure domain. // TODO(resouer) can we just do this for nodes in the same failure domain - c.equivalencePodCache.InvalidateCachedPredicateItemOfAllNodes( + c.equivalencePodCache.InvalidatePredicates( matchInterPodAffinitySet) // if this pod have these PV, cached result of disk conflict will become invalid. for _, volume := range pod.Spec.Volumes { if volume.GCEPersistentDisk != nil || volume.AWSElasticBlockStore != nil || volume.RBD != nil || volume.ISCSI != nil { - c.equivalencePodCache.InvalidateCachedPredicateItem( + c.equivalencePodCache.InvalidatePredicatesOnNode( pod.Spec.NodeName, noDiskConflictSet) } } @@ -858,7 +858,7 @@ func (c *configFactory) invalidateCachedPredicatesOnNodeUpdate(newNode *v1.Node, if newNode.Spec.Unschedulable != oldNode.Spec.Unschedulable { invalidPredicates.Insert(predicates.CheckNodeConditionPred) } - c.equivalencePodCache.InvalidateCachedPredicateItem(newNode.GetName(), invalidPredicates) + c.equivalencePodCache.InvalidatePredicatesOnNode(newNode.GetName(), invalidPredicates) } } @@ -885,7 +885,7 @@ func (c *configFactory) deleteNodeFromCache(obj interface{}) { glog.Errorf("scheduler cache RemoveNode failed: %v", err) } if c.enableEquivalenceClassCache { - c.equivalencePodCache.InvalidateAllCachedPredicateItemOfNode(node.GetName()) + c.equivalencePodCache.InvalidateAllPredicatesOnNode(node.GetName()) } } @@ -1315,7 +1315,7 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue c.schedulerCache.RemoveNode(&node) // invalidate cached predicate for the node if c.enableEquivalenceClassCache { - c.equivalencePodCache.InvalidateAllCachedPredicateItemOfNode(nodeName) + c.equivalencePodCache.InvalidateAllPredicatesOnNode(nodeName) } } } diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index 98beabe5c57..b7990da15ae 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -284,7 +284,7 @@ func (sched *Scheduler) assumeAndBindVolumes(assumed *v1.Pod, host string) error if bindingRequired { if sched.config.Ecache != nil { invalidPredicates := sets.NewString(predicates.CheckVolumeBindingPred) - sched.config.Ecache.InvalidateCachedPredicateItemOfAllNodes(invalidPredicates) + sched.config.Ecache.InvalidatePredicates(invalidPredicates) } // bindVolumesWorker() will update the Pod object to put it back in the scheduler queue From a48008f5ad7906c3260c1562c3b01f13d1ebd69e Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Tue, 29 May 2018 12:56:37 -0700 Subject: [PATCH 118/416] e2e node: mark pod cgroup test as [NodeConformance] --- test/e2e_node/pods_container_manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e_node/pods_container_manager_test.go b/test/e2e_node/pods_container_manager_test.go index 1768e7d9fa7..128860b17c0 100644 --- a/test/e2e_node/pods_container_manager_test.go +++ b/test/e2e_node/pods_container_manager_test.go @@ -152,7 +152,7 @@ var _ = framework.KubeDescribe("Kubelet Cgroup Manager", func() { f := framework.NewDefaultFramework("kubelet-cgroup-manager") Describe("QOS containers", func() { Context("On enabling QOS cgroup hierarchy", func() { - It("Top level QoS containers should have been created", func() { + It("Top level QoS containers should have been created [NodeConformance]", func() { if !framework.TestContext.KubeletConfig.CgroupsPerQOS { return } From ffe817fcababca46d4989ff3982807468645d2ea Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Fri, 25 May 2018 15:55:49 -0700 Subject: [PATCH 119/416] Deprecate the in-tree keystone plugin We now have the `client-keystone-auth` in cloud-provider-openstack repository: https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/using-client-keystone-auth.md So let's drop the in-tree one, so we can remove it in 1.12 --- .../client-go/plugin/pkg/client/auth/openstack/openstack.go | 1 + 1 file changed, 1 insertion(+) diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go index 3c1c896addb..e6d7f04934a 100644 --- a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go @@ -140,6 +140,7 @@ func newOpenstackAuthProvider(_ string, config map[string]string, persister rest var ttlDuration time.Duration var err error + glog.Warningf("WARNING: in-tree openstack auth plugin is now deprecated. please use the \"client-keystone-auth\" kubectl/client-go credential plugin instead") ttl, found := config["ttl"] if !found { ttlDuration = DefaultTTLDuration From 20cd94de176842cbe97970d4db22bbdec103176e Mon Sep 17 00:00:00 2001 From: Matt Rogers Date: Tue, 29 May 2018 12:50:14 -0400 Subject: [PATCH 120/416] Add dry-run to auth reconcile Signed-off-by: Matt Rogers --- pkg/kubectl/cmd/auth/reconcile.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/kubectl/cmd/auth/reconcile.go b/pkg/kubectl/cmd/auth/reconcile.go index 555244ca61f..b6f1f6ac3eb 100644 --- a/pkg/kubectl/cmd/auth/reconcile.go +++ b/pkg/kubectl/cmd/auth/reconcile.go @@ -39,6 +39,7 @@ import ( type ReconcileOptions struct { PrintFlags *genericclioptions.PrintFlags FilenameOptions *resource.FilenameOptions + DryRun bool Visitor resource.Visitor RBACClient rbacv1client.RbacV1Interface @@ -87,6 +88,7 @@ func NewCmdReconcile(f cmdutil.Factory, streams genericclioptions.IOStreams) *co o.PrintFlags.AddFlags(cmd) cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to reconcile.") + cmd.Flags().BoolVar(&o.DryRun, "dry-run", o.DryRun, "If true, display results but do not submit changes") cmd.MarkFlagRequired("filename") return cmd @@ -128,6 +130,9 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args return err } + if o.DryRun { + o.PrintFlags.Complete("%s (dry run)") + } printer, err := o.PrintFlags.ToPrinter() if err != nil { return err @@ -168,7 +173,7 @@ func (o *ReconcileOptions) RunReconcile() error { switch t := info.Object.(type) { case *rbacv1.Role: reconcileOptions := reconciliation.ReconcileRoleOptions{ - Confirm: true, + Confirm: !o.DryRun, RemoveExtraPermissions: false, Role: reconciliation.RoleRuleOwner{Role: t}, Client: reconciliation.RoleModifier{ @@ -184,7 +189,7 @@ func (o *ReconcileOptions) RunReconcile() error { case *rbacv1.ClusterRole: reconcileOptions := reconciliation.ReconcileRoleOptions{ - Confirm: true, + Confirm: !o.DryRun, RemoveExtraPermissions: false, Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: t}, Client: reconciliation.ClusterRoleModifier{ @@ -199,7 +204,7 @@ func (o *ReconcileOptions) RunReconcile() error { case *rbacv1.RoleBinding: reconcileOptions := reconciliation.ReconcileRoleBindingOptions{ - Confirm: true, + Confirm: !o.DryRun, RemoveExtraSubjects: false, RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: t}, Client: reconciliation.RoleBindingClientAdapter{ @@ -215,7 +220,7 @@ func (o *ReconcileOptions) RunReconcile() error { case *rbacv1.ClusterRoleBinding: reconcileOptions := reconciliation.ReconcileRoleBindingOptions{ - Confirm: true, + Confirm: !o.DryRun, RemoveExtraSubjects: false, RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: t}, Client: reconciliation.ClusterRoleBindingClientAdapter{ From aeb6cacf01409361091750e6910500afc340544e Mon Sep 17 00:00:00 2001 From: Lantao Liu Date: Wed, 16 May 2018 01:14:06 -0700 Subject: [PATCH 121/416] Remove direct and indirect streaming runtime interface. --- pkg/kubelet/container/helpers.go | 19 --- pkg/kubelet/container/runtime.go | 16 +- pkg/kubelet/container/testing/fake_runtime.go | 73 +-------- pkg/kubelet/kubelet.go | 4 + pkg/kubelet/kubelet_pods.go | 155 ++++++------------ pkg/kubelet/kubelet_pods_test.go | 101 +++--------- .../kuberuntime/kuberuntime_manager.go | 2 +- 7 files changed, 79 insertions(+), 291 deletions(-) diff --git a/pkg/kubelet/container/helpers.go b/pkg/kubelet/container/helpers.go index 180a3e6df2d..abe80c545e5 100644 --- a/pkg/kubelet/container/helpers.go +++ b/pkg/kubelet/container/helpers.go @@ -17,11 +17,9 @@ limitations under the License. package container import ( - "bytes" "fmt" "hash/fnv" "strings" - "time" "github.com/golang/glog" @@ -32,7 +30,6 @@ import ( "k8s.io/client-go/tools/record" runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2" "k8s.io/kubernetes/pkg/kubelet/util/format" - "k8s.io/kubernetes/pkg/kubelet/util/ioutils" hashutil "k8s.io/kubernetes/pkg/util/hash" "k8s.io/kubernetes/third_party/forked/golang/expansion" ) @@ -253,22 +250,6 @@ func FormatPod(pod *Pod) string { return fmt.Sprintf("%s_%s(%s)", pod.Name, pod.Namespace, pod.ID) } -type containerCommandRunnerWrapper struct { - DirectStreamingRuntime -} - -var _ ContainerCommandRunner = &containerCommandRunnerWrapper{} - -func (r *containerCommandRunnerWrapper) RunInContainer(id ContainerID, cmd []string, timeout time.Duration) ([]byte, error) { - var buffer bytes.Buffer - output := ioutils.WriteCloserWrapper(&buffer) - err := r.ExecInContainer(id, cmd, nil, output, output, false, nil, timeout) - // Even if err is non-nil, there still may be output (e.g. the exec wrote to stdout or stderr but - // the command returned a nonzero exit code). Therefore, always return the output along with the - // error. - return buffer.Bytes(), err -} - // GetContainerSpec gets the container spec by containerName. func GetContainerSpec(pod *v1.Pod, containerName string) *v1.Container { for i, c := range pod.Spec.Containers { diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index 29852d435ed..70b72024c9c 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -124,22 +124,10 @@ type Runtime interface { UpdatePodCIDR(podCIDR string) error } -// DirectStreamingRuntime is the interface implemented by runtimes for which the streaming calls -// (exec/attach/port-forward) should be served directly by the Kubelet. -type DirectStreamingRuntime interface { - // Runs the command in the container of the specified pod. Attaches - // the processes stdin, stdout, and stderr. Optionally uses a tty. - ExecInContainer(containerID ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error - // Forward the specified port from the specified pod to the stream. - PortForward(pod *Pod, port int32, stream io.ReadWriteCloser) error - // ContainerAttach encapsulates the attaching to containers for testability - ContainerAttacher -} - -// IndirectStreamingRuntime is the interface implemented by runtimes that handle the serving of the +// StreamingRuntime is the interface implemented by runtimes that handle the serving of the // streaming calls (exec/attach/port-forward) themselves. In this case, Kubelet should redirect to // the runtime server. -type IndirectStreamingRuntime interface { +type StreamingRuntime interface { GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error) GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) GetPortForward(podName, podNamespace string, podUID types.UID, ports []int32) (*url.URL, error) diff --git a/pkg/kubelet/container/testing/fake_runtime.go b/pkg/kubelet/container/testing/fake_runtime.go index 3019d30094e..707ee1ac456 100644 --- a/pkg/kubelet/container/testing/fake_runtime.go +++ b/pkg/kubelet/container/testing/fake_runtime.go @@ -26,7 +26,6 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/remotecommand" "k8s.io/client-go/util/flowcontrol" . "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/volume" @@ -59,34 +58,13 @@ type FakeRuntime struct { StatusErr error } -type FakeDirectStreamingRuntime struct { - *FakeRuntime - - // Arguments to streaming method calls. - Args struct { - // Attach / Exec args - ContainerID ContainerID - Cmd []string - Stdin io.Reader - Stdout io.WriteCloser - Stderr io.WriteCloser - TTY bool - // Port-forward args - Pod *Pod - Port int32 - Stream io.ReadWriteCloser - } -} - -var _ DirectStreamingRuntime = &FakeDirectStreamingRuntime{} - const FakeHost = "localhost:12345" -type FakeIndirectStreamingRuntime struct { +type FakeStreamingRuntime struct { *FakeRuntime } -var _ IndirectStreamingRuntime = &FakeIndirectStreamingRuntime{} +var _ StreamingRuntime = &FakeStreamingRuntime{} // FakeRuntime should implement Runtime. var _ Runtime = &FakeRuntime{} @@ -311,35 +289,6 @@ func (f *FakeRuntime) GetPodStatus(uid types.UID, name, namespace string) (*PodS return &status, f.Err } -func (f *FakeDirectStreamingRuntime) ExecInContainer(containerID ContainerID, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { - f.Lock() - defer f.Unlock() - - f.CalledFunctions = append(f.CalledFunctions, "ExecInContainer") - f.Args.ContainerID = containerID - f.Args.Cmd = cmd - f.Args.Stdin = stdin - f.Args.Stdout = stdout - f.Args.Stderr = stderr - f.Args.TTY = tty - - return f.Err -} - -func (f *FakeDirectStreamingRuntime) AttachContainer(containerID ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error { - f.Lock() - defer f.Unlock() - - f.CalledFunctions = append(f.CalledFunctions, "AttachContainer") - f.Args.ContainerID = containerID - f.Args.Stdin = stdin - f.Args.Stdout = stdout - f.Args.Stderr = stderr - f.Args.TTY = tty - - return f.Err -} - func (f *FakeRuntime) GetContainerLogs(pod *v1.Pod, containerID ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) (err error) { f.Lock() defer f.Unlock() @@ -394,18 +343,6 @@ func (f *FakeRuntime) RemoveImage(image ImageSpec) error { return f.Err } -func (f *FakeDirectStreamingRuntime) PortForward(pod *Pod, port int32, stream io.ReadWriteCloser) error { - f.Lock() - defer f.Unlock() - - f.CalledFunctions = append(f.CalledFunctions, "PortForward") - f.Args.Pod = pod - f.Args.Port = port - f.Args.Stream = stream - - return f.Err -} - func (f *FakeRuntime) GetNetNS(containerID ContainerID) (string, error) { f.Lock() defer f.Unlock() @@ -455,7 +392,7 @@ func (f *FakeRuntime) ImageStats() (*ImageStats, error) { return nil, f.Err } -func (f *FakeIndirectStreamingRuntime) GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error) { +func (f *FakeStreamingRuntime) GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error) { f.Lock() defer f.Unlock() @@ -463,7 +400,7 @@ func (f *FakeIndirectStreamingRuntime) GetExec(id ContainerID, cmd []string, std return &url.URL{Host: FakeHost}, f.Err } -func (f *FakeIndirectStreamingRuntime) GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) { +func (f *FakeStreamingRuntime) GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) { f.Lock() defer f.Unlock() @@ -471,7 +408,7 @@ func (f *FakeIndirectStreamingRuntime) GetAttach(id ContainerID, stdin, stdout, return &url.URL{Host: FakeHost}, f.Err } -func (f *FakeIndirectStreamingRuntime) GetPortForward(podName, podNamespace string, podUID types.UID, ports []int32) (*url.URL, error) { +func (f *FakeStreamingRuntime) GetPortForward(podName, podNamespace string, podUID types.UID, ports []int32) (*url.URL, error) { f.Lock() defer f.Unlock() diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 421c0c98bea..7b31fdeffbb 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -670,6 +670,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration, return nil, err } klet.containerRuntime = runtime + klet.streamingRuntime = runtime klet.runner = runtime if cadvisor.UsingLegacyCadvisorStats(containerRuntime, remoteRuntimeEndpoint) { @@ -1002,6 +1003,9 @@ type Kubelet struct { // Container runtime. containerRuntime kubecontainer.Runtime + // Streaming runtime handles container streaming. + streamingRuntime kubecontainer.StreamingRuntime + // Container runtime service (needed by container runtime Start()). // TODO(CD): try to make this available without holding a reference in this // struct. For example, by adding a getter to generic runtime. diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index 46a17a01a58..99694d0b5fc 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -1592,139 +1592,78 @@ func (kl *Kubelet) RunInContainer(podFullName string, podUID types.UID, containe // ExecInContainer executes a command in a container, connecting the supplied // stdin/stdout/stderr to the command's IO streams. func (kl *Kubelet) ExecInContainer(podFullName string, podUID types.UID, containerName string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { - streamingRuntime, ok := kl.containerRuntime.(kubecontainer.DirectStreamingRuntime) - if !ok { - return fmt.Errorf("streaming methods not supported by runtime") - } - - container, err := kl.findContainer(podFullName, podUID, containerName) - if err != nil { - return err - } - if container == nil { - return fmt.Errorf("container not found (%q)", containerName) - } - return streamingRuntime.ExecInContainer(container.ID, cmd, stdin, stdout, stderr, tty, resize, timeout) + // TODO(random-liu): Remove this. + return fmt.Errorf("unimplemented") } // AttachContainer uses the container runtime to attach the given streams to // the given container. func (kl *Kubelet) AttachContainer(podFullName string, podUID types.UID, containerName string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error { - streamingRuntime, ok := kl.containerRuntime.(kubecontainer.DirectStreamingRuntime) - if !ok { - return fmt.Errorf("streaming methods not supported by runtime") - } - - container, err := kl.findContainer(podFullName, podUID, containerName) - if err != nil { - return err - } - if container == nil { - return fmt.Errorf("container not found (%q)", containerName) - } - return streamingRuntime.AttachContainer(container.ID, stdin, stdout, stderr, tty, resize) + // TODO(random-liu): Remove this. + return fmt.Errorf("unimplemented") } // PortForward connects to the pod's port and copies data between the port // and the stream. func (kl *Kubelet) PortForward(podFullName string, podUID types.UID, port int32, stream io.ReadWriteCloser) error { - streamingRuntime, ok := kl.containerRuntime.(kubecontainer.DirectStreamingRuntime) - if !ok { - return fmt.Errorf("streaming methods not supported by runtime") - } - - pods, err := kl.containerRuntime.GetPods(false) - if err != nil { - return err - } - // Resolve and type convert back again. - // We need the static pod UID but the kubecontainer API works with types.UID. - podUID = types.UID(kl.podManager.TranslatePodUID(podUID)) - pod := kubecontainer.Pods(pods).FindPod(podFullName, podUID) - if pod.IsEmpty() { - return fmt.Errorf("pod not found (%q)", podFullName) - } - return streamingRuntime.PortForward(&pod, port, stream) + // TODO(random-liu): Remove this. + return fmt.Errorf("unimplemented") } // GetExec gets the URL the exec will be served from, or nil if the Kubelet will serve it. func (kl *Kubelet) GetExec(podFullName string, podUID types.UID, containerName string, cmd []string, streamOpts remotecommandserver.Options) (*url.URL, error) { - switch streamingRuntime := kl.containerRuntime.(type) { - case kubecontainer.DirectStreamingRuntime: - // Kubelet will serve the exec directly. - return nil, nil - case kubecontainer.IndirectStreamingRuntime: - container, err := kl.findContainer(podFullName, podUID, containerName) - if err != nil { - return nil, err - } - if container == nil { - return nil, fmt.Errorf("container not found (%q)", containerName) - } - return streamingRuntime.GetExec(container.ID, cmd, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr, streamOpts.TTY) - default: - return nil, fmt.Errorf("container runtime does not support exec") + container, err := kl.findContainer(podFullName, podUID, containerName) + if err != nil { + return nil, err } + if container == nil { + return nil, fmt.Errorf("container not found (%q)", containerName) + } + return kl.streamingRuntime.GetExec(container.ID, cmd, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr, streamOpts.TTY) } // GetAttach gets the URL the attach will be served from, or nil if the Kubelet will serve it. func (kl *Kubelet) GetAttach(podFullName string, podUID types.UID, containerName string, streamOpts remotecommandserver.Options) (*url.URL, error) { - switch streamingRuntime := kl.containerRuntime.(type) { - case kubecontainer.DirectStreamingRuntime: - // Kubelet will serve the attach directly. - return nil, nil - case kubecontainer.IndirectStreamingRuntime: - container, err := kl.findContainer(podFullName, podUID, containerName) - if err != nil { - return nil, err - } - if container == nil { - return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName) - } - - // The TTY setting for attach must match the TTY setting in the initial container configuration, - // since whether the process is running in a TTY cannot be changed after it has started. We - // need the api.Pod to get the TTY status. - pod, found := kl.GetPodByFullName(podFullName) - if !found || (string(podUID) != "" && pod.UID != podUID) { - return nil, fmt.Errorf("pod %s not found", podFullName) - } - containerSpec := kubecontainer.GetContainerSpec(pod, containerName) - if containerSpec == nil { - return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName) - } - tty := containerSpec.TTY - - return streamingRuntime.GetAttach(container.ID, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr, tty) - default: - return nil, fmt.Errorf("container runtime does not support attach") + container, err := kl.findContainer(podFullName, podUID, containerName) + if err != nil { + return nil, err } + if container == nil { + return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName) + } + + // The TTY setting for attach must match the TTY setting in the initial container configuration, + // since whether the process is running in a TTY cannot be changed after it has started. We + // need the api.Pod to get the TTY status. + pod, found := kl.GetPodByFullName(podFullName) + if !found || (string(podUID) != "" && pod.UID != podUID) { + return nil, fmt.Errorf("pod %s not found", podFullName) + } + containerSpec := kubecontainer.GetContainerSpec(pod, containerName) + if containerSpec == nil { + return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName) + } + tty := containerSpec.TTY + + return kl.streamingRuntime.GetAttach(container.ID, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr, tty) } // GetPortForward gets the URL the port-forward will be served from, or nil if the Kubelet will serve it. func (kl *Kubelet) GetPortForward(podName, podNamespace string, podUID types.UID, portForwardOpts portforward.V4Options) (*url.URL, error) { - switch streamingRuntime := kl.containerRuntime.(type) { - case kubecontainer.DirectStreamingRuntime: - // Kubelet will serve the attach directly. - return nil, nil - case kubecontainer.IndirectStreamingRuntime: - pods, err := kl.containerRuntime.GetPods(false) - if err != nil { - return nil, err - } - // Resolve and type convert back again. - // We need the static pod UID but the kubecontainer API works with types.UID. - podUID = types.UID(kl.podManager.TranslatePodUID(podUID)) - podFullName := kubecontainer.BuildPodFullName(podName, podNamespace) - pod := kubecontainer.Pods(pods).FindPod(podFullName, podUID) - if pod.IsEmpty() { - return nil, fmt.Errorf("pod not found (%q)", podFullName) - } - - return streamingRuntime.GetPortForward(podName, podNamespace, podUID, portForwardOpts.Ports) - default: - return nil, fmt.Errorf("container runtime does not support port-forward") + pods, err := kl.containerRuntime.GetPods(false) + if err != nil { + return nil, err } + // Resolve and type convert back again. + // We need the static pod UID but the kubecontainer API works with types.UID. + podUID = types.UID(kl.podManager.TranslatePodUID(podUID)) + podFullName := kubecontainer.BuildPodFullName(podName, podNamespace) + pod := kubecontainer.Pods(pods).FindPod(podFullName, podUID) + if pod.IsEmpty() { + return nil, fmt.Errorf("pod not found (%q)", podFullName) + } + + return kl.streamingRuntime.GetPortForward(podName, podNamespace, podUID, portForwardOpts.Ports) } // cleanupOrphanedPodCgroups removes cgroups that should no longer exist. diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 9d2c813848b..146ef3056ef 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -2149,53 +2149,21 @@ func TestExec(t *testing.T) { }}, } - { // No streaming case - description := "no streaming - " + tc.description - redirect, err := kubelet.GetExec(tc.podFullName, podUID, tc.container, command, remotecommand.Options{}) - assert.Error(t, err, description) - assert.Nil(t, redirect, description) + description := "streaming - " + tc.description + fakeRuntime := &containertest.FakeStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} + kubelet.containerRuntime = fakeRuntime + kubelet.streamingRuntime = fakeRuntime - err = kubelet.ExecInContainer(tc.podFullName, podUID, tc.container, command, stdin, stdout, stderr, tty, nil, 0) + redirect, err := kubelet.GetExec(tc.podFullName, podUID, tc.container, command, remotecommand.Options{}) + if tc.expectError { assert.Error(t, err, description) - } - { // Direct streaming case - description := "direct streaming - " + tc.description - fakeRuntime := &containertest.FakeDirectStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} - kubelet.containerRuntime = fakeRuntime - - redirect, err := kubelet.GetExec(tc.podFullName, podUID, tc.container, command, remotecommand.Options{}) + } else { assert.NoError(t, err, description) - assert.Nil(t, redirect, description) - - err = kubelet.ExecInContainer(tc.podFullName, podUID, tc.container, command, stdin, stdout, stderr, tty, nil, 0) - if tc.expectError { - assert.Error(t, err, description) - } else { - assert.NoError(t, err, description) - assert.Equal(t, fakeRuntime.Args.ContainerID.ID, containerID, description+": ID") - assert.Equal(t, fakeRuntime.Args.Cmd, command, description+": Command") - assert.Equal(t, fakeRuntime.Args.Stdin, stdin, description+": Stdin") - assert.Equal(t, fakeRuntime.Args.Stdout, stdout, description+": Stdout") - assert.Equal(t, fakeRuntime.Args.Stderr, stderr, description+": Stderr") - assert.Equal(t, fakeRuntime.Args.TTY, tty, description+": TTY") - } + assert.Equal(t, containertest.FakeHost, redirect.Host, description+": redirect") } - { // Indirect streaming case - description := "indirect streaming - " + tc.description - fakeRuntime := &containertest.FakeIndirectStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} - kubelet.containerRuntime = fakeRuntime - redirect, err := kubelet.GetExec(tc.podFullName, podUID, tc.container, command, remotecommand.Options{}) - if tc.expectError { - assert.Error(t, err, description) - } else { - assert.NoError(t, err, description) - assert.Equal(t, containertest.FakeHost, redirect.Host, description+": redirect") - } - - err = kubelet.ExecInContainer(tc.podFullName, podUID, tc.container, command, stdin, stdout, stderr, tty, nil, 0) - assert.Error(t, err, description) - } + err = kubelet.ExecInContainer(tc.podFullName, podUID, tc.container, command, stdin, stdout, stderr, tty, nil, 0) + assert.Error(t, err, description) } } @@ -2241,50 +2209,21 @@ func TestPortForward(t *testing.T) { } podFullName := kubecontainer.GetPodFullName(podWithUIDNameNs(podUID, tc.podName, podNamespace)) - { // No streaming case - description := "no streaming - " + tc.description - redirect, err := kubelet.GetPortForward(tc.podName, podNamespace, podUID, portforward.V4Options{}) - assert.Error(t, err, description) - assert.Nil(t, redirect, description) + description := "streaming - " + tc.description + fakeRuntime := &containertest.FakeStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} + kubelet.containerRuntime = fakeRuntime + kubelet.streamingRuntime = fakeRuntime - err = kubelet.PortForward(podFullName, podUID, port, stream) + redirect, err := kubelet.GetPortForward(tc.podName, podNamespace, podUID, portforward.V4Options{}) + if tc.expectError { assert.Error(t, err, description) - } - { // Direct streaming case - description := "direct streaming - " + tc.description - fakeRuntime := &containertest.FakeDirectStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} - kubelet.containerRuntime = fakeRuntime - - redirect, err := kubelet.GetPortForward(tc.podName, podNamespace, podUID, portforward.V4Options{}) + } else { assert.NoError(t, err, description) - assert.Nil(t, redirect, description) - - err = kubelet.PortForward(podFullName, podUID, port, stream) - if tc.expectError { - assert.Error(t, err, description) - } else { - assert.NoError(t, err, description) - require.Equal(t, fakeRuntime.Args.Pod.ID, podUID, description+": Pod UID") - require.Equal(t, fakeRuntime.Args.Port, port, description+": Port") - require.Equal(t, fakeRuntime.Args.Stream, stream, description+": stream") - } + assert.Equal(t, containertest.FakeHost, redirect.Host, description+": redirect") } - { // Indirect streaming case - description := "indirect streaming - " + tc.description - fakeRuntime := &containertest.FakeIndirectStreamingRuntime{FakeRuntime: testKubelet.fakeRuntime} - kubelet.containerRuntime = fakeRuntime - redirect, err := kubelet.GetPortForward(tc.podName, podNamespace, podUID, portforward.V4Options{}) - if tc.expectError { - assert.Error(t, err, description) - } else { - assert.NoError(t, err, description) - assert.Equal(t, containertest.FakeHost, redirect.Host, description+": redirect") - } - - err = kubelet.PortForward(podFullName, podUID, port, stream) - assert.Error(t, err, description) - } + err = kubelet.PortForward(podFullName, podUID, port, stream) + assert.Error(t, err, description) } } diff --git a/pkg/kubelet/kuberuntime/kuberuntime_manager.go b/pkg/kubelet/kuberuntime/kuberuntime_manager.go index c34136b569a..df207fb4352 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_manager.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_manager.go @@ -120,7 +120,7 @@ type kubeGenericRuntimeManager struct { type KubeGenericRuntime interface { kubecontainer.Runtime - kubecontainer.IndirectStreamingRuntime + kubecontainer.StreamingRuntime kubecontainer.ContainerCommandRunner } From 82f9d9365e9e96dfa36b76a4323962ea5661818d Mon Sep 17 00:00:00 2001 From: Cheng Xing Date: Tue, 29 May 2018 14:31:46 -0700 Subject: [PATCH 122/416] Modified regional PD test to fetch template name from GCE --- test/e2e/framework/google_compute.go | 48 ++++++++++++++++++++++++++++ test/e2e/storage/regional_pd.go | 6 ++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/test/e2e/framework/google_compute.go b/test/e2e/framework/google_compute.go index cf937b72f5b..3c1ed0d5cdb 100644 --- a/test/e2e/framework/google_compute.go +++ b/test/e2e/framework/google_compute.go @@ -145,6 +145,28 @@ func CreateManagedInstanceGroup(size int64, zone, template string) error { return nil } +func GetManagedInstanceGroupTemplateName(zone string) (string, error) { + // TODO(verult): make this hit the compute API directly instead of + // shelling out to gcloud. Use InstanceGroupManager to get Instance Template name. + + stdout, _, err := retryCmd("gcloud", "compute", "instance-groups", "managed", + "list", + fmt.Sprintf("--filter=name:%s", TestContext.CloudConfig.NodeInstanceGroup), + fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID), + fmt.Sprintf("--zones=%s", zone), + ) + + if err != nil { + return "", fmt.Errorf("gcloud compute instance-groups managed list call failed with err: %v", err) + } + + templateName, err := parseInstanceTemplateName(stdout) + if err != nil { + return "", fmt.Errorf("error parsing gcloud output: %v", err) + } + return templateName, nil +} + func DeleteManagedInstanceGroup(zone string) error { // TODO(verult): make this hit the compute API directly instead of // shelling out to gcloud. @@ -158,3 +180,29 @@ func DeleteManagedInstanceGroup(zone string) error { } return nil } + +func parseInstanceTemplateName(gcloudOutput string) (string, error) { + const templateNameField = "INSTANCE_TEMPLATE" + + lines := strings.Split(gcloudOutput, "\n") + if len(lines) <= 1 { // Empty output or only contains column names + return "", fmt.Errorf("the list is empty") + } + + // Otherwise, there should be exactly 1 entry, i.e. 2 lines + fieldNames := strings.Fields(lines[0]) + instanceTemplateColumn := 0 + for instanceTemplateColumn < len(fieldNames) && + fieldNames[instanceTemplateColumn] != templateNameField { + instanceTemplateColumn++ + } + + if instanceTemplateColumn == len(fieldNames) { + return "", fmt.Errorf("the list does not contain instance template information") + } + + fields := strings.Fields(lines[1]) + instanceTemplateName := fields[instanceTemplateColumn] + + return instanceTemplateName, nil +} diff --git a/test/e2e/storage/regional_pd.go b/test/e2e/storage/regional_pd.go index c478a386a67..a4ce5ec5821 100644 --- a/test/e2e/storage/regional_pd.go +++ b/test/e2e/storage/regional_pd.go @@ -205,6 +205,9 @@ func testZonalFailover(c clientset.Interface, ns string) { instanceGroup, err := cloud.GetInstanceGroup(instanceGroupName, podZone) Expect(err).NotTo(HaveOccurred(), "Error getting instance group %s in zone %s", instanceGroupName, podZone) + templateName, err := framework.GetManagedInstanceGroupTemplateName(podZone) + Expect(err).NotTo(HaveOccurred(), + "Error getting instance group template in zone %s", podZone) err = framework.DeleteManagedInstanceGroup(podZone) Expect(err).NotTo(HaveOccurred(), "Error deleting instance group in zone %s", podZone) @@ -212,9 +215,6 @@ func testZonalFailover(c clientset.Interface, ns string) { defer func() { framework.Logf("recreating instance group %s", instanceGroup.Name) - // HACK improve this when Managed Instance Groups are available through the cloud provider API - templateName := strings.Replace(instanceGroupName, "group", "template", 1 /* n */) - framework.ExpectNoError(framework.CreateManagedInstanceGroup(instanceGroup.Size, podZone, templateName), "Error recreating instance group %s in zone %s", instanceGroup.Name, podZone) framework.ExpectNoError(framework.WaitForReadyNodes(c, nodeCount, framework.RestartNodeReadyAgainTimeout), From aeccffc3396b9207d0ebfbc99ad33fe3d1f095a5 Mon Sep 17 00:00:00 2001 From: ravisantoshgudimetla Date: Sat, 26 May 2018 19:41:18 -0400 Subject: [PATCH 123/416] Phase out rescheduler in favor of priority and preemption --- build/lib/release.sh | 1 - .../fluentd-es-configmap.yaml | 15 -- .../fluentd-gcp-configmap-old.yaml | 14 -- .../fluentd-gcp/fluentd-gcp-configmap.yaml | 14 -- cluster/gce/config-default.sh | 3 - cluster/gce/config-test.sh | 3 - cluster/gce/gci/configure-helper.sh | 11 -- cluster/gce/manifests/kube-proxy.manifest | 3 +- cluster/gce/manifests/rescheduler.manifest | 36 ----- cluster/gce/util.sh | 1 - cluster/log-dump/log-dump.sh | 2 +- pkg/kubelet/types/pod_update.go | 2 +- .../equivalence_cache_predicates.go | 4 + test/e2e/scheduling/rescheduler.go | 133 ------------------ 14 files changed, 7 insertions(+), 235 deletions(-) delete mode 100644 cluster/gce/manifests/rescheduler.manifest delete mode 100644 test/e2e/scheduling/rescheduler.go diff --git a/build/lib/release.sh b/build/lib/release.sh index aaec30f52e0..46e143e31d2 100644 --- a/build/lib/release.sh +++ b/build/lib/release.sh @@ -387,7 +387,6 @@ function kube::release::package_kube_manifests_tarball() { cp "${src_dir}/kube-controller-manager.manifest" "${dst_dir}" cp "${src_dir}/kube-addon-manager.yaml" "${dst_dir}" cp "${src_dir}/glbc.manifest" "${dst_dir}" - cp "${src_dir}/rescheduler.manifest" "${dst_dir}/" cp "${src_dir}/e2e-image-puller.manifest" "${dst_dir}/" cp "${src_dir}/etcd-empty-dir-cleanup.yaml" "${dst_dir}/" cp "${KUBE_ROOT}/cluster/gce/gci/configure-helper.sh" "${dst_dir}/gci-configure-helper.sh" diff --git a/cluster/addons/fluentd-elasticsearch/fluentd-es-configmap.yaml b/cluster/addons/fluentd-elasticsearch/fluentd-es-configmap.yaml index 5b861084da0..7dd846248d8 100644 --- a/cluster/addons/fluentd-elasticsearch/fluentd-es-configmap.yaml +++ b/cluster/addons/fluentd-elasticsearch/fluentd-es-configmap.yaml @@ -273,21 +273,6 @@ data: tag kube-scheduler - # Example: - # I1104 10:36:20.242766 5 rescheduler.go:73] Running Rescheduler - - @id rescheduler.log - @type tail - format multiline - multiline_flush_interval 5s - format_firstline /^\w\d{4}/ - format1 /^(?\w)(?