github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/tests/e2e/utils/common.go (about)

     1  /*
     2  Copyright 2019 The KubeEdge Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package utils
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/tls"
    22  	"encoding/json"
    23  	"io"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"os/exec"
    27  	"reflect"
    28  	"strings"
    29  	"time"
    30  
    31  	MQTT "github.com/eclipse/paho.mqtt.golang"
    32  	"github.com/onsi/ginkgo"
    33  	. "github.com/onsi/gomega"
    34  	"github.com/pkg/errors"
    35  	apps "k8s.io/api/apps/v1"
    36  	"k8s.io/api/core/v1"
    37  	"k8s.io/apimachinery/pkg/api/resource"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/util/intstr"
    40  
    41  	"github.com/kubeedge/kubeedge/cloud/pkg/apis/devices/v1alpha1"
    42  	"github.com/kubeedge/viaduct/pkg/api"
    43  )
    44  
    45  const (
    46  	Namespace             = "default"
    47  	DeviceETPrefix        = "$hw/events/device/"
    48  	TwinETUpdateSuffix    = "/twin/update"
    49  	TwinETGetSuffix       = "/twin/get"
    50  	TwinETGetResultSuffix = "/twin/get/result"
    51  )
    52  
    53  var (
    54  	ProtocolQuic      bool
    55  	ProtocolWebsocket bool
    56  )
    57  
    58  var TokenClient Token
    59  var ClientOpts *MQTT.ClientOptions
    60  var Client MQTT.Client
    61  var TwinResult DeviceTwinResult
    62  
    63  // Token interface to validate the MQTT connection.
    64  type Token interface {
    65  	Wait() bool
    66  	WaitTimeout(time.Duration) bool
    67  	Error() error
    68  }
    69  
    70  // BaseMessage the base struct of event message
    71  type BaseMessage struct {
    72  	EventID   string `json:"event_id"`
    73  	Timestamp int64  `json:"timestamp"`
    74  }
    75  
    76  // TwinValue the struct of twin value
    77  type TwinValue struct {
    78  	Value    *string        `json:"value,omitempty"`
    79  	Metadata *ValueMetadata `json:"metadata,omitempty"`
    80  }
    81  
    82  // ValueMetadata the meta of value
    83  type ValueMetadata struct {
    84  	Timestamp int64 `json:"timestamp,omitempty"`
    85  }
    86  
    87  // TypeMetadata the meta of value type
    88  type TypeMetadata struct {
    89  	Type string `json:"type,omitempty"`
    90  }
    91  
    92  // TwinVersion twin version
    93  type TwinVersion struct {
    94  	CloudVersion int64 `json:"cloud"`
    95  	EdgeVersion  int64 `json:"edge"`
    96  }
    97  
    98  // MsgTwin the struct of device twin
    99  type MsgTwin struct {
   100  	Expected        *TwinValue    `json:"expected,omitempty"`
   101  	Actual          *TwinValue    `json:"actual,omitempty"`
   102  	Optional        *bool         `json:"optional,omitempty"`
   103  	Metadata        *TypeMetadata `json:"metadata,omitempty"`
   104  	ExpectedVersion *TwinVersion  `json:"expected_version,omitempty"`
   105  	ActualVersion   *TwinVersion  `json:"actual_version,omitempty"`
   106  }
   107  
   108  // DeviceTwinUpdate the struct of device twin update
   109  type DeviceTwinUpdate struct {
   110  	BaseMessage
   111  	Twin map[string]*MsgTwin `json:"twin"`
   112  }
   113  
   114  // DeviceTwinResult device get result
   115  type DeviceTwinResult struct {
   116  	BaseMessage
   117  	Twin map[string]*MsgTwin `json:"twin"`
   118  }
   119  
   120  // Function to get nginx deployment spec
   121  func nginxDeploymentSpec(imgUrl, selector string, replicas int) *apps.DeploymentSpec {
   122  	var nodeselector map[string]string
   123  	if selector == "" {
   124  		nodeselector = map[string]string{}
   125  	} else {
   126  		nodeselector = map[string]string{"disktype": selector}
   127  	}
   128  	deplObj := apps.DeploymentSpec{
   129  		Replicas: func() *int32 { i := int32(replicas); return &i }(),
   130  		Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "nginx"}},
   131  		Template: v1.PodTemplateSpec{
   132  			ObjectMeta: metav1.ObjectMeta{
   133  				Labels: map[string]string{"app": "nginx"},
   134  			},
   135  			Spec: v1.PodSpec{
   136  				Containers: []v1.Container{
   137  					{
   138  						Name:  "nginx",
   139  						Image: imgUrl,
   140  					},
   141  				},
   142  				NodeSelector: nodeselector,
   143  			},
   144  		},
   145  	}
   146  
   147  	return &deplObj
   148  }
   149  
   150  // Function to get edgecore deploymentspec object
   151  func edgecoreDeploymentSpec(imgURL, configmap string, replicas int) *apps.DeploymentSpec {
   152  	IsSecureCtx := true
   153  	deplObj := apps.DeploymentSpec{
   154  		Replicas: func() *int32 { i := int32(replicas); return &i }(),
   155  		Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "edgecore"}},
   156  		Template: v1.PodTemplateSpec{
   157  			ObjectMeta: metav1.ObjectMeta{
   158  				Labels: map[string]string{"app": "edgecore"},
   159  			},
   160  			Spec: v1.PodSpec{
   161  				Containers: []v1.Container{
   162  					{
   163  						Name:            "edgecore",
   164  						Image:           imgURL,
   165  						SecurityContext: &v1.SecurityContext{Privileged: &IsSecureCtx},
   166  						ImagePullPolicy: v1.PullPolicy("IfNotPresent"),
   167  						Resources: v1.ResourceRequirements{
   168  							Requests: v1.ResourceList{
   169  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("200m"),
   170  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("100Mi"),
   171  							},
   172  							Limits: v1.ResourceList{
   173  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("200m"),
   174  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("100Mi"),
   175  							},
   176  						},
   177  						Env:          []v1.EnvVar{{Name: "DOCKER_HOST", Value: "tcp://localhost:2375"}},
   178  						VolumeMounts: []v1.VolumeMount{{Name: "cert", MountPath: "/etc/kubeedge/certs"}, {Name: "conf", MountPath: "/etc/kubeedge/edge/conf"}},
   179  					}, {
   180  						Name:            "dind-daemon",
   181  						SecurityContext: &v1.SecurityContext{Privileged: &IsSecureCtx},
   182  						Image:           "docker:dind",
   183  						Resources: v1.ResourceRequirements{
   184  							Requests: v1.ResourceList{
   185  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("20m"),
   186  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("256Mi"),
   187  							},
   188  						},
   189  						VolumeMounts: []v1.VolumeMount{{Name: "docker-graph-storage", MountPath: "/var/lib/docker"}},
   190  					},
   191  				},
   192  				NodeSelector: map[string]string{"k8snode": "kb-perf-node"},
   193  				Volumes: []v1.Volume{
   194  					{Name: "cert", VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubeedge/certs"}}},
   195  					{Name: "conf", VolumeSource: v1.VolumeSource{ConfigMap: &v1.ConfigMapVolumeSource{LocalObjectReference: v1.LocalObjectReference{Name: configmap}}}},
   196  					{Name: "docker-graph-storage", VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}},
   197  				},
   198  			},
   199  		},
   200  	}
   201  	return &deplObj
   202  }
   203  
   204  // Function to create cloudcore deploymentspec object
   205  func cloudcoreDeploymentSpec(imgURL, configmap string, replicas int) *apps.DeploymentSpec {
   206  	portInfo := []v1.ContainerPort{{ContainerPort: 10000, Protocol: "TCP", Name: "websocket"}, {ContainerPort: 10001, Protocol: "UDP", Name: "quic"}}
   207  
   208  	deplObj := apps.DeploymentSpec{
   209  		Replicas: func() *int32 { i := int32(replicas); return &i }(),
   210  		Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "cloudcore"}},
   211  		Template: v1.PodTemplateSpec{
   212  			ObjectMeta: metav1.ObjectMeta{
   213  				Labels: map[string]string{"app": "cloudcore"},
   214  			},
   215  			Spec: v1.PodSpec{
   216  				HostNetwork:   true,
   217  				RestartPolicy: "Always",
   218  				Containers: []v1.Container{
   219  					{
   220  						Name:            "cloudcore",
   221  						Image:           imgURL,
   222  						ImagePullPolicy: v1.PullPolicy("IfNotPresent"),
   223  						Resources: v1.ResourceRequirements{
   224  							Requests: v1.ResourceList{
   225  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("100m"),
   226  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("512Mi"),
   227  							},
   228  						},
   229  						Ports:        portInfo,
   230  						VolumeMounts: []v1.VolumeMount{{Name: "cert", MountPath: "/etc/kubeedge/certs"}, {Name: "conf", MountPath: "/etc/kubeedge/cloud/conf"}},
   231  					},
   232  				},
   233  				Volumes: []v1.Volume{
   234  					{Name: "cert", VolumeSource: v1.VolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/etc/kubeedge/certs"}}},
   235  					{Name: "conf", VolumeSource: v1.VolumeSource{ConfigMap: &v1.ConfigMapVolumeSource{LocalObjectReference: v1.LocalObjectReference{Name: configmap}}}},
   236  				},
   237  			},
   238  		},
   239  	}
   240  	return &deplObj
   241  }
   242  
   243  func newDeployment(cloudcore, edgecore bool, name, imgUrl, nodeselector, configmap string, replicas int) *apps.Deployment {
   244  	var depObj *apps.DeploymentSpec
   245  	var namespace string
   246  
   247  	if edgecore == true {
   248  		depObj = edgecoreDeploymentSpec(imgUrl, configmap, replicas)
   249  		namespace = Namespace
   250  	} else if cloudcore == true {
   251  		depObj = cloudcoreDeploymentSpec(imgUrl, configmap, replicas)
   252  		namespace = Namespace
   253  	} else {
   254  		depObj = nginxDeploymentSpec(imgUrl, nodeselector, replicas)
   255  		namespace = Namespace
   256  	}
   257  
   258  	deployment := apps.Deployment{
   259  		TypeMeta: metav1.TypeMeta{APIVersion: "apps/v1", Kind: "Deployment"},
   260  		ObjectMeta: metav1.ObjectMeta{
   261  			Name:      name,
   262  			Labels:    map[string]string{"app": "kubeedge"},
   263  			Namespace: namespace,
   264  		},
   265  		Spec: *depObj,
   266  	}
   267  	return &deployment
   268  }
   269  
   270  func NewPodObj(podName, imgUrl, nodeselector string) *v1.Pod {
   271  	pod := v1.Pod{
   272  		TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Pod"},
   273  		ObjectMeta: metav1.ObjectMeta{
   274  			Name:   podName,
   275  			Labels: map[string]string{"app": "nginx"},
   276  		},
   277  		Spec: v1.PodSpec{
   278  			Containers: []v1.Container{
   279  				{
   280  					Name:  "nginx",
   281  					Image: imgUrl,
   282  				},
   283  			},
   284  			NodeSelector: map[string]string{"disktype": nodeselector},
   285  		},
   286  	}
   287  	return &pod
   288  }
   289  
   290  // GetDeployments to get the deployments list
   291  func GetDeployments(list *apps.DeploymentList, getDeploymentApi string) error {
   292  
   293  	err, resp := SendHttpRequest(http.MethodGet, getDeploymentApi)
   294  	defer resp.Body.Close()
   295  	contents, err := ioutil.ReadAll(resp.Body)
   296  	if err != nil {
   297  		Fatalf("HTTP Response reading has failed: %v", err)
   298  		return err
   299  	}
   300  	err = json.Unmarshal(contents, &list)
   301  	if err != nil {
   302  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   303  		return err
   304  	}
   305  	return nil
   306  
   307  }
   308  func VerifyDeleteDeployment(getDeploymentApi string) int {
   309  	err, resp := SendHttpRequest(http.MethodGet, getDeploymentApi)
   310  	if err != nil {
   311  		Fatalf("SendHttpRequest is failed: %v", err)
   312  	}
   313  	defer resp.Body.Close()
   314  	return resp.StatusCode
   315  }
   316  
   317  // HandlePod to handle app deployment/delete using pod spec.
   318  func HandlePod(operation string, apiserver string, UID string, pod *v1.Pod) bool {
   319  	var req *http.Request
   320  	var err error
   321  	var body io.Reader
   322  
   323  	tr := &http.Transport{
   324  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   325  	}
   326  	client := &http.Client{
   327  		Transport: tr,
   328  	}
   329  	switch operation {
   330  	case "POST":
   331  		body := pod
   332  		respBytes, err := json.Marshal(body)
   333  		if err != nil {
   334  			Fatalf("Marshalling body failed: %v", err)
   335  		}
   336  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   337  	case "DELETE":
   338  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   339  	}
   340  	if err != nil {
   341  		// handle error
   342  		Fatalf("Frame HTTP request failed: %v", err)
   343  		return false
   344  	}
   345  	req.Header.Set("Content-Type", "application/json")
   346  	t := time.Now()
   347  	resp, err := client.Do(req)
   348  	if err != nil {
   349  		// handle error
   350  		Fatalf("HTTP request is failed :%v", err)
   351  		return false
   352  	}
   353  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   354  	return true
   355  }
   356  
   357  // HandleDeployment to handle app deployment/delete deployment.
   358  func HandleDeployment(IsCloudCore, IsEdgeCore bool, operation, apiserver, UID, ImageUrl, nodeselector, configmapname string, replica int) bool {
   359  	var req *http.Request
   360  	var err error
   361  	var body io.Reader
   362  
   363  	defer ginkgo.GinkgoRecover()
   364  	tr := &http.Transport{
   365  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   366  	}
   367  	client := &http.Client{
   368  		Transport: tr,
   369  	}
   370  
   371  	switch operation {
   372  	case "POST":
   373  		depObj := newDeployment(IsCloudCore, IsEdgeCore, UID, ImageUrl, nodeselector, configmapname, replica)
   374  		if err != nil {
   375  			Fatalf("GenerateDeploymentBody marshalling failed: %v", err)
   376  		}
   377  		respBytes, err := json.Marshal(depObj)
   378  		if err != nil {
   379  			Fatalf("Marshalling body failed: %v", err)
   380  		}
   381  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   382  	case "DELETE":
   383  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   384  	}
   385  	if err != nil {
   386  		// handle error
   387  		Fatalf("Frame HTTP request failed: %v", err)
   388  		return false
   389  	}
   390  	req.Header.Set("Content-Type", "application/json")
   391  	t := time.Now()
   392  	resp, err := client.Do(req)
   393  	if err != nil {
   394  		// handle error
   395  		Fatalf("HTTP request is failed :%v", err)
   396  		return false
   397  	}
   398  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   399  	return true
   400  }
   401  
   402  // DeleteDeployment to delete deployment
   403  func DeleteDeployment(DeploymentApi, deploymentname string) int {
   404  	err, resp := SendHttpRequest(http.MethodDelete, DeploymentApi+"/"+deploymentname)
   405  	if err != nil {
   406  		// handle error
   407  		Fatalf("HTTP request is failed :%v", err)
   408  		return -1
   409  	}
   410  
   411  	defer resp.Body.Close()
   412  
   413  	return resp.StatusCode
   414  }
   415  
   416  // PrintCombinedOutput to show the os command injuction in combined format
   417  func PrintCombinedOutput(cmd *exec.Cmd) error {
   418  	Infof("===========> Executing: %s\n", strings.Join(cmd.Args, " "))
   419  	output, err := cmd.CombinedOutput()
   420  	if err != nil {
   421  		Infof("CombinedOutput failed %v", err)
   422  		return err
   423  	}
   424  	if len(output) > 0 {
   425  		Infof("=====> Output: %s\n", string(output))
   426  	}
   427  	return nil
   428  }
   429  
   430  // ExposeCloudService function to expose the service for cloud deployment
   431  func ExposeCloudService(name, serviceHandler string) error {
   432  	ServiceObj := CreateServiceObject(name)
   433  	respBytes, err := json.Marshal(ServiceObj)
   434  	if err != nil {
   435  		Fatalf("Marshalling body failed: %v", err)
   436  	}
   437  	req, err := http.NewRequest(http.MethodPost, serviceHandler, bytes.NewBuffer(respBytes))
   438  	if err != nil {
   439  		// handle error
   440  		Fatalf("Frame HTTP request failed: %v", err)
   441  		return err
   442  	}
   443  	client := &http.Client{}
   444  	req.Header.Set("Content-Type", "application/json")
   445  	t := time.Now()
   446  	resp, err := client.Do(req)
   447  	if err != nil {
   448  		// handle error
   449  		Fatalf("HTTP request is failed :%v", err)
   450  		return err
   451  	}
   452  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   453  	Expect(resp.StatusCode).Should(Equal(http.StatusCreated))
   454  	return nil
   455  }
   456  
   457  // CreateServiceObject function to create a servcice object
   458  func CreateServiceObject(name string) *v1.Service {
   459  	var portInfo []v1.ServicePort
   460  
   461  	portInfo = []v1.ServicePort{
   462  		{
   463  			Name: "websocket", Protocol: "TCP", Port: 10000, TargetPort: intstr.FromInt(10000),
   464  		}, {
   465  			Name: "quic", Protocol: "UDP", Port: 10001, TargetPort: intstr.FromInt(10001),
   466  		},
   467  	}
   468  
   469  	Service := v1.Service{
   470  		TypeMeta:   metav1.TypeMeta{APIVersion: "v1", Kind: "Service"},
   471  		ObjectMeta: metav1.ObjectMeta{Name: name, Labels: map[string]string{"app": "kubeedge"}},
   472  
   473  		Spec: v1.ServiceSpec{
   474  			Ports:    portInfo,
   475  			Selector: map[string]string{"app": "cloudcore"},
   476  			Type:     "NodePort",
   477  		},
   478  	}
   479  	return &Service
   480  }
   481  
   482  // GetServicePort function to get the service port created for deployment.
   483  func GetServicePort(cloudName, serviceHandler string) (int32, int32) {
   484  	var svc v1.ServiceList
   485  	var wssport, quicport int32
   486  	err, resp := SendHttpRequest(http.MethodGet, serviceHandler)
   487  	if err != nil {
   488  		// handle error
   489  		Fatalf("HTTP request is failed :%v", err)
   490  		return -1, -1
   491  	}
   492  
   493  	contents, err := ioutil.ReadAll(resp.Body)
   494  	if err != nil {
   495  		Fatalf("HTTP Response reading has failed: %v", err)
   496  		return -1, -1
   497  	}
   498  
   499  	err = json.Unmarshal(contents, &svc)
   500  	if err != nil {
   501  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   502  		return -1, -1
   503  	}
   504  	defer resp.Body.Close()
   505  
   506  	for _, svcs := range svc.Items {
   507  		if svcs.Name == cloudName {
   508  			for _, nodePort := range svcs.Spec.Ports {
   509  				if nodePort.Name == api.ProtocolTypeQuic {
   510  					quicport = nodePort.NodePort
   511  				}
   512  				if nodePort.Name == api.ProtocolTypeWS {
   513  					wssport = nodePort.NodePort
   514  				}
   515  			}
   516  			break
   517  		}
   518  	}
   519  	return wssport, quicport
   520  }
   521  
   522  // DeleteSvc function to delete service
   523  func DeleteSvc(svcname string) int {
   524  	err, resp := SendHttpRequest(http.MethodDelete, svcname)
   525  	if err != nil {
   526  		// handle error
   527  		Fatalf("HTTP request is failed :%v", err)
   528  		return -1
   529  	}
   530  
   531  	defer resp.Body.Close()
   532  
   533  	return resp.StatusCode
   534  }
   535  
   536  // HandleDeviceModel to handle app deployment/delete using pod spec.
   537  func HandleDeviceModel(operation string, apiserver string, UID string, protocolType string) (bool, int) {
   538  	var req *http.Request
   539  	var err error
   540  	var body io.Reader
   541  
   542  	tr := &http.Transport{
   543  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   544  	}
   545  	client := &http.Client{
   546  		Transport: tr,
   547  	}
   548  
   549  	switch operation {
   550  	case "POST":
   551  		body := newDeviceModelObject(protocolType, false)
   552  		respBytes, err := json.Marshal(body)
   553  		if err != nil {
   554  			Fatalf("Marshalling body failed: %v", err)
   555  		}
   556  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   557  		req.Header.Set("Content-Type", "application/json")
   558  	case "PATCH":
   559  		body := newDeviceModelObject(protocolType, true)
   560  		respBytes, err := json.Marshal(body)
   561  		if err != nil {
   562  			Fatalf("Marshalling body failed: %v", err)
   563  		}
   564  		req, err = http.NewRequest(http.MethodPatch, apiserver+UID, bytes.NewBuffer(respBytes))
   565  		req.Header.Set("Content-Type", "application/merge-patch+json")
   566  	case "DELETE":
   567  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   568  		req.Header.Set("Content-Type", "application/json")
   569  	}
   570  	if err != nil {
   571  		// handle error
   572  		Fatalf("Frame HTTP request failed: %v", err)
   573  		return false, 0
   574  	}
   575  	t := time.Now()
   576  	resp, err := client.Do(req)
   577  	if err != nil {
   578  		// handle error
   579  		Fatalf("HTTP request is failed :%v", err)
   580  		return false, 0
   581  	}
   582  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   583  	return true, resp.StatusCode
   584  }
   585  
   586  // HandleDeviceInstance to handle app deployment/delete using pod spec.
   587  func HandleDeviceInstance(operation string, apiserver string, nodeSelector string, UID string, protocolType string) (bool, int) {
   588  	var req *http.Request
   589  	var err error
   590  	var body io.Reader
   591  
   592  	tr := &http.Transport{
   593  		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
   594  	}
   595  	client := &http.Client{
   596  		Transport: tr,
   597  	}
   598  	switch operation {
   599  	case "POST":
   600  		body := newDeviceInstanceObject(nodeSelector, protocolType, false)
   601  		respBytes, err := json.Marshal(body)
   602  		if err != nil {
   603  			Fatalf("Marshalling body failed: %v", err)
   604  		}
   605  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   606  		req.Header.Set("Content-Type", "application/json")
   607  	case "PATCH":
   608  		body := newDeviceInstanceObject(nodeSelector, protocolType, true)
   609  		respBytes, err := json.Marshal(body)
   610  		if err != nil {
   611  			Fatalf("Marshalling body failed: %v", err)
   612  		}
   613  		req, err = http.NewRequest(http.MethodPatch, apiserver+UID, bytes.NewBuffer(respBytes))
   614  		req.Header.Set("Content-Type", "application/merge-patch+json")
   615  	case "DELETE":
   616  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   617  		req.Header.Set("Content-Type", "application/json")
   618  	}
   619  	if err != nil {
   620  		// handle error
   621  		Fatalf("Frame HTTP request failed: %v", err)
   622  		return false, 0
   623  	}
   624  	t := time.Now()
   625  	resp, err := client.Do(req)
   626  	if err != nil {
   627  		// handle error
   628  		Fatalf("HTTP request is failed :%v", err)
   629  		return false, 0
   630  	}
   631  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   632  	return true, resp.StatusCode
   633  }
   634  
   635  // newDeviceInstanceObject creates a new device instance object
   636  func newDeviceInstanceObject(nodeSelector string, protocolType string, updated bool) *v1alpha1.Device {
   637  	var deviceInstance v1alpha1.Device
   638  	if updated == false {
   639  		switch protocolType {
   640  		case "bluetooth":
   641  			deviceInstance = NewBluetoothDeviceInstance(nodeSelector)
   642  		case "modbus":
   643  			deviceInstance = NewModbusDeviceInstance(nodeSelector)
   644  		case "led":
   645  			deviceInstance = NewLedDeviceInstance(nodeSelector)
   646  		case "incorrect-instance":
   647  			deviceInstance = IncorrectDeviceInstance()
   648  		}
   649  	} else {
   650  		switch protocolType {
   651  		case "bluetooth":
   652  			deviceInstance = UpdatedBluetoothDeviceInstance(nodeSelector)
   653  		case "modbus":
   654  			deviceInstance = UpdatedModbusDeviceInstance(nodeSelector)
   655  		case "led":
   656  			deviceInstance = UpdatedLedDeviceInstance(nodeSelector)
   657  		case "incorrect-instance":
   658  			deviceInstance = IncorrectDeviceInstance()
   659  		}
   660  	}
   661  	return &deviceInstance
   662  }
   663  
   664  // newDeviceModelObject creates a new device model object
   665  func newDeviceModelObject(protocolType string, updated bool) *v1alpha1.DeviceModel {
   666  	var deviceModel v1alpha1.DeviceModel
   667  	if updated == false {
   668  		switch protocolType {
   669  		case "bluetooth":
   670  			deviceModel = NewBluetoothDeviceModel()
   671  		case "modbus":
   672  			deviceModel = NewModbusDeviceModel()
   673  		case "led":
   674  			deviceModel = NewLedDeviceModel()
   675  		case "incorrect-model":
   676  			deviceModel = IncorrectDeviceModel()
   677  		}
   678  	} else {
   679  		switch protocolType {
   680  		case "bluetooth":
   681  			deviceModel = UpdatedBluetoothDeviceModel()
   682  		case "modbus":
   683  			deviceModel = UpdatedModbusDeviceModel()
   684  		case "led":
   685  			deviceModel = UpdatedLedDeviceModel()
   686  		case "incorrect-model":
   687  			deviceModel = IncorrectDeviceModel()
   688  		}
   689  	}
   690  	return &deviceModel
   691  }
   692  
   693  // GetDeviceModel to get the deviceModel list and verify whether the contents of the device model matches with what is expected
   694  func GetDeviceModel(list *v1alpha1.DeviceModelList, getDeviceModelApi string, expectedDeviceModel *v1alpha1.DeviceModel) ([]v1alpha1.DeviceModel, error) {
   695  	err, resp := SendHttpRequest(http.MethodGet, getDeviceModelApi)
   696  	defer resp.Body.Close()
   697  	contents, err := ioutil.ReadAll(resp.Body)
   698  	if err != nil {
   699  		Fatalf("HTTP Response reading has failed: %v", err)
   700  		return nil, err
   701  	}
   702  	err = json.Unmarshal(contents, &list)
   703  	if err != nil {
   704  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   705  		return nil, err
   706  	}
   707  	if expectedDeviceModel != nil {
   708  		modelExists := false
   709  		for _, deviceModel := range list.Items {
   710  			if expectedDeviceModel.ObjectMeta.Name == deviceModel.ObjectMeta.Name {
   711  				modelExists = true
   712  				if reflect.DeepEqual(expectedDeviceModel.TypeMeta, deviceModel.TypeMeta) == false || expectedDeviceModel.ObjectMeta.Namespace != deviceModel.ObjectMeta.Namespace || reflect.DeepEqual(expectedDeviceModel.Spec, deviceModel.Spec) == false {
   713  					return nil, errors.New("The device model is not matching with what was expected")
   714  				}
   715  			}
   716  		}
   717  		if !modelExists {
   718  			return nil, errors.New("The requested device model is not found")
   719  		}
   720  	}
   721  	return list.Items, nil
   722  }
   723  
   724  // GetDevice to get the device list
   725  func GetDevice(list *v1alpha1.DeviceList, getDeviceApi string, expectedDevice *v1alpha1.Device) ([]v1alpha1.Device, error) {
   726  	err, resp := SendHttpRequest(http.MethodGet, getDeviceApi)
   727  	defer resp.Body.Close()
   728  	contents, err := ioutil.ReadAll(resp.Body)
   729  	if err != nil {
   730  		Fatalf("HTTP Response reading has failed: %v", err)
   731  		return nil, err
   732  	}
   733  	err = json.Unmarshal(contents, &list)
   734  	if err != nil {
   735  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   736  		return nil, err
   737  	}
   738  	if expectedDevice != nil {
   739  		deviceExists := false
   740  		for _, device := range list.Items {
   741  			if expectedDevice.ObjectMeta.Name == device.ObjectMeta.Name {
   742  				deviceExists = true
   743  				if reflect.DeepEqual(expectedDevice.TypeMeta, device.TypeMeta) == false || expectedDevice.ObjectMeta.Namespace != device.ObjectMeta.Namespace || reflect.DeepEqual(expectedDevice.ObjectMeta.Labels, device.ObjectMeta.Labels) == false || reflect.DeepEqual(expectedDevice.Spec, device.Spec) == false {
   744  					return nil, errors.New("The device is not matching with what was expected")
   745  				}
   746  				twinExists := false
   747  				for _, expectedTwin := range expectedDevice.Status.Twins {
   748  					for _, twin := range device.Status.Twins {
   749  						if expectedTwin.PropertyName == twin.PropertyName {
   750  							twinExists = true
   751  							if reflect.DeepEqual(expectedTwin.Desired, twin.Desired) == false {
   752  								return nil, errors.New("Status twin " + twin.PropertyName + " not as expected")
   753  							}
   754  						}
   755  					}
   756  				}
   757  				if !twinExists {
   758  					return nil, errors.New("Status twin(s) not found !!!!")
   759  				}
   760  			}
   761  		}
   762  		if !deviceExists {
   763  			return nil, errors.New("The requested device is not found")
   764  		}
   765  	}
   766  	return list.Items, nil
   767  }
   768  
   769  // MqttClientInit create mqtt client config
   770  func MqttClientInit(server, clientID, username, password string) *MQTT.ClientOptions {
   771  	opts := MQTT.NewClientOptions().AddBroker(server).SetClientID(clientID).SetCleanSession(true)
   772  	if username != "" {
   773  		opts.SetUsername(username)
   774  		if password != "" {
   775  			opts.SetPassword(password)
   776  		}
   777  	}
   778  	tlsConfig := &tls.Config{InsecureSkipVerify: true, ClientAuth: tls.NoClientCert}
   779  	opts.SetTLSConfig(tlsConfig)
   780  	return opts
   781  }
   782  
   783  // MqttConnect function felicitates the MQTT connection
   784  func MqttConnect() error {
   785  	// Initiate the MQTT connection
   786  	ClientOpts = MqttClientInit("tcp://127.0.0.1:1884", "eventbus", "", "")
   787  	Client = MQTT.NewClient(ClientOpts)
   788  	if TokenClient = Client.Connect(); TokenClient.Wait() && TokenClient.Error() != nil {
   789  		return errors.New("client.Connect() Error is %s" + TokenClient.Error().Error())
   790  	}
   791  	return nil
   792  }
   793  
   794  // ChangeTwinValue sends the updated twin value to the edge through the MQTT broker
   795  func ChangeTwinValue(updateMessage DeviceTwinUpdate, deviceID string) error {
   796  	twinUpdateBody, err := json.Marshal(updateMessage)
   797  	if err != nil {
   798  		return errors.New("Error in marshalling: %s" + err.Error())
   799  	}
   800  	deviceTwinUpdate := DeviceETPrefix + deviceID + TwinETUpdateSuffix
   801  	TokenClient = Client.Publish(deviceTwinUpdate, 0, false, twinUpdateBody)
   802  	if TokenClient.Wait() && TokenClient.Error() != nil {
   803  		return errors.New("client.publish() Error in device twin update is %s" + TokenClient.Error().Error())
   804  	}
   805  	return nil
   806  }
   807  
   808  // GetTwin function is used to get the device twin details from the edge
   809  func GetTwin(updateMessage DeviceTwinUpdate, deviceID string) error {
   810  	getTwin := DeviceETPrefix + deviceID + TwinETGetSuffix
   811  	twinUpdateBody, err := json.Marshal(updateMessage)
   812  	if err != nil {
   813  		return errors.New("Error in marshalling: %s" + err.Error())
   814  	}
   815  	TokenClient = Client.Publish(getTwin, 0, false, twinUpdateBody)
   816  	if TokenClient.Wait() && TokenClient.Error() != nil {
   817  		return errors.New("client.publish() Error in device twin get  is: %s " + TokenClient.Error().Error())
   818  	}
   819  	return nil
   820  }
   821  
   822  // subscribe function subscribes  the device twin information through the MQTT broker
   823  func TwinSubscribe(deviceID string) {
   824  	getTwinResult := DeviceETPrefix + deviceID + TwinETGetResultSuffix
   825  	TokenClient = Client.Subscribe(getTwinResult, 0, OnTwinMessageReceived)
   826  	if TokenClient.Wait() && TokenClient.Error() != nil {
   827  		Errorf("subscribe() Error in device twin result get  is %v", TokenClient.Error().Error())
   828  	}
   829  	for {
   830  		twin := DeviceTwinUpdate{}
   831  		err := GetTwin(twin, deviceID)
   832  		if err != nil {
   833  			Errorf("Error in getting device twin: %v", err.Error())
   834  		}
   835  		time.Sleep(1 * time.Second)
   836  		if TwinResult.Twin != nil {
   837  			break
   838  		}
   839  	}
   840  }
   841  
   842  // OnTwinMessageReceived callback function which is called when message is received
   843  func OnTwinMessageReceived(client MQTT.Client, message MQTT.Message) {
   844  	err := json.Unmarshal(message.Payload(), &TwinResult)
   845  	if err != nil {
   846  		Errorf("Error in unmarshalling: %v", err.Error())
   847  	}
   848  }
   849  
   850  // CompareConfigMaps is used to compare 2 config maps
   851  func CompareConfigMaps(configMap, expectedConfigMap v1.ConfigMap) bool {
   852  	if reflect.DeepEqual(expectedConfigMap.TypeMeta, configMap.TypeMeta) == false || expectedConfigMap.ObjectMeta.Namespace != configMap.ObjectMeta.Namespace || reflect.DeepEqual(expectedConfigMap.Data, configMap.Data) == false {
   853  		return false
   854  
   855  	}
   856  	return true
   857  }
   858  
   859  // CompareTwin is used to compare 2 device Twins
   860  func CompareTwin(deviceTwin map[string]*MsgTwin, expectedDeviceTwin map[string]*MsgTwin) bool {
   861  	for key := range expectedDeviceTwin {
   862  		if deviceTwin[key].Metadata != nil && deviceTwin[key].Expected.Value != nil {
   863  			if *deviceTwin[key].Metadata != *expectedDeviceTwin[key].Metadata || *deviceTwin[key].Expected.Value != *expectedDeviceTwin[key].Expected.Value {
   864  				return false
   865  			}
   866  		} else {
   867  			return false
   868  		}
   869  	}
   870  	return true
   871  }