mirror of https://github.com/k3s-io/k3s
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 @nikhiljindalpull/6/head
commit
3c17fa5d36
|
@ -21,12 +21,15 @@ go_library(
|
|||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/apis/extensions: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/util:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
"//pkg/util/cert/triple:go_default_library",
|
||||
"//pkg/util/intstr:go_default_library",
|
||||
"//pkg/util/wait:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
],
|
||||
)
|
||||
|
@ -36,5 +39,22 @@ go_test(
|
|||
srcs = ["init_test.go"],
|
||||
library = "go_default_library",
|
||||
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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -42,12 +42,15 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
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"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
triple "k8s.io/kubernetes/pkg/util/cert/triple"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -55,6 +58,7 @@ import (
|
|||
const (
|
||||
APIServerCN = "federation-apiserver"
|
||||
ControllerManagerCN = "federation-controller-manager"
|
||||
AdminCN = "admin"
|
||||
HostClusterLocalDNSZoneName = "cluster.local."
|
||||
|
||||
lbAddrRetryInterval = 5 * time.Second
|
||||
|
@ -92,7 +96,7 @@ var (
|
|||
"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
|
||||
|
@ -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)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -118,6 +125,7 @@ type entityKeyPairs struct {
|
|||
ca *triple.KeyPair
|
||||
server *triple.KeyPair
|
||||
controllerManager *triple.KeyPair
|
||||
admin *triple.KeyPair
|
||||
}
|
||||
|
||||
// initFederation initializes a federation control plane.
|
||||
|
@ -129,6 +137,7 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||
return err
|
||||
}
|
||||
dnsZoneName := cmdutil.GetFlagString(cmd, "dns-zone-name")
|
||||
image := cmdutil.GetFlagString(cmd, "image")
|
||||
|
||||
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig)
|
||||
hostClientset, err := hostFactory.ClientSet()
|
||||
|
@ -189,14 +198,26 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||
advertiseAddress = ips[0]
|
||||
}
|
||||
|
||||
endpoint := advertiseAddress
|
||||
if advertiseAddress == "" && len(hostnames) > 0 {
|
||||
endpoint = hostnames[0]
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -282,10 +303,15 @@ func genCerts(svcNamespace, name, svcName, localDNSZoneName string, ips, hostnam
|
|||
if err != nil {
|
||||
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{
|
||||
ca: ca,
|
||||
server: server,
|
||||
controllerManager: cm,
|
||||
admin: admin,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -305,7 +331,6 @@ func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, cr
|
|||
|
||||
// Boilerplate to create the secret in the host cluster.
|
||||
return clientset.Core().Secrets(namespace).Create(secret)
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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{
|
||||
"/hyperkube",
|
||||
"federation-apiserver",
|
||||
|
@ -364,8 +389,6 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
|
|||
"--etcd-servers=http://localhost:2379",
|
||||
"--service-cluster-ip-range=10.0.0.0/16",
|
||||
"--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",
|
||||
"--tls-cert-file=/etc/federation/apiserver/server.crt",
|
||||
"--tls-private-key-file=/etc/federation/apiserver/server.key",
|
||||
|
@ -394,7 +417,7 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
|
|||
Containers: []api.Container{
|
||||
{
|
||||
Name: "apiserver",
|
||||
Image: hyperkubeImage,
|
||||
Image: image,
|
||||
Command: command,
|
||||
Ports: []api.ContainerPort{
|
||||
{
|
||||
|
@ -456,7 +479,7 @@ func createAPIServer(clientset *client.Clientset, namespace, name, credentialsNa
|
|||
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{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: cmName,
|
||||
|
@ -474,7 +497,7 @@ func createControllerManager(clientset *client.Clientset, namespace, name, cmNam
|
|||
Containers: []api.Container{
|
||||
{
|
||||
Name: "controller-manager",
|
||||
Image: hyperkubeImage,
|
||||
Image: image,
|
||||
Command: []string{
|
||||
"/hyperkube",
|
||||
"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, ", "))
|
||||
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
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package init
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
@ -28,6 +29,23 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
"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 (
|
||||
|
@ -38,15 +56,79 @@ const (
|
|||
helloMsg = "Hello, certificate test!"
|
||||
)
|
||||
|
||||
type clientServerTLSConfigs struct {
|
||||
server *tls.Config
|
||||
client *tls.Config
|
||||
}
|
||||
func TestInitFederation(t *testing.T) {
|
||||
cmdErrMsg := ""
|
||||
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||
cmdErrMsg = str
|
||||
})
|
||||
|
||||
type certParams struct {
|
||||
cAddr string
|
||||
ips []string
|
||||
hostnames []string
|
||||
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: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
cmdErrMsg = ""
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
|
||||
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
|
||||
|
@ -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 {
|
||||
// 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.
|
||||
|
|
Loading…
Reference in New Issue