"Fossies" - the Fresh Open Source Software Archive

Member "istio-1.6.5/istioctl/cmd/add-to-mesh_test.go" (8 Jul 2020, 9958 Bytes) of package /linux/misc/istio-1.6.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "add-to-mesh_test.go": 1.5.4_vs_1.6.0.

    1 // Copyright 2019 Istio Authors.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 //
    7 //     http://www.apache.org/licenses/LICENSE-2.0
    8 //
    9 // Unless required by applicable law or agreed to in writing, software
   10 // distributed under the License is distributed on an "AS IS" BASIS,
   11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12 // See the License for the specific language governing permissions and
   13 // limitations under the License.
   14 
   15 package cmd
   16 
   17 import (
   18     "bytes"
   19     "fmt"
   20     "strings"
   21     "testing"
   22 
   23     appsv1 "k8s.io/api/apps/v1"
   24     coreV1 "k8s.io/api/core/v1"
   25     metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
   26     "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
   27     "k8s.io/apimachinery/pkg/runtime"
   28     "k8s.io/client-go/dynamic"
   29     "k8s.io/client-go/dynamic/fake"
   30 
   31     "istio.io/istio/pkg/config/schema/collections"
   32 )
   33 
   34 type testcase struct {
   35     description       string
   36     expectedException bool
   37     args              []string
   38     k8sConfigs        []runtime.Object
   39     dynamicConfigs    []runtime.Object
   40     expectedOutput    string
   41     namespace         string
   42 }
   43 
   44 var (
   45     one              = int32(1)
   46     cannedK8sConfigs = []runtime.Object{
   47         &coreV1.ConfigMapList{Items: []coreV1.ConfigMap{}},
   48 
   49         &appsv1.DeploymentList{Items: []appsv1.Deployment{
   50             {
   51                 ObjectMeta: metaV1.ObjectMeta{
   52                     Name:      "details-v1",
   53                     Namespace: "default",
   54                     Labels: map[string]string{
   55                         "app": "details",
   56                     },
   57                 },
   58                 Spec: appsv1.DeploymentSpec{
   59                     Replicas: &one,
   60                     Selector: &metaV1.LabelSelector{
   61                         MatchLabels: map[string]string{"app": "details"},
   62                     },
   63                     Template: coreV1.PodTemplateSpec{
   64                         ObjectMeta: metaV1.ObjectMeta{
   65                             Labels: map[string]string{"app": "details"},
   66                         },
   67                         Spec: coreV1.PodSpec{
   68                             Containers: []coreV1.Container{
   69                                 {Name: "details", Image: "docker.io/istio/examples-bookinfo-details-v1:1.15.0"},
   70                             },
   71                         },
   72                     },
   73                 },
   74             },
   75         }},
   76         &coreV1.ServiceList{Items: []coreV1.Service{
   77             {
   78                 ObjectMeta: metaV1.ObjectMeta{
   79                     Name:      "details",
   80                     Namespace: "default",
   81                 },
   82                 Spec: coreV1.ServiceSpec{
   83                     Ports: []coreV1.ServicePort{
   84                         {
   85                             Port: 9080,
   86                             Name: "http",
   87                         },
   88                     },
   89                     Selector: map[string]string{"app": "details"},
   90                 },
   91             },
   92             {
   93                 ObjectMeta: metaV1.ObjectMeta{
   94                     Name:      "dummyservice",
   95                     Namespace: "default",
   96                 },
   97                 Spec: coreV1.ServiceSpec{
   98                     Ports: []coreV1.ServicePort{
   99                         {
  100                             Port: 9080,
  101                             Name: "http",
  102                         },
  103                     },
  104                     Selector: map[string]string{"app": "dummy"},
  105                 },
  106             },
  107         }},
  108     }
  109     cannedDynamicConfigs = []runtime.Object{
  110         &unstructured.Unstructured{
  111             Object: map[string]interface{}{
  112                 "apiVersion": collections.IstioNetworkingV1Alpha3Serviceentries.Resource().APIVersion(),
  113                 "kind":       collections.IstioNetworkingV1Alpha3Serviceentries.Resource().Kind(),
  114                 "metadata": map[string]interface{}{
  115                     "namespace": "default",
  116                     "name":      "mesh-expansion-vmtest",
  117                 },
  118             },
  119         },
  120     }
  121 )
  122 
  123 func TestAddToMesh(t *testing.T) {
  124     cases := []testcase{
  125         {
  126             description:       "Invalid command args - missing service name",
  127             args:              strings.Split("experimental add-to-mesh service", " "),
  128             expectedException: true,
  129             expectedOutput:    "Error: expecting service name\n",
  130         },
  131         {
  132             description:       "Invalid command args - missing deployment name",
  133             args:              strings.Split("experimental add-to-mesh deployment", " "),
  134             expectedException: true,
  135             expectedOutput:    "Error: expecting deployment name\n",
  136         },
  137         {
  138             description: "valid case - add service into mesh",
  139             args: strings.Split("experimental add-to-mesh service details --meshConfigFile testdata/mesh-config.yaml"+
  140                 " --injectConfigFile testdata/inject-config.yaml"+
  141                 " --valuesFile testdata/inject-values.yaml", " "),
  142             expectedException: false,
  143             k8sConfigs:        cannedK8sConfigs,
  144             expectedOutput: "deployment details-v1.default updated successfully with Istio sidecar injected.\n" +
  145                 "Next Step: Add related labels to the deployment to align with Istio's requirement: " +
  146                 "https://istio.io/docs/setup/kubernetes/additional-setup/requirements/\n",
  147             namespace: "default",
  148         },
  149         {
  150             description: "valid case - add deployment into mesh",
  151             args: strings.Split("experimental add-to-mesh deployment details-v1 --meshConfigFile testdata/mesh-config.yaml"+
  152                 " --injectConfigFile testdata/inject-config.yaml"+
  153                 " --valuesFile testdata/inject-values.yaml", " "),
  154             expectedException: false,
  155             k8sConfigs:        cannedK8sConfigs,
  156             expectedOutput: "deployment details-v1.default updated successfully with Istio sidecar injected.\n" +
  157                 "Next Step: Add related labels to the deployment to align with Istio's requirement: " +
  158                 "https://istio.io/docs/setup/kubernetes/additional-setup/requirements/\n",
  159             namespace: "default",
  160         },
  161         {
  162             description: "service does not exist",
  163             args: strings.Split("experimental add-to-mesh service test --meshConfigFile testdata/mesh-config.yaml"+
  164                 " --injectConfigFile testdata/inject-config.yaml"+
  165                 " --valuesFile testdata/inject-values.yaml", " "),
  166             expectedException: true,
  167             k8sConfigs:        cannedK8sConfigs,
  168             expectedOutput:    "Error: services \"test\" not found\n",
  169         },
  170         {
  171             description: "deployment does not exist",
  172             args: strings.Split("experimental add-to-mesh deployment test --meshConfigFile testdata/mesh-config.yaml"+
  173                 " --injectConfigFile testdata/inject-config.yaml"+
  174                 " --valuesFile testdata/inject-values.yaml", " "),
  175             expectedException: true,
  176             k8sConfigs:        cannedK8sConfigs,
  177             expectedOutput:    "Error: deployment \"test\" does not exist\n",
  178         },
  179         {
  180             description: "service without deployment",
  181             args: strings.Split("experimental add-to-mesh service dummyservice --meshConfigFile testdata/mesh-config.yaml"+
  182                 " --injectConfigFile testdata/inject-config.yaml"+
  183                 " --valuesFile testdata/inject-values.yaml", " "),
  184             namespace:         "default",
  185             expectedException: false,
  186             k8sConfigs:        cannedK8sConfigs,
  187             expectedOutput:    "No deployments found for service dummyservice.default\n",
  188         },
  189         {
  190             description:       "Invalid command args - missing service name",
  191             args:              strings.Split("experimental add-to-mesh service", " "),
  192             expectedException: true,
  193             expectedOutput:    "Error: expecting service name\n",
  194         },
  195         {
  196             description:       "Invalid command args - missing service IP",
  197             args:              strings.Split("experimental add-to-mesh external-service test tcp:12345", " "),
  198             expectedException: true,
  199             expectedOutput:    "Error: provide service name, IP and Port List\n",
  200         },
  201         {
  202             description:       "Invalid command args - missing service Ports",
  203             args:              strings.Split("experimental add-to-mesh external-service test 172.186.15.123", " "),
  204             expectedException: true,
  205             expectedOutput:    "Error: provide service name, IP and Port List\n",
  206         },
  207         {
  208             description:       "Invalid command args - invalid port protocol",
  209             args:              strings.Split("experimental add-to-mesh external-service test 172.186.15.123 tcp1:12345", " "),
  210             expectedException: true,
  211             expectedOutput:    "Error: protocol tcp1 is not supported by Istio\n",
  212         },
  213         {
  214             description:       "service already exists",
  215             args:              strings.Split("experimental add-to-mesh external-service dummyservice 11.11.11.11 tcp:12345", " "),
  216             expectedException: true,
  217             k8sConfigs:        cannedK8sConfigs,
  218             dynamicConfigs:    cannedDynamicConfigs,
  219             namespace:         "default",
  220             expectedOutput:    "Error: service \"dummyservice\" already exists, skip\n",
  221         },
  222         {
  223             description:       "ServiceEntry already exists",
  224             args:              strings.Split("experimental add-to-mesh external-service vmtest 11.11.11.11 tcp:12345", " "),
  225             expectedException: true,
  226             k8sConfigs:        cannedK8sConfigs,
  227             dynamicConfigs:    cannedDynamicConfigs,
  228             namespace:         "default",
  229             expectedOutput:    "Error: service entry \"mesh-expansion-vmtest\" already exists, skip\n",
  230         },
  231         {
  232             description:    "external service banana namespace",
  233             args:           strings.Split("experimental add-to-mesh external-service vmtest 11.11.11.11 tcp:12345 tcp:12346", " "),
  234             k8sConfigs:     cannedK8sConfigs,
  235             dynamicConfigs: cannedDynamicConfigs,
  236             namespace:      "banana",
  237             expectedOutput: `ServiceEntry "mesh-expansion-vmtest.banana" has been created in the Istio service mesh for the external service "vmtest"
  238 Kubernetes Service "vmtest.banana" has been created in the Istio service mesh for the external service "vmtest"
  239 `,
  240         },
  241     }
  242 
  243     for i, c := range cases {
  244         t.Run(fmt.Sprintf("case %d %s", i, c.description), func(t *testing.T) {
  245             verifyAddToMeshOutput(t, c)
  246         })
  247     }
  248 }
  249 
  250 func verifyAddToMeshOutput(t *testing.T, c testcase) {
  251     t.Helper()
  252 
  253     interfaceFactory = mockInterfaceFactoryGenerator(c.k8sConfigs)
  254     crdFactory = mockDynamicClientGenerator(c.dynamicConfigs)
  255     var out bytes.Buffer
  256     rootCmd := GetRootCmd(c.args)
  257     rootCmd.SetOutput(&out)
  258     if c.namespace != "" {
  259         namespace = c.namespace
  260     }
  261 
  262     fErr := rootCmd.Execute()
  263     output := out.String()
  264 
  265     if c.expectedException {
  266         if fErr == nil {
  267             t.Fatalf("Wanted an exception, "+
  268                 "didn't get one, output was %q", output)
  269         }
  270     } else {
  271         if fErr != nil {
  272             t.Fatalf("Unwanted exception: %v", fErr)
  273         }
  274     }
  275 
  276     if c.expectedOutput != "" && c.expectedOutput != output {
  277         t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput)
  278     }
  279 }
  280 
  281 func mockDynamicClientGenerator(dynamicConfigs []runtime.Object) func(kubeconfig string) (dynamic.Interface, error) {
  282     outFactory := func(_ string) (dynamic.Interface, error) {
  283         types := runtime.NewScheme()
  284         client := fake.NewSimpleDynamicClient(types, dynamicConfigs...)
  285         return client, nil
  286     }
  287     return outFactory
  288 }