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 }