github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/cloud/pkg/devicecontroller/controller/upstream.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 controller
    18  
    19  import (
    20  	"encoding/json"
    21  	"strconv"
    22  
    23  	"k8s.io/client-go/rest"
    24  	"k8s.io/klog"
    25  
    26  	beehiveContext "github.com/kubeedge/beehive/pkg/core/context"
    27  	"github.com/kubeedge/beehive/pkg/core/model"
    28  	"github.com/kubeedge/kubeedge/cloud/pkg/apis/devices/v1alpha1"
    29  	"github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/config"
    30  	"github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/constants"
    31  	"github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/messagelayer"
    32  	"github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/types"
    33  	"github.com/kubeedge/kubeedge/cloud/pkg/devicecontroller/utils"
    34  )
    35  
    36  // DeviceStatus is structure to patch device status
    37  type DeviceStatus struct {
    38  	Status v1alpha1.DeviceStatus `json:"status"`
    39  }
    40  
    41  const (
    42  	// MergePatchType is patch type
    43  	MergePatchType = "application/merge-patch+json"
    44  	// ResourceTypeDevices is plural of device resource in apiserver
    45  	ResourceTypeDevices = "devices"
    46  )
    47  
    48  // UpstreamController subscribe messages from edge and sync to k8s api server
    49  type UpstreamController struct {
    50  	crdClient    *rest.RESTClient
    51  	messageLayer messagelayer.MessageLayer
    52  	// message channel
    53  	deviceStatusChan chan model.Message
    54  
    55  	// downstream controller to update device status in cache
    56  	dc *DownstreamController
    57  }
    58  
    59  // Start UpstreamController
    60  func (uc *UpstreamController) Start() error {
    61  	klog.Info("Start upstream devicecontroller")
    62  
    63  	uc.deviceStatusChan = make(chan model.Message, config.Config.Buffer.UpdateDeviceStatus)
    64  	go uc.dispatchMessage()
    65  
    66  	for i := 0; i < int(config.Config.Buffer.UpdateDeviceStatus); i++ {
    67  		go uc.updateDeviceStatus()
    68  	}
    69  	return nil
    70  }
    71  
    72  func (uc *UpstreamController) dispatchMessage() {
    73  	for {
    74  		select {
    75  		case <-beehiveContext.Done():
    76  			klog.Info("Stop dispatchMessage")
    77  			return
    78  		default:
    79  		}
    80  		msg, err := uc.messageLayer.Receive()
    81  		if err != nil {
    82  			klog.Warningf("Receive message failed, %s", err)
    83  			continue
    84  		}
    85  
    86  		klog.Infof("Dispatch message: %s", msg.GetID())
    87  
    88  		resourceType, err := messagelayer.GetResourceType(msg.GetResource())
    89  		if err != nil {
    90  			klog.Warningf("Parse message: %s resource type with error: %s", msg.GetID(), err)
    91  			continue
    92  		}
    93  		klog.Infof("Message: %s, resource type is: %s", msg.GetID(), resourceType)
    94  
    95  		switch resourceType {
    96  		case constants.ResourceTypeTwinEdgeUpdated:
    97  			uc.deviceStatusChan <- msg
    98  		default:
    99  			klog.Warningf("Message: %s, with resource type: %s not intended for device controller", msg.GetID(), resourceType)
   100  		}
   101  	}
   102  }
   103  
   104  func (uc *UpstreamController) updateDeviceStatus() {
   105  	for {
   106  		select {
   107  		case <-beehiveContext.Done():
   108  			klog.Info("Stop updateDeviceStatus")
   109  			return
   110  		case msg := <-uc.deviceStatusChan:
   111  			klog.Infof("Message: %s, operation is: %s, and resource is: %s", msg.GetID(), msg.GetOperation(), msg.GetResource())
   112  			msgTwin, err := uc.unmarshalDeviceStatusMessage(msg)
   113  			if err != nil {
   114  				klog.Warningf("Unmarshall failed due to error %v", err)
   115  				continue
   116  			}
   117  			deviceID, err := messagelayer.GetDeviceID(msg.GetResource())
   118  			if err != nil {
   119  				klog.Warning("Failed to get device id")
   120  				continue
   121  			}
   122  			device, ok := uc.dc.deviceManager.Device.Load(deviceID)
   123  			if !ok {
   124  				klog.Warningf("Device %s does not exist in downstream controller", deviceID)
   125  				continue
   126  			}
   127  			cacheDevice, ok := device.(*v1alpha1.Device)
   128  			if !ok {
   129  				klog.Warning("Failed to assert to CacheDevice type")
   130  				continue
   131  			}
   132  			deviceStatus := &DeviceStatus{Status: cacheDevice.Status}
   133  			for twinName, twin := range msgTwin.Twin {
   134  				for i, cacheTwin := range deviceStatus.Status.Twins {
   135  					if twinName == cacheTwin.PropertyName && twin.Actual != nil && twin.Actual.Value != nil {
   136  						reported := v1alpha1.TwinProperty{}
   137  						reported.Value = *twin.Actual.Value
   138  						reported.Metadata = make(map[string]string)
   139  						if twin.Actual.Metadata != nil {
   140  							reported.Metadata["timestamp"] = strconv.FormatInt(twin.Actual.Metadata.Timestamp, 10)
   141  						}
   142  						if twin.Metadata != nil {
   143  							reported.Metadata["type"] = twin.Metadata.Type
   144  						}
   145  						deviceStatus.Status.Twins[i].Reported = reported
   146  						break
   147  					}
   148  				}
   149  			}
   150  
   151  			// Store the status in cache so that when update is received by informer, it is not processed by downstream controller
   152  			cacheDevice.Status = deviceStatus.Status
   153  			uc.dc.deviceManager.Device.Store(deviceID, cacheDevice)
   154  
   155  			body, err := json.Marshal(deviceStatus)
   156  			if err != nil {
   157  				klog.Errorf("Failed to marshal device status %v", deviceStatus)
   158  				continue
   159  			}
   160  			result := uc.crdClient.Patch(MergePatchType).Namespace(cacheDevice.Namespace).Resource(ResourceTypeDevices).Name(deviceID).Body(body).Do()
   161  			if result.Error() != nil {
   162  				klog.Errorf("Failed to patch device status %v of device %v in namespace %v", deviceStatus, deviceID, cacheDevice.Namespace)
   163  				continue
   164  			}
   165  			klog.Infof("Message: %s process successfully", msg.GetID())
   166  		}
   167  	}
   168  }
   169  
   170  func (uc *UpstreamController) unmarshalDeviceStatusMessage(msg model.Message) (*types.DeviceTwinUpdate, error) {
   171  	content := msg.GetContent()
   172  	twinUpdate := &types.DeviceTwinUpdate{}
   173  	var contentData []byte
   174  	var err error
   175  	contentData, ok := content.([]byte)
   176  	if !ok {
   177  		contentData, err = json.Marshal(content)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  	err = json.Unmarshal(contentData, twinUpdate)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	return twinUpdate, nil
   187  }
   188  
   189  // NewUpstreamController create UpstreamController from config
   190  func NewUpstreamController(dc *DownstreamController) (*UpstreamController, error) {
   191  	config, err := utils.KubeConfig()
   192  	if err != nil {
   193  		klog.Warningf("Failed to create kube client: %s", err)
   194  		return nil, err
   195  	}
   196  
   197  	crdcli, err := utils.NewCRDClient(config)
   198  	if err != nil {
   199  		klog.Warningf("Failed to create crd client: %s", err)
   200  		return nil, err
   201  	}
   202  
   203  	uc := &UpstreamController{
   204  		crdClient:    crdcli,
   205  		messageLayer: messagelayer.NewContextMessageLayer(),
   206  		dc:           dc,
   207  	}
   208  	return uc, nil
   209  }