Merge pull request #36048 from madhusudancs/federation-kubefed-init-10c

Automatic merge from submit-queue

[Federation][init-10c] Fix hyperkube binary path in the image and disable auth mechanisms we don't support for now.

Please review only the last commit here. This is based on PR #36047 which will be reviewed independently.

Design Doc: PR #34484

cc @kubernetes/sig-cluster-federation @nikhiljindal
This commit is contained in:
Kubernetes Submit Queue 2016-11-06 20:46:52 -08:00 committed by GitHub
commit 3c17fa5d36
3 changed files with 625 additions and 19 deletions

View File

@ -21,12 +21,15 @@ go_library(
"//pkg/api/resource:go_default_library", "//pkg/api/resource:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/util/cert:go_default_library", "//pkg/util/cert:go_default_library",
"//pkg/util/cert/triple:go_default_library", "//pkg/util/cert/triple:go_default_library",
"//pkg/util/intstr:go_default_library", "//pkg/util/intstr:go_default_library",
"//pkg/util/wait:go_default_library", "//pkg/util/wait:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/cobra",
], ],
) )
@ -36,5 +39,22 @@ go_test(
srcs = ["init_test.go"], srcs = ["init_test.go"],
library = "go_default_library", library = "go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
deps = [], deps = [
"//federation/pkg/kubefed/testing:go_default_library",
"//federation/pkg/kubefed/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/client/typed/dynamic:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/util/intstr:go_default_library",
"//vendor:k8s.io/client-go/pkg/util/diff",
],
) )

View File

@ -42,12 +42,15 @@ import (
"k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
certutil "k8s.io/kubernetes/pkg/util/cert" certutil "k8s.io/kubernetes/pkg/util/cert"
triple "k8s.io/kubernetes/pkg/util/cert/triple" triple "k8s.io/kubernetes/pkg/util/cert/triple"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -55,6 +58,7 @@ import (
const ( const (
APIServerCN = "federation-apiserver" APIServerCN = "federation-apiserver"
ControllerManagerCN = "federation-controller-manager" ControllerManagerCN = "federation-controller-manager"
AdminCN = "admin"
HostClusterLocalDNSZoneName = "cluster.local." HostClusterLocalDNSZoneName = "cluster.local."
lbAddrRetryInterval = 5 * time.Second lbAddrRetryInterval = 5 * time.Second
@ -92,7 +96,7 @@ var (
"module": "federation-controller-manager", "module": "federation-controller-manager",
} }
hyperkubeImage = "gcr.io/google_containers/hyperkube-amd64:v1.5.0" hyperkubeImageName = "gcr.io/google_containers/hyperkube-amd64"
) )
// NewCmdInit defines the `init` command that bootstraps a federation // NewCmdInit defines the `init` command that bootstraps a federation
@ -109,8 +113,11 @@ func NewCmdInit(cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
}, },
} }
defaultImage := fmt.Sprintf("%s:%s", hyperkubeImageName, version.Get())
util.AddSubcommandFlags(cmd) util.AddSubcommandFlags(cmd)
cmd.Flags().String("dns-zone-name", "", "DNS suffix for this federation. Federated Service DNS names are published with this suffix.") cmd.Flags().String("dns-zone-name", "", "DNS suffix for this federation. Federated Service DNS names are published with this suffix.")
cmd.Flags().String("image", defaultImage, "Image to use for federation API server and controller manager binaries.")
return cmd return cmd
} }
@ -118,6 +125,7 @@ type entityKeyPairs struct {
ca *triple.KeyPair ca *triple.KeyPair
server *triple.KeyPair server *triple.KeyPair
controllerManager *triple.KeyPair controllerManager *triple.KeyPair
admin *triple.KeyPair
} }
// initFederation initializes a federation control plane. // initFederation initializes a federation control plane.
@ -129,6 +137,7 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
return err return err
} }
dnsZoneName := cmdutil.GetFlagString(cmd, "dns-zone-name") dnsZoneName := cmdutil.GetFlagString(cmd, "dns-zone-name")
image := cmdutil.GetFlagString(cmd, "image")
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig) hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig)
hostClientset, err := hostFactory.ClientSet() hostClientset, err := hostFactory.ClientSet()
@ -189,14 +198,26 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
advertiseAddress = ips[0] advertiseAddress = ips[0]
} }
endpoint := advertiseAddress
if advertiseAddress == "" && len(hostnames) > 0 {
endpoint = hostnames[0]
}
// 6. Create federation API server // 6. Create federation API server
_, err = createAPIServer(hostClientset, initFlags.FederationSystemNamespace, serverName, serverCredName, pvc.Name, advertiseAddress) _, err = createAPIServer(hostClientset, initFlags.FederationSystemNamespace, serverName, image, serverCredName, pvc.Name, advertiseAddress)
if err != nil { if err != nil {
return err return err
} }
// 7. Create federation controller manager // 7. Create federation controller manager
_, err = createControllerManager(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, cmName, cmKubeconfigName, dnsZoneName) _, err = createControllerManager(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, cmName, image, cmKubeconfigName, dnsZoneName)
if err != nil {
return err
}
// 8. Write the federation API server endpoint info, credentials
// and context to kubeconfig
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs)
if err != nil { if err != nil {
return err return err
} }
@ -282,10 +303,15 @@ func genCerts(svcNamespace, name, svcName, localDNSZoneName string, ips, hostnam
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create federation controller manager client key and certificate: %v", err) return nil, fmt.Errorf("failed to create federation controller manager client key and certificate: %v", err)
} }
admin, err := triple.NewClientKeyPair(ca, AdminCN)
if err != nil {
return nil, fmt.Errorf("failed to create client key and certificate for an admin: %v", err)
}
return &entityKeyPairs{ return &entityKeyPairs{
ca: ca, ca: ca,
server: server, server: server,
controllerManager: cm, controllerManager: cm,
admin: admin,
}, nil }, nil
} }
@ -305,7 +331,6 @@ func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, cr
// Boilerplate to create the secret in the host cluster. // Boilerplate to create the secret in the host cluster.
return clientset.Core().Secrets(namespace).Create(secret) return clientset.Core().Secrets(namespace).Create(secret)
} }
func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs) (*api.Secret, error) { func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs) (*api.Secret, error) {
@ -356,7 +381,7 @@ func createPVC(clientset *client.Clientset, namespace, svcName string) (*api.Per
return clientset.Core().PersistentVolumeClaims(namespace).Create(pvc) return clientset.Core().PersistentVolumeClaims(namespace).Create(pvc)
} }
func createAPIServer(clientset *client.Clientset, namespace, name, credentialsName, pvcName, advertiseAddress string) (*extensions.Deployment, error) { func createAPIServer(clientset *client.Clientset, namespace, name, image, credentialsName, pvcName, advertiseAddress string) (*extensions.Deployment, error) {
command := []string{ command := []string{
"/hyperkube", "/hyperkube",
"federation-apiserver", "federation-apiserver",
@ -364,8 +389,6 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
"--etcd-servers=http://localhost:2379", "--etcd-servers=http://localhost:2379",
"--service-cluster-ip-range=10.0.0.0/16", "--service-cluster-ip-range=10.0.0.0/16",
"--secure-port=443", "--secure-port=443",
"--token-auth-file=/etc/federation/apiserver/known_tokens.csv",
"--basic-auth-file=/etc/federation/apiserver/basic_auth.csv",
"--client-ca-file=/etc/federation/apiserver/ca.crt", "--client-ca-file=/etc/federation/apiserver/ca.crt",
"--tls-cert-file=/etc/federation/apiserver/server.crt", "--tls-cert-file=/etc/federation/apiserver/server.crt",
"--tls-private-key-file=/etc/federation/apiserver/server.key", "--tls-private-key-file=/etc/federation/apiserver/server.key",
@ -394,7 +417,7 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
Containers: []api.Container{ Containers: []api.Container{
{ {
Name: "apiserver", Name: "apiserver",
Image: hyperkubeImage, Image: image,
Command: command, Command: command,
Ports: []api.ContainerPort{ Ports: []api.ContainerPort{
{ {
@ -456,7 +479,7 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
return clientset.Extensions().Deployments(namespace).Create(dep) return clientset.Extensions().Deployments(namespace).Create(dep)
} }
func createControllerManager(clientset *client.Clientset, namespace, name, cmName, kubeconfigName, dnsZoneName string) (*extensions.Deployment, error) { func createControllerManager(clientset *client.Clientset, namespace, name, cmName, image, kubeconfigName, dnsZoneName string) (*extensions.Deployment, error) {
dep := &extensions.Deployment{ dep := &extensions.Deployment{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: cmName, Name: cmName,
@ -474,7 +497,7 @@ func createControllerManager(clientset *client.Clientset, namespace, name, cmNam
Containers: []api.Container{ Containers: []api.Container{
{ {
Name: "controller-manager", Name: "controller-manager",
Image: hyperkubeImage, Image: image,
Command: []string{ Command: []string{
"/hyperkube", "/hyperkube",
"federation-controller-manager", "federation-controller-manager",
@ -527,3 +550,44 @@ func printSuccess(cmdOut io.Writer, ips, hostnames []string) error {
_, err := fmt.Fprintf(cmdOut, "Federation API server is running at: %s\n", strings.Join(svcEndpoints, ", ")) _, err := fmt.Fprintf(cmdOut, "Federation API server is running at: %s\n", strings.Join(svcEndpoints, ", "))
return err return err
} }
func updateKubeconfig(config util.AdminConfig, name, endpoint string, entKeyPairs *entityKeyPairs) error {
po := config.PathOptions()
kubeconfig, err := po.GetStartingConfig()
if err != nil {
return err
}
// Populate API server endpoint info.
cluster := clientcmdapi.NewCluster()
// Prefix "https" as the URL scheme to endpoint.
if !strings.HasPrefix(endpoint, "https://") {
endpoint = fmt.Sprintf("https://%s", endpoint)
}
cluster.Server = endpoint
cluster.CertificateAuthorityData = certutil.EncodeCertPEM(entKeyPairs.ca.Cert)
// Populate credentials.
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientCertificateData = certutil.EncodeCertPEM(entKeyPairs.admin.Cert)
authInfo.ClientKeyData = certutil.EncodePrivateKeyPEM(entKeyPairs.admin.Key)
authInfo.Username = AdminCN
// Populate context.
context := clientcmdapi.NewContext()
context.Cluster = name
context.AuthInfo = name
// Update the config struct with API server endpoint info,
// credentials and context.
kubeconfig.Clusters[name] = cluster
kubeconfig.AuthInfos[name] = authInfo
kubeconfig.Contexts[name] = context
// Write the update kubeconfig.
if err := clientcmd.ModifyConfig(po, *kubeconfig, true); err != nil {
return err
}
return nil
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package init package init
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
@ -28,6 +29,23 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"k8s.io/client-go/pkg/util/diff"
kubefedtesting "k8s.io/kubernetes/federation/pkg/kubefed/testing"
"k8s.io/kubernetes/federation/pkg/kubefed/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/restclient/fake"
"k8s.io/kubernetes/pkg/client/typed/dynamic"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/intstr"
) )
const ( const (
@ -38,15 +56,79 @@ const (
helloMsg = "Hello, certificate test!" helloMsg = "Hello, certificate test!"
) )
type clientServerTLSConfigs struct { func TestInitFederation(t *testing.T) {
server *tls.Config cmdErrMsg := ""
client *tls.Config cmdutil.BehaviorOnFatal(func(str string, code int) {
cmdErrMsg = str
})
fakeKubeFiles, err := kubefedtesting.FakeKubeconfigFiles()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer kubefedtesting.RemoveFakeKubeconfigFiles(fakeKubeFiles)
testCases := []struct {
federation string
kubeconfigGlobal string
kubeconfigExplicit string
dnsZoneName string
lbIP string
image string
expectedErr string
}{
{
federation: "union",
kubeconfigGlobal: fakeKubeFiles[0],
kubeconfigExplicit: "",
dnsZoneName: "example.test.",
lbIP: "10.20.30.40",
image: "example.test/foo:bar",
expectedErr: "",
},
} }
type certParams struct { for i, tc := range testCases {
cAddr string cmdErrMsg = ""
ips []string buf := bytes.NewBuffer([]byte{})
hostnames []string
hostFactory, err := fakeInitHostFactory(tc.federation, util.DefaultFederationSystemNamespace, tc.lbIP, tc.dnsZoneName, tc.image)
if err != nil {
t.Fatalf("[%d] unexpected error: %v", i, err)
}
adminConfig, err := kubefedtesting.NewFakeAdminConfig(hostFactory, tc.kubeconfigGlobal)
if err != nil {
t.Fatalf("[%d] unexpected error: %v", i, err)
}
cmd := NewCmdInit(buf, adminConfig)
cmd.Flags().Set("kubeconfig", tc.kubeconfigExplicit)
cmd.Flags().Set("host-cluster-context", "substrate")
cmd.Flags().Set("dns-zone-name", tc.dnsZoneName)
cmd.Flags().Set("image", tc.image)
cmd.Run(cmd, []string{tc.federation})
if tc.expectedErr == "" {
// uses the name from the federation, not the response
// Actual data passed are tested in the fake secret and cluster
// REST clients.
want := fmt.Sprintf("Federation API server is running at: %s\n", tc.lbIP)
if got := buf.String(); got != want {
t.Errorf("[%d] unexpected output: got: %s, want: %s", i, got, want)
if cmdErrMsg != "" {
t.Errorf("[%d] unexpected error message: %s", i, cmdErrMsg)
}
}
} else {
if cmdErrMsg != tc.expectedErr {
t.Errorf("[%d] expected error: %s, got: %s, output: %s", i, tc.expectedErr, cmdErrMsg, buf.String())
}
}
testKubeconfigUpdate(t, tc.federation, tc.lbIP, tc.kubeconfigGlobal, tc.kubeconfigExplicit)
}
} }
// TestCertsTLS tests TLS handshake with client authentication for any server // TestCertsTLS tests TLS handshake with client authentication for any server
@ -288,6 +370,446 @@ func TestCertsHTTPS(t *testing.T) {
} }
} }
func fakeInitHostFactory(federationName, namespaceName, ip, dnsZoneName, image string) (cmdutil.Factory, error) {
svcName := federationName + "-apiserver"
svcUrlPrefix := "/api/v1/namespaces/federation-system/services"
credSecretName := svcName + "-credentials"
cmKubeconfigSecretName := federationName + "-controller-manager-kubeconfig"
capacity, err := resource.ParseQuantity("10Gi")
if err != nil {
return nil, err
}
pvcName := svcName + "-etcd-claim"
replicas := int32(1)
namespace := v1.Namespace{
TypeMeta: unversioned.TypeMeta{
Kind: "Namespace",
APIVersion: testapi.Default.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: namespaceName,
},
}
svc := v1.Service{
TypeMeta: unversioned.TypeMeta{
Kind: "Service",
APIVersion: testapi.Default.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Namespace: namespaceName,
Name: svcName,
Labels: componentLabel,
},
Spec: v1.ServiceSpec{
Type: v1.ServiceTypeLoadBalancer,
Selector: apiserverSvcSelector,
Ports: []v1.ServicePort{
{
Name: "https",
Protocol: "TCP",
Port: 443,
TargetPort: intstr.FromInt(443),
},
},
},
}
svcWithLB := svc
svcWithLB.Status = v1.ServiceStatus{
LoadBalancer: v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{
IP: ip,
},
},
},
}
credSecret := v1.Secret{
TypeMeta: unversioned.TypeMeta{
Kind: "Secret",
APIVersion: testapi.Default.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: credSecretName,
Namespace: namespaceName,
},
Data: nil,
}
cmKubeconfigSecret := v1.Secret{
TypeMeta: unversioned.TypeMeta{
Kind: "Secret",
APIVersion: testapi.Default.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: cmKubeconfigSecretName,
Namespace: namespaceName,
},
Data: nil,
}
pvc := v1.PersistentVolumeClaim{
TypeMeta: unversioned.TypeMeta{
Kind: "PersistentVolumeClaim",
APIVersion: testapi.Default.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: pvcName,
Namespace: namespaceName,
Labels: componentLabel,
Annotations: map[string]string{
"volume.alpha.kubernetes.io/storage-class": "yes",
},
},
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: capacity,
},
},
},
}
apiserver := v1beta1.Deployment{
TypeMeta: unversioned.TypeMeta{
Kind: "Deployment",
APIVersion: testapi.Extensions.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: svcName,
Namespace: namespaceName,
Labels: componentLabel,
},
Spec: v1beta1.DeploymentSpec{
Replicas: &replicas,
Selector: nil,
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Name: svcName,
Labels: apiserverPodLabels,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "apiserver",
Image: image,
Command: []string{
"/hyperkube",
"federation-apiserver",
"--bind-address=0.0.0.0",
"--etcd-servers=http://localhost:2379",
"--service-cluster-ip-range=10.0.0.0/16",
"--secure-port=443",
"--client-ca-file=/etc/federation/apiserver/ca.crt",
"--tls-cert-file=/etc/federation/apiserver/server.crt",
"--tls-private-key-file=/etc/federation/apiserver/server.key",
"--advertise-address=" + ip,
},
Ports: []v1.ContainerPort{
{
Name: "https",
ContainerPort: 443,
},
{
Name: "local",
ContainerPort: 8080,
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: credSecretName,
MountPath: "/etc/federation/apiserver",
ReadOnly: true,
},
},
},
{
Name: "etcd",
Image: "quay.io/coreos/etcd:v2.3.3",
Command: []string{
"/etcd",
"--data-dir",
"/var/etcd/data",
},
VolumeMounts: []v1.VolumeMount{
{
Name: "etcddata",
MountPath: "/var/etcd",
},
},
},
},
Volumes: []v1.Volume{
{
Name: credSecretName,
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: credSecretName,
},
},
},
{
Name: "etcddata",
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: pvcName,
},
},
},
},
},
},
},
}
cmName := federationName + "-controller-manager"
cm := v1beta1.Deployment{
TypeMeta: unversioned.TypeMeta{
Kind: "Deployment",
APIVersion: testapi.Extensions.GroupVersion().String(),
},
ObjectMeta: v1.ObjectMeta{
Name: cmName,
Namespace: namespaceName,
Labels: componentLabel,
},
Spec: v1beta1.DeploymentSpec{
Replicas: &replicas,
Selector: nil,
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Name: cmName,
Labels: controllerManagerPodLabels,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "controller-manager",
Image: image,
Command: []string{
"/hyperkube",
"federation-controller-manager",
"--master=https://federation-apiserver",
"--kubeconfig=/etc/federation/controller-manager/kubeconfig",
"--dns-provider=gce",
"--dns-provider-config=",
fmt.Sprintf("--federation-name=%s", federationName),
fmt.Sprintf("--zone-name=%s", dnsZoneName),
},
VolumeMounts: []v1.VolumeMount{
{
Name: cmKubeconfigSecretName,
MountPath: "/etc/federation/controller-manager",
ReadOnly: true,
},
},
Env: []v1.EnvVar{
{
Name: "POD_NAMESPACE",
ValueFrom: &v1.EnvVarSource{
FieldRef: &v1.ObjectFieldSelector{
FieldPath: "metadata.namespace",
},
},
},
},
},
},
Volumes: []v1.Volume{
{
Name: cmKubeconfigSecretName,
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: cmKubeconfigSecretName,
},
},
},
},
},
},
},
}
f, tf, codec, _ := cmdtesting.NewAPIFactory()
extCodec := testapi.Extensions.Codec()
ns := dynamic.ContentConfig().NegotiatedSerializer
tf.ClientConfig = kubefedtesting.DefaultClientConfig()
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/api/v1/namespaces" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got v1.Namespace
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
}
if !api.Semantic.DeepEqual(got, namespace) {
return nil, fmt.Errorf("Unexpected namespace object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, namespace))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &namespace)}, nil
case p == svcUrlPrefix && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got v1.Service
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
}
if !api.Semantic.DeepEqual(got, svc) {
return nil, fmt.Errorf("Unexpected service object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, svc))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &svc)}, nil
case strings.HasPrefix(p, svcUrlPrefix) && m == http.MethodGet:
got := strings.TrimPrefix(p, svcUrlPrefix+"/")
if got != svcName {
return nil, errors.NewNotFound(api.Resource("services"), got)
}
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &svcWithLB)}, nil
case p == "/api/v1/namespaces/federation-system/secrets" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got, want v1.Secret
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
}
// Obtained secret contains generated data which cannot
// be compared, so we just nullify the generated part
// and compare the rest of the secret. The generated
// parts are tested in other tests.
got.Data = nil
switch got.Name {
case credSecretName:
want = credSecret
case cmKubeconfigSecretName:
want = cmKubeconfigSecret
}
if !api.Semantic.DeepEqual(got, want) {
return nil, fmt.Errorf("Unexpected secret object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, want))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &want)}, nil
case p == "/api/v1/namespaces/federation-system/persistentvolumeclaims" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got v1.PersistentVolumeClaim
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
}
if !api.Semantic.DeepEqual(got, pvc) {
return nil, fmt.Errorf("Unexpected PVC object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, pvc))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &pvc)}, nil
case p == "/apis/extensions/v1beta1/namespaces/federation-system/deployments" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got, want v1beta1.Deployment
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
}
switch got.Name {
case svcName:
want = apiserver
case cmName:
want = cm
}
if !api.Semantic.DeepEqual(got, want) {
return nil, fmt.Errorf("Unexpected deployment object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, want))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(extCodec, &want)}, nil
default:
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
}
}),
}
return f, nil
}
func testKubeconfigUpdate(t *testing.T, federationName, lbIP, kubeconfigGlobal, kubeconfigExplicit string) {
filename := kubeconfigGlobal
if kubeconfigExplicit != "" {
filename = kubeconfigExplicit
}
config, err := clientcmd.LoadFromFile(filename)
if err != nil {
t.Errorf("Failed to open kubeconfig file: %v", err)
return
}
cluster, ok := config.Clusters[federationName]
if !ok {
t.Errorf("No cluster info for %q", federationName)
return
}
endpoint := lbIP
if !strings.HasSuffix(lbIP, "https://") {
endpoint = fmt.Sprintf("https://%s", lbIP)
}
if cluster.Server != endpoint {
t.Errorf("Want federation API server endpoint %q, got %q", endpoint, cluster.Server)
}
authInfo, ok := config.AuthInfos[federationName]
if !ok {
t.Errorf("No credentials for %q", federationName)
return
}
if len(authInfo.ClientCertificateData) == 0 {
t.Errorf("Expected client certificate to be non-empty")
return
}
if len(authInfo.ClientKeyData) == 0 {
t.Errorf("Expected client key to be non-empty")
return
}
if authInfo.Username != AdminCN {
t.Errorf("Want username: %q, got: %q", AdminCN, authInfo.Username)
}
context, ok := config.Contexts[federationName]
if !ok {
t.Errorf("No context for %q", federationName)
return
}
if context.Cluster != federationName {
t.Errorf("Want context cluster name: %q, got: %q", federationName, context.Cluster)
}
if context.AuthInfo != federationName {
t.Errorf("Want context auth info: %q, got: %q", federationName, context.AuthInfo)
}
}
type clientServerTLSConfigs struct {
server *tls.Config
client *tls.Config
}
type certParams struct {
cAddr string
ips []string
hostnames []string
}
func tlsHandshake(t *testing.T, sCfg, cCfg *tls.Config) error { func tlsHandshake(t *testing.T, sCfg, cCfg *tls.Config) error {
// Tried to use net.Pipe() instead of TCP. But the connections returned by // Tried to use net.Pipe() instead of TCP. But the connections returned by
// net.Pipe() do a fully-synchronous reads and writes on both the ends. // net.Pipe() do a fully-synchronous reads and writes on both the ends.