From ffa95b8112633db84316e852f756f27d8ff86c81 Mon Sep 17 00:00:00 2001 From: Derek VerLee Date: Mon, 19 Jun 2017 18:30:39 -0400 Subject: [PATCH] Add support for cluster using http forward proxy --- docs/http_proxy.md | 40 ++++++++ docs/run_in_existing_vpc.md | 4 + nodeup/pkg/model/convenience.go | 36 ++++++- nodeup/pkg/model/kubeapiserver.go | 1 + nodeup/pkg/model/kubecontrollermanager.go | 1 + nodeup/pkg/model/kubescheduler.go | 1 + nodeup/pkg/model/protokube.go | 16 +++- pkg/apis/kops/cluster.go | 16 ++++ pkg/apis/kops/v1alpha1/cluster.go | 16 ++++ .../kops/v1alpha1/zz_generated.conversion.go | 70 ++++++++++++++ pkg/apis/kops/v1alpha2/cluster.go | 17 ++++ .../kops/v1alpha2/zz_generated.conversion.go | 70 ++++++++++++++ pkg/model/awsmodel/autoscalinggroup.go | 2 +- pkg/model/bootstrapscript.go | 71 +++++++++++++- pkg/model/bootstrapscript_test.go | 53 +++++++++++ pkg/model/gcemodel/autoscalinggroup.go | 2 +- pkg/model/resources/nodeup.go | 2 + tests/integration/minimal/cloudformation.json | 4 +- .../k8s-1.6.yaml.template | 7 ++ upup/pkg/fi/cloudup/defaults.go | 90 +++++++++++++++++- upup/pkg/fi/cloudup/defaults_test.go | 94 +++++++++++++++++++ upup/pkg/fi/cloudup/template_functions.go | 28 ++++++ upup/pkg/fi/cloudup/vspheretasks/attachiso.go | 3 +- 23 files changed, 635 insertions(+), 9 deletions(-) create mode 100644 docs/http_proxy.md create mode 100644 pkg/model/bootstrapscript_test.go create mode 100644 upup/pkg/fi/cloudup/defaults_test.go diff --git a/docs/http_proxy.md b/docs/http_proxy.md new file mode 100644 index 0000000000..5366fc23bc --- /dev/null +++ b/docs/http_proxy.md @@ -0,0 +1,40 @@ + +HTTP Forward Proxy Support +========================== + +It is possible to launch a kubernetes cluster from behind an http forward proxy ("corporate proxy"). To do so, you will need to configure the `egressProxy` for the cluster. + +It is assumed the proxy is already existing. If you want a private topology on AWS, for example, with an proxy instead of a NAT instance, you'll need to create the proxy yourself. See [Running in a shared VPC](run_in_existing_vpc.md). + +This configuration only manages proxy configurations for Kops and the Kubernetes cluster. We can not handle proxy configuration for application containers and pods. + +## Configuration + +Add `spec.egressProxy` port and url as follows + +``` yaml +spec: + egressProxy: + httpProxy: + host: proxy.corp.local + port: 3128 +``` + +Currently we assume the same configuration for http and https traffic. + +## Proxy Excludes + +Most clients will blindly try to use the proxy to make all calls, even to localhost and the local subnet, unless configured otherwise. Some basic exclusions necessary for successful launch and operation are added for you at initial cluster creation. If you wish to add additional exclusions, add or edit `egressProxy.excludes` with a comma separated list of hostnames. Matching is based on suffix, ie, `corp.local` will match `images.corp.local`, and `.corp.local` will match `corp.local` and `images.corp.local`, following typical `no_proxy` environment variable conventions. + +``` yaml +spec: + egressProxy: + httpProxy: + host: proxy.corp.local + port: 3128 + excludes: corp.local,internal.corp.com +``` + +## AWS VPC Endpoints and S3 access + +If you are hosting on AWS have configured VPC "Endpoints" for S3 or other services, you may want to add these to the `spec.egressProxy.excludes`. Keep in mind that the S3 bucket must be in the same region as the VPC for it to be accessible via the endpoint. diff --git a/docs/run_in_existing_vpc.md b/docs/run_in_existing_vpc.md index e07ad37292..89942a028b 100644 --- a/docs/run_in_existing_vpc.md +++ b/docs/run_in_existing_vpc.md @@ -146,3 +146,7 @@ Please note: in their route table. Private subnets should not have public IPs, and will typically have a NAT gateway configured as their default route. * kops won't create a route-table at all if we're not creating subnets. + +### Proxy VPC Egress + +See [HTTP Forward Proxy Support](http_proxy.md) diff --git a/nodeup/pkg/model/convenience.go b/nodeup/pkg/model/convenience.go index a48f73c635..b8e89ea0d1 100644 --- a/nodeup/pkg/model/convenience.go +++ b/nodeup/pkg/model/convenience.go @@ -16,7 +16,14 @@ limitations under the License. package model -import "k8s.io/kops/upup/pkg/fi" +import ( + "strconv" + + "github.com/golang/glog" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/upup/pkg/fi" +) // s is a helper that builds a *string from a string value func s(v string) *string { @@ -28,6 +35,33 @@ func i64(v int64) *int64 { return fi.Int64(v) } +func getProxyEnvVars(proxies *kops.EgressProxySpec) []v1.EnvVar { + if proxies == nil { + glog.V(8).Info("proxies is == nil, returning empty list") + return []v1.EnvVar{} + } + + if proxies.HTTPProxy.Host == "" { + glog.Warning("EgressProxy set but no proxy host provided") + } + + var httpProxyURL string + if proxies.HTTPProxy.Port == 0 { + httpProxyURL = "http://" + proxies.HTTPProxy.Host + } else { + httpProxyURL = "http://" + proxies.HTTPProxy.Host + ":" + strconv.Itoa(proxies.HTTPProxy.Port) + } + + noProxy := proxies.ProxyExcludes + + return []v1.EnvVar{ + {Name: "http_proxy", Value: httpProxyURL}, + {Name: "https_proxy", Value: httpProxyURL}, + {Name: "NO_PROXY", Value: noProxy}, + {Name: "no_proxy", Value: noProxy}, + } +} + // b returns a pointer to a boolean func b(v bool) *bool { return fi.Bool(v) diff --git a/nodeup/pkg/model/kubeapiserver.go b/nodeup/pkg/model/kubeapiserver.go index 2fcdb22484..247d51eadd 100644 --- a/nodeup/pkg/model/kubeapiserver.go +++ b/nodeup/pkg/model/kubeapiserver.go @@ -223,6 +223,7 @@ func (b *KubeAPIServerBuilder) buildPod() (*v1.Pod, error) { HostPort: 8080, }, }, + Env: getProxyEnvVars(b.Cluster.Spec.EgressProxy), } for _, path := range b.SSLHostPaths() { diff --git a/nodeup/pkg/model/kubecontrollermanager.go b/nodeup/pkg/model/kubecontrollermanager.go index d9a268d737..e433575f0e 100644 --- a/nodeup/pkg/model/kubecontrollermanager.go +++ b/nodeup/pkg/model/kubecontrollermanager.go @@ -186,6 +186,7 @@ func (b *KubeControllerManagerBuilder) buildPod() (*v1.Pod, error) { InitialDelaySeconds: 15, TimeoutSeconds: 15, }, + Env: getProxyEnvVars(b.Cluster.Spec.EgressProxy), } for _, path := range b.SSLHostPaths() { diff --git a/nodeup/pkg/model/kubescheduler.go b/nodeup/pkg/model/kubescheduler.go index 00de7907a7..fb21450efe 100644 --- a/nodeup/pkg/model/kubescheduler.go +++ b/nodeup/pkg/model/kubescheduler.go @@ -140,6 +140,7 @@ func (b *KubeSchedulerBuilder) buildPod() (*v1.Pod, error) { InitialDelaySeconds: 15, TimeoutSeconds: 15, }, + Env: getProxyEnvVars(b.Cluster.Spec.EgressProxy), } addHostPathMapping(pod, container, "varlibkubescheduler", "/var/lib/kube-scheduler") diff --git a/nodeup/pkg/model/protokube.go b/nodeup/pkg/model/protokube.go index ba9a2c64d3..5e51bef84b 100644 --- a/nodeup/pkg/model/protokube.go +++ b/nodeup/pkg/model/protokube.go @@ -276,6 +276,9 @@ func (t *ProtokubeBuilder) ProtokubeFlags(k8sVersion semver.Version) *ProtokubeF func (t *ProtokubeBuilder) ProtokubeEnvironmentVariables() string { var buffer bytes.Buffer + // TODO write out an environments file for this. This is getting a tad long. + + // Pass in required credentials when using user-defined s3 endpoint if os.Getenv("AWS_REGION") != "" { buffer.WriteString(" ") buffer.WriteString("-e 'AWS_REGION=") @@ -284,7 +287,6 @@ func (t *ProtokubeBuilder) ProtokubeEnvironmentVariables() string { buffer.WriteString(" ") } - // Pass in required credentials when using user-defined s3 endpoint if os.Getenv("S3_ENDPOINT") != "" { buffer.WriteString(" ") buffer.WriteString("-e S3_ENDPOINT=") @@ -306,9 +308,21 @@ func (t *ProtokubeBuilder) ProtokubeEnvironmentVariables() string { buffer.WriteString(" ") } + t.writeProxyEnvVars(&buffer) + return buffer.String() } +func (t *ProtokubeBuilder) writeProxyEnvVars(buffer *bytes.Buffer) { + for _, envVar := range getProxyEnvVars(t.Cluster.Spec.EgressProxy) { + buffer.WriteString(" -e ") + buffer.WriteString(envVar.Name) + buffer.WriteString("=") + buffer.WriteString(envVar.Value) + buffer.WriteString(" ") + } +} + // buildCertificateTask is responsible for build a certificate request task func (t *ProtokubeBuilder) buildCeritificateTask(c *fi.ModelBuilderContext, name, filename string) error { cert, err := t.KeyStore.Cert(name) diff --git a/pkg/apis/kops/cluster.go b/pkg/apis/kops/cluster.go index 8c8ab61dd7..1e8579f44f 100644 --- a/pkg/apis/kops/cluster.go +++ b/pkg/apis/kops/cluster.go @@ -93,6 +93,9 @@ type ClusterSpec struct { NonMasqueradeCIDR string `json:"nonMasqueradeCIDR,omitempty"` // SSHAccess is a list of the CIDRs that can access SSH. SSHAccess []string `json:"sshAccess,omitempty"` + // HTTPProxy defines connection information to support use of a private cluster behind an forward HTTP Proxy + EgressProxy *EgressProxySpec `json:"egressProxy,omitempty"` + // KubernetesAPIAccess is a list of the CIDRs that can access the Kubernetes API endpoint (master HTTPS) KubernetesAPIAccess []string `json:"kubernetesApiAccess,omitempty"` // IsolatesMasters determines whether we should lock down masters so that they are not on the pod network. @@ -285,6 +288,19 @@ type ClusterSubnetSpec struct { Type SubnetType `json:"type,omitempty"` } +type EgressProxySpec struct { + HTTPProxy HTTPProxy `json:"httpProxy,omitempty"` + ProxyExcludes string `json:"excludes,omitempty"` +} + +type HTTPProxy struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + // TODO #3070 + // User string `json:"user,omitempty"` + // Password string `json:"password,omitempty"` +} + // FillDefaults populates default values. // This is different from PerformAssignments, because these values are changeable, and thus we don't need to // store them (i.e. we don't need to 'lock them') diff --git a/pkg/apis/kops/v1alpha1/cluster.go b/pkg/apis/kops/v1alpha1/cluster.go index 241e7ec41d..044867dae9 100644 --- a/pkg/apis/kops/v1alpha1/cluster.go +++ b/pkg/apis/kops/v1alpha1/cluster.go @@ -212,6 +212,9 @@ type ClusterSpec struct { //KubeProxyTestArgs string `json:",omitempty"` //KubeProxyTestLogLevel string `json:",omitempty"` + // HTTPProxy defines connection information to support use of a private cluster behind an forward HTTP Proxy + EgressProxy *EgressProxySpec `json:"egressProxy,omitempty"` + // EtcdClusters stores the configuration for each cluster EtcdClusters []*EtcdClusterSpec `json:"etcdClusters,omitempty"` @@ -368,3 +371,16 @@ type ClusterZoneSpec struct { Egress string `json:"egress,omitempty"` } + +type EgressProxySpec struct { + HTTPProxy HTTPProxy `json:"httpProxy,omitempty"` + ProxyExcludes string `json:"excludes,omitempty"` +} + +type HTTPProxy struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + // TODO #3070 + // User string `json:"user,omitempty"` + // Password string `json:"password,omitempty"` +} diff --git a/pkg/apis/kops/v1alpha1/zz_generated.conversion.go b/pkg/apis/kops/v1alpha1/zz_generated.conversion.go index e133bc8ac5..77abadf894 100644 --- a/pkg/apis/kops/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/kops/v1alpha1/zz_generated.conversion.go @@ -67,6 +67,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_kops_DNSSpec_To_v1alpha1_DNSSpec, Convert_v1alpha1_DockerConfig_To_kops_DockerConfig, Convert_kops_DockerConfig_To_v1alpha1_DockerConfig, + Convert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec, + Convert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec, Convert_v1alpha1_EtcdClusterSpec_To_kops_EtcdClusterSpec, Convert_kops_EtcdClusterSpec_To_v1alpha1_EtcdClusterSpec, Convert_v1alpha1_EtcdMemberSpec_To_kops_EtcdMemberSpec, @@ -83,6 +85,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_kops_FederationSpec_To_v1alpha1_FederationSpec, Convert_v1alpha1_FlannelNetworkingSpec_To_kops_FlannelNetworkingSpec, Convert_kops_FlannelNetworkingSpec_To_v1alpha1_FlannelNetworkingSpec, + Convert_v1alpha1_HTTPProxy_To_kops_HTTPProxy, + Convert_kops_HTTPProxy_To_v1alpha1_HTTPProxy, Convert_v1alpha1_HookSpec_To_kops_HookSpec, Convert_kops_HookSpec_To_v1alpha1_HookSpec, Convert_v1alpha1_InstanceGroup_To_kops_InstanceGroup, @@ -524,6 +528,15 @@ func autoConvert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out * out.IsolateMasters = in.IsolateMasters out.UpdatePolicy = in.UpdatePolicy out.AdditionalPolicies = in.AdditionalPolicies + if in.EgressProxy != nil { + in, out := &in.EgressProxy, &out.EgressProxy + *out = new(kops.EgressProxySpec) + if err := Convert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec(*in, *out, s); err != nil { + return err + } + } else { + out.EgressProxy = nil + } if in.EtcdClusters != nil { in, out := &in.EtcdClusters, &out.EtcdClusters *out = make([]*kops.EtcdClusterSpec, len(*in)) @@ -705,6 +718,15 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec, out.ServiceClusterIPRange = in.ServiceClusterIPRange out.NonMasqueradeCIDR = in.NonMasqueradeCIDR // WARNING: in.SSHAccess requires manual conversion: does not exist in peer-type + if in.EgressProxy != nil { + in, out := &in.EgressProxy, &out.EgressProxy + *out = new(EgressProxySpec) + if err := Convert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec(*in, *out, s); err != nil { + return err + } + } else { + out.EgressProxy = nil + } // WARNING: in.KubernetesAPIAccess requires manual conversion: does not exist in peer-type out.IsolateMasters = in.IsolateMasters out.UpdatePolicy = in.UpdatePolicy @@ -946,6 +968,32 @@ func Convert_kops_DockerConfig_To_v1alpha1_DockerConfig(in *kops.DockerConfig, o return autoConvert_kops_DockerConfig_To_v1alpha1_DockerConfig(in, out, s) } +func autoConvert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec(in *EgressProxySpec, out *kops.EgressProxySpec, s conversion.Scope) error { + if err := Convert_v1alpha1_HTTPProxy_To_kops_HTTPProxy(&in.HTTPProxy, &out.HTTPProxy, s); err != nil { + return err + } + out.ProxyExcludes = in.ProxyExcludes + return nil +} + +// Convert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec is an autogenerated conversion function. +func Convert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec(in *EgressProxySpec, out *kops.EgressProxySpec, s conversion.Scope) error { + return autoConvert_v1alpha1_EgressProxySpec_To_kops_EgressProxySpec(in, out, s) +} + +func autoConvert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec(in *kops.EgressProxySpec, out *EgressProxySpec, s conversion.Scope) error { + if err := Convert_kops_HTTPProxy_To_v1alpha1_HTTPProxy(&in.HTTPProxy, &out.HTTPProxy, s); err != nil { + return err + } + out.ProxyExcludes = in.ProxyExcludes + return nil +} + +// Convert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec is an autogenerated conversion function. +func Convert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec(in *kops.EgressProxySpec, out *EgressProxySpec, s conversion.Scope) error { + return autoConvert_kops_EgressProxySpec_To_v1alpha1_EgressProxySpec(in, out, s) +} + func autoConvert_v1alpha1_EtcdClusterSpec_To_kops_EtcdClusterSpec(in *EtcdClusterSpec, out *kops.EtcdClusterSpec, s conversion.Scope) error { out.Name = in.Name out.EnableEtcdTLS = in.EnableEtcdTLS @@ -1162,6 +1210,28 @@ func Convert_kops_FlannelNetworkingSpec_To_v1alpha1_FlannelNetworkingSpec(in *ko return autoConvert_kops_FlannelNetworkingSpec_To_v1alpha1_FlannelNetworkingSpec(in, out, s) } +func autoConvert_v1alpha1_HTTPProxy_To_kops_HTTPProxy(in *HTTPProxy, out *kops.HTTPProxy, s conversion.Scope) error { + out.Host = in.Host + out.Port = in.Port + return nil +} + +// Convert_v1alpha1_HTTPProxy_To_kops_HTTPProxy is an autogenerated conversion function. +func Convert_v1alpha1_HTTPProxy_To_kops_HTTPProxy(in *HTTPProxy, out *kops.HTTPProxy, s conversion.Scope) error { + return autoConvert_v1alpha1_HTTPProxy_To_kops_HTTPProxy(in, out, s) +} + +func autoConvert_kops_HTTPProxy_To_v1alpha1_HTTPProxy(in *kops.HTTPProxy, out *HTTPProxy, s conversion.Scope) error { + out.Host = in.Host + out.Port = in.Port + return nil +} + +// Convert_kops_HTTPProxy_To_v1alpha1_HTTPProxy is an autogenerated conversion function. +func Convert_kops_HTTPProxy_To_v1alpha1_HTTPProxy(in *kops.HTTPProxy, out *HTTPProxy, s conversion.Scope) error { + return autoConvert_kops_HTTPProxy_To_v1alpha1_HTTPProxy(in, out, s) +} + func autoConvert_v1alpha1_HookSpec_To_kops_HookSpec(in *HookSpec, out *kops.HookSpec, s conversion.Scope) error { if in.ExecContainer != nil { in, out := &in.ExecContainer, &out.ExecContainer diff --git a/pkg/apis/kops/v1alpha2/cluster.go b/pkg/apis/kops/v1alpha2/cluster.go index ee1096a6fc..eb05a00003 100644 --- a/pkg/apis/kops/v1alpha2/cluster.go +++ b/pkg/apis/kops/v1alpha2/cluster.go @@ -116,6 +116,9 @@ type ClusterSpec struct { // Currently only a single CIDR is supported (though a richer grammar could be added in future) SSHAccess []string `json:"sshAccess,omitempty"` + // HTTPProxy defines connection information to support use of a private cluster behind an forward HTTP Proxy + EgressProxy *EgressProxySpec `json:"egressProxy,omitempty"` + // KubernetesAPIAccess determines the permitted access to the API endpoints (master HTTPS) // Currently only a single CIDR is supported (though a richer grammar could be added in future) KubernetesAPIAccess []string `json:"kubernetesApiAccess,omitempty"` @@ -293,3 +296,17 @@ type ClusterSubnetSpec struct { Type SubnetType `json:"type,omitempty"` } + +type EgressProxySpec struct { + HTTPProxy HTTPProxy `json:"httpProxy,omitempty"` + ProxyExcludes string `json:"excludes,omitempty"` +} + +type HTTPProxy struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + + // TODO #3070 + // User string `json:"user,omitempty"` + // Password string `json:"password,omitempty"` +} diff --git a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go index 3301b977c4..83516df44f 100644 --- a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go +++ b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go @@ -71,6 +71,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_kops_DNSSpec_To_v1alpha2_DNSSpec, Convert_v1alpha2_DockerConfig_To_kops_DockerConfig, Convert_kops_DockerConfig_To_v1alpha2_DockerConfig, + Convert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec, + Convert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec, Convert_v1alpha2_EtcdClusterSpec_To_kops_EtcdClusterSpec, Convert_kops_EtcdClusterSpec_To_v1alpha2_EtcdClusterSpec, Convert_v1alpha2_EtcdMemberSpec_To_kops_EtcdMemberSpec, @@ -87,6 +89,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_kops_FederationSpec_To_v1alpha2_FederationSpec, Convert_v1alpha2_FlannelNetworkingSpec_To_kops_FlannelNetworkingSpec, Convert_kops_FlannelNetworkingSpec_To_v1alpha2_FlannelNetworkingSpec, + Convert_v1alpha2_HTTPProxy_To_kops_HTTPProxy, + Convert_kops_HTTPProxy_To_v1alpha2_HTTPProxy, Convert_v1alpha2_HookSpec_To_kops_HookSpec, Convert_kops_HookSpec_To_v1alpha2_HookSpec, Convert_v1alpha2_InstanceGroup_To_kops_InstanceGroup, @@ -558,6 +562,15 @@ func autoConvert_v1alpha2_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out * out.ServiceClusterIPRange = in.ServiceClusterIPRange out.NonMasqueradeCIDR = in.NonMasqueradeCIDR out.SSHAccess = in.SSHAccess + if in.EgressProxy != nil { + in, out := &in.EgressProxy, &out.EgressProxy + *out = new(kops.EgressProxySpec) + if err := Convert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec(*in, *out, s); err != nil { + return err + } + } else { + out.EgressProxy = nil + } out.KubernetesAPIAccess = in.KubernetesAPIAccess out.IsolateMasters = in.IsolateMasters out.UpdatePolicy = in.UpdatePolicy @@ -758,6 +771,15 @@ func autoConvert_kops_ClusterSpec_To_v1alpha2_ClusterSpec(in *kops.ClusterSpec, out.ServiceClusterIPRange = in.ServiceClusterIPRange out.NonMasqueradeCIDR = in.NonMasqueradeCIDR out.SSHAccess = in.SSHAccess + if in.EgressProxy != nil { + in, out := &in.EgressProxy, &out.EgressProxy + *out = new(EgressProxySpec) + if err := Convert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec(*in, *out, s); err != nil { + return err + } + } else { + out.EgressProxy = nil + } out.KubernetesAPIAccess = in.KubernetesAPIAccess out.IsolateMasters = in.IsolateMasters out.UpdatePolicy = in.UpdatePolicy @@ -1034,6 +1056,32 @@ func Convert_kops_DockerConfig_To_v1alpha2_DockerConfig(in *kops.DockerConfig, o return autoConvert_kops_DockerConfig_To_v1alpha2_DockerConfig(in, out, s) } +func autoConvert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec(in *EgressProxySpec, out *kops.EgressProxySpec, s conversion.Scope) error { + if err := Convert_v1alpha2_HTTPProxy_To_kops_HTTPProxy(&in.HTTPProxy, &out.HTTPProxy, s); err != nil { + return err + } + out.ProxyExcludes = in.ProxyExcludes + return nil +} + +// Convert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec is an autogenerated conversion function. +func Convert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec(in *EgressProxySpec, out *kops.EgressProxySpec, s conversion.Scope) error { + return autoConvert_v1alpha2_EgressProxySpec_To_kops_EgressProxySpec(in, out, s) +} + +func autoConvert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec(in *kops.EgressProxySpec, out *EgressProxySpec, s conversion.Scope) error { + if err := Convert_kops_HTTPProxy_To_v1alpha2_HTTPProxy(&in.HTTPProxy, &out.HTTPProxy, s); err != nil { + return err + } + out.ProxyExcludes = in.ProxyExcludes + return nil +} + +// Convert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec is an autogenerated conversion function. +func Convert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec(in *kops.EgressProxySpec, out *EgressProxySpec, s conversion.Scope) error { + return autoConvert_kops_EgressProxySpec_To_v1alpha2_EgressProxySpec(in, out, s) +} + func autoConvert_v1alpha2_EtcdClusterSpec_To_kops_EtcdClusterSpec(in *EtcdClusterSpec, out *kops.EtcdClusterSpec, s conversion.Scope) error { out.Name = in.Name out.EnableEtcdTLS = in.EnableEtcdTLS @@ -1260,6 +1308,28 @@ func Convert_kops_FlannelNetworkingSpec_To_v1alpha2_FlannelNetworkingSpec(in *ko return autoConvert_kops_FlannelNetworkingSpec_To_v1alpha2_FlannelNetworkingSpec(in, out, s) } +func autoConvert_v1alpha2_HTTPProxy_To_kops_HTTPProxy(in *HTTPProxy, out *kops.HTTPProxy, s conversion.Scope) error { + out.Host = in.Host + out.Port = in.Port + return nil +} + +// Convert_v1alpha2_HTTPProxy_To_kops_HTTPProxy is an autogenerated conversion function. +func Convert_v1alpha2_HTTPProxy_To_kops_HTTPProxy(in *HTTPProxy, out *kops.HTTPProxy, s conversion.Scope) error { + return autoConvert_v1alpha2_HTTPProxy_To_kops_HTTPProxy(in, out, s) +} + +func autoConvert_kops_HTTPProxy_To_v1alpha2_HTTPProxy(in *kops.HTTPProxy, out *HTTPProxy, s conversion.Scope) error { + out.Host = in.Host + out.Port = in.Port + return nil +} + +// Convert_kops_HTTPProxy_To_v1alpha2_HTTPProxy is an autogenerated conversion function. +func Convert_kops_HTTPProxy_To_v1alpha2_HTTPProxy(in *kops.HTTPProxy, out *HTTPProxy, s conversion.Scope) error { + return autoConvert_kops_HTTPProxy_To_v1alpha2_HTTPProxy(in, out, s) +} + func autoConvert_v1alpha2_HookSpec_To_kops_HookSpec(in *HookSpec, out *kops.HookSpec, s conversion.Scope) error { if in.ExecContainer != nil { in, out := &in.ExecContainer, &out.ExecContainer diff --git a/pkg/model/awsmodel/autoscalinggroup.go b/pkg/model/awsmodel/autoscalinggroup.go index b41a12f192..cd69ddd4dd 100644 --- a/pkg/model/awsmodel/autoscalinggroup.go +++ b/pkg/model/awsmodel/autoscalinggroup.go @@ -117,7 +117,7 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error { return err } - if t.UserData, err = b.BootstrapScript.ResourceNodeUp(ig); err != nil { + if t.UserData, err = b.BootstrapScript.ResourceNodeUp(ig, b.Cluster.Spec.EgressProxy); err != nil { return err } diff --git a/pkg/model/bootstrapscript.go b/pkg/model/bootstrapscript.go index 0cea9981bc..09046b433b 100644 --- a/pkg/model/bootstrapscript.go +++ b/pkg/model/bootstrapscript.go @@ -17,8 +17,11 @@ limitations under the License. package model import ( + "bytes" "fmt" "os" + "strconv" + "strings" "text/template" "k8s.io/kops/pkg/apis/kops" @@ -34,7 +37,7 @@ type BootstrapScript struct { NodeUpConfigBuilder func(ig *kops.InstanceGroup) (*nodeup.Config, error) } -func (b *BootstrapScript) ResourceNodeUp(ig *kops.InstanceGroup) (*fi.ResourceHolder, error) { +func (b *BootstrapScript) ResourceNodeUp(ig *kops.InstanceGroup, ps *kops.EgressProxySpec) (*fi.ResourceHolder, error) { if ig.Spec.Role == kops.InstanceGroupRoleBastion { // Bastions are just bare machines (currently), used as SSH jump-hosts return nil, nil @@ -73,6 +76,9 @@ func (b *BootstrapScript) ResourceNodeUp(ig *kops.InstanceGroup) (*fi.ResourceHo return "" }, + "ProxyEnv": func() string { + return b.createProxyEnv(ps) + }, "AWS_REGION": func() string { if os.Getenv("AWS_REGION") != "" { return fmt.Sprintf("export AWS_REGION=%s\n", @@ -88,3 +94,66 @@ func (b *BootstrapScript) ResourceNodeUp(ig *kops.InstanceGroup) (*fi.ResourceHo } return fi.WrapResource(templateResource), nil } + +func (b *BootstrapScript) createProxyEnv(ps *kops.EgressProxySpec) string { + var buffer bytes.Buffer + + if ps != nil && ps.HTTPProxy.Host != "" { + var httpProxyUrl string + + // TODO double check that all the code does this + // TODO move this into a validate so we can enforce the string syntax + if !strings.HasPrefix(ps.HTTPProxy.Host, "http://") { + httpProxyUrl = "http://" + } + + if ps.HTTPProxy.Port != 0 { + httpProxyUrl += ps.HTTPProxy.Host + ":" + strconv.Itoa(ps.HTTPProxy.Port) + } else { + httpProxyUrl += ps.HTTPProxy.Host + } + + // Set base env variables + buffer.WriteString("export http_proxy=" + httpProxyUrl + "\n") + buffer.WriteString("export https_proxy=${http_proxy}\n") + buffer.WriteString("export no_proxy=" + ps.ProxyExcludes + "\n") + buffer.WriteString("export NO_PROXY=${no_proxy}\n") + + // TODO move the rest of this configuration work to nodeup + + // Set env variables for docker + buffer.WriteString("echo \"export http_proxy=${http_proxy}\" >> /etc/default/docker\n") + buffer.WriteString("echo \"export https_proxy=${http_proxy}\" >> /etc/default/docker\n") + buffer.WriteString("echo \"export no_proxy=${no_proxy}\" >> /etc/default/docker\n") + buffer.WriteString("echo \"export NO_PROXY=${no_proxy}\" >> /etc/default/docker\n") + + // Set env variables for base environment + buffer.WriteString("echo \"export http_proxy=${http_proxy}\" >> /etc/environment\n") + buffer.WriteString("echo \"export https_proxy=${http_proxy}\" >> /etc/environment\n") + buffer.WriteString("echo \"export no_proxy=${no_proxy}\" >> /etc/environment\n") + buffer.WriteString("echo \"export NO_PROXY=${no_proxy}\" >> /etc/environment\n") + + // Set env variables to systemd + buffer.WriteString("echo DefaultEnvironment=\\\"http_proxy=${http_proxy}\\\" \\\"https_proxy=${http_proxy}\\\"") + buffer.WriteString("echo DefaultEnvironment=\\\"http_proxy=${http_proxy}\\\" \\\"https_proxy=${http_proxy}\\\"") + buffer.WriteString(" \\\"NO_PROXY=${no_proxy}\\\" \\\"no_proxy=${no_proxy}\\\"") + buffer.WriteString(" >> /etc/systemd/system.conf\n") + + // source in the environment this step ensures that environment file is correct + buffer.WriteString("source /etc/environment\n") + + // Restart stuff + buffer.WriteString("systemctl daemon-reload\n") + buffer.WriteString("systemctl daemon-reexec\n") + + // TODO do we need no_proxy in these as well?? + // TODO handle CoreOS + // Depending on OS set package manager proxy settings + buffer.WriteString("if [ -f /etc/lsb-release ] || [ -f /etc/debian_version ]; then\n") + buffer.WriteString(" echo \"Acquire::http::Proxy \\\"${http_proxy}\\\";\" > /etc/apt/apt.conf.d/30proxy\n") + buffer.WriteString("elif [ -f /etc/redhat-release ]; then\n") + buffer.WriteString(" echo \"http_proxy=${http_proxy}\" >> /etc/yum.conf\n") + buffer.WriteString("fi\n") + } + return buffer.String() +} diff --git a/pkg/model/bootstrapscript_test.go b/pkg/model/bootstrapscript_test.go new file mode 100644 index 0000000000..3c6b738dbd --- /dev/null +++ b/pkg/model/bootstrapscript_test.go @@ -0,0 +1,53 @@ +/* +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 ( + "strings" + "testing" + + "k8s.io/kops/pkg/apis/kops" +) + +func Test_ProxyFunc(t *testing.T) { + b := &BootstrapScript{} + ps := &kops.EgressProxySpec{ + HTTPProxy: kops.HTTPProxy{ + Host: "example.com", + Port: 80, + }, + } + + script := b.createProxyEnv(ps) + + if script == "" { + t.Fatalf("script cannot be empty") + } + + if !strings.HasPrefix(script, "export http_proxy=http://example.com:80") { + t.Fatalf("script not setting http_proxy properly") + } + + ps.ProxyExcludes = "www.google.com,www.kubernetes.io" + + script = b.createProxyEnv(ps) + t.Logf(script) + if !strings.Contains(script, "export no_proxy="+ps.ProxyExcludes) { + t.Fatalf("script not setting no_proxy properly") + } + +} diff --git a/pkg/model/gcemodel/autoscalinggroup.go b/pkg/model/gcemodel/autoscalinggroup.go index d0e46c7d74..c0ed53caa0 100644 --- a/pkg/model/gcemodel/autoscalinggroup.go +++ b/pkg/model/gcemodel/autoscalinggroup.go @@ -44,7 +44,7 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error { for _, ig := range b.InstanceGroups { name := b.SafeObjectName(ig.ObjectMeta.Name) - startupScript, err := b.BootstrapScript.ResourceNodeUp(ig) + startupScript, err := b.BootstrapScript.ResourceNodeUp(ig, b.Cluster.Spec.EgressProxy) if err != nil { return err } diff --git a/pkg/model/resources/nodeup.go b/pkg/model/resources/nodeup.go index 0188e14e50..ef9f5015b6 100644 --- a/pkg/model/resources/nodeup.go +++ b/pkg/model/resources/nodeup.go @@ -41,6 +41,8 @@ NODEUP_HASH={{ NodeUpSourceHash }} {{ S3Env }} {{ AWS_REGION }} +{{ ProxyEnv }} + function ensure-install-dir() { INSTALL_DIR="/var/cache/kubernetes-install" # On ContainerOS, we install to /var/lib/toolbox install (because of noexec) diff --git a/tests/integration/minimal/cloudformation.json b/tests/integration/minimal/cloudformation.json index 8f58dcc477..add161f896 100644 --- a/tests/integration/minimal/cloudformation.json +++ b/tests/integration/minimal/cloudformation.json @@ -93,7 +93,7 @@ "Ref": "AWSEC2SecurityGroupmastersminimalexamplecom" } ], - "UserData": "IyEvYmluL2Jhc2gKIyBDb3B5cmlnaHQgMjAxNiBUaGUgS3ViZXJuZXRlcyBBdXRob3JzIEFsbCByaWdodHMgcmVzZXJ2ZWQuCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCiMKIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywKIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCgpzZXQgLW8gZXJyZXhpdApzZXQgLW8gbm91bnNldApzZXQgLW8gcGlwZWZhaWwKCk5PREVVUF9VUkw9aHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvbm9kZXVwCk5PREVVUF9IQVNIPQoKCgoKZnVuY3Rpb24gZW5zdXJlLWluc3RhbGwtZGlyKCkgewogIElOU1RBTExfRElSPSIvdmFyL2NhY2hlL2t1YmVybmV0ZXMtaW5zdGFsbCIKICAjIE9uIENvbnRhaW5lck9TLCB3ZSBpbnN0YWxsIHRvIC92YXIvbGliL3Rvb2xib3ggaW5zdGFsbCAoYmVjYXVzZSBvZiBub2V4ZWMpCiAgaWYgW1sgLWQgL3Zhci9saWIvdG9vbGJveCBdXTsgdGhlbgogICAgSU5TVEFMTF9ESVI9Ii92YXIvbGliL3Rvb2xib3gva3ViZXJuZXRlcy1pbnN0YWxsIgogIGZpCiAgbWtkaXIgLXAgJHtJTlNUQUxMX0RJUn0KICBjZCAke0lOU1RBTExfRElSfQp9CgojIFJldHJ5IGEgZG93bmxvYWQgdW50aWwgd2UgZ2V0IGl0LiBUYWtlcyBhIGhhc2ggYW5kIGEgc2V0IG9mIFVSTHMuCiMKIyAkMSBpcyB0aGUgc2hhMSBvZiB0aGUgVVJMLiBDYW4gYmUgIiIgaWYgdGhlIHNoYTEgaXMgdW5rbm93bi4KIyAkMisgYXJlIHRoZSBVUkxzIHRvIGRvd25sb2FkLgpkb3dubG9hZC1vci1idXN0KCkgewogIGxvY2FsIC1yIGhhc2g9IiQxIgogIHNoaWZ0IDEKCiAgdXJscz0oICQqICkKICB3aGlsZSB0cnVlOyBkbwogICAgZm9yIHVybCBpbiAiJHt1cmxzW0BdfSI7IGRvCiAgICAgIGxvY2FsIGZpbGU9IiR7dXJsIyMqL30iCiAgICAgIHJtIC1mICIke2ZpbGV9IgogICAgICBpZiAhIGN1cmwgLWYgLS1pcHY0IC1MbyAiJHtmaWxlfSIgLS1jb25uZWN0LXRpbWVvdXQgMjAgLS1yZXRyeSA2IC0tcmV0cnktZGVsYXkgMTAgIiR7dXJsfSI7IHRoZW4KICAgICAgICBlY2hvICI9PSBGYWlsZWQgdG8gZG93bmxvYWQgJHt1cmx9LiBSZXRyeWluZy4gPT0iCiAgICAgIGVsaWYgW1sgLW4gIiR7aGFzaH0iIF1dICYmICEgdmFsaWRhdGUtaGFzaCAiJHtmaWxlfSIgIiR7aGFzaH0iOyB0aGVuCiAgICAgICAgZWNobyAiPT0gSGFzaCB2YWxpZGF0aW9uIG9mICR7dXJsfSBmYWlsZWQuIFJldHJ5aW5nLiA9PSIKICAgICAgZWxzZQogICAgICAgIGlmIFtbIC1uICIke2hhc2h9IiBdXTsgdGhlbgogICAgICAgICAgZWNobyAiPT0gRG93bmxvYWRlZCAke3VybH0gKFNIQTEgPSAke2hhc2h9KSA9PSIKICAgICAgICBlbHNlCiAgICAgICAgICBlY2hvICI9PSBEb3dubG9hZGVkICR7dXJsfSA9PSIKICAgICAgICBmaQogICAgICAgIHJldHVybgogICAgICBmaQogICAgZG9uZQoKICAgIGVjaG8gIkFsbCBkb3dubG9hZHMgZmFpbGVkOyBzbGVlcGluZyBiZWZvcmUgcmV0cnlpbmciCiAgICBzbGVlcCA2MAogIGRvbmUKfQoKdmFsaWRhdGUtaGFzaCgpIHsKICBsb2NhbCAtciBmaWxlPSIkMSIKICBsb2NhbCAtciBleHBlY3RlZD0iJDIiCiAgbG9jYWwgYWN0dWFsCgogIGFjdHVhbD0kKHNoYTFzdW0gJHtmaWxlfSB8IGF3ayAneyBwcmludCAkMSB9JykgfHwgdHJ1ZQogIGlmIFtbICIke2FjdHVhbH0iICE9ICIke2V4cGVjdGVkfSIgXV07IHRoZW4KICAgIGVjaG8gIj09ICR7ZmlsZX0gY29ycnVwdGVkLCBzaGExICR7YWN0dWFsfSBkb2Vzbid0IG1hdGNoIGV4cGVjdGVkICR7ZXhwZWN0ZWR9ID09IgogICAgcmV0dXJuIDEKICBmaQp9CgpmdW5jdGlvbiBzcGxpdC1jb21tYXMoKSB7CiAgZWNobyAkMSB8IHRyICIsIiAiXG4iCn0KCmZ1bmN0aW9uIHRyeS1kb3dubG9hZC1yZWxlYXNlKCkgewogICMgVE9ETyh6bWVybHlubik6IE5vdyB3ZSBSRUFMTFkgaGF2ZSBubyBleGN1c2Ugbm90IHRvIGRvIHRoZSByZWJvb3QKICAjIG9wdGltaXphdGlvbi4KCiAgbG9jYWwgLXIgbm9kZXVwX3VybHM9KCAkKHNwbGl0LWNvbW1hcyAiJHtOT0RFVVBfVVJMfSIpICkKICBsb2NhbCAtciBub2RldXBfZmlsZW5hbWU9IiR7bm9kZXVwX3VybHNbMF0jIyovfSIKICBpZiBbWyAtbiAiJHtOT0RFVVBfSEFTSDotfSIgXV07IHRoZW4KICAgIGxvY2FsIC1yIG5vZGV1cF9oYXNoPSIke05PREVVUF9IQVNIfSIKICBlbHNlCiAgIyBUT0RPOiBSZW1vdmU/CiAgICBlY2hvICJEb3dubG9hZGluZyBzaGExIChub3QgZm91bmQgaW4gZW52KSIKICAgIGRvd25sb2FkLW9yLWJ1c3QgIiIgIiR7bm9kZXVwX3VybHNbQF0vJS8uc2hhMX0iCiAgICBsb2NhbCAtciBub2RldXBfaGFzaD0kKGNhdCAiJHtub2RldXBfZmlsZW5hbWV9LnNoYTEiKQogIGZpCgogIGVjaG8gIkRvd25sb2FkaW5nIG5vZGV1cCAoJHtub2RldXBfdXJsc1tAXX0pIgogIGRvd25sb2FkLW9yLWJ1c3QgIiR7bm9kZXVwX2hhc2h9IiAiJHtub2RldXBfdXJsc1tAXX0iCgogIGNobW9kICt4IG5vZGV1cAp9CgpmdW5jdGlvbiBkb3dubG9hZC1yZWxlYXNlKCkgewogICMgSW4gY2FzZSBvZiBmYWlsdXJlIGNoZWNraW5nIGludGVncml0eSBvZiByZWxlYXNlLCByZXRyeS4KICB1bnRpbCB0cnktZG93bmxvYWQtcmVsZWFzZTsgZG8KICAgIHNsZWVwIDE1CiAgICBlY2hvICJDb3VsZG4ndCBkb3dubG9hZCByZWxlYXNlLiBSZXRyeWluZy4uLiIKICBkb25lCgogIGVjaG8gIlJ1bm5pbmcgbm9kZXVwIgogICMgV2UgY2FuJ3QgcnVuIGluIHRoZSBmb3JlZ3JvdW5kIGJlY2F1c2Ugb2YgaHR0cHM6Ly9naXRodWIuY29tL2RvY2tlci9kb2NrZXIvaXNzdWVzLzIzNzkzCiAgKCBjZCAke0lOU1RBTExfRElSfTsgLi9ub2RldXAgLS1pbnN0YWxsLXN5c3RlbWQtdW5pdCAtLWNvbmY9JHtJTlNUQUxMX0RJUn0va3ViZV9lbnYueWFtbCAtLXY9OCAgKQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCi9iaW4vc3lzdGVtZC1tYWNoaW5lLWlkLXNldHVwIHx8IGVjaG8gImZhaWxlZCB0byBzZXQgdXAgZW5zdXJlIG1hY2hpbmUtaWQgY29uZmlndXJlZCIKCmVjaG8gIj09IG5vZGV1cCBub2RlIGNvbmZpZyBzdGFydGluZyA9PSIKZW5zdXJlLWluc3RhbGwtZGlyCgpjYXQgPiBrdWJlX2Vudi55YW1sIDw8IF9fRU9GX0tVQkVfRU5WCkFzc2V0czoKLSA3ZDcwZTA5MDk1MTQ4NmNhZTUyZDlhODJiN2FhZjUwNTZmODRmOGVkQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvcmVsZWFzZS92MS40LjYvYmluL2xpbnV4L2FtZDY0L2t1YmVsZXQKLSA5YWRjZDEyMGZkYjdhZDZlNjRjMDYxZTU2YTA1ZmVmYzEyZTk2MThiQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvcmVsZWFzZS92MS40LjYvYmluL2xpbnV4L2FtZDY0L2t1YmVjdGwKLSAxOWQ0OWY3YjJiOTljZDI0OTNkNWFlMGFjZTg5NmM2NGUyODljY2JiQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvbmV0d29yay1wbHVnaW5zL2NuaS0wN2E4YTI4NjM3ZTk3YjIyZWI4ZGZlNzEwZWVhZTEzNDRmNjlkMTZlLnRhci5negotIGNiYmE4NTY3NDZhNDQxYzdkMWE5ZTk1ZTE0MWM5ODJhMWI4ODY0ZTZAaHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvdXRpbHMudGFyLmd6CkNsdXN0ZXJOYW1lOiBtaW5pbWFsLmV4YW1wbGUuY29tCkNvbmZpZ0Jhc2U6IG1lbWZzOi8vY2x1c3RlcnMuZXhhbXBsZS5jb20vbWluaW1hbC5leGFtcGxlLmNvbQpJbnN0YW5jZUdyb3VwTmFtZTogbWFzdGVyLXVzLXRlc3QtMWEKVGFnczoKLSBfYXV0b21hdGljX3VwZ3JhZGVzCi0gX2F3cwotIF9rdWJlcm5ldGVzX21hc3RlcgpjaGFubmVsczoKLSBtZW1mczovL2NsdXN0ZXJzLmV4YW1wbGUuY29tL21pbmltYWwuZXhhbXBsZS5jb20vYWRkb25zL2Jvb3RzdHJhcC1jaGFubmVsLnlhbWwKcHJvdG9rdWJlSW1hZ2U6CiAgaGFzaDogN2MzYTBlYzA3MjNmZDM1MDYwOWIyOTU4YmM1YjhhYjAyNTgzODUxYwogIG5hbWU6IHByb3Rva3ViZToxLjUuMAogIHNvdXJjZTogaHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvaW1hZ2VzL3Byb3Rva3ViZS50YXIuZ3oKCl9fRU9GX0tVQkVfRU5WCgpkb3dubG9hZC1yZWxlYXNlCmVjaG8gIj09IG5vZGV1cCBub2RlIGNvbmZpZyBkb25lID09Igo=" + "UserData": "IyEvYmluL2Jhc2gKIyBDb3B5cmlnaHQgMjAxNiBUaGUgS3ViZXJuZXRlcyBBdXRob3JzIEFsbCByaWdodHMgcmVzZXJ2ZWQuCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCiMKIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywKIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCgpzZXQgLW8gZXJyZXhpdApzZXQgLW8gbm91bnNldApzZXQgLW8gcGlwZWZhaWwKCk5PREVVUF9VUkw9aHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvbm9kZXVwCk5PREVVUF9IQVNIPQoKCgoKCgpmdW5jdGlvbiBlbnN1cmUtaW5zdGFsbC1kaXIoKSB7CiAgSU5TVEFMTF9ESVI9Ii92YXIvY2FjaGUva3ViZXJuZXRlcy1pbnN0YWxsIgogICMgT24gQ29udGFpbmVyT1MsIHdlIGluc3RhbGwgdG8gL3Zhci9saWIvdG9vbGJveCBpbnN0YWxsIChiZWNhdXNlIG9mIG5vZXhlYykKICBpZiBbWyAtZCAvdmFyL2xpYi90b29sYm94IF1dOyB0aGVuCiAgICBJTlNUQUxMX0RJUj0iL3Zhci9saWIvdG9vbGJveC9rdWJlcm5ldGVzLWluc3RhbGwiCiAgZmkKICBta2RpciAtcCAke0lOU1RBTExfRElSfQogIGNkICR7SU5TVEFMTF9ESVJ9Cn0KCiMgUmV0cnkgYSBkb3dubG9hZCB1bnRpbCB3ZSBnZXQgaXQuIFRha2VzIGEgaGFzaCBhbmQgYSBzZXQgb2YgVVJMcy4KIwojICQxIGlzIHRoZSBzaGExIG9mIHRoZSBVUkwuIENhbiBiZSAiIiBpZiB0aGUgc2hhMSBpcyB1bmtub3duLgojICQyKyBhcmUgdGhlIFVSTHMgdG8gZG93bmxvYWQuCmRvd25sb2FkLW9yLWJ1c3QoKSB7CiAgbG9jYWwgLXIgaGFzaD0iJDEiCiAgc2hpZnQgMQoKICB1cmxzPSggJCogKQogIHdoaWxlIHRydWU7IGRvCiAgICBmb3IgdXJsIGluICIke3VybHNbQF19IjsgZG8KICAgICAgbG9jYWwgZmlsZT0iJHt1cmwjIyovfSIKICAgICAgcm0gLWYgIiR7ZmlsZX0iCiAgICAgIGlmICEgY3VybCAtZiAtLWlwdjQgLUxvICIke2ZpbGV9IiAtLWNvbm5lY3QtdGltZW91dCAyMCAtLXJldHJ5IDYgLS1yZXRyeS1kZWxheSAxMCAiJHt1cmx9IjsgdGhlbgogICAgICAgIGVjaG8gIj09IEZhaWxlZCB0byBkb3dubG9hZCAke3VybH0uIFJldHJ5aW5nLiA9PSIKICAgICAgZWxpZiBbWyAtbiAiJHtoYXNofSIgXV0gJiYgISB2YWxpZGF0ZS1oYXNoICIke2ZpbGV9IiAiJHtoYXNofSI7IHRoZW4KICAgICAgICBlY2hvICI9PSBIYXNoIHZhbGlkYXRpb24gb2YgJHt1cmx9IGZhaWxlZC4gUmV0cnlpbmcuID09IgogICAgICBlbHNlCiAgICAgICAgaWYgW1sgLW4gIiR7aGFzaH0iIF1dOyB0aGVuCiAgICAgICAgICBlY2hvICI9PSBEb3dubG9hZGVkICR7dXJsfSAoU0hBMSA9ICR7aGFzaH0pID09IgogICAgICAgIGVsc2UKICAgICAgICAgIGVjaG8gIj09IERvd25sb2FkZWQgJHt1cmx9ID09IgogICAgICAgIGZpCiAgICAgICAgcmV0dXJuCiAgICAgIGZpCiAgICBkb25lCgogICAgZWNobyAiQWxsIGRvd25sb2FkcyBmYWlsZWQ7IHNsZWVwaW5nIGJlZm9yZSByZXRyeWluZyIKICAgIHNsZWVwIDYwCiAgZG9uZQp9Cgp2YWxpZGF0ZS1oYXNoKCkgewogIGxvY2FsIC1yIGZpbGU9IiQxIgogIGxvY2FsIC1yIGV4cGVjdGVkPSIkMiIKICBsb2NhbCBhY3R1YWwKCiAgYWN0dWFsPSQoc2hhMXN1bSAke2ZpbGV9IHwgYXdrICd7IHByaW50ICQxIH0nKSB8fCB0cnVlCiAgaWYgW1sgIiR7YWN0dWFsfSIgIT0gIiR7ZXhwZWN0ZWR9IiBdXTsgdGhlbgogICAgZWNobyAiPT0gJHtmaWxlfSBjb3JydXB0ZWQsIHNoYTEgJHthY3R1YWx9IGRvZXNuJ3QgbWF0Y2ggZXhwZWN0ZWQgJHtleHBlY3RlZH0gPT0iCiAgICByZXR1cm4gMQogIGZpCn0KCmZ1bmN0aW9uIHNwbGl0LWNvbW1hcygpIHsKICBlY2hvICQxIHwgdHIgIiwiICJcbiIKfQoKZnVuY3Rpb24gdHJ5LWRvd25sb2FkLXJlbGVhc2UoKSB7CiAgIyBUT0RPKHptZXJseW5uKTogTm93IHdlIFJFQUxMWSBoYXZlIG5vIGV4Y3VzZSBub3QgdG8gZG8gdGhlIHJlYm9vdAogICMgb3B0aW1pemF0aW9uLgoKICBsb2NhbCAtciBub2RldXBfdXJscz0oICQoc3BsaXQtY29tbWFzICIke05PREVVUF9VUkx9IikgKQogIGxvY2FsIC1yIG5vZGV1cF9maWxlbmFtZT0iJHtub2RldXBfdXJsc1swXSMjKi99IgogIGlmIFtbIC1uICIke05PREVVUF9IQVNIOi19IiBdXTsgdGhlbgogICAgbG9jYWwgLXIgbm9kZXVwX2hhc2g9IiR7Tk9ERVVQX0hBU0h9IgogIGVsc2UKICAjIFRPRE86IFJlbW92ZT8KICAgIGVjaG8gIkRvd25sb2FkaW5nIHNoYTEgKG5vdCBmb3VuZCBpbiBlbnYpIgogICAgZG93bmxvYWQtb3ItYnVzdCAiIiAiJHtub2RldXBfdXJsc1tAXS8lLy5zaGExfSIKICAgIGxvY2FsIC1yIG5vZGV1cF9oYXNoPSQoY2F0ICIke25vZGV1cF9maWxlbmFtZX0uc2hhMSIpCiAgZmkKCiAgZWNobyAiRG93bmxvYWRpbmcgbm9kZXVwICgke25vZGV1cF91cmxzW0BdfSkiCiAgZG93bmxvYWQtb3ItYnVzdCAiJHtub2RldXBfaGFzaH0iICIke25vZGV1cF91cmxzW0BdfSIKCiAgY2htb2QgK3ggbm9kZXVwCn0KCmZ1bmN0aW9uIGRvd25sb2FkLXJlbGVhc2UoKSB7CiAgIyBJbiBjYXNlIG9mIGZhaWx1cmUgY2hlY2tpbmcgaW50ZWdyaXR5IG9mIHJlbGVhc2UsIHJldHJ5LgogIHVudGlsIHRyeS1kb3dubG9hZC1yZWxlYXNlOyBkbwogICAgc2xlZXAgMTUKICAgIGVjaG8gIkNvdWxkbid0IGRvd25sb2FkIHJlbGVhc2UuIFJldHJ5aW5nLi4uIgogIGRvbmUKCiAgZWNobyAiUnVubmluZyBub2RldXAiCiAgIyBXZSBjYW4ndCBydW4gaW4gdGhlIGZvcmVncm91bmQgYmVjYXVzZSBvZiBodHRwczovL2dpdGh1Yi5jb20vZG9ja2VyL2RvY2tlci9pc3N1ZXMvMjM3OTMKICAoIGNkICR7SU5TVEFMTF9ESVJ9OyAuL25vZGV1cCAtLWluc3RhbGwtc3lzdGVtZC11bml0IC0tY29uZj0ke0lOU1RBTExfRElSfS9rdWJlX2Vudi55YW1sIC0tdj04ICApCn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKL2Jpbi9zeXN0ZW1kLW1hY2hpbmUtaWQtc2V0dXAgfHwgZWNobyAiZmFpbGVkIHRvIHNldCB1cCBlbnN1cmUgbWFjaGluZS1pZCBjb25maWd1cmVkIgoKZWNobyAiPT0gbm9kZXVwIG5vZGUgY29uZmlnIHN0YXJ0aW5nID09IgplbnN1cmUtaW5zdGFsbC1kaXIKCmNhdCA+IGt1YmVfZW52LnlhbWwgPDwgX19FT0ZfS1VCRV9FTlYKQXNzZXRzOgotIDdkNzBlMDkwOTUxNDg2Y2FlNTJkOWE4MmI3YWFmNTA1NmY4NGY4ZWRAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9yZWxlYXNlL3YxLjQuNi9iaW4vbGludXgvYW1kNjQva3ViZWxldAotIDlhZGNkMTIwZmRiN2FkNmU2NGMwNjFlNTZhMDVmZWZjMTJlOTYxOGJAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9yZWxlYXNlL3YxLjQuNi9iaW4vbGludXgvYW1kNjQva3ViZWN0bAotIDE5ZDQ5ZjdiMmI5OWNkMjQ5M2Q1YWUwYWNlODk2YzY0ZTI4OWNjYmJAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9uZXR3b3JrLXBsdWdpbnMvY25pLTA3YThhMjg2MzdlOTdiMjJlYjhkZmU3MTBlZWFlMTM0NGY2OWQxNmUudGFyLmd6Ci0gY2JiYTg1Njc0NmE0NDFjN2QxYTllOTVlMTQxYzk4MmExYjg4NjRlNkBodHRwczovL2t1YmV1cHYyLnMzLmFtYXpvbmF3cy5jb20va29wcy8xLjUuMC9saW51eC9hbWQ2NC91dGlscy50YXIuZ3oKQ2x1c3Rlck5hbWU6IG1pbmltYWwuZXhhbXBsZS5jb20KQ29uZmlnQmFzZTogbWVtZnM6Ly9jbHVzdGVycy5leGFtcGxlLmNvbS9taW5pbWFsLmV4YW1wbGUuY29tCkluc3RhbmNlR3JvdXBOYW1lOiBtYXN0ZXItdXMtdGVzdC0xYQpUYWdzOgotIF9hdXRvbWF0aWNfdXBncmFkZXMKLSBfYXdzCi0gX2t1YmVybmV0ZXNfbWFzdGVyCmNoYW5uZWxzOgotIG1lbWZzOi8vY2x1c3RlcnMuZXhhbXBsZS5jb20vbWluaW1hbC5leGFtcGxlLmNvbS9hZGRvbnMvYm9vdHN0cmFwLWNoYW5uZWwueWFtbApwcm90b2t1YmVJbWFnZToKICBoYXNoOiA3YzNhMGVjMDcyM2ZkMzUwNjA5YjI5NThiYzViOGFiMDI1ODM4NTFjCiAgbmFtZTogcHJvdG9rdWJlOjEuNS4wCiAgc291cmNlOiBodHRwczovL2t1YmV1cHYyLnMzLmFtYXpvbmF3cy5jb20va29wcy8xLjUuMC9pbWFnZXMvcHJvdG9rdWJlLnRhci5negoKX19FT0ZfS1VCRV9FTlYKCmRvd25sb2FkLXJlbGVhc2UKZWNobyAiPT0gbm9kZXVwIG5vZGUgY29uZmlnIGRvbmUgPT0iCg==" } }, "AWSAutoScalingLaunchConfigurationnodesminimalexamplecom": { @@ -121,7 +121,7 @@ "Ref": "AWSEC2SecurityGroupnodesminimalexamplecom" } ], - "UserData": "IyEvYmluL2Jhc2gKIyBDb3B5cmlnaHQgMjAxNiBUaGUgS3ViZXJuZXRlcyBBdXRob3JzIEFsbCByaWdodHMgcmVzZXJ2ZWQuCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCiMKIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywKIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCgpzZXQgLW8gZXJyZXhpdApzZXQgLW8gbm91bnNldApzZXQgLW8gcGlwZWZhaWwKCk5PREVVUF9VUkw9aHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvbm9kZXVwCk5PREVVUF9IQVNIPQoKCgoKZnVuY3Rpb24gZW5zdXJlLWluc3RhbGwtZGlyKCkgewogIElOU1RBTExfRElSPSIvdmFyL2NhY2hlL2t1YmVybmV0ZXMtaW5zdGFsbCIKICAjIE9uIENvbnRhaW5lck9TLCB3ZSBpbnN0YWxsIHRvIC92YXIvbGliL3Rvb2xib3ggaW5zdGFsbCAoYmVjYXVzZSBvZiBub2V4ZWMpCiAgaWYgW1sgLWQgL3Zhci9saWIvdG9vbGJveCBdXTsgdGhlbgogICAgSU5TVEFMTF9ESVI9Ii92YXIvbGliL3Rvb2xib3gva3ViZXJuZXRlcy1pbnN0YWxsIgogIGZpCiAgbWtkaXIgLXAgJHtJTlNUQUxMX0RJUn0KICBjZCAke0lOU1RBTExfRElSfQp9CgojIFJldHJ5IGEgZG93bmxvYWQgdW50aWwgd2UgZ2V0IGl0LiBUYWtlcyBhIGhhc2ggYW5kIGEgc2V0IG9mIFVSTHMuCiMKIyAkMSBpcyB0aGUgc2hhMSBvZiB0aGUgVVJMLiBDYW4gYmUgIiIgaWYgdGhlIHNoYTEgaXMgdW5rbm93bi4KIyAkMisgYXJlIHRoZSBVUkxzIHRvIGRvd25sb2FkLgpkb3dubG9hZC1vci1idXN0KCkgewogIGxvY2FsIC1yIGhhc2g9IiQxIgogIHNoaWZ0IDEKCiAgdXJscz0oICQqICkKICB3aGlsZSB0cnVlOyBkbwogICAgZm9yIHVybCBpbiAiJHt1cmxzW0BdfSI7IGRvCiAgICAgIGxvY2FsIGZpbGU9IiR7dXJsIyMqL30iCiAgICAgIHJtIC1mICIke2ZpbGV9IgogICAgICBpZiAhIGN1cmwgLWYgLS1pcHY0IC1MbyAiJHtmaWxlfSIgLS1jb25uZWN0LXRpbWVvdXQgMjAgLS1yZXRyeSA2IC0tcmV0cnktZGVsYXkgMTAgIiR7dXJsfSI7IHRoZW4KICAgICAgICBlY2hvICI9PSBGYWlsZWQgdG8gZG93bmxvYWQgJHt1cmx9LiBSZXRyeWluZy4gPT0iCiAgICAgIGVsaWYgW1sgLW4gIiR7aGFzaH0iIF1dICYmICEgdmFsaWRhdGUtaGFzaCAiJHtmaWxlfSIgIiR7aGFzaH0iOyB0aGVuCiAgICAgICAgZWNobyAiPT0gSGFzaCB2YWxpZGF0aW9uIG9mICR7dXJsfSBmYWlsZWQuIFJldHJ5aW5nLiA9PSIKICAgICAgZWxzZQogICAgICAgIGlmIFtbIC1uICIke2hhc2h9IiBdXTsgdGhlbgogICAgICAgICAgZWNobyAiPT0gRG93bmxvYWRlZCAke3VybH0gKFNIQTEgPSAke2hhc2h9KSA9PSIKICAgICAgICBlbHNlCiAgICAgICAgICBlY2hvICI9PSBEb3dubG9hZGVkICR7dXJsfSA9PSIKICAgICAgICBmaQogICAgICAgIHJldHVybgogICAgICBmaQogICAgZG9uZQoKICAgIGVjaG8gIkFsbCBkb3dubG9hZHMgZmFpbGVkOyBzbGVlcGluZyBiZWZvcmUgcmV0cnlpbmciCiAgICBzbGVlcCA2MAogIGRvbmUKfQoKdmFsaWRhdGUtaGFzaCgpIHsKICBsb2NhbCAtciBmaWxlPSIkMSIKICBsb2NhbCAtciBleHBlY3RlZD0iJDIiCiAgbG9jYWwgYWN0dWFsCgogIGFjdHVhbD0kKHNoYTFzdW0gJHtmaWxlfSB8IGF3ayAneyBwcmludCAkMSB9JykgfHwgdHJ1ZQogIGlmIFtbICIke2FjdHVhbH0iICE9ICIke2V4cGVjdGVkfSIgXV07IHRoZW4KICAgIGVjaG8gIj09ICR7ZmlsZX0gY29ycnVwdGVkLCBzaGExICR7YWN0dWFsfSBkb2Vzbid0IG1hdGNoIGV4cGVjdGVkICR7ZXhwZWN0ZWR9ID09IgogICAgcmV0dXJuIDEKICBmaQp9CgpmdW5jdGlvbiBzcGxpdC1jb21tYXMoKSB7CiAgZWNobyAkMSB8IHRyICIsIiAiXG4iCn0KCmZ1bmN0aW9uIHRyeS1kb3dubG9hZC1yZWxlYXNlKCkgewogICMgVE9ETyh6bWVybHlubik6IE5vdyB3ZSBSRUFMTFkgaGF2ZSBubyBleGN1c2Ugbm90IHRvIGRvIHRoZSByZWJvb3QKICAjIG9wdGltaXphdGlvbi4KCiAgbG9jYWwgLXIgbm9kZXVwX3VybHM9KCAkKHNwbGl0LWNvbW1hcyAiJHtOT0RFVVBfVVJMfSIpICkKICBsb2NhbCAtciBub2RldXBfZmlsZW5hbWU9IiR7bm9kZXVwX3VybHNbMF0jIyovfSIKICBpZiBbWyAtbiAiJHtOT0RFVVBfSEFTSDotfSIgXV07IHRoZW4KICAgIGxvY2FsIC1yIG5vZGV1cF9oYXNoPSIke05PREVVUF9IQVNIfSIKICBlbHNlCiAgIyBUT0RPOiBSZW1vdmU/CiAgICBlY2hvICJEb3dubG9hZGluZyBzaGExIChub3QgZm91bmQgaW4gZW52KSIKICAgIGRvd25sb2FkLW9yLWJ1c3QgIiIgIiR7bm9kZXVwX3VybHNbQF0vJS8uc2hhMX0iCiAgICBsb2NhbCAtciBub2RldXBfaGFzaD0kKGNhdCAiJHtub2RldXBfZmlsZW5hbWV9LnNoYTEiKQogIGZpCgogIGVjaG8gIkRvd25sb2FkaW5nIG5vZGV1cCAoJHtub2RldXBfdXJsc1tAXX0pIgogIGRvd25sb2FkLW9yLWJ1c3QgIiR7bm9kZXVwX2hhc2h9IiAiJHtub2RldXBfdXJsc1tAXX0iCgogIGNobW9kICt4IG5vZGV1cAp9CgpmdW5jdGlvbiBkb3dubG9hZC1yZWxlYXNlKCkgewogICMgSW4gY2FzZSBvZiBmYWlsdXJlIGNoZWNraW5nIGludGVncml0eSBvZiByZWxlYXNlLCByZXRyeS4KICB1bnRpbCB0cnktZG93bmxvYWQtcmVsZWFzZTsgZG8KICAgIHNsZWVwIDE1CiAgICBlY2hvICJDb3VsZG4ndCBkb3dubG9hZCByZWxlYXNlLiBSZXRyeWluZy4uLiIKICBkb25lCgogIGVjaG8gIlJ1bm5pbmcgbm9kZXVwIgogICMgV2UgY2FuJ3QgcnVuIGluIHRoZSBmb3JlZ3JvdW5kIGJlY2F1c2Ugb2YgaHR0cHM6Ly9naXRodWIuY29tL2RvY2tlci9kb2NrZXIvaXNzdWVzLzIzNzkzCiAgKCBjZCAke0lOU1RBTExfRElSfTsgLi9ub2RldXAgLS1pbnN0YWxsLXN5c3RlbWQtdW5pdCAtLWNvbmY9JHtJTlNUQUxMX0RJUn0va3ViZV9lbnYueWFtbCAtLXY9OCAgKQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCi9iaW4vc3lzdGVtZC1tYWNoaW5lLWlkLXNldHVwIHx8IGVjaG8gImZhaWxlZCB0byBzZXQgdXAgZW5zdXJlIG1hY2hpbmUtaWQgY29uZmlndXJlZCIKCmVjaG8gIj09IG5vZGV1cCBub2RlIGNvbmZpZyBzdGFydGluZyA9PSIKZW5zdXJlLWluc3RhbGwtZGlyCgpjYXQgPiBrdWJlX2Vudi55YW1sIDw8IF9fRU9GX0tVQkVfRU5WCkFzc2V0czoKLSA3ZDcwZTA5MDk1MTQ4NmNhZTUyZDlhODJiN2FhZjUwNTZmODRmOGVkQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvcmVsZWFzZS92MS40LjYvYmluL2xpbnV4L2FtZDY0L2t1YmVsZXQKLSA5YWRjZDEyMGZkYjdhZDZlNjRjMDYxZTU2YTA1ZmVmYzEyZTk2MThiQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvcmVsZWFzZS92MS40LjYvYmluL2xpbnV4L2FtZDY0L2t1YmVjdGwKLSAxOWQ0OWY3YjJiOTljZDI0OTNkNWFlMGFjZTg5NmM2NGUyODljY2JiQGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9rdWJlcm5ldGVzLXJlbGVhc2UvbmV0d29yay1wbHVnaW5zL2NuaS0wN2E4YTI4NjM3ZTk3YjIyZWI4ZGZlNzEwZWVhZTEzNDRmNjlkMTZlLnRhci5negotIGNiYmE4NTY3NDZhNDQxYzdkMWE5ZTk1ZTE0MWM5ODJhMWI4ODY0ZTZAaHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvdXRpbHMudGFyLmd6CkNsdXN0ZXJOYW1lOiBtaW5pbWFsLmV4YW1wbGUuY29tCkNvbmZpZ0Jhc2U6IG1lbWZzOi8vY2x1c3RlcnMuZXhhbXBsZS5jb20vbWluaW1hbC5leGFtcGxlLmNvbQpJbnN0YW5jZUdyb3VwTmFtZTogbm9kZXMKVGFnczoKLSBfYXV0b21hdGljX3VwZ3JhZGVzCi0gX2F3cwpjaGFubmVsczoKLSBtZW1mczovL2NsdXN0ZXJzLmV4YW1wbGUuY29tL21pbmltYWwuZXhhbXBsZS5jb20vYWRkb25zL2Jvb3RzdHJhcC1jaGFubmVsLnlhbWwKcHJvdG9rdWJlSW1hZ2U6CiAgaGFzaDogN2MzYTBlYzA3MjNmZDM1MDYwOWIyOTU4YmM1YjhhYjAyNTgzODUxYwogIG5hbWU6IHByb3Rva3ViZToxLjUuMAogIHNvdXJjZTogaHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvaW1hZ2VzL3Byb3Rva3ViZS50YXIuZ3oKCl9fRU9GX0tVQkVfRU5WCgpkb3dubG9hZC1yZWxlYXNlCmVjaG8gIj09IG5vZGV1cCBub2RlIGNvbmZpZyBkb25lID09Igo=" + "UserData": "IyEvYmluL2Jhc2gKIyBDb3B5cmlnaHQgMjAxNiBUaGUgS3ViZXJuZXRlcyBBdXRob3JzIEFsbCByaWdodHMgcmVzZXJ2ZWQuCiMKIyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuCiMgWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CiMKIyAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wCiMKIyBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiMgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywKIyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIyBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiMgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuCgpzZXQgLW8gZXJyZXhpdApzZXQgLW8gbm91bnNldApzZXQgLW8gcGlwZWZhaWwKCk5PREVVUF9VUkw9aHR0cHM6Ly9rdWJldXB2Mi5zMy5hbWF6b25hd3MuY29tL2tvcHMvMS41LjAvbGludXgvYW1kNjQvbm9kZXVwCk5PREVVUF9IQVNIPQoKCgoKCgpmdW5jdGlvbiBlbnN1cmUtaW5zdGFsbC1kaXIoKSB7CiAgSU5TVEFMTF9ESVI9Ii92YXIvY2FjaGUva3ViZXJuZXRlcy1pbnN0YWxsIgogICMgT24gQ29udGFpbmVyT1MsIHdlIGluc3RhbGwgdG8gL3Zhci9saWIvdG9vbGJveCBpbnN0YWxsIChiZWNhdXNlIG9mIG5vZXhlYykKICBpZiBbWyAtZCAvdmFyL2xpYi90b29sYm94IF1dOyB0aGVuCiAgICBJTlNUQUxMX0RJUj0iL3Zhci9saWIvdG9vbGJveC9rdWJlcm5ldGVzLWluc3RhbGwiCiAgZmkKICBta2RpciAtcCAke0lOU1RBTExfRElSfQogIGNkICR7SU5TVEFMTF9ESVJ9Cn0KCiMgUmV0cnkgYSBkb3dubG9hZCB1bnRpbCB3ZSBnZXQgaXQuIFRha2VzIGEgaGFzaCBhbmQgYSBzZXQgb2YgVVJMcy4KIwojICQxIGlzIHRoZSBzaGExIG9mIHRoZSBVUkwuIENhbiBiZSAiIiBpZiB0aGUgc2hhMSBpcyB1bmtub3duLgojICQyKyBhcmUgdGhlIFVSTHMgdG8gZG93bmxvYWQuCmRvd25sb2FkLW9yLWJ1c3QoKSB7CiAgbG9jYWwgLXIgaGFzaD0iJDEiCiAgc2hpZnQgMQoKICB1cmxzPSggJCogKQogIHdoaWxlIHRydWU7IGRvCiAgICBmb3IgdXJsIGluICIke3VybHNbQF19IjsgZG8KICAgICAgbG9jYWwgZmlsZT0iJHt1cmwjIyovfSIKICAgICAgcm0gLWYgIiR7ZmlsZX0iCiAgICAgIGlmICEgY3VybCAtZiAtLWlwdjQgLUxvICIke2ZpbGV9IiAtLWNvbm5lY3QtdGltZW91dCAyMCAtLXJldHJ5IDYgLS1yZXRyeS1kZWxheSAxMCAiJHt1cmx9IjsgdGhlbgogICAgICAgIGVjaG8gIj09IEZhaWxlZCB0byBkb3dubG9hZCAke3VybH0uIFJldHJ5aW5nLiA9PSIKICAgICAgZWxpZiBbWyAtbiAiJHtoYXNofSIgXV0gJiYgISB2YWxpZGF0ZS1oYXNoICIke2ZpbGV9IiAiJHtoYXNofSI7IHRoZW4KICAgICAgICBlY2hvICI9PSBIYXNoIHZhbGlkYXRpb24gb2YgJHt1cmx9IGZhaWxlZC4gUmV0cnlpbmcuID09IgogICAgICBlbHNlCiAgICAgICAgaWYgW1sgLW4gIiR7aGFzaH0iIF1dOyB0aGVuCiAgICAgICAgICBlY2hvICI9PSBEb3dubG9hZGVkICR7dXJsfSAoU0hBMSA9ICR7aGFzaH0pID09IgogICAgICAgIGVsc2UKICAgICAgICAgIGVjaG8gIj09IERvd25sb2FkZWQgJHt1cmx9ID09IgogICAgICAgIGZpCiAgICAgICAgcmV0dXJuCiAgICAgIGZpCiAgICBkb25lCgogICAgZWNobyAiQWxsIGRvd25sb2FkcyBmYWlsZWQ7IHNsZWVwaW5nIGJlZm9yZSByZXRyeWluZyIKICAgIHNsZWVwIDYwCiAgZG9uZQp9Cgp2YWxpZGF0ZS1oYXNoKCkgewogIGxvY2FsIC1yIGZpbGU9IiQxIgogIGxvY2FsIC1yIGV4cGVjdGVkPSIkMiIKICBsb2NhbCBhY3R1YWwKCiAgYWN0dWFsPSQoc2hhMXN1bSAke2ZpbGV9IHwgYXdrICd7IHByaW50ICQxIH0nKSB8fCB0cnVlCiAgaWYgW1sgIiR7YWN0dWFsfSIgIT0gIiR7ZXhwZWN0ZWR9IiBdXTsgdGhlbgogICAgZWNobyAiPT0gJHtmaWxlfSBjb3JydXB0ZWQsIHNoYTEgJHthY3R1YWx9IGRvZXNuJ3QgbWF0Y2ggZXhwZWN0ZWQgJHtleHBlY3RlZH0gPT0iCiAgICByZXR1cm4gMQogIGZpCn0KCmZ1bmN0aW9uIHNwbGl0LWNvbW1hcygpIHsKICBlY2hvICQxIHwgdHIgIiwiICJcbiIKfQoKZnVuY3Rpb24gdHJ5LWRvd25sb2FkLXJlbGVhc2UoKSB7CiAgIyBUT0RPKHptZXJseW5uKTogTm93IHdlIFJFQUxMWSBoYXZlIG5vIGV4Y3VzZSBub3QgdG8gZG8gdGhlIHJlYm9vdAogICMgb3B0aW1pemF0aW9uLgoKICBsb2NhbCAtciBub2RldXBfdXJscz0oICQoc3BsaXQtY29tbWFzICIke05PREVVUF9VUkx9IikgKQogIGxvY2FsIC1yIG5vZGV1cF9maWxlbmFtZT0iJHtub2RldXBfdXJsc1swXSMjKi99IgogIGlmIFtbIC1uICIke05PREVVUF9IQVNIOi19IiBdXTsgdGhlbgogICAgbG9jYWwgLXIgbm9kZXVwX2hhc2g9IiR7Tk9ERVVQX0hBU0h9IgogIGVsc2UKICAjIFRPRE86IFJlbW92ZT8KICAgIGVjaG8gIkRvd25sb2FkaW5nIHNoYTEgKG5vdCBmb3VuZCBpbiBlbnYpIgogICAgZG93bmxvYWQtb3ItYnVzdCAiIiAiJHtub2RldXBfdXJsc1tAXS8lLy5zaGExfSIKICAgIGxvY2FsIC1yIG5vZGV1cF9oYXNoPSQoY2F0ICIke25vZGV1cF9maWxlbmFtZX0uc2hhMSIpCiAgZmkKCiAgZWNobyAiRG93bmxvYWRpbmcgbm9kZXVwICgke25vZGV1cF91cmxzW0BdfSkiCiAgZG93bmxvYWQtb3ItYnVzdCAiJHtub2RldXBfaGFzaH0iICIke25vZGV1cF91cmxzW0BdfSIKCiAgY2htb2QgK3ggbm9kZXVwCn0KCmZ1bmN0aW9uIGRvd25sb2FkLXJlbGVhc2UoKSB7CiAgIyBJbiBjYXNlIG9mIGZhaWx1cmUgY2hlY2tpbmcgaW50ZWdyaXR5IG9mIHJlbGVhc2UsIHJldHJ5LgogIHVudGlsIHRyeS1kb3dubG9hZC1yZWxlYXNlOyBkbwogICAgc2xlZXAgMTUKICAgIGVjaG8gIkNvdWxkbid0IGRvd25sb2FkIHJlbGVhc2UuIFJldHJ5aW5nLi4uIgogIGRvbmUKCiAgZWNobyAiUnVubmluZyBub2RldXAiCiAgIyBXZSBjYW4ndCBydW4gaW4gdGhlIGZvcmVncm91bmQgYmVjYXVzZSBvZiBodHRwczovL2dpdGh1Yi5jb20vZG9ja2VyL2RvY2tlci9pc3N1ZXMvMjM3OTMKICAoIGNkICR7SU5TVEFMTF9ESVJ9OyAuL25vZGV1cCAtLWluc3RhbGwtc3lzdGVtZC11bml0IC0tY29uZj0ke0lOU1RBTExfRElSfS9rdWJlX2Vudi55YW1sIC0tdj04ICApCn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKL2Jpbi9zeXN0ZW1kLW1hY2hpbmUtaWQtc2V0dXAgfHwgZWNobyAiZmFpbGVkIHRvIHNldCB1cCBlbnN1cmUgbWFjaGluZS1pZCBjb25maWd1cmVkIgoKZWNobyAiPT0gbm9kZXVwIG5vZGUgY29uZmlnIHN0YXJ0aW5nID09IgplbnN1cmUtaW5zdGFsbC1kaXIKCmNhdCA+IGt1YmVfZW52LnlhbWwgPDwgX19FT0ZfS1VCRV9FTlYKQXNzZXRzOgotIDdkNzBlMDkwOTUxNDg2Y2FlNTJkOWE4MmI3YWFmNTA1NmY4NGY4ZWRAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9yZWxlYXNlL3YxLjQuNi9iaW4vbGludXgvYW1kNjQva3ViZWxldAotIDlhZGNkMTIwZmRiN2FkNmU2NGMwNjFlNTZhMDVmZWZjMTJlOTYxOGJAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9yZWxlYXNlL3YxLjQuNi9iaW4vbGludXgvYW1kNjQva3ViZWN0bAotIDE5ZDQ5ZjdiMmI5OWNkMjQ5M2Q1YWUwYWNlODk2YzY0ZTI4OWNjYmJAaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2t1YmVybmV0ZXMtcmVsZWFzZS9uZXR3b3JrLXBsdWdpbnMvY25pLTA3YThhMjg2MzdlOTdiMjJlYjhkZmU3MTBlZWFlMTM0NGY2OWQxNmUudGFyLmd6Ci0gY2JiYTg1Njc0NmE0NDFjN2QxYTllOTVlMTQxYzk4MmExYjg4NjRlNkBodHRwczovL2t1YmV1cHYyLnMzLmFtYXpvbmF3cy5jb20va29wcy8xLjUuMC9saW51eC9hbWQ2NC91dGlscy50YXIuZ3oKQ2x1c3Rlck5hbWU6IG1pbmltYWwuZXhhbXBsZS5jb20KQ29uZmlnQmFzZTogbWVtZnM6Ly9jbHVzdGVycy5leGFtcGxlLmNvbS9taW5pbWFsLmV4YW1wbGUuY29tCkluc3RhbmNlR3JvdXBOYW1lOiBub2RlcwpUYWdzOgotIF9hdXRvbWF0aWNfdXBncmFkZXMKLSBfYXdzCmNoYW5uZWxzOgotIG1lbWZzOi8vY2x1c3RlcnMuZXhhbXBsZS5jb20vbWluaW1hbC5leGFtcGxlLmNvbS9hZGRvbnMvYm9vdHN0cmFwLWNoYW5uZWwueWFtbApwcm90b2t1YmVJbWFnZToKICBoYXNoOiA3YzNhMGVjMDcyM2ZkMzUwNjA5YjI5NThiYzViOGFiMDI1ODM4NTFjCiAgbmFtZTogcHJvdG9rdWJlOjEuNS4wCiAgc291cmNlOiBodHRwczovL2t1YmV1cHYyLnMzLmFtYXpvbmF3cy5jb20va29wcy8xLjUuMC9pbWFnZXMvcHJvdG9rdWJlLnRhci5negoKX19FT0ZfS1VCRV9FTlYKCmRvd25sb2FkLXJlbGVhc2UKZWNobyAiPT0gbm9kZXVwIG5vZGUgY29uZmlnIGRvbmUgPT0iCg==" } }, "AWSEC2DHCPOptionsminimalexamplecom": { diff --git a/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.6.yaml.template b/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.6.yaml.template index 3487c00f9a..68d4acfc92 100644 --- a/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.6.yaml.template +++ b/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.6.yaml.template @@ -38,6 +38,13 @@ spec: {{ range $arg := DnsControllerArgv }} - "{{ $arg }}" {{ end }} +{{- if .EgressProxy }} + env: +{{ range $name, $value := ProxyEnv }} + - name: {{ $name }} + value: {{ $value }} +{{ end }} +{{- end }} resources: requests: cpu: 50m diff --git a/upup/pkg/fi/cloudup/defaults.go b/upup/pkg/fi/cloudup/defaults.go index 6bab22603b..a7c6c0d14a 100644 --- a/upup/pkg/fi/cloudup/defaults.go +++ b/upup/pkg/fi/cloudup/defaults.go @@ -18,10 +18,12 @@ package cloudup import ( "fmt" + "net" + "strings" + "github.com/golang/glog" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/util/pkg/vfs" - "strings" kopsversion "k8s.io/kops" ) @@ -81,6 +83,11 @@ func PerformAssignments(c *kops.Cluster) error { return err } + c.Spec.EgressProxy, err = assignProxy(c) + if err != nil { + return err + } + return ensureKubernetesVersion(c) } @@ -129,3 +136,84 @@ func FindLatestKubernetesVersion() (string, error) { latestVersion := strings.TrimSpace(string(b)) return latestVersion, nil } + +func assignProxy(cluster *kops.Cluster) (*kops.EgressProxySpec, error) { + + egressProxy := cluster.Spec.EgressProxy + // Add default no_proxy values if we are using a http proxy + if egressProxy != nil { + + var egressSlice []string + if egressProxy.ProxyExcludes != "" { + egressSlice = strings.Split(egressProxy.ProxyExcludes, ",") + } + + ip, _, err := net.ParseCIDR(cluster.Spec.NonMasqueradeCIDR) + if err != nil { + return nil, fmt.Errorf("unable to parse Non Masquerade CIDR") + } + + firstIP, err := incrementIP(ip, cluster.Spec.NonMasqueradeCIDR) + if err != nil { + return nil, fmt.Errorf("unable to get first ip address in Non Masquerade CIDR") + } + + // run through the basic list + for _, exclude := range []string{ + "127.0.0.1", + "localhost", + cluster.Spec.ClusterDNSDomain, // TODO we may want this for public loadbalancers + cluster.Spec.MasterPublicName, + cluster.ObjectMeta.Name, + firstIP, + cluster.Spec.NonMasqueradeCIDR, + } { + if exclude == "" { + continue + } + if !strings.Contains(egressProxy.ProxyExcludes, exclude) { + egressSlice = append(egressSlice, exclude) + } + } + + awsNoProxy := "169.254.169.254" + + if cluster.Spec.CloudProvider == "aws" && !strings.Contains(cluster.Spec.EgressProxy.ProxyExcludes, awsNoProxy) { + egressSlice = append(egressSlice, awsNoProxy) + } + + // the kube-apiserver will need to talk to kubelets on their node IP addresses port 10250 + // for pod logs to be available via the api + if cluster.Spec.NetworkCIDR != "" { + if !strings.Contains(cluster.Spec.EgressProxy.ProxyExcludes, cluster.Spec.NetworkCIDR) { + egressSlice = append(egressSlice, cluster.Spec.NetworkCIDR) + } + } else { + glog.Warningf("No NetworkCIDR defined (yet), not adding to egressProxy.excludes") + } + + egressProxy.ProxyExcludes = strings.Join(egressSlice, ",") + glog.V(8).Infof("Completed setting up Proxy excludes as follows: %q", egressProxy.ProxyExcludes) + } else { + glog.V(8).Info("Not setting up Proxy Excludes") + } + + return egressProxy, nil +} + +func incrementIP(ip net.IP, cidr string) (string, error) { + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + return "", err + } + for i := len(ip) - 1; i >= 0; i-- { + ip[i]++ + if ip[i] != 0 { + break + } + } + if !ipNet.Contains(ip) { + return "", fmt.Errorf("overflowed CIDR while incrementing IP") + } + return ip.String(), nil +} diff --git a/upup/pkg/fi/cloudup/defaults_test.go b/upup/pkg/fi/cloudup/defaults_test.go new file mode 100644 index 0000000000..259ff8f3cc --- /dev/null +++ b/upup/pkg/fi/cloudup/defaults_test.go @@ -0,0 +1,94 @@ +/* +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 cloudup + +import ( + "k8s.io/kops/pkg/apis/kops" + "testing" +) + +func TestPopulateClusterSpec_Proxy(t *testing.T) { + c := buildMinimalCluster() + + c.Spec.EgressProxy = &kops.EgressProxySpec{ + ProxyExcludes: "google.com", + HTTPProxy: kops.HTTPProxy{ + Host: "52.205.179.249", + Port: 3128, + }, + } + + c.Spec.NonMasqueradeCIDR = "100.64.0.1/10" + c.Spec.NetworkCIDR = "192.168.0.0/20" + var err error + c.Spec.EgressProxy, err = assignProxy(c) + if err != nil { + t.Fatalf("unable to assign proxy, %v", err) + } + + expectedExcludes := "google.com,127.0.0.1,localhost,testcluster.test.com,100.64.0.2,100.64.0.1/10,169.254.169.254,192.168.0.0/20" + if c.Spec.EgressProxy.ProxyExcludes != expectedExcludes { + t.Fatalf("Incorrect proxy excludes set: %v, expected %v", c.Spec.EgressProxy.ProxyExcludes, expectedExcludes) + } + + c.Spec.EgressProxy = &kops.EgressProxySpec{ + HTTPProxy: kops.HTTPProxy{ + Host: "52.205.179.249", + Port: 3128, + }, + } + + c.Spec.NonMasqueradeCIDR = "100.64.0.0/10" + c.Spec.NetworkCIDR = "192.168.0.0/20" + c.Spec.EgressProxy.ProxyExcludes = "" + + c.Spec.EgressProxy, err = assignProxy(c) + if err != nil { + t.Fatalf("unable to assign proxy, %v", err) + } + + expectedExcludes = "127.0.0.1,localhost,testcluster.test.com,100.64.0.1,100.64.0.0/10,169.254.169.254,192.168.0.0/20" + if c.Spec.EgressProxy.ProxyExcludes != expectedExcludes { + t.Fatalf("Incorrect proxy excludes set: %v, expected %v", c.Spec.EgressProxy.ProxyExcludes, expectedExcludes) + } + + c.Spec.NonMasqueradeCIDR = "172.16.0.5/12" + c.Spec.NetworkCIDR = "192.168.0.0/20" + c.Spec.CloudProvider = "gce" + c.Spec.EgressProxy.ProxyExcludes = "" + c.Spec.EgressProxy, err = assignProxy(c) + if err != nil { + t.Fatalf("unable to assign proxy, %v", err) + } + + expectedExcludes = "127.0.0.1,localhost,testcluster.test.com,172.16.0.6,172.16.0.5/12,192.168.0.0/20" + if c.Spec.EgressProxy.ProxyExcludes != expectedExcludes { + t.Fatalf("Incorrect proxy excludes set: %v", c.Spec.EgressProxy.ProxyExcludes) + } + + // idempotency test + c.Spec.EgressProxy, err = assignProxy(c) + if err != nil { + t.Fatalf("unable to assign proxy, %v", err) + } + + expectedExcludes = "127.0.0.1,localhost,testcluster.test.com,172.16.0.6,172.16.0.5/12,192.168.0.0/20" + if c.Spec.EgressProxy.ProxyExcludes != expectedExcludes { + t.Fatalf("Incorrect proxy excludes set during idempotency check: %v should have been %v", c.Spec.EgressProxy.ProxyExcludes, expectedExcludes) + } + +} diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 2681eca5dc..b2f0db5fd3 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -31,6 +31,7 @@ import ( "encoding/base64" "fmt" "os" + "strconv" "strings" "text/template" @@ -102,6 +103,8 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap) { dest["Region"] = func() string { return tf.region } + + dest["ProxyEnv"] = tf.ProxyEnv } // SharedVPC is a simple helper function which makes the templates for a shared VPC clearer @@ -197,3 +200,28 @@ func (tf *TemplateFunctions) ExternalDnsArgv() ([]string, error) { return argv, nil } + +func (tf *TemplateFunctions) ProxyEnv() map[string]string { + envs := map[string]string{} + proxies := tf.cluster.Spec.EgressProxy + if proxies == nil { + return envs + } + httpProxy := proxies.HTTPProxy + if httpProxy.Host != "" { + var portSuffix string + if httpProxy.Port != 0 { + portSuffix = ":" + strconv.Itoa(httpProxy.Port) + } else { + portSuffix = "" + } + url := "http://" + httpProxy.Host + portSuffix + envs["http_proxy"] = url + envs["https_proxy"] = url + } + if proxies.ProxyExcludes != "" { + envs["no_proxy"] = proxies.ProxyExcludes + envs["NO_PROXY"] = proxies.ProxyExcludes + } + return envs +} diff --git a/upup/pkg/fi/cloudup/vspheretasks/attachiso.go b/upup/pkg/fi/cloudup/vspheretasks/attachiso.go index eee4a8c257..d2c83dcde7 100644 --- a/upup/pkg/fi/cloudup/vspheretasks/attachiso.go +++ b/upup/pkg/fi/cloudup/vspheretasks/attachiso.go @@ -91,7 +91,8 @@ func (_ *AttachISO) CheckChanges(a, e, changes *AttachISO) error { // RenderVSphere executes the actual task logic, for vSphere cloud. func (_ *AttachISO) RenderVSphere(t *vsphere.VSphereAPITarget, a, e, changes *AttachISO) error { - startupScript, err := changes.BootstrapScript.ResourceNodeUp(changes.IG) + // TODO #3071 .. need to replace the nil for http proxy support + startupScript, err := changes.BootstrapScript.ResourceNodeUp(changes.IG, nil) if err != nil { return fmt.Errorf("error on resource nodeup: %v", err) }