github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/step/expose/step_expose_test.go (about) 1 // +build unit 2 3 package expose_test 4 5 import ( 6 "fmt" 7 "testing" 8 9 "github.com/olli-ai/jx/v2/pkg/cmd/opts" 10 "github.com/olli-ai/jx/v2/pkg/cmd/step/expose" 11 "github.com/olli-ai/jx/v2/pkg/cmd/testhelpers" 12 "github.com/olli-ai/jx/v2/pkg/config" 13 "k8s.io/api/extensions/v1beta1" 14 "k8s.io/client-go/kubernetes" 15 16 "github.com/olli-ai/jx/v2/pkg/gits" 17 "github.com/olli-ai/jx/v2/pkg/helm" 18 resources_test "github.com/olli-ai/jx/v2/pkg/kube/resources/mocks" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 corev1 "k8s.io/api/core/v1" 22 apierrors "k8s.io/apimachinery/pkg/api/errors" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/runtime" 25 ) 26 27 func TestStepExpose(t *testing.T) { 28 t.Parallel() 29 30 type testData struct { 31 Dir, Domain, NamespaceSubDomain string 32 TLSEnabled bool 33 } 34 35 ns := "jx-staging" 36 subDomain := "-" + ns + "." 37 38 tests := []testData{ 39 { 40 Dir: "test_data/http", 41 Domain: "35.195.192.178.nip.io", 42 NamespaceSubDomain: subDomain, 43 TLSEnabled: false, 44 }, 45 { 46 Dir: "test_data/https", 47 Domain: "my-domain.com", 48 NamespaceSubDomain: subDomain, 49 TLSEnabled: true, 50 }, 51 } 52 53 for _, td := range tests { 54 commonOpts := opts.NewCommonOptionsWithFactory(nil) 55 o := &expose.StepExposeOptions{ 56 Dir: td.Dir, 57 } 58 o.CommonOptions = &commonOpts 59 o.Namespace = ns 60 61 testhelpers.ConfigureTestOptionsWithResources(o.CommonOptions, 62 []runtime.Object{}, 63 []runtime.Object{}, 64 gits.NewGitCLI(), 65 nil, 66 helm.NewHelmCLI("helm", helm.V2, "", true), 67 resources_test.NewMockInstaller(), 68 ) 69 70 kubeClient, err := o.KubeClient() 71 require.NoError(t, err) 72 73 expectedRequirements := &config.RequirementsConfig{ 74 Ingress: config.IngressConfig{ 75 Domain: td.Domain, 76 NamespaceSubDomain: td.NamespaceSubDomain, 77 TLS: config.TLSConfig{ 78 Enabled: td.TLSEnabled, 79 }, 80 }, 81 } 82 ings := []*v1beta1.Ingress{ 83 { 84 ObjectMeta: metav1.ObjectMeta{ 85 Name: "update-my-ingress", 86 }, 87 }, 88 } 89 svcs := []*corev1.Service{ 90 { 91 ObjectMeta: metav1.ObjectMeta{ 92 Name: "svc1", 93 Labels: map[string]string{ 94 "fabric8.io/expose": "true", 95 }, 96 }, 97 Spec: corev1.ServiceSpec{ 98 Ports: []corev1.ServicePort{ 99 { 100 Name: "http", 101 Protocol: "TCP", 102 Port: 80, 103 }, 104 }, 105 }, 106 }, 107 { 108 ObjectMeta: metav1.ObjectMeta{ 109 Name: "update-my-ingress", 110 Labels: map[string]string{ 111 "fabric8.io/expose": "true", 112 }, 113 }, 114 Spec: corev1.ServiceSpec{ 115 Ports: []corev1.ServicePort{ 116 { 117 Name: "http", 118 Protocol: "TCP", 119 Port: 8080, 120 }, 121 }, 122 }, 123 }, 124 { 125 ObjectMeta: metav1.ObjectMeta{ 126 Name: "dont-expose", 127 Labels: map[string]string{ 128 "fabric8.io/expose": "false", 129 }, 130 }, 131 Spec: corev1.ServiceSpec{ 132 Ports: []corev1.ServicePort{ 133 { 134 Name: "http", 135 Protocol: "TCP", 136 Port: 80, 137 }, 138 }, 139 }, 140 }, 141 { 142 ObjectMeta: metav1.ObjectMeta{ 143 Name: "no-expose-label", 144 Labels: map[string]string{ 145 "fabric8.io/expose": "false", 146 }, 147 }, 148 Spec: corev1.ServiceSpec{ 149 Ports: []corev1.ServicePort{ 150 { 151 Name: "http", 152 Protocol: "TCP", 153 Port: 80, 154 }, 155 }, 156 }, 157 }, 158 } 159 for _, ing := range ings { 160 _, err = kubeClient.ExtensionsV1beta1().Ingresses(ns).Create(ing) 161 require.NoError(t, err, "failed to create Ingress %s in namespace %s", ing.Name, ns) 162 } 163 164 for _, svc := range svcs { 165 _, err = kubeClient.CoreV1().Services(ns).Create(svc) 166 require.NoError(t, err, "failed to create Service %s in namespace %s", svc.Name, ns) 167 } 168 169 err = o.Run() 170 assert.NoError(t, err) 171 172 for _, svc := range svcs { 173 if expose.IsExposedService(svc) { 174 assertIngressForService(t, kubeClient, ns, svc, expectedRequirements) 175 } else { 176 assertNoIngressExistsForService(t, kubeClient, ns, svc) 177 } 178 } 179 } 180 } 181 182 func assertIngressForService(t *testing.T, kubeClient kubernetes.Interface, ns string, svc *corev1.Service, expectedRequirements *config.RequirementsConfig) { 183 svcName := svc.Name 184 ingress, err := kubeClient.ExtensionsV1beta1().Ingresses(ns).Get(svcName, metav1.GetOptions{}) 185 if assert.NoError(t, err, "finding Ingress %s in namespace %s", svcName, ns) { 186 t.Logf("found the Ingress for service %s in namespace %s which should be exposed", svcName, ns) 187 } 188 189 rules := ingress.Spec.Rules 190 require.Equal(t, 1, len(rules), "Ingress rule count for Ingress %s", svcName) 191 rule := rules[0] 192 expectedHost := fmt.Sprintf("%s%s%s", svcName, expectedRequirements.Ingress.NamespaceSubDomain, expectedRequirements.Ingress.Domain) 193 if assert.Equal(t, expectedHost, rule.Host, "host for Ingress %s in namespace %s", svcName, ns) { 194 t.Logf("Ingress %s is at the correct host %s", svcName, expectedHost) 195 } 196 assert.True(t, rule.HTTP != nil && len(rule.HTTP.Paths) == 1, "has a HTTP path for Ingress %s in namespace %s", svcName, ns) 197 198 path := rule.HTTP.Paths[0] 199 if assert.Equal(t, svcName, path.Backend.ServiceName, "has a HTTP path backend service name for Ingress %s in namespace %s", svcName, ns) { 200 t.Logf("Ingress %s is using the correct backend service", svcName) 201 } 202 203 servicePort := svc.Spec.Ports[0].Port 204 if assert.Equal(t, servicePort, path.Backend.ServicePort.IntVal, "the exposed service port on the Ingress %s in namespace %s", svcName, ns) { 205 t.Logf("Ingress %s is at the correct service port %d", svcName, int(servicePort)) 206 } 207 208 scheme := "http://" 209 if expectedRequirements.Ingress.TLS.Enabled { 210 scheme = "https://" 211 } 212 expectedURL := scheme + expectedHost 213 actualSvc, err := kubeClient.CoreV1().Services(ns).Get(svcName, metav1.GetOptions{}) 214 require.NoError(t, err, "could not load Service %s", svcName) 215 216 actualURL := actualSvc.Annotations[expose.ExposeAnnotationKey] 217 if assert.Equal(t, expectedURL, actualURL, "the exposed service URL was not annotated correctly on the Service %s in namespace %s", svcName, ns) { 218 t.Logf("added the exposed URL %s to the Service %s using the annotation %s", actualURL, svcName, expose.ExposeAnnotationKey) 219 } 220 221 if expectedRequirements.Ingress.TLS.Enabled { 222 tlss := ingress.Spec.TLS 223 require.Equal(t, 1, len(tlss), "Ingress TLS count for Ingress %s", svcName) 224 tls := tlss[0] 225 t.Logf("got TLS secret name %s for Ingress %s", tls.SecretName, ingress.Name) 226 227 require.Equal(t, 1, len(tls.Hosts), "Ingress TLS host should be defined for Ingress %s", svcName) 228 host := tls.Hosts[0] 229 require.Equal(t, expectedHost, host, "Ingress TLS host for Ingress %s", svcName) 230 } 231 } 232 233 func assertNoIngressExistsForService(t *testing.T, kubeClient kubernetes.Interface, ns string, svc *corev1.Service) { 234 svcName := svc.Name 235 _, err := kubeClient.ExtensionsV1beta1().Ingresses(ns).Get(svcName, metav1.GetOptions{}) 236 notExposed := err != nil && apierrors.IsNotFound(err) 237 if assert.True(t, notExposed, "we should not have around Ingress %s in namespace %s as the service is not meant to be exposed. error: %v", svcName, ns, err) { 238 t.Logf("did not create an Ingress for service %s in namespace %s which is not meant to be exposed", svcName, ns) 239 } 240 }