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 }