Skip to content

Commit 8739632

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request #3633 from brdude/extra_user-data
Automatic merge from submit-queue. Allow passing in extra user-data to cloud-init This allows for the utilization of multipart mime to pass extra data to cloud-init. This gives you the ability to utilize cloud-init features such as setting up a CM or monitoring so that existing infrastructure components can be leveraged.
2 parents 760d58e + ef24cec commit 8739632

17 files changed

Lines changed: 1784 additions & 16 deletions

cmd/kops/integration_test.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ func TestMinimalCloudformation(t *testing.T) {
7979
runTestCloudformation(t, "minimal.example.com", "minimal-cloudformation", "v1alpha2", false)
8080
}
8181

82+
// TestAdditionalUserData runs the test on passing additional user-data to an instance at bootstrap.
83+
func TestAdditionalUserData(t *testing.T) {
84+
runTestCloudformation(t, "additionaluserdata.example.com", "additional_user-data", "v1alpha2", false)
85+
}
86+
8287
// TestMinimal_141 runs the test on a configuration from 1.4.1 release
8388
func TestMinimal_141(t *testing.T) {
8489
runTestAWS(t, "minimal-141.example.com", "minimal-141", "v1alpha0", false, 1)
@@ -508,21 +513,36 @@ func runTestCloudformation(t *testing.T, clusterName string, srcDir string, vers
508513
t.Fatalf("cloudformation output differed from expected")
509514
}
510515

511-
actualExtracted, err := yaml.Marshal(extracted)
512-
if err != nil {
513-
t.Fatalf("unexpected error serializing extracted values: %v", err)
514-
}
515516
expectedExtracted, err := ioutil.ReadFile(path.Join(srcDir, expectedCfPath+".extracted.yaml"))
516517
if err != nil {
517518
t.Fatalf("unexpected error reading expected extracted cloudformation output: %v", err)
518519
}
519520

520-
actualExtractedTrimmed := strings.TrimSpace(string(actualExtracted))
521-
expectedExtractedTrimmed := strings.TrimSpace(string(expectedExtracted))
522-
if actualExtractedTrimmed != expectedExtractedTrimmed {
523-
diffString := diff.FormatDiff(actualExtractedTrimmed, expectedExtractedTrimmed)
524-
t.Logf("diff:\n%s\n", diffString)
525-
t.Fatalf("cloudformation output differed from expected")
521+
expected := make(map[string]string)
522+
err = yaml.Unmarshal(expectedExtracted, &expected)
523+
if err != nil {
524+
t.Fatalf("unexpected error unmarshal expected extracted cloudformation output: %v", err)
525+
}
526+
527+
if len(extracted) != len(expected) {
528+
t.Fatalf("error differed number of cloudformation in expected and extracted: %v", err)
529+
}
530+
531+
for key, expectedValue := range expected {
532+
extractedValue, ok := extracted[key]
533+
if !ok {
534+
t.Fatalf("unexpected error expected cloudformation not found for k: %v", key)
535+
}
536+
537+
// Strip cariage return as expectedValue is stored in a yaml string literal
538+
// and golang will automaticaly strip CR from any string literal
539+
extractedValueTrimmed := strings.Replace(extractedValue, "\r", "", -1)
540+
if expectedValue != extractedValueTrimmed {
541+
542+
diffString := diff.FormatDiff(expectedValue, extractedValueTrimmed)
543+
t.Logf("diff for key %s:\n%s\n\n\n\n\n\n", key, diffString)
544+
t.Fatalf("cloudformation output differed from expected")
545+
}
526546
}
527547
}
528548
}

docs/instance_groups.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,32 @@ EBS-Optimized instances can be created by setting the following field:
213213
spec:
214214
rootVolumeOptimization: true
215215
```
216+
217+
## Additional user-data for cloud-init
218+
219+
Kops utilizes cloud-init to initialize and setup a host at boot time. However in certain cases you may already be leaveraging certain features of cloud-init in your infrastructure and would like to continue doing so. More information on cloud-init can be found [here](http://cloudinit.readthedocs.io/en/latest/)
220+
221+
222+
Aditional user-user data can be passed to the host provisioning by setting the `ExtraUserData` field. A list of valid user-data content-types can be found [here](http://cloudinit.readthedocs.io/en/latest/topics/format.html#mime-multi-part-archive)
223+
224+
Example:
225+
```
226+
spec:
227+
extraUserData:
228+
- name: myscript.sh
229+
type: text/x-shellscript
230+
content: |
231+
#!/bin/sh
232+
echo "Hello World. The time is now $(date -R)!" | tee /root/output.txt
233+
- name: local_repo.txt
234+
type: text/cloud-config
235+
content: |
236+
#cloud-config
237+
apt:
238+
primary:
239+
- arches: [default]
240+
uri: http://local-mirror.mydomain
241+
search:
242+
- http://local-mirror.mydomain
243+
- http://archive.ubuntu.com
244+
```

pkg/apis/kops/instancegroup.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ type InstanceGroupSpec struct {
109109
Kubelet *KubeletConfigSpec `json:"kubelet,omitempty"`
110110
// Taints indicates the kubernetes taints for nodes in this group
111111
Taints []string `json:"taints,omitempty"`
112+
// AdditionalUserData is any aditional user-data to be passed to the host
113+
AdditionalUserData []UserData `json:"additionalUserData,omitempty"`
114+
}
115+
116+
// UserData defines a user-data section
117+
type UserData struct {
118+
// Name is the name of the user-data
119+
Name string `json:"name,omitempty"`
120+
// Type is the type of user-data
121+
Type string `json:"type,omitempty"`
122+
// Content is the user-data content
123+
Content string `json:"content,omitempty"`
112124
}
113125

114126
// PerformAssignmentsInstanceGroups populates InstanceGroups with default values

pkg/apis/kops/v1alpha1/instancegroup.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,19 @@ type InstanceGroupSpec struct {
8989
Kubelet *KubeletConfigSpec `json:"kubelet,omitempty"`
9090
// Taints indicates the kubernetes taints for nodes in this group
9191
Taints []string `json:"taints,omitempty"`
92+
// AdditionalUserData is any aditional user-data to be passed to the host
93+
AdditionalUserData []UserData `json:"additionalUserData,omitempty"`
9294
// Zones is the names of the Zones where machines in this instance group should be placed
9395
// This is needed for regional subnets (e.g. GCE), to restrict placement to particular zones
9496
Zones []string `json:"zones,omitempty"`
9597
}
98+
99+
// UserData defines a user-data section
100+
type UserData struct {
101+
// Name is the name of the user-data
102+
Name string `json:"name,omitempty"`
103+
// Type is the type of user-data
104+
Type string `json:"type,omitempty"`
105+
// Content is the user-data content
106+
Content string `json:"content,omitempty"`
107+
}

pkg/apis/kops/v1alpha1/zz_generated.conversion.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
139139
Convert_kops_SSHCredentialList_To_v1alpha1_SSHCredentialList,
140140
Convert_v1alpha1_SSHCredentialSpec_To_kops_SSHCredentialSpec,
141141
Convert_kops_SSHCredentialSpec_To_v1alpha1_SSHCredentialSpec,
142+
Convert_v1alpha1_UserData_To_kops_UserData,
143+
Convert_kops_UserData_To_v1alpha1_UserData,
142144
Convert_v1alpha1_WeaveNetworkingSpec_To_kops_WeaveNetworkingSpec,
143145
Convert_kops_WeaveNetworkingSpec_To_v1alpha1_WeaveNetworkingSpec,
144146
)
@@ -587,7 +589,6 @@ func autoConvert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *
587589
out.Project = in.Project
588590
out.MasterPublicName = in.MasterPublicName
589591
out.MasterInternalName = in.MasterInternalName
590-
out.AdditionalSANs = in.AdditionalSANs
591592
out.NetworkCIDR = in.NetworkCIDR
592593
out.NetworkID = in.NetworkID
593594
if in.Topology != nil {
@@ -603,6 +604,7 @@ func autoConvert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *
603604
out.KeyStore = in.KeyStore
604605
out.ConfigStore = in.ConfigStore
605606
out.DNSZone = in.DNSZone
607+
out.AdditionalSANs = in.AdditionalSANs
606608
out.ClusterDNSDomain = in.ClusterDNSDomain
607609
// WARNING: in.Multizone requires manual conversion: does not exist in peer-type
608610
out.ServiceClusterIPRange = in.ServiceClusterIPRange
@@ -822,7 +824,6 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec,
822824
out.Project = in.Project
823825
out.MasterPublicName = in.MasterPublicName
824826
out.MasterInternalName = in.MasterInternalName
825-
out.AdditionalSANs = in.AdditionalSANs
826827
out.NetworkCIDR = in.NetworkCIDR
827828
out.NetworkID = in.NetworkID
828829
if in.Topology != nil {
@@ -838,6 +839,7 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec,
838839
out.KeyStore = in.KeyStore
839840
out.ConfigStore = in.ConfigStore
840841
out.DNSZone = in.DNSZone
842+
out.AdditionalSANs = in.AdditionalSANs
841843
out.ClusterDNSDomain = in.ClusterDNSDomain
842844
out.ServiceClusterIPRange = in.ServiceClusterIPRange
843845
out.NonMasqueradeCIDR = in.NonMasqueradeCIDR
@@ -1681,6 +1683,17 @@ func autoConvert_v1alpha1_InstanceGroupSpec_To_kops_InstanceGroupSpec(in *Instan
16811683
out.Kubelet = nil
16821684
}
16831685
out.Taints = in.Taints
1686+
if in.AdditionalUserData != nil {
1687+
in, out := &in.AdditionalUserData, &out.AdditionalUserData
1688+
*out = make([]kops.UserData, len(*in))
1689+
for i := range *in {
1690+
if err := Convert_v1alpha1_UserData_To_kops_UserData(&(*in)[i], &(*out)[i], s); err != nil {
1691+
return err
1692+
}
1693+
}
1694+
} else {
1695+
out.AdditionalUserData = nil
1696+
}
16841697
out.Zones = in.Zones
16851698
return nil
16861699
}
@@ -1735,6 +1748,17 @@ func autoConvert_kops_InstanceGroupSpec_To_v1alpha1_InstanceGroupSpec(in *kops.I
17351748
out.Kubelet = nil
17361749
}
17371750
out.Taints = in.Taints
1751+
if in.AdditionalUserData != nil {
1752+
in, out := &in.AdditionalUserData, &out.AdditionalUserData
1753+
*out = make([]UserData, len(*in))
1754+
for i := range *in {
1755+
if err := Convert_kops_UserData_To_v1alpha1_UserData(&(*in)[i], &(*out)[i], s); err != nil {
1756+
return err
1757+
}
1758+
}
1759+
} else {
1760+
out.AdditionalUserData = nil
1761+
}
17381762
return nil
17391763
}
17401764

@@ -2606,6 +2630,30 @@ func Convert_kops_SSHCredentialSpec_To_v1alpha1_SSHCredentialSpec(in *kops.SSHCr
26062630
return autoConvert_kops_SSHCredentialSpec_To_v1alpha1_SSHCredentialSpec(in, out, s)
26072631
}
26082632

2633+
func autoConvert_v1alpha1_UserData_To_kops_UserData(in *UserData, out *kops.UserData, s conversion.Scope) error {
2634+
out.Name = in.Name
2635+
out.Type = in.Type
2636+
out.Content = in.Content
2637+
return nil
2638+
}
2639+
2640+
// Convert_v1alpha1_UserData_To_kops_UserData is an autogenerated conversion function.
2641+
func Convert_v1alpha1_UserData_To_kops_UserData(in *UserData, out *kops.UserData, s conversion.Scope) error {
2642+
return autoConvert_v1alpha1_UserData_To_kops_UserData(in, out, s)
2643+
}
2644+
2645+
func autoConvert_kops_UserData_To_v1alpha1_UserData(in *kops.UserData, out *UserData, s conversion.Scope) error {
2646+
out.Name = in.Name
2647+
out.Type = in.Type
2648+
out.Content = in.Content
2649+
return nil
2650+
}
2651+
2652+
// Convert_kops_UserData_To_v1alpha1_UserData is an autogenerated conversion function.
2653+
func Convert_kops_UserData_To_v1alpha1_UserData(in *kops.UserData, out *UserData, s conversion.Scope) error {
2654+
return autoConvert_kops_UserData_To_v1alpha1_UserData(in, out, s)
2655+
}
2656+
26092657
func autoConvert_v1alpha1_WeaveNetworkingSpec_To_kops_WeaveNetworkingSpec(in *WeaveNetworkingSpec, out *kops.WeaveNetworkingSpec, s conversion.Scope) error {
26102658
out.MTU = in.MTU
26112659
return nil

pkg/apis/kops/v1alpha1/zz_generated.deepcopy.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
258258
in.(*TopologySpec).DeepCopyInto(out.(*TopologySpec))
259259
return nil
260260
}, InType: reflect.TypeOf(&TopologySpec{})},
261+
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
262+
in.(*UserData).DeepCopyInto(out.(*UserData))
263+
return nil
264+
}, InType: reflect.TypeOf(&UserData{})},
261265
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
262266
in.(*WeaveNetworkingSpec).DeepCopyInto(out.(*WeaveNetworkingSpec))
263267
return nil
@@ -759,6 +763,11 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) {
759763
(*in).DeepCopyInto(*out)
760764
}
761765
}
766+
if in.AdditionalSANs != nil {
767+
in, out := &in.AdditionalSANs, &out.AdditionalSANs
768+
*out = make([]string, len(*in))
769+
copy(*out, *in)
770+
}
762771
if in.Multizone != nil {
763772
in, out := &in.Multizone, &out.Multizone
764773
if *in == nil {
@@ -1776,6 +1785,11 @@ func (in *InstanceGroupSpec) DeepCopyInto(out *InstanceGroupSpec) {
17761785
*out = make([]string, len(*in))
17771786
copy(*out, *in)
17781787
}
1788+
if in.AdditionalUserData != nil {
1789+
in, out := &in.AdditionalUserData, &out.AdditionalUserData
1790+
*out = make([]UserData, len(*in))
1791+
copy(*out, *in)
1792+
}
17791793
if in.Zones != nil {
17801794
in, out := &in.Zones, &out.Zones
17811795
*out = make([]string, len(*in))
@@ -2827,6 +2841,22 @@ func (in *TopologySpec) DeepCopy() *TopologySpec {
28272841
return out
28282842
}
28292843

2844+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
2845+
func (in *UserData) DeepCopyInto(out *UserData) {
2846+
*out = *in
2847+
return
2848+
}
2849+
2850+
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserData.
2851+
func (in *UserData) DeepCopy() *UserData {
2852+
if in == nil {
2853+
return nil
2854+
}
2855+
out := new(UserData)
2856+
in.DeepCopyInto(out)
2857+
return out
2858+
}
2859+
28302860
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
28312861
func (in *WeaveNetworkingSpec) DeepCopyInto(out *WeaveNetworkingSpec) {
28322862
*out = *in

pkg/apis/kops/v1alpha2/instancegroup.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,16 @@ type InstanceGroupSpec struct {
101101
Kubelet *KubeletConfigSpec `json:"kubelet,omitempty"`
102102
// Taints indicates the kubernetes taints for nodes in this group
103103
Taints []string `json:"taints,omitempty"`
104+
// AdditionalUserData is any aditional user-data to be passed to the host
105+
AdditionalUserData []UserData `json:"additionalUserData,omitempty"`
106+
}
107+
108+
// UserData defines a user-data section
109+
type UserData struct {
110+
// Name is the name of the user-data
111+
Name string `json:"name,omitempty"`
112+
// Type is the type of user-data
113+
Type string `json:"type,omitempty"`
114+
// Content is the user-data content
115+
Content string `json:"content,omitempty"`
104116
}

0 commit comments

Comments
 (0)