github.com/midokura/kubeedge@v1.2.0-mido.0/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  	client := &http.Client{}
   543  	switch operation {
   544  	case "POST":
   545  		body := newDeviceModelObject(protocolType, false)
   546  		respBytes, err := json.Marshal(body)
   547  		if err != nil {
   548  			Fatalf("Marshalling body failed: %v", err)
   549  		}
   550  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   551  		req.Header.Set("Content-Type", "application/json")
   552  	case "PATCH":
   553  		body := newDeviceModelObject(protocolType, true)
   554  		respBytes, err := json.Marshal(body)
   555  		if err != nil {
   556  			Fatalf("Marshalling body failed: %v", err)
   557  		}
   558  		req, err = http.NewRequest(http.MethodPatch, apiserver+UID, bytes.NewBuffer(respBytes))
   559  		req.Header.Set("Content-Type", "application/merge-patch+json")
   560  	case "DELETE":
   561  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   562  		req.Header.Set("Content-Type", "application/json")
   563  	}
   564  	if err != nil {
   565  		// handle error
   566  		Fatalf("Frame HTTP request failed: %v", err)
   567  		return false, 0
   568  	}
   569  	t := time.Now()
   570  	resp, err := client.Do(req)
   571  	if err != nil {
   572  		// handle error
   573  		Fatalf("HTTP request is failed :%v", err)
   574  		return false, 0
   575  	}
   576  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   577  	return true, resp.StatusCode
   578  }
   579  
   580  // HandleDeviceInstance to handle app deployment/delete using pod spec.
   581  func HandleDeviceInstance(operation string, apiserver string, nodeSelector string, UID string, protocolType string) (bool, int) {
   582  	var req *http.Request
   583  	var err error
   584  	var body io.Reader
   585  
   586  	client := &http.Client{}
   587  	switch operation {
   588  	case "POST":
   589  		body := newDeviceInstanceObject(nodeSelector, protocolType, false)
   590  		respBytes, err := json.Marshal(body)
   591  		if err != nil {
   592  			Fatalf("Marshalling body failed: %v", err)
   593  		}
   594  		req, err = http.NewRequest(http.MethodPost, apiserver, bytes.NewBuffer(respBytes))
   595  		req.Header.Set("Content-Type", "application/json")
   596  	case "PATCH":
   597  		body := newDeviceInstanceObject(nodeSelector, protocolType, true)
   598  		respBytes, err := json.Marshal(body)
   599  		if err != nil {
   600  			Fatalf("Marshalling body failed: %v", err)
   601  		}
   602  		req, err = http.NewRequest(http.MethodPatch, apiserver+UID, bytes.NewBuffer(respBytes))
   603  		req.Header.Set("Content-Type", "application/merge-patch+json")
   604  	case "DELETE":
   605  		req, err = http.NewRequest(http.MethodDelete, apiserver+UID, body)
   606  		req.Header.Set("Content-Type", "application/json")
   607  	}
   608  	if err != nil {
   609  		// handle error
   610  		Fatalf("Frame HTTP request failed: %v", err)
   611  		return false, 0
   612  	}
   613  	t := time.Now()
   614  	resp, err := client.Do(req)
   615  	if err != nil {
   616  		// handle error
   617  		Fatalf("HTTP request is failed :%v", err)
   618  		return false, 0
   619  	}
   620  	Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   621  	return true, resp.StatusCode
   622  }
   623  
   624  // newDeviceInstanceObject creates a new device instance object
   625  func newDeviceInstanceObject(nodeSelector string, protocolType string, updated bool) *v1alpha1.Device {
   626  	var deviceInstance v1alpha1.Device
   627  	if updated == false {
   628  		switch protocolType {
   629  		case "bluetooth":
   630  			deviceInstance = NewBluetoothDeviceInstance(nodeSelector)
   631  		case "modbus":
   632  			deviceInstance = NewModbusDeviceInstance(nodeSelector)
   633  		case "led":
   634  			deviceInstance = NewLedDeviceInstance(nodeSelector)
   635  		case "incorrect-instance":
   636  			deviceInstance = IncorrectDeviceInstance()
   637  		}
   638  	} else {
   639  		switch protocolType {
   640  		case "bluetooth":
   641  			deviceInstance = UpdatedBluetoothDeviceInstance(nodeSelector)
   642  		case "modbus":
   643  			deviceInstance = UpdatedModbusDeviceInstance(nodeSelector)
   644  		case "led":
   645  			deviceInstance = UpdatedLedDeviceInstance(nodeSelector)
   646  		case "incorrect-instance":
   647  			deviceInstance = IncorrectDeviceInstance()
   648  		}
   649  	}
   650  	return &deviceInstance
   651  }
   652  
   653  // newDeviceModelObject creates a new device model object
   654  func newDeviceModelObject(protocolType string, updated bool) *v1alpha1.DeviceModel {
   655  	var deviceModel v1alpha1.DeviceModel
   656  	if updated == false {
   657  		switch protocolType {
   658  		case "bluetooth":
   659  			deviceModel = NewBluetoothDeviceModel()
   660  		case "modbus":
   661  			deviceModel = NewModbusDeviceModel()
   662  		case "led":
   663  			deviceModel = NewLedDeviceModel()
   664  		case "incorrect-model":
   665  			deviceModel = IncorrectDeviceModel()
   666  		}
   667  	} else {
   668  		switch protocolType {
   669  		case "bluetooth":
   670  			deviceModel = UpdatedBluetoothDeviceModel()
   671  		case "modbus":
   672  			deviceModel = UpdatedModbusDeviceModel()
   673  		case "led":
   674  			deviceModel = UpdatedLedDeviceModel()
   675  		case "incorrect-model":
   676  			deviceModel = IncorrectDeviceModel()
   677  		}
   678  	}
   679  	return &deviceModel
   680  }
   681  
   682  // GetDeviceModel to get the deviceModel list and verify whether the contents of the device model matches with what is expected
   683  func GetDeviceModel(list *v1alpha1.DeviceModelList, getDeviceModelApi string, expectedDeviceModel *v1alpha1.DeviceModel) ([]v1alpha1.DeviceModel, error) {
   684  	err, resp := SendHttpRequest(http.MethodGet, getDeviceModelApi)
   685  	defer resp.Body.Close()
   686  	contents, err := ioutil.ReadAll(resp.Body)
   687  	if err != nil {
   688  		Fatalf("HTTP Response reading has failed: %v", err)
   689  		return nil, err
   690  	}
   691  	err = json.Unmarshal(contents, &list)
   692  	if err != nil {
   693  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   694  		return nil, err
   695  	}
   696  	if expectedDeviceModel != nil {
   697  		modelExists := false
   698  		for _, deviceModel := range list.Items {
   699  			if expectedDeviceModel.ObjectMeta.Name == deviceModel.ObjectMeta.Name {
   700  				modelExists = true
   701  				if reflect.DeepEqual(expectedDeviceModel.TypeMeta, deviceModel.TypeMeta) == false || expectedDeviceModel.ObjectMeta.Namespace != deviceModel.ObjectMeta.Namespace || reflect.DeepEqual(expectedDeviceModel.Spec, deviceModel.Spec) == false {
   702  					return nil, errors.New("The device model is not matching with what was expected")
   703  				}
   704  			}
   705  		}
   706  		if !modelExists {
   707  			return nil, errors.New("The requested device model is not found")
   708  		}
   709  	}
   710  	return list.Items, nil
   711  }
   712  
   713  // GetDevice to get the device list
   714  func GetDevice(list *v1alpha1.DeviceList, getDeviceApi string, expectedDevice *v1alpha1.Device) ([]v1alpha1.Device, error) {
   715  	err, resp := SendHttpRequest(http.MethodGet, getDeviceApi)
   716  	defer resp.Body.Close()
   717  	contents, err := ioutil.ReadAll(resp.Body)
   718  	if err != nil {
   719  		Fatalf("HTTP Response reading has failed: %v", err)
   720  		return nil, err
   721  	}
   722  	err = json.Unmarshal(contents, &list)
   723  	if err != nil {
   724  		Fatalf("Unmarshal HTTP Response has failed: %v", err)
   725  		return nil, err
   726  	}
   727  	if expectedDevice != nil {
   728  		deviceExists := false
   729  		for _, device := range list.Items {
   730  			if expectedDevice.ObjectMeta.Name == device.ObjectMeta.Name {
   731  				deviceExists = true
   732  				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 {
   733  					return nil, errors.New("The device is not matching with what was expected")
   734  				}
   735  				twinExists := false
   736  				for _, expectedTwin := range expectedDevice.Status.Twins {
   737  					for _, twin := range device.Status.Twins {
   738  						if expectedTwin.PropertyName == twin.PropertyName {
   739  							twinExists = true
   740  							if reflect.DeepEqual(expectedTwin.Desired, twin.Desired) == false {
   741  								return nil, errors.New("Status twin " + twin.PropertyName + " not as expected")
   742  							}
   743  						}
   744  					}
   745  				}
   746  				if !twinExists {
   747  					return nil, errors.New("Status twin(s) not found !!!!")
   748  				}
   749  			}
   750  		}
   751  		if !deviceExists {
   752  			return nil, errors.New("The requested device is not found")
   753  		}
   754  	}
   755  	return list.Items, nil
   756  }
   757  
   758  // MqttClientInit create mqtt client config
   759  func MqttClientInit(server, clientID, username, password string) *MQTT.ClientOptions {
   760  	opts := MQTT.NewClientOptions().AddBroker(server).SetClientID(clientID).SetCleanSession(true)
   761  	if username != "" {
   762  		opts.SetUsername(username)
   763  		if password != "" {
   764  			opts.SetPassword(password)
   765  		}
   766  	}
   767  	tlsConfig := &tls.Config{InsecureSkipVerify: true, ClientAuth: tls.NoClientCert}
   768  	opts.SetTLSConfig(tlsConfig)
   769  	return opts
   770  }
   771  
   772  // MqttConnect function felicitates the MQTT connection
   773  func MqttConnect() error {
   774  	// Initiate the MQTT connection
   775  	ClientOpts = MqttClientInit("tcp://127.0.0.1:1884", "eventbus", "", "")
   776  	Client = MQTT.NewClient(ClientOpts)
   777  	if TokenClient = Client.Connect(); TokenClient.Wait() && TokenClient.Error() != nil {
   778  		return errors.New("client.Connect() Error is %s" + TokenClient.Error().Error())
   779  	}
   780  	return nil
   781  }
   782  
   783  // ChangeTwinValue sends the updated twin value to the edge through the MQTT broker
   784  func ChangeTwinValue(updateMessage DeviceTwinUpdate, deviceID string) error {
   785  	twinUpdateBody, err := json.Marshal(updateMessage)
   786  	if err != nil {
   787  		return errors.New("Error in marshalling: %s" + err.Error())
   788  	}
   789  	deviceTwinUpdate := DeviceETPrefix + deviceID + TwinETUpdateSuffix
   790  	TokenClient = Client.Publish(deviceTwinUpdate, 0, false, twinUpdateBody)
   791  	if TokenClient.Wait() && TokenClient.Error() != nil {
   792  		return errors.New("client.publish() Error in device twin update is %s" + TokenClient.Error().Error())
   793  	}
   794  	return nil
   795  }
   796  
   797  // GetTwin function is used to get the device twin details from the edge
   798  func GetTwin(updateMessage DeviceTwinUpdate, deviceID string) error {
   799  	getTwin := DeviceETPrefix + deviceID + TwinETGetSuffix
   800  	twinUpdateBody, err := json.Marshal(updateMessage)
   801  	if err != nil {
   802  		return errors.New("Error in marshalling: %s" + err.Error())
   803  	}
   804  	TokenClient = Client.Publish(getTwin, 0, false, twinUpdateBody)
   805  	if TokenClient.Wait() && TokenClient.Error() != nil {
   806  		return errors.New("client.publish() Error in device twin get  is: %s " + TokenClient.Error().Error())
   807  	}
   808  	return nil
   809  }
   810  
   811  // subscribe function subscribes  the device twin information through the MQTT broker
   812  func TwinSubscribe(deviceID string) {
   813  	getTwinResult := DeviceETPrefix + deviceID + TwinETGetResultSuffix
   814  	TokenClient = Client.Subscribe(getTwinResult, 0, OnTwinMessageReceived)
   815  	if TokenClient.Wait() && TokenClient.Error() != nil {
   816  		Errorf("subscribe() Error in device twin result get  is %v", TokenClient.Error().Error())
   817  	}
   818  	for {
   819  		twin := DeviceTwinUpdate{}
   820  		err := GetTwin(twin, deviceID)
   821  		if err != nil {
   822  			Errorf("Error in getting device twin: %v", err.Error())
   823  		}
   824  		time.Sleep(1 * time.Second)
   825  		if TwinResult.Twin != nil {
   826  			break
   827  		}
   828  	}
   829  }
   830  
   831  // OnTwinMessageReceived callback function which is called when message is received
   832  func OnTwinMessageReceived(client MQTT.Client, message MQTT.Message) {
   833  	err := json.Unmarshal(message.Payload(), &TwinResult)
   834  	if err != nil {
   835  		Errorf("Error in unmarshalling: %v", err.Error())
   836  	}
   837  }
   838  
   839  // CompareConfigMaps is used to compare 2 config maps
   840  func CompareConfigMaps(configMap, expectedConfigMap v1.ConfigMap) bool {
   841  	if reflect.DeepEqual(expectedConfigMap.TypeMeta, configMap.TypeMeta) == false || expectedConfigMap.ObjectMeta.Namespace != configMap.ObjectMeta.Namespace || reflect.DeepEqual(expectedConfigMap.Data, configMap.Data) == false {
   842  		return false
   843  
   844  	}
   845  	return true
   846  }
   847  
   848  // CompareTwin is used to compare 2 device Twins
   849  func CompareTwin(deviceTwin map[string]*MsgTwin, expectedDeviceTwin map[string]*MsgTwin) bool {
   850  	for key := range expectedDeviceTwin {
   851  		if deviceTwin[key].Metadata != nil && deviceTwin[key].Expected.Value != nil {
   852  			if *deviceTwin[key].Metadata != *expectedDeviceTwin[key].Metadata || *deviceTwin[key].Expected.Value != *expectedDeviceTwin[key].Expected.Value {
   853  				return false
   854  			}
   855  		} else {
   856  			return false
   857  		}
   858  	}
   859  	return true
   860  }