diff --git a/pkg/model/components/apiserver.go b/pkg/model/components/apiserver.go index f8170b67c3..15f1764bd5 100644 --- a/pkg/model/components/apiserver.go +++ b/pkg/model/components/apiserver.go @@ -33,29 +33,29 @@ type KubeAPIServerOptionsBuilder struct { var _ loader.OptionsBuilder = &KubeAPIServerOptionsBuilder{} func (b *KubeAPIServerOptionsBuilder) BuildOptions(o interface{}) error { - options := o.(*kops.ClusterSpec) - if options.KubeAPIServer == nil { - options.KubeAPIServer = &kops.KubeAPIServerConfig{} + clusterSpec := o.(*kops.ClusterSpec) + if clusterSpec.KubeAPIServer == nil { + clusterSpec.KubeAPIServer = &kops.KubeAPIServerConfig{} } - if options.KubeAPIServer.APIServerCount == nil { - count := b.buildAPIServerCount() + if clusterSpec.KubeAPIServer.APIServerCount == nil { + count := b.buildAPIServerCount(clusterSpec) if count == 0 { return fmt.Errorf("no instance groups found") } - options.KubeAPIServer.APIServerCount = fi.Int32(int32(count)) + clusterSpec.KubeAPIServer.APIServerCount = fi.Int32(int32(count)) } - if options.KubeAPIServer.StorageBackend == nil { + if clusterSpec.KubeAPIServer.StorageBackend == nil { // For the moment, we continue to use etcd2 - options.KubeAPIServer.StorageBackend = fi.String("etcd2") + clusterSpec.KubeAPIServer.StorageBackend = fi.String("etcd2") } - k8sVersion, err := b.Context.KubernetesVersion() + k8sVersion, err := KubernetesVersion(clusterSpec) if err != nil { return err } - if options.KubeAPIServer.KubeletPreferredAddressTypes == nil { + if clusterSpec.KubeAPIServer.KubeletPreferredAddressTypes == nil { if k8sVersion.GTE(semver.MustParse("1.5.0")) { // Default precedence //options.KubeAPIServer.KubeletPreferredAddressTypes = []string { @@ -66,7 +66,7 @@ func (b *KubeAPIServerOptionsBuilder) BuildOptions(o interface{}) error { //} // We prioritize the internal IP above the hostname - options.KubeAPIServer.KubeletPreferredAddressTypes = []string{ + clusterSpec.KubeAPIServer.KubeletPreferredAddressTypes = []string{ string(api.NodeInternalIP), string(api.NodeHostName), string(api.NodeExternalIP), @@ -78,7 +78,7 @@ func (b *KubeAPIServerOptionsBuilder) BuildOptions(o interface{}) error { return nil } -func (b *KubeAPIServerOptionsBuilder) buildAPIServerCount() int { +func (b *KubeAPIServerOptionsBuilder) buildAPIServerCount(clusterSpec *kops.ClusterSpec) int { // The --apiserver-count flag is (generally agreed) to be something we need to get rid of in k8s // We should do something like this: @@ -103,7 +103,7 @@ func (b *KubeAPIServerOptionsBuilder) buildAPIServerCount() int { // the flag won't exist counts := make(map[string]int) - for _, etcdCluster := range b.Context.Cluster.Spec.EtcdClusters { + for _, etcdCluster := range clusterSpec.EtcdClusters { counts[etcdCluster.Name] = len(etcdCluster.Members) } diff --git a/pkg/model/components/context.go b/pkg/model/components/context.go index b8d3177b79..6255dfad08 100644 --- a/pkg/model/components/context.go +++ b/pkg/model/components/context.go @@ -17,20 +17,22 @@ limitations under the License. package components import ( + "encoding/binary" "fmt" "github.com/blang/semver" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/util" + "math/big" + "net" ) // OptionsContext is the context object for options builders type OptionsContext struct { - Cluster *kops.Cluster } // KubernetesVersion parses the semver version of kubernetes, from the cluster spec -func (c *OptionsContext) KubernetesVersion() (*semver.Version, error) { - kubernetesVersion := c.Cluster.Spec.KubernetesVersion +func KubernetesVersion(clusterSpec *kops.ClusterSpec) (*semver.Version, error) { + kubernetesVersion := clusterSpec.KubernetesVersion if kubernetesVersion == "" { return nil, fmt.Errorf("KubernetesVersion is required") @@ -45,8 +47,8 @@ func (c *OptionsContext) KubernetesVersion() (*semver.Version, error) { } // UsesKubenet returns true if our networking is derived from kubenet -func (c *OptionsContext) UsesKubenet() (bool, error) { - networking := c.Cluster.Spec.Networking +func UsesKubenet(clusterSpec *kops.ClusterSpec) (bool, error) { + networking := clusterSpec.Networking if networking == nil || networking.Classic != nil { return false, nil } else if networking.Kubenet != nil { @@ -63,3 +65,35 @@ func (c *OptionsContext) UsesKubenet() (bool, error) { return false, fmt.Errorf("No networking mode set") } } + +func WellKnownServiceIP(clusterSpec *kops.ClusterSpec, id int) (net.IP, error) { + _, cidr, err := net.ParseCIDR(clusterSpec.ServiceClusterIPRange) + if err != nil { + return nil, fmt.Errorf("error parsing ServiceClusterIPRange %q: %v", clusterSpec.ServiceClusterIPRange, err) + } + + ip4 := cidr.IP.To4() + if ip4 != nil { + n := binary.BigEndian.Uint32(ip4) + n += uint32(id) + serviceIP := make(net.IP, len(ip4)) + binary.BigEndian.PutUint32(serviceIP, n) + return serviceIP, nil + } + + ip6 := cidr.IP.To16() + if ip6 != nil { + baseIPInt := big.NewInt(0) + baseIPInt.SetBytes(ip6) + serviceIPInt := big.NewInt(0) + serviceIPInt.Add(big.NewInt(int64(id)), baseIPInt) + serviceIP := make(net.IP, len(ip6)) + serviceIPBytes := serviceIPInt.Bytes() + for i := range serviceIPBytes { + serviceIP[len(serviceIP)-len(serviceIPBytes)+i] = serviceIPBytes[i] + } + return serviceIP, nil + } + + return nil, fmt.Errorf("Unexpected IP address type for ServiceClusterIPRange: %s", clusterSpec.ServiceClusterIPRange) +} diff --git a/pkg/model/components/defaults.go b/pkg/model/components/defaults.go new file mode 100644 index 0000000000..c3b674c749 --- /dev/null +++ b/pkg/model/components/defaults.go @@ -0,0 +1,39 @@ +/* +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 components + +import ( + "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/upup/pkg/fi/loader" +) + +// DefaultsOptionsBuilder adds default options. This should come first! +type DefaultsOptionsBuilder struct { + Context *OptionsContext +} + +var _ loader.OptionsBuilder = &DefaultsOptionsBuilder{} + +func (b *DefaultsOptionsBuilder) BuildOptions(o interface{}) error { + options := o.(*kops.ClusterSpec) + + if options.ClusterDNSDomain == "" { + options.ClusterDNSDomain = "cluster.local" + } + + return nil +} diff --git a/pkg/model/components/docker.go b/pkg/model/components/docker.go index bfe06f32f2..787d493dde 100644 --- a/pkg/model/components/docker.go +++ b/pkg/model/components/docker.go @@ -31,19 +31,19 @@ type DockerOptionsBuilder struct { var _ loader.OptionsBuilder = &DockerOptionsBuilder{} func (b *DockerOptionsBuilder) BuildOptions(o interface{}) error { - options := o.(*kops.ClusterSpec) - if options.Docker == nil { - options.Docker = &kops.DockerConfig{} + clusterSpec := o.(*kops.ClusterSpec) + if clusterSpec.Docker == nil { + clusterSpec.Docker = &kops.DockerConfig{} } - if fi.StringValue(options.Docker.Version) == "" { - if options.KubernetesVersion == "" { + if fi.StringValue(clusterSpec.Docker.Version) == "" { + if clusterSpec.KubernetesVersion == "" { return fmt.Errorf("KubernetesVersion is required") } - sv, err := b.Context.KubernetesVersion() + sv, err := KubernetesVersion(clusterSpec) if err != nil { - return fmt.Errorf("unable to determine kubernetes version from %q", options.KubernetesVersion) + return fmt.Errorf("unable to determine kubernetes version from %q", clusterSpec.KubernetesVersion) } dockerVersion := "" @@ -54,10 +54,10 @@ func (b *DockerOptionsBuilder) BuildOptions(o interface{}) error { } if dockerVersion == "" { - return fmt.Errorf("unknown version of kubernetes %q (cannot infer docker version)", options.KubernetesVersion) + return fmt.Errorf("unknown version of kubernetes %q (cannot infer docker version)", clusterSpec.KubernetesVersion) } - options.Docker.Version = &dockerVersion + clusterSpec.Docker.Version = &dockerVersion } return nil diff --git a/pkg/model/components/kubecontrollermanager.go b/pkg/model/components/kubecontrollermanager.go index c101da9d37..665e543c05 100644 --- a/pkg/model/components/kubecontrollermanager.go +++ b/pkg/model/components/kubecontrollermanager.go @@ -41,10 +41,10 @@ var _ loader.OptionsBuilder = &KubeControllerManagerOptionsBuilder{} // BuildOptions generates the configurations used to create kubernetes controller manager manifest func (b *KubeControllerManagerOptionsBuilder) BuildOptions(o interface{}) error { - options := o.(*kops.ClusterSpec) + clusterSpec := o.(*kops.ClusterSpec) - if options.KubeControllerManager == nil { - options.KubeControllerManager = &kops.KubeControllerManagerConfig{} + if clusterSpec.KubeControllerManager == nil { + clusterSpec.KubeControllerManager = &kops.KubeControllerManagerConfig{} } k8sv148, err := util.ParseKubernetesVersion("v1.4.8") @@ -63,7 +63,7 @@ func (b *KubeControllerManagerOptionsBuilder) BuildOptions(o interface{}) error return fmt.Errorf("Unable to parse kubernetesVersion %s", err) } - kubernetesVersion, err := b.Context.KubernetesVersion() + kubernetesVersion, err := KubernetesVersion(clusterSpec) if err != nil { return fmt.Errorf("Unable to parse kubernetesVersion %s", err) } @@ -78,27 +78,27 @@ func (b *KubeControllerManagerOptionsBuilder) BuildOptions(o interface{}) error glog.V(4).Infof("Kubernetes version %q supports AttachDetachReconcileSyncPeriod; will configure", kubernetesVersion) // If not set ... or set to 0s ... which is stupid - if options.KubeControllerManager.AttachDetachReconcileSyncPeriod == nil || - options.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration.String() == "0s" { + if clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod == nil || + clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration.String() == "0s" { glog.V(8).Infof("AttachDetachReconcileSyncPeriod is not set; will set to default %v", defaultAttachDetachReconcileSyncPeriod) - options.KubeControllerManager.AttachDetachReconcileSyncPeriod = &metav1.Duration{Duration: defaultAttachDetachReconcileSyncPeriod} + clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod = &metav1.Duration{Duration: defaultAttachDetachReconcileSyncPeriod} // If less than 1 min and greater than 1 sec ... you get a warning - } else if options.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration < defaultAttachDetachReconcileSyncPeriod && - options.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration > time.Second { + } else if clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration < defaultAttachDetachReconcileSyncPeriod && + clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration > time.Second { glog.Infof("KubeControllerManager AttachDetachReconcileSyncPeriod is set lower than recommended: %s", defaultAttachDetachReconcileSyncPeriod) // If less than 1sec you get an error. Controller is coded to not allow configuration // less than one second. - } else if options.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration < time.Second { + } else if clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod.Duration < time.Second { return fmt.Errorf("AttachDetachReconcileSyncPeriod cannot be set to less than 1 second") } } else { glog.V(4).Infof("not setting AttachDetachReconcileSyncPeriod, k8s version is too low") - options.KubeControllerManager.AttachDetachReconcileSyncPeriod = nil + clusterSpec.KubeControllerManager.AttachDetachReconcileSyncPeriod = nil } return nil diff --git a/pkg/model/components/kubecontrollermanager_test.go b/pkg/model/components/kubecontrollermanager_test.go index 8058393ddd..dc9b5cad9e 100644 --- a/pkg/model/components/kubecontrollermanager_test.go +++ b/pkg/model/components/kubecontrollermanager_test.go @@ -47,9 +47,7 @@ func Test_Build_KCM_Builder_Lower_Version(t *testing.T) { c := buildCluster() kcm := &KubeControllerManagerOptionsBuilder{ - Context: &OptionsContext{ - Cluster: c, - }, + Context: &OptionsContext{}, } spec := c.Spec @@ -76,9 +74,7 @@ func Test_Build_KCM_Builder_High_Enough_Version(t *testing.T) { c.Spec.KubernetesVersion = v kcm := &KubeControllerManagerOptionsBuilder{ - Context: &OptionsContext{ - Cluster: c, - }, + Context: &OptionsContext{}, } spec := c.Spec @@ -101,9 +97,7 @@ func Test_Build_KCM_Builder_Change_Duration(t *testing.T) { c.Spec.KubernetesVersion = "v1.5.2" kcm := &KubeControllerManagerOptionsBuilder{ - Context: &OptionsContext{ - Cluster: c, - }, + Context: &OptionsContext{}, } spec := c.Spec diff --git a/pkg/model/components/kubedns.go b/pkg/model/components/kubedns.go new file mode 100644 index 0000000000..3901043ab0 --- /dev/null +++ b/pkg/model/components/kubedns.go @@ -0,0 +1,49 @@ +/* +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 components + +import ( + "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/upup/pkg/fi/loader" +) + +// KubeDnsOptionsBuilder adds options for kube-dns +type KubeDnsOptionsBuilder struct { + Context *OptionsContext +} + +var _ loader.OptionsBuilder = &KubeDnsOptionsBuilder{} + +func (b *KubeDnsOptionsBuilder) BuildOptions(o interface{}) error { + clusterSpec := o.(*kops.ClusterSpec) + + if clusterSpec.KubeDNS == nil { + clusterSpec.KubeDNS = &kops.KubeDNSConfig{} + } + + clusterSpec.KubeDNS.Replicas = 2 + ip, err := WellKnownServiceIP(clusterSpec, 10) + if err != nil { + return err + } + clusterSpec.KubeDNS.ServerIP = ip.String() + clusterSpec.KubeDNS.Domain = clusterSpec.ClusterDNSDomain + // TODO: Once we start shipping more images, start using them + clusterSpec.KubeDNS.Image = "gcr.io/google_containers/kubedns-amd64:1.3" + + return nil +} diff --git a/pkg/model/components/kubelet.go b/pkg/model/components/kubelet.go index 9850e3a5fe..54684c57a7 100644 --- a/pkg/model/components/kubelet.go +++ b/pkg/model/components/kubelet.go @@ -31,47 +31,70 @@ type KubeletOptionsBuilder struct { var _ loader.OptionsBuilder = &KubeletOptionsBuilder{} func (b *KubeletOptionsBuilder) BuildOptions(o interface{}) error { - options := o.(*kops.ClusterSpec) + clusterSpec := o.(*kops.ClusterSpec) - kubernetesVersion, err := b.Context.KubernetesVersion() + kubernetesVersion, err := KubernetesVersion(clusterSpec) if err != nil { return err } - if options.Kubelet == nil { - options.Kubelet = &kops.KubeletConfigSpec{} + if clusterSpec.Kubelet == nil { + clusterSpec.Kubelet = &kops.KubeletConfigSpec{} } - if options.MasterKubelet == nil { - options.MasterKubelet = &kops.KubeletConfigSpec{} + if clusterSpec.MasterKubelet == nil { + clusterSpec.MasterKubelet = &kops.KubeletConfigSpec{} } + ip, err := WellKnownServiceIP(clusterSpec, 10) + if err != nil { + return err + } + + // Standard options + clusterSpec.Kubelet.EnableDebuggingHandlers = fi.Bool(true) + clusterSpec.Kubelet.PodManifestPath = "/etc/kubernetes/manifests" + clusterSpec.Kubelet.AllowPrivileged = fi.Bool(true) + clusterSpec.Kubelet.LogLevel = fi.Int32(2) + clusterSpec.Kubelet.ClusterDNS = ip.String() + clusterSpec.Kubelet.ClusterDomain = clusterSpec.ClusterDNSDomain + clusterSpec.Kubelet.BabysitDaemons = fi.Bool(true) + clusterSpec.Kubelet.APIServers = "https://" + clusterSpec.MasterInternalName + clusterSpec.Kubelet.NonMasqueradeCIDR = clusterSpec.NonMasqueradeCIDR + + clusterSpec.MasterKubelet.RegisterSchedulable = fi.Bool(false) + clusterSpec.MasterKubelet.APIServers = "http://127.0.0.1:8080" + // Replace the CIDR with a CIDR allocated by KCM (the default, but included for clarity) + // We _do_ allow debugging handlers, so we can do logs + // This does allow more access than we would like though + clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(true) + // In 1.5 we fixed this, but in 1.4 we need to set the PodCIDR on the master // so that hostNetwork pods can come up if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 { // We bootstrap with a fake CIDR, but then this will be replaced (unless we're running with _isolated_master) - options.MasterKubelet.PodCIDR = "10.123.45.0/28" + clusterSpec.MasterKubelet.PodCIDR = "10.123.45.0/28" } // 1.5 deprecates the reconcile cidr option (and 1.6 removes it) if kubernetesVersion.Major == 1 && kubernetesVersion.Minor <= 4 { - options.MasterKubelet.ReconcileCIDR = fi.Bool(true) + clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(true) - if fi.BoolValue(b.Context.Cluster.Spec.IsolateMasters) { - options.MasterKubelet.ReconcileCIDR = fi.Bool(false) + if fi.BoolValue(clusterSpec.IsolateMasters) { + clusterSpec.MasterKubelet.ReconcileCIDR = fi.Bool(false) } - usesKubenet, err := b.Context.UsesKubenet() + usesKubenet, err := UsesKubenet(clusterSpec) if err != nil { return err } if usesKubenet { - options.Kubelet.ReconcileCIDR = fi.Bool(true) + clusterSpec.Kubelet.ReconcileCIDR = fi.Bool(true) } } if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 { // For pod eviction in low memory or empty disk situations - if options.Kubelet.EvictionHard == nil { + if clusterSpec.Kubelet.EvictionHard == nil { evictionHard := []string{ // TODO: Some people recommend 250Mi, but this would hurt small machines "memory.available<100Mi", @@ -83,15 +106,48 @@ func (b *KubeletOptionsBuilder) BuildOptions(o interface{}) error { "imagefs.available<10%", "imagefs.inodesFree<5%", } - options.Kubelet.EvictionHard = fi.String(strings.Join(evictionHard, ",")) + clusterSpec.Kubelet.EvictionHard = fi.String(strings.Join(evictionHard, ",")) } } // IsolateMasters enables the legacy behaviour, where master pods on a separate network // In newer versions of kubernetes, most of that functionality has been removed though - if fi.BoolValue(b.Context.Cluster.Spec.IsolateMasters) { - options.MasterKubelet.EnableDebuggingHandlers = fi.Bool(false) - options.MasterKubelet.HairpinMode = "none" + if fi.BoolValue(clusterSpec.IsolateMasters) { + clusterSpec.MasterKubelet.EnableDebuggingHandlers = fi.Bool(false) + clusterSpec.MasterKubelet.HairpinMode = "none" + } + + cloudProvider := fi.CloudProviderID(clusterSpec.CloudProvider) + + if cloudProvider == fi.CloudProviderAWS { + clusterSpec.Kubelet.CloudProvider = "aws" + clusterSpec.Kubelet.CgroupRoot = "docker" + + // Use the hostname from the AWS metadata service + clusterSpec.Kubelet.HostnameOverride = "@aws" + } + + if cloudProvider == fi.CloudProviderGCE { + clusterSpec.Kubelet.CloudProvider = "gce" + clusterSpec.Kubelet.HairpinMode = "promiscuous-bridge" + + clusterSpec.Kubelet.RuntimeCgroups = "/docker-daemon" + clusterSpec.Kubelet.KubeletCgroups = "/kubelet" + clusterSpec.Kubelet.SystemCgroups = "/system" + clusterSpec.Kubelet.CgroupRoot = "/" + } + + usesKubenet, err := UsesKubenet(clusterSpec) + if err != nil { + return err + } + if usesKubenet { + clusterSpec.Kubelet.NetworkPluginName = "kubenet" + + if kubernetesVersion.Major == 1 && kubernetesVersion.Minor >= 4 { + // AWS MTU is 9001 + clusterSpec.Kubelet.NetworkPluginMTU = fi.Int32(9001) + } } return nil diff --git a/pkg/model/components/networking.go b/pkg/model/components/networking.go index 1603d0876e..bb819b85a3 100644 --- a/pkg/model/components/networking.go +++ b/pkg/model/components/networking.go @@ -31,7 +31,9 @@ type NetworkingOptionsBuilder struct { var _ loader.OptionsBuilder = &NetworkingOptionsBuilder{} func (b *NetworkingOptionsBuilder) BuildOptions(o interface{}) error { - k8sVersion, err := b.Context.KubernetesVersion() + clusterSpec := o.(*kops.ClusterSpec) + + k8sVersion, err := KubernetesVersion(clusterSpec) if err != nil { return err } @@ -41,7 +43,7 @@ func (b *NetworkingOptionsBuilder) BuildOptions(o interface{}) error { options.Kubelet = &kops.KubeletConfigSpec{} } - networking := b.Context.Cluster.Spec.Networking + networking := clusterSpec.Networking if networking == nil { return fmt.Errorf("networking not set") } diff --git a/pkg/model/context.go b/pkg/model/context.go index dc753df6bc..05ee9dbbcf 100644 --- a/pkg/model/context.go +++ b/pkg/model/context.go @@ -25,11 +25,14 @@ import ( "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/util" + "k8s.io/kops/pkg/model/components" + "net" ) type KopsModelContext struct { + Cluster *kops.Cluster + Region string - Cluster *kops.Cluster InstanceGroups []*kops.InstanceGroup SSHPublicKeys [][]byte @@ -223,3 +226,7 @@ func VersionGTE(version semver.Version, major uint64, minor uint64) bool { } return false } + +func (c *KopsModelContext) WellKnownServiceIP(id int) (net.IP, error) { + return components.WellKnownServiceIP(&c.Cluster.Spec, id) +} diff --git a/pkg/model/pki.go b/pkg/model/pki.go new file mode 100644 index 0000000000..29fd7e8d83 --- /dev/null +++ b/pkg/model/pki.go @@ -0,0 +1,86 @@ +/* +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 model + +import ( + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/fitasks" +) + +// PKIModelBuilder configures PKI keypairs +type PKIModelBuilder struct { + *KopsModelContext +} + +var _ fi.ModelBuilder = &PKIModelBuilder{} + +func (b *PKIModelBuilder) Build(c *fi.ModelBuilderContext) error { + { + // Keypair used by the kubelet + t := &fitasks.Keypair{ + Name: fi.String("kubelet"), + Subject: "cn=kubelet", + Type: "client", + } + c.AddTask(t) + } + + { + // Keypair used for admin kubecfg + t := &fitasks.Keypair{ + Name: fi.String("kubecfg"), + Subject: "cn=kubecfg", + Type: "client", + } + c.AddTask(t) + } + + { + // Keypair used for apiserver + + // A few names used from inside the cluster, which all resolve the same based on our default suffixes + alternateNames := []string{ + "kubernetes", + "kubernetes.default", + "kubernetes.default.svc", + "kubernetes.default.svc." + b.Cluster.Spec.ClusterDNSDomain, + } + + // Names specified in the cluster spec + alternateNames = append(alternateNames, b.Cluster.Spec.MasterPublicName) + alternateNames = append(alternateNames, b.Cluster.Spec.MasterInternalName) + + // Referencing it by internal IP should work also + { + ip, err := b.WellKnownServiceIP(1) + if err != nil { + return err + } + alternateNames = append(alternateNames, ip.String()) + } + + t := &fitasks.Keypair{ + Name: fi.String("master"), + Subject: "cn=kubernetes-master", + Type: "server", + AlternateNames: alternateNames, + } + c.AddTask(t) + } + + return nil +} diff --git a/upup/models/cloudup/pki/kubecfg b/upup/models/cloudup/pki/kubecfg deleted file mode 100644 index fb4f579a5c..0000000000 --- a/upup/models/cloudup/pki/kubecfg +++ /dev/null @@ -1,3 +0,0 @@ -keypair/kubecfg: - subject: cn=kubecfg - type: client diff --git a/upup/models/cloudup/pki/kubelet b/upup/models/cloudup/pki/kubelet deleted file mode 100644 index 29cedf84d1..0000000000 --- a/upup/models/cloudup/pki/kubelet +++ /dev/null @@ -1,3 +0,0 @@ -keypair/kubelet: - subject: cn=kubelet - type: client diff --git a/upup/models/cloudup/pki/master b/upup/models/cloudup/pki/master deleted file mode 100644 index dc8db9c18e..0000000000 --- a/upup/models/cloudup/pki/master +++ /dev/null @@ -1,11 +0,0 @@ -keypair/master: - subject: cn=kubernetes-master - type: server - alternateNames: - - kubernetes - - kubernetes.default - - kubernetes.default.svc - - kubernetes.default.svc.{{ .ClusterDNSDomain }} - - "{{ .MasterPublicName }}" - - "{{ .MasterInternalName }}" - - "{{ WellKnownServiceIP 1 }}" diff --git a/upup/models/config/components/kube-dns/kube-dns.options b/upup/models/config/components/kube-dns/kube-dns.options deleted file mode 100644 index 3f28e94746..0000000000 --- a/upup/models/config/components/kube-dns/kube-dns.options +++ /dev/null @@ -1,6 +0,0 @@ -KubeDNS: - Replicas: 2 - ServerIP: {{ WellKnownServiceIP 10 }} - Domain: {{ .ClusterDNSDomain }} - # TODO: Once we start shipping more images, start using them - Image: gcr.io/google_containers/kubedns-amd64:1.3 diff --git a/upup/models/config/components/kubelet/_aws/kubelet.aws.options b/upup/models/config/components/kubelet/_aws/kubelet.aws.options deleted file mode 100644 index 5bd1fc036d..0000000000 --- a/upup/models/config/components/kubelet/_aws/kubelet.aws.options +++ /dev/null @@ -1,5 +0,0 @@ -Kubelet: - CloudProvider: aws - CgroupRoot: docker - # Use the hostname from the AWS metadata service - HostnameOverride: "@aws" diff --git a/upup/models/config/components/kubelet/_gce/kubelet.gce.options b/upup/models/config/components/kubelet/_gce/kubelet.gce.options deleted file mode 100644 index fc09b8b3cf..0000000000 --- a/upup/models/config/components/kubelet/_gce/kubelet.gce.options +++ /dev/null @@ -1,7 +0,0 @@ -Kubelet: - CloudProvider: gce - HairpinMode: promiscuous-bridge - RuntimeCgroups: /docker-daemon - KubeletCgroups: /kubelet - SystemCgroups: /system - CgroupRoot: / diff --git a/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_4/kubelet.kubenet.1_4.options b/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_4/kubelet.kubenet.1_4.options deleted file mode 100644 index 907327d7b6..0000000000 --- a/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_4/kubelet.kubenet.1_4.options +++ /dev/null @@ -1,8 +0,0 @@ -Kubelet: - # AWS MTU is 9001 - NetworkPluginMTU: 9001 - -# TODO: Having to duplicate MasterKubelet & Kubelet feels wrong -MasterKubelet: - # AWS MTU is 9001 - NetworkPluginMTU: 9001 diff --git a/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_5/kubelet.kubenet.1_5.options b/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_5/kubelet.kubenet.1_5.options deleted file mode 100644 index 907327d7b6..0000000000 --- a/upup/models/config/components/kubelet/_networking_kubenet/_k8s_1_5/kubelet.kubenet.1_5.options +++ /dev/null @@ -1,8 +0,0 @@ -Kubelet: - # AWS MTU is 9001 - NetworkPluginMTU: 9001 - -# TODO: Having to duplicate MasterKubelet & Kubelet feels wrong -MasterKubelet: - # AWS MTU is 9001 - NetworkPluginMTU: 9001 diff --git a/upup/models/config/components/kubelet/_networking_kubenet/kubelet.kubenet.options b/upup/models/config/components/kubelet/_networking_kubenet/kubelet.kubenet.options deleted file mode 100644 index 66025876ee..0000000000 --- a/upup/models/config/components/kubelet/_networking_kubenet/kubelet.kubenet.options +++ /dev/null @@ -1,2 +0,0 @@ -Kubelet: - NetworkPluginName: kubenet diff --git a/upup/models/config/components/kubelet/kubelet.options b/upup/models/config/components/kubelet/kubelet.options deleted file mode 100644 index a21fbdfb43..0000000000 --- a/upup/models/config/components/kubelet/kubelet.options +++ /dev/null @@ -1,18 +0,0 @@ -Kubelet: - EnableDebuggingHandlers: true - PodManifestPath: /etc/kubernetes/manifests - AllowPrivileged: true - LogLevel: 2 - ClusterDNS: {{ WellKnownServiceIP 10 }} - ClusterDomain: {{ .ClusterDNSDomain }} - BabysitDaemons: true - APIServers: https://{{ .MasterInternalName }} - NonMasqueradeCIDR: {{ .NonMasqueradeCIDR }} - -MasterKubelet: - RegisterSchedulable: false - APIServers: http://127.0.0.1:8080 - # Replace the CIDR with a CIDR allocated by KCM (the default, but included for clarity) - # We _do_ allow debugging handlers, so we can do logs - # This does allow more access than we would like though - EnableDebuggingHandlers: true diff --git a/upup/models/config/defaults.options b/upup/models/config/defaults.options deleted file mode 100644 index 177b6f504d..0000000000 --- a/upup/models/config/defaults.options +++ /dev/null @@ -1,6 +0,0 @@ -Multizone: true - -ClusterDNSDomain: cluster.local - -KubeUser: admin - diff --git a/upup/pkg/fi/cloudup/apply_cluster.go b/upup/pkg/fi/cloudup/apply_cluster.go index b4ab910ca6..8b3adecc27 100644 --- a/upup/pkg/fi/cloudup/apply_cluster.go +++ b/upup/pkg/fi/cloudup/apply_cluster.go @@ -378,6 +378,7 @@ func (c *ApplyClusterCmd) Run() error { &model.ExternalAccessModelBuilder{KopsModelContext: modelContext}, &model.FirewallModelBuilder{KopsModelContext: modelContext}, &model.IAMModelBuilder{KopsModelContext: modelContext}, + &model.PKIModelBuilder{KopsModelContext: modelContext}, &model.MasterVolumeBuilder{KopsModelContext: modelContext}, &model.NetworkModelBuilder{KopsModelContext: modelContext}, &model.SSHKeyModelBuilder{KopsModelContext: modelContext}, diff --git a/upup/pkg/fi/cloudup/populate_cluster_spec.go b/upup/pkg/fi/cloudup/populate_cluster_spec.go index 8c0add30d3..ad1496a007 100644 --- a/upup/pkg/fi/cloudup/populate_cluster_spec.go +++ b/upup/pkg/fi/cloudup/populate_cluster_spec.go @@ -254,17 +254,19 @@ func (c *populateClusterSpec) run() error { tf.AddTo(templateFunctions) - optionsContext := &components.OptionsContext{ - Cluster: cluster, - } + optionsContext := &components.OptionsContext{} var fileModels []string var codeModels []loader.OptionsBuilder for _, m := range c.Models { switch m { case "config": + // Note: DefaultOptionsBuilder comes first + codeModels = append(codeModels, &components.DefaultsOptionsBuilder{Context: optionsContext}) + codeModels = append(codeModels, &components.KubeAPIServerOptionsBuilder{Context: optionsContext}) codeModels = append(codeModels, &components.DockerOptionsBuilder{Context: optionsContext}) codeModels = append(codeModels, &components.NetworkingOptionsBuilder{Context: optionsContext}) + codeModels = append(codeModels, &components.KubeDnsOptionsBuilder{Context: optionsContext}) codeModels = append(codeModels, &components.KubeletOptionsBuilder{Context: optionsContext}) codeModels = append(codeModels, &components.KubeControllerManagerOptionsBuilder{Context: optionsContext}) fileModels = append(fileModels, m) diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 72c78254f2..b492488bed 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -29,15 +29,12 @@ package cloudup import ( "encoding/base64" - "encoding/binary" "fmt" "github.com/golang/glog" api "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/model" "k8s.io/kops/util/pkg/vfs" "k8s.io/kubernetes/pkg/util/sets" - "math/big" - "net" "strings" "text/template" ) @@ -52,38 +49,6 @@ type TemplateFunctions struct { modelContext *model.KopsModelContext } -func (tf *TemplateFunctions) WellKnownServiceIP(id int) (net.IP, error) { - _, cidr, err := net.ParseCIDR(tf.cluster.Spec.ServiceClusterIPRange) - if err != nil { - return nil, fmt.Errorf("error parsing ServiceClusterIPRange %q: %v", tf.cluster.Spec.ServiceClusterIPRange, err) - } - - ip4 := cidr.IP.To4() - if ip4 != nil { - n := binary.BigEndian.Uint32(ip4) - n += uint32(id) - serviceIP := make(net.IP, len(ip4)) - binary.BigEndian.PutUint32(serviceIP, n) - return serviceIP, nil - } - - ip6 := cidr.IP.To16() - if ip6 != nil { - baseIPInt := big.NewInt(0) - baseIPInt.SetBytes(ip6) - serviceIPInt := big.NewInt(0) - serviceIPInt.Add(big.NewInt(int64(id)), baseIPInt) - serviceIP := make(net.IP, len(ip6)) - serviceIPBytes := serviceIPInt.Bytes() - for i := range serviceIPBytes { - serviceIP[len(serviceIP)-len(serviceIPBytes)+i] = serviceIPBytes[i] - } - return serviceIP, nil - } - - return nil, fmt.Errorf("Unexpected IP address type for ServiceClusterIPRange: %s", tf.cluster.Spec.ServiceClusterIPRange) -} - // This will define the available functions we can use in our YAML models // If we are trying to get a new function implemented it MUST // be defined here. @@ -96,8 +61,6 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap) { // Network topology definitions dest["GetELBName32"] = tf.modelContext.GetELBName32 - dest["WellKnownServiceIP"] = tf.WellKnownServiceIP - dest["Base64Encode"] = func(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) } diff --git a/upup/pkg/fi/fitasks/keypair.go b/upup/pkg/fi/fitasks/keypair.go index c71af53754..a6d711041b 100644 --- a/upup/pkg/fi/fitasks/keypair.go +++ b/upup/pkg/fi/fitasks/keypair.go @@ -41,6 +41,7 @@ type Keypair struct { } var _ fi.HasCheckExisting = &Keypair{} +var _ fi.HasName = &Keypair{} // It's important always to check for the existing key, so we don't regenerate keys e.g. on terraform func (e *Keypair) CheckExisting(c *fi.Context) bool {