diff --git a/examples/14-windows-nodes.yaml b/examples/14-windows-nodes.yaml index 59dcb25f5f..698247685c 100644 --- a/examples/14-windows-nodes.yaml +++ b/examples/14-windows-nodes.yaml @@ -1,5 +1,5 @@ # An example of ClusterConfig containing Windows and Linux node groups to support Windows workloads -# This example should be run with `eksctl create cluster -f 14-windows-nodes.yaml --install-vpc-controllers` +# This example should be run with `eksctl create cluster -f 14-windows-nodes.yaml` --- apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig @@ -13,6 +13,8 @@ nodeGroups: amiFamily: WindowsServer2019FullContainer minSize: 2 maxSize: 3 + +managedNodeGroups: - name: linux-ng instanceType: t2.large minSize: 2 diff --git a/integration/tests/windows/windows_test.go b/integration/tests/windows/windows_test.go index 67b08e1994..298726b343 100644 --- a/integration/tests/windows/windows_test.go +++ b/integration/tests/windows/windows_test.go @@ -70,7 +70,6 @@ var _ = Describe("(Integration) [Windows Nodegroups]", func() { "--config-file", "-", "--verbose", "4", "--kubeconfig", params.KubeconfigPath, - "--install-vpc-controllers", ). WithoutArg("--region", params.Region). WithStdin(bytes.NewReader(data)) diff --git a/pkg/actions/nodegroup/create.go b/pkg/actions/nodegroup/create.go index 57205cc409..77cc8bbb75 100644 --- a/pkg/actions/nodegroup/create.go +++ b/pkg/actions/nodegroup/create.go @@ -160,6 +160,15 @@ func (m *Manager) nodeCreationTasks(options CreateOpts, nodegroupFilter filter.N taskTree.Append(m.stackManager.NewClusterCompatTask()) } + if m.cfg.HasWindowsNodeGroup() { + taskTree.Append(&eks.WindowsIPAMTask{ + Info: "enable Windows IPAM", + ClientsetFunc: func() (kubernetes.Interface, error) { + return m.ctl.NewStdClientSet(m.cfg) + }, + }) + } + awsNodeUsesIRSA, err := init.DoesAWSNodeUseIRSA(m.ctl.Provider, m.clientSet) if err != nil { return errors.Wrap(err, "couldn't check aws-node for annotation") diff --git a/pkg/addons/assets.go b/pkg/addons/assets.go index 5baaf0a456..8acdc6f886 100644 --- a/pkg/addons/assets.go +++ b/pkg/addons/assets.go @@ -3,12 +3,7 @@ // assets/efa-device-plugin.yaml (3.084kB) // assets/neuron-device-plugin.yaml (3.623kB) // assets/nvidia-device-plugin.yaml (2.369kB) -// assets/vpc-admission-webhook-config.yaml (524B) -// assets/vpc-admission-webhook-csr.yaml (234B) -// assets/vpc-admission-webhook-dep.yaml (1.675kB) -// assets/vpc-admission-webhook.yaml (231B) -// assets/vpc-resource-controller-dep.yaml (1.679kB) -// assets/vpc-resource-controller.yaml (565B) +// assets/vpc-controller-metadata.yaml (924B) package addons @@ -137,123 +132,23 @@ func nvidiaDevicePluginYaml() (*asset, error) { return a, nil } -var _vpcAdmissionWebhookConfigYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x91\x4f\x6b\xf3\x30\x0c\xc6\xef\xf9\x14\x22\xf7\xa4\xf4\xf6\xe2\xdb\x4b\x29\x63\x87\xc1\x18\x63\x3b\x8c\x1d\x14\x47\x4d\x45\x62\xcb\x58\x76\x4a\xf7\xe9\x47\xfe\xb4\xac\xb0\xd5\x17\xdb\x7a\xa4\xdf\x63\xc9\x18\xf8\x8d\xa2\xb2\x78\x03\xd8\x3a\xd6\xe9\x18\xa9\x63\x4d\x11\x13\x8b\xaf\xfb\x7f\x5a\xb3\x6c\xc6\x6d\x43\x09\xb7\x45\xcf\xbe\x35\xf0\x94\x13\x26\xf6\xdd\x3b\x35\x47\x91\x7e\x27\xfe\xc0\x5d\x5e\x2a\x0a\x47\x09\x5b\x4c\x68\x0a\x00\x8f\x8e\x0c\x8c\xc1\x56\x57\x7a\x75\x5a\x8a\x2a\x7b\xe8\xd6\x0c\x0d\x68\xc9\x40\x9f\x1b\xaa\xf4\xac\x89\x5c\x01\x30\x60\x43\x83\x4e\x10\x00\x0c\xe1\x0f\x4a\xb1\xee\x73\x62\x75\xcf\xaf\x46\x87\x5f\xe2\xf1\xa4\xb5\x15\x37\x63\xed\xc0\xe4\xd3\xf2\xfa\xc5\x08\x40\x29\x8e\x6c\xe9\x72\xbd\xdb\xc2\x4d\xce\xaf\x4d\x2c\x2b\x60\x3a\x1a\x28\x37\x6e\x1a\x1b\x95\x73\x3c\xe6\x81\xf4\xe2\x52\x81\x04\x5a\xc6\xa7\x06\x3e\xa0\xdc\xbd\xec\xff\xbf\xee\x4b\xf8\xbc\x32\x30\xf0\x43\x94\x1c\x26\xbd\x2c\x6f\xe2\xeb\x0f\xce\xca\xb8\xfd\xa1\x45\x52\xc9\xd1\xd2\xac\x04\x69\x75\xd5\x0e\xc8\x43\x8e\xf4\x2c\x03\xdb\xb3\x81\xc7\xce\x4b\xa4\xe2\x3b\x00\x00\xff\xff\x49\xee\x9e\x02\x0c\x02\x00\x00") +var _vpcControllerMetadataYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x93\xc1\x6e\xc2\x40\x0c\x44\xef\xf9\x8a\xfc\xc0\x06\x71\xab\x72\x2c\xbd\xf6\x02\x52\x7b\x76\x36\x66\xb1\x92\xd8\xa9\xed\x4d\x45\xbf\xbe\x42\x14\x5a\x21\xa8\x82\x38\x27\xe3\x79\x9e\xf1\x86\x10\x0a\x18\xe9\x0d\xd5\x48\xb8\x2e\xa7\x65\xd1\x11\xb7\x75\xb9\x41\x9d\x28\x62\x31\xa0\x43\x0b\x0e\x75\x51\x96\x0c\x03\xd6\xe5\x34\xc6\x00\xed\x40\x76\x50\x84\x4f\x6c\x76\x22\xdd\xcf\x57\x1b\x21\x62\x5d\x76\xb9\xc1\x60\x7b\x73\x1c\x8a\xe2\xd2\xe2\xac\x55\x4c\x64\xae\xe0\x24\x5c\x75\x4f\x56\x91\x2c\xa6\x65\x83\x0e\x27\x88\xd7\xec\xe0\xc4\xe9\xfd\x68\xb2\x12\xde\x52\xca\x47\xc5\x5c\xb2\x10\xb7\xe9\x0e\xba\x88\xea\xb4\xa5\x08\x8e\x76\x1d\x6a\xf5\xfb\xc7\x86\x12\x13\xa7\x35\x7e\x64\x34\x9f\x4b\x54\xfd\x1f\xcf\x38\xda\xe2\x5c\xc3\x0b\x8e\xbd\xec\x07\xe4\xd9\xd3\xef\xd8\x55\x1b\x88\x15\x64\xdf\x89\xd2\xd7\x45\x0d\xa7\x65\xfb\x6c\x8e\xba\x96\xfe\xd6\x29\x28\x9a\x64\x8d\x18\xa2\xb0\xab\xf4\x3d\xea\xa3\x4e\xcf\xc4\x2d\x71\x7a\xc4\xf0\xce\x14\xaf\xcd\x9c\x9f\xe3\x9f\x47\x13\x15\x67\x37\x15\x0e\xb7\x66\xb7\x7d\xbe\x03\x00\x00\xff\xff\xad\xa6\xf6\xbd\x9c\x03\x00\x00") -func vpcAdmissionWebhookConfigYamlBytes() ([]byte, error) { +func vpcControllerMetadataYamlBytes() ([]byte, error) { return bindataRead( - _vpcAdmissionWebhookConfigYaml, - "vpc-admission-webhook-config.yaml", + _vpcControllerMetadataYaml, + "vpc-controller-metadata.yaml", ) } -func vpcAdmissionWebhookConfigYaml() (*asset, error) { - bytes, err := vpcAdmissionWebhookConfigYamlBytes() +func vpcControllerMetadataYaml() (*asset, error) { + bytes, err := vpcControllerMetadataYamlBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "vpc-admission-webhook-config.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7d, 0x11, 0x23, 0x17, 0x2f, 0xdc, 0x4e, 0x18, 0xaa, 0xc8, 0x66, 0xee, 0xf3, 0xc1, 0x85, 0x63, 0xb1, 0xe3, 0x53, 0x57, 0x80, 0x96, 0xe6, 0x54, 0x26, 0x46, 0x6b, 0x3f, 0x17, 0x20, 0x31, 0x8}} - return a, nil -} - -var _vpcAdmissionWebhookCsrYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\x8d\x4b\x4e\xc4\x30\x10\x44\xf7\x3e\x45\x5f\x20\x41\xb3\x43\xde\x72\x03\x90\xd8\x77\xec\xc2\x69\x19\x7f\x70\xb7\x83\xe6\xf6\x28\x13\xa4\xd9\x95\xaa\x9e\x5e\x71\x97\x4f\x0c\x95\x56\x3d\x05\x0c\x93\x2f\x09\x6c\xd0\x35\xbf\xea\x2a\xed\xe5\xb8\x6d\x30\xbe\xb9\x2c\x35\x7a\x7a\x7b\x12\x1f\x92\xaa\xd4\xf4\x8e\x9f\x09\x35\x57\x60\x1c\xd9\xd8\x3b\xa2\xca\x05\x9e\x8e\x1e\x16\x8e\x45\xf4\x94\x2f\xbf\xd8\xf6\xd6\xf2\x9a\xe7\x86\x45\xef\x6a\x28\x4e\x3b\xc2\xc9\xa7\xd1\x66\xd7\x33\x2d\x74\x4d\x9e\xa7\xed\xa8\xf6\x78\x8a\x8e\x68\x2a\x27\xfc\x23\x51\x92\x18\x7f\x93\x4a\xaa\x6c\x73\xe0\xd1\x66\xdc\x09\x35\x48\xdf\x31\x0a\xaa\x5d\x36\x8c\x03\x83\x4e\x9b\xfb\x0b\x00\x00\xff\xff\xf1\x7d\x42\x97\xea\x00\x00\x00") - -func vpcAdmissionWebhookCsrYamlBytes() ([]byte, error) { - return bindataRead( - _vpcAdmissionWebhookCsrYaml, - "vpc-admission-webhook-csr.yaml", - ) -} - -func vpcAdmissionWebhookCsrYaml() (*asset, error) { - bytes, err := vpcAdmissionWebhookCsrYamlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "vpc-admission-webhook-csr.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x56, 0xf4, 0x54, 0x57, 0xf8, 0xbd, 0x8a, 0x1a, 0x67, 0xb2, 0x29, 0x18, 0xe2, 0x44, 0x8a, 0xc2, 0x7f, 0x44, 0x26, 0x4c, 0x52, 0xe0, 0x70, 0xe0, 0xc8, 0x5a, 0x74, 0x76, 0x2, 0xcd, 0x3e, 0xa}} - return a, nil -} - -var _vpcAdmissionWebhookDepYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x54\xc1\x6e\xdb\x3a\x10\xbc\xfb\x2b\xf6\x92\x97\x93\xa5\x97\xa0\xc8\x81\x40\x0a\x04\x4d\x0a\x04\x6d\x93\xa0\x29\x7a\x5f\x53\x13\x99\x10\x45\xb2\xe4\x4a\x8e\xfe\xbe\xa0\xad\xa4\xb6\x1c\x37\x39\x14\xad\x0e\x3e\xec\xec\xcc\x2c\x87\x5e\x72\x30\xdf\x11\x93\xf1\x4e\x11\x87\x90\xca\xfe\x64\xd6\x18\x57\x29\xba\x44\xb0\x7e\x68\xe1\x64\xd6\x42\xb8\x62\x61\x35\x23\x72\xdc\x42\x51\x1f\xf4\x9c\xab\xd6\xa4\xcc\x9c\xaf\xb0\x58\x7a\xdf\x8c\x68\x0a\xac\xa1\xa8\xe9\x16\x98\xa7\x21\x09\xda\x19\x91\xe5\x05\x6c\xca\x02\x94\x7d\x0e\x29\xa4\x00\x9d\x9b\x22\x82\x35\x9a\x93\xa2\x93\x19\x51\x92\xc8\x82\x7a\xd8\xd0\x65\x08\x50\xf4\x15\x3a\x82\x05\x19\x86\x85\x16\x1f\x37\x70\xcb\xa2\x97\x9f\xb7\xec\x7e\x6b\x48\x24\x68\x83\x65\xc1\xc8\xde\x3a\x6a\xfe\xec\x8e\xd0\x2b\x52\x44\x4f\xf3\xe7\x4f\x7b\x27\x6c\x1c\xe2\x16\x7d\xfe\x4a\x7e\xcf\x36\xb1\xde\x62\x6d\x98\x73\xb1\xe9\x03\xa2\x7c\x34\x16\xe7\x25\x44\x97\x23\xaf\xd4\x88\x92\xd6\xbf\x45\x58\xa7\x3d\xa5\x7d\xc2\x70\x88\xd5\x60\x78\x89\x74\x7b\xbf\x8e\xf0\x7e\x8c\xf6\xb6\x47\x8c\xa6\xc2\xf9\xca\xb8\xca\xaf\xd2\xb4\x9d\x6d\xf2\xd6\xd7\xe2\x93\x54\x88\x71\x0a\xf7\xe7\xef\x26\xa5\xd3\xf7\xff\x9d\x6c\x95\x4c\xcb\x35\x14\x1d\x1f\xa5\xa2\x6a\x62\x01\x1d\x8b\xa3\x54\x1c\xa5\x12\x4d\x2a\x5f\x0c\x4b\xf5\xff\x17\xa7\xc5\xd9\xf1\x54\xe4\xae\xb3\xf6\xce\x5b\xa3\x07\x45\x17\x76\xc5\xc3\xf6\xac\xbd\xb7\x5d\x8b\x2f\xbe\x73\xb2\x17\xef\xe6\x62\x46\xf5\xf9\x3a\x9c\x9d\x0e\xa2\x36\xf3\xee\x58\x96\x8a\xf6\x83\x9c\xf4\x46\x70\x75\xeb\xec\xa0\x48\x62\x87\x11\x5c\xfa\x24\x37\x90\x95\x8f\xcd\x4e\x9d\x1f\x1e\x8c\x33\x32\xfc\x1a\xc9\xf9\x0a\x17\x7b\xd5\x2c\xfb\xa3\x33\x11\xd5\x65\x17\x8d\xab\xef\xf5\x12\x55\x67\x8d\xab\xaf\x6b\xe7\x9f\xcb\x57\x8f\xd0\x9d\xe4\x95\xde\x19\x2a\x6b\x3e\xdd\xe7\x37\xc4\x76\x2f\x81\xf5\xe6\x5c\x3d\x86\x88\x75\xd2\x13\x3c\x77\x34\x18\x14\x2d\x20\x5c\xe4\xed\x8e\x0e\x82\x54\x18\x5f\xfa\xe9\xf1\x89\x7c\x40\xe4\xbc\x94\x74\xed\xf6\xc0\x9e\x6d\x87\x3d\xfd\xec\x60\x8d\xeb\x1e\xdf\xec\xcb\x51\x2f\xff\x94\x33\xb7\xd5\xd9\xf4\x5f\xfa\xc6\x44\xfe\x41\x18\x7f\x27\x87\xcd\xbe\xbc\xf0\x7e\x1d\x5a\x93\x94\x5f\x65\xd9\x95\xdd\xd4\x6e\x0e\xbf\x7b\xa3\xca\xcf\x00\x00\x00\xff\xff\x52\xd4\x1b\x58\x8b\x06\x00\x00") - -func vpcAdmissionWebhookDepYamlBytes() ([]byte, error) { - return bindataRead( - _vpcAdmissionWebhookDepYaml, - "vpc-admission-webhook-dep.yaml", - ) -} - -func vpcAdmissionWebhookDepYaml() (*asset, error) { - bytes, err := vpcAdmissionWebhookDepYamlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "vpc-admission-webhook-dep.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3b, 0x48, 0x71, 0x63, 0xbb, 0x59, 0xcc, 0xd5, 0x1b, 0x30, 0xc7, 0x1c, 0x5e, 0xf2, 0x65, 0x72, 0xff, 0xa9, 0x4d, 0x14, 0x6c, 0xce, 0x49, 0xa8, 0x6f, 0x4d, 0x8e, 0x19, 0xf7, 0xee, 0xfa, 0x8c}} - return a, nil -} - -var _vpcAdmissionWebhookYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8c\x31\xae\xc2\x40\x0c\x05\xfb\x3d\x85\x2f\xe0\xe2\xeb\xa7\xf2\x29\x90\x90\xe8\x9d\xcd\x13\xac\x92\xcd\x5a\x6b\x13\xc4\xed\x51\x22\x0a\x1a\x44\x67\xf9\xcd\x0c\x33\x27\xb5\x72\x41\xf7\xd2\x56\xa1\xed\x2f\xcd\x65\x9d\x84\xce\xe8\x5b\xc9\x48\x15\xa1\x93\x86\x4a\x22\x5a\xb5\x42\x68\xb3\xcc\x3a\xd5\xe2\xbb\xc1\x0f\x8c\xb7\xd6\xe6\xf7\xea\xa6\x19\x42\xf3\x7d\x04\xfb\xd3\x03\x35\x11\x2d\x3a\x62\xf1\x3d\x40\xa4\x66\xdf\x0a\x6e\xc8\x3b\x64\xad\xc7\x41\xf3\x71\x0a\x0d\xc3\xff\xe1\x86\xf6\x2b\xe2\xf4\xf1\x73\x2c\xc8\xd1\xfa\xcf\xf6\x2b\x00\x00\xff\xff\xbc\xa7\x78\x3e\xe7\x00\x00\x00") - -func vpcAdmissionWebhookYamlBytes() ([]byte, error) { - return bindataRead( - _vpcAdmissionWebhookYaml, - "vpc-admission-webhook.yaml", - ) -} - -func vpcAdmissionWebhookYaml() (*asset, error) { - bytes, err := vpcAdmissionWebhookYamlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "vpc-admission-webhook.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x14, 0x3e, 0x68, 0x1c, 0x57, 0x26, 0x7e, 0x3b, 0xab, 0xf1, 0x55, 0x21, 0x61, 0xc3, 0xe1, 0xaf, 0x46, 0xb6, 0xf7, 0xdd, 0x11, 0x29, 0x41, 0x64, 0x57, 0xc8, 0xd4, 0xea, 0x97, 0xba, 0xff}} - return a, nil -} - -var _vpcResourceControllerDepYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x94\x4f\x6f\xdb\x3c\x0c\xc6\xef\xfe\x14\xbc\x14\x3d\xd9\x49\xfa\xbe\x6b\x31\x01\x3b\x14\x6b\xb1\x15\x18\x86\x00\x0d\x76\x67\x24\x26\x16\x2c\x4b\x1a\x45\x3b\xf1\x3e\xfd\xa0\xb5\xf9\xe3\xa6\x5b\x8b\x61\xc3\x7c\x32\x9e\x9f\xc4\x87\x24\x44\x96\x65\x59\x60\xb4\x5f\x88\x93\x0d\x5e\x01\xc6\x98\x26\xfd\xac\x68\xac\x37\x0a\x6e\x28\xba\x30\xb4\xe4\xa5\x68\x49\xd0\xa0\xa0\x2a\x00\x3c\xb6\xa4\xa0\x8f\xba\x64\x4a\xa1\x63\x4d\xa5\x0e\x5e\x38\x38\x47\xfc\xc8\x53\x44\x4d\x0a\x9a\x6e\x49\x65\x1a\x92\x50\x5b\xa4\x48\x3a\x5f\x67\x8a\xce\x6a\x4c\x0a\x66\x05\x40\x22\x47\x5a\x02\x67\x02\xd0\xa2\xe8\xfa\x13\x2e\xc9\xa5\x07\x01\x72\x4a\xbf\x32\xcb\x9f\x58\x62\x05\x4b\xd4\x0d\x79\xb3\xd3\x18\x75\xa3\x20\x09\x2e\x1d\x15\x00\x42\x6d\x74\x28\xf4\xe8\x73\x54\x4e\xfe\xdc\xc8\xf2\x55\xa6\xcf\xdb\x9e\x1a\x03\xec\x0a\xff\xf1\x4f\xdc\x5b\x4d\xd7\x5a\x87\xce\xcb\x4b\x1e\x59\x40\xeb\x89\xf7\xa9\x95\xa0\x43\xdb\xa2\x37\x87\x5c\x4b\x98\xbc\x94\x29\xf2\x3a\x1d\x5f\x28\x93\x18\x62\x96\x9a\x29\xd5\xc1\x99\x77\xd6\xaf\xc2\x9e\xdb\x16\xd7\xa4\xe0\xfc\x2c\x55\xa6\xe1\x8a\x34\x57\x67\xa9\x3a\x4b\x13\x6a\xd2\x64\x63\xbd\x09\x9b\x54\xfe\xc4\x52\xf5\xd3\xea\xa2\xba\x3c\x1f\x07\x9b\x77\xce\xcd\x83\xb3\x7a\x50\x70\xed\x36\x38\xa4\x3d\x77\xb6\x27\x4f\x29\xcd\x39\x2c\xe9\x90\x23\xc0\x0a\xad\xeb\x98\x16\xbb\x1c\x15\xbc\x39\xa2\xb5\x48\xfc\x40\x72\x7c\x01\xa0\x0e\x49\x14\xcc\x2e\xae\xaa\x69\x35\xad\x66\x23\x16\x51\x6a\x05\x93\x9a\xd0\x49\xfd\x6d\x8c\x02\x8b\x82\xcb\xd9\xd5\xd5\xdb\x91\x9e\x74\x4d\xf9\xa5\x7f\x5c\x2c\xe6\x47\xc0\x7a\x2b\x16\xdd\x0d\x39\x1c\xee\x49\x07\x6f\x92\x82\xff\xa6\x47\x27\x22\xb1\x0d\xe6\x79\x26\xb6\xa5\xd0\xc9\x1e\x1e\x8a\x7a\x69\xac\x76\x0f\x48\x77\x6c\x65\x78\x1f\xbc\xd0\x76\xd4\x80\xc8\xb6\xb7\x8e\xd6\x64\x14\x08\x77\x54\x1c\xba\xf2\x99\x64\x13\xb8\x19\xe9\xb8\x5a\xe5\x52\x86\x43\x08\x1f\x0c\x5d\x9f\xa8\x79\x62\xbf\x76\x96\xc9\xdc\x74\x6c\xfd\xfa\x5e\xd7\x64\x3a\x67\xfd\xfa\x6e\xed\xc3\x5e\xbe\xdd\x92\xee\x24\x6f\x91\x51\x13\x73\xcc\xfb\xc7\x19\x5f\x10\xb7\x69\x8c\xcb\x87\x91\xbf\xdd\x46\xa6\x94\x77\xd0\x13\x9e\x4f\x34\x34\x28\x58\x92\x60\x95\xd7\x09\x7b\x12\x4a\x95\x0d\x93\x90\x9e\x1c\x05\x08\x91\x18\xf3\x36\x81\x3b\x7f\x02\x7b\x74\x1d\x9d\xc4\xcf\x0e\xce\xfa\x6e\xfb\x6a\x5f\x64\x5d\xff\x29\x67\x6c\xcd\xe5\xff\xbf\xd7\x91\x7f\xd0\x8c\xbf\xdd\x87\xef\x01\x00\x00\xff\xff\xec\x16\x91\xef\x8f\x06\x00\x00") - -func vpcResourceControllerDepYamlBytes() ([]byte, error) { - return bindataRead( - _vpcResourceControllerDepYaml, - "vpc-resource-controller-dep.yaml", - ) -} - -func vpcResourceControllerDepYaml() (*asset, error) { - bytes, err := vpcResourceControllerDepYamlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "vpc-resource-controller-dep.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x0, 0xbf, 0x11, 0xf3, 0x5f, 0x76, 0xa3, 0xb2, 0x2e, 0x2f, 0x80, 0xa7, 0x86, 0xae, 0x3f, 0xb7, 0x7e, 0x76, 0xed, 0x68, 0xfe, 0x23, 0x5c, 0x76, 0xb9, 0xd0, 0xd8, 0xed, 0xf9, 0x9d, 0x96, 0x93}} - return a, nil -} - -var _vpcResourceControllerYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x90\x31\x4f\x33\x31\x0c\x86\xf7\xfc\x8a\xa8\x7b\x5a\x7d\xdb\xa7\xdb\x80\x81\xbd\x48\xec\x3e\x9f\xdb\x9a\xe6\xe2\xc8\x76\x0e\xc1\xaf\x47\x77\xd7\xc2\x80\x44\x85\x98\xf2\xc4\xf6\x6b\x4b\x4f\x4a\x29\x40\xe5\x67\x52\x63\x29\x5d\xd4\x1e\x70\x0b\xcd\x4f\xa2\xfc\x0e\xce\x52\xb6\xe7\xff\xb6\x65\xd9\x4d\xff\xc2\x99\xcb\xd0\xc5\x87\xdc\xcc\x49\xf7\x92\x29\x8c\xe4\x30\x80\x43\x17\x62\x2c\x30\x52\x17\xa7\x8a\x49\xc9\xa4\x29\x52\x42\x29\xae\x92\x33\x69\xd0\x96\xc9\xba\x90\x22\x54\x7e\x54\x69\xd5\xe6\x4c\x8a\x9b\x4d\x88\xf1\x1a\xb8\xd4\x8a\x0c\x64\x5f\xb4\x33\x07\x6f\x6b\xa1\xca\xb0\x02\x4a\x39\xf0\x71\x84\x3a\x7f\x27\xd2\xfe\x92\x6d\x75\x00\xa7\x05\x8f\xe4\xcb\x9b\xd9\x56\x78\x05\xc7\xd3\xba\xe6\x93\x50\x69\x9e\xff\x9b\x87\x7b\x2e\x03\x97\xe3\x6f\x74\x48\xa6\x3d\x1d\xe6\xc1\xab\x90\x1f\x8e\x86\x18\xbf\xbb\xbf\x75\xc2\x5a\xff\x42\xe8\x8b\xf4\x35\xfd\x44\x3a\x31\xd2\x1d\xa2\xb4\xe2\x37\x17\xac\x7d\xab\x80\xd4\xc5\x73\xeb\x29\xd9\x9b\x39\x8d\xe1\x23\x00\x00\xff\xff\x83\x2e\x8d\x64\x35\x02\x00\x00") - -func vpcResourceControllerYamlBytes() ([]byte, error) { - return bindataRead( - _vpcResourceControllerYaml, - "vpc-resource-controller.yaml", - ) -} - -func vpcResourceControllerYaml() (*asset, error) { - bytes, err := vpcResourceControllerYamlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "vpc-resource-controller.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xff, 0xd2, 0xc, 0x46, 0xfb, 0xe4, 0x4e, 0xb, 0x26, 0x4a, 0xa0, 0x7d, 0x73, 0x5e, 0xaf, 0x6d, 0xab, 0xfd, 0xe5, 0xc2, 0x4f, 0xed, 0xae, 0xb6, 0xd7, 0x17, 0x38, 0x68, 0xb0, 0xe8, 0x2d}} + info := bindataFileInfo{name: "vpc-controller-metadata.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x89, 0x6b, 0xa9, 0x33, 0x90, 0x94, 0x63, 0xb2, 0xaf, 0x3a, 0x5c, 0x22, 0x5b, 0x19, 0xc7, 0xb9, 0x22, 0xd6, 0x5a, 0xbb, 0x68, 0xac, 0xf8, 0x24, 0x68, 0x4d, 0x2c, 0x54, 0x12, 0x18, 0x57}} return a, nil } @@ -348,15 +243,10 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "efa-device-plugin.yaml": efaDevicePluginYaml, - "neuron-device-plugin.yaml": neuronDevicePluginYaml, - "nvidia-device-plugin.yaml": nvidiaDevicePluginYaml, - "vpc-admission-webhook-config.yaml": vpcAdmissionWebhookConfigYaml, - "vpc-admission-webhook-csr.yaml": vpcAdmissionWebhookCsrYaml, - "vpc-admission-webhook-dep.yaml": vpcAdmissionWebhookDepYaml, - "vpc-admission-webhook.yaml": vpcAdmissionWebhookYaml, - "vpc-resource-controller-dep.yaml": vpcResourceControllerDepYaml, - "vpc-resource-controller.yaml": vpcResourceControllerYaml, + "efa-device-plugin.yaml": efaDevicePluginYaml, + "neuron-device-plugin.yaml": neuronDevicePluginYaml, + "nvidia-device-plugin.yaml": nvidiaDevicePluginYaml, + "vpc-controller-metadata.yaml": vpcControllerMetadataYaml, } // AssetDebug is true if the assets were built with the debug flag enabled. @@ -406,12 +296,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "efa-device-plugin.yaml": {efaDevicePluginYaml, map[string]*bintree{}}, "neuron-device-plugin.yaml": {neuronDevicePluginYaml, map[string]*bintree{}}, "nvidia-device-plugin.yaml": {nvidiaDevicePluginYaml, map[string]*bintree{}}, - "vpc-admission-webhook-config.yaml": {vpcAdmissionWebhookConfigYaml, map[string]*bintree{}}, - "vpc-admission-webhook-csr.yaml": {vpcAdmissionWebhookCsrYaml, map[string]*bintree{}}, - "vpc-admission-webhook-dep.yaml": {vpcAdmissionWebhookDepYaml, map[string]*bintree{}}, - "vpc-admission-webhook.yaml": {vpcAdmissionWebhookYaml, map[string]*bintree{}}, - "vpc-resource-controller-dep.yaml": {vpcResourceControllerDepYaml, map[string]*bintree{}}, - "vpc-resource-controller.yaml": {vpcResourceControllerYaml, map[string]*bintree{}}, + "vpc-controller-metadata.yaml": {vpcControllerMetadataYaml, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/pkg/addons/assets/vpc-admission-webhook-config.yaml b/pkg/addons/assets/vpc-admission-webhook-config.yaml deleted file mode 100644 index 652a257203..0000000000 --- a/pkg/addons/assets/vpc-admission-webhook-config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: admissionregistration.k8s.io/v1beta1 -kind: MutatingWebhookConfiguration -metadata: - name: vpc-admission-webhook-cfg - namespace: kube-system - labels: - app: vpc-admission-webhook -webhooks: - - name: vpc-admission-webhook.amazonaws.com - clientConfig: - service: - name: vpc-admission-webhook - namespace: kube-system - path: "/mutate" - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - failurePolicy: Ignore diff --git a/pkg/addons/assets/vpc-admission-webhook-csr.yaml b/pkg/addons/assets/vpc-admission-webhook-csr.yaml deleted file mode 100644 index 09e4f77a6d..0000000000 --- a/pkg/addons/assets/vpc-admission-webhook-csr.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: certificates.k8s.io/v1beta1 -kind: CertificateSigningRequest -metadata: - name: vpc-admission-webhook.kube-system -spec: - groups: - - system:authenticated - usages: - - digital signature - - key encipherment - - server auth diff --git a/pkg/addons/assets/vpc-admission-webhook-dep.yaml b/pkg/addons/assets/vpc-admission-webhook-dep.yaml deleted file mode 100644 index 2c08acab80..0000000000 --- a/pkg/addons/assets/vpc-admission-webhook-dep.yaml +++ /dev/null @@ -1,61 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: vpc-admission-webhook - namespace: kube-system - labels: - app: vpc-admission-webhook -spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: vpc-admission-webhook - template: - metadata: - labels: - app: vpc-admission-webhook - spec: - containers: - - name: vpc-admission-webhook - args: - - -tlsCertFile=/etc/webhook/certs/cert.pem - - -tlsKeyFile=/etc/webhook/certs/key.pem - - -OSLabelSelectorOverride=windows - - -alsologtostderr - - -v=4 - - 2>&1 - image: '%s.dkr.ecr.%s.%s/eks/vpc-admission-webhook:v0.2.6' - imagePullPolicy: Always - volumeMounts: - - name: webhook-certs - mountPath: /etc/webhook/certs - readOnly: true - hostNetwork: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: beta.kubernetes.io/os - operator: In - values: - - linux - - key: beta.kubernetes.io/arch - operator: In - values: - - amd64 - - matchExpressions: - - key: kubernetes.io/os - operator: In - values: - - linux - - key: kubernetes.io/arch - operator: In - values: - - amd64 - volumes: - - name: webhook-certs - secret: - secretName: vpc-admission-webhook-certs diff --git a/pkg/addons/assets/vpc-admission-webhook.yaml b/pkg/addons/assets/vpc-admission-webhook.yaml deleted file mode 100644 index b8f351fb12..0000000000 --- a/pkg/addons/assets/vpc-admission-webhook.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: vpc-admission-webhook - namespace: kube-system - labels: - app: vpc-admission-webhook -spec: - ports: - - port: 443 - targetPort: 443 - selector: - app: vpc-admission-webhook diff --git a/pkg/addons/assets/vpc-controller-metadata.yaml b/pkg/addons/assets/vpc-controller-metadata.yaml new file mode 100644 index 0000000000..93bfd78257 --- /dev/null +++ b/pkg/addons/assets/vpc-controller-metadata.yaml @@ -0,0 +1,52 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: vpc-admission-webhook + namespace: kube-system + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: vpc-admission-webhook-cfg + namespace: kube-system + +--- +apiVersion: certificates.k8s.io/v1beta1 +kind: CertificateSigningRequest +metadata: + name: vpc-admission-webhook.kube-system + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vpc-admission-webhook + namespace: kube-system + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: vpc-resource-controller + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: vpc-resource-controller + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vpc-resource-controller + namespace: kube-system + +--- +apiVersion: v1 +kind: Secret +metadata: + name: vpc-admission-webhook-certs + namespace: kube-system diff --git a/pkg/addons/assets/vpc-resource-controller-dep.yaml b/pkg/addons/assets/vpc-resource-controller-dep.yaml deleted file mode 100644 index 3966657e1b..0000000000 --- a/pkg/addons/assets/vpc-resource-controller-dep.yaml +++ /dev/null @@ -1,64 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: vpc-resource-controller - namespace: kube-system -spec: - replicas: 1 - selector: - matchLabels: - app: vpc-resource-controller - tier: backend - track: stable - template: - metadata: - labels: - app: vpc-resource-controller - tier: backend - track: stable - spec: - serviceAccount: vpc-resource-controller - containers: - - command: - - /vpc-resource-controller - args: - - -stderrthreshold=info - image: '%s.dkr.ecr.%s.%s/eks/windows-vpc-resource-controller:v0.2.6' - imagePullPolicy: Always - livenessProbe: - failureThreshold: 5 - httpGet: - host: 127.0.0.1 - path: /healthz - port: 61779 - scheme: HTTP - initialDelaySeconds: 30 - periodSeconds: 30 - timeoutSeconds: 5 - name: vpc-resource-controller - securityContext: - privileged: true - hostNetwork: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: beta.kubernetes.io/os - operator: In - values: - - linux - - key: beta.kubernetes.io/arch - operator: In - values: - - amd64 - - matchExpressions: - - key: kubernetes.io/os - operator: In - values: - - linux - - key: kubernetes.io/arch - operator: In - values: - - amd64 diff --git a/pkg/addons/assets/vpc-resource-controller.yaml b/pkg/addons/assets/vpc-resource-controller.yaml deleted file mode 100644 index 91b1c9f085..0000000000 --- a/pkg/addons/assets/vpc-resource-controller.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: vpc-resource-controller -rules: -- apiGroups: - - "" - resources: - - nodes - - nodes/status - - pods - - configmaps - verbs: - - update - - get - - list - - watch - - patch - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: vpc-resource-controller -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: vpc-resource-controller -subjects: -- kind: ServiceAccount - name: vpc-resource-controller - namespace: kube-system diff --git a/pkg/addons/device_plugin.go b/pkg/addons/device_plugin.go index 694668ad8b..70683fe99d 100644 --- a/pkg/addons/device_plugin.go +++ b/pkg/addons/device_plugin.go @@ -71,6 +71,15 @@ type DevicePlugin interface { Deploy() error } +type typeAssertionError struct { + expected interface{} + got interface{} +} + +func (t *typeAssertionError) Error() string { + return fmt.Sprintf("expected type to be %T; got %T", t.expected, t.got) +} + func applyDevicePlugin(dp DevicePlugin) error { list, err := kubernetes.NewList(dp.Manifest()) if err != nil { diff --git a/pkg/addons/vpc_controller.go b/pkg/addons/vpc_controller.go index 4af6c3ab57..e6f9e059a3 100644 --- a/pkg/addons/vpc_controller.go +++ b/pkg/addons/vpc_controller.go @@ -1,410 +1,56 @@ package addons import ( - "context" - "fmt" - "time" - - "github.com/cloudflare/cfssl/csr" "github.com/kris-nova/logger" "github.com/pkg/errors" - api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5" - "github.com/weaveworks/eksctl/pkg/assetutil" "github.com/weaveworks/eksctl/pkg/kubernetes" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" - admv1beta1 "k8s.io/api/admissionregistration/v1beta1" - appsv1 "k8s.io/api/apps/v1" - certsv1beta1 "k8s.io/api/certificates/v1beta1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) const ( - vpcControllerNamespace = metav1.NamespaceSystem - vpcControllerName = "vpc-resource-controller" - webhookServiceName = "vpc-admission-webhook" - - certWaitTimeout = 45 * time.Second + VPCControllerInfoMessage = "you no longer need to install the VPC resource controller on Linux worker nodes to run " + + "Windows workloads in EKS clusters. You can enable Windows IP address management on the EKS control plane via " + + "a ConfigMap setting (see https://todo.com for details). eksctl will automatically patch the ConfigMap to enable " + + "Windows IP address management when a Windows nodegroup is created. For existing clusters, you can enable it manually " + + "and run `eksctl utils install-vpc-controllers` with the --delete flag to remove the worker node installation of the VPC resource controller" ) -// NewVPCController creates a new VPCController -func NewVPCController(rawClient kubernetes.RawClientInterface, irsa IRSAHelper, clusterStatus *api.ClusterStatus, region string, planMode bool) *VPCController { - return &VPCController{ - rawClient: rawClient, - irsa: irsa, - clusterStatus: clusterStatus, - region: region, - planMode: planMode, - } -} - -// A VPCController deploys Windows VPC controller to a cluster +// VPCController deletes an existing installation of VPC controller from worker nodes. type VPCController struct { - rawClient kubernetes.RawClientInterface - irsa IRSAHelper - clusterStatus *api.ClusterStatus - region string - planMode bool -} - -// Deploy deploys VPC controller to the specified cluster -func (v *VPCController) Deploy() (err error) { - defer func() { - if r := recover(); r != nil { - if ae, ok := r.(*assetutil.Error); ok { - err = ae - } else { - panic(r) - } - } - }() - - if err := v.deployVPCResourceController(); err != nil { - return err - } - - if err := v.generateCert(); err != nil { - return err - } - - return v.deployVPCWebhook() -} - -type typeAssertionError struct { - expected interface{} - got interface{} + RawClient *kubernetes.RawClient + PlanMode bool } -func (t *typeAssertionError) Error() string { - return fmt.Sprintf("expected type to be %T; got %T", t.expected, t.got) -} - -func (v *VPCController) generateCert() error { - var ( - csrName = fmt.Sprintf("%s.%s", webhookServiceName, vpcControllerNamespace) - csrClientSet = v.rawClient.ClientSet().CertificatesV1beta1().CertificateSigningRequests() - ) - - hasApprovedCert, err := v.hasApprovedCert() - if err != nil { - return err - } - if hasApprovedCert { - // Delete existing CSR if the secret is missing - _, err := v.rawClient.ClientSet().CoreV1().Secrets(vpcControllerNamespace).Get(context.TODO(), "vpc-admission-webhook-certs", metav1.GetOptions{}) - if err != nil { - if !apierrors.IsNotFound(err) { - return err - } - if err := csrClientSet.Delete(context.TODO(), csrName, metav1.DeleteOptions{}); err != nil { - return err - } - } - return nil - } - - csrPEM, privateKey, err := generateCertReq(webhookServiceName, vpcControllerNamespace) +// Delete deletes the resources for VPC controller. +func (v *VPCController) Delete() error { + vpcControllerMetadata, err := vpcControllerMetadataYamlBytes() if err != nil { - return errors.Wrap(err, "generating CSR") + return errors.Wrap(err, "unexpected error loading manifests") } - - manifest := assetutil.MustLoad(vpcAdmissionWebhookCsrYamlBytes) - rawExtension, err := kubernetes.NewRawExtension(manifest) + list, err := kubernetes.NewList(vpcControllerMetadata) if err != nil { - return err - } - - certificateSigningRequest, ok := rawExtension.Object.(*certsv1beta1.CertificateSigningRequest) - if !ok { - return &typeAssertionError{&certsv1beta1.CertificateSigningRequest{}, rawExtension.Object} - } - - certificateSigningRequest.Spec.Request = csrPEM - certificateSigningRequest.Name = csrName - - if err := v.applyRawResource(certificateSigningRequest); err != nil { - return errors.Wrap(err, "creating CertificateSigningRequest") + return errors.Wrap(err, "unexpected error parsing manifests") } - - certificateSigningRequest.Status.Conditions = []certsv1beta1.CertificateSigningRequestCondition{ - { - Type: certsv1beta1.CertificateApproved, - LastUpdateTime: metav1.NewTime(time.Now()), - Message: "This CSR was approved by eksctl", - Reason: "eksctl-approve", - }, - } - - if _, err := csrClientSet.UpdateApproval(context.TODO(), certificateSigningRequest, metav1.UpdateOptions{}); err != nil { - return errors.Wrap(err, "updating approval") - } - - logger.Info("waiting for certificate to be available") - - cert, err := watchCSRApproval(csrClientSet, csrName, certWaitTimeout) - if err != nil { - return err - } - - return v.createCertSecrets(privateKey, cert) -} - -func watchCSRApproval(csrClientSet v1beta1.CertificateSigningRequestInterface, csrName string, timeout time.Duration) ([]byte, error) { - watcher, err := csrClientSet.Watch(context.TODO(), metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", csrName), - }) - - if err != nil { - return nil, err - } - - defer watcher.Stop() - - timer := time.NewTimer(timeout) - defer timer.Stop() - - for { - select { - case event, ok := <-watcher.ResultChan(): - if !ok { - return nil, errors.New("failed waiting for certificate: unexpected close of ResultChan") - } - switch event.Type { - case watch.Added, watch.Modified: - req := event.Object.(*certsv1beta1.CertificateSigningRequest) - if cert := req.Status.Certificate; cert != nil { - return cert, nil - } - logger.Warning("certificate not yet available (event: %s)", event.Type) - } - case <-timer.C: - return nil, fmt.Errorf("timed out (after %v) waiting for certificate", timeout) - } - - } -} - -func (v *VPCController) createCertSecrets(key, cert []byte) error { - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "vpc-admission-webhook-certs", - Namespace: vpcControllerNamespace, - }, - Data: map[string][]byte{ - "key.pem": key, - "cert.pem": cert, - }, - } - - err := v.applyRawResource(secret) - if err != nil { - return errors.Wrap(err, "error creating secret") - } - return err -} - -func makePolicyDocument() map[string]interface{} { - return map[string]interface{}{ - "Version": "2012-10-17", - "Statement": []map[string]interface{}{ - { - "Effect": "Allow", - "Action": []string{ - "ec2:AssignPrivateIpAddresses", - "ec2:DescribeInstances", - "ec2:DescribeNetworkInterfaces", - "ec2:UnassignPrivateIpAddresses", - "ec2:DescribeRouteTables", - "ec2:DescribeSubnets", - }, - "Resource": "*", - }, - }, - } -} - -func (v *VPCController) deployVPCResourceController() error { - irsaEnabled, err := v.irsa.IsSupported() - if err != nil { - return err - } - if irsaEnabled { - sa := &api.ClusterIAMServiceAccount{ - ClusterIAMMeta: api.ClusterIAMMeta{ - Name: vpcControllerName, - Namespace: vpcControllerNamespace, - }, - AttachPolicy: makePolicyDocument(), - } - if err := v.irsa.CreateOrUpdate(sa); err != nil { - return errors.Wrap(err, "error enabling IRSA") - } - } else { - // If an OIDC provider isn't associated with the cluster, the VPC controller relies on the managed policy - // attached to the node role for the AWS VPC CNI plugin. - sa := kubernetes.NewServiceAccount(metav1.ObjectMeta{ - Name: vpcControllerName, - Namespace: vpcControllerNamespace, - }) - if err := v.applyRawResource(sa); err != nil { - return err - } - } - if err := v.applyResources(assetutil.MustLoad(vpcResourceControllerYamlBytes)); err != nil { - return err - } - - return v.applyDeployment(assetutil.MustLoad(vpcResourceControllerDepYamlBytes)) -} - -func (v *VPCController) deployVPCWebhook() error { - if err := v.applyResources(assetutil.MustLoad(vpcAdmissionWebhookYamlBytes)); err != nil { - return err - } - if err := v.applyDeployment(assetutil.MustLoad(vpcAdmissionWebhookDepYamlBytes)); err != nil { - return err - } - - manifest := assetutil.MustLoad(vpcAdmissionWebhookConfigYamlBytes) - rawExtension, err := kubernetes.NewRawExtension(manifest) - if err != nil { - return err - } - - mutatingWebhook, ok := rawExtension.Object.(*admv1beta1.MutatingWebhookConfiguration) - if !ok { - return &typeAssertionError{&admv1beta1.MutatingWebhookConfiguration{}, rawExtension.Object} - } - - mutatingWebhook.Webhooks[0].ClientConfig.CABundle = v.clusterStatus.CertificateAuthorityData - return v.applyRawResource(rawExtension.Object) -} - -func (v *VPCController) hasApprovedCert() (bool, error) { - csrClientSet := v.rawClient.ClientSet().CertificatesV1beta1().CertificateSigningRequests() - request, err := csrClientSet.Get(context.TODO(), fmt.Sprintf("%s.%s", webhookServiceName, vpcControllerNamespace), metav1.GetOptions{}) - if err != nil { - if !apierrors.IsNotFound(err) { - return false, err - } - return false, nil - } - - conditions := request.Status.Conditions - switch len(conditions) { - case 1: - if conditions[0].Type == certsv1beta1.CertificateApproved { - return true, nil - } - return false, fmt.Errorf("expected certificate to be approved; got %q", conditions[0].Type) - - case 0: - return false, nil - default: - return false, fmt.Errorf("unexpected number of request conditions: %d", len(conditions)) - } -} - -func (v *VPCController) applyResources(manifests []byte) error { - list, err := kubernetes.NewList(manifests) - if err != nil { - return err - } - for _, item := range list.Items { - if err := v.applyRawResource(item.Object); err != nil { + if err := v.deleteResource(item.Object); err != nil { return err } } return nil } -func (v *VPCController) applyDeployment(manifests []byte) error { - rawExtension, err := kubernetes.NewRawExtension(manifests) +func (v *VPCController) deleteResource(o runtime.Object) error { + r, err := v.RawClient.NewRawResource(o) if err != nil { - return err - } - - deployment, ok := rawExtension.Object.(*appsv1.Deployment) - if !ok { - return &typeAssertionError{&appsv1.Deployment{}, rawExtension.Object} - } - if err := UseRegionalImage(&deployment.Spec.Template, v.region); err != nil { - return err + return errors.Wrap(err, "unexpected error creating raw resource") } - return v.applyRawResource(rawExtension.Object) -} - -func (v *VPCController) applyRawResource(object runtime.Object) error { - rawResource, err := v.rawClient.NewRawResource(object) + msg, err := r.DeleteSync(v.PlanMode) if err != nil { - return err - } - - switch newObject := object.(type) { - case *corev1.Service: - r, found, err := rawResource.Get() - if err != nil { - return err - } - if found { - service, ok := r.(*corev1.Service) - if !ok { - return &typeAssertionError{&corev1.Service{}, r} - } - newObject.Spec.ClusterIP = service.Spec.ClusterIP - newObject.SetResourceVersion(service.GetResourceVersion()) - } - case *admv1beta1.MutatingWebhookConfiguration: - r, found, err := rawResource.Get() - if err != nil { - return err - } - if found { - mwc, ok := r.(*admv1beta1.MutatingWebhookConfiguration) - if !ok { - return &typeAssertionError{&admv1beta1.MutatingWebhookConfiguration{}, r} - } - newObject.SetResourceVersion(mwc.GetResourceVersion()) - } + return errors.Wrapf(err, "error deleting resource %q", r.Info.String()) } - - msg, err := rawResource.CreateOrReplace(v.planMode) - if err != nil { - return err + if msg != "" { + logger.Info(msg) } - logger.Info(msg) return nil } - -func generateCertReq(service, namespace string) ([]byte, []byte, error) { - generator := csr.Generator{ - Validator: func(request *csr.CertificateRequest) error { - // ignore validation as all required fields are being set - return nil - }, - } - - serviceCN := fmt.Sprintf("%s.%s.svc", service, namespace) - - return generator.ProcessRequest(&csr.CertificateRequest{ - KeyRequest: &csr.KeyRequest{ - A: "rsa", - S: 2048, - }, - CN: serviceCN, - Hosts: []string{ - service, - fmt.Sprintf("%s.%s", service, namespace), - serviceCN, - }, - }) -} diff --git a/pkg/apis/eksctl.io/v1alpha5/nodegroups.go b/pkg/apis/eksctl.io/v1alpha5/nodegroups.go index 00382c7ca2..548c6d5928 100644 --- a/pkg/apis/eksctl.io/v1alpha5/nodegroups.go +++ b/pkg/apis/eksctl.io/v1alpha5/nodegroups.go @@ -78,3 +78,13 @@ func (c *ClusterConfig) AllNodeGroups() []*NodeGroupBase { } return baseNodeGroups } + +// HasWindowsNodeGroup returns true if an unmanaged Windows nodegroup exists. +func (c *ClusterConfig) HasWindowsNodeGroup() bool { + for _, ng := range c.NodeGroups { + if IsWindowsImage(ng.AMIFamily) { + return true + } + } + return false +} diff --git a/pkg/ctl/create/cluster.go b/pkg/ctl/create/cluster.go index f3d96d66f1..45fbb32e77 100644 --- a/pkg/ctl/create/cluster.go +++ b/pkg/ctl/create/cluster.go @@ -6,6 +6,8 @@ import ( "os" "strings" + "github.com/weaveworks/eksctl/pkg/addons" + "github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector" "github.com/weaveworks/eksctl/pkg/kops" "github.com/weaveworks/eksctl/pkg/utils" @@ -71,6 +73,8 @@ func createClusterCmdWithRunFunc(cmd *cmdutils.Cmd, runFunc func(cmd *cmdutils.C fs.BoolVarP(¶ms.InstallWindowsVPCController, "install-vpc-controllers", "", false, "Install VPC controller that's required for Windows workloads") fs.BoolVarP(¶ms.Fargate, "fargate", "", false, "Create a Fargate profile scheduling pods in the default and kube-system namespaces onto Fargate") fs.BoolVarP(¶ms.DryRun, "dry-run", "", false, "Dry-run mode that skips cluster creation and outputs a ClusterConfig") + + _ = fs.MarkDeprecated("install-vpc-controllers", addons.VPCControllerInfoMessage) }) cmd.FlagSetGroup.InFlagSet("Initial nodegroup", func(fs *pflag.FlagSet) { @@ -190,6 +194,7 @@ func doCreateCluster(cmd *cmdutils.Cmd, ngFilter *filter.NodeGroupFilter, params if !eks.SupportsWindowsWorkloads(kubeNodeGroups) { return errors.New("running Windows workloads requires having both Windows and Linux (AmazonLinux2) node groups") } + logger.Warning(addons.VPCControllerInfoMessage) } else { eks.LogWindowsCompatibility(kubeNodeGroups, cfg.Metadata) } @@ -248,7 +253,7 @@ func doCreateCluster(cmd *cmdutils.Cmd, ngFilter *filter.NodeGroupFilter, params if err != nil { return err } - postClusterCreationTasks := ctl.CreateExtraClusterConfigTasks(cfg, params.InstallWindowsVPCController) + postClusterCreationTasks := ctl.CreateExtraClusterConfigTasks(cfg) supported, err := utils.IsMinVersion(api.Version1_18, cfg.Metadata.Version) if err != nil { diff --git a/pkg/ctl/utils/install_vpc_controllers.go b/pkg/ctl/utils/install_vpc_controllers.go index 79e3893844..1306f64e5e 100644 --- a/pkg/ctl/utils/install_vpc_controllers.go +++ b/pkg/ctl/utils/install_vpc_controllers.go @@ -4,6 +4,7 @@ import ( "github.com/kris-nova/logger" "github.com/spf13/cobra" "github.com/spf13/pflag" + "github.com/weaveworks/eksctl/pkg/addons" "github.com/weaveworks/eksctl/pkg/eks" "github.com/weaveworks/eksctl/pkg/utils/tasks" @@ -16,13 +17,17 @@ func installWindowsVPCController(cmd *cmdutils.Cmd) { cmd.ClusterConfig = cfg cmd.SetDescription("install-vpc-controllers", "Install Windows VPC controller to support running Windows workloads", "") + cmd.CobraCommand.Deprecated = addons.VPCControllerInfoMessage + + var deleteController bool cmd.CobraCommand.RunE = func(_ *cobra.Command, args []string) error { cmd.NameArg = cmdutils.GetNameArg(args) - return doInstallWindowsVPCController(cmd) + return doInstallWindowsVPCController(cmd, deleteController) } cmd.FlagSetGroup.InFlagSet("General", func(fs *pflag.FlagSet) { + fs.BoolVar(&deleteController, "delete", false, "Deletes VPC resource controller from worker nodes") cmdutils.AddClusterFlagWithDeprecated(fs, cfg.Metadata) cmdutils.AddRegionFlag(fs, &cmd.ProviderConfig) cmdutils.AddConfigFileFlag(fs, &cmd.ClusterConfigFile) @@ -33,33 +38,33 @@ func installWindowsVPCController(cmd *cmdutils.Cmd) { cmdutils.AddCommonFlagsForAWS(cmd.FlagSetGroup, &cmd.ProviderConfig, false) } -func doInstallWindowsVPCController(cmd *cmdutils.Cmd) error { +func doInstallWindowsVPCController(cmd *cmdutils.Cmd, deleteController bool) error { + if !deleteController { + logger.Warning(addons.VPCControllerInfoMessage) + return nil + } + if err := cmdutils.NewMetadataLoader(cmd).Load(); err != nil { return err } - cfg := cmd.ClusterConfig - meta := cmd.ClusterConfig.Metadata - ctl, err := cmd.NewProviderForExistingCluster() if err != nil { return err } - logger.Info("using region %s", meta.Region) - - if ok, err := ctl.CanUpdate(cfg); !ok { + logger.Info("using region %s", cmd.ClusterConfig.Metadata.Region) + rawClient, err := ctl.NewRawClient(cmd.ClusterConfig) + if err != nil { return err } - - vpcControllerTask := &eks.VPCControllerTask{ - Info: "install Windows VPC controller", - ClusterConfig: cfg, - ClusterProvider: ctl, - PlanMode: cmd.Plan, + deleteControllerTask := &eks.DeleteVPCControllerTask{ + Info: "delete Windows VPC controller", + PlanMode: cmd.Plan, + RawClient: rawClient, } taskTree := &tasks.TaskTree{ - Tasks: []tasks.Task{vpcControllerTask}, + Tasks: []tasks.Task{deleteControllerTask}, } if errs := taskTree.DoAllSync(); len(errs) > 0 { diff --git a/pkg/eks/tasks.go b/pkg/eks/tasks.go index b457a8b22a..b749d07170 100644 --- a/pkg/eks/tasks.go +++ b/pkg/eks/tasks.go @@ -6,11 +6,10 @@ import ( "time" "github.com/weaveworks/eksctl/pkg/actions/identityproviders" - "github.com/weaveworks/eksctl/pkg/actions/irsa" + "github.com/weaveworks/eksctl/pkg/windows" "github.com/kris-nova/logger" "github.com/pkg/errors" - "github.com/weaveworks/eksctl/pkg/cfn/manager" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -40,44 +39,52 @@ func (t *clusterConfigTask) Do(errs chan error) error { return err } -// VPCControllerTask represents a task to install the VPC controller -type VPCControllerTask struct { - Info string - ClusterProvider *ClusterProvider - ClusterConfig *api.ClusterConfig - PlanMode bool +// WindowsIPAMTask is a task for enabling Windows IPAM. +type WindowsIPAMTask struct { + Info string + ClientsetFunc func() (kubernetes.Interface, error) } -// Describe implements Task -func (v *VPCControllerTask) Describe() string { return v.Info } - -// Do implements Task -func (v *VPCControllerTask) Do(errCh chan error) error { +// Do implements Task. +func (w *WindowsIPAMTask) Do(errCh chan error) error { defer close(errCh) - rawClient, err := v.ClusterProvider.NewRawClient(v.ClusterConfig) + + clientset, err := w.ClientsetFunc() if err != nil { return err } - oidc, err := v.ClusterProvider.NewOpenIDConnectManager(v.ClusterConfig) - if err != nil { - return err + windowsIPAM := windows.IPAM{ + Clientset: clientset, } + return windowsIPAM.Enable(context.TODO()) +} - stackCollection := manager.NewStackCollection(v.ClusterProvider.Provider, v.ClusterConfig) +// Describe implements Task. +func (w *WindowsIPAMTask) Describe() string { + return w.Info +} - clientSet, err := v.ClusterProvider.NewStdClientSet(v.ClusterConfig) - if err != nil { - return err - } - irsaManager := irsa.New(v.ClusterConfig.Metadata.Name, stackCollection, oidc, clientSet) - irsa := addons.NewIRSAHelper(oidc, stackCollection, irsaManager, v.ClusterConfig.Metadata.Name) +// DeleteVPCControllerTask is a task for deleting VPC controller resources. +type DeleteVPCControllerTask struct { + RawClient *kubernetes.RawClient + PlanMode bool + Info string +} + +// Do implements Task. +func (v *DeleteVPCControllerTask) Do(errCh chan error) error { + defer close(errCh) - // TODO PlanMode doesn't work as intended - vpcController := addons.NewVPCController(rawClient, irsa, v.ClusterConfig.Status, v.ClusterProvider.Provider.Region(), v.PlanMode) - if err := vpcController.Deploy(); err != nil { - return errors.Wrap(err, "error installing VPC controller") + vpcController := &addons.VPCController{ + RawClient: v.RawClient, + PlanMode: v.PlanMode, } - return nil + return vpcController.Delete() +} + +// Describe implements Task. +func (v *DeleteVPCControllerTask) Describe() string { + return v.Info } type devicePluginTask struct { @@ -188,7 +195,7 @@ func (t *restartDaemonsetTask) Do(errCh chan error) error { } // CreateExtraClusterConfigTasks returns all tasks for updating cluster configuration not depending on the control plane availability -func (c *ClusterProvider) CreateExtraClusterConfigTasks(cfg *api.ClusterConfig, installVPCController bool) *tasks.TaskTree { +func (c *ClusterProvider) CreateExtraClusterConfigTasks(cfg *api.ClusterConfig) *tasks.TaskTree { newTasks := &tasks.TaskTree{ Parallel: false, IsSubTask: true, @@ -254,11 +261,12 @@ func (c *ClusterProvider) CreateExtraClusterConfigTasks(cfg *api.ClusterConfig, newTasks.Append(identityproviders.NewAssociateProvidersTask(*cfg.Metadata, cfg.IdentityProviders, c.Provider.EKS())) } - if installVPCController { - newTasks.Append(&VPCControllerTask{ - Info: "install Windows VPC controller", - ClusterConfig: cfg, - ClusterProvider: c, + if cfg.HasWindowsNodeGroup() { + newTasks.Append(&WindowsIPAMTask{ + Info: "enable Windows IP address management", + ClientsetFunc: func() (kubernetes.Interface, error) { + return c.NewStdClientSet(cfg) + }, }) } diff --git a/pkg/kubernetes/client.go b/pkg/kubernetes/client.go index 10b7bf2a3e..cd6494264a 100644 --- a/pkg/kubernetes/client.go +++ b/pkg/kubernetes/client.go @@ -245,7 +245,7 @@ func (c *RawClient) deleteObject(object runtime.RawExtension) error { if err != nil { return err } - status, err := resource.DeleteSync() + status, err := resource.DeleteSync(false) if err != nil { return err } @@ -415,7 +415,7 @@ func (r *RawResource) CreatePatchOrReplace() error { // DeleteSync attempts to delete this Kubernetes resource, or returns doing // nothing if it does not exist. It blocks until the resource has been deleted. -func (r *RawResource) DeleteSync() (string, error) { +func (r *RawResource) DeleteSync(plan bool) (string, error) { _, exists, err := r.Get() if err != nil { return "", err @@ -423,6 +423,9 @@ func (r *RawResource) DeleteSync() (string, error) { if !exists { return "", nil } + if plan { + return r.LogAction(true, "deleted"), nil + } propagationPolicy := metav1.DeletePropagationForeground if _, err := r.Helper.DeleteWithOptions(r.Info.Namespace, r.Info.Name, &metav1.DeleteOptions{ PropagationPolicy: &propagationPolicy, diff --git a/pkg/kubernetes/client_test.go b/pkg/kubernetes/client_test.go index aa037e04ef..5a553a9a77 100644 --- a/pkg/kubernetes/client_test.go +++ b/pkg/kubernetes/client_test.go @@ -243,7 +243,7 @@ var _ = Describe("Kubernetes client wrappers", func() { Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) // The Kubernetes resource already exists. - status, err := rc.DeleteSync() + status, err := rc.DeleteSync(false) Expect(err).ToNot(HaveOccurred()) Expect(status).To(Equal(fmt.Sprintf("deleted %q", rc))) Expect(track).ToNot(BeNil()) diff --git a/pkg/windows/ipam.go b/pkg/windows/ipam.go new file mode 100644 index 0000000000..a2390e0028 --- /dev/null +++ b/pkg/windows/ipam.go @@ -0,0 +1,86 @@ +package windows + +import ( + "context" + "encoding/json" + + jsonpatch "github.com/evanphx/json-patch/v5" + "github.com/kris-nova/logger" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +const ( + vpcCNIName = "amazon-vpc-cni" + vpcCNINamespace = metav1.NamespaceSystem + windowsIPAMField = "enable-windows-ipam" +) + +// IPAM enables Windows IPAM in the VPC CNI ConfigMap. +type IPAM struct { + Clientset kubernetes.Interface +} + +// Enable enables Windows IPAM in the VPC CNI ConfigMap. +func (w *IPAM) Enable(ctx context.Context) error { + configMaps := w.Clientset.CoreV1().ConfigMaps(metav1.NamespaceSystem) + vpcCNIConfig, err := configMaps.Get(ctx, vpcCNIName, metav1.GetOptions{}) + if err != nil { + if !apierrors.IsNotFound(err) { + return errors.Wrapf(err, "error getting ConfigMap %q", vpcCNIName) + } + return createConfigMap(ctx, configMaps) + } + + if val, ok := vpcCNIConfig.Data[windowsIPAMField]; ok && val == "true" { + logger.Info("Windows IPAM is already enabled") + return nil + } + + patch, err := createPatch(vpcCNIConfig) + if err != nil { + return errors.Wrap(err, "error creating merge patch") + } + + _, err = configMaps.Patch(ctx, vpcCNIName, types.StrategicMergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + return errors.Wrapf(err, "failed to patch resource %q", vpcCNIName) + } + return nil +} + +func createPatch(cm *corev1.ConfigMap) ([]byte, error) { + oldData, err := json.Marshal(cm) + if err != nil { + return nil, err + } + cm.Data[windowsIPAMField] = "true" + modifiedData, err := json.Marshal(cm) + if err != nil { + return nil, err + } + return jsonpatch.CreateMergePatch(oldData, modifiedData) +} + +func createConfigMap(ctx context.Context, configMaps corev1client.ConfigMapInterface) error { + cm := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: vpcCNIName, + Namespace: vpcCNINamespace, + }, + Data: map[string]string{ + windowsIPAMField: "true", + }, + } + _, err := configMaps.Create(ctx, cm, metav1.CreateOptions{}) + return err +} diff --git a/pkg/windows/ipam_test.go b/pkg/windows/ipam_test.go new file mode 100644 index 0000000000..97ced2e57c --- /dev/null +++ b/pkg/windows/ipam_test.go @@ -0,0 +1,97 @@ +package windows_test + +import ( + "context" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + + . "github.com/onsi/ginkgo/extensions/table" + "github.com/weaveworks/eksctl/pkg/windows" + "k8s.io/client-go/kubernetes/fake" +) + +type ipamEntry struct { + existingConfigMapData map[string]string + + expectedConfigMapData map[string]string +} + +var _ = DescribeTable("Windows IPAM", func(e ipamEntry) { + var clientset *fake.Clientset + if e.existingConfigMapData != nil { + clientset = fake.NewSimpleClientset(&v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "amazon-vpc-cni", + Namespace: "kube-system", + }, + Data: e.existingConfigMapData, + }) + } else { + clientset = fake.NewSimpleClientset() + } + + ipam := &windows.IPAM{ + Clientset: clientset, + } + ctx := context.Background() + err := ipam.Enable(ctx) + Expect(err).ToNot(HaveOccurred()) + + cm, err := clientset.CoreV1().ConfigMaps("kube-system").Get(ctx, "amazon-vpc-cni", metav1.GetOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(cm.Data).To(Equal(e.expectedConfigMapData)) + +}, + Entry("VPC CNI ConfigMap is missing", ipamEntry{ + expectedConfigMapData: map[string]string{ + "enable-windows-ipam": "true", + }, + }), + + Entry("VPC CNI ConfigMap has data", ipamEntry{ + existingConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "other": "true", + }, + expectedConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "other": "true", + "enable-windows-ipam": "true", + }, + }), + + Entry("VPC CNI ConfigMap has Windows IPAM already enabled", ipamEntry{ + existingConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "enable-windows-ipam": "true", + }, + expectedConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "enable-windows-ipam": "true", + }, + }), + + Entry("VPC CNI ConfigMap has Windows IPAM explicitly disabled", ipamEntry{ + existingConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "enable-windows-ipam": "false", + }, + expectedConfigMapData: map[string]string{ + "VPC_CNI_1": "yes", + "VPC_CNI_2": "no", + "enable-windows-ipam": "true", + }, + }), +) diff --git a/pkg/windows/windows_suite_test.go b/pkg/windows/windows_suite_test.go new file mode 100644 index 0000000000..aa7fa2b4bf --- /dev/null +++ b/pkg/windows/windows_suite_test.go @@ -0,0 +1,11 @@ +package windows_test + +import ( + "testing" + + "github.com/weaveworks/eksctl/pkg/testutils" +) + +func TestWindows(t *testing.T) { + testutils.RegisterAndRun(t) +} diff --git a/userdocs/src/usage/windows-worker-nodes.md b/userdocs/src/usage/windows-worker-nodes.md index 96bd63ba20..3c5998e872 100644 --- a/userdocs/src/usage/windows-worker-nodes.md +++ b/userdocs/src/usage/windows-worker-nodes.md @@ -1,10 +1,15 @@ # Windows Worker Nodes From version 1.14, Amazon EKS supports [Windows Nodes][eks-user-guide] that allow running Windows containers. -In addition to having Windows nodes, a Linux node in the cluster is required to run the VPC resource controller and CoreDNS, as Microsoft doesn't support host-networking mode yet. Thus, a Windows EKS cluster will be a mixed-mode cluster containing Windows nodes and at least one Linux node. +In addition to having Windows nodes, a Linux node in the cluster is required to run CoreDNS, as Microsoft doesn't support host-networking mode yet. Thus, a Windows EKS cluster will be a mixture of Windows nodes and at least one Linux node. The Linux nodes are critical to the functioning of the cluster, and thus, for a production-grade cluster, it's recommended to have at least two `t2.large` Linux nodes for HA. -`eksctl` provides a flag to install the VPC resource controller as part of cluster creation, and a command to install it after a cluster has been created. +!!!note + You no longer need to install the VPC resource controller on Linux worker nodes to run Windows workloads in EKS clusters. + You can enable Windows IP address management on the EKS control plane via a ConfigMap setting (see https://todo.com for details). + eksctl will automatically patch the ConfigMap to enable Windows IP address management when a Windows nodegroup is created. + For existing clusters, you can enable it manually, and run `eksctl utils install-vpc-controllers` with the `--delete` flag + to remove the worker node installation of the VPC resource controller. ## Creating a new Windows cluster @@ -26,6 +31,8 @@ nodeGroups: amiFamily: WindowsServer2019FullContainer minSize: 2 maxSize: 3 + +managedNodeGroups: - name: linux-ng instanceType: t2.large minSize: 2 @@ -33,7 +40,7 @@ nodeGroups: ``` ```console -eksctl create cluster -f cluster.yaml --install-vpc-controllers +eksctl create cluster -f cluster.yaml ``` @@ -42,7 +49,6 @@ To create a new cluster without using a config file, issue the following command ```console eksctl create cluster --managed=false --name=windows-cluster --node-ami-family=WindowsServer2019CoreContainer eksctl create nodegroup --cluster=windows-cluster --node-ami-family=AmazonLinux2 --nodes-min=2 --node-type=t2.large -eksctl utils install-vpc-controllers --cluster=windows-cluster --approve ``` !!!note @@ -50,11 +56,10 @@ eksctl utils install-vpc-controllers --cluster=windows-cluster --approve ## Adding Windows support to an existing Linux cluster -To enable running Windows workloads on an existing cluster with Linux nodes (`AmazonLinux2` AMI family), you need to add a Windows node group and install the Windows VPC controller: +To enable running Windows workloads on an existing cluster with Linux nodes (`AmazonLinux2` AMI family), you need to add a Windows nodegroup. ```console eksctl create nodegroup --managed=false --cluster=existing-cluster --node-ami-family=WindowsServer2019CoreContainer -eksctl utils install-vpc-controllers --cluster=existing-cluster --approve ``` To ensure workloads are scheduled on the right OS, they must have a `nodeSelector` targeting the OS it must run on: