mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
commit
52c07683ef
@ -1,26 +0,0 @@
|
||||
### Version 1.9 (Fri November 18 2016 Bowei Du <bowei@google.com>)
|
||||
- Add limited ConfigMap support (pr #36775)
|
||||
|
||||
### Version 1.8 (Thu September 29 2016 Zihong Zheng <zihongz@google.com>)
|
||||
- Add support for graceful termination (issue #31807)
|
||||
|
||||
### Version 1.7 (Wed August 24 2016 Zihong Zheng <zihongz@google.com>)
|
||||
- Add support for ExternalName services (pr #31159)
|
||||
|
||||
### Version 1.6 (Wed June 29 2016 Girish Kalele <gkalele@google.com>)
|
||||
- Godeps update for vendor code (skydns/mux)
|
||||
|
||||
### Version 1.5 (Thu June 23 2016 Nikhil Jindal <nikhiljindal@google.com>)
|
||||
- Adding support to return local service (pr #27708)
|
||||
|
||||
### Version 1.4 (Tue June 21 2016 Nikhil Jindal <nikhiljindal@google.com>)
|
||||
- Initialising nodesStore (issue #27820)
|
||||
|
||||
### Version 1.3 (Fri June 3 2016 Prashanth.B <beeps@google.com>)
|
||||
- Fixed SRV record lookup (issue #26116)
|
||||
|
||||
### Version 1.2 (Fri May 27 2016 Tim Hockin <thockin@google.com>)
|
||||
- First Changelog entry
|
||||
|
||||
|
||||
[]()
|
@ -1,17 +0,0 @@
|
||||
# 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.
|
||||
|
||||
FROM BASEIMAGE
|
||||
ADD kube-dns /
|
||||
ENTRYPOINT ["/kube-dns"]
|
@ -1,5 +0,0 @@
|
||||
# Maintainers
|
||||
|
||||
Tim Hockin <thockin@google.com>
|
||||
|
||||
[]()
|
@ -1,68 +0,0 @@
|
||||
# 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 Docker image gcr.io/google_containers/kubedns-<ARCH>
|
||||
# MAINTAINER: Tim Hockin <thockin@google.com>
|
||||
# If you update this image please bump the tag value before pushing.
|
||||
#
|
||||
# Usage:
|
||||
# [ARCH=amd64] [TAG=1.6] [REGISTRY=gcr.io/google_containers] [BASEIMAGE=busybox] make (container|push)
|
||||
|
||||
# Default registry, arch and tag. This can be overwritten by arguments to make
|
||||
PLATFORM?=linux
|
||||
ARCH?=amd64
|
||||
TAG?=1.9
|
||||
REGISTRY?=gcr.io/google_containers
|
||||
|
||||
GOLANG_VERSION=1.6
|
||||
KUBE_ROOT=$(shell pwd)/../..
|
||||
TEMP_DIR:=$(shell mktemp -d)
|
||||
|
||||
ifeq ($(ARCH),amd64)
|
||||
BASEIMAGE?=busybox
|
||||
endif
|
||||
ifeq ($(ARCH),arm)
|
||||
BASEIMAGE?=armel/busybox
|
||||
endif
|
||||
ifeq ($(ARCH),arm64)
|
||||
BASEIMAGE?=aarch64/busybox
|
||||
endif
|
||||
ifeq ($(ARCH),ppc64le)
|
||||
BASEIMAGE?=ppc64le/busybox
|
||||
endif
|
||||
ifeq ($(ARCH),s390x)
|
||||
BASEIMAGE?=s390x/busybox
|
||||
endif
|
||||
|
||||
|
||||
all: container
|
||||
|
||||
container:
|
||||
# Copy the content in this dir to the temp dir
|
||||
cp $(KUBE_ROOT)/_output/dockerized/bin/$(PLATFORM)/$(ARCH)/kube-dns $(TEMP_DIR)
|
||||
cp $(KUBE_ROOT)/build/kube-dns/Dockerfile $(TEMP_DIR)
|
||||
|
||||
# Replace BASEIMAGE with the real base image
|
||||
cd $(TEMP_DIR) && sed -i "s|BASEIMAGE|$(BASEIMAGE)|g" Dockerfile
|
||||
|
||||
# And build the image
|
||||
docker build -t $(REGISTRY)/kubedns-$(ARCH):$(TAG) $(TEMP_DIR)
|
||||
|
||||
# delete temp dir
|
||||
rm -rf $(TEMP_DIR)
|
||||
|
||||
push: container
|
||||
gcloud docker -- push $(REGISTRY)/kubedns-$(ARCH):$(TAG)
|
||||
|
||||
.PHONY: all container push
|
@ -1,11 +0,0 @@
|
||||
approvers:
|
||||
- thockin
|
||||
- boweidu
|
||||
- mrhohn
|
||||
reviewers:
|
||||
- mikedanese
|
||||
- nikhiljindal
|
||||
- bprashanth
|
||||
- luxas
|
||||
- jessfraz
|
||||
- david-mcmahon
|
@ -1,22 +0,0 @@
|
||||
# DNS in Kubernetes
|
||||
|
||||
Kubernetes offers a DNS cluster addon, which most of the supported environments
|
||||
enable by default. The source code is in [cmd/kube-dns][kube-dns].
|
||||
|
||||
The [Kubernetes DNS Admin Guide][dns-admin] provides further details on this plugin.
|
||||
|
||||
[kube-dns]: https://github.com/kubernetes/kubernetes/tree/master/cmd/kube-dns
|
||||
[dns-admin]: http://kubernetes.io/docs/admin/dns/
|
||||
|
||||
## Making Changes
|
||||
|
||||
The container containing the kube-dns binary needs to be built for every
|
||||
architecture and pushed to the registry manually whenever the kube-dns binary
|
||||
has code changes. Every significant change to the functionality should result
|
||||
in a bump of the TAG in the Makefile.
|
||||
|
||||
Any significant changes to the YAML template for `kube-dns` should result a bump
|
||||
of the version number for the `kube-dns` replication controller and well as the
|
||||
`version` label. This will permit a rolling update of `kube-dns`.
|
||||
|
||||
[]()
|
@ -1,51 +0,0 @@
|
||||
# Cutting a release
|
||||
|
||||
Until we have a proper setup for building this automatically with every binary
|
||||
release, here are the steps for making a release. We make releases when they
|
||||
are ready, not on every PR.
|
||||
|
||||
1. Build the container for testing:
|
||||
|
||||
```
|
||||
make release
|
||||
cd build/kube-dns
|
||||
make container PREFIX=<your-docker-hub> TAG=rc
|
||||
```
|
||||
|
||||
2. Manually deploy this to your own cluster by updating the replication
|
||||
controller and deleting the running pod(s).
|
||||
|
||||
3. Verify it works.
|
||||
|
||||
4. Update the TAG version in `Makefile` and update the `Changelog`. Update the
|
||||
`*.yaml.in` to point to the new tag. Send a PR but mark it as "DO NOT MERGE".
|
||||
|
||||
5. Once the PR is approved, build and push the container for real **for all architectures**:
|
||||
|
||||
```console
|
||||
# Build for linux/amd64 (default)
|
||||
$ make push ARCH=amd64
|
||||
# ---> gcr.io/google_containers/kube-dns-amd64:TAG
|
||||
|
||||
$ make push ARCH=arm
|
||||
# ---> gcr.io/google_containers/kube-dns-arm:TAG
|
||||
|
||||
$ make push ARCH=arm64
|
||||
# ---> gcr.io/google_containers/kube-dns-arm64:TAG
|
||||
|
||||
$ make push ARCH=ppc64le
|
||||
# ---> gcr.io/google_containers/kube-dns-ppc64le:TAG
|
||||
|
||||
$ make push ARCH=s390x
|
||||
# ---> gcr.io/google_containers/kube-dns-s390x:TAG
|
||||
```
|
||||
|
||||
6. Manually deploy this to your own cluster by updating the replication
|
||||
controller and deleting the running pod(s).
|
||||
|
||||
7. Verify it works.
|
||||
|
||||
8. Allow the PR to be merged.
|
||||
|
||||
|
||||
[]()
|
@ -1,10 +1,13 @@
|
||||
# kube-dns
|
||||
kube-dns schedules DNS Pods and Service on the cluster, other pods in cluster can
|
||||
use the DNS Service’s IP to resolve DNS names.
|
||||
|
||||
More details on http://kubernetes.io/docs/admin/dns/.
|
||||
`kube-dns` schedules DNS Pods and Service on the cluster, other pods in cluster
|
||||
can use the DNS Service’s IP to resolve DNS names.
|
||||
|
||||
* [Administrators guide](http://kubernetes.io/docs/admin/dns/)
|
||||
* [Code repository](http://www.github.com/kubernetes/dns)
|
||||
|
||||
## Manually scale kube-dns Deployment
|
||||
|
||||
kube-dns creates only one DNS Pod by default. If
|
||||
[dns-horizontal-autoscaler](../dns-horizontal-autoscaler/)
|
||||
is not enabled, you may need to manually scale kube-dns Deployment.
|
||||
@ -14,27 +17,28 @@ Please use below `kubectl scale` command to scale:
|
||||
kubectl --namespace=kube-system scale deployment kube-dns --replicas=<NUM_YOU_WANT>
|
||||
```
|
||||
|
||||
Do not use `kubectl edit` to modify kube-dns Deployment object if it is controlled by
|
||||
[Addon Manager](../addon-manager/). Otherwise the modifications will be clobbered,
|
||||
in addition the replicas count for kube-dns Deployment will be reset to 1. See
|
||||
[Cluster add-ons README](../README.md) and [#36411](https://github.com/kubernetes/kubernetes/issues/36411)
|
||||
for reference.
|
||||
Do not use `kubectl edit` to modify kube-dns Deployment object if it is
|
||||
controlled by [Addon Manager](../addon-manager/). Otherwise the modifications
|
||||
will be clobbered, in addition the replicas count for kube-dns Deployment will
|
||||
be reset to 1. See [Cluster add-ons README](../README.md) and
|
||||
[#36411](https://github.com/kubernetes/kubernetes/issues/36411) for reference.
|
||||
|
||||
## kube-dns Deployment and Service templates
|
||||
|
||||
This directory contains the base UNDERSCORE templates that can be used
|
||||
to generate the kubedns-controller.yaml.in and kubedns.controller.yaml.in needed in Salt format.
|
||||
This directory contains the base UNDERSCORE templates that can be used to
|
||||
generate the kubedns-controller.yaml.in and kubedns.controller.yaml.in needed in
|
||||
Salt format.
|
||||
|
||||
Due to a varied preference in templating language choices, the transform
|
||||
Makefile in this directory should be enhanced to generate all required
|
||||
formats from the base underscore templates.
|
||||
Makefile in this directory should be enhanced to generate all required formats
|
||||
from the base underscore templates.
|
||||
|
||||
**N.B.**: When you add a parameter you should also update the various scripts
|
||||
that supply values for your new parameter. Here is one way you might find those
|
||||
scripts:
|
||||
|
||||
**NOTE WELL**: Developers, when you add a parameter you should also
|
||||
update the various scripts that supply values for your new parameter.
|
||||
Here is one way you might find those scripts:
|
||||
```
|
||||
cd kubernetes
|
||||
find [a-zA-Z0-9]* -type f -exec grep kubedns-controller.yaml \{\} \; -print -exec echo \;
|
||||
cd kubernetes && git grep 'kubedns-controller.yaml'
|
||||
```
|
||||
|
||||
### Base Template files
|
||||
@ -42,17 +46,23 @@ find [a-zA-Z0-9]* -type f -exec grep kubedns-controller.yaml \{\} \; -print -exe
|
||||
These are the authoritative base templates.
|
||||
Run 'make' to generate the Salt and Sed yaml templates from these.
|
||||
|
||||
```
|
||||
kubedns-controller.yaml.base
|
||||
kubedns-svc.yaml.base
|
||||
```
|
||||
|
||||
### Generated Salt files
|
||||
|
||||
```
|
||||
kubedns-controller.yaml.in
|
||||
kubedns-svc.yaml.in
|
||||
```
|
||||
|
||||
### Generated Sed files
|
||||
|
||||
```
|
||||
kubedns-controller.yaml.sed
|
||||
kubedns-svc.yaml.sed
|
||||
```
|
||||
|
||||
[]()
|
||||
|
@ -47,7 +47,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kubedns
|
||||
image: gcr.io/google_containers/kubedns-amd64:1.9
|
||||
image: gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.10.1
|
||||
resources:
|
||||
# TODO: Set memory limits when we've profiled the container for large
|
||||
# clusters, then set request = limit to keep this container in
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
- name: dnsmasq
|
||||
image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
|
||||
image: gcr.io/google_containers/k8s-dns-dnsmasq-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthcheck/dnsmasq
|
||||
@ -124,7 +124,7 @@ spec:
|
||||
cpu: 150m
|
||||
memory: 10Mi
|
||||
- name: sidecar
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.0
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
|
@ -47,7 +47,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kubedns
|
||||
image: gcr.io/google_containers/kubedns-amd64:1.9
|
||||
image: gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.10.1
|
||||
resources:
|
||||
# TODO: Set memory limits when we've profiled the container for large
|
||||
# clusters, then set request = limit to keep this container in
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
- name: dnsmasq
|
||||
image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
|
||||
image: gcr.io/google_containers/k8s-dns-dnsmasq-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthcheck/dnsmasq
|
||||
@ -124,7 +124,7 @@ spec:
|
||||
cpu: 150m
|
||||
memory: 10Mi
|
||||
- name: sidecar
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.0
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
|
@ -47,7 +47,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kubedns
|
||||
image: gcr.io/google_containers/kubedns-amd64:1.9
|
||||
image: gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.10.1
|
||||
resources:
|
||||
# TODO: Set memory limits when we've profiled the container for large
|
||||
# clusters, then set request = limit to keep this container in
|
||||
@ -95,7 +95,7 @@ spec:
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
- name: dnsmasq
|
||||
image: gcr.io/google_containers/kube-dnsmasq-amd64:1.4
|
||||
image: gcr.io/google_containers/k8s-dns-dnsmasq-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthcheck/dnsmasq
|
||||
@ -123,7 +123,7 @@ spec:
|
||||
cpu: 150m
|
||||
memory: 10Mi
|
||||
- name: sidecar
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.0
|
||||
image: gcr.io/google_containers/k8s-dns-sidecar-amd64:1.10.1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /metrics
|
||||
|
@ -26,7 +26,6 @@ filegroup(
|
||||
"//cmd/kube-apiserver:all-srcs",
|
||||
"//cmd/kube-controller-manager:all-srcs",
|
||||
"//cmd/kube-discovery:all-srcs",
|
||||
"//cmd/kube-dns:all-srcs",
|
||||
"//cmd/kube-proxy:all-srcs",
|
||||
"//cmd/kubeadm:all-srcs",
|
||||
"//cmd/kubectl:all-srcs",
|
||||
|
@ -1,49 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "kube-dns",
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["dns.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kube-dns/app:go_default_library",
|
||||
"//cmd/kube-dns/app/options:go_default_library",
|
||||
"//pkg/client/metrics/prometheus:go_default_library",
|
||||
"//pkg/util/flag:go_default_library",
|
||||
"//pkg/util/logs:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//pkg/version/prometheus:go_default_library",
|
||||
"//pkg/version/verflag:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//cmd/kube-dns/app:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,42 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["server.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kube-dns/app/options:go_default_library",
|
||||
"//pkg/dns:go_default_library",
|
||||
"//pkg/dns/config:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/skynetservices/skydns/metrics",
|
||||
"//vendor:github.com/skynetservices/skydns/server",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
"//vendor:k8s.io/client-go/kubernetes",
|
||||
"//vendor:k8s.io/client-go/rest",
|
||||
"//vendor:k8s.io/client-go/tools/clientcmd",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//cmd/kube-dns/app/options:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,33 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["options.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/dns/federation:go_default_library",
|
||||
"//pkg/util/validation:go_default_library",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package options contains flags for initializing a proxy.
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "net/http/pprof"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
fed "k8s.io/kubernetes/pkg/dns/federation"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
type KubeDNSConfig struct {
|
||||
ClusterDomain string
|
||||
KubeConfigFile string
|
||||
KubeMasterURL string
|
||||
InitialSyncTimeout time.Duration
|
||||
|
||||
HealthzPort int
|
||||
DNSBindAddress string
|
||||
DNSPort int
|
||||
|
||||
Federations map[string]string
|
||||
|
||||
ConfigMapNs string
|
||||
ConfigMap string
|
||||
}
|
||||
|
||||
func NewKubeDNSConfig() *KubeDNSConfig {
|
||||
return &KubeDNSConfig{
|
||||
ClusterDomain: "cluster.local.",
|
||||
HealthzPort: 8081,
|
||||
DNSBindAddress: "0.0.0.0",
|
||||
DNSPort: 53,
|
||||
InitialSyncTimeout: 60 * time.Second,
|
||||
|
||||
Federations: make(map[string]string),
|
||||
|
||||
ConfigMapNs: api.NamespaceSystem,
|
||||
ConfigMap: "", // default to using command line flags
|
||||
}
|
||||
}
|
||||
|
||||
type clusterDomainVar struct {
|
||||
val *string
|
||||
}
|
||||
|
||||
func (m clusterDomainVar) Set(v string) error {
|
||||
v = strings.TrimSuffix(v, ".")
|
||||
segments := strings.Split(v, ".")
|
||||
for _, segment := range segments {
|
||||
if errs := validation.IsDNS1123Label(segment); len(errs) > 0 {
|
||||
return fmt.Errorf("Not a valid DNS label. %v", errs)
|
||||
}
|
||||
}
|
||||
if !strings.HasSuffix(v, ".") {
|
||||
v = fmt.Sprintf("%s.", v)
|
||||
}
|
||||
*m.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m clusterDomainVar) String() string {
|
||||
return *m.val
|
||||
}
|
||||
|
||||
func (m clusterDomainVar) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
type kubeMasterURLVar struct {
|
||||
val *string
|
||||
}
|
||||
|
||||
func (m kubeMasterURLVar) Set(v string) error {
|
||||
parsedURL, err := url.Parse(os.ExpandEnv(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse kube-master-url")
|
||||
}
|
||||
if parsedURL.Scheme == "" || parsedURL.Host == "" || parsedURL.Host == ":" {
|
||||
return fmt.Errorf("invalid kube-master-url specified")
|
||||
}
|
||||
*m.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m kubeMasterURLVar) String() string {
|
||||
return *m.val
|
||||
}
|
||||
|
||||
func (m kubeMasterURLVar) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
type federationsVar struct {
|
||||
nameDomainMap map[string]string
|
||||
}
|
||||
|
||||
func (fv federationsVar) Set(keyVal string) error {
|
||||
return fed.ParseFederationsFlag(keyVal, fv.nameDomainMap)
|
||||
}
|
||||
|
||||
func (fv federationsVar) String() string {
|
||||
var splits []string
|
||||
for name, domain := range fv.nameDomainMap {
|
||||
splits = append(splits, fmt.Sprintf("%s=%s", name, domain))
|
||||
}
|
||||
return strings.Join(splits, ",")
|
||||
}
|
||||
|
||||
func (fv federationsVar) Type() string {
|
||||
return "[]string"
|
||||
}
|
||||
|
||||
func (s *KubeDNSConfig) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.Var(clusterDomainVar{&s.ClusterDomain}, "domain",
|
||||
"domain under which to create names")
|
||||
|
||||
fs.StringVar(&s.KubeConfigFile, "kubecfg-file", s.KubeConfigFile,
|
||||
"Location of kubecfg file for access to kubernetes master service;"+
|
||||
" --kube-master-url overrides the URL part of this; if neither this nor"+
|
||||
" --kube-master-url are provided, defaults to service account tokens")
|
||||
fs.Var(kubeMasterURLVar{&s.KubeMasterURL}, "kube-master-url",
|
||||
"URL to reach kubernetes master. Env variables in this flag will be expanded.")
|
||||
|
||||
fs.IntVar(&s.HealthzPort, "healthz-port", s.HealthzPort,
|
||||
"port on which to serve a kube-dns HTTP readiness probe.")
|
||||
fs.StringVar(&s.DNSBindAddress, "dns-bind-address", s.DNSBindAddress,
|
||||
"address on which to serve DNS requests.")
|
||||
fs.IntVar(&s.DNSPort, "dns-port", s.DNSPort, "port on which to serve DNS requests.")
|
||||
|
||||
fs.Var(federationsVar{s.Federations}, "federations",
|
||||
"a comma separated list of the federation names and their corresponding"+
|
||||
" domain names to which this cluster belongs. Example:"+
|
||||
" \"myfederation1=example.com,myfederation2=example2.com,myfederation3=example.com\"."+
|
||||
" It is an error to set both the federations and config-map flags.")
|
||||
fs.MarkDeprecated("federations", "use config-map instead. Will be removed in future version")
|
||||
|
||||
fs.StringVar(&s.ConfigMapNs, "config-map-namespace", s.ConfigMapNs,
|
||||
"namespace for the config-map")
|
||||
fs.StringVar(&s.ConfigMap, "config-map", s.ConfigMap,
|
||||
"config-map name. If empty, then the config-map will not used. Cannot be "+
|
||||
" used in conjunction with federations flag. config-map contains "+
|
||||
"dynamically adjustable configuration.")
|
||||
fs.DurationVar(&s.InitialSyncTimeout, "initial-sync-timeout", s.InitialSyncTimeout,
|
||||
"Timeout for initial resource sync.")
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/skynetservices/skydns/metrics"
|
||||
"github.com/skynetservices/skydns/server"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kube-dns/app/options"
|
||||
"k8s.io/kubernetes/pkg/dns"
|
||||
dnsconfig "k8s.io/kubernetes/pkg/dns/config"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type KubeDNSServer struct {
|
||||
// DNS domain name.
|
||||
domain string
|
||||
healthzPort int
|
||||
dnsBindAddress string
|
||||
dnsPort int
|
||||
kd *dns.KubeDNS
|
||||
}
|
||||
|
||||
func NewKubeDNSServerDefault(config *options.KubeDNSConfig) *KubeDNSServer {
|
||||
kubeClient, err := newKubeClient(config)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to create a kubernetes client: %v", err)
|
||||
}
|
||||
|
||||
var configSync dnsconfig.Sync
|
||||
if config.ConfigMap == "" {
|
||||
glog.V(0).Infof("ConfigMap not configured, using values from command line flags")
|
||||
configSync = dnsconfig.NewNopSync(
|
||||
&dnsconfig.Config{Federations: config.Federations})
|
||||
} else {
|
||||
glog.V(0).Infof("Using configuration read from ConfigMap: %v:%v",
|
||||
config.ConfigMapNs, config.ConfigMap)
|
||||
configSync = dnsconfig.NewSync(
|
||||
kubeClient, config.ConfigMapNs, config.ConfigMap)
|
||||
}
|
||||
|
||||
return &KubeDNSServer{
|
||||
domain: config.ClusterDomain,
|
||||
healthzPort: config.HealthzPort,
|
||||
dnsBindAddress: config.DNSBindAddress,
|
||||
dnsPort: config.DNSPort,
|
||||
kd: dns.NewKubeDNS(kubeClient, config.ClusterDomain, config.InitialSyncTimeout, configSync),
|
||||
}
|
||||
}
|
||||
|
||||
func newKubeClient(dnsConfig *options.KubeDNSConfig) (kubernetes.Interface, error) {
|
||||
var config *rest.Config
|
||||
var err error
|
||||
|
||||
if dnsConfig.KubeConfigFile == "" {
|
||||
config, err = rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
config, err = clientcmd.BuildConfigFromFlags(
|
||||
dnsConfig.KubeMasterURL, dnsConfig.KubeConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return kubernetes.NewForConfig(config)
|
||||
}
|
||||
|
||||
func (server *KubeDNSServer) Run() {
|
||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
||||
glog.V(0).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
|
||||
})
|
||||
setupSignalHandlers()
|
||||
server.startSkyDNSServer()
|
||||
server.kd.Start()
|
||||
server.setupHandlers()
|
||||
|
||||
glog.V(0).Infof("Status HTTP port %v", server.healthzPort)
|
||||
glog.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", server.healthzPort), nil))
|
||||
}
|
||||
|
||||
// setupHealthzHandlers sets up a readiness and liveness endpoint for kube2sky.
|
||||
func (server *KubeDNSServer) setupHandlers() {
|
||||
glog.V(0).Infof("Setting up Healthz Handler (/readiness)")
|
||||
http.HandleFunc("/readiness", func(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, "ok\n")
|
||||
})
|
||||
|
||||
glog.V(0).Infof("Setting up cache handler (/cache)")
|
||||
http.HandleFunc("/cache", func(w http.ResponseWriter, req *http.Request) {
|
||||
serializedJSON, err := server.kd.GetCacheAsJSON()
|
||||
if err == nil {
|
||||
fmt.Fprint(w, serializedJSON)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// setupSignalHandlers installs signal handler to ignore SIGINT and
|
||||
// SIGTERM. This daemon will be killed by SIGKILL after the grace
|
||||
// period to allow for some manner of graceful shutdown.
|
||||
func setupSignalHandlers() {
|
||||
sigChan := make(chan os.Signal)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
glog.V(0).Infof("Ignoring signal %v (can only be terminated by SIGKILL)", <-sigChan)
|
||||
}()
|
||||
}
|
||||
|
||||
func (d *KubeDNSServer) startSkyDNSServer() {
|
||||
glog.V(0).Infof("Starting SkyDNS server (%v:%v)", d.dnsBindAddress, d.dnsPort)
|
||||
skydnsConfig := &server.Config{
|
||||
Domain: d.domain,
|
||||
DnsAddr: fmt.Sprintf("%s:%d", d.dnsBindAddress, d.dnsPort),
|
||||
}
|
||||
server.SetDefaults(skydnsConfig)
|
||||
s := server.New(d.kd, skydnsConfig)
|
||||
if err := metrics.Metrics(); err != nil {
|
||||
glog.Fatalf("Skydns metrics error: %s", err)
|
||||
} else if metrics.Port != "" {
|
||||
glog.V(0).Infof("Skydns metrics enabled (%v:%v)", metrics.Path, metrics.Port)
|
||||
} else {
|
||||
glog.V(0).Infof("Skydns metrics not enabled")
|
||||
}
|
||||
|
||||
go s.Run()
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kube-dns/app"
|
||||
"k8s.io/kubernetes/cmd/kube-dns/app/options"
|
||||
"k8s.io/kubernetes/pkg/util/flag"
|
||||
"k8s.io/kubernetes/pkg/util/logs"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/pkg/version/verflag"
|
||||
|
||||
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
|
||||
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := options.NewKubeDNSConfig()
|
||||
config.AddFlags(pflag.CommandLine)
|
||||
|
||||
flag.InitFlags()
|
||||
logs.InitLogs()
|
||||
defer logs.FlushLogs()
|
||||
|
||||
verflag.PrintAndExitIfRequested()
|
||||
|
||||
glog.V(0).Infof("version: %+v", version.Get())
|
||||
|
||||
server := app.NewKubeDNSServerDefault(config)
|
||||
server.Run()
|
||||
}
|
@ -15,7 +15,6 @@ cmd/kube-apiserver/app/options
|
||||
cmd/kube-controller-manager
|
||||
cmd/kube-controller-manager/app/options
|
||||
cmd/kube-discovery
|
||||
cmd/kube-dns
|
||||
cmd/kube-proxy
|
||||
cmd/kubeadm
|
||||
cmd/kubeadm
|
||||
|
@ -23,7 +23,6 @@ readonly KUBE_GOPATH="${KUBE_OUTPUT}/go"
|
||||
# kube::build::source_targets in build/common.sh as well.
|
||||
kube::golang::server_targets() {
|
||||
local targets=(
|
||||
cmd/kube-dns
|
||||
cmd/kube-proxy
|
||||
cmd/kube-apiserver
|
||||
cmd/kube-controller-manager
|
||||
@ -189,7 +188,6 @@ readonly KUBE_ALL_BINARIES=("${KUBE_ALL_TARGETS[@]##*/}")
|
||||
readonly KUBE_STATIC_LIBRARIES=(
|
||||
kube-apiserver
|
||||
kube-controller-manager
|
||||
kube-dns
|
||||
kube-scheduler
|
||||
kube-proxy
|
||||
kube-discovery
|
||||
|
@ -85,7 +85,6 @@ filegroup(
|
||||
"//pkg/controller:all-srcs",
|
||||
"//pkg/conversion:all-srcs",
|
||||
"//pkg/credentialprovider:all-srcs",
|
||||
"//pkg/dns:all-srcs",
|
||||
"//pkg/fieldpath:all-srcs",
|
||||
"//pkg/fields:all-srcs",
|
||||
"//pkg/generated:all-srcs",
|
||||
|
@ -1,77 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dns.go",
|
||||
"doc.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/dns/config:go_default_library",
|
||||
"//pkg/dns/treecache:go_default_library",
|
||||
"//pkg/dns/util:go_default_library",
|
||||
"//pkg/util/validation:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//vendor:github.com/coreos/etcd/client",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/miekg/dns",
|
||||
"//vendor:github.com/skynetservices/skydns/msg",
|
||||
"//vendor:k8s.io/client-go/kubernetes",
|
||||
"//vendor:k8s.io/client-go/pkg/api/v1",
|
||||
"//vendor:k8s.io/client-go/pkg/apis/meta/v1",
|
||||
"//vendor:k8s.io/client-go/pkg/runtime",
|
||||
"//vendor:k8s.io/client-go/pkg/watch",
|
||||
"//vendor:k8s.io/client-go/tools/cache",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["dns_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/dns/config:go_default_library",
|
||||
"//pkg/dns/treecache:go_default_library",
|
||||
"//pkg/dns/util:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//vendor:github.com/coreos/etcd/client",
|
||||
"//vendor:github.com/miekg/dns",
|
||||
"//vendor:github.com/skynetservices/skydns/msg",
|
||||
"//vendor:github.com/skynetservices/skydns/server",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
"//vendor:github.com/stretchr/testify/require",
|
||||
"//vendor:k8s.io/client-go/kubernetes/fake",
|
||||
"//vendor:k8s.io/client-go/pkg/api/v1",
|
||||
"//vendor:k8s.io/client-go/pkg/apis/meta/v1",
|
||||
"//vendor:k8s.io/client-go/tools/cache",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/dns/config:all-srcs",
|
||||
"//pkg/dns/federation:all-srcs",
|
||||
"//pkg/dns/treecache:all-srcs",
|
||||
"//pkg/dns/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,53 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"mocksync.go",
|
||||
"nopsync.go",
|
||||
"sync.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/dns/federation:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/client-go/kubernetes",
|
||||
"//vendor:k8s.io/client-go/pkg/api/v1",
|
||||
"//vendor:k8s.io/client-go/pkg/apis/meta/v1",
|
||||
"//vendor:k8s.io/client-go/pkg/fields",
|
||||
"//vendor:k8s.io/client-go/pkg/runtime",
|
||||
"//vendor:k8s.io/client-go/pkg/util/wait",
|
||||
"//vendor:k8s.io/client-go/pkg/watch",
|
||||
"//vendor:k8s.io/client-go/tools/cache",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["config_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/stretchr/testify/assert"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
types "k8s.io/client-go/pkg/apis/meta/v1"
|
||||
fed "k8s.io/kubernetes/pkg/dns/federation"
|
||||
)
|
||||
|
||||
// Config populated either from the configuration source (command
|
||||
// line flags or via the config map mechanism).
|
||||
type Config struct {
|
||||
// The inclusion of TypeMeta is to ensure future compatibility if the
|
||||
// Config object was populated directly via a Kubernetes API mechanism.
|
||||
//
|
||||
// For example, instead of the custom implementation here, the
|
||||
// configuration could be obtained from an API that unifies
|
||||
// command-line flags, config-map, etc mechanisms.
|
||||
types.TypeMeta
|
||||
|
||||
// Map of federation names that the cluster in which this kube-dns
|
||||
// is running belongs to, to the corresponding domain names.
|
||||
Federations map[string]string `json:"federations"`
|
||||
}
|
||||
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Federations: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid returns whether or not the configuration is valid.
|
||||
func (config *Config) Validate() error {
|
||||
if err := config.validateFederations(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *Config) validateFederations() error {
|
||||
for name, domain := range config.Federations {
|
||||
if err := fed.ValidateName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := fed.ValidateDomain(domain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
config *Config
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
config: &Config{Federations: map[string]string{}},
|
||||
},
|
||||
{
|
||||
config: &Config{
|
||||
Federations: map[string]string{
|
||||
"abc": "d.e.f",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: &Config{
|
||||
Federations: map[string]string{
|
||||
"a.b": "cdef",
|
||||
},
|
||||
},
|
||||
hasError: true,
|
||||
},
|
||||
} {
|
||||
err := testCase.config.Validate()
|
||||
if !testCase.hasError {
|
||||
assert.Nil(t, err, "should be valid", testCase)
|
||||
} else {
|
||||
assert.NotNil(t, err, "should not be valid", testCase)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
// MockSync is a testing mock.
|
||||
type MockSync struct {
|
||||
// Config that will be returned from Once().
|
||||
Config *Config
|
||||
// Error that will be returned from Once().
|
||||
Error error
|
||||
|
||||
// Chan to send new configurations on.
|
||||
Chan chan *Config
|
||||
}
|
||||
|
||||
var _ Sync = (*MockSync)(nil)
|
||||
|
||||
func NewMockSync(config *Config, error error) *MockSync {
|
||||
return &MockSync{
|
||||
Config: config,
|
||||
Error: error,
|
||||
Chan: make(chan *Config),
|
||||
}
|
||||
}
|
||||
|
||||
func (sync *MockSync) Once() (*Config, error) {
|
||||
return sync.Config, sync.Error
|
||||
}
|
||||
|
||||
func (sync *MockSync) Periodic() <-chan *Config {
|
||||
return sync.Chan
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
// nopSync does no synchronization, used when the DNS server is
|
||||
// started without a ConfigMap configured.
|
||||
type nopSync struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
var _ Sync = (*nopSync)(nil)
|
||||
|
||||
func NewNopSync(config *Config) Sync {
|
||||
return &nopSync{config: config}
|
||||
}
|
||||
|
||||
func (sync *nopSync) Once() (*Config, error) {
|
||||
return sync.config, nil
|
||||
}
|
||||
|
||||
func (sync *nopSync) Periodic() <-chan *Config {
|
||||
return make(chan *Config)
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
metav1 "k8s.io/client-go/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/pkg/fields"
|
||||
"k8s.io/client-go/pkg/runtime"
|
||||
"k8s.io/client-go/pkg/util/wait"
|
||||
"k8s.io/client-go/pkg/watch"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
fed "k8s.io/kubernetes/pkg/dns/federation"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Sync manages synchronization of the config map.
|
||||
type Sync interface {
|
||||
// Once does a blocking synchronization of the config map. If the
|
||||
// ConfigMap fails to validate, this method will return nil, err.
|
||||
Once() (*Config, error)
|
||||
|
||||
// Start a periodic synchronization of the configuration map. When a
|
||||
// successful configuration map update is detected, the
|
||||
// configuration will be sent to the channel.
|
||||
//
|
||||
// It is an error to call this more than once.
|
||||
Periodic() <-chan *Config
|
||||
}
|
||||
|
||||
// NewSync for ConfigMap from namespace `ns` and `name`.
|
||||
func NewSync(client kubernetes.Interface, ns string, name string) Sync {
|
||||
sync := &kubeSync{
|
||||
ns: ns,
|
||||
name: name,
|
||||
client: client,
|
||||
channel: make(chan *Config),
|
||||
}
|
||||
|
||||
listWatch := &cache.ListWatch{
|
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||
options.FieldSelector = fields.Set{"metadata.name": name}.AsSelector().String()
|
||||
return client.Core().ConfigMaps(ns).List(options)
|
||||
},
|
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||
options.FieldSelector = fields.Set{"metadata.name": name}.AsSelector().String()
|
||||
return client.Core().ConfigMaps(ns).Watch(options)
|
||||
},
|
||||
}
|
||||
|
||||
store, controller := cache.NewInformer(
|
||||
listWatch,
|
||||
&v1.ConfigMap{},
|
||||
time.Duration(0),
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: sync.onAdd,
|
||||
DeleteFunc: sync.onDelete,
|
||||
UpdateFunc: sync.onUpdate,
|
||||
})
|
||||
|
||||
sync.store = store
|
||||
sync.controller = controller
|
||||
|
||||
return sync
|
||||
}
|
||||
|
||||
// kubeSync implements Sync for the Kubernetes API.
|
||||
type kubeSync struct {
|
||||
ns string
|
||||
name string
|
||||
|
||||
client kubernetes.Interface
|
||||
store cache.Store
|
||||
controller *cache.Controller
|
||||
|
||||
channel chan *Config
|
||||
|
||||
latestVersion string
|
||||
}
|
||||
|
||||
var _ Sync = (*kubeSync)(nil)
|
||||
|
||||
func (sync *kubeSync) Once() (*Config, error) {
|
||||
cm, err := sync.client.Core().ConfigMaps(sync.ns).Get(sync.name, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Error getting ConfigMap %v:%v err: %v",
|
||||
sync.ns, sync.name, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config, _, err := sync.processUpdate(cm)
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (sync *kubeSync) Periodic() <-chan *Config {
|
||||
go sync.controller.Run(wait.NeverStop)
|
||||
return sync.channel
|
||||
}
|
||||
|
||||
func (sync *kubeSync) toConfigMap(obj interface{}) *v1.ConfigMap {
|
||||
cm, ok := obj.(*v1.ConfigMap)
|
||||
if !ok {
|
||||
glog.Fatalf("Expected ConfigMap, got %T", obj)
|
||||
}
|
||||
return cm
|
||||
}
|
||||
|
||||
func (sync *kubeSync) onAdd(obj interface{}) {
|
||||
cm := sync.toConfigMap(obj)
|
||||
|
||||
glog.V(2).Infof("ConfigMap %s:%s was created", sync.ns, sync.name)
|
||||
|
||||
config, updated, err := sync.processUpdate(cm)
|
||||
if updated && err == nil {
|
||||
sync.channel <- config
|
||||
}
|
||||
}
|
||||
|
||||
func (sync *kubeSync) onDelete(_ interface{}) {
|
||||
glog.V(2).Infof("ConfigMap %s:%s was deleted, reverting to default configuration",
|
||||
sync.ns, sync.name)
|
||||
|
||||
sync.latestVersion = ""
|
||||
sync.channel <- NewDefaultConfig()
|
||||
}
|
||||
|
||||
func (sync *kubeSync) onUpdate(_, obj interface{}) {
|
||||
cm := sync.toConfigMap(obj)
|
||||
|
||||
glog.V(2).Infof("ConfigMap %s:%s was updated", sync.ns, sync.name)
|
||||
|
||||
config, changed, err := sync.processUpdate(cm)
|
||||
|
||||
if changed && err == nil {
|
||||
sync.channel <- config
|
||||
}
|
||||
}
|
||||
|
||||
func (sync *kubeSync) processUpdate(cm *v1.ConfigMap) (config *Config, changed bool, err error) {
|
||||
glog.V(4).Infof("processUpdate ConfigMap %+v", *cm)
|
||||
|
||||
if cm.ObjectMeta.ResourceVersion != sync.latestVersion {
|
||||
glog.V(3).Infof("Updating config to version %v (was %v)",
|
||||
cm.ObjectMeta.ResourceVersion, sync.latestVersion)
|
||||
changed = true
|
||||
sync.latestVersion = cm.ObjectMeta.ResourceVersion
|
||||
} else {
|
||||
glog.V(4).Infof("Config was unchanged (version %v)", sync.latestVersion)
|
||||
return
|
||||
}
|
||||
|
||||
config = &Config{}
|
||||
|
||||
if err = sync.updateFederations(cm, config); err != nil {
|
||||
glog.Errorf("Invalid configuration, ignoring update")
|
||||
return
|
||||
}
|
||||
|
||||
if err = config.Validate(); err != nil {
|
||||
glog.Errorf("Invalid onfiguration: %v (value was %+v), ignoring update",
|
||||
err, config)
|
||||
config = nil
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (sync *kubeSync) updateFederations(cm *v1.ConfigMap, config *Config) (err error) {
|
||||
if flagValue, ok := cm.Data["federations"]; ok {
|
||||
config.Federations = make(map[string]string)
|
||||
if err = fed.ParseFederationsFlag(flagValue, config.Federations); err != nil {
|
||||
glog.Errorf("Invalid federations value: %v (value was %q)",
|
||||
err, cm.Data["federations"])
|
||||
return
|
||||
}
|
||||
glog.V(2).Infof("Updated federations to %v", config.Federations)
|
||||
} else {
|
||||
glog.V(2).Infof("No federations present")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
868
pkg/dns/dns.go
868
pkg/dns/dns.go
@ -1,868 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
metav1 "k8s.io/client-go/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/pkg/runtime"
|
||||
"k8s.io/client-go/pkg/watch"
|
||||
kcache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/kubernetes/pkg/dns/config"
|
||||
"k8s.io/kubernetes/pkg/dns/treecache"
|
||||
"k8s.io/kubernetes/pkg/dns/util"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
etcd "github.com/coreos/etcd/client"
|
||||
"github.com/golang/glog"
|
||||
"github.com/miekg/dns"
|
||||
skymsg "github.com/skynetservices/skydns/msg"
|
||||
)
|
||||
|
||||
const (
|
||||
// A subdomain added to the user specified domain for all services.
|
||||
serviceSubdomain = "svc"
|
||||
|
||||
// A subdomain added to the user specified dmoain for all pods.
|
||||
podSubdomain = "pod"
|
||||
|
||||
// Resync period for the kube controller loop.
|
||||
resyncPeriod = 5 * time.Minute
|
||||
|
||||
// Duration for which the TTL cache should hold the node resource to retrieve the zone
|
||||
// annotation from it so that it could be added to federation CNAMEs. There is ideally
|
||||
// no need to expire this cache, but we don't want to assume that node annotations
|
||||
// never change. So we expire the cache and retrieve a node once every 180 seconds.
|
||||
// The value is chosen to be neither too long nor too short.
|
||||
nodeCacheTTL = 180 * time.Second
|
||||
)
|
||||
|
||||
type KubeDNS struct {
|
||||
// kubeClient makes calls to API Server and registers calls with API Server
|
||||
// to get Endpoints and Service objects.
|
||||
kubeClient clientset.Interface
|
||||
|
||||
// domain for which this DNS Server is authoritative.
|
||||
domain string
|
||||
// configMap where kube-dns dynamic configuration is store. If this
|
||||
// is empty then getting configuration from a configMap will be
|
||||
// disabled.
|
||||
configMap string
|
||||
|
||||
// endpointsStore that contains all the endpoints in the system.
|
||||
endpointsStore kcache.Store
|
||||
// servicesStore that contains all the services in the system.
|
||||
servicesStore kcache.Store
|
||||
// nodesStore contains some subset of nodes in the system so that we
|
||||
// can retrieve the cluster zone annotation from the cached node
|
||||
// instead of getting it from the API server every time.
|
||||
nodesStore kcache.Store
|
||||
|
||||
// cache stores DNS records for the domain. A Records and SRV Records for
|
||||
// (regular) services and headless Services. CNAME Records for
|
||||
// ExternalName Services.
|
||||
cache treecache.TreeCache
|
||||
// TODO(nikhiljindal): Remove this. It can be recreated using
|
||||
// clusterIPServiceMap.
|
||||
reverseRecordMap map[string]*skymsg.Service
|
||||
// clusterIPServiceMap to service object. Headless services are not
|
||||
// part of this map. Used to get a service when given its cluster
|
||||
// IP. Access to this is coordinated using cacheLock. We use the
|
||||
// same lock for cache and this map to ensure that they don't get
|
||||
// out of sync.
|
||||
clusterIPServiceMap map[string]*v1.Service
|
||||
// cacheLock protecting the cache. caller is responsible for using
|
||||
// the cacheLock before invoking methods on cache the cache is not
|
||||
// thread-safe, and the caller can guarantee thread safety by using
|
||||
// the cacheLock
|
||||
cacheLock sync.RWMutex
|
||||
|
||||
// The domain for which this DNS Server is authoritative, in array
|
||||
// format and reversed. e.g. if domain is "cluster.local",
|
||||
// domainPath is []string{"local", "cluster"}
|
||||
domainPath []string
|
||||
|
||||
// endpointsController invokes registered callbacks when endpoints change.
|
||||
endpointsController *kcache.Controller
|
||||
// serviceController invokes registered callbacks when services change.
|
||||
serviceController *kcache.Controller
|
||||
|
||||
// config set from the dynamic configuration source.
|
||||
config *config.Config
|
||||
// configLock protects the config below.
|
||||
configLock sync.RWMutex
|
||||
// configSync manages synchronization of the config map
|
||||
configSync config.Sync
|
||||
|
||||
// Initial timeout for endpoints and services to be synced from APIServer
|
||||
initialSyncTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewKubeDNS(client clientset.Interface, clusterDomain string, timeout time.Duration, configSync config.Sync) *KubeDNS {
|
||||
kd := &KubeDNS{
|
||||
kubeClient: client,
|
||||
domain: clusterDomain,
|
||||
cache: treecache.NewTreeCache(),
|
||||
cacheLock: sync.RWMutex{},
|
||||
nodesStore: kcache.NewStore(kcache.MetaNamespaceKeyFunc),
|
||||
reverseRecordMap: make(map[string]*skymsg.Service),
|
||||
clusterIPServiceMap: make(map[string]*v1.Service),
|
||||
domainPath: util.ReverseArray(strings.Split(strings.TrimRight(clusterDomain, "."), ".")),
|
||||
initialSyncTimeout: timeout,
|
||||
|
||||
configLock: sync.RWMutex{},
|
||||
configSync: configSync,
|
||||
}
|
||||
|
||||
kd.setEndpointsStore()
|
||||
kd.setServicesStore()
|
||||
|
||||
return kd
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) Start() {
|
||||
glog.V(2).Infof("Starting endpointsController")
|
||||
go kd.endpointsController.Run(wait.NeverStop)
|
||||
|
||||
glog.V(2).Infof("Starting serviceController")
|
||||
go kd.serviceController.Run(wait.NeverStop)
|
||||
|
||||
kd.startConfigMapSync()
|
||||
|
||||
// Wait synchronously for the initial list operations to be
|
||||
// complete of endpoints and services from APIServer.
|
||||
kd.waitForResourceSyncedOrDie()
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) waitForResourceSyncedOrDie() {
|
||||
// Wait for both controllers have completed an initial resource listing
|
||||
timeout := time.After(kd.initialSyncTimeout)
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
glog.Fatalf("Timeout waiting for initialization")
|
||||
case <-ticker.C:
|
||||
if kd.endpointsController.HasSynced() && kd.serviceController.HasSynced() {
|
||||
glog.V(0).Infof("Initialized services and endpoints from apiserver")
|
||||
return
|
||||
}
|
||||
glog.V(0).Infof("DNS server not ready, retry in 500 milliseconds")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) startConfigMapSync() {
|
||||
initialConfig, err := kd.configSync.Once()
|
||||
if err != nil {
|
||||
glog.Errorf(
|
||||
"Error getting initial ConfigMap: %v, starting with default values", err)
|
||||
kd.config = config.NewDefaultConfig()
|
||||
} else {
|
||||
kd.config = initialConfig
|
||||
}
|
||||
|
||||
go kd.syncConfigMap(kd.configSync.Periodic())
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) syncConfigMap(syncChan <-chan *config.Config) {
|
||||
for {
|
||||
nextConfig := <-syncChan
|
||||
|
||||
kd.configLock.Lock()
|
||||
kd.config = nextConfig
|
||||
glog.V(2).Infof("Configuration updated: %+v", *kd.config)
|
||||
kd.configLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) GetCacheAsJSON() (string, error) {
|
||||
kd.cacheLock.RLock()
|
||||
defer kd.cacheLock.RUnlock()
|
||||
json, err := kd.cache.Serialize()
|
||||
return json, err
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) setServicesStore() {
|
||||
// Returns a cache.ListWatch that gets all changes to services.
|
||||
kd.servicesStore, kd.serviceController = kcache.NewInformer(
|
||||
&kcache.ListWatch{
|
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||
return kd.kubeClient.Core().Services(v1.NamespaceAll).List(options)
|
||||
},
|
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||
return kd.kubeClient.Core().Services(v1.NamespaceAll).Watch(options)
|
||||
},
|
||||
},
|
||||
&v1.Service{},
|
||||
resyncPeriod,
|
||||
kcache.ResourceEventHandlerFuncs{
|
||||
AddFunc: kd.newService,
|
||||
DeleteFunc: kd.removeService,
|
||||
UpdateFunc: kd.updateService,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) setEndpointsStore() {
|
||||
// Returns a cache.ListWatch that gets all changes to endpoints.
|
||||
kd.endpointsStore, kd.endpointsController = kcache.NewInformer(
|
||||
&kcache.ListWatch{
|
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||
return kd.kubeClient.Core().Endpoints(v1.NamespaceAll).List(options)
|
||||
},
|
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||
return kd.kubeClient.Core().Endpoints(v1.NamespaceAll).Watch(options)
|
||||
},
|
||||
},
|
||||
&v1.Endpoints{},
|
||||
resyncPeriod,
|
||||
kcache.ResourceEventHandlerFuncs{
|
||||
AddFunc: kd.handleEndpointAdd,
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
// TODO: Avoid unwanted updates.
|
||||
kd.handleEndpointAdd(newObj)
|
||||
},
|
||||
// No DeleteFunc for EndpointsStore because endpoint object will be deleted
|
||||
// when corresponding service is deleted.
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func assertIsService(obj interface{}) (*v1.Service, bool) {
|
||||
if service, ok := obj.(*v1.Service); ok {
|
||||
return service, ok
|
||||
} else {
|
||||
glog.Errorf("Type assertion failed! Expected 'Service', got %T", service)
|
||||
return nil, ok
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) newService(obj interface{}) {
|
||||
if service, ok := assertIsService(obj); ok {
|
||||
glog.V(2).Infof("New service: %v", service.Name)
|
||||
glog.V(4).Infof("Service details: %v", service)
|
||||
|
||||
// ExternalName services are a special kind that return CNAME records
|
||||
if service.Spec.Type == v1.ServiceTypeExternalName {
|
||||
kd.newExternalNameService(service)
|
||||
return
|
||||
}
|
||||
// if ClusterIP is not set, a DNS entry should not be created
|
||||
if !v1.IsServiceIPSet(service) {
|
||||
kd.newHeadlessService(service)
|
||||
return
|
||||
}
|
||||
if len(service.Spec.Ports) == 0 {
|
||||
glog.Warningf("Service with no ports, this should not have happened: %v",
|
||||
service)
|
||||
}
|
||||
kd.newPortalService(service)
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) removeService(obj interface{}) {
|
||||
if s, ok := assertIsService(obj); ok {
|
||||
subCachePath := append(kd.domainPath, serviceSubdomain, s.Namespace, s.Name)
|
||||
kd.cacheLock.Lock()
|
||||
defer kd.cacheLock.Unlock()
|
||||
|
||||
success := kd.cache.DeletePath(subCachePath...)
|
||||
glog.V(2).Infof("removeService %v at path %v. Success: %v",
|
||||
s.Name, subCachePath, success)
|
||||
|
||||
// ExternalName services have no IP
|
||||
if v1.IsServiceIPSet(s) {
|
||||
delete(kd.reverseRecordMap, s.Spec.ClusterIP)
|
||||
delete(kd.clusterIPServiceMap, s.Spec.ClusterIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) updateService(oldObj, newObj interface{}) {
|
||||
if new, ok := assertIsService(newObj); ok {
|
||||
if old, ok := assertIsService(oldObj); ok {
|
||||
// Remove old cache path only if changing type to/from ExternalName.
|
||||
// In all other cases, we'll update records in place.
|
||||
if (new.Spec.Type == v1.ServiceTypeExternalName) !=
|
||||
(old.Spec.Type == v1.ServiceTypeExternalName) {
|
||||
kd.removeService(oldObj)
|
||||
}
|
||||
kd.newService(newObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) handleEndpointAdd(obj interface{}) {
|
||||
if e, ok := obj.(*v1.Endpoints); ok {
|
||||
kd.addDNSUsingEndpoints(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) addDNSUsingEndpoints(e *v1.Endpoints) error {
|
||||
svc, err := kd.getServiceFromEndpoints(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if svc == nil || v1.IsServiceIPSet(svc) {
|
||||
// No headless service found corresponding to endpoints object.
|
||||
return nil
|
||||
}
|
||||
return kd.generateRecordsForHeadlessService(e, svc)
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) getServiceFromEndpoints(e *v1.Endpoints) (*v1.Service, error) {
|
||||
key, err := kcache.MetaNamespaceKeyFunc(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, exists, err := kd.servicesStore.GetByKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get service object from services store - %v", err)
|
||||
}
|
||||
if !exists {
|
||||
glog.V(3).Infof("No service for endpoint %q in namespace %q",
|
||||
e.Name, e.Namespace)
|
||||
return nil, nil
|
||||
}
|
||||
if svc, ok := assertIsService(obj); ok {
|
||||
return svc, nil
|
||||
}
|
||||
return nil, fmt.Errorf("got a non service object in services store %v", obj)
|
||||
}
|
||||
|
||||
// fqdn constructs the fqdn for the given service. subpaths is a list of path
|
||||
// elements rooted at the given service, ending at a service record.
|
||||
func (kd *KubeDNS) fqdn(service *v1.Service, subpaths ...string) string {
|
||||
domainLabels := append(append(kd.domainPath, serviceSubdomain, service.Namespace, service.Name), subpaths...)
|
||||
return dns.Fqdn(strings.Join(util.ReverseArray(domainLabels), "."))
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) newPortalService(service *v1.Service) {
|
||||
subCache := treecache.NewTreeCache()
|
||||
recordValue, recordLabel := util.GetSkyMsg(service.Spec.ClusterIP, 0)
|
||||
subCache.SetEntry(recordLabel, recordValue, kd.fqdn(service, recordLabel))
|
||||
|
||||
// Generate SRV Records
|
||||
for i := range service.Spec.Ports {
|
||||
port := &service.Spec.Ports[i]
|
||||
if port.Name != "" && port.Protocol != "" {
|
||||
srvValue := kd.generateSRVRecordValue(service, int(port.Port))
|
||||
|
||||
l := []string{"_" + strings.ToLower(string(port.Protocol)), "_" + port.Name}
|
||||
glog.V(2).Infof("Added SRV record %+v", srvValue)
|
||||
|
||||
subCache.SetEntry(recordLabel, srvValue, kd.fqdn(service, append(l, recordLabel)...), l...)
|
||||
}
|
||||
}
|
||||
subCachePath := append(kd.domainPath, serviceSubdomain, service.Namespace)
|
||||
host := getServiceFQDN(kd.domain, service)
|
||||
reverseRecord, _ := util.GetSkyMsg(host, 0)
|
||||
|
||||
kd.cacheLock.Lock()
|
||||
defer kd.cacheLock.Unlock()
|
||||
kd.cache.SetSubCache(service.Name, subCache, subCachePath...)
|
||||
kd.reverseRecordMap[service.Spec.ClusterIP] = reverseRecord
|
||||
kd.clusterIPServiceMap[service.Spec.ClusterIP] = service
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) generateRecordsForHeadlessService(e *v1.Endpoints, svc *v1.Service) error {
|
||||
subCache := treecache.NewTreeCache()
|
||||
glog.V(4).Infof("Endpoints Annotations: %v", e.Annotations)
|
||||
for idx := range e.Subsets {
|
||||
for subIdx := range e.Subsets[idx].Addresses {
|
||||
address := &e.Subsets[idx].Addresses[subIdx]
|
||||
endpointIP := address.IP
|
||||
recordValue, endpointName := util.GetSkyMsg(endpointIP, 0)
|
||||
if hostLabel, exists := getHostname(address); exists {
|
||||
endpointName = hostLabel
|
||||
}
|
||||
subCache.SetEntry(endpointName, recordValue, kd.fqdn(svc, endpointName))
|
||||
for portIdx := range e.Subsets[idx].Ports {
|
||||
endpointPort := &e.Subsets[idx].Ports[portIdx]
|
||||
if endpointPort.Name != "" && endpointPort.Protocol != "" {
|
||||
srvValue := kd.generateSRVRecordValue(svc, int(endpointPort.Port), endpointName)
|
||||
glog.V(2).Infof("Added SRV record %+v", srvValue)
|
||||
|
||||
l := []string{"_" + strings.ToLower(string(endpointPort.Protocol)), "_" + endpointPort.Name}
|
||||
subCache.SetEntry(endpointName, srvValue, kd.fqdn(svc, append(l, endpointName)...), l...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subCachePath := append(kd.domainPath, serviceSubdomain, svc.Namespace)
|
||||
kd.cacheLock.Lock()
|
||||
defer kd.cacheLock.Unlock()
|
||||
kd.cache.SetSubCache(svc.Name, subCache, subCachePath...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHostname(address *v1.EndpointAddress) (string, bool) {
|
||||
if len(address.Hostname) > 0 {
|
||||
return address.Hostname, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) generateSRVRecordValue(svc *v1.Service, portNumber int, labels ...string) *skymsg.Service {
|
||||
host := strings.Join([]string{svc.Name, svc.Namespace, serviceSubdomain, kd.domain}, ".")
|
||||
for _, cNameLabel := range labels {
|
||||
host = cNameLabel + "." + host
|
||||
}
|
||||
recordValue, _ := util.GetSkyMsg(host, portNumber)
|
||||
return recordValue
|
||||
}
|
||||
|
||||
// Generates skydns records for a headless service.
|
||||
func (kd *KubeDNS) newHeadlessService(service *v1.Service) error {
|
||||
// Create an A record for every pod in the service.
|
||||
// This record must be periodically updated.
|
||||
// Format is as follows:
|
||||
// For a service x, with pods a and b create DNS records,
|
||||
// a.x.ns.domain. and, b.x.ns.domain.
|
||||
key, err := kcache.MetaNamespaceKeyFunc(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e, exists, err := kd.endpointsStore.GetByKey(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get endpoints object from endpoints store - %v", err)
|
||||
}
|
||||
if !exists {
|
||||
glog.V(1).Infof("Could not find endpoints for service %q in namespace %q. DNS records will be created once endpoints show up.",
|
||||
service.Name, service.Namespace)
|
||||
return nil
|
||||
}
|
||||
if e, ok := e.(*v1.Endpoints); ok {
|
||||
return kd.generateRecordsForHeadlessService(e, service)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates skydns records for an ExternalName service.
|
||||
func (kd *KubeDNS) newExternalNameService(service *v1.Service) {
|
||||
// Create a CNAME record for the service's ExternalName.
|
||||
// TODO: TTL?
|
||||
recordValue, _ := util.GetSkyMsg(service.Spec.ExternalName, 0)
|
||||
cachePath := append(kd.domainPath, serviceSubdomain, service.Namespace)
|
||||
fqdn := kd.fqdn(service)
|
||||
glog.V(2).Infof("newExternalNameService: storing key %s with value %v as %s under %v",
|
||||
service.Name, recordValue, fqdn, cachePath)
|
||||
kd.cacheLock.Lock()
|
||||
defer kd.cacheLock.Unlock()
|
||||
// Store the service name directly as the leaf key
|
||||
kd.cache.SetEntry(service.Name, recordValue, fqdn, cachePath...)
|
||||
}
|
||||
|
||||
// Records responds with DNS records that match the given name, in a format
|
||||
// understood by the skydns server. If "exact" is true, a single record
|
||||
// matching the given name is returned, otherwise all records stored under
|
||||
// the subtree matching the name are returned.
|
||||
func (kd *KubeDNS) Records(name string, exact bool) (retval []skymsg.Service, err error) {
|
||||
glog.V(3).Infof("Query for %q, exact: %v", name, exact)
|
||||
|
||||
trimmed := strings.TrimRight(name, ".")
|
||||
segments := strings.Split(trimmed, ".")
|
||||
isFederationQuery := false
|
||||
federationSegments := []string{}
|
||||
|
||||
if !exact && kd.isFederationQuery(segments) {
|
||||
glog.V(3).Infof("Received federation query, trying local service first")
|
||||
// Try querying the non-federation (local) service first. Will try
|
||||
// the federation one later, if this fails.
|
||||
isFederationQuery = true
|
||||
federationSegments = append(federationSegments, segments...)
|
||||
// To try local service, remove federation name from segments.
|
||||
// Federation name is 3rd in the segment (after service name and
|
||||
// namespace).
|
||||
segments = append(segments[:2], segments[3:]...)
|
||||
}
|
||||
|
||||
path := util.ReverseArray(segments)
|
||||
records, err := kd.getRecordsForPath(path, exact)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isFederationQuery {
|
||||
return kd.recordsForFederation(records, path, exact, federationSegments)
|
||||
} else if len(records) > 0 {
|
||||
glog.V(4).Infof("Records for %v: %v", name, records)
|
||||
return records, nil
|
||||
}
|
||||
|
||||
glog.V(3).Infof("No record found for %v", name)
|
||||
return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) recordsForFederation(records []skymsg.Service, path []string, exact bool, federationSegments []string) (retval []skymsg.Service, err error) {
|
||||
// For federation query, verify that the local service has endpoints.
|
||||
validRecord := false
|
||||
for _, val := range records {
|
||||
// We know that a headless service has endpoints for sure if a
|
||||
// record was returned for it. The record contains endpoint
|
||||
// IPs. So nothing to check for headless services.
|
||||
//
|
||||
// TODO: this access to the cluster IP map does not seem to be
|
||||
// threadsafe.
|
||||
if !kd.isHeadlessServiceRecord(&val) {
|
||||
ok, err := kd.serviceWithClusterIPHasEndpoints(&val)
|
||||
if err != nil {
|
||||
glog.V(2).Infof(
|
||||
"Federation: error finding if service has endpoint: %v", err)
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
glog.V(2).Infof("Federation: skipping record since service has no endpoint: %v", val)
|
||||
continue
|
||||
}
|
||||
}
|
||||
validRecord = true
|
||||
break
|
||||
}
|
||||
|
||||
if validRecord {
|
||||
// There is a local service with valid endpoints, return its CNAME.
|
||||
name := strings.Join(util.ReverseArray(path), ".")
|
||||
// Ensure that this name that we are returning as a CNAME response
|
||||
// is a fully qualified domain name so that the client's resolver
|
||||
// library doesn't have to go through its search list all over
|
||||
// again.
|
||||
if !strings.HasSuffix(name, ".") {
|
||||
name = name + "."
|
||||
}
|
||||
glog.V(3).Infof(
|
||||
"Federation: Returning CNAME for local service: %v", name)
|
||||
return []skymsg.Service{{Host: name}}, nil
|
||||
}
|
||||
|
||||
// If the name query is not an exact query and does not match any
|
||||
// records in the local store, attempt to send a federation redirect
|
||||
// (CNAME) response.
|
||||
if !exact {
|
||||
glog.V(3).Infof(
|
||||
"Federation: Did not find a local service. Trying federation redirect (CNAME)")
|
||||
return kd.federationRecords(util.ReverseArray(federationSegments))
|
||||
}
|
||||
|
||||
return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) getRecordsForPath(path []string, exact bool) ([]skymsg.Service, error) {
|
||||
if kd.isPodRecord(path) {
|
||||
ip, err := kd.getPodIP(path)
|
||||
if err == nil {
|
||||
skyMsg, _ := util.GetSkyMsg(ip, 0)
|
||||
return []skymsg.Service{*skyMsg}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exact {
|
||||
key := path[len(path)-1]
|
||||
if key == "" {
|
||||
return []skymsg.Service{}, nil
|
||||
}
|
||||
kd.cacheLock.RLock()
|
||||
defer kd.cacheLock.RUnlock()
|
||||
if record, ok := kd.cache.GetEntry(key, path[:len(path)-1]...); ok {
|
||||
glog.V(3).Infof("Exact match %v for %v received from cache", record, path[:len(path)-1])
|
||||
return []skymsg.Service{*(record.(*skymsg.Service))}, nil
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Exact match for %v not found in cache", path)
|
||||
return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
|
||||
}
|
||||
|
||||
kd.cacheLock.RLock()
|
||||
defer kd.cacheLock.RUnlock()
|
||||
records := kd.cache.GetValuesForPathWithWildcards(path...)
|
||||
glog.V(3).Infof("Found %d records for %v in the cache", len(records), path)
|
||||
|
||||
retval := []skymsg.Service{}
|
||||
for _, val := range records {
|
||||
retval = append(retval, *val)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("getRecordsForPath retval=%+v, path=%v", retval, path)
|
||||
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// Returns true if the given record corresponds to a headless service.
|
||||
// Important: Assumes that we already have the cacheLock. Callers responsibility to acquire it.
|
||||
// This is because the code will panic, if we try to acquire it again if we already have it.
|
||||
func (kd *KubeDNS) isHeadlessServiceRecord(msg *skymsg.Service) bool {
|
||||
// If it is not a headless service, then msg.Host will be the cluster IP.
|
||||
// So we can check if msg.host exists in our clusterIPServiceMap.
|
||||
_, ok := kd.clusterIPServiceMap[msg.Host]
|
||||
// It is headless service if no record was found.
|
||||
return !ok
|
||||
}
|
||||
|
||||
// Returns true if the service corresponding to the given message has endpoints.
|
||||
// Note: Works only for services with ClusterIP. Will return an error for headless service (service without a clusterIP).
|
||||
// Important: Assumes that we already have the cacheLock. Callers responsibility to acquire it.
|
||||
// This is because the code will panic, if we try to acquire it again if we already have it.
|
||||
func (kd *KubeDNS) serviceWithClusterIPHasEndpoints(msg *skymsg.Service) (bool, error) {
|
||||
svc, ok := kd.clusterIPServiceMap[msg.Host]
|
||||
if !ok {
|
||||
// It is a headless service.
|
||||
return false, fmt.Errorf("method not expected to be called for headless service")
|
||||
}
|
||||
key, err := kcache.MetaNamespaceKeyFunc(svc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
e, exists, err := kd.endpointsStore.GetByKey(key)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get endpoints object from endpoints store - %v", err)
|
||||
}
|
||||
if !exists {
|
||||
return false, nil
|
||||
}
|
||||
if e, ok := e.(*v1.Endpoints); ok {
|
||||
return len(e.Subsets) > 0, nil
|
||||
}
|
||||
return false, fmt.Errorf("unexpected: found non-endpoint object in endpoint store: %v", e)
|
||||
}
|
||||
|
||||
// ReverseRecords performs a reverse lookup for the given name.
|
||||
func (kd *KubeDNS) ReverseRecord(name string) (*skymsg.Service, error) {
|
||||
glog.V(3).Infof("Query for ReverseRecord %q", name)
|
||||
|
||||
// if portalIP is not a valid IP, the reverseRecordMap lookup will fail
|
||||
portalIP, ok := util.ExtractIP(name)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("does not support reverse lookup for %s", name)
|
||||
}
|
||||
|
||||
kd.cacheLock.RLock()
|
||||
defer kd.cacheLock.RUnlock()
|
||||
if reverseRecord, ok := kd.reverseRecordMap[portalIP]; ok {
|
||||
return reverseRecord, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("must be exactly one service record")
|
||||
}
|
||||
|
||||
// e.g {"local", "cluster", "pod", "default", "10-0-0-1"}
|
||||
func (kd *KubeDNS) isPodRecord(path []string) bool {
|
||||
if len(path) != len(kd.domainPath)+3 {
|
||||
return false
|
||||
}
|
||||
if path[len(kd.domainPath)] != "pod" {
|
||||
return false
|
||||
}
|
||||
for _, segment := range path {
|
||||
if segment == "*" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (kd *KubeDNS) getPodIP(path []string) (string, error) {
|
||||
ipStr := path[len(path)-1]
|
||||
ip := strings.Replace(ipStr, "-", ".", -1)
|
||||
if parsed := net.ParseIP(ip); parsed != nil {
|
||||
return ip, nil
|
||||
}
|
||||
return "", fmt.Errorf("Invalid IP Address %v", ip)
|
||||
}
|
||||
|
||||
// isFederationQuery checks if the given query `path` matches the federated service query pattern.
|
||||
// The conjunction of the following conditions forms the test for the federated service query
|
||||
// pattern:
|
||||
// 1. `path` has exactly 4+len(domainPath) segments: mysvc.myns.myfederation.svc.domain.path.
|
||||
// 2. Service name component must be a valid RFC 1035 name.
|
||||
// 3. Namespace component must be a valid RFC 1123 name.
|
||||
// 4. Federation component must also be a valid RFC 1123 name.
|
||||
// 5. Fourth segment is exactly "svc"
|
||||
// 6. The remaining segments match kd.domainPath.
|
||||
// 7. And federation must be one of the listed federations in the config.
|
||||
// Note: Because of the above conditions, this method will treat wildcard queries such as
|
||||
// *.mysvc.myns.myfederation.svc.domain.path as non-federation queries.
|
||||
// We can add support for wildcard queries later, if needed.
|
||||
func (kd *KubeDNS) isFederationQuery(path []string) bool {
|
||||
if len(path) != 4+len(kd.domainPath) {
|
||||
glog.V(4).Infof("Not a federation query: len(%q) != 4+len(%q)", path, kd.domainPath)
|
||||
return false
|
||||
}
|
||||
if errs := validation.IsDNS1035Label(path[0]); len(errs) != 0 {
|
||||
glog.V(4).Infof("Not a federation query: %q is not an RFC 1035 label: %q",
|
||||
path[0], errs)
|
||||
return false
|
||||
}
|
||||
if errs := validation.IsDNS1123Label(path[1]); len(errs) != 0 {
|
||||
glog.V(4).Infof("Not a federation query: %q is not an RFC 1123 label: %q",
|
||||
path[1], errs)
|
||||
return false
|
||||
}
|
||||
if errs := validation.IsDNS1123Label(path[2]); len(errs) != 0 {
|
||||
glog.V(4).Infof("Not a federation query: %q is not an RFC 1123 label: %q",
|
||||
path[2], errs)
|
||||
return false
|
||||
}
|
||||
if path[3] != serviceSubdomain {
|
||||
glog.V(4).Infof("Not a federation query: %q != %q (serviceSubdomain)",
|
||||
path[3], serviceSubdomain)
|
||||
return false
|
||||
}
|
||||
for i, domComp := range kd.domainPath {
|
||||
// kd.domainPath is reversed, so we need to look in the `path` in the reverse order.
|
||||
if domComp != path[len(path)-i-1] {
|
||||
glog.V(4).Infof("Not a federation query: kd.domainPath[%d] != path[%d] (%q != %q)",
|
||||
i, len(path)-i-1, domComp, path[len(path)-i-1])
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
kd.configLock.RLock()
|
||||
defer kd.configLock.RUnlock()
|
||||
|
||||
if _, ok := kd.config.Federations[path[2]]; !ok {
|
||||
glog.V(4).Infof("Not a federation query: label %q not found", path[2])
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// federationRecords checks if the given `queryPath` is for a federated service and if it is,
|
||||
// it returns a CNAME response containing the cluster zone name and federation domain name
|
||||
// suffix.
|
||||
func (kd *KubeDNS) federationRecords(queryPath []string) ([]skymsg.Service, error) {
|
||||
// `queryPath` is a reversed-array of the queried name, reverse it back to make it easy
|
||||
// to follow through this code and reduce confusion. There is no reason for it to be
|
||||
// reversed here.
|
||||
path := util.ReverseArray(queryPath)
|
||||
|
||||
// Check if the name query matches the federation query pattern.
|
||||
if !kd.isFederationQuery(path) {
|
||||
return nil, etcd.Error{Code: etcd.ErrorCodeKeyNotFound}
|
||||
}
|
||||
|
||||
// Now that we have already established that the query is a federation query, remove the local
|
||||
// domain path components, i.e. kd.domainPath, from the query.
|
||||
path = path[:len(path)-len(kd.domainPath)]
|
||||
|
||||
// Append the zone name (zone in the cloud provider terminology, not a DNS
|
||||
// zone) and the region name.
|
||||
zone, region, err := kd.getClusterZoneAndRegion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to obtain the cluster zone and region: %v", err)
|
||||
}
|
||||
path = append(path, zone, region)
|
||||
|
||||
// We have already established that the map entry exists for the given federation,
|
||||
// we just need to retrieve the domain name, validate it and append it to the path.
|
||||
kd.configLock.RLock()
|
||||
domain := kd.config.Federations[path[2]]
|
||||
kd.configLock.RUnlock()
|
||||
|
||||
// We accept valid subdomains as well, so just let all the valid subdomains.
|
||||
if len(validation.IsDNS1123Subdomain(domain)) != 0 {
|
||||
return nil, fmt.Errorf("%s is not a valid domain name for federation %s", domain, path[2])
|
||||
}
|
||||
name := strings.Join(append(path, domain), ".")
|
||||
|
||||
// Ensure that this name that we are returning as a CNAME response is a fully qualified
|
||||
// domain name so that the client's resolver library doesn't have to go through its
|
||||
// search list all over again.
|
||||
if !strings.HasSuffix(name, ".") {
|
||||
name = name + "."
|
||||
}
|
||||
return []skymsg.Service{{Host: name}}, nil
|
||||
}
|
||||
|
||||
// getClusterZoneAndRegion returns the name of the zone and the region the
|
||||
// cluster is running in. It arbitrarily selects a node and reads the failure
|
||||
// domain label on the node. An alternative is to obtain this pod's
|
||||
// (i.e. kube-dns pod's) name using the downward API, get the pod, get the
|
||||
// node the pod is bound to and retrieve that node's labels. But even just by
|
||||
// reading those steps, it looks complex and it is not entirely clear what
|
||||
// that complexity is going to buy us. So taking a simpler approach here.
|
||||
// Also note that zone here means the zone in cloud provider terminology, not
|
||||
// the DNS zone.
|
||||
func (kd *KubeDNS) getClusterZoneAndRegion() (string, string, error) {
|
||||
var node *v1.Node
|
||||
|
||||
objs := kd.nodesStore.List()
|
||||
if len(objs) > 0 {
|
||||
var ok bool
|
||||
if node, ok = objs[0].(*v1.Node); !ok {
|
||||
return "", "", fmt.Errorf("expected node object, got: %T", objs[0])
|
||||
}
|
||||
} else {
|
||||
// An alternative to listing nodes each time is to set a watch, but that is totally
|
||||
// wasteful in case of non-federated independent Kubernetes clusters. So carefully
|
||||
// proceeding here.
|
||||
// TODO(madhusudancs): Move this to external/v1 API.
|
||||
nodeList, err := kd.kubeClient.Core().Nodes().List(v1.ListOptions{})
|
||||
if err != nil || len(nodeList.Items) == 0 {
|
||||
return "", "", fmt.Errorf("failed to retrieve the cluster nodes: %v", err)
|
||||
}
|
||||
|
||||
// Select a node (arbitrarily the first node) that has
|
||||
// `LabelZoneFailureDomain` and `LabelZoneRegion` set.
|
||||
for _, nodeItem := range nodeList.Items {
|
||||
_, zfound := nodeItem.Labels[metav1.LabelZoneFailureDomain]
|
||||
_, rfound := nodeItem.Labels[metav1.LabelZoneRegion]
|
||||
if !zfound || !rfound {
|
||||
continue
|
||||
}
|
||||
// Make a copy of the node, don't rely on the loop variable.
|
||||
node = &(*(&nodeItem))
|
||||
if err := kd.nodesStore.Add(node); err != nil {
|
||||
return "", "", fmt.Errorf("couldn't add the retrieved node to the cache: %v", err)
|
||||
}
|
||||
// Node is found, break out of the loop.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
return "", "", fmt.Errorf("Could not find any nodes")
|
||||
}
|
||||
|
||||
zone, ok := node.Labels[metav1.LabelZoneFailureDomain]
|
||||
if !ok || zone == "" {
|
||||
return "", "", fmt.Errorf("unknown cluster zone")
|
||||
}
|
||||
region, ok := node.Labels[metav1.LabelZoneRegion]
|
||||
if !ok || region == "" {
|
||||
return "", "", fmt.Errorf("unknown cluster region")
|
||||
}
|
||||
return zone, region, nil
|
||||
}
|
||||
|
||||
func getServiceFQDN(domain string, service *v1.Service) string {
|
||||
return strings.Join(
|
||||
[]string{service.Name, service.Namespace, serviceSubdomain, domain}, ".")
|
||||
}
|
@ -1,839 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
etcd "github.com/coreos/etcd/client"
|
||||
"github.com/miekg/dns"
|
||||
skymsg "github.com/skynetservices/skydns/msg"
|
||||
skyserver "github.com/skynetservices/skydns/server"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
metav1 "k8s.io/client-go/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"k8s.io/kubernetes/pkg/dns/config"
|
||||
"k8s.io/kubernetes/pkg/dns/treecache"
|
||||
"k8s.io/kubernetes/pkg/dns/util"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
const (
|
||||
testDomain = "cluster.local."
|
||||
testService = "testservice"
|
||||
testNamespace = "default"
|
||||
testExternalName = "foo.bar.example.com"
|
||||
)
|
||||
|
||||
func newKubeDNS() *KubeDNS {
|
||||
return &KubeDNS{
|
||||
domain: testDomain,
|
||||
domainPath: util.ReverseArray(strings.Split(strings.TrimRight(testDomain, "."), ".")),
|
||||
|
||||
endpointsStore: cache.NewStore(cache.MetaNamespaceKeyFunc),
|
||||
servicesStore: cache.NewStore(cache.MetaNamespaceKeyFunc),
|
||||
nodesStore: cache.NewStore(cache.MetaNamespaceKeyFunc),
|
||||
|
||||
cache: treecache.NewTreeCache(),
|
||||
reverseRecordMap: make(map[string]*skymsg.Service),
|
||||
clusterIPServiceMap: make(map[string]*v1.Service),
|
||||
cacheLock: sync.RWMutex{},
|
||||
|
||||
config: config.NewDefaultConfig(),
|
||||
configLock: sync.RWMutex{},
|
||||
configSync: config.NewNopSync(config.NewDefaultConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodDns(t *testing.T) {
|
||||
const (
|
||||
testPodIP = "1.2.3.4"
|
||||
sanitizedPodIP = "1-2-3-4"
|
||||
)
|
||||
kd := newKubeDNS()
|
||||
|
||||
records, err := kd.Records(sanitizedPodIP+".default.pod."+kd.domain, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, testPodIP, records[0].Host)
|
||||
}
|
||||
|
||||
func TestUnnamedSinglePortService(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
s := newService(testNamespace, testService, "1.2.3.4", "", 80)
|
||||
// Add the service
|
||||
kd.newService(s)
|
||||
assertDNSForClusterIP(t, kd, s)
|
||||
assertReverseRecord(t, kd, s)
|
||||
// Delete the service
|
||||
kd.removeService(s)
|
||||
assertNoDNSForClusterIP(t, kd, s)
|
||||
assertNoReverseRecord(t, kd, s)
|
||||
}
|
||||
|
||||
func TestNamedSinglePortService(t *testing.T) {
|
||||
const (
|
||||
portName1 = "http1"
|
||||
portName2 = "http2"
|
||||
)
|
||||
kd := newKubeDNS()
|
||||
s := newService(testNamespace, testService, "1.2.3.4", portName1, 80)
|
||||
// Add the service
|
||||
kd.newService(s)
|
||||
assertDNSForClusterIP(t, kd, s)
|
||||
assertSRVForNamedPort(t, kd, s, portName1)
|
||||
|
||||
newService := *s
|
||||
// update the portName of the service
|
||||
newService.Spec.Ports[0].Name = portName2
|
||||
kd.updateService(s, &newService)
|
||||
assertDNSForClusterIP(t, kd, s)
|
||||
assertSRVForNamedPort(t, kd, s, portName2)
|
||||
assertNoSRVForNamedPort(t, kd, s, portName1)
|
||||
|
||||
// Delete the service
|
||||
kd.removeService(s)
|
||||
assertNoDNSForClusterIP(t, kd, s)
|
||||
assertNoSRVForNamedPort(t, kd, s, portName1)
|
||||
assertNoSRVForNamedPort(t, kd, s, portName2)
|
||||
}
|
||||
|
||||
func assertARecordsMatchIPs(t *testing.T, records []dns.RR, ips ...string) {
|
||||
expectedEndpoints := sets.NewString(ips...)
|
||||
gotEndpoints := sets.NewString()
|
||||
for _, r := range records {
|
||||
if a, ok := r.(*dns.A); !ok {
|
||||
t.Errorf("Expected A record, got %#v", a)
|
||||
} else {
|
||||
gotEndpoints.Insert(a.A.String())
|
||||
}
|
||||
}
|
||||
if !gotEndpoints.Equal(expectedEndpoints) {
|
||||
t.Errorf("Expected %v got %v", expectedEndpoints, gotEndpoints)
|
||||
}
|
||||
}
|
||||
|
||||
func assertSRVRecordsMatchTarget(t *testing.T, records []dns.RR, targets ...string) {
|
||||
expectedTargets := sets.NewString(targets...)
|
||||
gotTargets := sets.NewString()
|
||||
for _, r := range records {
|
||||
if srv, ok := r.(*dns.SRV); !ok {
|
||||
t.Errorf("Expected SRV record, got %+v", srv)
|
||||
} else {
|
||||
gotTargets.Insert(srv.Target)
|
||||
}
|
||||
}
|
||||
if !gotTargets.Equal(expectedTargets) {
|
||||
t.Errorf("Expected %v got %v", expectedTargets, gotTargets)
|
||||
}
|
||||
}
|
||||
|
||||
func assertSRVRecordsMatchPort(t *testing.T, records []dns.RR, port ...int) {
|
||||
expectedPorts := sets.NewInt(port...)
|
||||
gotPorts := sets.NewInt()
|
||||
for _, r := range records {
|
||||
if srv, ok := r.(*dns.SRV); !ok {
|
||||
t.Errorf("Expected SRV record, got %+v", srv)
|
||||
} else {
|
||||
gotPorts.Insert(int(srv.Port))
|
||||
t.Logf("got %+v", srv)
|
||||
}
|
||||
}
|
||||
if !gotPorts.Equal(expectedPorts) {
|
||||
t.Errorf("Expected %v got %v", expectedPorts, gotPorts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkySimpleSRVLookup(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
skydnsConfig := &skyserver.Config{Domain: testDomain, DnsAddr: "0.0.0.0:53"}
|
||||
skyserver.SetDefaults(skydnsConfig)
|
||||
s := skyserver.New(kd, skydnsConfig)
|
||||
|
||||
service := newHeadlessService()
|
||||
endpointIPs := []string{"10.0.0.1", "10.0.0.2"}
|
||||
endpoints := newEndpoints(service, newSubsetWithOnePort("", 80, endpointIPs...))
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.newService(service)
|
||||
|
||||
name := strings.Join([]string{testService, testNamespace, "svc", testDomain}, ".")
|
||||
question := dns.Question{Name: name, Qtype: dns.TypeSRV, Qclass: dns.ClassINET}
|
||||
|
||||
rec, extra, err := s.SRVRecords(question, name, 512, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed srv record lookup on service with fqdn %v", name)
|
||||
}
|
||||
assertARecordsMatchIPs(t, extra, endpointIPs...)
|
||||
targets := []string{}
|
||||
for _, eip := range endpointIPs {
|
||||
// A portal service is always created with a port of '0'
|
||||
targets = append(targets,
|
||||
fmt.Sprintf("%x.%v",
|
||||
util.HashServiceRecord(util.NewServiceRecord(eip, 0)), name))
|
||||
}
|
||||
assertSRVRecordsMatchTarget(t, rec, targets...)
|
||||
}
|
||||
|
||||
func TestSkyPodHostnameSRVLookup(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
skydnsConfig := &skyserver.Config{Domain: testDomain, DnsAddr: "0.0.0.0:53"}
|
||||
skyserver.SetDefaults(skydnsConfig)
|
||||
s := skyserver.New(kd, skydnsConfig)
|
||||
|
||||
service := newHeadlessService()
|
||||
endpointIPs := []string{"10.0.0.1", "10.0.0.2"}
|
||||
endpoints := newEndpoints(
|
||||
service,
|
||||
newSubsetWithOnePortWithHostname("", 80, true, endpointIPs...))
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.newService(service)
|
||||
name := strings.Join([]string{testService, testNamespace, "svc", testDomain}, ".")
|
||||
question := dns.Question{Name: name, Qtype: dns.TypeSRV, Qclass: dns.ClassINET}
|
||||
|
||||
rec, _, err := s.SRVRecords(question, name, 512, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed srv record lookup on service with fqdn %v", name)
|
||||
}
|
||||
targets := []string{}
|
||||
for i := range endpointIPs {
|
||||
targets = append(targets, fmt.Sprintf("%v.%v", fmt.Sprintf("ep-%d", i), name))
|
||||
}
|
||||
assertSRVRecordsMatchTarget(t, rec, targets...)
|
||||
}
|
||||
|
||||
func TestSkyNamedPortSRVLookup(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
skydnsConfig := &skyserver.Config{Domain: testDomain, DnsAddr: "0.0.0.0:53"}
|
||||
skyserver.SetDefaults(skydnsConfig)
|
||||
s := skyserver.New(kd, skydnsConfig)
|
||||
|
||||
service := newHeadlessService()
|
||||
eip := "10.0.0.1"
|
||||
endpoints := newEndpoints(service, newSubsetWithOnePort("http", 8081, eip))
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.newService(service)
|
||||
|
||||
name := strings.Join([]string{"_http", "_tcp", testService, testNamespace, "svc", testDomain}, ".")
|
||||
question := dns.Question{Name: name, Qtype: dns.TypeSRV, Qclass: dns.ClassINET}
|
||||
rec, extra, err := s.SRVRecords(question, name, 512, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed srv record lookup on service with fqdn %v", name)
|
||||
}
|
||||
|
||||
svcDomain := strings.Join([]string{testService, testNamespace, "svc", testDomain}, ".")
|
||||
assertARecordsMatchIPs(t, extra, eip)
|
||||
assertSRVRecordsMatchTarget(
|
||||
t, rec, fmt.Sprintf("%x.%v", util.HashServiceRecord(util.NewServiceRecord(eip, 0)), svcDomain))
|
||||
assertSRVRecordsMatchPort(t, rec, 8081)
|
||||
}
|
||||
|
||||
func TestSimpleExternalService(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
s := newExternalNameService()
|
||||
assert.NoError(t, kd.servicesStore.Add(s))
|
||||
|
||||
kd.newService(s)
|
||||
assertDNSForExternalService(t, kd, s)
|
||||
kd.removeService(s)
|
||||
assertNoDNSForExternalService(t, kd, s)
|
||||
}
|
||||
|
||||
func TestSimpleHeadlessService(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
s := newHeadlessService()
|
||||
assert.NoError(t, kd.servicesStore.Add(s))
|
||||
endpoints := newEndpoints(s, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2"), newSubsetWithOnePort("", 8080, "10.0.0.3", "10.0.0.4"))
|
||||
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.newService(s)
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
kd.removeService(s)
|
||||
assertNoDNSForHeadlessService(t, kd, s)
|
||||
}
|
||||
|
||||
func TestHeadlessServiceWithNamedPorts(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
service := newHeadlessService()
|
||||
// add service to store
|
||||
assert.NoError(t, kd.servicesStore.Add(service))
|
||||
endpoints := newEndpoints(service, newSubsetWithTwoPorts("http1", 80, "http2", 81, "10.0.0.1", "10.0.0.2"),
|
||||
newSubsetWithOnePort("https", 443, "10.0.0.3", "10.0.0.4"))
|
||||
|
||||
// We expect 10 records. 6 SRV records. 4 POD records.
|
||||
// add endpoints
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
|
||||
// add service
|
||||
kd.newService(service)
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
assertSRVForHeadlessService(t, kd, service, endpoints)
|
||||
|
||||
// reduce endpoints
|
||||
endpoints.Subsets = endpoints.Subsets[:1]
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
// We expect 6 records. 4 SRV records. 2 POD records.
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
assertSRVForHeadlessService(t, kd, service, endpoints)
|
||||
|
||||
kd.removeService(service)
|
||||
assertNoDNSForHeadlessService(t, kd, service)
|
||||
}
|
||||
|
||||
func TestHeadlessServiceEndpointsUpdate(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
service := newHeadlessService()
|
||||
// add service to store
|
||||
assert.NoError(t, kd.servicesStore.Add(service))
|
||||
|
||||
endpoints := newEndpoints(service, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2"))
|
||||
// add endpoints to store
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
|
||||
// add service
|
||||
kd.newService(service)
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
|
||||
// increase endpoints
|
||||
endpoints.Subsets = append(endpoints.Subsets,
|
||||
newSubsetWithOnePort("", 8080, "10.0.0.3", "10.0.0.4"),
|
||||
)
|
||||
// expected DNSRecords = 4
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
|
||||
// remove all endpoints
|
||||
endpoints.Subsets = []v1.EndpointSubset{}
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
assertNoDNSForHeadlessService(t, kd, service)
|
||||
|
||||
// remove service
|
||||
kd.removeService(service)
|
||||
assertNoDNSForHeadlessService(t, kd, service)
|
||||
}
|
||||
|
||||
func TestHeadlessServiceWithDelayedEndpointsAddition(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
// create service
|
||||
service := newHeadlessService()
|
||||
|
||||
// add service to store
|
||||
assert.NoError(t, kd.servicesStore.Add(service))
|
||||
|
||||
// add service
|
||||
kd.newService(service)
|
||||
assertNoDNSForHeadlessService(t, kd, service)
|
||||
|
||||
// create endpoints
|
||||
endpoints := newEndpoints(service, newSubsetWithOnePort("", 80, "10.0.0.1", "10.0.0.2"))
|
||||
|
||||
// add endpoints to store
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
|
||||
// add endpoints
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
|
||||
assertDNSForHeadlessService(t, kd, endpoints)
|
||||
|
||||
// remove service
|
||||
kd.removeService(service)
|
||||
assertNoDNSForHeadlessService(t, kd, service)
|
||||
}
|
||||
|
||||
// Verifies that a single record with host "a" is returned for query "q".
|
||||
func verifyRecord(q, a string, t *testing.T, kd *KubeDNS) {
|
||||
records, err := kd.Records(q, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, a, records[0].Host)
|
||||
}
|
||||
|
||||
const federatedServiceFQDN = "testservice.default.myfederation.svc.testcontinent-testreg-testzone.testcontinent-testreg.example.com."
|
||||
|
||||
// Verifies that querying KubeDNS for a headless federation service
|
||||
// returns the DNS hostname when a local service does not exist and
|
||||
// returns the endpoint IP when a local service exists.
|
||||
func TestFederationHeadlessService(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
kd.config.Federations = map[string]string{
|
||||
"myfederation": "example.com",
|
||||
}
|
||||
kd.kubeClient = fake.NewSimpleClientset(newNodes())
|
||||
|
||||
// Verify that querying for federation service returns a federation domain name.
|
||||
verifyRecord("testservice.default.myfederation.svc.cluster.local.",
|
||||
federatedServiceFQDN, t, kd)
|
||||
|
||||
// Add a local service without any endpoint.
|
||||
s := newHeadlessService()
|
||||
assert.NoError(t, kd.servicesStore.Add(s))
|
||||
kd.newService(s)
|
||||
|
||||
// Verify that querying for federation service still returns the federation domain name.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
federatedServiceFQDN, t, kd)
|
||||
|
||||
// Now add an endpoint.
|
||||
endpoints := newEndpoints(s, newSubsetWithOnePort("", 80, "10.0.0.1"))
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.updateService(s, s)
|
||||
|
||||
// Verify that querying for federation service returns the local service domain name this time.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
"testservice.default.svc.cluster.local.", t, kd)
|
||||
|
||||
// Delete the endpoint.
|
||||
endpoints.Subsets = []v1.EndpointSubset{}
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
kd.updateService(s, s)
|
||||
|
||||
// Verify that querying for federation service returns the federation domain name again.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
federatedServiceFQDN, t, kd)
|
||||
}
|
||||
|
||||
// Verifies that querying KubeDNS for a federation service returns the
|
||||
// DNS hostname if no endpoint exists and returns the local cluster IP
|
||||
// if endpoints exist.
|
||||
func TestFederationService(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
kd.config.Federations = map[string]string{
|
||||
"myfederation": "example.com",
|
||||
}
|
||||
kd.kubeClient = fake.NewSimpleClientset(newNodes())
|
||||
|
||||
// Verify that querying for federation service returns the federation domain name.
|
||||
verifyRecord("testservice.default.myfederation.svc.cluster.local.",
|
||||
federatedServiceFQDN, t, kd)
|
||||
|
||||
// Add a local service without any endpoint.
|
||||
s := newService(testNamespace, testService, "1.2.3.4", "", 80)
|
||||
assert.NoError(t, kd.servicesStore.Add(s))
|
||||
kd.newService(s)
|
||||
|
||||
// Verify that querying for federation service still returns the federation domain name.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
federatedServiceFQDN, t, kd)
|
||||
|
||||
// Now add an endpoint.
|
||||
endpoints := newEndpoints(s, newSubsetWithOnePort("", 80, "10.0.0.1"))
|
||||
assert.NoError(t, kd.endpointsStore.Add(endpoints))
|
||||
kd.updateService(s, s)
|
||||
|
||||
// Verify that querying for federation service returns the local service domain name this time.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
"testservice.default.svc.cluster.local.", t, kd)
|
||||
|
||||
// Remove the endpoint.
|
||||
endpoints.Subsets = []v1.EndpointSubset{}
|
||||
kd.handleEndpointAdd(endpoints)
|
||||
kd.updateService(s, s)
|
||||
|
||||
// Verify that querying for federation service returns the federation domain name again.
|
||||
verifyRecord(getFederationServiceFQDN(kd, s, "myfederation"),
|
||||
federatedServiceFQDN, t, kd)
|
||||
}
|
||||
|
||||
func TestFederationQueryWithoutCache(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
kd.config.Federations = map[string]string{
|
||||
"myfederation": "example.com",
|
||||
"secondfederation": "second.example.com",
|
||||
}
|
||||
kd.kubeClient = fake.NewSimpleClientset(newNodes())
|
||||
|
||||
testValidFederationQueries(t, kd)
|
||||
testInvalidFederationQueries(t, kd)
|
||||
}
|
||||
|
||||
func TestFederationQueryWithCache(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
kd.config.Federations = map[string]string{
|
||||
"myfederation": "example.com",
|
||||
"secondfederation": "second.example.com",
|
||||
}
|
||||
|
||||
// Add a node to the cache.
|
||||
nodeList := newNodes()
|
||||
if err := kd.nodesStore.Add(&nodeList.Items[1]); err != nil {
|
||||
t.Errorf("failed to add the node to the cache: %v", err)
|
||||
}
|
||||
|
||||
testValidFederationQueries(t, kd)
|
||||
testInvalidFederationQueries(t, kd)
|
||||
}
|
||||
|
||||
func testValidFederationQueries(t *testing.T, kd *KubeDNS) {
|
||||
queries := []struct {
|
||||
q string
|
||||
a string
|
||||
}{
|
||||
// Federation suffix is just a domain.
|
||||
{
|
||||
q: "mysvc.myns.myfederation.svc.cluster.local.",
|
||||
a: "mysvc.myns.myfederation.svc.testcontinent-testreg-testzone.testcontinent-testreg.example.com.",
|
||||
},
|
||||
// Federation suffix is a subdomain.
|
||||
{
|
||||
q: "secsvc.default.secondfederation.svc.cluster.local.",
|
||||
a: "secsvc.default.secondfederation.svc.testcontinent-testreg-testzone.testcontinent-testreg.second.example.com.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
verifyRecord(query.q, query.a, t, kd)
|
||||
}
|
||||
}
|
||||
|
||||
func testInvalidFederationQueries(t *testing.T, kd *KubeDNS) {
|
||||
noAnswerQueries := []string{
|
||||
"mysvc.myns.svc.cluster.local.",
|
||||
"mysvc.default.nofederation.svc.cluster.local.",
|
||||
}
|
||||
for _, q := range noAnswerQueries {
|
||||
records, err := kd.Records(q, false)
|
||||
if err == nil {
|
||||
t.Errorf("expected not found error, got nil")
|
||||
}
|
||||
if etcdErr, ok := err.(etcd.Error); !ok || etcdErr.Code != etcd.ErrorCodeKeyNotFound {
|
||||
t.Errorf("expected not found error, got %v", err)
|
||||
}
|
||||
assert.Equal(t, 0, len(records))
|
||||
}
|
||||
}
|
||||
|
||||
func checkConfigEqual(t *testing.T, kd *KubeDNS, expected *config.Config) {
|
||||
const timeout = time.Duration(5)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
ok := false
|
||||
|
||||
for time.Since(start) < timeout*time.Second {
|
||||
kd.configLock.RLock()
|
||||
isEqual := reflect.DeepEqual(expected.Federations, kd.config.Federations)
|
||||
kd.configLock.RUnlock()
|
||||
|
||||
if isEqual {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Errorf("Federations should be %v, got %v",
|
||||
expected.Federations, kd.config.Federations)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigSync(t *testing.T) {
|
||||
kd := newKubeDNS()
|
||||
mockSync := config.NewMockSync(
|
||||
&config.Config{Federations: make(map[string]string)}, nil)
|
||||
kd.configSync = mockSync
|
||||
|
||||
kd.startConfigMapSync()
|
||||
|
||||
checkConfigEqual(t, kd, &config.Config{Federations: make(map[string]string)})
|
||||
// update
|
||||
mockSync.Chan <- &config.Config{Federations: map[string]string{"name1": "domain1"}}
|
||||
checkConfigEqual(t, kd, &config.Config{Federations: map[string]string{"name1": "domain1"}})
|
||||
// update
|
||||
mockSync.Chan <- &config.Config{Federations: map[string]string{"name2": "domain2"}}
|
||||
checkConfigEqual(t, kd, &config.Config{Federations: map[string]string{"name2": "domain2"}})
|
||||
}
|
||||
|
||||
func TestConfigSyncInitialMap(t *testing.T) {
|
||||
// start with different initial map
|
||||
kd := newKubeDNS()
|
||||
mockSync := config.NewMockSync(
|
||||
&config.Config{Federations: map[string]string{"name3": "domain3"}}, nil)
|
||||
kd.configSync = mockSync
|
||||
|
||||
kd.startConfigMapSync()
|
||||
checkConfigEqual(t, kd, &config.Config{Federations: map[string]string{"name3": "domain3"}})
|
||||
}
|
||||
|
||||
func newNodes() *v1.NodeList {
|
||||
return &v1.NodeList{
|
||||
Items: []v1.Node{
|
||||
// Node without annotation.
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "testnode-0",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "testnode-1",
|
||||
Labels: map[string]string{
|
||||
// Note: The zone name here is an arbitrary string and doesn't exactly follow the
|
||||
// format used by the cloud providers to name their zones. But that shouldn't matter
|
||||
// for these tests here.
|
||||
metav1.LabelZoneFailureDomain: "testcontinent-testreg-testzone",
|
||||
metav1.LabelZoneRegion: "testcontinent-testreg",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newService(namespace, serviceName, clusterIP, portName string, portNumber int32) *v1.Service {
|
||||
service := v1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: serviceName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: clusterIP,
|
||||
Ports: []v1.ServicePort{
|
||||
{Port: portNumber, Name: portName, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
}
|
||||
return &service
|
||||
}
|
||||
|
||||
func newExternalNameService() *v1.Service {
|
||||
service := v1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: testService,
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "None",
|
||||
Type: v1.ServiceTypeExternalName,
|
||||
ExternalName: testExternalName,
|
||||
Ports: []v1.ServicePort{
|
||||
{Port: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
return &service
|
||||
}
|
||||
|
||||
func newHeadlessService() *v1.Service {
|
||||
service := v1.Service{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: testService,
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "None",
|
||||
Ports: []v1.ServicePort{
|
||||
{Port: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
return &service
|
||||
}
|
||||
|
||||
func newEndpoints(service *v1.Service, subsets ...v1.EndpointSubset) *v1.Endpoints {
|
||||
endpoints := v1.Endpoints{
|
||||
ObjectMeta: service.ObjectMeta,
|
||||
Subsets: []v1.EndpointSubset{},
|
||||
}
|
||||
|
||||
endpoints.Subsets = append(endpoints.Subsets, subsets...)
|
||||
return &endpoints
|
||||
}
|
||||
|
||||
func newSubsetWithOnePort(portName string, port int32, ips ...string) v1.EndpointSubset {
|
||||
return newSubsetWithOnePortWithHostname(portName, port, false, ips...)
|
||||
}
|
||||
|
||||
func newSubsetWithOnePortWithHostname(portName string, port int32, addHostname bool, ips ...string) v1.EndpointSubset {
|
||||
subset := newSubset()
|
||||
subset.Ports = append(subset.Ports, v1.EndpointPort{Port: port, Name: portName, Protocol: "TCP"})
|
||||
for i, ip := range ips {
|
||||
var hostname string
|
||||
if addHostname {
|
||||
hostname = fmt.Sprintf("ep-%d", i)
|
||||
}
|
||||
subset.Addresses = append(subset.Addresses, v1.EndpointAddress{IP: ip, Hostname: hostname})
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
func newSubsetWithTwoPorts(portName1 string, portNumber1 int32, portName2 string, portNumber2 int32, ips ...string) v1.EndpointSubset {
|
||||
subset := newSubsetWithOnePort(portName1, portNumber1, ips...)
|
||||
subset.Ports = append(subset.Ports, v1.EndpointPort{Port: portNumber2, Name: portName2, Protocol: "TCP"})
|
||||
return subset
|
||||
}
|
||||
|
||||
func newSubset() v1.EndpointSubset {
|
||||
subset := v1.EndpointSubset{
|
||||
Addresses: []v1.EndpointAddress{},
|
||||
Ports: []v1.EndpointPort{},
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
func assertSRVForHeadlessService(t *testing.T, kd *KubeDNS, s *v1.Service, e *v1.Endpoints) {
|
||||
for _, subset := range e.Subsets {
|
||||
for _, port := range subset.Ports {
|
||||
records, err := kd.Records(getSRVFQDN(kd, s, port.Name), false)
|
||||
require.NoError(t, err)
|
||||
assertRecordPortsMatchPort(t, port.Port, records)
|
||||
assertCNameRecordsMatchEndpointIPs(t, kd, subset.Addresses, records)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertDNSForHeadlessService(t *testing.T, kd *KubeDNS, e *v1.Endpoints) {
|
||||
records, err := kd.Records(getEndpointsFQDN(kd, e), false)
|
||||
require.NoError(t, err)
|
||||
endpoints := map[string]bool{}
|
||||
for _, subset := range e.Subsets {
|
||||
for _, endpointAddress := range subset.Addresses {
|
||||
endpoints[endpointAddress.IP] = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, len(endpoints), len(records))
|
||||
for _, record := range records {
|
||||
_, found := endpoints[record.Host]
|
||||
assert.True(t, found)
|
||||
}
|
||||
}
|
||||
|
||||
func assertDNSForExternalService(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
records, err := kd.Records(getServiceFQDN(kd.domain, s), false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, testExternalName, records[0].Host)
|
||||
}
|
||||
|
||||
func assertRecordPortsMatchPort(t *testing.T, port int32, records []skymsg.Service) {
|
||||
for _, record := range records {
|
||||
assert.Equal(t, port, int32(record.Port))
|
||||
}
|
||||
}
|
||||
|
||||
func assertCNameRecordsMatchEndpointIPs(t *testing.T, kd *KubeDNS, e []v1.EndpointAddress, records []skymsg.Service) {
|
||||
endpoints := map[string]bool{}
|
||||
for _, endpointAddress := range e {
|
||||
endpoints[endpointAddress.IP] = true
|
||||
}
|
||||
assert.Equal(t, len(e), len(records), "unexpected record count")
|
||||
for _, record := range records {
|
||||
_, found := endpoints[getIPForCName(t, kd, record.Host)]
|
||||
assert.True(t, found, "Did not find endpoint with address:%s", record.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func getIPForCName(t *testing.T, kd *KubeDNS, cname string) string {
|
||||
records, err := kd.Records(cname, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records), "Could not get IP for CNAME record for %s", cname)
|
||||
assert.NotNil(t, net.ParseIP(records[0].Host), "Invalid IP address %q", records[0].Host)
|
||||
return records[0].Host
|
||||
}
|
||||
|
||||
func assertNoDNSForHeadlessService(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
records, err := kd.Records(getServiceFQDN(kd.domain, s), false)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0, len(records))
|
||||
}
|
||||
|
||||
func assertNoDNSForExternalService(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
records, err := kd.Records(getServiceFQDN(kd.domain, s), false)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0, len(records))
|
||||
}
|
||||
|
||||
func assertSRVForNamedPort(t *testing.T, kd *KubeDNS, s *v1.Service, portName string) {
|
||||
records, err := kd.Records(getSRVFQDN(kd, s, portName), false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, getServiceFQDN(kd.domain, s), records[0].Host)
|
||||
}
|
||||
|
||||
func assertNoSRVForNamedPort(t *testing.T, kd *KubeDNS, s *v1.Service, portName string) {
|
||||
records, err := kd.Records(getSRVFQDN(kd, s, portName), false)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0, len(records))
|
||||
}
|
||||
|
||||
func assertNoDNSForClusterIP(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
serviceFQDN := getServiceFQDN(kd.domain, s)
|
||||
queries := getEquivalentQueries(serviceFQDN, s.Namespace)
|
||||
for _, query := range queries {
|
||||
records, err := kd.Records(query, false)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0, len(records))
|
||||
}
|
||||
}
|
||||
|
||||
func assertDNSForClusterIP(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
serviceFQDN := getServiceFQDN(kd.domain, s)
|
||||
queries := getEquivalentQueries(serviceFQDN, s.Namespace)
|
||||
for _, query := range queries {
|
||||
records, err := kd.Records(query, false)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(records))
|
||||
assert.Equal(t, s.Spec.ClusterIP, records[0].Host)
|
||||
}
|
||||
}
|
||||
|
||||
func assertReverseRecord(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
segments := util.ReverseArray(strings.Split(s.Spec.ClusterIP, "."))
|
||||
reverseLookup := fmt.Sprintf("%s%s", strings.Join(segments, "."), util.ArpaSuffix)
|
||||
reverseRecord, err := kd.ReverseRecord(reverseLookup)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, getServiceFQDN(kd.domain, s), reverseRecord.Host)
|
||||
}
|
||||
|
||||
func assertNoReverseRecord(t *testing.T, kd *KubeDNS, s *v1.Service) {
|
||||
segments := util.ReverseArray(strings.Split(s.Spec.ClusterIP, "."))
|
||||
reverseLookup := fmt.Sprintf("%s%s", strings.Join(segments, "."), util.ArpaSuffix)
|
||||
reverseRecord, err := kd.ReverseRecord(reverseLookup)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, reverseRecord)
|
||||
}
|
||||
|
||||
func getEquivalentQueries(serviceFQDN, namespace string) []string {
|
||||
return []string{
|
||||
serviceFQDN,
|
||||
strings.Replace(serviceFQDN, ".svc.", ".*.", 1),
|
||||
strings.Replace(serviceFQDN, namespace, "*", 1),
|
||||
strings.Replace(strings.Replace(serviceFQDN, namespace, "*", 1), ".svc.", ".*.", 1),
|
||||
"*." + serviceFQDN,
|
||||
}
|
||||
}
|
||||
|
||||
func getFederationServiceFQDN(kd *KubeDNS, s *v1.Service, federationName string) string {
|
||||
return fmt.Sprintf("%s.%s.%s.svc.%s", s.Name, s.Namespace, federationName, kd.domain)
|
||||
}
|
||||
|
||||
func getEndpointsFQDN(kd *KubeDNS, e *v1.Endpoints) string {
|
||||
return fmt.Sprintf("%s.%s.svc.%s", e.Name, e.Namespace, kd.domain)
|
||||
}
|
||||
|
||||
func getSRVFQDN(kd *KubeDNS, s *v1.Service, portName string) string {
|
||||
return fmt.Sprintf("_%s._tcp.%s.%s.svc.%s", portName, s.Name, s.Namespace, kd.domain)
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package DNS provides a backend for the skydns DNS server started by the
|
||||
// kubedns cluster addon. It exposes the 2 interface method: Records and
|
||||
// ReverseRecord, which skydns invokes according to the DNS queries it
|
||||
// receives. It serves these records by consulting an in memory tree
|
||||
// populated with Kubernetes Services and Endpoints received from the Kubernetes
|
||||
// API server.
|
||||
package dns // import "k8s.io/kubernetes/pkg/dns"
|
@ -1,37 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["federation.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/validation:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["federation_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/stretchr/testify/assert"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Fed contains federation specific DNS code.
|
||||
package fed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
)
|
||||
|
||||
var ErrExpectedKeyEqualsValue = errors.New("invalid format, must be key=value")
|
||||
|
||||
// ParseFederationsFlag parses the federations command line flag. The
|
||||
// flag is a comma-separated list of zero or more "name=label" pairs,
|
||||
// e.g. "a=b,c=d".
|
||||
func ParseFederationsFlag(str string, federations map[string]string) error {
|
||||
if strings.TrimSpace(str) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, val := range strings.Split(str, ",") {
|
||||
splits := strings.SplitN(strings.TrimSpace(val), "=", 2)
|
||||
if len(splits) != 2 {
|
||||
return ErrExpectedKeyEqualsValue
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(splits[0])
|
||||
domain := strings.TrimSpace(splits[1])
|
||||
|
||||
if err := ValidateName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ValidateDomain(domain); err != nil {
|
||||
return err
|
||||
}
|
||||
federations[name] = domain
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateName checks the validity of a federation name.
|
||||
func ValidateName(name string) error {
|
||||
if errs := validation.IsDNS1123Label(name); len(errs) != 0 {
|
||||
return fmt.Errorf("%q not a valid federation name: %q", name, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateDomain checks the validity of a federation label.
|
||||
func ValidateDomain(name string) error {
|
||||
// The federation domain name need not strictly be domain names, we
|
||||
// accept valid dns names with subdomain components.
|
||||
if errs := validation.IsDNS1123Subdomain(name); len(errs) != 0 {
|
||||
return fmt.Errorf("%q not a valid domain name: %q", name, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fed
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseFederationsFlag(t *testing.T) {
|
||||
type TestCase struct {
|
||||
input string
|
||||
hasError bool
|
||||
expected map[string]string
|
||||
}
|
||||
|
||||
for _, testCase := range []TestCase{
|
||||
{input: "", expected: make(map[string]string)},
|
||||
{input: "a=b", expected: map[string]string{"a": "b"}},
|
||||
{input: "a=b,cc=dd", expected: map[string]string{"a": "b", "cc": "dd"}},
|
||||
{input: "abc=d.e.f", expected: map[string]string{"abc": "d.e.f"}},
|
||||
|
||||
{input: "ccdd", hasError: true},
|
||||
{input: "a=b,ccdd", hasError: true},
|
||||
{input: "-", hasError: true},
|
||||
{input: "a.b.c=d.e.f", hasError: true},
|
||||
} {
|
||||
output := make(map[string]string)
|
||||
err := ParseFederationsFlag(testCase.input, output)
|
||||
|
||||
if !testCase.hasError {
|
||||
assert.Nil(t, err, "unexpected err", testCase)
|
||||
assert.True(t, reflect.DeepEqual(
|
||||
testCase.expected, output), output, testCase)
|
||||
} else {
|
||||
assert.NotNil(t, err, testCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateName(t *testing.T) {
|
||||
// More complete testing is done in validation.IsDNS1123Label. These
|
||||
// tests are to catch issues specific to the implementation of
|
||||
// kube-dns.
|
||||
assert.NotNil(t, ValidateName(""))
|
||||
assert.NotNil(t, ValidateName("."))
|
||||
assert.NotNil(t, ValidateName("ab.cd"))
|
||||
assert.Nil(t, ValidateName("abcd"))
|
||||
}
|
||||
|
||||
func TestValidateDomain(t *testing.T) {
|
||||
// More complete testing is done in
|
||||
// validation.IsDNS1123Subdomain. These tests are to catch issues
|
||||
// specific to the implementation of kube-dns.
|
||||
assert.NotNil(t, ValidateDomain(""))
|
||||
assert.NotNil(t, ValidateDomain("."))
|
||||
assert.Nil(t, ValidateDomain("ab.cd"))
|
||||
assert.Nil(t, ValidateDomain("abcd"))
|
||||
assert.Nil(t, ValidateDomain("a.b.c.d"))
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["treecache.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/skynetservices/skydns/msg"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["treecache_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/skynetservices/skydns/msg"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,213 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package treecache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
skymsg "github.com/skynetservices/skydns/msg"
|
||||
)
|
||||
|
||||
type TreeCache interface {
|
||||
// GetEntry with the given key for the given path.
|
||||
GetEntry(key string, path ...string) (interface{}, bool)
|
||||
|
||||
// Get a list of values including wildcards labels (e.g. "*").
|
||||
GetValuesForPathWithWildcards(path ...string) []*skymsg.Service
|
||||
|
||||
// SetEntry creates the entire path if it doesn't already exist in
|
||||
// the cache, then sets the given service record under the given
|
||||
// key. The path this entry would have occupied in an etcd datastore
|
||||
// is computed from the given fqdn and stored as the "Key" of the
|
||||
// skydns service; this is only required because skydns expects the
|
||||
// service record to contain a key in a specific format (presumably
|
||||
// for legacy compatibility). Note that the fqnd string typically
|
||||
// contains both the key and all elements in the path.
|
||||
SetEntry(key string, val *skymsg.Service, fqdn string, path ...string)
|
||||
|
||||
// SetSubCache inserts the given subtree under the given
|
||||
// path:key. Usually the key is the name of a Kubernetes Service,
|
||||
// and the path maps to the cluster subdomains matching the Service.
|
||||
SetSubCache(key string, subCache TreeCache, path ...string)
|
||||
|
||||
// DeletePath removes all entries associated with a given path.
|
||||
DeletePath(path ...string) bool
|
||||
|
||||
// Serialize dumps a JSON representation of the cache.
|
||||
Serialize() (string, error)
|
||||
}
|
||||
|
||||
type treeCache struct {
|
||||
ChildNodes map[string]*treeCache
|
||||
Entries map[string]interface{}
|
||||
}
|
||||
|
||||
func NewTreeCache() TreeCache {
|
||||
return &treeCache{
|
||||
ChildNodes: make(map[string]*treeCache),
|
||||
Entries: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *treeCache) Serialize() (string, error) {
|
||||
prettyJSON, err := json.MarshalIndent(cache, "", "\t")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(prettyJSON), nil
|
||||
}
|
||||
|
||||
func (cache *treeCache) SetEntry(key string, val *skymsg.Service, fqdn string, path ...string) {
|
||||
// TODO: Consolidate setEntry and setSubCache into a single method with a
|
||||
// type switch.
|
||||
// TODO: Instead of passing the fqdn as an argument, we can reconstruct
|
||||
// it from the path, provided callers always pass the full path to the
|
||||
// object. This is currently *not* the case, since callers first create
|
||||
// a new, empty node, populate it, then parent it under the right path.
|
||||
// So we don't know the full key till the final parenting operation.
|
||||
node := cache.ensureChildNode(path...)
|
||||
|
||||
// This key is used to construct the "target" for SRV record lookups.
|
||||
// For normal service/endpoint lookups, this will result in a key like:
|
||||
// /skydns/local/cluster/svc/svcNS/svcName/record-hash
|
||||
// but for headless services that govern pods requesting a specific
|
||||
// hostname (as used by petset), this will end up being:
|
||||
// /skydns/local/cluster/svc/svcNS/svcName/pod-hostname
|
||||
val.Key = skymsg.Path(fqdn)
|
||||
node.Entries[key] = val
|
||||
}
|
||||
|
||||
func (cache *treeCache) getSubCache(path ...string) *treeCache {
|
||||
childCache := cache
|
||||
for _, subpath := range path {
|
||||
childCache = childCache.ChildNodes[subpath]
|
||||
if childCache == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return childCache
|
||||
}
|
||||
|
||||
func (cache *treeCache) SetSubCache(key string, subCache TreeCache, path ...string) {
|
||||
node := cache.ensureChildNode(path...)
|
||||
node.ChildNodes[key] = subCache.(*treeCache)
|
||||
}
|
||||
|
||||
func (cache *treeCache) GetEntry(key string, path ...string) (interface{}, bool) {
|
||||
childNode := cache.getSubCache(path...)
|
||||
if childNode == nil {
|
||||
return nil, false
|
||||
}
|
||||
val, ok := childNode.Entries[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (cache *treeCache) GetValuesForPathWithWildcards(path ...string) []*skymsg.Service {
|
||||
retval := []*skymsg.Service{}
|
||||
nodesToExplore := []*treeCache{cache}
|
||||
for idx, subpath := range path {
|
||||
nextNodesToExplore := []*treeCache{}
|
||||
if idx == len(path)-1 {
|
||||
// if path ends on an entry, instead of a child node, add the entry
|
||||
for _, node := range nodesToExplore {
|
||||
if subpath == "*" {
|
||||
nextNodesToExplore = append(nextNodesToExplore, node)
|
||||
} else {
|
||||
if val, ok := node.Entries[subpath]; ok {
|
||||
retval = append(retval, val.(*skymsg.Service))
|
||||
} else {
|
||||
childNode := node.ChildNodes[subpath]
|
||||
if childNode != nil {
|
||||
nextNodesToExplore = append(nextNodesToExplore, childNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nodesToExplore = nextNodesToExplore
|
||||
break
|
||||
}
|
||||
|
||||
if subpath == "*" {
|
||||
for _, node := range nodesToExplore {
|
||||
for subkey, subnode := range node.ChildNodes {
|
||||
if !strings.HasPrefix(subkey, "_") {
|
||||
nextNodesToExplore = append(nextNodesToExplore, subnode)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, node := range nodesToExplore {
|
||||
childNode := node.ChildNodes[subpath]
|
||||
if childNode != nil {
|
||||
nextNodesToExplore = append(nextNodesToExplore, childNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
nodesToExplore = nextNodesToExplore
|
||||
}
|
||||
|
||||
for _, node := range nodesToExplore {
|
||||
for _, val := range node.Entries {
|
||||
retval = append(retval, val.(*skymsg.Service))
|
||||
}
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
func (cache *treeCache) DeletePath(path ...string) bool {
|
||||
if len(path) == 0 {
|
||||
return false
|
||||
}
|
||||
if parentNode := cache.getSubCache(path[:len(path)-1]...); parentNode != nil {
|
||||
name := path[len(path)-1]
|
||||
if _, ok := parentNode.ChildNodes[name]; ok {
|
||||
delete(parentNode.ChildNodes, name)
|
||||
return true
|
||||
}
|
||||
// ExternalName services are stored with their name as the leaf key
|
||||
if _, ok := parentNode.Entries[name]; ok {
|
||||
delete(parentNode.Entries, name)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (cache *treeCache) appendValues(recursive bool, ref [][]interface{}) {
|
||||
for _, value := range cache.Entries {
|
||||
ref[0] = append(ref[0], value)
|
||||
}
|
||||
if recursive {
|
||||
for _, node := range cache.ChildNodes {
|
||||
node.appendValues(recursive, ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *treeCache) ensureChildNode(path ...string) *treeCache {
|
||||
childNode := cache
|
||||
for _, subpath := range path {
|
||||
newNode, ok := childNode.ChildNodes[subpath]
|
||||
if !ok {
|
||||
newNode = NewTreeCache().(*treeCache)
|
||||
childNode.ChildNodes[subpath] = newNode
|
||||
}
|
||||
childNode = newNode
|
||||
}
|
||||
return childNode
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package treecache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/skynetservices/skydns/msg"
|
||||
)
|
||||
|
||||
func TestTreeCache(t *testing.T) {
|
||||
tc := NewTreeCache()
|
||||
|
||||
{
|
||||
_, ok := tc.GetEntry("key1", "p1", "p2")
|
||||
if ok {
|
||||
t.Errorf("key should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
checkExists := func(key string, expectedSvc *msg.Service, path ...string) {
|
||||
svc, ok := tc.GetEntry(key, path...)
|
||||
if !ok {
|
||||
t.Fatalf("key %v should exist", key)
|
||||
}
|
||||
if svc := svc.(*msg.Service); svc != nil {
|
||||
if svc != expectedSvc {
|
||||
t.Errorf("value is not correct (%v != %v)", svc, expectedSvc)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("entry is not of the right type: %T", svc)
|
||||
}
|
||||
}
|
||||
setEntryTC := []struct {
|
||||
key string
|
||||
svc *msg.Service
|
||||
fqdn string
|
||||
path []string
|
||||
}{
|
||||
{"key1", &msg.Service{}, "key1.p2.p1.", []string{"p1", "p2"}},
|
||||
{"key2", &msg.Service{}, "key2.p2.p1.", []string{"p1", "p2"}},
|
||||
{"key3", &msg.Service{}, "key3.p2.p1.", []string{"p1", "p3"}},
|
||||
}
|
||||
|
||||
for _, testCase := range setEntryTC {
|
||||
tc.SetEntry(testCase.key, testCase.svc, testCase.fqdn, testCase.path...)
|
||||
checkExists(testCase.key, testCase.svc, testCase.path...)
|
||||
}
|
||||
|
||||
wildcardTC := []struct {
|
||||
path []string
|
||||
count int
|
||||
}{
|
||||
{[]string{"p1"}, 0},
|
||||
{[]string{"p1", "p2"}, 2},
|
||||
{[]string{"p1", "p3"}, 1},
|
||||
{[]string{"p1", "p2", "key1"}, 1},
|
||||
{[]string{"p1", "p2", "key2"}, 1},
|
||||
{[]string{"p1", "p2", "key3"}, 0},
|
||||
{[]string{"p1", "p3", "key3"}, 1},
|
||||
{[]string{"p1", "p2", "*"}, 2},
|
||||
{[]string{"p1", "*", "*"}, 3},
|
||||
}
|
||||
|
||||
for _, testCase := range wildcardTC {
|
||||
services := tc.GetValuesForPathWithWildcards(testCase.path...)
|
||||
if len(services) != testCase.count {
|
||||
t.Fatalf("Expected %v services for path %v, got %v",
|
||||
testCase.count, testCase.path, len(services))
|
||||
}
|
||||
}
|
||||
|
||||
// Delete some paths
|
||||
if !tc.DeletePath("p1", "p2") {
|
||||
t.Fatal("should delete path p2.p1.")
|
||||
}
|
||||
if _, ok := tc.GetEntry("key3", "p1", "p3"); !ok {
|
||||
t.Error("should not affect p3.p1.")
|
||||
}
|
||||
if tc.DeletePath("p1", "p2") {
|
||||
t.Fatalf("should not be able to delete p2.p1")
|
||||
}
|
||||
if !tc.DeletePath("p1", "p3") {
|
||||
t.Fatalf("should be able to delete p3.p1")
|
||||
}
|
||||
if tc.DeletePath("p1", "p3") {
|
||||
t.Fatalf("should not be able to delete p3.t1")
|
||||
}
|
||||
|
||||
for _, testCase := range []struct {
|
||||
k string
|
||||
p []string
|
||||
}{
|
||||
{"key1", []string{"p1", "p2"}},
|
||||
{"key2", []string{"p1", "p2"}},
|
||||
{"key3", []string{"p1", "p3"}},
|
||||
} {
|
||||
if _, ok := tc.GetEntry(testCase.k, testCase.p...); ok {
|
||||
t.Error("path should not exist")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCacheSetSubCache(t *testing.T) {
|
||||
tc := NewTreeCache()
|
||||
|
||||
m := &msg.Service{}
|
||||
|
||||
branch := NewTreeCache()
|
||||
branch.SetEntry("key1", m, "key", "p2")
|
||||
|
||||
tc.SetSubCache("p1", branch, "p0")
|
||||
|
||||
if _, ok := tc.GetEntry("key1", "p0", "p1", "p2"); !ok {
|
||||
t.Errorf("should be able to get entry p0.p1.p2.key1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCacheSerialize(t *testing.T) {
|
||||
tc := NewTreeCache()
|
||||
tc.SetEntry("key1", &msg.Service{}, "key1.p2.p1.", "p1", "p2")
|
||||
|
||||
const expected = `{
|
||||
"ChildNodes": {
|
||||
"p1": {
|
||||
"ChildNodes": {
|
||||
"p2": {
|
||||
"ChildNodes": {},
|
||||
"Entries": {
|
||||
"key1": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Entries": {}
|
||||
}
|
||||
},
|
||||
"Entries": {}
|
||||
}`
|
||||
|
||||
actual, err := tc.Serialize()
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
if actual != expected {
|
||||
t.Errorf("expected %q, got %q", expected, actual)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/skynetservices/skydns/msg",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/skynetservices/skydns/msg"
|
||||
)
|
||||
|
||||
const (
|
||||
// ArpaSuffix is the standard suffix for PTR IP reverse lookups.
|
||||
ArpaSuffix = ".in-addr.arpa."
|
||||
// defaultPriority used for service records
|
||||
defaultPriority = 10
|
||||
// defaultWeight used for service records
|
||||
defaultWeight = 10
|
||||
// defaultTTL used for service records
|
||||
defaultTTL = 30
|
||||
)
|
||||
|
||||
// extractIP turns a standard PTR reverse record lookup name
|
||||
// into an IP address
|
||||
func ExtractIP(reverseName string) (string, bool) {
|
||||
if !strings.HasSuffix(reverseName, ArpaSuffix) {
|
||||
return "", false
|
||||
}
|
||||
search := strings.TrimSuffix(reverseName, ArpaSuffix)
|
||||
|
||||
// reverse the segments and then combine them
|
||||
segments := ReverseArray(strings.Split(search, "."))
|
||||
return strings.Join(segments, "."), true
|
||||
}
|
||||
|
||||
// ReverseArray reverses an array.
|
||||
func ReverseArray(arr []string) []string {
|
||||
for i := 0; i < len(arr)/2; i++ {
|
||||
j := len(arr) - i - 1
|
||||
arr[i], arr[j] = arr[j], arr[i]
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Returns record in a format that SkyDNS understands.
|
||||
// Also return the hash of the record.
|
||||
func GetSkyMsg(ip string, port int) (*msg.Service, string) {
|
||||
msg := NewServiceRecord(ip, port)
|
||||
hash := HashServiceRecord(msg)
|
||||
glog.V(5).Infof("Constructed new DNS record: %s, hash:%s",
|
||||
fmt.Sprintf("%v", msg), hash)
|
||||
return msg, fmt.Sprintf("%x", hash)
|
||||
}
|
||||
|
||||
// NewServiceRecord creates a new service DNS message.
|
||||
func NewServiceRecord(ip string, port int) *msg.Service {
|
||||
return &msg.Service{
|
||||
Host: ip,
|
||||
Port: port,
|
||||
Priority: defaultPriority,
|
||||
Weight: defaultWeight,
|
||||
Ttl: defaultTTL,
|
||||
}
|
||||
}
|
||||
|
||||
// HashServiceRecord hashes the string representation of a DNS
|
||||
// message.
|
||||
func HashServiceRecord(msg *msg.Service) string {
|
||||
s := fmt.Sprintf("%v", msg)
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(s))
|
||||
return fmt.Sprintf("%x", h.Sum32())
|
||||
}
|
@ -75,7 +75,7 @@ func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _translationsKubectlDefaultLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x93\xcf\x6e\xd3\x4e\x10\xc7\x27\xbf\xf6\x77\xf1\x91\x33\x87\x41\x02\xa9\x08\x8d\x59\xbb\xa5\xaa\x5c\x82\x50\x4b\x8b\x22\x25\x6a\x54\x5c\xd4\x23\x1b\xef\xd4\x59\xb4\xd9\xb5\x76\xd7\x4d\x7b\xe4\x21\x78\x05\x2e\xf0\x16\xbc\x02\x67\x9e\x05\x39\x86\x14\x6e\xbd\xb4\x7b\xb0\xfc\x1d\x7d\xfc\x9d\x3f\x9e\xfd\xf9\x60\xf3\x33\x00\xc0\x06\x00\x3c\x04\x80\x1d\x00\xf8\x1f\x00\xc6\xd0\x9f\x0f\x00\x30\x02\x00\x09\x00\x5b\x03\x80\xaf\x00\xf0\x6d\x00\xf0\x63\x00\xf0\x18\x00\x3e\x6d\x00\x7c\x07\x80\x2f\x1b\x00\x83\xdf\x3e\x7f\xce\x7f\xdd\xe3\xac\x51\x32\x32\xc6\x39\xa3\xb4\xd6\x45\x19\xb5\xb3\x01\x9d\x45\x89\x9e\x83\x6b\x7d\xc5\x9b\xb7\x81\x60\x29\x63\x35\x47\xdd\x85\xcd\x35\x86\xb6\x69\x9c\x8f\xac\x3a\x4a\x5b\xa5\x2f\xb5\x6a\xa5\x59\xe3\x01\xa5\x55\x6b\x85\x95\x33\x86\xab\xde\x96\xf0\x89\xfa\x8b\x5b\xb2\x67\xbc\x70\xad\x55\x9b\x77\x9f\xe2\x1e\xba\x80\xa9\x77\x1f\xb9\x8a\x34\x52\xf4\x9e\x7d\xd0\xce\x16\x58\x73\x8c\x7c\x15\xa9\x76\xc4\x57\x72\xd1\x18\x0e\x34\x67\x63\x5c\x72\xca\x5d\x05\x34\x09\xb5\x56\x74\xd0\xd6\x81\x4a\x57\x60\x32\x3d\x29\xe9\xd0\xf3\xea\x4f\xd0\x1b\x19\xb9\xc0\x5c\x64\xdb\x94\xe5\x94\xe5\x98\x8b\x42\x6c\x3f\x13\x42\x88\x64\x7a\x42\xa7\x7c\xa9\xc3\x3f\xdc\xee\x8a\xdb\xc6\x3c\x2b\x5e\xec\x90\xd8\x13\x22\x19\xcb\x10\xa9\xf4\xd2\x06\x23\xa3\xf3\x05\x1e\x78\xb6\x4a\x5a\x3c\x68\xbd\x0d\xf8\x72\xd6\xcb\x54\xa5\xb3\x2e\xf0\xba\x5e\x48\x6d\xd2\xca\x2d\x5e\x25\x93\xd1\xe4\xe8\xa6\x95\x2c\x15\xc9\xa1\xb3\x91\x6d\xa4\xf2\xba\xe1\x02\xbb\xce\x9e\x37\x46\x6a\xbb\x8f\xd5\x5c\xfa\xc0\x71\x78\x56\x1e\xd3\xde\x0d\xd7\xe5\xbd\x60\x4f\x47\xb6\x72\x4a\xdb\xba\xc0\xbd\x99\x8e\xc9\x39\xbd\x65\xcb\xbe\x2f\x68\xea\x58\xe9\x88\x59\xba\x9b\x66\x22\x39\xa7\x5e\xd3\xbb\xd5\x84\x0f\x7b\xdf\x02\x7b\xe3\xb1\xb4\x75\x2b\x6b\xa6\x92\xe5\xa2\x1b\x97\x69\xbd\x34\x74\xec\xfc\x22\x14\x68\x9b\x95\x0c\xc3\x7c\x1f\xfb\xd7\xe1\x96\xc5\x47\x43\xcc\x9e\xee\xaf\x3f\x2d\x90\x6d\x72\xab\xfb\x71\x47\x4b\x83\x4b\x19\xee\x6f\x2b\x7f\x05\x00\x00\xff\xff\xaa\xa3\x15\xc3\x6a\x04\x00\x00")
|
||||
var _translationsKubectlDefaultLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\xc1\x6e\xd3\x40\x10\x75\x21\x5c\x7c\xe4\xcc\x61\x90\x40\x2a\x42\x13\xec\xa4\x54\x91\x43\x10\x4a\x68\x51\xa4\x44\x44\x25\x45\x1c\xd9\x78\xa7\xce\xa2\xf5\xae\xb5\xbb\x6e\xda\x23\x1f\xc1\x2f\x70\x81\xbf\xe0\x17\x38\xf3\x2d\x8c\x63\x48\xe1\xd6\x4b\x3b\xf2\xf3\xcc\xac\xdf\xbe\xd9\xd9\x5d\xff\xba\xdf\xf9\x12\xb1\xdd\x65\x3c\x60\x1c\x30\xee\x31\x66\x51\x6b\x1f\x19\x53\x86\x60\xec\xef\x45\xd1\x37\xf6\xdf\xd9\xff\x64\x3c\xe2\xf8\x33\x4f\xfc\xc1\xfe\x2b\xfb\xbd\x3f\x3a\x7f\xed\x4e\xf3\x3a\xad\xa4\x08\x04\x61\x4d\x20\x8c\xb1\x41\x04\x65\x8d\x07\x6b\x40\x80\x23\x6f\x6b\x97\x53\xe7\x3a\xa4\x68\x23\x42\xbe\x06\xd5\x0c\xeb\x4b\xf0\x75\x55\x59\x17\x48\x36\x2c\x65\xa4\x3a\x57\xb2\x16\x7a\x47\xf7\xac\x24\x77\x19\xe4\x56\x6b\xca\x5b\x59\x84\xc7\xf2\x1f\xde\x86\x1c\xc1\x99\xad\x8d\xec\xdc\x7c\x89\x5b\xe8\x22\x5a\x38\xfb\x89\x59\x38\x95\xf8\x9e\x9c\x67\x76\x06\x05\x85\x40\x17\x01\x0b\x8b\x74\x21\xca\x4a\x93\xc7\x35\x69\x6d\xe3\x13\x6a\x56\x80\x73\x5f\x28\x89\xe3\xba\xf0\xb8\xb4\x19\xc4\x8b\xb7\x4b\x9c\x38\xda\x9e\x04\xbe\xe6\xd3\xc9\xa0\x97\xa4\x7d\x4c\x7b\xfc\x70\x98\x25\xfd\xa7\x09\x1b\x13\xf1\x84\xce\x95\xff\x8f\x77\xb8\xe5\xf5\xa1\x97\x66\xcf\x0f\x30\x19\x30\x6f\x26\x7c\xc0\xa5\x13\xc6\x6b\x11\xac\xcb\x60\xec\xc8\x48\x61\x60\x5c\x3b\x6e\xe7\xc5\xaa\x4d\xbb\xb2\xbb\x6a\x06\x5e\x15\xa5\x50\xba\x9b\xdb\xf2\x65\x3c\x9f\xce\x8f\xae\x5a\x49\xbb\x49\x3c\xb1\x26\x90\x61\xbd\xcb\x8a\x0b\x36\x9d\x3d\xab\xb4\x50\x66\x08\xf9\x5a\x38\x4f\x61\x74\xba\x3c\xc6\xc1\x15\xaf\xa9\x7b\x46\x0e\x8f\x4c\x6e\xa5\x32\x45\x06\x83\x95\x0a\xf1\x07\x7c\x43\x86\x5c\xbb\xa0\x85\x25\xa9\x02\xeb\x1f\x76\xd3\x84\x3f\xb5\x39\xbe\xdb\xee\xf0\xa4\xd5\xcd\xa0\x15\x9e\x09\x53\xd4\xa2\x20\x5c\x92\x28\x9b\xed\xd2\xb5\x13\x1a\x8f\xad\x2b\x7d\x06\xa6\xda\xa6\x7e\xd4\x1b\x42\x1b\x8e\xf6\x0d\x3c\x1c\x41\xfa\x64\xb8\x9b\x9a\x01\x99\xf8\x5a\xff\xc7\x0d\x5d\x1a\xd8\x08\x7f\x7b\xb7\xf2\x77\x00\x00\x00\xff\xff\xaa\xa3\x15\xc3\x6a\x04\x00\x00")
|
||||
|
||||
func translationsKubectlDefaultLc_messagesK8sMoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -95,7 +95,7 @@ func translationsKubectlDefaultLc_messagesK8sMo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsKubectlDefaultLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\xdd\x6e\xd3\x4c\x10\xbd\xcf\x53\xcc\xe7\xe8\x93\x5a\xc1\x1a\x3b\x85\xaa\x72\x09\xa2\x0d\x6d\xa9\x68\xd5\x28\x75\x11\x12\x20\xb4\xf1\x4e\x9c\x85\xf5\xac\xb5\x33\xee\xcf\xdb\x23\xdb\x69\x43\x90\x90\x7a\xd3\xde\xcd\xce\x9e\x99\x33\x67\xf6\xec\x10\x72\x64\x01\x09\x9a\xd8\x69\xb1\x9e\x18\x16\x3e\x40\x43\x56\x40\x90\x85\xe3\xc1\x10\x26\xbe\xbe\x0b\xb6\x5c\x0a\x6c\x4d\xb6\x61\x94\xa4\xbb\x83\x21\xe4\x4b\xcb\xb0\xb0\x0e\xc1\x32\x18\xcb\x12\xec\xbc\x11\x34\xd0\x90\xc1\x00\xb2\x44\x60\x5d\x21\x38\x5b\x20\x31\x82\xe6\x2e\x37\x3d\x98\x7c\x3a\x38\x39\x82\x5a\x17\xbf\x74\x89\x6d\xfb\xe3\xd3\xd9\x65\x0e\x07\x57\xf9\xc7\x8b\x19\xcc\x03\x92\xd1\x14\x9b\x78\xde\x04\xe2\xf7\x65\xa5\xad\x8b\x0b\x5f\xbd\xec\x88\xe3\xc1\x70\x50\x71\x69\x0d\x44\x51\x1b\xb0\x84\x36\x8a\xa6\xc1\xff\xc4\x42\xd4\xa9\x51\x9f\x31\xb0\xf5\x94\x41\x89\x22\x78\x2b\xaa\xf4\x0a\x6f\x75\x55\x3b\x64\xb5\x44\xe7\xfc\x37\x8a\x06\xd1\x0c\x6b\x1f\x44\x9d\xb7\xcd\xd4\x61\x53\xb2\xca\x7d\x06\xdd\xd5\xf4\x22\x57\x93\x80\xdd\x3e\xd4\x07\x2d\x98\xb5\xdc\x3b\x2a\x1d\xa9\x74\x04\xa3\x24\x4b\x76\x5e\x24\x49\x92\xac\xc0\x6a\x86\xd7\x96\x37\xb0\xbb\x1d\x76\x07\x46\x69\xf6\xe6\xb5\x4a\xf6\x56\xd8\x33\xcd\xa2\xf2\xd5\xb2\x7d\xc8\xe0\xb0\x57\x0b\x87\xad\x56\x78\xfb\x4f\xf1\xef\xba\xf2\xf3\xd3\xf3\xa3\xb5\xbc\x34\xee\x9b\x4e\x3c\x09\x92\xa8\xfc\xae\xc6\x0c\x5a\xc5\xaf\x6a\xa7\x2d\xed\x43\xb1\xd4\x81\x51\xc6\x57\xf9\xb1\xda\xdb\xc4\xb6\x33\x2c\x30\xa8\x23\x2a\xbc\xb1\x54\x66\xb0\x37\xb7\xd2\x61\xbe\xa8\x13\x24\x0c\xfd\x80\x53\x8f\xc6\x0a\xa4\xf1\x6e\x9c\x26\xab\xeb\x3e\xa7\x2e\x7d\x13\x0a\x9c\xf4\x1c\x19\xac\x49\xce\x34\x95\x8d\x2e\x51\xe5\xa8\xab\xfb\x95\xba\x26\x68\xa7\x8e\x7d\xa8\x38\x03\xaa\xbb\x23\x8f\x47\xfb\xd0\x87\xe3\x2d\x82\xff\xc6\x90\x6e\xef\x6f\xb4\xc8\x00\xa9\x4d\xb4\x4f\x5d\xc8\xad\x40\x74\x55\x1b\x2d\xd8\x59\x49\x13\x79\x59\x99\xd6\x13\x68\x08\xc8\xdd\x4c\xd1\xbd\x45\x1e\x0d\xee\x6c\xf4\x38\xf4\x7a\x94\x68\x10\xdd\x68\x29\x96\xed\x07\xf0\xe4\xee\x80\x9b\xba\xf5\x14\x9a\xb6\xc6\x92\xb1\xd7\xd6\x34\xda\x3d\x14\x33\x68\x32\x0f\x27\x28\xbc\x73\x58\xf4\x24\x0a\xa2\x41\xf4\xbf\xf9\x03\x7a\x83\x01\x61\xe1\x1b\x32\xd1\xda\xf1\xcf\x4a\xf8\xa3\x7f\x9a\x67\xe6\x65\x09\x5f\x93\xef\x4f\x4b\x0a\x37\x9a\xff\xa2\x4c\x9f\x98\x72\x53\xe7\xef\x00\x00\x00\xff\xff\x96\x33\xff\xe8\x78\x05\x00\x00")
|
||||
var _translationsKubectlDefaultLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\x6f\x4f\xdb\x3e\x10\x7e\xcf\xa7\xb8\x5f\xd1\x4f\x02\x6d\xce\x92\xb2\x21\x14\xd6\x69\x90\x01\x43\x03\x51\x95\x30\x4d\xda\xa6\xc9\x8d\x8f\xc4\x9b\x63\x47\xb6\x03\xf4\xdb\xef\xec\x14\xba\x4e\x9a\xc4\x1b\xa8\x2a\xc5\x3e\x3f\x77\xcf\xfd\x79\x6e\x13\x4a\x74\x1e\xbc\xe5\xda\x29\xee\xa5\xd1\x0e\xae\x8d\x85\x5e\x4b\xb2\xd2\x93\x4b\x36\x36\xa1\x30\xdd\xc2\xca\xba\xf1\xb0\x55\x6c\xc3\x38\xcd\x76\xc9\x58\x36\x92\xb0\x52\x21\xd0\x57\x48\xe7\xad\x9c\xf7\x1e\x05\xf9\x0a\xb4\xe0\x1b\x04\xc7\x5b\x04\x25\x2b\xd4\x0e\x81\xbb\x68\x9b\x1e\x14\x9f\x0e\x4e\x8e\xa0\xe3\xd5\x2f\x5e\x63\x08\x7f\x7c\x3a\xbb\x2c\xe1\xe0\xaa\xfc\x78\x31\x83\xb9\x45\x2d\xb8\x4e\x44\x32\xef\xad\x76\xef\xeb\x96\x4b\x95\x54\xa6\x7d\x19\x89\x09\xbf\xd1\xba\x5a\x0a\x18\x8d\xc2\x81\x68\xc3\x69\x34\xb5\xe6\x27\x56\x9e\x9d\x0a\xf6\x19\xad\xa3\x42\x72\xa8\xd1\x7b\xbc\xf3\xac\x36\x0c\xef\x78\xdb\x29\x74\xac\x41\xa5\xcc\x37\x4d\x1e\x33\xec\x8c\xf5\xec\x3c\x04\x63\x87\x7d\xed\x58\x69\x72\x88\x4f\xd3\x8b\x92\x15\x16\x63\x3f\xd8\x07\xee\x31\x0f\xdc\x3b\x2c\x1b\xd3\x9f\x8e\x79\xba\xf3\x22\xa5\xdf\x12\xcc\x66\x78\x23\xdd\x1a\x76\x37\x62\x77\x60\x9c\xe5\x6f\x5e\xb3\x74\x6f\x89\x3d\xe3\xce\xb3\x72\xd9\x6c\x63\x73\x38\x1c\xaa\x85\xc3\x50\x2b\xbc\xfd\x67\xf1\xef\xa2\xfb\xf9\xe9\xf9\xd1\xaa\xbc\x2c\x19\x82\x16\x46\x7b\xd4\x14\x77\xd1\x11\x79\xa8\xf8\x55\xa7\xb8\xd4\xfb\x50\x35\xdc\x3a\xf4\x93\xab\xf2\x98\xed\xad\x63\x43\x0e\xd7\x68\xd9\x91\xae\x8c\x90\xba\xce\x61\x6f\x2e\x7d\xc4\x7c\x61\x27\xa8\xd1\x0e\x09\x4e\x0d\x0a\x92\x42\x96\xec\x26\x59\xba\x7c\x1e\x6c\xec\xd2\xf4\xb6\xc2\x62\xe0\xc8\x61\x45\x72\xc6\x75\xdd\xd3\x6c\x59\x89\xbc\xbd\x6f\xa9\xea\x2d\x57\xec\xd8\xd8\xd6\xe5\xa0\xbb\x78\x75\x93\xf1\x3e\x0c\xc7\xc9\x96\x86\xff\x26\x90\x6d\xef\xaf\x85\xc8\x01\x75\x30\x84\x51\x57\xfe\xce\xc3\xe8\xaa\x13\xd4\xe4\x28\x25\xae\xb5\xf1\x4b\xd1\x1a\x0d\x1c\x2c\xba\x98\xd3\xe8\x5e\x22\x8f\x06\x47\x19\x3d\x0e\xbd\x4a\x85\xf2\xbc\xe5\xbe\x6a\xc2\x02\x18\xad\x16\xe0\xfa\x2e\x68\x8a\x76\x80\x7c\xa4\x16\xf2\x46\x8a\x9e\xab\x07\x67\x47\x71\xc5\xc3\x0d\x2a\xa3\x14\x69\x36\x92\x30\xa0\x68\xff\x8b\x3f\xa0\xb7\x68\x91\x76\x91\xb6\x69\xb4\x52\xfc\xb3\x12\xfe\x18\x46\xf3\xcc\xbc\x34\x8a\xaf\xe9\xf7\xa7\x25\x85\x5b\xee\xfe\xa2\xcc\x9e\x98\x72\xbd\xce\xdf\x01\x00\x00\xff\xff\x96\x33\xff\xe8\x78\x05\x00\x00")
|
||||
|
||||
func translationsKubectlDefaultLc_messagesK8sPoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -115,7 +115,7 @@ func translationsKubectlDefaultLc_messagesK8sPo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsKubectlEn_usLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x93\xcf\x6e\xd3\x40\x10\xc6\x27\xb4\x5c\x7c\xe4\xcc\x61\x90\x40\x2a\x42\x63\xd6\x0e\x54\x95\x4b\x10\x6a\x69\x51\xa5\x44\x8d\x8a\x8b\x7a\x64\xe3\x9d\x3a\x8b\x36\xbb\xd6\xee\xba\x69\x8f\x3c\x04\xaf\xc0\x05\xde\x82\x57\xe0\xcc\xb3\x20\xc7\x90\xc2\xad\x97\x76\x0f\x96\xbf\xd1\xcf\xdf\xfc\xf1\xec\xaf\x07\x9b\x5f\x00\x00\x36\x00\xe0\x21\x00\xbc\x00\x80\xfb\x00\x30\x86\xfe\x7c\x04\x80\x23\x00\x90\x00\xb0\x35\x00\xf8\x06\x00\xdf\x07\x00\x3f\x07\x00\x8f\x01\xe0\xf3\x06\xc0\x0f\x00\xf8\xba\x01\x30\xf8\xe3\xf3\xf7\xdc\xeb\x1e\xa7\x8d\x92\x91\x31\xce\x19\xa5\xb5\x2e\xca\xa8\x9d\x0d\xe8\x2c\x4a\xf4\x1c\x5c\xeb\x2b\xde\xbc\x09\x04\x4b\x19\xab\x39\xea\x2e\x6c\xae\x30\xb4\x4d\xe3\x7c\x64\xd5\x51\xda\x2a\x7d\xa1\x55\x2b\xcd\x1a\x0f\x28\xad\x5a\x2b\xac\x9c\x31\x5c\xf5\xb6\x84\x4f\xd4\x3f\xdc\x92\x3d\xe3\xb9\x6b\xad\xda\xbc\xfd\x14\x77\xd0\x05\x4c\xbd\xfb\xc4\x55\xa4\x23\x45\x1f\xd8\x07\xed\x6c\x81\x35\xc7\xc8\x97\x91\x6a\x47\x7c\x29\x17\x8d\xe1\x40\x73\x36\xc6\x25\x27\xdc\x55\x40\x93\x50\x6b\x45\x7b\x6d\x1d\xa8\x74\x05\x26\xd3\xe3\x92\xf6\x3d\xaf\xfe\x04\xbd\x95\x91\x0b\xcc\x45\x36\xa4\x2c\xa7\x2c\xc7\x5c\x14\x62\xf8\x4c\x08\x21\x92\xe9\x31\x9d\xf0\x85\x0e\xff\x71\xdb\x2b\x6e\x88\x79\x5e\x0c\x5f\x92\xd8\x11\x22\x19\xcb\x10\xa9\xf4\xd2\x06\x23\xa3\xf3\x05\xee\x79\xb6\x4a\x5a\xdc\x6b\xbd\x0d\xf8\x6a\xd6\xcb\x54\xa5\xb3\x2e\xf0\xa6\x5e\x48\x6d\xd2\xca\x2d\x5e\x27\x93\xa3\xc9\xc1\x75\x2b\x59\x2a\x92\x7d\x67\x23\xdb\x48\xe5\x55\xc3\x05\x76\x9d\x3d\x6f\x8c\xd4\x76\x17\xab\xb9\xf4\x81\xe3\xe8\xb4\x3c\xa4\x9d\x6b\xae\xcb\x7b\xce\x9e\x0e\x6c\xe5\x94\xb6\x75\x81\x3b\x33\x1d\x93\x33\x7a\xc7\x96\x7d\x5f\xd0\xd4\xb1\xd2\x11\xb3\x74\x3b\xcd\x44\x72\x46\xbd\xa6\xf7\xab\x09\xef\xf7\xbe\x05\xf6\xc6\x63\x69\xeb\x56\xd6\x4c\x25\xcb\x45\x37\x2e\xd3\x7a\x69\xe8\xd0\xf9\x45\x28\xd0\x36\x2b\x19\x46\xf9\x2e\xf6\xaf\xa3\x2d\x8b\x8f\x46\x98\x3d\xdd\x5d\x7f\x5a\x20\xdb\xe4\x46\xf7\xe3\x96\x96\x06\x97\x32\xdc\xdd\x56\xfe\x0e\x00\x00\xff\xff\x52\x15\x0d\x38\x6a\x04\x00\x00")
|
||||
var _translationsKubectlEn_usLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\xc1\x6e\x13\x31\x10\xdd\x42\xb8\xec\x91\x33\x87\x41\x02\xa9\x08\x4d\xd8\x24\x50\x45\x0e\x41\x28\xa1\x45\x91\x12\x11\x95\x14\x71\xc4\x59\x4f\x37\x46\x8e\xbd\xb2\xbd\x4d\x7b\xe4\x23\xf8\x05\x2e\xf0\x17\xfc\x02\x67\xbe\x85\xd9\x2c\xa4\xf4\xd6\x4b\x3b\xda\xb7\x33\xe3\x7d\x7e\xe3\xb1\xbd\xbf\xef\xb7\xbe\x26\x6c\x77\x19\x0f\x18\xcf\x19\xf7\x18\xd3\xa4\xb1\x4f\x8c\x09\x43\x32\xf6\xf7\x92\xe4\x3b\xfb\x1f\xec\x7f\x31\x1e\x71\xfc\x85\x27\xfe\x64\xff\x8d\xfd\xde\x5f\x9d\x7f\x76\xa7\x7e\x9d\x94\x4a\x46\x82\xb8\x22\x90\xd6\xba\x28\xa3\x76\x36\x80\xb3\x20\xc1\x53\x70\x95\xcf\xa9\x75\x1d\x52\xb2\x91\x31\x5f\x81\xae\x87\xcd\x05\x84\xaa\x2c\x9d\x8f\xa4\x6a\x96\xb6\x4a\x9f\x69\x55\x49\xb3\xa3\x07\x56\x52\xbb\x0c\x72\x67\x0c\xe5\x8d\x2c\xc2\x63\xf5\x1f\x6f\x43\x9e\xe0\xd4\x55\x56\xb5\x6e\xbe\xc4\x2d\x74\x91\xcc\xbd\xfb\xcc\x2c\x9c\x28\xfc\x40\x3e\x30\x5b\x40\x41\x31\xd2\x79\xc4\xc2\x21\x9d\xcb\x75\x69\x28\xe0\x8a\x8c\x71\xe9\x31\xd5\x2b\xc0\x59\x28\xb4\xc2\x51\x55\x04\x5c\x38\x01\xe9\xfc\xdd\x02\xc7\x9e\xb6\x27\x81\x6f\xf8\x74\x04\x74\xb3\x4e\x0f\x3b\x5d\x7e\x38\x14\x59\xef\x69\xc6\xc6\x44\x3c\xa6\x33\x1d\xae\xf0\x0e\xb6\xbc\x1e\x74\xbb\xa2\xf7\x02\xb3\x3e\xf3\xa6\x32\x44\x5c\x78\x69\x83\x91\xd1\x79\x01\x23\x4f\x56\x49\x0b\xa3\xca\x73\x3b\x2f\x97\x4d\xda\x56\xed\x65\x3d\xf0\xba\x58\x4b\x6d\xda\xb9\x5b\xbf\x4a\x67\x93\xd9\xe1\x65\x2b\x9d\x76\x96\x8e\x9d\x8d\x64\x59\xef\xa2\xe4\x82\x75\x67\xcf\x4a\x23\xb5\x1d\x40\xbe\x92\x3e\x50\x1c\x9e\x2c\x8e\xb0\x7f\xc9\xab\xeb\x9e\x92\xc7\x43\x9b\x3b\xa5\x6d\x21\xa0\xbf\xd4\x31\xfd\x88\x6f\xc9\x92\x6f\x16\x34\x77\xa4\x74\x64\xfd\x83\x76\x27\xe3\x4f\x4d\x8e\xef\xb7\x3b\x3c\x6e\x74\x05\x34\xc2\x53\x69\x8b\x4a\x16\x84\x0b\x92\xeb\x7a\xbb\x4c\xe5\xa5\xc1\x23\xe7\xd7\x41\x80\x2d\xb7\x69\x18\x76\x07\xd0\x84\xc3\x7d\x0b\x0f\x87\xd0\x79\x32\xd8\x4d\x15\x40\x36\xbd\xd6\xff\x71\x43\x97\x06\x36\x32\xdc\xde\xad\xfc\x13\x00\x00\xff\xff\x52\x15\x0d\x38\x6a\x04\x00\x00")
|
||||
|
||||
func translationsKubectlEn_usLc_messagesK8sMoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -135,7 +135,7 @@ func translationsKubectlEn_usLc_messagesK8sMo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsKubectlEn_usLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\x5d\x4f\xdb\x4a\x10\x7d\xcf\xaf\x98\xeb\xe8\x4a\xa0\x7b\xd7\xb5\x13\x15\x21\xd3\x54\x85\x14\x28\x2a\x88\x28\x98\xaa\x52\x5b\x55\x1b\xef\xc4\xd9\x76\x3d\x6b\xed\x8c\xf9\xf8\xf7\x95\xed\x40\x9a\x4a\x95\x78\x81\xb7\xd9\xd9\x33\x73\xe6\xcc\x9e\x1d\x42\x8e\x2c\x20\x41\x13\x3b\x2d\xd6\x13\xc3\xd2\x07\x68\xc8\x0a\x08\xb2\x70\x3c\x18\xc2\xd4\xd7\xf7\xc1\x96\x2b\x81\x9d\xe9\x2e\x8c\x92\x74\x6f\x30\x84\x7c\x65\x19\x96\xd6\x21\x58\x06\x63\x59\x82\x5d\x34\x82\x06\x1a\x32\x18\x40\x56\x08\xac\x2b\x04\x67\x0b\x24\x46\xd0\xdc\xe5\x66\x87\xd3\x8f\x87\xa7\xc7\x50\xeb\xe2\xa7\x2e\xb1\x6d\x7f\x72\x36\xbf\xca\xe1\xf0\x3a\xff\x70\x39\x87\x45\x40\x32\x9a\x62\x13\x2f\x9a\x40\xfc\xae\xac\xb4\x75\x71\xe1\xab\xff\x3b\xe2\x78\x30\x1c\x54\x5c\x5a\x03\x51\xd4\x06\x2c\xa1\x8d\xa2\x59\xf0\x3f\xb0\x10\x75\x66\xd4\x27\x0c\x6c\x3d\x65\x50\xa2\x08\xde\x89\x2a\xbd\xc2\x3b\x5d\xd5\x0e\x59\xad\xd0\x39\xff\x95\xa2\x41\x34\xc7\xda\x07\x51\x17\x6d\x33\x75\xd4\x94\xac\x72\x9f\x41\x77\x35\xbb\xcc\xd5\x34\x60\xb7\x0f\xf5\x5e\x0b\x66\x2d\xf7\x58\xa5\x23\x95\x8e\x60\x94\x64\xc9\xf8\xbf\x24\x49\x92\x35\x58\xcd\xf1\xc6\xf2\x16\x76\xaf\xc3\x8e\x61\x34\xca\xc6\xaf\x55\xb2\xbf\xc6\x9e\x6b\x16\x95\xaf\x97\xed\x43\x06\x47\xbd\x5a\x38\x6a\xb5\xc2\x9b\xbf\x8a\x7f\xdb\x95\x5f\x9c\x5d\x1c\x6f\xe4\xa5\x71\xdf\x74\xea\x49\x90\x44\xe5\xf7\x35\x66\xd0\x2a\x7e\x55\x3b\x6d\xe9\x00\x8a\x95\x0e\x8c\x32\xb9\xce\x4f\xd4\xfe\x36\xb6\x9d\x61\x89\x41\x1d\x53\xe1\x8d\xa5\x32\x83\xfd\x85\x95\x0e\xf3\x59\x9d\x22\x61\xe8\x07\x9c\x79\x34\x56\x20\x8d\xf7\xe2\x34\x59\x5f\xf7\x39\x75\xe5\x9b\x50\xe0\xb4\xe7\xc8\x60\x43\x72\xae\xa9\x6c\x74\x89\x2a\x47\x5d\x3d\xac\xd4\x35\x41\x3b\x75\xe2\x43\xc5\x19\x50\xdd\x1d\x79\x32\x3a\x80\x3e\x9c\xec\x10\xfc\x33\x81\x74\xf7\x60\xab\x45\x06\x48\x6d\xa2\x7d\xea\x42\xee\x04\xa2\xeb\xda\x68\xc1\xce\x4a\x9a\xc8\xcb\xda\xb4\x9e\x40\x43\x40\xee\x66\x8a\x1e\x2c\xf2\x64\x70\x67\xa3\xa7\xa1\x37\xa3\x44\x83\xe8\x56\x4b\xb1\x6a\x3f\x80\x27\x77\x0f\xdc\xd4\xad\xa7\xd0\xb4\x35\x96\x8c\xbd\xb1\xa6\xd1\xee\xb1\x98\x41\x93\x79\x3c\x41\xe1\x9d\xc3\xa2\x27\x51\x10\x0d\xa2\x7f\xcd\x6f\xd0\x5b\x0c\x08\x4b\xdf\x90\x89\x36\x8e\x7f\x51\xc2\xef\xfd\xd3\xbc\x30\x2f\x4b\xf8\x92\x7c\x7b\x5e\x52\xb8\xd5\xfc\x07\x65\xfa\xcc\x94\xdb\x3a\x7f\x05\x00\x00\xff\xff\x61\x66\xb9\x11\x78\x05\x00\x00")
|
||||
var _translationsKubectlEn_usLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x53\x6d\x4f\xdb\x30\x10\xfe\xce\xaf\xb8\x15\x4d\x02\x6d\xce\x92\x56\x43\x28\xac\xd3\x20\x03\x86\x06\xa2\x2a\x61\x9a\xb4\x4d\x93\x1b\x1f\x89\x37\xc7\x8e\x6c\x87\x97\x7f\xbf\xb3\x53\xe8\x3a\x69\x12\x5f\xa0\xaa\x14\xfb\xfc\xdc\x3d\xf7\xf2\xdc\x26\x94\xe8\x3c\x78\xcb\xb5\x53\xdc\x4b\xa3\x1d\x5c\x19\x0b\xbd\x96\x64\xa5\x27\x97\x6c\x6c\x42\x61\xba\x3b\x2b\xeb\xc6\xc3\x56\xb1\x0d\xe3\x34\xdb\x21\x63\xd9\x48\xc2\x4a\x85\x40\x5f\x21\x9d\xb7\x72\xd1\x7b\x14\xe4\x2b\xd0\x82\x6f\x10\x1c\x6f\x11\x94\xac\x50\x3b\x04\xee\xa2\x6d\xb6\x5f\x7c\xde\x3f\x3e\x84\x8e\x57\xbf\x79\x8d\x21\xfc\xd1\xc9\xfc\xa2\x84\xfd\xcb\xf2\xd3\xf9\x1c\x16\x16\xb5\xe0\x3a\x11\xc9\xa2\xb7\xda\x7d\xa8\x5b\x2e\x55\x52\x99\xf6\x75\x24\x26\xfc\x46\xeb\x6a\x29\x60\x34\x0a\x07\xa2\x0d\xa7\xd1\xcc\x9a\x5f\x58\x79\x76\x22\xd8\x17\xb4\x8e\x0a\xc9\xa1\x46\xef\xf1\xd6\xb3\xda\x30\xbc\xe5\x6d\xa7\xd0\xb1\x06\x95\x32\xdf\x35\x79\xcc\xb1\x33\xd6\xb3\xb3\x10\x8c\x1d\xf4\xb5\x63\xa5\xc9\x21\x3e\xcd\xce\x4b\x56\x58\x8c\xfd\x60\x1f\xb9\xc7\x3c\x70\x4f\x58\x36\xa6\x3f\x1d\xf3\x74\xf2\x2a\xa5\xdf\x12\xcc\xe6\x78\x2d\xdd\x1a\x76\x27\x62\x27\x30\x1e\xe7\x93\xb7\x2c\xdd\x5d\x62\x4f\xb9\xf3\xac\x5c\x36\xdb\xd8\x1c\x0e\x86\x6a\xe1\x20\xd4\x0a\xef\xfe\x5b\xfc\xfb\xe8\x7e\x76\x72\x76\xb8\x2a\x2f\x4b\x86\xa0\x85\xd1\x1e\x35\xc5\xbd\xeb\x88\x3c\x54\xfc\xa6\x53\x5c\xea\x3d\xa8\x1a\x6e\x1d\xfa\xe9\x65\x79\xc4\x76\xd7\xb1\x21\x87\x2b\xb4\xec\x50\x57\x46\x48\x5d\xe7\xb0\xbb\x90\x3e\x62\xbe\xb2\x63\xd4\x68\x87\x04\x67\x06\x05\x49\x21\x4b\x76\x92\x2c\x5d\x3e\x0f\x36\x76\x61\x7a\x5b\x61\x31\x70\xe4\xb0\x22\x39\xe5\xba\xee\x69\xb6\xac\x44\xde\xde\xb7\x54\xf5\x96\x2b\x76\x64\x6c\xeb\x72\xd0\x5d\xbc\xba\xe9\x78\x0f\x86\xe3\x74\x4b\xc3\x8b\x29\x64\xdb\x7b\x6b\x21\x72\x40\x1d\x0c\x61\xd4\x95\xbf\xf5\x30\xba\xec\x04\x35\x39\x4a\x89\x6b\x6d\xfc\x52\xb4\x46\x03\x07\x8b\x2e\xe6\x34\xba\x97\xc8\xa3\xc1\x51\x46\x8f\x43\xaf\x52\xa1\x3c\x6f\xb8\xaf\x9a\xb0\x00\x46\xab\x3b\x70\x7d\x17\x34\x45\x3b\x40\x3e\x52\x0b\x79\x2d\x45\xcf\xd5\x83\xb3\xa3\xb8\xe2\xe1\x06\x95\x51\x8a\x34\x1b\x49\x18\x50\xb4\x97\xe2\x2f\xe8\x0d\x5a\xa4\x5d\xa4\x6d\x1a\xad\x14\xff\xac\x84\x3f\x87\xd1\x3c\x33\x2f\x8d\xe2\x5b\xfa\xe3\x69\x49\xe1\x86\xbb\x7f\x28\xb3\x27\xa6\x5c\xaf\xf3\x4f\x00\x00\x00\xff\xff\x61\x66\xb9\x11\x78\x05\x00\x00")
|
||||
|
||||
func translationsKubectlEn_usLc_messagesK8sPoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -155,7 +155,7 @@ func translationsKubectlEn_usLc_messagesK8sPo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsTestDefaultLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x51\xc1\x6e\x13\x31\x10\x7d\x0d\xe1\xb2\x47\x2e\x5c\x38\x18\xa1\x4a\x20\x34\x8b\x77\x03\x55\xe4\x10\x84\x12\x5a\x54\x94\xa8\x51\x59\x50\x6f\xe0\x64\xa7\x1b\xa3\x5d\x7b\x65\x3b\x50\x3e\x80\x4f\xe0\xc8\x1f\xf0\x4d\x7c\x0b\x4a\x36\xd0\x22\xe6\x60\xbd\x79\x7a\xef\xd9\xe3\xf9\x75\xa7\xff\x1d\x00\x6e\x01\xb8\x07\xe0\x29\x80\xdb\x00\x66\xe8\xea\x23\x80\x07\x00\x34\x80\xbb\x00\xbe\x01\xf8\x79\x00\xfc\x00\x70\x08\xe0\x4d\xaf\xf3\xb6\x3d\xe0\x60\xef\xe9\xed\xf3\x76\x15\x39\xc4\x0f\x6d\xbd\xf1\xba\xee\xdf\xc0\xf8\x0f\x87\xe8\x8d\xad\xfa\x37\x30\x16\xde\x7d\xe2\x55\xa4\xd3\x92\xde\xb3\x0f\xc6\x59\x25\x2a\x8e\x91\xaf\x22\x55\x8e\xf8\x4a\x37\x6d\xcd\x81\xd6\x5c\xd7\x2e\x39\xe7\xd6\xf9\x48\xf3\x50\x99\x92\x26\x9b\x2a\x50\xe1\x94\x48\x16\x67\x05\x4d\x3d\xeb\x68\x9c\xa5\x57\x3a\xb2\x12\xb9\xcc\x06\x94\xe5\x94\xe5\x22\x97\x4a\x0e\x1e\x4b\x29\x65\xb2\x38\xa3\x73\xfe\x6c\xc2\x3f\xba\xa3\x9d\x6e\x20\xf2\x4c\x0d\x9e\x91\x1c\x4a\x99\xcc\x74\x88\x54\x78\x6d\x43\xad\xa3\xf3\x4a\x4c\x3c\xdb\x52\x5b\x31\xd9\x78\x1b\xc4\xf3\x65\xd7\xa6\x65\xba\xdc\x12\x2f\xab\x46\x9b\x3a\x5d\xb9\xe6\x45\x32\x3f\x9d\x1f\x5f\x8f\x92\xa5\x32\x99\x3a\x1b\xd9\x46\x2a\xbe\xb6\xac\xc4\x76\xb2\x27\x6d\xad\x8d\x1d\x89\xd5\x5a\xfb\xc0\x71\xfc\xae\x38\xa1\xe1\xb5\x6e\x7b\xef\x25\x7b\x3a\xb6\x2b\x57\x1a\x5b\x29\x31\x5c\x9a\x98\x5c\xd0\x6b\xb6\xec\xbb\x07\x2d\x1c\x97\x26\x8a\x2c\x3d\x4a\x33\x99\x5c\x50\xd7\xd3\x5b\xb7\xf1\x2b\x9e\x76\xb9\x4a\x74\xc1\x33\x6d\xab\x8d\xae\x98\x0a\xd6\xcd\xf6\xbb\x76\x2b\xa1\x13\xe7\x9b\xa0\x84\xed\x36\x14\xc6\xf9\x48\x74\x70\xfc\xd0\x8a\xfb\x63\x91\x3d\x1a\xfd\xb5\x2a\xc1\x36\x41\x5c\xb3\x67\xf1\x45\x07\x71\x58\x0a\x13\xb9\xf9\xc3\x6c\x8f\x3d\x15\x70\xe9\x1c\x7e\x07\x00\x00\xff\xff\x59\x11\x2e\xef\x74\x02\x00\x00")
|
||||
var _translationsTestDefaultLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x91\xc1\x8e\xd3\x30\x10\x86\xb3\x4b\xb9\xe4\xc8\x85\x0b\x87\x41\x68\x25\x10\x9a\x92\xb4\xb0\xaa\x5c\x8a\x50\xcb\x2e\x5a\xd4\x8a\x6a\x09\x88\x1b\xb8\xc9\x6c\x1a\x94\xd8\x91\x3d\x81\xe5\x01\x78\x04\x8e\xbc\x01\xcf\xc4\xb3\x30\xa9\x0b\xbb\x88\x51\xbe\x78\x3c\xf9\xe7\x77\x6c\xff\xba\x35\xf8\x1e\x49\xdc\x10\xee\x08\x8f\x85\x9b\xc2\x32\x0a\xf1\x51\xb8\x27\x68\xe1\xb6\xf0\x4d\xf8\x79\x10\x45\x3f\x64\x3c\x12\x5e\x1d\x86\xde\x56\xc6\x83\x7d\xcf\xe1\xde\x6f\x17\x4c\x9e\x3f\xb4\x75\xe7\x74\x3d\xb8\x96\x47\xff\xe5\x9e\x5d\x65\xca\xc1\xb5\x3c\x5a\x3b\xfb\x89\x72\xc6\xb3\x02\xdf\x91\xf3\x95\x35\x0a\x4a\x62\xa6\x4b\xc6\xd2\x22\x5d\xea\xa6\xad\xc9\xe3\x96\xea\xda\xc6\xe7\xd4\x5a\xc7\xb8\xf2\x65\x55\xe0\xbc\x2b\x3d\x66\x56\x41\xbc\x7e\x9d\xe1\xc2\x91\x66\x69\xc7\x17\x9a\x49\xc1\x28\x49\xc7\x98\x8e\xe4\x91\x54\x25\xe3\x87\x89\x84\x08\xf1\x9c\x3e\x57\xfe\x1f\xdd\xf1\x4e\x37\x86\x51\xaa\xc6\x4f\x30\x99\x88\x6e\xa9\x3d\x63\xe6\xb4\xf1\xb5\x66\xeb\x14\xcc\x1d\x99\x42\x1b\x98\x77\xce\x78\x78\xba\x09\xd3\x61\x31\xdc\xf4\x85\xe7\x65\xa3\xab\x7a\x98\xdb\xe6\x59\xbc\x3a\x5b\x9d\x5c\x6d\x25\x1d\x26\xf1\xc2\x1a\x26\x23\x7e\x5f\x5b\x59\xb0\xdf\xd9\xa3\xb6\xd6\x95\x99\x42\xbe\xd5\xce\x13\xcf\xde\x66\xa7\x38\xb9\xd2\xf5\xeb\x5e\x90\xc3\x13\x93\xdb\x42\x0e\x49\xc1\x64\x53\x71\xfc\x1e\x5f\x92\x21\x17\x7e\x68\x6d\xa9\xa8\x58\xfc\x8f\x87\x69\x22\x9f\xc2\x1c\xdf\xd8\xce\xe5\xb4\x08\xbe\x0a\x82\xf1\x52\x9b\xb2\xd3\x25\x61\x46\xba\xe9\x8f\x6b\x77\x25\x78\x6a\x5d\xe3\x15\x98\x70\x43\x7e\x36\x9a\x42\x48\x67\xf7\x0d\xdc\x9d\x41\xfa\x60\xfa\xb7\x55\x01\x99\x38\xe2\x2d\x39\x82\x2f\xda\xc3\x51\x01\x15\x53\xf3\xa7\xd2\xbf\xf6\x25\x1f\x5d\x58\x1b\xfd\x0e\x00\x00\xff\xff\x59\x11\x2e\xef\x74\x02\x00\x00")
|
||||
|
||||
func translationsTestDefaultLc_messagesK8sMoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -175,7 +175,7 @@ func translationsTestDefaultLc_messagesK8sMo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsTestDefaultLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x52\x61\x6f\xd3\x30\x10\xfd\x9e\x5f\x71\xa4\x42\xda\x04\x0e\x49\x2a\xa6\x29\xa3\x88\x2d\xb4\xa3\x62\xd5\xaa\x2e\x43\x48\x80\x90\x9b\x5c\x13\x43\x62\x47\xbe\x0b\x74\xff\x1e\x39\xc9\xa8\xca\xc4\x97\xc8\x7e\xef\xdd\x3d\xbf\xcb\x4d\x20\x43\x62\x60\x2b\x35\xd5\x92\x95\xd1\x04\x3b\x63\xa1\xd3\x8a\x81\x91\x98\x02\x6f\x02\xa9\x69\x1f\xac\x2a\x2b\x86\x93\xf4\x14\xe2\x30\x3a\xf3\x26\x90\x55\x8a\x60\xa7\x6a\x04\x45\x50\x28\x62\xab\xb6\x1d\x63\x01\x9d\x2e\xd0\x02\x57\x08\x24\x1b\x84\x5a\xe5\xa8\x09\x41\x52\x8f\xad\x2f\xd3\x8f\x97\xd7\x73\x68\x65\xfe\x53\x96\xe8\xda\x2f\x96\x9b\xbb\x0c\x2e\xef\xb3\x0f\xb7\x1b\xd8\x5a\xd4\x85\xd4\x41\x11\x6c\x3b\xab\xe9\x5d\xd9\x48\x55\x07\xb9\x69\x5e\xf6\xc6\x81\x37\xf1\x1a\x2a\x55\x01\xbe\xef\x0e\xc4\xd6\x9d\xfc\xb5\x35\x3f\x30\x67\xb1\x2c\xc4\x27\xb4\xa4\x8c\x4e\xa0\x44\x66\xdc\xb3\x28\x8d\xc0\xbd\x6c\xda\x1a\x49\x54\x58\xd7\xe6\xab\xf6\x3d\x7f\x83\xad\xb1\x2c\x56\xae\x99\xb8\xea\x4a\x12\x99\x49\xa0\xa7\xd6\xb7\x99\x48\x2d\xf6\xf3\x10\xef\x25\x63\xe2\xbc\xa7\x22\x8a\x45\x14\x43\x1c\x26\xe1\xf4\x45\x18\x86\xe1\x28\x16\x1b\xfc\xa5\xe8\x48\x7b\xd6\x6b\xa7\x10\x47\xc9\xf4\xb5\x08\xcf\x47\xed\x8d\x24\x16\xd9\x38\x6c\x63\x13\xb8\x1a\xd2\xc2\x95\xcb\x0a\x6f\xfe\x1b\xfe\x6d\x5f\xbe\x5a\xae\xe6\x87\x78\x51\x30\x34\x4d\x8d\x66\xd4\x2c\xb2\x87\x16\x13\x70\x89\x5f\xb5\xb5\x54\xfa\x02\xf2\x4a\x5a\x42\x9e\xdd\x67\x0b\x71\x7e\xac\x75\x6f\xd8\xa1\x15\x73\x9d\x9b\x42\xe9\x32\x81\xf3\xad\xe2\x5e\xf3\x59\x5c\xa3\x46\x3b\x3c\x70\x6d\xb0\x50\x0c\x51\x70\x16\x44\xe1\x48\x0f\x98\xb8\x33\x9d\xcd\x31\x1d\x3c\x12\x38\x98\xdc\x48\x5d\x76\xb2\x44\x91\xa1\x6c\x1e\x47\x5a\x77\x56\xd6\x62\x61\x6c\x43\x09\xe8\xb6\xbf\xd2\x2c\xbe\x80\xe1\x38\x3b\xd1\xf0\x6c\x06\xd1\xe9\xc5\x51\x8b\x04\x50\x3b\xc0\xfd\xea\x9c\xf7\x0c\xbe\x5b\xca\xef\x6e\xd9\x74\xe9\x3f\x6e\xc2\xbf\x58\xbf\x14\x3b\x63\x9e\xd4\x0d\x5e\xc7\x75\x47\xd8\x78\x7b\x4a\x11\xdb\x2f\xe1\x37\xf0\xb9\x42\x8b\xf0\x5b\x12\x3c\x2f\x40\x31\x36\x7f\xd9\xe8\xc0\xba\xcf\x48\x93\xef\xfd\x09\x00\x00\xff\xff\xbf\xc0\xcb\xd2\x64\x03\x00\x00")
|
||||
var _translationsTestDefaultLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x52\x61\x6f\xd3\x30\x10\xfd\xde\x5f\x71\x64\x42\xda\x04\x0e\x49\x2b\xaa\x2a\xa5\x88\x36\xb4\xa3\x62\xd5\xaa\x2e\x43\x48\x80\x90\x9b\x5c\x13\x43\x62\x47\xb6\x03\xdd\xbf\xe7\x9c\x64\x54\x65\x22\x8a\x92\xf3\x7b\xef\xee\xf9\xec\xbb\x80\x04\x8d\x05\xab\xb9\x34\x25\xb7\x42\x49\x03\x07\xa5\xa1\x91\x82\x50\xa2\x8c\x3f\xb8\x80\x58\xd5\x0f\x5a\xe4\x85\x85\xcb\xf8\x0a\x86\x41\x38\x26\x30\x29\x04\x69\x45\x89\x40\xff\x4c\x18\xab\xc5\xbe\xb1\x98\x51\x6e\x86\x1a\x6c\x81\x60\x78\x85\x50\x8a\x14\xa5\x41\xe0\xa6\xc5\xb6\xf3\xf8\xe3\xfc\x7a\x09\x35\x4f\x7f\xf2\x1c\x5d\xf9\xd5\x7a\x77\x97\xc0\xfc\x3e\xf9\x70\xbb\x83\xbd\x46\x99\x71\xe9\x67\xfe\xbe\xd1\xd2\xbc\xcb\x2b\x2e\x4a\x3f\x55\xd5\xcb\xd6\x98\xf4\x83\xca\xe4\x22\x03\xcf\x73\x01\xd9\xba\xc8\xdb\x6a\xf5\x03\x53\xcb\xd6\x19\xfb\x84\xda\x50\x23\x11\xe4\x68\x2d\x1e\x2d\xcb\x15\xc3\x23\xaf\xea\x12\x0d\x2b\xb0\x2c\xd5\x57\x49\x19\x3b\xac\x95\xb6\x6c\xe3\x8a\xb1\x45\x93\x1b\x96\xa8\x08\x5a\x6a\x7b\x9b\xb0\x58\x63\x7b\x1e\xec\x3d\xb7\x18\x39\xef\x11\x0b\x87\xf4\x52\x18\x05\xa3\x17\x01\x3d\xbd\x98\xed\xf0\x97\x30\x67\xda\x71\xab\x1d\xc1\x30\x8c\x46\xaf\x59\x30\xe9\xb5\x37\xdc\x58\x96\xf4\x87\xad\x74\x04\x8b\xae\x5b\x58\xb8\x5e\xe1\xcd\x7f\x9b\x7f\xdb\xa6\x6f\xd6\x9b\xe5\xa9\xbd\xd0\xef\x8a\xc6\x4a\x5a\x94\x54\xf7\xa1\x26\x73\xd7\xf1\xab\xba\xe4\x42\x4e\x21\x2d\xb8\x36\x68\x67\xf7\xc9\x8a\x4d\xce\xb5\x6e\x0f\x07\xd4\x6c\x29\x53\x95\x09\x99\x47\x30\xd9\x0b\xdb\x6a\x3e\xb3\x6b\x94\xa8\xbb\x0d\x6e\x15\x66\x34\x0a\xa1\x3f\xf6\xc3\xa0\xa7\x3b\x8c\xdd\xa9\x46\xa7\x18\x77\x1e\x11\x9c\x4c\x6e\xb8\xcc\x1b\xba\x5b\x96\x20\xaf\x1e\x8f\xb4\x6c\x34\x2f\xd9\x4a\xe9\xca\x44\x20\xeb\x76\x69\x66\xc3\x29\x74\xe1\xec\x52\xc2\xb3\x19\x84\x57\xd3\xb3\x12\x11\xa0\x74\x80\xbb\xea\xd4\x1e\x2d\x78\x6e\x28\xbf\xbb\x61\x93\xb9\xf7\x38\x09\xff\x62\xed\x50\x1c\x94\x7a\x92\xd7\x79\x9d\xe7\x9d\x61\xfd\xea\x29\x45\x25\xbf\x04\xdf\x08\x2f\x50\x23\xfc\xa6\x61\x7e\x9e\x81\xb0\x58\xfd\x65\xc3\x13\xeb\x3e\x3d\x6d\xbc\xc1\x9f\x00\x00\x00\xff\xff\xbf\xc0\xcb\xd2\x64\x03\x00\x00")
|
||||
|
||||
func translationsTestDefaultLc_messagesK8sPoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -195,7 +195,7 @@ func translationsTestDefaultLc_messagesK8sPo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsTestEn_usLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x51\xc1\x6e\x13\x31\x10\x7d\x0d\xe1\xb2\x47\x2e\x5c\x38\x18\xa1\x4a\x20\x34\x8b\x77\x83\xaa\xc8\x21\x08\x25\xb4\xa8\x28\x51\xa3\xb2\xa0\xde\xc0\xc9\x0e\x1b\xa3\x5d\x7b\x65\x3b\x50\xb8\xf3\x09\x1c\xf9\x03\xbe\x89\x6f\x41\xc9\x06\x5a\xd4\x39\x58\x6f\x9e\xde\x7b\xf6\x78\x7e\xdf\xe9\xff\x00\x80\x5b\x00\xee\x01\x78\x0a\xe0\x36\x80\x19\xba\xfa\x00\xe0\x01\x00\x0d\xe0\x2e\x80\xef\x00\x7e\x1d\x00\x3f\x01\x1c\x02\x78\xdd\xeb\xbc\x6d\x0f\x38\xd8\x7b\x7a\xfb\xbc\x5d\x45\x0e\xf1\x7d\x5b\x6f\xbc\xae\xfb\xd7\x30\x6e\xe0\x10\xbd\xb1\x55\xff\x1a\xc6\xc2\xbb\x4f\xbc\x8a\x74\x5a\xd2\x3b\xf6\xc1\x38\xab\x44\xc5\x31\xf2\x65\xa4\xca\x11\x5f\xea\xa6\xad\x39\xd0\x9a\xeb\xda\x25\xe7\xdc\x3a\x1f\x69\x1e\x2a\x53\xd2\x64\x53\x05\x2a\x9c\x12\xc9\xe2\xac\xa0\xa9\x67\x1d\x8d\xb3\xf4\x52\x47\x56\x22\x97\xd9\x80\xb2\x9c\xb2\x5c\xe4\x52\xc9\xc1\x63\x29\xa5\x4c\x16\x67\x74\xce\x9f\x4d\xf8\x4f\x77\xb4\xd3\x0d\x44\x9e\xab\x2c\x27\x39\x94\x32\x99\xe9\x10\xa9\xf0\xda\x86\x5a\x47\xe7\x95\x98\x78\xb6\xa5\xb6\x62\xb2\xf1\x36\x88\x67\xcb\xae\x4d\xcb\x74\xb9\x25\x5e\x54\x8d\x36\x75\xba\x72\xcd\xf3\x64\x7e\x3a\x3f\xbe\x1a\x25\x4b\x65\x32\x75\x36\xb2\x8d\x54\x7c\x6d\x59\x89\xed\x64\x4f\xda\x5a\x1b\x3b\x12\xab\xb5\xf6\x81\xe3\xf8\x6d\x71\x42\xc3\x2b\xdd\xf6\xde\x8f\xec\xe9\xd8\xae\x5c\x69\x6c\xa5\xc4\x70\x69\x62\x72\x41\xaf\xd8\xb2\xef\x1e\xb4\x70\x5c\x9a\x28\xb2\xf4\x28\xcd\x64\x72\x41\x5d\x4f\x6f\xdc\xc6\xaf\x78\xda\xe5\x2a\xd1\x05\xcf\xb4\xad\x36\xba\x62\x2a\x58\x37\xdb\xef\xda\xad\x84\x4e\x9c\x6f\x82\x12\xb6\xdb\x50\x18\xe7\x23\xd1\xc1\xf1\x43\x2b\xee\x8f\x45\xf6\x68\xf4\xcf\xaa\x04\xdb\x04\x71\xcd\x9e\xc5\x17\x1d\xc4\x61\x29\x4c\xe4\xe6\x2f\xb3\x3d\xf6\x54\xc0\x52\x7f\xc3\x9f\x00\x00\x00\xff\xff\xa9\x73\xeb\x8c\x74\x02\x00\x00")
|
||||
var _translationsTestEn_usLc_messagesK8sMo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x64\x91\xc1\x6e\xd3\x40\x10\x86\xdd\x12\x2e\x3e\x72\xe1\xc2\x61\x10\xaa\x04\x42\x63\x6c\x07\x55\xd1\x86\x20\x94\xd0\xa2\xa2\x44\x44\xc5\x20\x6e\xb0\xb1\x07\xc7\xc8\xde\xb5\x76\xc7\x50\xb8\xf3\x08\x1c\x79\x03\x9e\x89\x67\x61\x1c\x07\x5a\xd4\x91\x3f\xef\xbf\xe3\x99\x7f\xbd\xbb\xbf\x6f\x8d\x7e\x04\x12\x37\x84\x3b\xc2\x63\xe1\xa6\xb0\x0c\x86\xf8\x20\xdc\x13\xb4\x70\x5b\xf8\x2e\xfc\x3a\x08\x82\x9f\x32\x1e\x09\x2f\x0f\x87\xde\x56\xc6\x83\x7d\xcf\xe1\xde\x6f\x17\x4c\x9e\xdf\xb7\x75\xe7\x74\x3d\xba\xa2\x83\x6b\xda\xb3\xab\x4c\x39\xba\xa2\x83\xb5\xb3\x9f\x28\x67\x3c\x2b\xf0\x2d\x39\x5f\x59\xa3\xa0\x24\x66\xba\x60\x2c\x2d\xd2\x85\x6e\xda\x9a\x3c\x6e\xa9\xae\x6d\x78\x4e\xad\x75\x8c\x2b\x5f\x56\x05\xce\xbb\xd2\x63\x66\x15\x84\xeb\x57\x19\x2e\x1c\x69\x96\x76\x7c\xae\x99\x14\xa4\x71\x32\xc6\x24\x95\x47\xa4\x8a\xc7\x0f\x63\x09\x29\xc4\x73\xfa\x5c\xf9\xff\xea\x8e\x77\x75\x63\x48\x53\x25\x22\x9e\x48\xdd\x52\x7b\xc6\xcc\x69\xe3\x6b\xcd\xd6\x29\x98\x3b\x32\x85\x36\x30\xef\x9c\xf1\xf0\x64\x33\x4c\xa3\x22\xda\xf4\x89\x67\x65\xa3\xab\x3a\xca\x6d\xf3\x34\x5c\x9d\xad\x4e\x2e\xb7\x92\x44\x71\xb8\xb0\x86\xc9\x88\xdf\xd7\x56\x16\xec\x77\xf6\xa8\xad\x75\x65\xa6\x90\x6f\xb5\xf3\xc4\xb3\x37\xd9\x29\x4e\x2e\xeb\xfa\x75\x3f\x92\xc3\x13\x93\xdb\x42\x0e\x49\xc1\x64\x53\x71\xf8\x0e\x5f\x90\x21\x37\xfc\xd0\xda\x52\x51\xb1\xf8\x1f\x47\x49\x2c\x9f\x86\x39\xbe\xb6\x9d\xcb\x69\x31\xf8\x2a\x18\x8c\x97\xda\x94\x9d\x2e\x09\x33\xd2\x4d\x7f\x5c\xbb\x2b\xc1\x53\xeb\x1a\xaf\xc0\x0c\x37\xe4\x67\xe9\x14\x06\x39\xbb\x6f\xe0\xee\x0c\x92\x07\xd3\x7f\xad\x0a\xc8\x84\x01\x6f\xc9\x11\x7c\xd1\x1e\x8e\x0a\xa8\x98\x9a\xbf\x99\xfe\xb5\x4f\xf9\x60\xa3\xbf\x05\x7f\x02\x00\x00\xff\xff\xa9\x73\xeb\x8c\x74\x02\x00\x00")
|
||||
|
||||
func translationsTestEn_usLc_messagesK8sMoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
@ -215,7 +215,7 @@ func translationsTestEn_usLc_messagesK8sMo() (*asset, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _translationsTestEn_usLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x52\x61\x6f\xd3\x30\x10\xfd\x9e\x5f\x71\xa4\x42\xda\x04\x0e\x49\x26\x4d\x53\x46\x11\x6b\x68\x47\xc5\xaa\x55\x5d\x86\x90\x00\x21\x37\xb9\x26\x86\xc4\x8e\x7c\x17\xe8\xf8\xf5\xc8\x49\x46\x55\x26\xbe\x44\xf6\x7b\xef\xee\xf9\x5d\x6e\x02\x19\x12\x03\x5b\xa9\xa9\x96\xac\x8c\x26\xd8\x19\x0b\x9d\x56\x0c\x8c\xc4\x14\x78\x13\x48\x4d\xfb\x60\x55\x59\x31\x9c\xa4\xa7\x10\x87\xd1\xb9\x37\x81\xac\x52\x04\x3b\x55\x23\x28\x82\x42\x11\x5b\xb5\xed\x18\x0b\xe8\x74\x81\x16\xb8\x42\x20\xd9\x20\xd4\x2a\x47\x4d\x08\x92\x7a\x6c\x7d\x95\x7e\xb8\xba\x9e\x43\x2b\xf3\x1f\xb2\x44\xd7\x7e\xb1\xdc\xdc\x65\x70\x75\x9f\xbd\xbf\xdd\xc0\xd6\xa2\x2e\xa4\x0e\x8a\x60\xdb\x59\x4d\x6f\xcb\x46\xaa\x3a\xc8\x4d\xf3\xb2\x37\x0e\xbc\x89\xd7\x50\xa9\x0a\xf0\x7d\x77\x20\xb6\xee\xe4\xaf\xad\xf9\x8e\x39\x8b\x65\x21\x3e\xa2\x25\x65\x74\x02\x25\x32\xe3\x9e\x45\x69\x04\xee\x65\xd3\xd6\x48\xa2\xc2\xba\x36\x5f\xb4\xef\xf9\x1b\x6c\x8d\x65\xb1\x72\xcd\xc4\xac\x2b\x49\x64\x26\x81\x9e\x5a\xdf\x66\x22\xb5\xd8\xcf\x43\xbc\x93\x8c\x89\xf3\x3e\x13\x51\x2c\xa2\x18\xe2\x30\x09\xcf\x5e\x84\x61\x18\x8e\x62\xb1\xc1\x9f\x8a\x8e\xb4\xe7\xbd\xf6\x0c\xe2\x38\x89\x62\x11\x5e\x8c\xda\x1b\x49\x2c\xb2\x71\xd8\xc6\x26\x30\x1b\xd2\xc2\xcc\x65\x85\xd7\xff\x0d\xff\xa6\x2f\x5f\x2d\x57\xf3\x43\xbc\x28\x18\x9a\xa6\x46\x33\x6a\x16\xd9\x43\x8b\x09\xb8\xc4\xaf\xda\x5a\x2a\x7d\x09\x79\x25\x2d\x21\x4f\xef\xb3\x85\xb8\x38\xd6\xba\x37\xec\xd0\x8a\xb9\xce\x4d\xa1\x74\x99\xc0\xc5\x56\x71\xaf\xf9\x24\xae\x51\xa3\x1d\x1e\xb8\x36\x58\x28\x86\x28\x38\x0f\xa2\x70\xa4\x07\x4c\xdc\x99\xce\xe6\x98\x0e\x1e\x09\x1c\x4c\x6e\xa4\x2e\x3b\x59\xa2\xc8\x50\x36\x8f\x23\xad\x3b\x2b\x6b\xb1\x30\xb6\xa1\x04\x74\xdb\x5f\x69\x1a\x5f\xc2\x70\x9c\x9e\x68\x78\x36\x85\xe8\xf4\xf2\xa8\x45\x02\xa8\x1d\xe0\x7e\x75\xce\x7b\x06\xdf\x2d\xe5\x37\xb7\x6c\xba\xf4\x1f\x37\xe1\x5f\xac\x5f\x8a\xad\xfc\xfd\xa4\x6e\xf0\x3a\xae\x3b\xc2\xc6\xdb\x53\x8a\xd8\x7e\x0e\xbf\x82\xcf\x15\x5a\x84\x5f\x92\xe0\x79\x01\x8a\xb1\xf9\xcb\x46\x07\xd6\x7d\x46\x9a\x7c\xef\x4f\x00\x00\x00\xff\xff\x03\xfe\xb1\xf0\x64\x03\x00\x00")
|
||||
var _translationsTestEn_usLc_messagesK8sPo = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x52\x61\x6f\xd3\x30\x10\xfd\xbe\x5f\x71\x64\x42\xda\x04\x0e\x49\x2b\x55\x55\x4a\x11\x6d\x68\x47\xc5\xaa\x55\x5d\x86\x90\x00\x21\x37\xb9\x25\x86\xc4\x8e\x6c\x07\x3a\x7e\x3d\xe7\x24\xa3\x0a\x13\x51\x94\x9c\xdf\x7b\x77\xcf\x67\xdf\x39\x24\x68\x2c\x58\xcd\xa5\x29\xb9\x15\x4a\x1a\xb8\x57\x1a\x1a\x29\x08\x25\xca\xf8\x67\xe7\x10\xab\xfa\x41\x8b\xbc\xb0\x70\x11\x5f\xc2\x28\x08\x27\x04\x26\x85\x20\xad\x28\x11\xe8\x9f\x09\x63\xb5\x38\x34\x16\x33\xca\xcd\x50\x83\x2d\x10\x0c\xaf\x10\x4a\x91\xa2\x34\x08\xdc\xb4\xd8\x6e\x11\x7f\x58\x5c\xad\xa0\xe6\xe9\x0f\x9e\xa3\x2b\xbf\xde\xec\x6f\x13\x58\xdc\x25\xef\x6f\xf6\x70\xd0\x28\x33\x2e\xfd\xcc\x3f\x34\x5a\x9a\xb7\x79\xc5\x45\xe9\xa7\xaa\x7a\xd9\x1a\x93\xfe\xac\x32\xb9\xc8\xc0\xf3\x5c\x40\xb6\x2e\xf2\x76\x5a\x7d\xc7\xd4\xb2\x4d\xc6\x3e\xa2\x36\xd4\x48\x04\x39\x5a\x8b\x47\xcb\x72\xc5\xf0\xc8\xab\xba\x44\xc3\x0a\x2c\x4b\xf5\x45\x52\xc6\x1e\x6b\xa5\x2d\xdb\xba\x62\x6c\xd9\xe4\x86\x25\x2a\x82\x96\xda\xdd\x24\x2c\xd6\xd8\x9e\x07\x7b\xc7\x2d\x46\xce\x7b\xcc\xc2\x11\xbd\x14\x46\xc1\xf8\x45\x40\x4f\x2f\x66\x7b\xfc\x29\xcc\x40\x3b\x69\xb5\x63\x18\x8d\x22\x0a\x82\x69\xaf\xbd\xe6\xc6\xb2\xa4\x3f\x6c\xa5\x23\x58\x76\xdd\xc2\xd2\xf5\x0a\xaf\xff\xdb\xfc\x9b\x36\x7d\xbb\xd9\xae\x4e\xed\x85\x7e\x57\x34\x56\xd2\xa2\xa4\xba\x0f\x35\x99\xbb\x8e\x5f\xd5\x25\x17\x72\x06\x69\xc1\xb5\x41\x3b\xbf\x4b\xd6\x6c\x3a\xd4\xba\x3d\xdc\xa3\x66\x2b\x99\xaa\x4c\xc8\x3c\x82\xe9\x41\xd8\x56\xf3\x89\x5d\xa1\x44\xdd\x6d\x70\xa7\x30\xa3\x51\x08\xfd\x89\x1f\x06\x3d\xdd\x61\xec\x56\x35\x3a\xc5\xb8\xf3\x88\xe0\x64\x72\xcd\x65\xde\xd0\xdd\xb2\x04\x79\xf5\x78\xa4\x65\xa3\x79\xc9\xd6\x4a\x57\x26\x02\x59\xb7\x4b\x33\x1f\xcd\xa0\x0b\xe7\x17\x12\x9e\xcd\x21\xbc\x9c\x0d\x4a\x44\x80\xd2\x01\xee\xaa\x53\x7b\xb4\xe0\xb9\xa1\xfc\xe6\x86\x4d\xe6\xde\xe3\x24\xfc\x8b\xb5\x43\x71\xe0\xbf\x9f\xe4\x75\x5e\xc3\xbc\x01\xd6\xaf\x9e\x52\x54\xf2\x73\xf0\x95\xf0\x02\x35\xc2\x2f\x1a\xe6\xe7\x19\x08\x8b\xd5\x5f\x36\x3c\xb1\xee\xd3\xd3\xc6\x3b\xfb\x13\x00\x00\xff\xff\x03\xfe\xb1\xf0\x64\x03\x00\x00")
|
||||
|
||||
func translationsTestEn_usLc_messagesK8sPoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
|
@ -151,7 +151,6 @@ go_library(
|
||||
"//pkg/controller/petset:go_default_library",
|
||||
"//pkg/controller/replicaset:go_default_library",
|
||||
"//pkg/controller/replication:go_default_library",
|
||||
"//pkg/dns/federation:go_default_library",
|
||||
"//pkg/fields:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
fed "k8s.io/kubernetes/pkg/dns/federation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
@ -43,6 +42,7 @@ type dnsConfigMapTest struct {
|
||||
labels []string
|
||||
|
||||
cm *v1.ConfigMap
|
||||
fedMap map[string]string
|
||||
isValid bool
|
||||
|
||||
dnsPod *v1.Pod
|
||||
@ -90,23 +90,25 @@ func (t *dnsConfigMapTest) run() {
|
||||
|
||||
t.labels = []string{"abc", "ghi"}
|
||||
valid1 := map[string]string{"federations": t.labels[0] + "=def"}
|
||||
valid1m := map[string]string{t.labels[0]: "def"}
|
||||
valid2 := map[string]string{"federations": t.labels[1] + "=xyz"}
|
||||
valid2m := map[string]string{t.labels[1]: "xyz"}
|
||||
invalid := map[string]string{"federations": "invalid.map=xyz"}
|
||||
|
||||
By("empty -> valid1")
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid1}, true)
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid1}, valid1m, true)
|
||||
t.validate()
|
||||
|
||||
By("valid1 -> valid2")
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid2}, true)
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid2}, valid2m, true)
|
||||
t.validate()
|
||||
|
||||
By("valid2 -> invalid")
|
||||
t.setConfigMap(&v1.ConfigMap{Data: invalid}, false)
|
||||
t.setConfigMap(&v1.ConfigMap{Data: invalid}, nil, false)
|
||||
t.validate()
|
||||
|
||||
By("invalid -> valid1")
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid1}, true)
|
||||
t.setConfigMap(&v1.ConfigMap{Data: valid1}, valid1m, true)
|
||||
t.validate()
|
||||
|
||||
By("valid1 -> deleted")
|
||||
@ -114,7 +116,7 @@ func (t *dnsConfigMapTest) run() {
|
||||
t.validate()
|
||||
|
||||
By("deleted -> invalid")
|
||||
t.setConfigMap(&v1.ConfigMap{Data: invalid}, false)
|
||||
t.setConfigMap(&v1.ConfigMap{Data: invalid}, nil, false)
|
||||
t.validate()
|
||||
}
|
||||
|
||||
@ -123,11 +125,7 @@ func (t *dnsConfigMapTest) validate() {
|
||||
}
|
||||
|
||||
func (t *dnsConfigMapTest) validateFederation() {
|
||||
federations := make(map[string]string)
|
||||
if t.cm != nil {
|
||||
err := fed.ParseFederationsFlag(t.cm.Data["federations"], federations)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
federations := t.fedMap
|
||||
|
||||
if len(federations) == 0 {
|
||||
By(fmt.Sprintf("Validating federation labels %v do not exist", t.labels))
|
||||
@ -210,9 +208,10 @@ func (t *dnsConfigMapTest) runDig(dnsName string) []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *dnsConfigMapTest) setConfigMap(cm *v1.ConfigMap, isValid bool) {
|
||||
func (t *dnsConfigMapTest) setConfigMap(cm *v1.ConfigMap, fedMap map[string]string, isValid bool) {
|
||||
if isValid {
|
||||
t.cm = cm
|
||||
t.fedMap = fedMap
|
||||
}
|
||||
t.isValid = isValid
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user