diff --git a/assets/router/deployment.yaml b/assets/router/deployment.yaml index c256716ab5..f913a4b2a5 100644 --- a/assets/router/deployment.yaml +++ b/assets/router/deployment.yaml @@ -22,21 +22,8 @@ spec: # See https://bugzilla.redhat.com/2007246 allowPrivilegeEscalation: true terminationMessagePolicy: FallbackToLogsOnError - ports: - - name: http - containerPort: 80 - protocol: TCP - - name: https - containerPort: 443 - protocol: TCP - - name: metrics - containerPort: 1936 - protocol: TCP # Merged at runtime. env: - # stats username and password are generated at runtime - - name: STATS_PORT - value: "1936" - name: ROUTER_SERVICE_NAMESPACE value: openshift-ingress - name: DEFAULT_CERTIFICATE_DIR diff --git a/pkg/manifests/bindata.go b/pkg/manifests/bindata.go index aa27b0a9dc..a982848def 100644 --- a/pkg/manifests/bindata.go +++ b/pkg/manifests/bindata.go @@ -6,7 +6,7 @@ // assets/canary/service.yaml (331B) // assets/router/cluster-role-binding.yaml (329B) // assets/router/cluster-role.yaml (883B) -// assets/router/deployment.yaml (2.641kB) +// assets/router/deployment.yaml (2.26kB) // assets/router/metrics/cluster-role-binding.yaml (285B) // assets/router/metrics/cluster-role.yaml (259B) // assets/router/metrics/role-binding.yaml (297B) @@ -222,7 +222,7 @@ func assetsRouterClusterRoleYaml() (*asset, error) { return a, nil } -var _assetsRouterDeploymentYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x56\x4f\x73\xe2\xc6\x13\xbd\xfb\x53\x74\xe1\xc3\x9e\x84\xb0\xd7\x3f\xff\xb2\xba\x51\x98\x4d\xa8\x5a\x6c\x95\xd1\xee\x95\x6a\x8f\x1a\x31\xe5\xd1\xcc\xa4\xa7\x07\x87\x4d\xe5\xbb\xa7\x24\x30\x96\x58\xec\x75\x2a\x15\x4e\x42\xdd\xf3\xfa\xf5\x9b\xfe\xa3\x73\xb8\x21\x6f\xdc\xb6\x26\x2b\xf0\xa4\x65\x0d\x25\xad\x30\x1a\x81\x0d\x9a\x48\xe1\xec\x1c\x66\xb6\x62\x0a\x01\x26\xce\x0a\x3b\x63\x88\x21\x78\x52\x7a\xa5\xd5\xde\x09\x90\x09\xd0\x7b\xa3\xa9\x04\x14\xe0\x68\x45\xd7\x34\x3c\x7b\xd4\xb6\xcc\x3a\x11\xce\xd0\xeb\x6f\xc4\x41\x3b\x9b\x35\x07\x42\xba\xb9\x38\x3b\x07\x8b\x35\x01\xda\xb2\x7d\x08\x1e\x15\xb5\x88\x81\xa4\x87\xd6\x44\xcd\xce\x00\x3c\xbb\x96\xd1\x0d\x61\x69\xb4\xa5\x05\x29\x67\xcb\x90\xc1\xf5\x68\x74\x06\x20\x54\x7b\x83\x42\x8d\x2b\x40\x4d\x82\x25\x0a\xee\xfe\x01\xa0\xb5\x4e\x50\xb4\xb3\xe1\xf9\x15\xc0\x20\xda\x10\xbd\x77\x2c\x54\x0e\x4b\x97\x58\x27\x49\x0c\x34\x74\x9e\x6c\x58\xeb\x95\x0c\xb5\x4b\xdd\x86\x98\x75\x49\x89\xd1\x1b\xb2\x14\x42\x52\x31\x2a\x4a\x3c\xb1\x76\x65\x12\x76\x2c\x06\x19\x0c\x2e\x46\x83\x03\xb4\x20\x57\x24\xc3\x27\xc7\x8f\xc6\x61\xd9\x87\xac\xd1\x62\x45\x8d\x32\x19\x7c\xf8\x73\x40\xab\x15\x29\x69\x10\x72\xa6\x15\x31\x53\x79\x13\x59\xdb\x6a\xa1\xd6\x54\x46\xa3\x6d\x35\xf8\xeb\x43\x0b\xfd\xac\x45\xfb\x4c\xbc\xd1\x8a\xc6\x4a\xb9\x68\xe5\x16\x6b\xca\x80\x5d\x14\xe2\xbd\xc3\x39\x58\x57\xd2\x82\x0c\x29\x71\x0c\x3a\xfc\x20\xed\xce\xcf\xb3\x76\xac\x65\x3b\x31\x18\xc2\x0e\x27\x6c\x83\x50\x9d\x28\x13\x83\x10\x27\x8a\xb5\x68\x85\x66\x7f\x40\x39\x2b\xa8\x2d\x71\x47\xcb\xa4\xbd\xc5\x23\x06\x3b\x16\xba\xc6\x8a\x5e\x0f\xdf\xfc\x5a\x97\x3c\x1a\x93\x3b\xa3\xd5\x36\x83\xd9\xea\xd6\x49\xce\x14\x9a\xf2\x79\xf1\x0b\xa4\x62\x4b\xd5\x59\xa1\x3f\x24\xeb\x98\x9a\x48\x0b\x22\x58\x8b\xf8\x90\xa5\xe9\x43\xac\xbe\x6b\x63\x70\xc8\x54\xae\x51\x86\xca\xd5\xe9\xe5\x68\xf4\xff\xcb\xab\xeb\xde\x29\x34\xc6\x3d\xe5\xac\x37\xda\x50\x45\xd3\xa0\xd0\xb4\x75\x92\x81\x70\xa4\x8e\xab\x10\xd7\xda\xb6\xb6\x39\x85\xd0\x10\xde\x93\xfd\x8c\xc6\x3c\xa0\x7a\x2c\xdc\x17\x57\x85\x3b\x3b\x65\x76\x5d\x09\x9a\x0a\x0b\x5d\xae\xcf\x62\x35\x5c\x7b\x64\x0e\xca\xe6\x8e\x25\x83\x5f\x46\x3d\xab\x67\x27\x4e\x39\x93\x41\x31\xc9\x5f\x81\x0b\x6f\xe1\x5d\x5d\x7d\xfc\x47\x80\x35\x09\x6b\xf5\x26\xe4\xc5\xa7\x8f\xd7\xef\xc2\x3c\x87\x39\x71\x75\x34\x29\x5e\xcc\x64\x37\x59\xcf\x3b\x08\x4a\x80\x18\x88\x0f\x73\xc2\x63\x08\x4f\x8e\xcb\x76\x4c\x54\x64\x89\x51\x7a\x80\x27\x52\x58\x14\xe3\x62\xb1\xcc\xef\xee\x8b\x1e\xcb\x76\x82\x35\x3d\xfb\xe9\xe3\xf5\xe0\xc4\xb1\xfb\xbb\xaf\xc5\xf4\x7e\xb9\x98\xde\x7f\x9b\x4d\xa6\xcb\xdb\xf1\x7c\xba\xc8\xc7\x93\xe9\x29\x90\x43\x6f\x27\x7a\x37\x33\x4f\xe0\xdd\x4c\x3f\x8f\xbf\x7e\x29\x96\x93\xe9\x7d\x31\xfb\x3c\x9b\x8c\x8b\xe9\xf2\x66\x76\x7f\x0a\x2e\x25\x51\xa9\x7f\xd4\xa9\x98\x90\x7a\xd6\x1b\x94\x53\x89\x3d\x23\xde\x4c\x17\xc5\xec\x76\x5c\xcc\xee\x6e\x97\x93\xf1\x32\x1f\x17\xbf\x9d\x44\xdd\x20\xa7\x1c\x6d\xaa\x9c\x5d\xe9\xaa\x46\x1f\xd2\xfd\x04\x49\x14\x76\x1e\x87\x8a\xbb\x1d\xf7\x3c\xf5\x72\x76\x0f\xd4\xef\xb7\xa6\xdc\x7e\xa5\xa3\x26\x04\xf0\x28\xeb\x0c\xd2\x35\xa1\x91\xf5\xf7\x63\xe3\xa9\xa2\x61\xc2\x52\xff\x9b\x20\x69\x83\xb0\x7d\x4f\xa8\x20\xc8\x12\xfd\x89\x40\x2b\xd4\x26\x32\x15\x6b\xa6\xb0\x76\xa6\xcc\xe0\xe2\x72\xf4\x5f\x52\x01\xd8\xad\x90\xc3\x1e\xbb\xe8\x49\x12\x5c\x64\x45\xa1\x1f\x8d\xe9\xf7\x48\x41\xc2\x31\x07\xe5\x63\x06\x17\xa3\x51\x7d\xf4\xbe\xa6\xda\xf1\x36\x83\xcb\xff\x5d\xcf\x75\xc7\xb6\x71\x26\xd6\x34\x6f\x16\xc7\xd1\x60\xaa\x9b\x77\xf9\x2e\xa5\xb7\x0b\x11\xf6\x85\xb8\xff\x72\x48\x14\xb1\x34\x5f\x07\xc7\x5e\x8d\x20\x77\xd6\x6c\x7f\x18\xa8\xfd\x60\x6f\xd6\xe7\x89\xb0\x2f\xc6\xe4\x21\xda\xd2\xbc\x23\xe8\x2e\xeb\x43\xc2\xc9\x3b\x12\x08\xa4\xb8\x7f\xe5\x7b\xef\xb9\x2b\x29\x83\xab\x5e\x89\x34\x0b\xa8\x71\x6f\x76\x68\x7f\xdf\x25\xdd\x71\x97\xfc\x34\x83\x9d\x06\x73\xf4\xdd\xc0\x5a\xa8\x3e\xba\xac\x47\xda\x76\x61\x8e\x5a\xf7\xb9\x34\x5f\x75\xf8\xb9\x90\xce\x37\xeb\x0e\x4d\x06\x2b\x34\xe1\xc5\xf2\x83\x08\x7f\x07\x00\x00\xff\xff\xd8\x70\x85\x02\x51\x0a\x00\x00") +var _assetsRouterDeploymentYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x55\x4f\x6f\xe2\xc8\x13\xbd\xf3\x29\x4a\xe4\x30\x27\x63\xc8\x6f\x7e\x59\xad\x6f\x08\x9c\x5d\xa4\x21\x41\x81\x99\x2b\xaa\xb4\x0b\xd3\x4a\xbb\xbb\xb7\xba\x9a\x2c\xb3\xda\xef\xbe\xb2\x4d\x12\x9b\x64\x32\x91\x56\xcb\xa9\xe9\xaa\x7e\xaf\xfe\xbc\x2a\x5f\xc0\x9c\xbc\x71\xc7\x8a\xac\xc0\xa3\x96\x3d\x14\xb4\xc3\x68\x04\x0e\x68\x22\x85\xc1\x05\x2c\x6c\xc9\x14\x02\xcc\x9c\x15\x76\xc6\x10\x43\xf0\xa4\xf4\x4e\xab\x93\x13\x20\x13\xa0\xf7\x46\x53\x01\x28\xc0\xd1\x8a\xae\x68\x34\x78\xd0\xb6\xc8\x3a\x0c\x03\xf4\xfa\x1b\x71\xd0\xce\x66\xf5\x83\x90\x1e\x26\x83\x0b\xb0\x58\x11\xa0\x2d\x9a\x43\xf0\xa8\xa8\x41\x0c\x24\x3d\xb4\x9a\x35\x1b\x00\x78\x76\x4d\x44\x73\xc2\xc2\x68\x4b\x6b\x52\xce\x16\x21\x83\xab\xf1\x78\x00\x20\x54\x79\x83\x42\xb5\x2b\x40\x45\x82\x05\x0a\xb6\xff\x00\xd0\x5a\x27\x28\xda\xd9\xf0\x74\x05\x30\x8c\x36\x44\xef\x1d\x0b\x15\xa3\xc2\x25\xd6\x49\x12\x03\x8d\x9c\x27\x1b\xf6\x7a\x27\x23\xed\x52\x77\x20\x66\x5d\x50\x62\xf4\x81\x2c\x85\x90\x94\x8c\x8a\x12\x4f\xac\x5d\x91\x84\x36\x8a\x61\x06\xc3\xc9\x78\xf8\x0c\x2d\xc8\x25\xc9\xe8\xd1\xf1\x83\x71\x58\xf4\x21\x2b\xb4\x58\x52\x5d\x99\x0c\x3e\xfd\x35\xa4\xdd\x8e\x94\xd4\x08\x2b\xa6\x1d\x31\x53\x31\x8f\xac\x6d\xb9\x56\x7b\x2a\xa2\xd1\xb6\x1c\xfe\xfd\xa9\x81\x7e\xaa\x45\x73\x26\x3e\x68\x45\x53\xa5\x5c\xb4\x72\x83\x15\x65\xc0\x2e\x0a\xf1\xc9\xe1\x02\xac\x2b\x68\x4d\x86\x94\x38\x06\x1d\x5e\x95\xb6\xf5\xf3\xac\x1d\x6b\x39\xce\x0c\x86\xd0\xe2\x84\x63\x10\xaa\x12\x65\x62\x10\xe2\x44\xb1\x16\xad\xd0\x9c\x1e\x28\x67\x05\xb5\x25\xee\xd4\x32\x69\xba\x78\x16\x41\x1b\x85\xae\xb0\xa4\x1f\xd3\xd7\xbf\xc6\x65\x15\x8d\x59\x39\xa3\xd5\x31\x83\xc5\xee\xc6\xc9\x8a\x29\xd4\xf2\x79\xf1\x0b\xa4\x62\x13\xaa\xb3\x42\x7f\x4a\xd6\x31\xd5\x4c\x6b\x22\xd8\x8b\xf8\x90\xa5\xe9\x7d\x2c\xbf\x6b\x63\x70\xc4\x54\xec\x51\x46\xca\x55\xe9\xe5\x78\xfc\xcb\xe5\xe7\xab\xde\x2b\x34\xc6\x3d\xae\x58\x1f\xb4\xa1\x92\xf2\xa0\xd0\x34\x3a\xc9\x40\x38\x52\xc7\x55\x88\x2b\x6d\x1b\xdb\x92\x42\xa8\x03\x3e\x05\x7b\x8d\xc6\xdc\xa3\x7a\xd8\xb8\x2f\xae\x0c\xb7\x36\x67\x76\xfd\x12\x2c\x89\xcb\xb3\x21\x79\x31\x93\x3d\x74\x13\x79\xaa\xe4\xdd\xed\xd7\x4d\x7e\xb7\x5d\xe7\x77\xdf\x16\xb3\x7c\x7b\x33\x5d\xe6\xeb\xd5\x74\x96\xf7\xa2\x6f\x26\x31\x83\x67\x79\x25\xba\x1d\xdb\x37\xf0\xe6\xf9\xf5\xf4\xeb\x97\xcd\x76\x96\xdf\x6d\x16\xd7\x8b\xd9\x74\x93\x6f\xe7\x8b\xbb\xb7\xe0\x52\x12\x95\xfa\x07\x9d\x8a\x09\xa9\x67\x7d\x40\xa1\x77\x10\xe7\xf9\x7a\xb3\xb8\x99\x6e\x16\xb7\x37\xdb\xd9\x74\xbb\x9a\x6e\x7e\x7f\x13\xf5\x80\x9c\x72\xb4\xa9\x72\x76\xa7\xcb\x0a\x7d\x48\x4f\x22\x4e\x14\x76\x8e\x23\xc5\xdd\xa6\x3f\x0d\xde\x8a\xdd\x3d\xf5\x5b\x5e\x37\xfb\x37\x3a\xd3\x01\x80\x47\xd9\x67\x90\xee\x09\x8d\xec\xbf\x9f\x1b\x1d\x4b\x06\x93\x5f\xff\xd7\xd5\x01\x13\x16\xfa\xdf\x90\xa4\x35\xc2\xf1\x23\x54\x41\x90\x25\xfa\x37\x88\x76\xa8\x4d\x64\xda\xec\x99\xc2\xde\x99\x22\x83\xc9\xe5\xf8\xbf\x0c\x05\xa0\xdd\x62\xcf\xab\x74\xd2\x2b\x49\x70\x91\x15\x85\x3e\x1b\xd3\x1f\x91\x82\x84\xf3\x18\x94\x8f\x19\x4c\xc6\xe3\xea\xec\xbe\xa2\xca\xf1\x31\x83\xcb\xff\x5f\x2d\x75\xc7\x76\x70\x26\x56\xb4\xac\x77\x57\xe8\xcb\xbf\xaa\xef\x56\x6d\x4a\xef\x0b\x11\x4e\x42\x3c\x7d\xbc\x12\x45\x2c\xf5\x07\xea\xdc\xab\x2e\xc8\xad\x35\xc7\x57\x33\xdd\x27\x7b\x57\x9f\x6f\xd0\xbe\x18\x93\xfb\x68\x0b\xf3\x01\xd2\x36\xeb\xe7\x84\x93\x0f\x24\x10\x48\x71\xbf\xe5\x27\xef\xa5\x2b\x28\x83\xcf\x3d\x89\xd4\x3b\xb0\x76\xaf\xd7\x78\x7f\xe5\x26\xdd\xb5\x93\xfc\x34\x83\xb6\x06\x4b\xf4\x5d\x62\x2d\x54\x9d\x35\xeb\x81\x8e\x5d\x98\xb3\xd1\x7d\x92\xe6\x0f\x1d\x7e\x5e\x48\xe7\xeb\x8d\x8b\x26\x83\x1d\x9a\xf0\x62\x79\x55\x84\x7f\x02\x00\x00\xff\xff\x49\x0a\x8b\xa5\xd4\x08\x00\x00") func assetsRouterDeploymentYamlBytes() ([]byte, error) { return bindataRead( @@ -237,8 +237,8 @@ func assetsRouterDeploymentYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "assets/router/deployment.yaml", size: 2641, mode: os.FileMode(420), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x36, 0xa3, 0xe0, 0x8b, 0xad, 0x84, 0x5, 0xb8, 0x4, 0x12, 0x75, 0xbd, 0x5e, 0x99, 0x32, 0x76, 0xe1, 0xb, 0x93, 0x9b, 0xac, 0x1, 0xa3, 0xce, 0x6f, 0xc7, 0x9, 0x14, 0x77, 0x8e, 0xf5, 0x50}} + info := bindataFileInfo{name: "assets/router/deployment.yaml", size: 2260, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xd4, 0x77, 0x69, 0xab, 0xa7, 0xb4, 0xba, 0xb, 0xc6, 0xa6, 0x9, 0x6, 0x83, 0xe2, 0x16, 0x9e, 0x83, 0xa8, 0xc2, 0x5a, 0x50, 0xf5, 0x50, 0x56, 0x27, 0x27, 0xbb, 0x4, 0xc0, 0xc8, 0x94}} return a, nil } diff --git a/pkg/operator/controller/ingress/controller.go b/pkg/operator/controller/ingress/controller.go index b987eadb27..9b6e4b0c1a 100644 --- a/pkg/operator/controller/ingress/controller.go +++ b/pkg/operator/controller/ingress/controller.go @@ -54,6 +54,9 @@ const ( routerDefaultHeaderBufferSize = 32768 routerDefaultHeaderBufferMaxRewriteSize = 8192 + routerDefaultHostNetworkHTTPPort = 80 + routerDefaultHostNetworkHTTPSPort = 443 + routerDefaultHostNetworkStatsPort = 1936 ) var ( @@ -385,6 +388,17 @@ func setDefaultPublishingStrategy(ic *operatorv1.IngressController, infraConfig if effectiveStrategy.HostNetwork == nil { effectiveStrategy.HostNetwork = &operatorv1.HostNetworkStrategy{} } + // explicitly set the default ports if some of them are omitted + if effectiveStrategy.HostNetwork.HTTPPort == 0 { + effectiveStrategy.HostNetwork.HTTPPort = routerDefaultHostNetworkHTTPPort + } + if effectiveStrategy.HostNetwork.HTTPSPort == 0 { + effectiveStrategy.HostNetwork.HTTPSPort = routerDefaultHostNetworkHTTPSPort + } + if effectiveStrategy.HostNetwork.StatsPort == 0 { + effectiveStrategy.HostNetwork.StatsPort = routerDefaultHostNetworkStatsPort + } + if effectiveStrategy.HostNetwork.Protocol == operatorv1.DefaultProtocol { effectiveStrategy.HostNetwork.Protocol = operatorv1.TCPProtocol } @@ -441,16 +455,36 @@ func setDefaultPublishingStrategy(ic *operatorv1.IngressController, infraConfig return true } case operatorv1.HostNetworkStrategyType: - // Update if PROXY protocol is turned on or off. if ic.Status.EndpointPublishingStrategy.HostNetwork == nil { ic.Status.EndpointPublishingStrategy.HostNetwork = &operatorv1.HostNetworkStrategy{} } + statusHN := ic.Status.EndpointPublishingStrategy.HostNetwork specHN := effectiveStrategy.HostNetwork - if specHN != nil && specHN.Protocol != statusHN.Protocol { - statusHN.Protocol = specHN.Protocol - return true + + var changed bool + if specHN != nil { + // Update if PROXY protocol is turned on or off. + if specHN.Protocol != statusHN.Protocol { + statusHN.Protocol = specHN.Protocol + changed = true + } + + // Update if ports have been changed. + if specHN.HTTPPort != statusHN.HTTPPort { + statusHN.HTTPPort = specHN.HTTPPort + changed = true + } + if specHN.HTTPSPort != statusHN.HTTPSPort { + statusHN.HTTPSPort = specHN.HTTPSPort + changed = true + } + if specHN.StatsPort != statusHN.StatsPort { + statusHN.StatsPort = specHN.StatsPort + changed = true + } } + return changed } return false diff --git a/pkg/operator/controller/ingress/controller_test.go b/pkg/operator/controller/ingress/controller_test.go index 0a8ddf2291..29c5436bcb 100644 --- a/pkg/operator/controller/ingress/controller_test.go +++ b/pkg/operator/controller/ingress/controller_test.go @@ -124,7 +124,10 @@ func TestSetDefaultPublishingStrategySetsPlatformDefaults(t *testing.T) { EndpointPublishingStrategy: &operatorv1.EndpointPublishingStrategy{ Type: operatorv1.HostNetworkStrategyType, HostNetwork: &operatorv1.HostNetworkStrategy{ - Protocol: operatorv1.TCPProtocol, + Protocol: operatorv1.TCPProtocol, + HTTPPort: 80, + HTTPSPort: 443, + StatsPort: 1936, }, }, }, @@ -301,7 +304,21 @@ func TestSetDefaultPublishingStrategyHandlesUpdates(t *testing.T) { return &operatorv1.EndpointPublishingStrategy{ Type: operatorv1.HostNetworkStrategyType, HostNetwork: &operatorv1.HostNetworkStrategy{ - Protocol: proto, + Protocol: proto, + HTTPPort: 80, + HTTPSPort: 443, + StatsPort: 1936, + }, + } + } + customHostNetwork = func(httpPort, httpsPort, statsPort int32, protocol operatorv1.IngressControllerProtocol) *operatorv1.EndpointPublishingStrategy { + return &operatorv1.EndpointPublishingStrategy{ + Type: operatorv1.HostNetworkStrategyType, + HostNetwork: &operatorv1.HostNetworkStrategy{ + Protocol: operatorv1.TCPProtocol, + HTTPPort: httpPort, + HTTPSPort: httpsPort, + StatsPort: statsPort, }, } } @@ -410,6 +427,30 @@ func TestSetDefaultPublishingStrategyHandlesUpdates(t *testing.T) { expectedResult: true, expectedIC: makeIC(spec(hostNetwork(operatorv1.TCPProtocol)), status(hostNetwork(operatorv1.TCPProtocol))), }, + { + name: "hostnetwork ports changed", + ic: makeIC(spec(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol)), status(hostNetwork(operatorv1.TCPProtocol))), + expectedResult: true, + expectedIC: makeIC(spec(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol)), status(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol))), + }, + { + name: "hostnetwork ports changed, with status null", + ic: makeIC(spec(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol)), status(hostNetworkWithNull())), + expectedResult: true, + expectedIC: makeIC(spec(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol)), status(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol))), + }, + { + name: "hostnetwork ports removed", + ic: makeIC(spec(hostNetwork(operatorv1.TCPProtocol)), status(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol))), + expectedResult: true, + expectedIC: makeIC(spec(hostNetwork(operatorv1.TCPProtocol)), status(hostNetwork(operatorv1.TCPProtocol))), + }, + { + name: "hostnetwork ports removed, with spec null", + ic: makeIC(spec(hostNetworkWithNull()), status(customHostNetwork(8080, 8443, 8136, operatorv1.TCPProtocol))), + expectedResult: true, + expectedIC: makeIC(spec(hostNetworkWithNull()), status(hostNetwork(operatorv1.TCPProtocol))), + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/operator/controller/ingress/deployment.go b/pkg/operator/controller/ingress/deployment.go index 185947a50f..5400997c37 100644 --- a/pkg/operator/controller/ingress/deployment.go +++ b/pkg/operator/controller/ingress/deployment.go @@ -21,6 +21,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" operatorv1 "github.com/openshift/api/operator/v1" + "github.com/openshift/cluster-ingress-operator/pkg/manifests" "github.com/openshift/cluster-ingress-operator/pkg/operator/controller" oputil "github.com/openshift/cluster-ingress-operator/pkg/util" @@ -94,6 +95,14 @@ const ( RouterEnableCompression = "ROUTER_ENABLE_COMPRESSION" RouterCompressionMIMETypes = "ROUTER_COMPRESSION_MIME" RouterBackendCheckInterval = "ROUTER_BACKEND_CHECK_INTERVAL" + + RouterServiceHTTPPort = "ROUTER_SERVICE_HTTP_PORT" + RouterServiceHTTPSPort = "ROUTER_SERVICE_HTTPS_PORT" + StatsPort = "STATS_PORT" + + HTTPPortName = "http" + HTTPSPortName = "https" + StatsPortName = "metrics" ) // ensureRouterDeployment ensures the router deployment exists for a given @@ -621,6 +630,12 @@ func desiredRouterDeployment(ci *operatorv1.IngressController, ingressController deployment.Spec.Template.Spec.Containers[0].Image = ingressControllerImage deployment.Spec.Template.Spec.DNSPolicy = corev1.DNSClusterFirst + var ( + statsPort int32 = routerDefaultHostNetworkStatsPort + httpPort int32 = routerDefaultHostNetworkHTTPPort + httpsPort int32 = routerDefaultHostNetworkHTTPSPort + ) + if ci.Status.EndpointPublishingStrategy.Type == operatorv1.HostNetworkStrategyType { // Expose ports 80, 443, and 1936 on the host to provide // endpoints for the user's HA solution. @@ -634,8 +649,41 @@ func desiredRouterDeployment(ci *operatorv1.IngressController, ingressController deployment.Spec.Template.Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Host = "localhost" deployment.Spec.Template.Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Host = "localhost" deployment.Spec.Template.Spec.DNSPolicy = corev1.DNSClusterFirstWithHostNet + + config := ci.Status.EndpointPublishingStrategy.HostNetwork + if config.HTTPSPort == config.HTTPPort || config.HTTPPort == config.StatsPort || config.StatsPort == config.HTTPSPort { + return nil, fmt.Errorf("the specified HTTPS, HTTP and Stats ports %d, %d, %d are not unique", config.HTTPSPort, config.HTTPPort, config.StatsPort) + } + + // Set the ports to the values from the host network configuration + httpPort = config.HTTPPort + httpsPort = config.HTTPSPort + statsPort = config.StatsPort + + // Append the environment variables for the HTTP and HTTPS ports + env = append(env, + corev1.EnvVar{ + Name: RouterServiceHTTPSPort, + Value: strconv.Itoa(int(httpsPort)), + }, + corev1.EnvVar{ + Name: RouterServiceHTTPPort, + Value: strconv.Itoa(int(httpPort)), + }, + ) } + // Set the port for the probes from the host network configuration + deployment.Spec.Template.Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Port.IntVal = statsPort + deployment.Spec.Template.Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Port.IntVal = statsPort + deployment.Spec.Template.Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Port.IntVal = statsPort + + // append the value for the metrics port to the list of environment variables + env = append(env, corev1.EnvVar{ + Name: StatsPort, + Value: strconv.Itoa(int(statsPort)), + }) + // Fill in the default certificate secret name. secretName := controller.RouterEffectiveDefaultCertificateSecretName(ci, deployment.Namespace) deployment.Spec.Template.Spec.Volumes[0].Secret.SecretName = secretName.Name @@ -1041,6 +1089,23 @@ func desiredRouterDeployment(ci *operatorv1.IngressController, ingressController // Add the environment variables to the container deployment.Spec.Template.Spec.Containers[0].Env = append(deployment.Spec.Template.Spec.Containers[0].Env, env...) + // Add the ports to the container + deployment.Spec.Template.Spec.Containers[0].Ports = append( + deployment.Spec.Template.Spec.Containers[0].Ports, + corev1.ContainerPort{ + Name: HTTPPortName, + ContainerPort: httpPort, + }, + corev1.ContainerPort{ + Name: HTTPSPortName, + ContainerPort: httpsPort, + }, + corev1.ContainerPort{ + Name: StatsPortName, + ContainerPort: statsPort, + }, + ) + // Compute the hash for topology spread constraints and possibly // affinity policy now, after all the other fields have been computed, // and inject it into the appropriate fields. @@ -1272,6 +1337,7 @@ func hashableDeployment(deployment *appsv1.Deployment, onlyTemplate bool) *appsv ReadinessProbe: hashableProbe(container.ReadinessProbe), StartupProbe: hashableProbe(container.StartupProbe), SecurityContext: container.SecurityContext, + Ports: container.Ports, } } sort.Slice(containers, func(i, j int) bool { @@ -1468,6 +1534,7 @@ func deploymentConfigChanged(current, expected *appsv1.Deployment) (bool, *appsv copyProbe(expected.Spec.Template.Spec.Containers[0].ReadinessProbe, updated.Spec.Template.Spec.Containers[0].ReadinessProbe) copyProbe(expected.Spec.Template.Spec.Containers[0].StartupProbe, updated.Spec.Template.Spec.Containers[0].StartupProbe) updated.Spec.Template.Spec.Containers[0].VolumeMounts = expected.Spec.Template.Spec.Containers[0].VolumeMounts + updated.Spec.Template.Spec.Containers[0].Ports = expected.Spec.Template.Spec.Containers[0].Ports updated.Spec.Template.Spec.Tolerations = expected.Spec.Template.Spec.Tolerations updated.Spec.Template.Spec.TopologySpreadConstraints = expected.Spec.Template.Spec.TopologySpreadConstraints updated.Spec.Template.Spec.Affinity = expected.Spec.Template.Spec.Affinity diff --git a/pkg/operator/controller/ingress/deployment_test.go b/pkg/operator/controller/ingress/deployment_test.go index 53fd2aaaaa..25ba8a5c4e 100644 --- a/pkg/operator/controller/ingress/deployment_test.go +++ b/pkg/operator/controller/ingress/deployment_test.go @@ -11,6 +11,7 @@ import ( configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" + "github.com/openshift/cluster-ingress-operator/pkg/operator/controller" appsv1 "k8s.io/api/apps/v1" @@ -652,6 +653,12 @@ func TestDesiredRouterDeploymentVariety(t *testing.T) { expectedReplicas := int32(3) ic.Spec.Replicas = &expectedReplicas ic.Status.EndpointPublishingStrategy.Type = operatorv1.HostNetworkStrategyType + ic.Status.EndpointPublishingStrategy.HostNetwork = &operatorv1.HostNetworkStrategy{ + Protocol: operatorv1.TCPProtocol, + HTTPPort: 8080, + HTTPSPort: 8443, + StatsPort: 9146, + } networkConfig.Status.ClusterNetwork = []configv1.ClusterNetworkEntry{ {CIDR: "2620:0:2d0:200::7/32"}, } @@ -744,12 +751,29 @@ func TestDesiredRouterDeploymentVariety(t *testing.T) { {"ROUTER_H1_CASE_ADJUST", false, ""}, {RouterHardStopAfterEnvName, false, ""}, + {"STATS_PORT", true, "9146"}, + {"ROUTER_SERVICE_HTTP_PORT", true, "8080"}, + {"ROUTER_SERVICE_HTTPS_PORT", true, "8443"}, } if err := checkDeploymentEnvironment(t, deployment, tests); err != nil { t.Error(err) } checkDeploymentHasEnvSorted(t, deployment) + + checkContainerPort(t, deployment, "http", 8080) + checkContainerPort(t, deployment, "https", 8443) + checkContainerPort(t, deployment, "metrics", 9146) +} + +func checkContainerPort(t *testing.T, d *appsv1.Deployment, portName string, port int32) { + t.Helper() + for _, p := range d.Spec.Template.Spec.Containers[0].Ports { + if p.Name == portName && p.ContainerPort == port { + return + } + } + t.Errorf("deployment %s container does not have port with name %s and number %d", d.Name, portName, port) } func TestInferTLSProfileSpecFromDeployment(t *testing.T) { @@ -908,6 +932,16 @@ func TestDeploymentHash(t *testing.T) { expectDeploymentHashChanged: true, expectTemplateHashChanged: true, }, + { + description: "if ports are changed", + mutate: func(deployment *appsv1.Deployment) { + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = int32(8080) + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = int32(8443) + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = int32(8936) + }, + expectDeploymentHashChanged: true, + expectTemplateHashChanged: true, + }, } for _, tc := range testCases { @@ -922,6 +956,15 @@ func TestDeploymentHash(t *testing.T) { Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ Tolerations: []corev1.Toleration{toleration, otherToleration}, + Containers: []corev1.Container{ + { + Ports: []corev1.ContainerPort{ + {ContainerPort: 80}, + {ContainerPort: 443}, + {ContainerPort: 1936}, + }, + }, + }, }, }, Replicas: &two, @@ -1287,6 +1330,27 @@ func TestDeploymentConfigChanged(t *testing.T) { }, expect: true, }, + { + description: "if container HTTP port is changed", + mutate: func(deployment *appsv1.Deployment) { + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = 8080 + }, + expect: true, + }, + { + description: "if container HTTPS port is changed", + mutate: func(deployment *appsv1.Deployment) { + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = 8443 + }, + expect: true, + }, + { + description: "if container Stats port is changed", + mutate: func(deployment *appsv1.Deployment) { + deployment.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = 8936 + }, + expect: true, + }, } for _, tc := range testCases { @@ -1396,6 +1460,20 @@ func TestDeploymentConfigChanged(t *testing.T) { }, }, }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: int32(80), + }, + { + Name: "https", + ContainerPort: 443, + }, + { + Name: "metrics", + ContainerPort: 1936, + }, + }, }, }, Affinity: &corev1.Affinity{ diff --git a/test/e2e/operator_test.go b/test/e2e/operator_test.go index e1b4df4ddf..5a8494ffe3 100644 --- a/test/e2e/operator_test.go +++ b/test/e2e/operator_test.go @@ -25,9 +25,9 @@ import ( configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" + iov1 "github.com/openshift/api/operatoringress/v1" routev1 "github.com/openshift/api/route/v1" - iov1 "github.com/openshift/api/operatoringress/v1" "github.com/openshift/cluster-ingress-operator/pkg/manifests" operatorclient "github.com/openshift/cluster-ingress-operator/pkg/operator/client" "github.com/openshift/cluster-ingress-operator/pkg/operator/controller" @@ -81,6 +81,13 @@ var ( {Type: operatorv1.DNSReadyIngressConditionType, Status: operatorv1.ConditionTrue}, {Type: ingresscontroller.IngressControllerAdmittedConditionType, Status: operatorv1.ConditionTrue}, } + availableConditionsForIngressControllerWithHostNetwork = []operatorv1.OperatorCondition{ + {Type: ingresscontroller.IngressControllerAdmittedConditionType, Status: operatorv1.ConditionTrue}, + {Type: operatorv1.IngressControllerAvailableConditionType, Status: operatorv1.ConditionTrue}, + {Type: ingresscontroller.IngressControllerDeploymentReplicasAllAvailableConditionType, Status: operatorv1.ConditionTrue}, + {Type: operatorv1.LoadBalancerManagedIngressConditionType, Status: operatorv1.ConditionFalse}, + {Type: operatorv1.DNSManagedIngressConditionType, Status: operatorv1.ConditionFalse}, + } // The ingress canary check status condition only applies to the default ingress controller. defaultAvailableConditions = append(availableConditionsForIngressControllerWithLoadBalancer, operatorv1.OperatorCondition{Type: ingresscontroller.IngressControllerCanaryCheckSuccessConditionType, Status: operatorv1.ConditionTrue}) ) @@ -89,6 +96,7 @@ var kclient client.Client var dnsConfig configv1.DNS var infraConfig configv1.Infrastructure var operatorNamespace = operatorcontroller.DefaultOperatorNamespace +var operandNamespace = operatorcontroller.DefaultOperandNamespace var defaultName = types.NamespacedName{Namespace: operatorNamespace, Name: manifests.DefaultIngressControllerName} var clusterConfigName = types.NamespacedName{Namespace: operatorNamespace, Name: manifests.ClusterIngressConfigName} @@ -757,6 +765,106 @@ func TestHostNetworkEndpointPublishingStrategy(t *testing.T) { } } +// TestHostNetworkPortBinding creates two ingresscontrollers on the same node +// with different port bindings and verifies that both routers are available. +func TestHostNetworkPortBinding(t *testing.T) { + // deploy first ingresscontroller with the default port bindings + name1 := types.NamespacedName{Namespace: operatorNamespace, Name: "host"} + ing1 := newHostNetworkController(name1, name1.Name+"."+dnsConfig.Spec.BaseDomain) + if err := kclient.Create(context.TODO(), ing1); err != nil { + t.Fatalf("failed to create the first ingresscontroller: %v", err) + } + defer assertIngressControllerDeleted(t, kclient, ing1) + + err := waitForIngressControllerCondition(t, kclient, 5*time.Minute, name1, availableConditionsForIngressControllerWithHostNetwork...) + if err != nil { + t.Errorf("failed to observe expected conditions for the first ingresscontroller: %v", err) + } + + // get first router's single replica + pods := &corev1.PodList{} + if err := kclient.List(context.TODO(), pods, client.InNamespace(operandNamespace)); err != nil { + t.Fatalf("failed to list the first ingresscontroller's PODs: %v", err) + } + var pod1 *corev1.Pod + for _, p := range pods.Items { + if strings.HasPrefix(p.Name, "router-"+name1.Name) { + pod1 = &p + break + } + } + if pod1 == nil { + t.Fatal("failed to find the first ingresscontroller's POD") + } + + routerContainer := pod1.Spec.Containers[0] + assertContainerHasPort(t, routerContainer, "http", 80) + assertContainerHasPort(t, routerContainer, "https", 443) + assertContainerHasPort(t, routerContainer, "metrics", 1936) + if !pod1.Spec.HostNetwork { + t.Errorf("pod %s is not running on the host's network", pod1.Name) + } + + // create second ingresscontroller on the same node but with different port bindings + name2 := types.NamespacedName{Namespace: operatorNamespace, Name: "samehost"} + strategy := &operatorv1.HostNetworkStrategy{ + HTTPPort: 9080, + HTTPSPort: 9443, + StatsPort: 9936, + } + // take the node placement of the first router + placement := &operatorv1.NodePlacement{ + Tolerations: pod1.Spec.Tolerations, + NodeSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"kubernetes.io/hostname": pod1.Spec.NodeName}, + }, + } + + ing2 := newHostNetworkController(name2, name2.Name+"."+dnsConfig.Spec.BaseDomain) + ing2.Spec.NodePlacement = placement + ing2.Spec.EndpointPublishingStrategy.HostNetwork = strategy + if err := kclient.Create(context.TODO(), ing2); err != nil { + t.Fatalf("failed to create the second ingresscontroller: %v", err) + } + defer assertIngressControllerDeleted(t, kclient, ing2) + + err = waitForIngressControllerCondition(t, kclient, 5*time.Minute, name2, availableConditionsForIngressControllerWithHostNetwork...) + if err != nil { + t.Errorf("failed to observe expected conditions for the second ingresscontroller: %v", err) + } + + if err := kclient.List(context.TODO(), pods, client.InNamespace(operandNamespace)); err != nil { + t.Fatalf("failed to list the first ingresscontroller's PODs: %v", err) + } + var pod2 *corev1.Pod + for _, p := range pods.Items { + if strings.HasPrefix(p.Name, "router-"+name2.Name) { + pod2 = &p + break + } + } + if pod2 == nil { + t.Fatalf("failed to find the second ingresscontroller's POD") + } + routerContainer = pod2.Spec.Containers[0] + assertContainerHasPort(t, routerContainer, "http", 9080) + assertContainerHasPort(t, routerContainer, "https", 9443) + assertContainerHasPort(t, routerContainer, "metrics", 9936) + if !pod2.Spec.HostNetwork { + t.Errorf("pod %s is not running on the host's network", pod2.Name) + } +} + +func assertContainerHasPort(t *testing.T, container corev1.Container, name string, port int32) { + t.Helper() + for _, p := range container.Ports { + if p.Name == name && p.ContainerPort == port { + return + } + } + t.Errorf("container %s does not have port named %q open on %d", container.Name, name, port) +} + // TestInternalLoadBalancer creates an ingresscontroller with the // "LoadBalancerService" endpoint publishing strategy type with scope set to // "Internal" and verifies that the operator creates a load balancer and that