github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/test/integration/utils/helpers/helpers.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 helpers
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/tls"
    22  	"database/sql"
    23  	"encoding/json"
    24  	"io"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"time"
    28  
    29  	MQTT "github.com/eclipse/paho.mqtt.golang"
    30  	"github.com/onsi/gomega"
    31  	"k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/types"
    34  
    35  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dttype"
    36  	"github.com/kubeedge/kubeedge/edge/test/integration/utils"
    37  	"github.com/kubeedge/kubeedge/edge/test/integration/utils/common"
    38  	"github.com/kubeedge/kubeedge/edge/test/integration/utils/edge"
    39  )
    40  
    41  //DeviceUpdate device update
    42  type DeviceUpdate struct {
    43  	State      string                     `json:"state,omitempty"`
    44  	Attributes map[string]*dttype.MsgAttr `json:"attributes"`
    45  }
    46  
    47  //Device the struct of device
    48  type Device struct {
    49  	ID          string `json:"id,omitempty"`
    50  	Name        string `json:"name,omitempty"`
    51  	Description string `json:"description,omitempty"`
    52  	State       string `json:"state,omitempty"`
    53  	LastOnline  string `json:"last_online,omitempty"`
    54  }
    55  
    56  //Attribute Structure to read data from DB (Should match with the DB-table 'device_attr' schema)
    57  type Attribute struct {
    58  	ID          string `json:"id,omitempty"`
    59  	DeviceID    string `json:"deviceid,omitempty"`
    60  	Name        string `json:"name,omitempty"`
    61  	Description string `json:"description,omitempty"`
    62  	Value       string `json:"value,omitempty"`
    63  	Optional    bool   `json:"optional,omitempty"`
    64  	Type        string `json:"attr_type,omitempty"`
    65  	MetaData    string `json:"metadata,omitempty"`
    66  }
    67  
    68  //Twin Structure to read data from DB (Should match with the DB-table 'device_twin' schema)
    69  type TwinAttribute struct {
    70  	ID           string `json:"id,omitempty"`
    71  	DeviceID     string `json:"deviceid,omitempty"`
    72  	Name         string `json:"name,omitempty"`
    73  	Description  string `json:"description,omitempty"`
    74  	Expected     string `json:"expected,omitempty"`
    75  	Actual       string `json:"actual,omitempty"`
    76  	ExpectedMeta string `json:"expected_meta,omitempty"`
    77  	ActualMeta   string `json:"actual_meta,omitempty"`
    78  	ExpectedVer  string `json:"expected_version,omitempty"`
    79  	ActualVer    string `json:"actual_version,omitempty"`
    80  	Optional     bool   `json:"optional,omitempty"`
    81  	Type         string `json:"attr_type,omitempty"`
    82  	MetaData     string `json:"metadata,omitempty"`
    83  }
    84  
    85  func GenerateDeviceID(deviceSuffix string) string {
    86  	return deviceSuffix + edge.GetRandomString(10)
    87  }
    88  
    89  //Function to Generate Device
    90  func CreateDevice(deviceID string, deviceName string, deviceState string) dttype.Device {
    91  	device := dttype.Device{
    92  		ID:          deviceID,
    93  		Name:        deviceName,
    94  		Description: "IntegrationTest",
    95  		State:       deviceState,
    96  		Attributes:  make(map[string]*dttype.MsgAttr),
    97  		Twin:        make(map[string]*dttype.MsgTwin),
    98  	}
    99  	return device
   100  }
   101  
   102  //Function to add Device attribute to existing device
   103  func AddDeviceAttribute(device dttype.Device, attributeName string, attributeValue string, attributeType string) {
   104  	var optional = true
   105  	var typeMeta = dttype.TypeMetadata{Type: attributeType}
   106  	var attribute = dttype.MsgAttr{Value: attributeValue, Optional: &optional, Metadata: &typeMeta}
   107  	device.Attributes[attributeName] = &attribute
   108  }
   109  
   110  //Function to add Twin attribute to existing device
   111  func AddTwinAttribute(device dttype.Device, attributeName string, attributeValue string, attributeType string) {
   112  	value := attributeValue
   113  	optional := true
   114  	valueMeta := dttype.ValueMetadata{Timestamp: time.Now().Unix()}
   115  	typeMeta := dttype.TypeMetadata{Type: attributeType}
   116  	twinVersion := dttype.TwinVersion{CloudVersion: 1.0, EdgeVersion: 1.0}
   117  	twinValue := dttype.TwinValue{Value: &value, Metadata: &valueMeta}
   118  	msgTwin := dttype.MsgTwin{Expected: &twinValue,
   119  		Actual:          &twinValue,
   120  		Optional:        &optional,
   121  		Metadata:        &typeMeta,
   122  		ExpectedVersion: &twinVersion,
   123  		ActualVersion:   &twinVersion,
   124  	}
   125  
   126  	device.Twin[attributeName] = &msgTwin
   127  }
   128  
   129  //Function to access the edgecore DB and return the device state.
   130  func GetDeviceStateFromDB(deviceID string) string {
   131  	var device Device
   132  	db, err := sql.Open("sqlite3", utils.DBFile)
   133  	if err != nil {
   134  		common.Fatalf("Open Sqlite DB failed : %v", err)
   135  	}
   136  	defer db.Close()
   137  	row, err := db.Query("SELECT * FROM device")
   138  	if err != nil {
   139  		common.Fatalf("Query Sqlite DB failed: %v", err)
   140  	}
   141  	defer row.Close()
   142  	for row.Next() {
   143  		err = row.Scan(&device.ID, &device.Name, &device.Description, &device.State, &device.LastOnline)
   144  		if err != nil {
   145  			common.Fatalf("Failed to scan DB rows: %v", err)
   146  		}
   147  		if string(device.ID) == deviceID {
   148  			break
   149  		}
   150  	}
   151  	return device.State
   152  }
   153  
   154  func GetTwinAttributesFromDB(deviceID string, Name string) TwinAttribute {
   155  	var twinAttribute TwinAttribute
   156  	db, err := sql.Open("sqlite3", utils.DBFile)
   157  	if err != nil {
   158  		common.Fatalf("Open Sqlite DB failed : %v", err)
   159  	}
   160  	defer db.Close()
   161  	row, err := db.Query("SELECT * FROM device_twin")
   162  	defer row.Close()
   163  
   164  	for row.Next() {
   165  		err = row.Scan(&twinAttribute.ID,
   166  			&twinAttribute.DeviceID,
   167  			&twinAttribute.Name,
   168  			&twinAttribute.Description,
   169  			&twinAttribute.Expected,
   170  			&twinAttribute.Actual,
   171  			&twinAttribute.ExpectedMeta,
   172  			&twinAttribute.ActualMeta,
   173  			&twinAttribute.ExpectedVer,
   174  			&twinAttribute.ActualVer,
   175  			&twinAttribute.Optional,
   176  			&twinAttribute.Type,
   177  			&twinAttribute.MetaData)
   178  
   179  		if err != nil {
   180  			common.Fatalf("Failed to scan DB rows: %v", err)
   181  		}
   182  		if string(twinAttribute.DeviceID) == deviceID && twinAttribute.Name == Name {
   183  			break
   184  		}
   185  	}
   186  	return twinAttribute
   187  }
   188  
   189  func GetDeviceAttributesFromDB(deviceID string, Name string) Attribute {
   190  	var attribute Attribute
   191  
   192  	db, err := sql.Open("sqlite3", utils.DBFile)
   193  	if err != nil {
   194  		common.Fatalf("Open Sqlite DB failed : %v", err)
   195  	}
   196  	defer db.Close()
   197  	row, err := db.Query("SELECT * FROM device_attr")
   198  	defer row.Close()
   199  
   200  	for row.Next() {
   201  		err = row.Scan(&attribute.ID, &attribute.DeviceID, &attribute.Name, &attribute.Description, &attribute.Value, &attribute.Optional, &attribute.Type, &attribute.MetaData)
   202  		if err != nil {
   203  			common.Fatalf("Failed to scan DB rows: %v", err)
   204  		}
   205  		if string(attribute.DeviceID) == deviceID && attribute.Name == Name {
   206  			break
   207  		}
   208  	}
   209  	return attribute
   210  }
   211  
   212  // HubclientInit create mqtt client config
   213  func HubClientInit(server, clientID, username, password string) *MQTT.ClientOptions {
   214  	opts := MQTT.NewClientOptions().AddBroker(server).SetClientID(clientID).SetCleanSession(true)
   215  	if username != "" {
   216  		opts.SetUsername(username)
   217  		if password != "" {
   218  			opts.SetPassword(password)
   219  		}
   220  	}
   221  	tlsConfig := &tls.Config{InsecureSkipVerify: true, ClientAuth: tls.NoClientCert}
   222  	opts.SetTLSConfig(tlsConfig)
   223  	return opts
   224  }
   225  
   226  //function to handle device addition and deletion.
   227  func HandleAddAndDeleteDevice(operation, testMgrEndPoint string, device dttype.Device) bool {
   228  	var httpMethod string
   229  	var payload dttype.MembershipUpdate
   230  	switch operation {
   231  	case "PUT":
   232  		httpMethod = http.MethodPut
   233  		payload = dttype.MembershipUpdate{AddDevices: []dttype.Device{
   234  			device,
   235  		}}
   236  	case "DELETE":
   237  		httpMethod = http.MethodDelete
   238  		payload = dttype.MembershipUpdate{RemoveDevices: []dttype.Device{
   239  			device,
   240  		}}
   241  	default:
   242  		common.Fatalf("operation %q is invalid", operation)
   243  		return false
   244  	}
   245  
   246  	respbytes, err := json.Marshal(payload)
   247  	if err != nil {
   248  		common.Fatalf("Payload marshalling failed: %v", err)
   249  		return false
   250  	}
   251  
   252  	req, err := http.NewRequest(httpMethod, testMgrEndPoint, bytes.NewBuffer(respbytes))
   253  	if err != nil {
   254  		// handle error
   255  		common.Fatalf("Frame HTTP request failed: %v", err)
   256  		return false
   257  	}
   258  
   259  	client := &http.Client{}
   260  	req.Header.Set("Content-Type", "application/json; charset=utf-8")
   261  	t := time.Now()
   262  	resp, err := client.Do(req)
   263  
   264  	if err != nil {
   265  		// handle error
   266  		common.Fatalf("HTTP request is failed :%v", err)
   267  		return false
   268  	}
   269  	common.Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   270  	return true
   271  }
   272  
   273  //HandleAddAndDeletePods is function to handle app deployment/delete deployment.
   274  func HandleAddAndDeletePods(operation string, edgedpoint string, UID string, container []v1.Container, restartPolicy v1.RestartPolicy) bool {
   275  	var httpMethod string
   276  	switch operation {
   277  	case "PUT":
   278  		httpMethod = http.MethodPut
   279  	case "DELETE":
   280  		httpMethod = http.MethodDelete
   281  	default:
   282  		common.Fatalf("operation %q is invalid", operation)
   283  		return false
   284  	}
   285  
   286  	payload := &v1.Pod{
   287  		TypeMeta:   metav1.TypeMeta{Kind: "Job", APIVersion: "batch/v1"},
   288  		ObjectMeta: metav1.ObjectMeta{Name: UID, Namespace: metav1.NamespaceDefault, UID: types.UID(UID)},
   289  		Spec:       v1.PodSpec{RestartPolicy: restartPolicy, Containers: container},
   290  	}
   291  	respbytes, err := json.Marshal(payload)
   292  	if err != nil {
   293  		common.Fatalf("Payload marshalling failed: %v", err)
   294  		return false
   295  	}
   296  
   297  	req, err := http.NewRequest(httpMethod, edgedpoint, bytes.NewBuffer(respbytes))
   298  	if err != nil {
   299  		// handle error
   300  		common.Fatalf("Frame HTTP request failed: %v", err)
   301  		return false
   302  	}
   303  	req.Header.Set("Content-Type", "application/json; charset=utf-8")
   304  	t := time.Now()
   305  	client := &http.Client{}
   306  	resp, err := client.Do(req)
   307  	if err != nil {
   308  		// handle error
   309  		common.Fatalf("HTTP request is failed :%v", err)
   310  		return false
   311  	}
   312  	common.Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   313  	return true
   314  }
   315  
   316  //Function to get the pods from Edged
   317  func GetPods(EdgedEndpoint string) (v1.PodList, error) {
   318  	var pods v1.PodList
   319  	var bytes io.Reader
   320  	client := &http.Client{}
   321  	t := time.Now()
   322  	req, err := http.NewRequest(http.MethodGet, EdgedEndpoint, bytes)
   323  	req.Header.Set("Content-Type", "application/json; charset=utf-8")
   324  	if err != nil {
   325  		common.Fatalf("Frame HTTP request failed: %v", err)
   326  		return pods, nil
   327  	}
   328  	resp, err := client.Do(req)
   329  	if err != nil {
   330  		common.Fatalf("Sending HTTP request failed: %v", err)
   331  		return pods, nil
   332  	}
   333  	common.Infof("%s %s %v in %v", req.Method, req.URL, resp.Status, time.Now().Sub(t))
   334  	defer resp.Body.Close()
   335  	contents, err := ioutil.ReadAll(resp.Body)
   336  	if err != nil {
   337  		common.Fatalf("HTTP Response reading has failed: %v", err)
   338  		return pods, nil
   339  	}
   340  	err = json.Unmarshal(contents, &pods)
   341  	if err != nil {
   342  		common.Fatalf("Unmarshal HTTP Response has failed: %v", err)
   343  		return pods, nil
   344  	}
   345  	return pods, nil
   346  }
   347  
   348  //CheckPodRunningState is function to check the Pod state
   349  func CheckPodRunningState(EdgedEndPoint, podname string) {
   350  	gomega.Eventually(func() string {
   351  		var status string
   352  		pods, _ := GetPods(EdgedEndPoint)
   353  		for index := range pods.Items {
   354  			pod := &pods.Items[index]
   355  			if podname == pod.Name {
   356  				status = string(pod.Status.Phase)
   357  				common.Infof("PodName: %s PodStatus: %s", pod.Name, pod.Status.Phase)
   358  			}
   359  		}
   360  		return status
   361  	}, "240s", "2s").Should(gomega.Equal("Running"), "Application Deployment is Unsuccessful, Pod has not come to Running State")
   362  }
   363  
   364  //CheckPodDeletion is function to check pod deletion
   365  func CheckPodDeletion(EdgedEndPoint, UID string) {
   366  	gomega.Eventually(func() bool {
   367  		var IsExist = false
   368  		pods, _ := GetPods(EdgedEndPoint)
   369  		if len(pods.Items) > 0 {
   370  			for index := range pods.Items {
   371  				pod := &pods.Items[index]
   372  				common.Infof("PodName: %s PodStatus: %s", pod.Name, pod.Status.Phase)
   373  				if pod.Name == UID {
   374  					IsExist = true
   375  				}
   376  			}
   377  		}
   378  		return IsExist
   379  	}, "30s", "4s").Should(gomega.Equal(false), "Delete Application deployment is Unsuccessful, Pod has not come to Running State")
   380  }