From 764119ad9da670d91c42d854a43d5f7500feb01e Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 13 Apr 2022 08:48:30 +0200 Subject: [PATCH 01/16] Ability to apply licence --- api/v1alpha1/portalconfig_types.go | 3 + .../bases/storageos.com_portalconfigs.yaml | 4 + controllers/publish_controller.go | 17 +- endpoints/portal.go | 40 +- endpoints/storageos.go | 17 +- go.mod | 1 + main.go | 41 +- pkg/action/action.go | 57 +++ pkg/action/licence/licence.go | 72 +++ pkg/publisher/proto/Makefile | 2 +- pkg/publisher/proto/portal.proto | 16 + pkg/publisher/proto/portal/marshal.go | 15 + pkg/publisher/proto/portal/portal.pb.go | 469 +++++++++++++----- pkg/publisher/publisher.go | 17 +- pkg/publisher/sinks/gcp-iot/iot-core.go | 82 ++- pkg/storageos/client.go | 19 + pkg/utils/signature.go | 33 ++ pkg/utils/signature_test.go | 27 + vendor/modules.txt | 1 + 19 files changed, 773 insertions(+), 160 deletions(-) create mode 100644 pkg/action/action.go create mode 100644 pkg/action/licence/licence.go create mode 100644 pkg/publisher/proto/portal/marshal.go create mode 100644 pkg/utils/signature.go create mode 100644 pkg/utils/signature_test.go diff --git a/api/v1alpha1/portalconfig_types.go b/api/v1alpha1/portalconfig_types.go index 1cc46784..2085fc23 100644 --- a/api/v1alpha1/portalconfig_types.go +++ b/api/v1alpha1/portalconfig_types.go @@ -18,6 +18,9 @@ type IOTCoreConfig struct { // SeverURL is the url of the iot server ServerURL string `json:"serverUrl,omitempty"` + + // SignatureKey is the public part of message signature keys. + SignatureKey string `json:"signatureKey,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/storageos.com_portalconfigs.yaml b/config/crd/bases/storageos.com_portalconfigs.yaml index 40a110a2..fc6dba5c 100644 --- a/config/crd/bases/storageos.com_portalconfigs.yaml +++ b/config/crd/bases/storageos.com_portalconfigs.yaml @@ -92,6 +92,10 @@ spec: serverUrl: description: SeverURL is the url of the iot server type: string + signatureKey: + description: SignatureKey is the public part of message signature + keys. + type: string type: object kind: description: 'Kind is a string value representing the REST resource this diff --git a/controllers/publish_controller.go b/controllers/publish_controller.go index b8269c2e..f5dca59b 100644 --- a/controllers/publish_controller.go +++ b/controllers/publish_controller.go @@ -49,10 +49,10 @@ type Publisher struct { } // NewPublisher returns a new Publisher. -func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client client.Client, scheme *runtime.Scheme, queue workqueue.RateLimitingInterface, recorder record.EventRecorder, cfg storageosv1alpha1.PortalConfig, logger logr.Logger) (*Publisher, error) { +func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client client.Client, scheme *runtime.Scheme, queue workqueue.RateLimitingInterface, recorder record.EventRecorder, cfg storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { logger = logger.WithName("publish_controller") - sink, err := publisher.New(clusterID, privateKeyPem, cfg, logger) + sink, err := publisher.New(clusterID, privateKeyPem, cfg, deviceConfigCallback, logger) if err != nil { return nil, errors.Wrap(err, "unable to initialize new publisher") } @@ -69,15 +69,24 @@ func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client clien }, nil } -// SetupWithManager registers with the controller manager. +// SetupWithManager registers with the controller manager and sends initial statuses. // // Since this is an external controller, we don't need to register the // controller, just add it as a Runnable so that the manager can control startup // and shutdown. -func (p *Publisher) SetupWithManager(mgr ctrl.Manager) error { +func (p *Publisher) SetupWithManager(ctx context.Context, licence portal.CanMarshal, mgr ctrl.Manager) error { if err := p.sink.Init(); err != nil { return errors.Wrap(err, "unable to initialize sink") } + + if err := p.sink.PublishState(ctx, &portal.State{State: map[string]string{"acceptsConfiguration": "true"}}); err != nil { + return errors.Wrap(err, "unable to send initial status") + } + + if err := p.sink.PublishState(ctx, licence); err != nil { + return errors.Wrap(err, "unable to send licence status") + } + return mgr.Add(p) } diff --git a/endpoints/portal.go b/endpoints/portal.go index 459bff8c..f9fb3d9a 100644 --- a/endpoints/portal.go +++ b/endpoints/portal.go @@ -123,21 +123,38 @@ func (e *PortalEndpoint) GetConfig() (*v1alpha1.IOTCoreConfig, error) { return nil, err } - iotConfig := &ConfigurationResponse{} - err = json.Unmarshal(resp, iotConfig) + configResp := &ConfigurationResponse{} + err = json.Unmarshal(resp, configResp) if err != nil { return nil, err } + resp, err = utils.GetHttpContent(e.url+"/setup/signature-key?clusterId="+e.clusterID, headers) + if err != nil { + return nil, err + } + + signResp := &SignatureResponse{} + err = json.Unmarshal(resp, signResp) + if err != nil { + return nil, err + } + pubPEM, err := base64.StdEncoding.DecodeString(signResp.PublicSignKey) + if err != nil { + return nil, err + } + + iotConfig := v1alpha1.IOTCoreConfig{ + Project: configResp.ProjectId, + Registry: configResp.Registry, + Region: configResp.Region, + ServerURL: configResp.MttqURL, + SignatureKey: string(pubPEM), + } + e.logger.V(2).Info("Retrieved iotconfig", "iotconfig", iotConfig) - return &v1alpha1.IOTCoreConfig{ - Project: iotConfig.ProjectId, - Registry: iotConfig.Registry, - Region: iotConfig.Region, - ServerURL: iotConfig.MttqURL, - }, - nil + return &iotConfig, nil } // Stop close endpoint and resources. @@ -225,3 +242,8 @@ type ConfigurationResponse struct { Registry string `json:"registry"` Region string `json:"region"` } + +// SignatureResponse response of GET signture endpoint +type SignatureResponse struct { + PublicSignKey string `json:"publicSignKey"` +} diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 8770c3ea..201801a1 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -3,6 +3,7 @@ package endpoints import ( "github.com/go-logr/logr" "github.com/pkg/errors" + stosapiv2 "github.com/storageos/go-api/v2" "github.com/storageos/portal-manager/pkg/storageos" ) @@ -13,6 +14,7 @@ type StorageOSEndpoint struct { username string password string logger logr.Logger + client *storageos.Client } // GetClusterID returns the cluster ID. @@ -20,16 +22,27 @@ func (e *StorageOSEndpoint) GetClusterID() string { return e.clusterID } +// GetLicence returns the current licence. +func (e *StorageOSEndpoint) GetLicence() (*stosapiv2.Licence, error) { + return e.client.GetLicence() +} + +// UpdateLicence applies given licence. +func (e *StorageOSEndpoint) UpdateLicence(key string) (*stosapiv2.Licence, error) { + return e.client.UpdateLicence(key) +} + // Start start endpoint service and token refresh. func (e *StorageOSEndpoint) Start() error { e.logger.V(3).Info("Start storageos endpoint...") - stosClient, err := storageos.NewClient(e.username, e.password, e.endpoint, e.logger) + var err error + e.client, err = storageos.NewClient(e.username, e.password, e.endpoint, e.logger) if err != nil { return errors.Wrap(err, "unable to init StorageOS client") } - e.clusterID, err = stosClient.GetCluster() + e.clusterID, err = e.client.GetCluster() if err != nil { return errors.Wrap(err, "unable to retrieve cluster ID") } diff --git a/go.mod b/go.mod index c9fc2c11..8fc4c413 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/storageos/portal-manager go 1.16 require ( + github.com/antihax/optional v1.0.0 github.com/eclipse/paho.mqtt.golang v1.3.5 github.com/go-logr/logr v0.4.0 github.com/golang-jwt/jwt/v4 v4.1.0 diff --git a/main.go b/main.go index 423680fd..7fd42058 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. "go.uber.org/zap/zapcore" + "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -34,7 +35,10 @@ import ( "github.com/storageos/portal-manager/controllers" "github.com/storageos/portal-manager/endpoints" "github.com/storageos/portal-manager/managers" + "github.com/storageos/portal-manager/pkg/action" + "github.com/storageos/portal-manager/pkg/action/licence" "github.com/storageos/portal-manager/pkg/handler" + "github.com/storageos/portal-manager/pkg/publisher/proto/portal" "github.com/storageos/portal-manager/pkg/utils" "github.com/storageos/portal-manager/watchers" klog "k8s.io/klog/v2" @@ -160,7 +164,7 @@ func main() { } stosService := fmt.Sprintf(stosServiceTemplate, currentNS) - // TODO watch api-secret change + // TODO watch api-secret change. stosEndpoint := endpoints.NewStorageOSEndpoint(stosUsername, stosPassword, stosService, logger) err = stosEndpoint.Start() if err != nil { @@ -231,17 +235,42 @@ func main() { os.Exit(1) } - publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), mgr.GetClient(), mgr.GetScheme(), publishQueue, mgr.GetEventRecorderFor(EventSourceName), ctrlConfig, logger) + // Start action service. + actionService := action.NewActionService(logger, licence.NewLicenceActionHandler(*stosEndpoint)) + + publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), mgr.GetClient(), mgr.GetScheme(), publishQueue, mgr.GetEventRecorderFor(EventSourceName), ctrlConfig, actionService.Do, logger) if err != nil { setupLogger.Error(err, "unable to create controller", "controller", "publisher") os.Exit(1) } - if err = publisher.SetupWithManager(mgr); err != nil { - setupLogger.Error(err, "unable to initialize controller", "controller", "publisher") + // Start message broker. + currentLicence, err := stosEndpoint.GetLicence() + if err != nil { + setupLogger.Error(err, "unable to fetch licence") os.Exit(1) } + licenceMsg := portal.LicenceState{ + Licence: &portal.Licence{ + ExpireDate: timestamppb.New(currentLicence.ExpiresAt), + ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, + UsedBytes: currentLicence.UsedBytes, + Kind: currentLicence.Kind, + Version: currentLicence.Version, + }, + } + + func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + if err = publisher.SetupWithManager(ctx, &licenceMsg, mgr); err != nil { + setupLogger.Error(err, "unable to initialize controller", "controller", "publisher") + os.Exit(1) + } + }() + // TODO(sc): WatchTypes need to be configurable via the PortalConfig CR. if err = (&controllers.WatchReconciler{ Client: mgr.GetClient(), @@ -269,10 +298,10 @@ func main() { } //+kubebuilder:scaffold:builder - ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) - kubeClientSet := kubernetes.NewForConfigOrDie(restConfig) + ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) + // Watch secrets and restart on change. setupLogger.V(3).Info("Starting secret watcher...") secretWatcher := watchers.NewSecretWatcher(kubeClientSet.CoreV1().Secrets(currentNS), logger) diff --git a/pkg/action/action.go b/pkg/action/action.go new file mode 100644 index 00000000..5a598611 --- /dev/null +++ b/pkg/action/action.go @@ -0,0 +1,57 @@ +package action + +import ( + "github.com/go-logr/logr" + "github.com/storageos/portal-manager/pkg/publisher/proto/portal" +) + +// Action contains the details of action. +type Action struct { + Details interface{} +} + +// ActionHandler is a common interface of Action implementations. +type ActionHandler interface { + Convert([]byte) *Action + Do(Action) (portal.CanMarshal, error) +} + +// ActionService is the facade of action handlers. +type ActionService struct { + logger logr.Logger + registeredActions []ActionHandler +} + +// Do selects the right action and executes it. +func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal) error) { + for _, handler := range as.registeredActions { + action := handler.Convert(rawAction) + if action == nil { + continue + } + + status, err := handler.Do(*action) + if err != nil { + as.logger.Error(err, "unable to execute action", "payload", rawAction) + break + } + + if status != nil { + if err := updateState(status); err != nil { + as.logger.Error(err, "unable to update action status", "status", status) + } + } + } +} + +// NewActionService creates a service. +func NewActionService(logger logr.Logger, handlers ...ActionHandler) *ActionService { + logger = logger.WithName("action_handler") + + as := ActionService{ + logger: logger, + } + as.registeredActions = append(as.registeredActions, handlers...) + + return &as +} diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go new file mode 100644 index 00000000..ca55b723 --- /dev/null +++ b/pkg/action/licence/licence.go @@ -0,0 +1,72 @@ +package licence + +import ( + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/storageos/portal-manager/endpoints" + "github.com/storageos/portal-manager/pkg/action" + "github.com/storageos/portal-manager/pkg/publisher/proto/portal" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type wrapper struct { + Licence licence `json:"licence,omitempty"` +} + +type licence struct { + Value string `json:"value,omitempty"` + LicenceDefinitionId string `json:"licenceDefinitionId,omitempty"` + Kind string `json:"type,omitempty"` + ExpireDate time.Time `json:"expireDate,omitempty"` +} + +// LicenceActionHandler implements a licence apply action. +type LicenceActionHandler struct { + storageOSEndpoint endpoints.StorageOSEndpoint +} + +// IsAccepted accepts action if it is a licence kind. +func (h *LicenceActionHandler) Convert(rawAction []byte) *action.Action { + wrapper := wrapper{} + + err := json.Unmarshal(rawAction, &wrapper) + if err != nil { + return nil + } + + return &action.Action{ + Details: wrapper.Licence, + } +} + +// Do applies licence. +func (h *LicenceActionHandler) Do(action action.Action) (portal.CanMarshal, error) { + details, ok := action.Details.(licence) + if !ok { + return nil, errors.New("details not valid") + } + + currentLicence, err := h.storageOSEndpoint.UpdateLicence(details.Value) + if err != nil { + return nil, errors.Wrap(err, "unable update licence") + } + + return &portal.LicenceState{ + Licence: &portal.Licence{ + ExpireDate: timestamppb.New(currentLicence.ExpiresAt), + ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, + UsedBytes: currentLicence.UsedBytes, + Kind: currentLicence.Kind, + Version: currentLicence.Version, + }, + }, nil +} + +// NewLicenceActionHandler returns a new licence action. +func NewLicenceActionHandler(stosEP endpoints.StorageOSEndpoint) *LicenceActionHandler { + return &LicenceActionHandler{ + storageOSEndpoint: stosEP, + } +} diff --git a/pkg/publisher/proto/Makefile b/pkg/publisher/proto/Makefile index e681c10c..7b222200 100644 --- a/pkg/publisher/proto/Makefile +++ b/pkg/publisher/proto/Makefile @@ -99,7 +99,7 @@ $(PORTAL_GO_TMP): INCLUDE := -I$(GOPATH)/src -I$(HERE)/$(PROTOC_TMP_DIR)/include $(PORTAL_GO_TMP): $(PORTAL_PROTO) | $(PROTOC) $(PROTOC_GEN_GO) @mkdir -p "$(@D)" (cd "$(GOPATH)/src" && \ - $(HERE)/$(PROTOC) $(INCLUDE) --go_out=$(GO_OUT) "$(PORTAL_PKG_ROOT)/$( state = 1; +} + +message Licence { + google.protobuf.Timestamp expireDate = 1; + uint64 clusterCapacityBytes = 2; + uint64 usedBytes = 3; + string kind = 4; + string version = 5; +} + +message LicenceState { + Licence licence = 1; +} \ No newline at end of file diff --git a/pkg/publisher/proto/portal/marshal.go b/pkg/publisher/proto/portal/marshal.go new file mode 100644 index 00000000..e2e9398e --- /dev/null +++ b/pkg/publisher/proto/portal/marshal.go @@ -0,0 +1,15 @@ +package portal + +import "google.golang.org/protobuf/proto" + +type CanMarshal interface { + Marshal() ([]byte, error) +} + +func (s *State) Marshal() ([]byte, error) { + return proto.Marshal(s) +} + +func (l *LicenceState) Marshal() ([]byte, error) { + return proto.Marshal(l) +} diff --git a/pkg/publisher/proto/portal/portal.pb.go b/pkg/publisher/proto/portal/portal.pb.go index c7aff6e7..2fef06d4 100644 --- a/pkg/publisher/proto/portal/portal.pb.go +++ b/pkg/publisher/proto/portal/portal.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 +// protoc-gen-go v1.26.0 // protoc v3.9.1 -// source: github.com/storageos/portal-manager/pkg/publisher/proto/portal.proto +// source: portal.proto package portal import ( - proto "github.com/golang/protobuf/proto" timestamp "github.com/golang/protobuf/ptypes/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -22,10 +21,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - type EventType int32 const ( @@ -65,11 +60,11 @@ func (x EventType) String() string { } func (EventType) Descriptor() protoreflect.EnumDescriptor { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_enumTypes[0].Descriptor() + return file_portal_proto_enumTypes[0].Descriptor() } func (EventType) Type() protoreflect.EnumType { - return &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_enumTypes[0] + return &file_portal_proto_enumTypes[0] } func (x EventType) Number() protoreflect.EnumNumber { @@ -78,7 +73,7 @@ func (x EventType) Number() protoreflect.EnumNumber { // Deprecated: Use EventType.Descriptor instead. func (EventType) EnumDescriptor() ([]byte, []int) { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP(), []int{0} + return file_portal_proto_rawDescGZIP(), []int{0} } type ClusterMeta struct { @@ -93,7 +88,7 @@ type ClusterMeta struct { func (x *ClusterMeta) Reset() { *x = ClusterMeta{} if protoimpl.UnsafeEnabled { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[0] + mi := &file_portal_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -106,7 +101,7 @@ func (x *ClusterMeta) String() string { func (*ClusterMeta) ProtoMessage() {} func (x *ClusterMeta) ProtoReflect() protoreflect.Message { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[0] + mi := &file_portal_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -119,7 +114,7 @@ func (x *ClusterMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ClusterMeta.ProtoReflect.Descriptor instead. func (*ClusterMeta) Descriptor() ([]byte, []int) { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP(), []int{0} + return file_portal_proto_rawDescGZIP(), []int{0} } func (x *ClusterMeta) GetId() string { @@ -149,7 +144,7 @@ type TypeMeta struct { func (x *TypeMeta) Reset() { *x = TypeMeta{} if protoimpl.UnsafeEnabled { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[1] + mi := &file_portal_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -162,7 +157,7 @@ func (x *TypeMeta) String() string { func (*TypeMeta) ProtoMessage() {} func (x *TypeMeta) ProtoReflect() protoreflect.Message { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[1] + mi := &file_portal_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -175,7 +170,7 @@ func (x *TypeMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use TypeMeta.ProtoReflect.Descriptor instead. func (*TypeMeta) Descriptor() ([]byte, []int) { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP(), []int{1} + return file_portal_proto_rawDescGZIP(), []int{1} } func (x *TypeMeta) GetGroup() string { @@ -214,7 +209,7 @@ type ObjectMeta struct { func (x *ObjectMeta) Reset() { *x = ObjectMeta{} if protoimpl.UnsafeEnabled { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[2] + mi := &file_portal_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -227,7 +222,7 @@ func (x *ObjectMeta) String() string { func (*ObjectMeta) ProtoMessage() {} func (x *ObjectMeta) ProtoReflect() protoreflect.Message { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[2] + mi := &file_portal_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -240,7 +235,7 @@ func (x *ObjectMeta) ProtoReflect() protoreflect.Message { // Deprecated: Use ObjectMeta.ProtoReflect.Descriptor instead. func (*ObjectMeta) Descriptor() ([]byte, []int) { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP(), []int{2} + return file_portal_proto_rawDescGZIP(), []int{2} } func (x *ObjectMeta) GetName() string { @@ -294,7 +289,7 @@ type Event struct { func (x *Event) Reset() { *x = Event{} if protoimpl.UnsafeEnabled { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[3] + mi := &file_portal_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -307,7 +302,7 @@ func (x *Event) String() string { func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[3] + mi := &file_portal_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -320,7 +315,7 @@ func (x *Event) ProtoReflect() protoreflect.Message { // Deprecated: Use Event.ProtoReflect.Descriptor instead. func (*Event) Descriptor() ([]byte, []int) { - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP(), []int{3} + return file_portal_proto_rawDescGZIP(), []int{3} } func (x *Event) GetEventType() EventType { @@ -365,60 +360,253 @@ func (x *Event) GetTimestamp() *timestamp.Timestamp { return nil } -var File_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto protoreflect.FileDescriptor - -var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDesc = []byte{ - 0x0a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x6f, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2d, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, - 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, - 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x22, 0x4e, 0x0a, - 0x08, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xf1, 0x01, - 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, - 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, - 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x14, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, +type State struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State map[string]string `protobuf:"bytes,1,rep,name=state,proto3" json:"state,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *State) Reset() { + *x = State{} + if protoimpl.UnsafeEnabled { + mi := &file_portal_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *State) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*State) ProtoMessage() {} + +func (x *State) ProtoReflect() protoreflect.Message { + mi := &file_portal_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use State.ProtoReflect.Descriptor instead. +func (*State) Descriptor() ([]byte, []int) { + return file_portal_proto_rawDescGZIP(), []int{4} +} + +func (x *State) GetState() map[string]string { + if x != nil { + return x.State + } + return nil +} + +type Licence struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExpireDate *timestamp.Timestamp `protobuf:"bytes,1,opt,name=expireDate,proto3" json:"expireDate,omitempty"` + ClusterCapacityBytes uint64 `protobuf:"varint,2,opt,name=clusterCapacityBytes,proto3" json:"clusterCapacityBytes,omitempty"` + UsedBytes uint64 `protobuf:"varint,3,opt,name=usedBytes,proto3" json:"usedBytes,omitempty"` + Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` + Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` +} + +func (x *Licence) Reset() { + *x = Licence{} + if protoimpl.UnsafeEnabled { + mi := &file_portal_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Licence) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Licence) ProtoMessage() {} + +func (x *Licence) ProtoReflect() protoreflect.Message { + mi := &file_portal_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Licence.ProtoReflect.Descriptor instead. +func (*Licence) Descriptor() ([]byte, []int) { + return file_portal_proto_rawDescGZIP(), []int{5} +} + +func (x *Licence) GetExpireDate() *timestamp.Timestamp { + if x != nil { + return x.ExpireDate + } + return nil +} + +func (x *Licence) GetClusterCapacityBytes() uint64 { + if x != nil { + return x.ClusterCapacityBytes + } + return 0 +} + +func (x *Licence) GetUsedBytes() uint64 { + if x != nil { + return x.UsedBytes + } + return 0 +} + +func (x *Licence) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *Licence) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type LicenceState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Licence *Licence `protobuf:"bytes,1,opt,name=licence,proto3" json:"licence,omitempty"` +} + +func (x *LicenceState) Reset() { + *x = LicenceState{} + if protoimpl.UnsafeEnabled { + mi := &file_portal_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LicenceState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LicenceState) ProtoMessage() {} + +func (x *LicenceState) ProtoReflect() protoreflect.Message { + mi := &file_portal_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LicenceState.ProtoReflect.Descriptor instead. +func (*LicenceState) Descriptor() ([]byte, []int) { + return file_portal_proto_rawDescGZIP(), []int{6} +} + +func (x *LicenceState) GetLicence() *Licence { + if x != nil { + return x.Licence + } + return nil +} + +var File_portal_proto protoreflect.FileDescriptor + +var file_portal_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, + 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x0b, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x22, 0x4e, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x14, 0x0a, + 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x22, 0xf1, 0x01, 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x39, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x33, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x07, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, + 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, + 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x0b, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x74, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x1a, 0x38, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc5, 0x01, 0x0a, + 0x07, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x14, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x64, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x75, 0x73, 0x65, + 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x0a, 0x0c, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, + 0x63, 0x65, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, 0x03, @@ -431,49 +619,56 @@ var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_ra } var ( - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescOnce sync.Once - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescData = file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDesc + file_portal_proto_rawDescOnce sync.Once + file_portal_proto_rawDescData = file_portal_proto_rawDesc ) -func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescGZIP() []byte { - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescOnce.Do(func() { - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescData) +func file_portal_proto_rawDescGZIP() []byte { + file_portal_proto_rawDescOnce.Do(func() { + file_portal_proto_rawDescData = protoimpl.X.CompressGZIP(file_portal_proto_rawDescData) }) - return file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDescData + return file_portal_proto_rawDescData } -var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_goTypes = []interface{}{ +var file_portal_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_portal_proto_goTypes = []interface{}{ (EventType)(0), // 0: portal.v1.EventType (*ClusterMeta)(nil), // 1: portal.v1.ClusterMeta (*TypeMeta)(nil), // 2: portal.v1.TypeMeta (*ObjectMeta)(nil), // 3: portal.v1.ObjectMeta (*Event)(nil), // 4: portal.v1.Event - nil, // 5: portal.v1.ObjectMeta.LabelsEntry - (*timestamp.Timestamp)(nil), // 6: google.protobuf.Timestamp -} -var file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_depIdxs = []int32{ - 5, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry - 0, // 1: portal.v1.Event.event_type:type_name -> portal.v1.EventType - 1, // 2: portal.v1.Event.cluster:type_name -> portal.v1.ClusterMeta - 2, // 3: portal.v1.Event.object_type:type_name -> portal.v1.TypeMeta - 3, // 4: portal.v1.Event.object_meta:type_name -> portal.v1.ObjectMeta - 6, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_init() } -func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_init() { - if File_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto != nil { + (*State)(nil), // 5: portal.v1.State + (*Licence)(nil), // 6: portal.v1.Licence + (*LicenceState)(nil), // 7: portal.v1.LicenceState + nil, // 8: portal.v1.ObjectMeta.LabelsEntry + nil, // 9: portal.v1.State.StateEntry + (*timestamp.Timestamp)(nil), // 10: google.protobuf.Timestamp +} +var file_portal_proto_depIdxs = []int32{ + 8, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry + 0, // 1: portal.v1.Event.event_type:type_name -> portal.v1.EventType + 1, // 2: portal.v1.Event.cluster:type_name -> portal.v1.ClusterMeta + 2, // 3: portal.v1.Event.object_type:type_name -> portal.v1.TypeMeta + 3, // 4: portal.v1.Event.object_meta:type_name -> portal.v1.ObjectMeta + 10, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp + 9, // 6: portal.v1.State.state:type_name -> portal.v1.State.StateEntry + 10, // 7: portal.v1.Licence.expireDate:type_name -> google.protobuf.Timestamp + 6, // 8: portal.v1.LicenceState.licence:type_name -> portal.v1.Licence + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_portal_proto_init() } +func file_portal_proto_init() { + if File_portal_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_portal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ClusterMeta); i { case 0: return &v.state @@ -485,7 +680,7 @@ func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_i return nil } } - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_portal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TypeMeta); i { case 0: return &v.state @@ -497,7 +692,7 @@ func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_i return nil } } - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_portal_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ObjectMeta); i { case 0: return &v.state @@ -509,7 +704,7 @@ func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_i return nil } } - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_portal_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Event); i { case 0: return &v.state @@ -521,24 +716,60 @@ func file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_i return nil } } + file_portal_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*State); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_portal_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Licence); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_portal_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LicenceState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDesc, + RawDescriptor: file_portal_proto_rawDesc, NumEnums: 1, - NumMessages: 5, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_goTypes, - DependencyIndexes: file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_depIdxs, - EnumInfos: file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_enumTypes, - MessageInfos: file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_msgTypes, + GoTypes: file_portal_proto_goTypes, + DependencyIndexes: file_portal_proto_depIdxs, + EnumInfos: file_portal_proto_enumTypes, + MessageInfos: file_portal_proto_msgTypes, }.Build() - File_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto = out.File - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_rawDesc = nil - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_goTypes = nil - file_github_com_storageos_portal_manager_pkg_publisher_proto_portal_proto_depIdxs = nil + File_portal_proto = out.File + file_portal_proto_rawDesc = nil + file_portal_proto_goTypes = nil + file_portal_proto_depIdxs = nil } diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 3ccab386..c7ebc995 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -14,7 +14,8 @@ import ( // Sink is implemented by the publisher and all supported backends. type Sink interface { Init() error - Publish(ctx context.Context, event *portal.Event) error + Publish(context.Context, *portal.Event) error + PublishState(context.Context, portal.CanMarshal) error Shutdown() error } @@ -23,12 +24,12 @@ type Publisher struct { logger logr.Logger } -func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, logger logr.Logger) (*Publisher, error) { +func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { logger = logger.WithName("publisher") sinks := make(map[string]Sink) if config.IOTCore != nil { - sink, err := gcp.New(clusterID, privateKeyPem, config.IOTCore, logger) + sink, err := gcp.New(clusterID, privateKeyPem, config.IOTCore, deviceConfigCallback, logger) if err != nil { return nil, errors.Wrap(err, "unable to initialize new IOT core") } @@ -60,6 +61,16 @@ func (p *Publisher) Publish(ctx context.Context, event *portal.Event) error { return nil } +// PublishState a state change request. +func (p *Publisher) PublishState(ctx context.Context, state portal.CanMarshal) error { + for _, sink := range p.sinks { + if err := sink.PublishState(ctx, state); err != nil { + return errors.Wrap(err, "unable to publish state to sink") + } + } + return nil +} + // Shutdown the publisher. func (p *Publisher) Shutdown() error { for _, sink := range p.sinks { diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index 1f065758..a3becae8 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -42,12 +42,13 @@ var ( ) type MQTT struct { - device string - project string - registry string - region string - privateKeyPem []byte - privateKey *rsa.PrivateKey + deviceConfigCallback func([]byte, func(portal.CanMarshal) error) + device string + project string + registry string + region string + privateKeyPem []byte + privateKey *rsa.PrivateKey mu sync.Mutex conn mqtt.Client @@ -56,7 +57,7 @@ type MQTT struct { serverURL string } -func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, logger logr.Logger) (*MQTT, error) { +func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*MQTT, error) { logger = logger.WithName("iot") if config == nil { @@ -83,15 +84,16 @@ func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreC } return &MQTT{ - device: devicePrefix + device, - project: config.Project, - registry: config.Registry, - region: config.Region, - privateKeyPem: privateKeyPem, - privateKey: privateKey, - serverURL: config.ServerURL, - tokenExpired: true, - logger: logger, + deviceConfigCallback: deviceConfigCallback, + device: devicePrefix + device, + project: config.Project, + registry: config.Registry, + region: config.Region, + privateKeyPem: privateKeyPem, + privateKey: privateKey, + serverURL: config.ServerURL, + tokenExpired: true, + logger: logger, }, nil } @@ -103,6 +105,14 @@ func (m *MQTT) PublishTopic() string { return fmt.Sprintf("/devices/%s/%s", m.device, topicType) } +func (m *MQTT) StateTopic() string { + return fmt.Sprintf("/devices/%s/state", m.device) +} + +func (m *MQTT) ConfigTopic() string { + return fmt.Sprintf("/devices/%s/config", m.device) +} + // Init happens periodically before connect. func (m *MQTT) Init() error { return nil @@ -160,6 +170,13 @@ func (m *MQTT) getConnection() (mqtt.Client, error) { if !m.conn.IsConnected() { m.logger.V(3).Info("Reconnecting...") m.attemptConnect() + + m.conn.Unsubscribe(m.ConfigTopic()) + m.conn.Subscribe(m.ConfigTopic(), qos, func(_ mqtt.Client, msg mqtt.Message) { + m.deviceConfigCallback(msg.Payload(), func(cm portal.CanMarshal) error { + return m.PublishState(context.Background(), cm) + }) + }) } return m.conn, nil @@ -233,6 +250,39 @@ func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { return nil } +// PublishState will publish state. +func (m *MQTT) PublishState(ctx context.Context, state portal.CanMarshal) error { + logger := m.logger.WithValues("state", state) + + payload, err := state.Marshal() + if err != nil { + return errors.Wrap(err, "failed to marshal state to protobuf") + } + + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + + conn, err := m.getConnection() + if err != nil { + return errors.Wrap(err, "failed to get connection") + } + + token := conn.Publish(m.StateTopic(), qos, retain, payload) + select { + case <-token.Done(): + if token.Error() != nil { + logger.V(2).Info("Publish state failed") + return errors.Wrap(token.Error(), "publish state failed") + } + case <-ctx.Done(): + logger.V(2).Info("Publish state timeout") + return errors.Wrap(ctx.Err(), "publish state timeout") + } + + logger.V(2).Info("Publish state succeeded") + return nil +} + // Shutdown the sink. func (m *MQTT) Shutdown() error { m.mu.Lock() diff --git a/pkg/storageos/client.go b/pkg/storageos/client.go index cfec8c6e..9b8691fa 100644 --- a/pkg/storageos/client.go +++ b/pkg/storageos/client.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/antihax/optional" "github.com/go-logr/logr" "github.com/pkg/errors" stosapiv2 "github.com/storageos/go-api/v2" @@ -19,6 +20,8 @@ type ControlPlane interface { RefreshJwt(context.Context) (stosapiv2.UserSession, *http.Response, error) AuthenticateUser(context.Context, stosapiv2.AuthUserData) (stosapiv2.UserSession, *http.Response, error) GetCluster(context.Context) (stosapiv2.Cluster, *http.Response, error) + GetLicence(context.Context) (stosapiv2.Licence, *http.Response, error) + UpdateLicence(context.Context, stosapiv2.UpdateLicence, *stosapiv2.UpdateLicenceOpts) (stosapiv2.Licence, *http.Response, error) } // Client provides access to the StorageOS API. @@ -97,6 +100,22 @@ func (c *Client) GetCluster() (string, error) { return cluster.Id, nil } +// GetLicence retrieves StorageOS current licence. +func (c *Client) GetLicence() (*stosapiv2.Licence, error) { + licence, _, err := c.api.GetLicence(c.ctx) + if err != nil { + return nil, errors.Wrap(err, "unable to fetch licence") + } + + return &licence, nil +} + +// UpdateLicence applies given licence. +func (c *Client) UpdateLicence(key string) (*stosapiv2.Licence, error) { + licence, _, err := c.api.UpdateLicence(c.ctx, stosapiv2.UpdateLicence{Key: key}, &stosapiv2.UpdateLicenceOpts{IgnoreVersion: optional.NewBool(true)}) + return &licence, err +} + func (c *Client) initAuthenticatedClient() error { c.logger.V(3).Info("Initialize autheticated client...") diff --git a/pkg/utils/signature.go b/pkg/utils/signature.go new file mode 100644 index 00000000..649f3116 --- /dev/null +++ b/pkg/utils/signature.go @@ -0,0 +1,33 @@ +package utils + +import ( + "crypto" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" +) + +// ValidateDataIntegrity validates data integrity via signature. +func ValidateDataIntegrity(signature string, pubPem []byte, data []byte) error { + sign, err := base64.StdEncoding.DecodeString(signature) + if err != nil { + return err + } + + block, _ := pem.Decode(pubPem) + if block == nil { + return errors.New("failed to decode public PEM") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return err + } + + newHash := crypto.SHA512.New() + newHash.Write(data) + + return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA512, newHash.Sum(nil), sign) +} diff --git a/pkg/utils/signature_test.go b/pkg/utils/signature_test.go new file mode 100644 index 00000000..4e7dac5e --- /dev/null +++ b/pkg/utils/signature_test.go @@ -0,0 +1,27 @@ +package utils + +import "testing" + +func TestValidateDataIntegrity(t *testing.T) { + sign := "S3uFzcuAKdNI7hnlQYKE5EUBoWsQyVxjJTAQZR91+j/h8BoSDR0ZdIASECSjx+FPVAz4aG5iDdYD06mcQu8DWk2esttAeJdy3CugHH6oBJ0ILoCn3c7ZUEXVMAPpEAfXhp/GoZmo3AEySiRVs7c+sB2GR5/j/GeYi3WOLV7otqlKSrH+oCFpzOv/iOdN6HCYZNBfAK+qGqweW/TgOO0QKfjFUN/usLMRbCa7zjA5lIulR1gFge4vUmT4FbBeg3BOSrwUay5h1d82iVk84TZ8VZUm0KNpPDZATA8nh5fFTNFkt+ct4jPZ0RMUkchYOC4c8wh6r5glPNPdK07nWB5JV/G5/YkJ29rVnHWLPt1I7IgZlfKCRbRJ9IUkQ9sdbT6UfMvfrQ2SJJoLnPLJlfYXIpQrQqaG7k5JJAj3ohMDwaxdNZ7WJEmbID+Xxspf/+ZYakmBxhfMBwX6zB9v1JIeqIQmrx5s/lTqecvM9Hju2NXhYl/Ct9i1QgRG+mHItxuWK6QzBnCYo7Hnejc2Dp3rwE1EfwELqNMaoFmshjM6jewMNue5YJGRL3aDaAu9WbbWqJq8AVyS6ces7ZIXaSWMbRWQVNB29L5Uwrf6LA3QuOSs4EQc+YMGNMtvLyJlYGlAJjDu69vZokiCvRQiq6+7sHQ6gqZCDfzg18HOsu1RXEY=" + pubKey := `-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvdxE5r2By7SJMQ71w3wZ +954YohLRMISCrd0jtsGfj6TO6GVkFKXoxYvCYJMzIpTSXGDTfsHRS9ieJ0neqiXs +XoGr3/4ooaiWwNI2U4rSXT8SKPjLVrY3at1Nb8f/DrDGnrCDIv3CKm/FlFjAv5Ee +7g7Cp1/7TAxPaK9WxPLJcvxFKUYNEWrm6c30UNn60C62Gkm0IXNp1hfgE7QlUJHD +51uvTAZ22VxJm1jfD5y0vXv9J9qYGPkhU6C7NhAp6aOoeweNKMHXSTtDkNg2Ps14 +EoMgp/+5nnNMUSC7ofCK9D+iLNJWOJdBHol3Eey6wLvDEC8PBx/skOd2MM9zYxn7 +3oefJcfvmvHylsg8TtxqJ8U1yBP+T3CRYu2Yddzwk/w9COHF0HfAeeu7M6SJcJxl +FOwXwkPW6SNgv3dvE5+Btxxdfzj1AB3zttbvrLwl7CSmEk3nMNenPyhUO+56vUxq +D6eF+l3i+9s+CXvFRDeOLM2ECHOEspVKCkyFhB2iuLLSKlhfqNcgVTgxwSIhcpWF +YGb+GT5gXdFJmVhq/V8weILQ66P+8j4DhZDBhwAcbr1tQHL1B8h32kZhCtimnFN2 +A26XREmXGTzQOrp4knc2nDWXDLdTa+28h6wc69bwaMfcF04GSiYt4kP6gGFFMbgw +3igiF363WLbSEmHkn2MNf3cCAwEAAQ== +-----END PUBLIC KEY-----` + data := `33dca4b2-a074-43a9-9041-c696a4dd1e6d` + + err := ValidateDataIntegrity(sign, []byte(pubKey), []byte(data)) + if err != nil { + t.Errorf("failed to validate data integrity %s", err.Error()) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 54873397..846ffcb7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -14,6 +14,7 @@ github.com/Azure/go-autorest/logger # github.com/Azure/go-autorest/tracing v0.6.0 github.com/Azure/go-autorest/tracing # github.com/antihax/optional v1.0.0 +## explicit github.com/antihax/optional # github.com/beorn7/perks v1.0.1 github.com/beorn7/perks/quantile From 94e6c54b09e12bd93911adc9aac84228e1b9d0de Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 13 Apr 2022 09:03:28 +0200 Subject: [PATCH 02/16] Send error status on failed licence update --- pkg/action/action.go | 1 - pkg/action/licence/licence.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 5a598611..914b3456 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -33,7 +33,6 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal status, err := handler.Do(*action) if err != nil { as.logger.Error(err, "unable to execute action", "payload", rawAction) - break } if status != nil { diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index ca55b723..30445205 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -50,7 +50,7 @@ func (h *LicenceActionHandler) Do(action action.Action) (portal.CanMarshal, erro currentLicence, err := h.storageOSEndpoint.UpdateLicence(details.Value) if err != nil { - return nil, errors.Wrap(err, "unable update licence") + return &portal.State{State: map[string]string{"licence": err.Error()}}, errors.Wrap(err, "unable update licence") } return &portal.LicenceState{ From 0178abdccb3fbadb72dbb8bbacedcfe35d2d3651 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 13 Apr 2022 12:53:39 +0200 Subject: [PATCH 03/16] Send proper status response after action execution --- pkg/action/action.go | 52 ++++-- pkg/action/licence/licence.go | 41 +++-- pkg/publisher/proto/portal.proto | 11 ++ pkg/publisher/proto/portal/marshal.go | 4 + pkg/publisher/proto/portal/portal.pb.go | 207 +++++++++++++++++++++--- 5 files changed, 259 insertions(+), 56 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index 914b3456..da3ad568 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -1,19 +1,27 @@ package action import ( + "encoding/json" + "errors" + "github.com/go-logr/logr" "github.com/storageos/portal-manager/pkg/publisher/proto/portal" ) +// ActionPayload contains the action itself. +type ActionPayload interface{} + // Action contains the details of action. type Action struct { - Details interface{} + Signature string `json:"signature,omitempty"` + ConfigID string `json:"configurationId,omitempty"` + ActionPayload ActionPayload } // ActionHandler is a common interface of Action implementations. type ActionHandler interface { - Convert([]byte) *Action - Do(Action) (portal.CanMarshal, error) + Marshal([]byte, *Action) error + Do(ActionPayload, *portal.ActionState) error } // ActionService is the facade of action handlers. @@ -24,23 +32,45 @@ type ActionService struct { // Do selects the right action and executes it. func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal) error) { + status := portal.ActionState{ + Meta: &portal.ActionMeta{}, + } + + var err error + defer func() { + status.Meta.Success = err == nil + if err != nil { + status.Meta.Message = err.Error() + } + + if err := updateState(&status); err != nil { + as.logger.Error(err, "unable to send action status", "payload", rawAction, "actionError", status.Meta.Message) + } + }() + + action := Action{} + err = json.Unmarshal(rawAction, &action) + if err != nil { + as.logger.Error(err, "unable parse action", "payload", rawAction) + return + } + + status.Meta.ConfigurationId = action.ConfigID + for _, handler := range as.registeredActions { - action := handler.Convert(rawAction) - if action == nil { + if err := handler.Marshal(rawAction, &action); err != nil { continue } - status, err := handler.Do(*action) + err = handler.Do(action.ActionPayload, &status) if err != nil { as.logger.Error(err, "unable to execute action", "payload", rawAction) } - if status != nil { - if err := updateState(status); err != nil { - as.logger.Error(err, "unable to update action status", "status", status) - } - } + return } + + err = errors.New("action handler not found for request") } // NewActionService creates a service. diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index 30445205..4ec06252 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -27,41 +27,40 @@ type LicenceActionHandler struct { storageOSEndpoint endpoints.StorageOSEndpoint } -// IsAccepted accepts action if it is a licence kind. -func (h *LicenceActionHandler) Convert(rawAction []byte) *action.Action { +// Marshal tries to parse raw action into action details. +func (h *LicenceActionHandler) Marshal(rawAction []byte, action *action.Action) error { wrapper := wrapper{} - err := json.Unmarshal(rawAction, &wrapper) if err != nil { - return nil + return err } - return &action.Action{ - Details: wrapper.Licence, - } + action.ActionPayload = wrapper.Licence + + return nil } // Do applies licence. -func (h *LicenceActionHandler) Do(action action.Action) (portal.CanMarshal, error) { - details, ok := action.Details.(licence) +func (h *LicenceActionHandler) Do(payload action.ActionPayload, state *portal.ActionState) error { + desiredLicence, ok := payload.(licence) if !ok { - return nil, errors.New("details not valid") + return errors.New("unable to cast payload to licence") } - currentLicence, err := h.storageOSEndpoint.UpdateLicence(details.Value) + currentLicence, err := h.storageOSEndpoint.UpdateLicence(desiredLicence.Value) if err != nil { - return &portal.State{State: map[string]string{"licence": err.Error()}}, errors.Wrap(err, "unable update licence") + return errors.Wrap(err, "unable update licence") + } + + state.Licence = &portal.Licence{ + ExpireDate: timestamppb.New(currentLicence.ExpiresAt), + ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, + UsedBytes: currentLicence.UsedBytes, + Kind: currentLicence.Kind, + Version: currentLicence.Version, } - return &portal.LicenceState{ - Licence: &portal.Licence{ - ExpireDate: timestamppb.New(currentLicence.ExpiresAt), - ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, - UsedBytes: currentLicence.UsedBytes, - Kind: currentLicence.Kind, - Version: currentLicence.Version, - }, - }, nil + return nil } // NewLicenceActionHandler returns a new licence action. diff --git a/pkg/publisher/proto/portal.proto b/pkg/publisher/proto/portal.proto index 49f19db3..c802a21b 100644 --- a/pkg/publisher/proto/portal.proto +++ b/pkg/publisher/proto/portal.proto @@ -55,4 +55,15 @@ message Licence { message LicenceState { Licence licence = 1; +} + +message ActionMeta { + string configurationId = 1; + bool success = 2; + string message = 3; +} + +message ActionState { + ActionMeta meta = 1; + Licence licence = 2; } \ No newline at end of file diff --git a/pkg/publisher/proto/portal/marshal.go b/pkg/publisher/proto/portal/marshal.go index e2e9398e..3142736a 100644 --- a/pkg/publisher/proto/portal/marshal.go +++ b/pkg/publisher/proto/portal/marshal.go @@ -13,3 +13,7 @@ func (s *State) Marshal() ([]byte, error) { func (l *LicenceState) Marshal() ([]byte, error) { return proto.Marshal(l) } + +func (a *ActionState) Marshal() ([]byte, error) { + return proto.Marshal(a) +} diff --git a/pkg/publisher/proto/portal/portal.pb.go b/pkg/publisher/proto/portal/portal.pb.go index 2fef06d4..b3cd7faa 100644 --- a/pkg/publisher/proto/portal/portal.pb.go +++ b/pkg/publisher/proto/portal/portal.pb.go @@ -533,6 +533,124 @@ func (x *LicenceState) GetLicence() *Licence { return nil } +type ActionMeta struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ConfigurationId string `protobuf:"bytes,1,opt,name=configurationId,proto3" json:"configurationId,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *ActionMeta) Reset() { + *x = ActionMeta{} + if protoimpl.UnsafeEnabled { + mi := &file_portal_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActionMeta) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActionMeta) ProtoMessage() {} + +func (x *ActionMeta) ProtoReflect() protoreflect.Message { + mi := &file_portal_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActionMeta.ProtoReflect.Descriptor instead. +func (*ActionMeta) Descriptor() ([]byte, []int) { + return file_portal_proto_rawDescGZIP(), []int{7} +} + +func (x *ActionMeta) GetConfigurationId() string { + if x != nil { + return x.ConfigurationId + } + return "" +} + +func (x *ActionMeta) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ActionMeta) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ActionState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Meta *ActionMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Licence *Licence `protobuf:"bytes,2,opt,name=licence,proto3" json:"licence,omitempty"` +} + +func (x *ActionState) Reset() { + *x = ActionState{} + if protoimpl.UnsafeEnabled { + mi := &file_portal_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActionState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActionState) ProtoMessage() {} + +func (x *ActionState) ProtoReflect() protoreflect.Message { + mi := &file_portal_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActionState.ProtoReflect.Descriptor instead. +func (*ActionState) Descriptor() ([]byte, []int) { + return file_portal_proto_rawDescGZIP(), []int{8} +} + +func (x *ActionState) GetMeta() *ActionMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *ActionState) GetLicence() *Licence { + if x != nil { + return x.Licence + } + return nil +} + var File_portal_proto protoreflect.FileDescriptor var file_portal_proto_rawDesc = []byte{ @@ -606,16 +724,29 @@ var file_portal_proto_rawDesc = []byte{ 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, - 0x63, 0x65, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, 0x03, - 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x10, 0x04, 0x42, 0x40, 0x5a, - 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x6f, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2d, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, - 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x65, 0x22, 0x6a, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, + 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x66, + 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, + 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, + 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x72, 0x74, + 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, + 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x10, + 0x04, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x6f, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, + 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x6f, 0x72, + 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -631,7 +762,7 @@ func file_portal_proto_rawDescGZIP() []byte { } var file_portal_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_portal_proto_goTypes = []interface{}{ (EventType)(0), // 0: portal.v1.EventType (*ClusterMeta)(nil), // 1: portal.v1.ClusterMeta @@ -641,25 +772,29 @@ var file_portal_proto_goTypes = []interface{}{ (*State)(nil), // 5: portal.v1.State (*Licence)(nil), // 6: portal.v1.Licence (*LicenceState)(nil), // 7: portal.v1.LicenceState - nil, // 8: portal.v1.ObjectMeta.LabelsEntry - nil, // 9: portal.v1.State.StateEntry - (*timestamp.Timestamp)(nil), // 10: google.protobuf.Timestamp + (*ActionMeta)(nil), // 8: portal.v1.ActionMeta + (*ActionState)(nil), // 9: portal.v1.ActionState + nil, // 10: portal.v1.ObjectMeta.LabelsEntry + nil, // 11: portal.v1.State.StateEntry + (*timestamp.Timestamp)(nil), // 12: google.protobuf.Timestamp } var file_portal_proto_depIdxs = []int32{ - 8, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry + 10, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry 0, // 1: portal.v1.Event.event_type:type_name -> portal.v1.EventType 1, // 2: portal.v1.Event.cluster:type_name -> portal.v1.ClusterMeta 2, // 3: portal.v1.Event.object_type:type_name -> portal.v1.TypeMeta 3, // 4: portal.v1.Event.object_meta:type_name -> portal.v1.ObjectMeta - 10, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp - 9, // 6: portal.v1.State.state:type_name -> portal.v1.State.StateEntry - 10, // 7: portal.v1.Licence.expireDate:type_name -> google.protobuf.Timestamp + 12, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp + 11, // 6: portal.v1.State.state:type_name -> portal.v1.State.StateEntry + 12, // 7: portal.v1.Licence.expireDate:type_name -> google.protobuf.Timestamp 6, // 8: portal.v1.LicenceState.licence:type_name -> portal.v1.Licence - 9, // [9:9] is the sub-list for method output_type - 9, // [9:9] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 8, // 9: portal.v1.ActionState.meta:type_name -> portal.v1.ActionMeta + 6, // 10: portal.v1.ActionState.licence:type_name -> portal.v1.Licence + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_portal_proto_init() } @@ -752,6 +887,30 @@ func file_portal_proto_init() { return nil } } + file_portal_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActionMeta); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_portal_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActionState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -759,7 +918,7 @@ func file_portal_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_portal_proto_rawDesc, NumEnums: 1, - NumMessages: 9, + NumMessages: 11, NumExtensions: 0, NumServices: 0, }, From 7cabfd9b4a823eeac68c1fa123c9ecdf6abc3a8d Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 13 Apr 2022 13:51:22 +0200 Subject: [PATCH 04/16] Verify data integrity of action --- controllers/publish_controller.go | 12 ++--- endpoints/portal.go | 4 +- endpoints/storageos.go | 4 +- main.go | 2 +- pkg/action/action.go | 61 ++++++++++++++++++------- pkg/action/licence/licence.go | 11 +++-- pkg/handler/event_queuer.go | 4 +- pkg/publisher/publisher.go | 4 +- pkg/publisher/sinks/gcp-iot/iot-core.go | 12 +++-- pkg/storageos/client.go | 16 +++---- watchers/configmap.go | 2 +- watchers/secret.go | 6 +-- 12 files changed, 80 insertions(+), 58 deletions(-) diff --git a/controllers/publish_controller.go b/controllers/publish_controller.go index f5dca59b..80d19612 100644 --- a/controllers/publish_controller.go +++ b/controllers/publish_controller.go @@ -50,8 +50,6 @@ type Publisher struct { // NewPublisher returns a new Publisher. func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client client.Client, scheme *runtime.Scheme, queue workqueue.RateLimitingInterface, recorder record.EventRecorder, cfg storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { - logger = logger.WithName("publish_controller") - sink, err := publisher.New(clusterID, privateKeyPem, cfg, deviceConfigCallback, logger) if err != nil { return nil, errors.Wrap(err, "unable to initialize new publisher") @@ -65,11 +63,11 @@ func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client clien sink: sink, tenant: tenantID, cluster: clusterID, - logger: logger, + logger: logger.WithName("publish_controller"), }, nil } -// SetupWithManager registers with the controller manager and sends initial statuses. +// SetupWithManager registers with the controller manager and sends initial states. // // Since this is an external controller, we don't need to register the // controller, just add it as a Runnable so that the manager can control startup @@ -79,12 +77,14 @@ func (p *Publisher) SetupWithManager(ctx context.Context, licence portal.CanMars return errors.Wrap(err, "unable to initialize sink") } + p.logger.V(2).Info("Sending acceptsConfiguration state") if err := p.sink.PublishState(ctx, &portal.State{State: map[string]string{"acceptsConfiguration": "true"}}); err != nil { - return errors.Wrap(err, "unable to send initial status") + return errors.Wrap(err, "unable to send initial state") } + p.logger.V(2).Info("Sending current licence state") if err := p.sink.PublishState(ctx, licence); err != nil { - return errors.Wrap(err, "unable to send licence status") + return errors.Wrap(err, "unable to send licence state") } return mgr.Add(p) diff --git a/endpoints/portal.go b/endpoints/portal.go index f9fb3d9a..535409cb 100644 --- a/endpoints/portal.go +++ b/endpoints/portal.go @@ -195,8 +195,6 @@ func (e *PortalEndpoint) refreshToken() (int, error) { // NewPortalEndpoint contructs a new Portal endpoint. func NewPortalEndpoint(clusterID, url string, clientID string, password string, logger logr.Logger) *PortalEndpoint { - logger = logger.WithName("portal_endpoint") - return &PortalEndpoint{ clusterID: clusterID, url: url, @@ -205,7 +203,7 @@ func NewPortalEndpoint(clusterID, url string, clientID string, password string, tokenLock: make(chan bool, 1), refreshTokenDelay: refreshTokenDefaultDelay, refreshStopchan: make(chan bool), - logger: logger, + logger: logger.WithName("portal_endpoint"), } } diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 201801a1..5977e294 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -54,12 +54,10 @@ func (e *StorageOSEndpoint) Start() error { // NewStorageOSEndpoint contructs a new StorageOS endpoint. func NewStorageOSEndpoint(username, password, endpoint string, logger logr.Logger) *StorageOSEndpoint { - logger = logger.WithName("storageos_endpoint") - return &StorageOSEndpoint{ endpoint: endpoint, username: username, password: password, - logger: logger, + logger: logger.WithName("storageos_endpoint"), } } diff --git a/main.go b/main.go index 7fd42058..7771da21 100644 --- a/main.go +++ b/main.go @@ -236,7 +236,7 @@ func main() { } // Start action service. - actionService := action.NewActionService(logger, licence.NewLicenceActionHandler(*stosEndpoint)) + actionService := action.NewActionService(logger, licence.NewLicenceActionHandler(*stosEndpoint, logger)) publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), mgr.GetClient(), mgr.GetScheme(), publishQueue, mgr.GetEventRecorderFor(EventSourceName), ctrlConfig, actionService.Do, logger) if err != nil { diff --git a/pkg/action/action.go b/pkg/action/action.go index da3ad568..a9b5f01d 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -1,11 +1,14 @@ package action import ( + "encoding/base64" "encoding/json" "errors" + "fmt" "github.com/go-logr/logr" "github.com/storageos/portal-manager/pkg/publisher/proto/portal" + "github.com/storageos/portal-manager/pkg/utils" ) // ActionPayload contains the action itself. @@ -13,8 +16,10 @@ type ActionPayload interface{} // Action contains the details of action. type Action struct { - Signature string `json:"signature,omitempty"` - ConfigID string `json:"configurationId,omitempty"` + Signature string `json:"signature,omitempty"` + ConfigID string `json:"configurationId,omitempty"` + Body string `json:"body,omitempty"` + ActionPayload ActionPayload } @@ -26,45 +31,68 @@ type ActionHandler interface { // ActionService is the facade of action handlers. type ActionService struct { - logger logr.Logger + signatureKey []byte registeredActions []ActionHandler + logger logr.Logger } // Do selects the right action and executes it. func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal) error) { - status := portal.ActionState{ + logger := as.logger.WithValues("payload", string(rawAction)) + logger.V(2).Info("Action received") + + state := portal.ActionState{ Meta: &portal.ActionMeta{}, } var err error defer func() { - status.Meta.Success = err == nil + state.Meta.Success = err == nil if err != nil { - status.Meta.Message = err.Error() + state.Meta.Message = err.Error() } - if err := updateState(&status); err != nil { - as.logger.Error(err, "unable to send action status", "payload", rawAction, "actionError", status.Meta.Message) + logger.V(3).Info("Updating state") + if err := updateState(&state); err != nil { + logger.Error(err, "unable to send action state", "actionError", state.Meta.Message) } }() action := Action{} err = json.Unmarshal(rawAction, &action) if err != nil { - as.logger.Error(err, "unable parse action", "payload", rawAction) + logger.Error(err, "unable parse action") return } - status.Meta.ConfigurationId = action.ConfigID + state.Meta.ConfigurationId = action.ConfigID + logger = logger.WithValues("config", action.ConfigID) + + logger.V(3).Info("Validate data integrity") + err = utils.ValidateDataIntegrity(action.Signature, as.signatureKey, []byte(fmt.Sprintf("%s%s", action.ConfigID, action.Body))) + if err != nil { + logger.Error(err, "unable validate data integrity") + return + } + + var rawBody []byte + rawBody, err = base64.StdEncoding.DecodeString(action.Body) + if err != nil { + logger.Error(err, "unable decode action body") + return + } for _, handler := range as.registeredActions { - if err := handler.Marshal(rawAction, &action); err != nil { + if err := handler.Marshal(rawBody, &action); err != nil { continue } - err = handler.Do(action.ActionPayload, &status) + logger.V(3).Info("Executing action") + err = handler.Do(action.ActionPayload, &state) if err != nil { - as.logger.Error(err, "unable to execute action", "payload", rawAction) + logger.Error(err, "unable to execute action") + } else { + logger.V(2).Info("Action succeeded") } return @@ -74,11 +102,10 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal } // NewActionService creates a service. -func NewActionService(logger logr.Logger, handlers ...ActionHandler) *ActionService { - logger = logger.WithName("action_handler") - +func NewActionService(logger logr.Logger, signatureKey []byte, handlers ...ActionHandler) *ActionService { as := ActionService{ - logger: logger, + signatureKey: signatureKey, + logger: logger.WithName("action_handler"), } as.registeredActions = append(as.registeredActions, handlers...) diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index 4ec06252..978657d9 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -4,6 +4,7 @@ import ( "encoding/json" "time" + "github.com/go-logr/logr" "github.com/pkg/errors" "github.com/storageos/portal-manager/endpoints" "github.com/storageos/portal-manager/pkg/action" @@ -25,12 +26,13 @@ type licence struct { // LicenceActionHandler implements a licence apply action. type LicenceActionHandler struct { storageOSEndpoint endpoints.StorageOSEndpoint + logger logr.Logger } // Marshal tries to parse raw action into action details. -func (h *LicenceActionHandler) Marshal(rawAction []byte, action *action.Action) error { +func (h *LicenceActionHandler) Marshal(actionBody []byte, action *action.Action) error { wrapper := wrapper{} - err := json.Unmarshal(rawAction, &wrapper) + err := json.Unmarshal(actionBody, &wrapper) if err != nil { return err } @@ -47,6 +49,8 @@ func (h *LicenceActionHandler) Do(payload action.ActionPayload, state *portal.Ac return errors.New("unable to cast payload to licence") } + h.logger.V(3).Info("Updating license", "kind", desiredLicence.Kind, "expires", desiredLicence.ExpireDate.String()) + currentLicence, err := h.storageOSEndpoint.UpdateLicence(desiredLicence.Value) if err != nil { return errors.Wrap(err, "unable update licence") @@ -64,8 +68,9 @@ func (h *LicenceActionHandler) Do(payload action.ActionPayload, state *portal.Ac } // NewLicenceActionHandler returns a new licence action. -func NewLicenceActionHandler(stosEP endpoints.StorageOSEndpoint) *LicenceActionHandler { +func NewLicenceActionHandler(stosEP endpoints.StorageOSEndpoint, logger logr.Logger) *LicenceActionHandler { return &LicenceActionHandler{ storageOSEndpoint: stosEP, + logger: logger.WithName("licence_handler"), } } diff --git a/pkg/handler/event_queuer.go b/pkg/handler/event_queuer.go index a49fa907..e328d684 100644 --- a/pkg/handler/event_queuer.go +++ b/pkg/handler/event_queuer.go @@ -29,11 +29,9 @@ type Event struct { var _ handler.EventHandler = &EventQueuer{} func NewEventQueuer(queue workqueue.RateLimitingInterface, logger logr.Logger) *EventQueuer { - logger = logger.WithName("event_queuer") - return &EventQueuer{ queue: queue, - logger: logger, + logger: logger.WithName("event_queuer"), } } diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index c7ebc995..5bff74e9 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -25,8 +25,6 @@ type Publisher struct { } func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { - logger = logger.WithName("publisher") - sinks := make(map[string]Sink) if config.IOTCore != nil { sink, err := gcp.New(clusterID, privateKeyPem, config.IOTCore, deviceConfigCallback, logger) @@ -37,7 +35,7 @@ func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.Portal } return &Publisher{ sinks: sinks, - logger: logger, + logger: logger.WithName("publisher"), }, nil } diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index a3becae8..3843b953 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -58,8 +58,6 @@ type MQTT struct { } func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*MQTT, error) { - logger = logger.WithName("iot") - if config == nil { return nil, ErrNoConfig } @@ -93,7 +91,7 @@ func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreC privateKey: privateKey, serverURL: config.ServerURL, tokenExpired: true, - logger: logger, + logger: logger.WithName("iot"), }, nil } @@ -171,8 +169,12 @@ func (m *MQTT) getConnection() (mqtt.Client, error) { m.logger.V(3).Info("Reconnecting...") m.attemptConnect() + m.logger.V(3).Info("Manage device config subscription...") + m.conn.Unsubscribe(m.ConfigTopic()) m.conn.Subscribe(m.ConfigTopic(), qos, func(_ mqtt.Client, msg mqtt.Message) { + m.logger.V(5).Info("Event received", "message", msg) + m.deviceConfigCallback(msg.Payload(), func(cm portal.CanMarshal) error { return m.PublishState(context.Background(), cm) }) @@ -252,13 +254,13 @@ func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { // PublishState will publish state. func (m *MQTT) PublishState(ctx context.Context, state portal.CanMarshal) error { - logger := m.logger.WithValues("state", state) - payload, err := state.Marshal() if err != nil { return errors.Wrap(err, "failed to marshal state to protobuf") } + logger := m.logger.WithValues("state", state, "payload", string(payload)) + ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() diff --git a/pkg/storageos/client.go b/pkg/storageos/client.go index 9b8691fa..27998119 100644 --- a/pkg/storageos/client.go +++ b/pkg/storageos/client.go @@ -69,14 +69,12 @@ var ( // authentication token must be refreshed periodically using // Refresh(). func NewClient(username, password, endpoint string, logger logr.Logger) (*Client, error) { - logger = logger.WithName("storageos_client") - client := Client{ endpoint: endpoint, username: username, password: password, transport: http.DefaultTransport, - logger: logger, + logger: logger.WithName("storageos_client"), } if !strings.Contains(client.endpoint, "://") { @@ -171,7 +169,7 @@ func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time // Refresh will fail if the token has already expired. _, resp, err := c.api.RefreshJwt(c.ctx) if err != nil { - c.logger.V(2).Info("failed to refresh storageos api credentials, reauthenticating immediately", "error", err) + c.logger.V(2).Info("Failed to refresh storageos api credentials, reauthenticating immediately", "error", err) // Reset the api client directly on refresh errors. We can't // send to the reset channel as it may cause a deadlock. We may // also have a reset queued in the channel but that's ok, we'll @@ -186,26 +184,26 @@ func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time continue } c.ctx = newCtx - c.logger.V(3).Info("reset storageos api client") + c.logger.V(3).Info("Reset storageos api client") continue } defer resp.Body.Close() token := respAuthToken(resp) if token == "" { - c.logger.V(2).Info("failed to refresh storageos api credentials", "error", ErrNoAuthToken) + c.logger.V(2).Info("Failed to refresh storageos api credentials", "error", ErrNoAuthToken) continue } c.ctx = context.WithValue(c.ctx, stosapiv2.ContextAccessToken, token) - c.logger.V(3).Info("refreshed storageos api token") + c.logger.V(3).Info("Refreshed storageos api token") case <-reset: - c.logger.V(5).Info("processing request to reset storageos api client") + c.logger.V(5).Info("Processing request to reset storageos api client") newCtx, err := c.resetClient() if err != nil { c.logger.Error(err, "failed to reset storageos api client") continue } c.ctx = newCtx - c.logger.V(5).Info("reset storageos api client") + c.logger.V(5).Info("Reset storageos api client") case <-ctx.Done(): // Clean shutdown. return ctx.Err() diff --git a/watchers/configmap.go b/watchers/configmap.go index e03bd82a..95a00094 100644 --- a/watchers/configmap.go +++ b/watchers/configmap.go @@ -75,7 +75,7 @@ func (w *ConfigMapWatcher) watchChange(ctx context.Context, waitPeriod int) erro defer watcher.Stop() if waitPeriod != 0 { - w.logger.Info("wait before reading channel", "period", waitPeriod) + w.logger.Info("Wait before reading channel", "period", waitPeriod) time.Sleep(time.Second * time.Duration(waitPeriod)) } diff --git a/watchers/secret.go b/watchers/secret.go index 0fa5873c..0170ecc8 100644 --- a/watchers/secret.go +++ b/watchers/secret.go @@ -69,7 +69,7 @@ func (w *SecretWatcher) watchChange(ctx context.Context, waitDuration time.Durat defer watcher.Stop() if waitDuration != 0 { - w.logger.Info("wait before reading channel", "period", waitDuration) + w.logger.Info("Wait before reading channel", "period", waitDuration) time.Sleep(waitDuration) } @@ -95,10 +95,8 @@ func (w *SecretWatcher) watchChange(ctx context.Context, waitDuration time.Durat // NewSecretWatcher consructs a new secret watcher. func NewSecretWatcher(client tcorev1.SecretInterface, logger logr.Logger) *SecretWatcher { - logger = logger.WithName("secret_watcher") - return &SecretWatcher{ client: client, - logger: logger, + logger: logger.WithName("secret_watcher"), } } From 2d021e74d10c22786000a7ca00c8d09157954513 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Mon, 25 Apr 2022 09:10:09 +0200 Subject: [PATCH 05/16] ezaz --- main.go | 2 +- pkg/action/action.go | 30 ++++++++++++++++++------------ pkg/action/licence/licence.go | 7 ++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index 7771da21..5ee8e785 100644 --- a/main.go +++ b/main.go @@ -236,7 +236,7 @@ func main() { } // Start action service. - actionService := action.NewActionService(logger, licence.NewLicenceActionHandler(*stosEndpoint, logger)) + actionService := action.NewActionService(logger, []byte(iotConfig.SignatureKey), licence.NewLicenceActionHandler(*stosEndpoint, logger)) publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), mgr.GetClient(), mgr.GetScheme(), publishQueue, mgr.GetEventRecorderFor(EventSourceName), ctrlConfig, actionService.Do, logger) if err != nil { diff --git a/pkg/action/action.go b/pkg/action/action.go index a9b5f01d..0539268b 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "encoding/json" "errors" - "fmt" "github.com/go-logr/logr" "github.com/storageos/portal-manager/pkg/publisher/proto/portal" @@ -17,15 +16,18 @@ type ActionPayload interface{} // Action contains the details of action. type Action struct { Signature string `json:"signature,omitempty"` - ConfigID string `json:"configurationId,omitempty"` Body string `json:"body,omitempty"` ActionPayload ActionPayload } +type body struct { + ConfigID string `json:"configurationId,omitempty"` +} + // ActionHandler is a common interface of Action implementations. type ActionHandler interface { - Marshal([]byte, *Action) error + UnMarshal([]byte, *Action) error Do(ActionPayload, *portal.ActionState) error } @@ -59,17 +61,13 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal }() action := Action{} - err = json.Unmarshal(rawAction, &action) - if err != nil { + if err = json.Unmarshal(rawAction, &action); err != nil { logger.Error(err, "unable parse action") return } - state.Meta.ConfigurationId = action.ConfigID - logger = logger.WithValues("config", action.ConfigID) - logger.V(3).Info("Validate data integrity") - err = utils.ValidateDataIntegrity(action.Signature, as.signatureKey, []byte(fmt.Sprintf("%s%s", action.ConfigID, action.Body))) + err = utils.ValidateDataIntegrity(action.Signature, as.signatureKey, []byte(action.Body)) if err != nil { logger.Error(err, "unable validate data integrity") return @@ -82,14 +80,22 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal return } + body := body{} + if err = json.Unmarshal(rawBody, &body); err != nil { + logger.Error(err, "unable parse action body") + return + } + + state.Meta.ConfigurationId = body.ConfigID + logger = logger.WithValues("config", body.ConfigID) + for _, handler := range as.registeredActions { - if err := handler.Marshal(rawBody, &action); err != nil { + if err = handler.UnMarshal(rawBody, &action); err != nil { continue } logger.V(3).Info("Executing action") - err = handler.Do(action.ActionPayload, &state) - if err != nil { + if err = handler.Do(action.ActionPayload, &state); err != nil { logger.Error(err, "unable to execute action") } else { logger.V(2).Info("Action succeeded") diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index 978657d9..a6df8cd0 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -13,7 +13,8 @@ import ( ) type wrapper struct { - Licence licence `json:"licence,omitempty"` + Licence licence `json:"licence,omitempty"` + ConfigID string `json:"configurationId,omitempty"` } type licence struct { @@ -29,8 +30,8 @@ type LicenceActionHandler struct { logger logr.Logger } -// Marshal tries to parse raw action into action details. -func (h *LicenceActionHandler) Marshal(actionBody []byte, action *action.Action) error { +// UnMarshal tries to parse raw action into action details. +func (h *LicenceActionHandler) UnMarshal(actionBody []byte, action *action.Action) error { wrapper := wrapper{} err := json.Unmarshal(actionBody, &wrapper) if err != nil { From ee6277db4b034020aa20db1465022bab0fded4cf Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Tue, 24 May 2022 17:11:18 +0200 Subject: [PATCH 06/16] First working prototype --- controllers/publish_controller.go | 76 +-- go.mod | 2 +- go.sum | 3 +- hack/generate-tests.sh | 0 hack/grep-iot-logs.sh | 0 hack/update-kind-nodes.sh | 0 main.go | 84 ++- pkg/action/action.go | 40 +- pkg/action/licence/licence.go | 30 +- pkg/action/state/state.go | 24 + pkg/publisher/proto/Makefile | 10 +- pkg/publisher/proto/portal.proto | 28 +- pkg/publisher/proto/portal/marshal.go | 19 - pkg/publisher/proto/portal/portal.pb.go | 566 +++--------------- pkg/publisher/publisher.go | 6 +- pkg/publisher/sinks/gcp-iot/iot-core.go | 50 +- .../protoc-gen-go/descriptor/descriptor.pb.go | 200 +++++++ .../protobuf/encoding/prototext/decode.go | 3 - .../protobuf/encoding/protowire/wire.go | 19 +- .../protobuf/internal/encoding/text/decode.go | 2 +- .../protobuf/internal/encoding/text/encode.go | 5 + .../protobuf/internal/errors/is_go112.go | 1 + .../protobuf/internal/errors/is_go113.go | 1 + .../internal/flags/proto_legacy_disable.go | 1 + .../internal/flags/proto_legacy_enable.go | 1 + .../protobuf/internal/impl/codec_map_go111.go | 1 + .../protobuf/internal/impl/codec_map_go112.go | 1 + .../protobuf/internal/impl/codec_reflect.go | 1 + .../protobuf/internal/impl/codec_unsafe.go | 1 + .../protobuf/internal/impl/decode.go | 8 + .../protobuf/internal/impl/legacy_message.go | 7 + .../protobuf/internal/impl/pointer_reflect.go | 1 + .../protobuf/internal/impl/pointer_unsafe.go | 1 + .../protobuf/internal/strs/strings_pure.go | 1 + .../protobuf/internal/strs/strings_unsafe.go | 1 + .../protobuf/internal/version/version.go | 2 +- .../protobuf/proto/decode.go | 17 +- .../protobuf/proto/proto_methods.go | 1 + .../protobuf/proto/proto_reflect.go | 1 + .../protobuf/reflect/protoreflect/methods.go | 1 + .../reflect/protoreflect/value_pure.go | 1 + .../reflect/protoreflect/value_union.go | 25 + .../reflect/protoreflect/value_unsafe.go | 1 + .../reflect/protoregistry/registry.go | 43 +- .../protobuf/runtime/protoiface/methods.go | 1 + .../types/descriptorpb/descriptor.pb.go | 82 --- vendor/modules.txt | 3 +- 47 files changed, 585 insertions(+), 787 deletions(-) mode change 100755 => 100644 hack/generate-tests.sh mode change 100755 => 100644 hack/grep-iot-logs.sh mode change 100755 => 100644 hack/update-kind-nodes.sh create mode 100644 pkg/action/state/state.go delete mode 100644 pkg/publisher/proto/portal/marshal.go create mode 100644 vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go diff --git a/controllers/publish_controller.go b/controllers/publish_controller.go index 80d19612..a68774a8 100644 --- a/controllers/publish_controller.go +++ b/controllers/publish_controller.go @@ -11,12 +11,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - storageosv1alpha1 "github.com/storageos/portal-manager/api/v1alpha1" "github.com/storageos/portal-manager/pkg/handler" "github.com/storageos/portal-manager/pkg/publisher" "github.com/storageos/portal-manager/pkg/publisher/proto/portal" @@ -36,11 +33,9 @@ type Event struct { // Publisher watches a cache for changes and publishes them to an external sink. type Publisher struct { - k8s client.Client - scheme *runtime.Scheme - queue workqueue.RateLimitingInterface - recorder record.EventRecorder - sink publisher.Sink + scheme *runtime.Scheme + queue workqueue.RateLimitingInterface + sink publisher.Sink tenant string cluster string @@ -49,21 +44,14 @@ type Publisher struct { } // NewPublisher returns a new Publisher. -func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client client.Client, scheme *runtime.Scheme, queue workqueue.RateLimitingInterface, recorder record.EventRecorder, cfg storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { - sink, err := publisher.New(clusterID, privateKeyPem, cfg, deviceConfigCallback, logger) - if err != nil { - return nil, errors.Wrap(err, "unable to initialize new publisher") - } - +func NewPublisher(tenantID, clusterID string, sink *publisher.Publisher, scheme *runtime.Scheme, queue workqueue.RateLimitingInterface, logger logr.Logger) (*Publisher, error) { return &Publisher{ - k8s: client, - scheme: scheme, - queue: queue, - recorder: recorder, - sink: sink, - tenant: tenantID, - cluster: clusterID, - logger: logger.WithName("publish_controller"), + scheme: scheme, + queue: queue, + sink: sink, + tenant: tenantID, + cluster: clusterID, + logger: logger.WithName("publish_controller"), }, nil } @@ -72,52 +60,10 @@ func NewPublisher(tenantID, clusterID string, privateKeyPem []byte, client clien // Since this is an external controller, we don't need to register the // controller, just add it as a Runnable so that the manager can control startup // and shutdown. -func (p *Publisher) SetupWithManager(ctx context.Context, licence portal.CanMarshal, mgr ctrl.Manager) error { - if err := p.sink.Init(); err != nil { - return errors.Wrap(err, "unable to initialize sink") - } - - p.logger.V(2).Info("Sending acceptsConfiguration state") - if err := p.sink.PublishState(ctx, &portal.State{State: map[string]string{"acceptsConfiguration": "true"}}); err != nil { - return errors.Wrap(err, "unable to send initial state") - } - - p.logger.V(2).Info("Sending current licence state") - if err := p.sink.PublishState(ctx, licence); err != nil { - return errors.Wrap(err, "unable to send licence state") - } - +func (p *Publisher) SetupWithManager(mgr ctrl.Manager) error { return mgr.Add(p) } -// // Start runs the main reconcile loop until the context is cancelled or there is -// // a fatal error. It implements the controller-runtime Runnable interface so -// // that it can be controlled by controller manager. -// func (p *Publisher) Start(ctx context.Context) error { -// for { -// newEvent, quit := p.queue.Get() -// if quit { -// break -// } -// defer p.queue.Done(newEvent) -// handlerEvent := newEvent.(handler.Event) -// err := p.processEvent(ctx, handlerEvent) -// if err == nil { -// // No error, reset the ratelimit counters -// p.queue.Forget(newEvent) -// } else if p.queue.NumRequeues(newEvent) < maxRetries { -// p.log.Error(err, "Error processing (will retry)", "key", handlerEvent.Key, "action", handlerEvent.Action) -// p.queue.AddRateLimited(newEvent) -// } else { -// // err != nil and too many retries -// p.log.Error(err, "Error processing (giving up)", "key", handlerEvent.Key, "action", handlerEvent.Action) -// p.queue.Forget(newEvent) -// utilruntime.HandleError(err) -// } -// } -// return nil -// } - // Start runs the main reconcile loop until the context is cancelled or there is // a fatal error. It implements the controller-runtime Runnable interface so // that it can be controlled by controller manager. diff --git a/go.mod b/go.mod index 8fc4c413..38486796 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/storageos/go-api/v2 v2.6.1-0.20220117165820-8e9a07096ee3 github.com/storageos/operator v0.0.0-20211123123218-a7e0120a25e0 go.uber.org/zap v1.18.1 - google.golang.org/protobuf v1.26.0 + google.golang.org/protobuf v1.28.0 k8s.io/api v0.21.3 k8s.io/apimachinery v0.21.3 k8s.io/client-go v0.21.3 diff --git a/go.sum b/go.sum index 6e831196..c8dd7d57 100644 --- a/go.sum +++ b/go.sum @@ -1135,8 +1135,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/hack/generate-tests.sh b/hack/generate-tests.sh old mode 100755 new mode 100644 diff --git a/hack/grep-iot-logs.sh b/hack/grep-iot-logs.sh old mode 100755 new mode 100644 diff --git a/hack/update-kind-nodes.sh b/hack/update-kind-nodes.sh old mode 100755 new mode 100644 diff --git a/main.go b/main.go index 5ee8e785..ee02c8df 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,6 @@ import ( // to ensure that exec-entrypoint and run can make use of them. "go.uber.org/zap/zapcore" - "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -37,8 +36,9 @@ import ( "github.com/storageos/portal-manager/managers" "github.com/storageos/portal-manager/pkg/action" "github.com/storageos/portal-manager/pkg/action/licence" + "github.com/storageos/portal-manager/pkg/action/state" "github.com/storageos/portal-manager/pkg/handler" - "github.com/storageos/portal-manager/pkg/publisher/proto/portal" + "github.com/storageos/portal-manager/pkg/publisher" "github.com/storageos/portal-manager/pkg/utils" "github.com/storageos/portal-manager/watchers" klog "k8s.io/klog/v2" @@ -49,10 +49,6 @@ const ( // podNamespace is the operator's pod namespace environment variable. podNamespace = "POD_NAMESPACE" - // EventSourceName is added to Kubernetes events generated by the api - // manager. It can be used for filtering events. - EventSourceName = "storageos-portal-manager" - // portalURLEnv is the Portal API tenant ID environment variable. tenantIDEnv = "TENANT_ID" @@ -236,41 +232,73 @@ func main() { } // Start action service. - actionService := action.NewActionService(logger, []byte(iotConfig.SignatureKey), licence.NewLicenceActionHandler(*stosEndpoint, logger)) + actionService := action.NewActionService(logger, stosEndpoint.GetClusterID(), []byte(iotConfig.SignatureKey), licence.NewLicenceActionHandler(*stosEndpoint, logger)) - publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), mgr.GetClient(), mgr.GetScheme(), publishQueue, mgr.GetEventRecorderFor(EventSourceName), ctrlConfig, actionService.Do, logger) + // Start publisher. + sink, err := publisher.New(stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), ctrlConfig, actionService.Do, logger) if err != nil { - setupLogger.Error(err, "unable to create controller", "controller", "publisher") + setupLogger.Error(err, "unable to create sink") os.Exit(1) } - - // Start message broker. - currentLicence, err := stosEndpoint.GetLicence() - if err != nil { - setupLogger.Error(err, "unable to fetch licence") + if err := sink.Init(); err != nil { + setupLogger.Error(err, "unable to initialize sink") os.Exit(1) } - licenceMsg := portal.LicenceState{ - Licence: &portal.Licence{ - ExpireDate: timestamppb.New(currentLicence.ExpiresAt), - ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, - UsedBytes: currentLicence.UsedBytes, - Kind: currentLicence.Kind, - Version: currentLicence.Version, - }, - } - - func() { + // Send acceptsConfiguration. + go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - if err = publisher.SetupWithManager(ctx, &licenceMsg, mgr); err != nil { - setupLogger.Error(err, "unable to initialize controller", "controller", "publisher") - os.Exit(1) + if err := sink.PublishState(ctx, &map[string]string{"acceptsConfiguration": "true"}); err != nil { + setupLogger.Error(err, "unable to send initial state") + return + } + }() + + // Periodically send licence. + go func() { + ticker := time.NewTicker(time.Minute) + for { + currentLicence, err := stosEndpoint.GetLicence() + if err != nil { + setupLogger.Error(err, "unable to fetch licence") + return + } + + licenceState := state.ActionState{ + Meta: &state.ActionMeta{ + ClusterId: stosEndpoint.GetClusterID(), + Configurable: true, + Success: true, + }, + Licence: &state.Licence{ + ExpireDate: currentLicence.ExpiresAt, + LicenceType: currentLicence.Kind, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + if err := sink.PublishState(ctx, &licenceState); err != nil { + setupLogger.Error(err, "unable to send licence state") + } + cancel() + + <-ticker.C } }() + // Start message broker. + publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), sink, mgr.GetScheme(), publishQueue, logger) + if err != nil { + setupLogger.Error(err, "unable to create controller", "controller", "publisher") + os.Exit(1) + } + if err = publisher.SetupWithManager(mgr); err != nil { + setupLogger.Error(err, "unable to initialize controller", "controller", "publisher") + os.Exit(1) + } + // TODO(sc): WatchTypes need to be configurable via the PortalConfig CR. if err = (&controllers.WatchReconciler{ Client: mgr.GetClient(), diff --git a/pkg/action/action.go b/pkg/action/action.go index 0539268b..a278108b 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -6,7 +6,7 @@ import ( "errors" "github.com/go-logr/logr" - "github.com/storageos/portal-manager/pkg/publisher/proto/portal" + "github.com/storageos/portal-manager/pkg/action/state" "github.com/storageos/portal-manager/pkg/utils" ) @@ -28,23 +28,30 @@ type body struct { // ActionHandler is a common interface of Action implementations. type ActionHandler interface { UnMarshal([]byte, *Action) error - Do(ActionPayload, *portal.ActionState) error + Do(ActionPayload, *state.ActionState) error } // ActionService is the facade of action handlers. type ActionService struct { + clusterID string signatureKey []byte registeredActions []ActionHandler logger logr.Logger } // Do selects the right action and executes it. -func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal) error) { - logger := as.logger.WithValues("payload", string(rawAction)) - logger.V(2).Info("Action received") +func (as *ActionService) Do(rawAction []byte, updateState func(interface{}) error) { + if len(rawAction) == 0 { + return + } + + as.logger.V(2).Info("Action received") - state := portal.ActionState{ - Meta: &portal.ActionMeta{}, + state := state.ActionState{ + Meta: &state.ActionMeta{ + ClusterId: as.clusterID, + Configurable: true, + }, } var err error @@ -54,40 +61,40 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal state.Meta.Message = err.Error() } - logger.V(3).Info("Updating state") + as.logger.V(3).Info("Updating state") if err := updateState(&state); err != nil { - logger.Error(err, "unable to send action state", "actionError", state.Meta.Message) + as.logger.Error(err, "unable to send action state", "actionError", state.Meta.Message) } }() action := Action{} if err = json.Unmarshal(rawAction, &action); err != nil { - logger.Error(err, "unable parse action") + as.logger.Error(err, "unable parse action") return } - logger.V(3).Info("Validate data integrity") + as.logger.V(3).Info("Validate data integrity") err = utils.ValidateDataIntegrity(action.Signature, as.signatureKey, []byte(action.Body)) if err != nil { - logger.Error(err, "unable validate data integrity") + as.logger.Error(err, "unable validate data integrity") return } var rawBody []byte rawBody, err = base64.StdEncoding.DecodeString(action.Body) if err != nil { - logger.Error(err, "unable decode action body") + as.logger.Error(err, "unable decode action body") return } body := body{} if err = json.Unmarshal(rawBody, &body); err != nil { - logger.Error(err, "unable parse action body") + as.logger.Error(err, "unable parse action body") return } state.Meta.ConfigurationId = body.ConfigID - logger = logger.WithValues("config", body.ConfigID) + logger := as.logger.WithValues("config", body.ConfigID) for _, handler := range as.registeredActions { if err = handler.UnMarshal(rawBody, &action); err != nil { @@ -108,8 +115,9 @@ func (as *ActionService) Do(rawAction []byte, updateState func(portal.CanMarshal } // NewActionService creates a service. -func NewActionService(logger logr.Logger, signatureKey []byte, handlers ...ActionHandler) *ActionService { +func NewActionService(logger logr.Logger, clusterID string, signatureKey []byte, handlers ...ActionHandler) *ActionService { as := ActionService{ + clusterID: clusterID, signatureKey: signatureKey, logger: logger.WithName("action_handler"), } diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index a6df8cd0..f5012817 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -1,15 +1,14 @@ package licence import ( + "encoding/base64" "encoding/json" - "time" "github.com/go-logr/logr" "github.com/pkg/errors" "github.com/storageos/portal-manager/endpoints" "github.com/storageos/portal-manager/pkg/action" - "github.com/storageos/portal-manager/pkg/publisher/proto/portal" - "google.golang.org/protobuf/types/known/timestamppb" + "github.com/storageos/portal-manager/pkg/action/state" ) type wrapper struct { @@ -18,10 +17,7 @@ type wrapper struct { } type licence struct { - Value string `json:"value,omitempty"` - LicenceDefinitionId string `json:"licenceDefinitionId,omitempty"` - Kind string `json:"type,omitempty"` - ExpireDate time.Time `json:"expireDate,omitempty"` + Value string `json:"value,omitempty"` } // LicenceActionHandler implements a licence apply action. @@ -44,25 +40,27 @@ func (h *LicenceActionHandler) UnMarshal(actionBody []byte, action *action.Actio } // Do applies licence. -func (h *LicenceActionHandler) Do(payload action.ActionPayload, state *portal.ActionState) error { +func (h *LicenceActionHandler) Do(payload action.ActionPayload, actionState *state.ActionState) error { desiredLicence, ok := payload.(licence) if !ok { return errors.New("unable to cast payload to licence") } - h.logger.V(3).Info("Updating license", "kind", desiredLicence.Kind, "expires", desiredLicence.ExpireDate.String()) + h.logger.V(3).Info("Updating license") - currentLicence, err := h.storageOSEndpoint.UpdateLicence(desiredLicence.Value) + licence, err := base64.StdEncoding.DecodeString(desiredLicence.Value) + if err != nil { + return errors.Wrap(err, "unable decode licence") + } + + currentLicence, err := h.storageOSEndpoint.UpdateLicence(string(licence)) if err != nil { return errors.Wrap(err, "unable update licence") } - state.Licence = &portal.Licence{ - ExpireDate: timestamppb.New(currentLicence.ExpiresAt), - ClusterCapacityBytes: currentLicence.ClusterCapacityBytes, - UsedBytes: currentLicence.UsedBytes, - Kind: currentLicence.Kind, - Version: currentLicence.Version, + actionState.Licence = &state.Licence{ + ExpireDate: currentLicence.ExpiresAt, + LicenceType: currentLicence.Kind, } return nil diff --git a/pkg/action/state/state.go b/pkg/action/state/state.go new file mode 100644 index 00000000..c55378d4 --- /dev/null +++ b/pkg/action/state/state.go @@ -0,0 +1,24 @@ +package state + +import "time" + +// ActionState represents the device state message. +type ActionState struct { + Meta *ActionMeta `json:"meta,omitempty"` + Licence *Licence `json:"licence,omitempty"` +} + +// ActionMeta represents the state message meta data. +type ActionMeta struct { + ClusterId string `json:"clusterId,omitempty"` + ConfigurationId string `json:"configurationId,omitempty"` + Configurable bool `json:"configurable,omitempty"` + Success bool `json:"success,omitempty"` + Message string `json:"message,omitempty"` +} + +// Licence represents the licence state. +type Licence struct { + ExpireDate time.Time `json:"expireDate,omitempty"` + LicenceType string `json:"licenceType,omitempty"` +} diff --git a/pkg/publisher/proto/Makefile b/pkg/publisher/proto/Makefile index 7b222200..e1f990e8 100644 --- a/pkg/publisher/proto/Makefile +++ b/pkg/publisher/proto/Makefile @@ -21,7 +21,7 @@ export GOPATH # Only set PROTOC_VER if it has an empty value. ifeq (,$(strip $(PROTOC_VER))) -PROTOC_VER := 3.9.1 +PROTOC_VER := 3.20.1 endif PROTOC_OS := $(shell uname -s) @@ -34,7 +34,7 @@ ifeq (i386,$(PROTOC_ARCH)) PROTOC_ARCH := x86_32 endif -PROTOC := ./protoc +PROTOC := protoc PROTOC_ZIP := protoc-$(PROTOC_VER)-$(PROTOC_OS)-$(PROTOC_ARCH).zip PROTOC_URL := https://github.com/google/protobuf/releases/download/v$(PROTOC_VER)/$(PROTOC_ZIP) PROTOC_TMP_DIR := .protoc @@ -58,15 +58,15 @@ $(PROTOC): PROTOC_GEN_GO_PKG := github.com/golang/protobuf/protoc-gen-go PROTOC_GEN_GO := protoc-gen-go $(PROTOC_GEN_GO): PROTOBUF_PKG := $(dir $(PROTOC_GEN_GO_PKG)) -$(PROTOC_GEN_GO): PROTOBUF_VERSION := v1.3.2 +$(PROTOC_GEN_GO): PROTOBUF_VERSION := v1.5.2 $(PROTOC_GEN_GO): mkdir -p $(dir $(GOPATH)/src/$(PROTOBUF_PKG)) - test -d $(GOPATH)/src/$(PROTOBUF_PKG)/.git || git clone https://$(PROTOBUF_PKG) $(GOPATH)/src/$(PROTOBUF_PKG) + test -d $(GOPATH)/src/$(PROTOBUF_PKG)/.git && (cd $(GOPATH)/src/$(PROTOBUF_PKG); git fetch -a && git reset --hard $(PROTOBUF_VERSION)) || git clone --branch $(PROTOBUF_VERSION) https://$(PROTOBUF_PKG) $(GOPATH)/src/$(PROTOBUF_PKG) (cd $(GOPATH)/src/$(PROTOBUF_PKG) && \ (test "$$(git describe --tags | head -1)" = "$(PROTOBUF_VERSION)" || \ (git fetch && git checkout tags/$(PROTOBUF_VERSION)))) (cd $(GOPATH)/src/$(PROTOBUF_PKG) && go get -v -d $$(go list -f '{{ .ImportPath }}' ./...)) && \ - go build -o "$@" $(PROTOC_GEN_GO_PKG) + (cd $(GOPATH)/src/$(PROTOBUF_PKG) && go build -o "$(shell pwd)/$@" $(PROTOC_GEN_GO_PKG)) ######################################################################## diff --git a/pkg/publisher/proto/portal.proto b/pkg/publisher/proto/portal.proto index c802a21b..35477200 100644 --- a/pkg/publisher/proto/portal.proto +++ b/pkg/publisher/proto/portal.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package portal.v1; import "google/protobuf/timestamp.proto"; +import "google/protobuf/descriptor.proto"; option go_package = "github.com/storageos/portal-manager/pkg/publisher/proto;portal"; @@ -40,30 +41,3 @@ message Event { bytes object = 5; google.protobuf.Timestamp timestamp = 6; } - -message State { - map state = 1; -} - -message Licence { - google.protobuf.Timestamp expireDate = 1; - uint64 clusterCapacityBytes = 2; - uint64 usedBytes = 3; - string kind = 4; - string version = 5; -} - -message LicenceState { - Licence licence = 1; -} - -message ActionMeta { - string configurationId = 1; - bool success = 2; - string message = 3; -} - -message ActionState { - ActionMeta meta = 1; - Licence licence = 2; -} \ No newline at end of file diff --git a/pkg/publisher/proto/portal/marshal.go b/pkg/publisher/proto/portal/marshal.go deleted file mode 100644 index 3142736a..00000000 --- a/pkg/publisher/proto/portal/marshal.go +++ /dev/null @@ -1,19 +0,0 @@ -package portal - -import "google.golang.org/protobuf/proto" - -type CanMarshal interface { - Marshal() ([]byte, error) -} - -func (s *State) Marshal() ([]byte, error) { - return proto.Marshal(s) -} - -func (l *LicenceState) Marshal() ([]byte, error) { - return proto.Marshal(l) -} - -func (a *ActionState) Marshal() ([]byte, error) { - return proto.Marshal(a) -} diff --git a/pkg/publisher/proto/portal/portal.pb.go b/pkg/publisher/proto/portal/portal.pb.go index b3cd7faa..c31fc255 100644 --- a/pkg/publisher/proto/portal/portal.pb.go +++ b/pkg/publisher/proto/portal/portal.pb.go @@ -1,15 +1,16 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.9.1 +// protoc v3.20.1 // source: portal.proto package portal import ( - timestamp "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/golang/protobuf/protoc-gen-go/descriptor" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -278,12 +279,12 @@ type Event struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - EventType EventType `protobuf:"varint,1,opt,name=event_type,json=eventType,proto3,enum=portal.v1.EventType" json:"event_type,omitempty"` - Cluster *ClusterMeta `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` - ObjectType *TypeMeta `protobuf:"bytes,3,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"` - ObjectMeta *ObjectMeta `protobuf:"bytes,4,opt,name=object_meta,json=objectMeta,proto3" json:"object_meta,omitempty"` - Object []byte `protobuf:"bytes,5,opt,name=object,proto3" json:"object,omitempty"` - Timestamp *timestamp.Timestamp `protobuf:"bytes,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + EventType EventType `protobuf:"varint,1,opt,name=event_type,json=eventType,proto3,enum=portal.v1.EventType" json:"event_type,omitempty"` + Cluster *ClusterMeta `protobuf:"bytes,2,opt,name=cluster,proto3" json:"cluster,omitempty"` + ObjectType *TypeMeta `protobuf:"bytes,3,opt,name=object_type,json=objectType,proto3" json:"object_type,omitempty"` + ObjectMeta *ObjectMeta `protobuf:"bytes,4,opt,name=object_meta,json=objectMeta,proto3" json:"object_meta,omitempty"` + Object []byte `protobuf:"bytes,5,opt,name=object,proto3" json:"object,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *Event) Reset() { @@ -353,400 +354,74 @@ func (x *Event) GetObject() []byte { return nil } -func (x *Event) GetTimestamp() *timestamp.Timestamp { +func (x *Event) GetTimestamp() *timestamppb.Timestamp { if x != nil { return x.Timestamp } return nil } -type State struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - State map[string]string `protobuf:"bytes,1,rep,name=state,proto3" json:"state,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` -} - -func (x *State) Reset() { - *x = State{} - if protoimpl.UnsafeEnabled { - mi := &file_portal_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *State) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*State) ProtoMessage() {} - -func (x *State) ProtoReflect() protoreflect.Message { - mi := &file_portal_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use State.ProtoReflect.Descriptor instead. -func (*State) Descriptor() ([]byte, []int) { - return file_portal_proto_rawDescGZIP(), []int{4} -} - -func (x *State) GetState() map[string]string { - if x != nil { - return x.State - } - return nil -} - -type Licence struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ExpireDate *timestamp.Timestamp `protobuf:"bytes,1,opt,name=expireDate,proto3" json:"expireDate,omitempty"` - ClusterCapacityBytes uint64 `protobuf:"varint,2,opt,name=clusterCapacityBytes,proto3" json:"clusterCapacityBytes,omitempty"` - UsedBytes uint64 `protobuf:"varint,3,opt,name=usedBytes,proto3" json:"usedBytes,omitempty"` - Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` - Version string `protobuf:"bytes,5,opt,name=version,proto3" json:"version,omitempty"` -} - -func (x *Licence) Reset() { - *x = Licence{} - if protoimpl.UnsafeEnabled { - mi := &file_portal_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Licence) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Licence) ProtoMessage() {} - -func (x *Licence) ProtoReflect() protoreflect.Message { - mi := &file_portal_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Licence.ProtoReflect.Descriptor instead. -func (*Licence) Descriptor() ([]byte, []int) { - return file_portal_proto_rawDescGZIP(), []int{5} -} - -func (x *Licence) GetExpireDate() *timestamp.Timestamp { - if x != nil { - return x.ExpireDate - } - return nil -} - -func (x *Licence) GetClusterCapacityBytes() uint64 { - if x != nil { - return x.ClusterCapacityBytes - } - return 0 -} - -func (x *Licence) GetUsedBytes() uint64 { - if x != nil { - return x.UsedBytes - } - return 0 -} - -func (x *Licence) GetKind() string { - if x != nil { - return x.Kind - } - return "" -} - -func (x *Licence) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -type LicenceState struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Licence *Licence `protobuf:"bytes,1,opt,name=licence,proto3" json:"licence,omitempty"` -} - -func (x *LicenceState) Reset() { - *x = LicenceState{} - if protoimpl.UnsafeEnabled { - mi := &file_portal_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LicenceState) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LicenceState) ProtoMessage() {} - -func (x *LicenceState) ProtoReflect() protoreflect.Message { - mi := &file_portal_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LicenceState.ProtoReflect.Descriptor instead. -func (*LicenceState) Descriptor() ([]byte, []int) { - return file_portal_proto_rawDescGZIP(), []int{6} -} - -func (x *LicenceState) GetLicence() *Licence { - if x != nil { - return x.Licence - } - return nil -} - -type ActionMeta struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConfigurationId string `protobuf:"bytes,1,opt,name=configurationId,proto3" json:"configurationId,omitempty"` - Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *ActionMeta) Reset() { - *x = ActionMeta{} - if protoimpl.UnsafeEnabled { - mi := &file_portal_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionMeta) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionMeta) ProtoMessage() {} - -func (x *ActionMeta) ProtoReflect() protoreflect.Message { - mi := &file_portal_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionMeta.ProtoReflect.Descriptor instead. -func (*ActionMeta) Descriptor() ([]byte, []int) { - return file_portal_proto_rawDescGZIP(), []int{7} -} - -func (x *ActionMeta) GetConfigurationId() string { - if x != nil { - return x.ConfigurationId - } - return "" -} - -func (x *ActionMeta) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *ActionMeta) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -type ActionState struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Meta *ActionMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` - Licence *Licence `protobuf:"bytes,2,opt,name=licence,proto3" json:"licence,omitempty"` -} - -func (x *ActionState) Reset() { - *x = ActionState{} - if protoimpl.UnsafeEnabled { - mi := &file_portal_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ActionState) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ActionState) ProtoMessage() {} - -func (x *ActionState) ProtoReflect() protoreflect.Message { - mi := &file_portal_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ActionState.ProtoReflect.Descriptor instead. -func (*ActionState) Descriptor() ([]byte, []int) { - return file_portal_proto_rawDescGZIP(), []int{8} -} - -func (x *ActionState) GetMeta() *ActionMeta { - if x != nil { - return x.Meta - } - return nil -} - -func (x *ActionState) GetLicence() *Licence { - if x != nil { - return x.Licence - } - return nil -} - var File_portal_proto protoreflect.FileDescriptor var file_portal_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x0b, 0x43, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x65, 0x6e, - 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x22, 0x4e, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x14, 0x0a, - 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x22, 0xf1, 0x01, 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x39, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x33, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x07, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, - 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x0b, - 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x74, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x1a, 0x38, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc5, 0x01, 0x0a, - 0x07, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, - 0x44, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x14, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x64, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x75, 0x73, 0x65, - 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x0a, 0x0c, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, - 0x63, 0x65, 0x22, 0x6a, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, - 0x12, 0x28, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x66, - 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, - 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, - 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, - 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, - 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x72, 0x74, - 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x6c, - 0x69, 0x63, 0x65, 0x6e, 0x63, 0x65, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x10, - 0x04, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x6f, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, - 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x6f, 0x72, - 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x0b, + 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x22, 0x4e, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, + 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x22, 0xf1, 0x01, 0x0a, 0x0a, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, + 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, + 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x52, + 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, + 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x38, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2a, 0x49, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, + 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x10, 0x04, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x6f, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, + 0x61, 0x6c, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, + 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -762,39 +437,28 @@ func file_portal_proto_rawDescGZIP() []byte { } var file_portal_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_portal_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_portal_proto_goTypes = []interface{}{ - (EventType)(0), // 0: portal.v1.EventType - (*ClusterMeta)(nil), // 1: portal.v1.ClusterMeta - (*TypeMeta)(nil), // 2: portal.v1.TypeMeta - (*ObjectMeta)(nil), // 3: portal.v1.ObjectMeta - (*Event)(nil), // 4: portal.v1.Event - (*State)(nil), // 5: portal.v1.State - (*Licence)(nil), // 6: portal.v1.Licence - (*LicenceState)(nil), // 7: portal.v1.LicenceState - (*ActionMeta)(nil), // 8: portal.v1.ActionMeta - (*ActionState)(nil), // 9: portal.v1.ActionState - nil, // 10: portal.v1.ObjectMeta.LabelsEntry - nil, // 11: portal.v1.State.StateEntry - (*timestamp.Timestamp)(nil), // 12: google.protobuf.Timestamp + (EventType)(0), // 0: portal.v1.EventType + (*ClusterMeta)(nil), // 1: portal.v1.ClusterMeta + (*TypeMeta)(nil), // 2: portal.v1.TypeMeta + (*ObjectMeta)(nil), // 3: portal.v1.ObjectMeta + (*Event)(nil), // 4: portal.v1.Event + nil, // 5: portal.v1.ObjectMeta.LabelsEntry + (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp } var file_portal_proto_depIdxs = []int32{ - 10, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry - 0, // 1: portal.v1.Event.event_type:type_name -> portal.v1.EventType - 1, // 2: portal.v1.Event.cluster:type_name -> portal.v1.ClusterMeta - 2, // 3: portal.v1.Event.object_type:type_name -> portal.v1.TypeMeta - 3, // 4: portal.v1.Event.object_meta:type_name -> portal.v1.ObjectMeta - 12, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp - 11, // 6: portal.v1.State.state:type_name -> portal.v1.State.StateEntry - 12, // 7: portal.v1.Licence.expireDate:type_name -> google.protobuf.Timestamp - 6, // 8: portal.v1.LicenceState.licence:type_name -> portal.v1.Licence - 8, // 9: portal.v1.ActionState.meta:type_name -> portal.v1.ActionMeta - 6, // 10: portal.v1.ActionState.licence:type_name -> portal.v1.Licence - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 5, // 0: portal.v1.ObjectMeta.labels:type_name -> portal.v1.ObjectMeta.LabelsEntry + 0, // 1: portal.v1.Event.event_type:type_name -> portal.v1.EventType + 1, // 2: portal.v1.Event.cluster:type_name -> portal.v1.ClusterMeta + 2, // 3: portal.v1.Event.object_type:type_name -> portal.v1.TypeMeta + 3, // 4: portal.v1.Event.object_meta:type_name -> portal.v1.ObjectMeta + 6, // 5: portal.v1.Event.timestamp:type_name -> google.protobuf.Timestamp + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_portal_proto_init() } @@ -851,66 +515,6 @@ func file_portal_proto_init() { return nil } } - file_portal_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*State); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_portal_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Licence); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_portal_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LicenceState); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_portal_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionMeta); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_portal_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ActionState); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -918,7 +522,7 @@ func file_portal_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_portal_proto_rawDesc, NumEnums: 1, - NumMessages: 11, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 5bff74e9..6755e059 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -15,7 +15,7 @@ import ( type Sink interface { Init() error Publish(context.Context, *portal.Event) error - PublishState(context.Context, portal.CanMarshal) error + PublishState(context.Context, interface{}) error Shutdown() error } @@ -24,7 +24,7 @@ type Publisher struct { logger logr.Logger } -func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*Publisher, error) { +func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(interface{}) error), logger logr.Logger) (*Publisher, error) { sinks := make(map[string]Sink) if config.IOTCore != nil { sink, err := gcp.New(clusterID, privateKeyPem, config.IOTCore, deviceConfigCallback, logger) @@ -60,7 +60,7 @@ func (p *Publisher) Publish(ctx context.Context, event *portal.Event) error { } // PublishState a state change request. -func (p *Publisher) PublishState(ctx context.Context, state portal.CanMarshal) error { +func (p *Publisher) PublishState(ctx context.Context, state interface{}) error { for _, sink := range p.sinks { if err := sink.PublishState(ctx, state); err != nil { return errors.Wrap(err, "unable to publish state to sink") diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index 3843b953..c04fdd3a 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rsa" "crypto/tls" + "encoding/json" "fmt" "sync" "time" @@ -21,12 +22,13 @@ const ( Name = "iotcore" // MQTT parameters - keyLifeTime = 20 //minutes - topicType = "events" // or "state" - qos = 1 // QoS 2 isn't supported in GCP - retain = false - username = "unused" // always this value in GCP - devicePrefix = "clusterId-" + keyLifeTime = 20 //minutes + topicType = "events" // or "state" + qos = 1 // QoS 2 isn't supported in GCP + retain = false + username = "unused" // always this value in GCP + devicePrefix = "clusterId-" + publishStateWait = 2 * time.Second ) var ( @@ -42,7 +44,7 @@ var ( ) type MQTT struct { - deviceConfigCallback func([]byte, func(portal.CanMarshal) error) + deviceConfigCallback func([]byte, func(interface{}) error) device string project string registry string @@ -51,13 +53,14 @@ type MQTT struct { privateKey *rsa.PrivateKey mu sync.Mutex + stateMu sync.Mutex conn mqtt.Client tokenExpired bool logger logr.Logger serverURL string } -func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, deviceConfigCallback func([]byte, func(portal.CanMarshal) error), logger logr.Logger) (*MQTT, error) { +func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, deviceConfigCallback func([]byte, func(interface{}) error), logger logr.Logger) (*MQTT, error) { if config == nil { return nil, ErrNoConfig } @@ -111,8 +114,12 @@ func (m *MQTT) ConfigTopic() string { return fmt.Sprintf("/devices/%s/config", m.device) } -// Init happens periodically before connect. func (m *MQTT) Init() error { + if _, err := m.getConnection(); err != nil { + m.logger.V(2).Info("Failed to get connection") + return errors.Wrap(err, "failed to get connection") + } + return nil } @@ -161,7 +168,7 @@ func (m *MQTT) getConnection() (mqtt.Client, error) { if m.tokenExpired { m.logger.V(3).Info("Initializing...") if err := m.newClient(); err != nil { - return m.conn, errors.Wrap(err, "failed to init client") + return m.conn, errors.Wrap(err, "failed to create new client") } } @@ -171,14 +178,20 @@ func (m *MQTT) getConnection() (mqtt.Client, error) { m.logger.V(3).Info("Manage device config subscription...") - m.conn.Unsubscribe(m.ConfigTopic()) - m.conn.Subscribe(m.ConfigTopic(), qos, func(_ mqtt.Client, msg mqtt.Message) { + token := m.conn.Subscribe(m.ConfigTopic(), qos, func(_ mqtt.Client, msg mqtt.Message) { m.logger.V(5).Info("Event received", "message", msg) - m.deviceConfigCallback(msg.Payload(), func(cm portal.CanMarshal) error { + m.deviceConfigCallback(msg.Payload(), func(cm interface{}) error { return m.PublishState(context.Background(), cm) }) }) + if !token.WaitTimeout(time.Second) { + m.logger.V(2).Info("Subscribe timeout") + return m.conn, errors.Wrap(token.Error(), "subscribe timeout") + } else if token.Error() != nil { + m.logger.V(2).Info("Subscribe failed") + return m.conn, errors.Wrap(token.Error(), "subscribe failed") + } } return m.conn, nil @@ -253,9 +266,12 @@ func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { } // PublishState will publish state. -func (m *MQTT) PublishState(ctx context.Context, state portal.CanMarshal) error { - payload, err := state.Marshal() +func (m *MQTT) PublishState(ctx context.Context, state interface{}) error { + m.stateMu.Lock() + + payload, err := json.Marshal(state) if err != nil { + m.stateMu.Unlock() return errors.Wrap(err, "failed to marshal state to protobuf") } @@ -266,10 +282,14 @@ func (m *MQTT) PublishState(ctx context.Context, state portal.CanMarshal) error conn, err := m.getConnection() if err != nil { + m.stateMu.Unlock() return errors.Wrap(err, "failed to get connection") } token := conn.Publish(m.StateTopic(), qos, retain, payload) + + time.AfterFunc(publishStateWait, m.stateMu.Unlock) + select { case <-token.Done(): if token.Error() != nil { diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go new file mode 100644 index 00000000..63dc0578 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go @@ -0,0 +1,200 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto + +package descriptor + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" +) + +// Symbols defined in public import of google/protobuf/descriptor.proto. + +type FieldDescriptorProto_Type = descriptorpb.FieldDescriptorProto_Type + +const FieldDescriptorProto_TYPE_DOUBLE = descriptorpb.FieldDescriptorProto_TYPE_DOUBLE +const FieldDescriptorProto_TYPE_FLOAT = descriptorpb.FieldDescriptorProto_TYPE_FLOAT +const FieldDescriptorProto_TYPE_INT64 = descriptorpb.FieldDescriptorProto_TYPE_INT64 +const FieldDescriptorProto_TYPE_UINT64 = descriptorpb.FieldDescriptorProto_TYPE_UINT64 +const FieldDescriptorProto_TYPE_INT32 = descriptorpb.FieldDescriptorProto_TYPE_INT32 +const FieldDescriptorProto_TYPE_FIXED64 = descriptorpb.FieldDescriptorProto_TYPE_FIXED64 +const FieldDescriptorProto_TYPE_FIXED32 = descriptorpb.FieldDescriptorProto_TYPE_FIXED32 +const FieldDescriptorProto_TYPE_BOOL = descriptorpb.FieldDescriptorProto_TYPE_BOOL +const FieldDescriptorProto_TYPE_STRING = descriptorpb.FieldDescriptorProto_TYPE_STRING +const FieldDescriptorProto_TYPE_GROUP = descriptorpb.FieldDescriptorProto_TYPE_GROUP +const FieldDescriptorProto_TYPE_MESSAGE = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE +const FieldDescriptorProto_TYPE_BYTES = descriptorpb.FieldDescriptorProto_TYPE_BYTES +const FieldDescriptorProto_TYPE_UINT32 = descriptorpb.FieldDescriptorProto_TYPE_UINT32 +const FieldDescriptorProto_TYPE_ENUM = descriptorpb.FieldDescriptorProto_TYPE_ENUM +const FieldDescriptorProto_TYPE_SFIXED32 = descriptorpb.FieldDescriptorProto_TYPE_SFIXED32 +const FieldDescriptorProto_TYPE_SFIXED64 = descriptorpb.FieldDescriptorProto_TYPE_SFIXED64 +const FieldDescriptorProto_TYPE_SINT32 = descriptorpb.FieldDescriptorProto_TYPE_SINT32 +const FieldDescriptorProto_TYPE_SINT64 = descriptorpb.FieldDescriptorProto_TYPE_SINT64 + +var FieldDescriptorProto_Type_name = descriptorpb.FieldDescriptorProto_Type_name +var FieldDescriptorProto_Type_value = descriptorpb.FieldDescriptorProto_Type_value + +type FieldDescriptorProto_Label = descriptorpb.FieldDescriptorProto_Label + +const FieldDescriptorProto_LABEL_OPTIONAL = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL +const FieldDescriptorProto_LABEL_REQUIRED = descriptorpb.FieldDescriptorProto_LABEL_REQUIRED +const FieldDescriptorProto_LABEL_REPEATED = descriptorpb.FieldDescriptorProto_LABEL_REPEATED + +var FieldDescriptorProto_Label_name = descriptorpb.FieldDescriptorProto_Label_name +var FieldDescriptorProto_Label_value = descriptorpb.FieldDescriptorProto_Label_value + +type FileOptions_OptimizeMode = descriptorpb.FileOptions_OptimizeMode + +const FileOptions_SPEED = descriptorpb.FileOptions_SPEED +const FileOptions_CODE_SIZE = descriptorpb.FileOptions_CODE_SIZE +const FileOptions_LITE_RUNTIME = descriptorpb.FileOptions_LITE_RUNTIME + +var FileOptions_OptimizeMode_name = descriptorpb.FileOptions_OptimizeMode_name +var FileOptions_OptimizeMode_value = descriptorpb.FileOptions_OptimizeMode_value + +type FieldOptions_CType = descriptorpb.FieldOptions_CType + +const FieldOptions_STRING = descriptorpb.FieldOptions_STRING +const FieldOptions_CORD = descriptorpb.FieldOptions_CORD +const FieldOptions_STRING_PIECE = descriptorpb.FieldOptions_STRING_PIECE + +var FieldOptions_CType_name = descriptorpb.FieldOptions_CType_name +var FieldOptions_CType_value = descriptorpb.FieldOptions_CType_value + +type FieldOptions_JSType = descriptorpb.FieldOptions_JSType + +const FieldOptions_JS_NORMAL = descriptorpb.FieldOptions_JS_NORMAL +const FieldOptions_JS_STRING = descriptorpb.FieldOptions_JS_STRING +const FieldOptions_JS_NUMBER = descriptorpb.FieldOptions_JS_NUMBER + +var FieldOptions_JSType_name = descriptorpb.FieldOptions_JSType_name +var FieldOptions_JSType_value = descriptorpb.FieldOptions_JSType_value + +type MethodOptions_IdempotencyLevel = descriptorpb.MethodOptions_IdempotencyLevel + +const MethodOptions_IDEMPOTENCY_UNKNOWN = descriptorpb.MethodOptions_IDEMPOTENCY_UNKNOWN +const MethodOptions_NO_SIDE_EFFECTS = descriptorpb.MethodOptions_NO_SIDE_EFFECTS +const MethodOptions_IDEMPOTENT = descriptorpb.MethodOptions_IDEMPOTENT + +var MethodOptions_IdempotencyLevel_name = descriptorpb.MethodOptions_IdempotencyLevel_name +var MethodOptions_IdempotencyLevel_value = descriptorpb.MethodOptions_IdempotencyLevel_value + +type FileDescriptorSet = descriptorpb.FileDescriptorSet +type FileDescriptorProto = descriptorpb.FileDescriptorProto +type DescriptorProto = descriptorpb.DescriptorProto +type ExtensionRangeOptions = descriptorpb.ExtensionRangeOptions +type FieldDescriptorProto = descriptorpb.FieldDescriptorProto +type OneofDescriptorProto = descriptorpb.OneofDescriptorProto +type EnumDescriptorProto = descriptorpb.EnumDescriptorProto +type EnumValueDescriptorProto = descriptorpb.EnumValueDescriptorProto +type ServiceDescriptorProto = descriptorpb.ServiceDescriptorProto +type MethodDescriptorProto = descriptorpb.MethodDescriptorProto + +const Default_MethodDescriptorProto_ClientStreaming = descriptorpb.Default_MethodDescriptorProto_ClientStreaming +const Default_MethodDescriptorProto_ServerStreaming = descriptorpb.Default_MethodDescriptorProto_ServerStreaming + +type FileOptions = descriptorpb.FileOptions + +const Default_FileOptions_JavaMultipleFiles = descriptorpb.Default_FileOptions_JavaMultipleFiles +const Default_FileOptions_JavaStringCheckUtf8 = descriptorpb.Default_FileOptions_JavaStringCheckUtf8 +const Default_FileOptions_OptimizeFor = descriptorpb.Default_FileOptions_OptimizeFor +const Default_FileOptions_CcGenericServices = descriptorpb.Default_FileOptions_CcGenericServices +const Default_FileOptions_JavaGenericServices = descriptorpb.Default_FileOptions_JavaGenericServices +const Default_FileOptions_PyGenericServices = descriptorpb.Default_FileOptions_PyGenericServices +const Default_FileOptions_PhpGenericServices = descriptorpb.Default_FileOptions_PhpGenericServices +const Default_FileOptions_Deprecated = descriptorpb.Default_FileOptions_Deprecated +const Default_FileOptions_CcEnableArenas = descriptorpb.Default_FileOptions_CcEnableArenas + +type MessageOptions = descriptorpb.MessageOptions + +const Default_MessageOptions_MessageSetWireFormat = descriptorpb.Default_MessageOptions_MessageSetWireFormat +const Default_MessageOptions_NoStandardDescriptorAccessor = descriptorpb.Default_MessageOptions_NoStandardDescriptorAccessor +const Default_MessageOptions_Deprecated = descriptorpb.Default_MessageOptions_Deprecated + +type FieldOptions = descriptorpb.FieldOptions + +const Default_FieldOptions_Ctype = descriptorpb.Default_FieldOptions_Ctype +const Default_FieldOptions_Jstype = descriptorpb.Default_FieldOptions_Jstype +const Default_FieldOptions_Lazy = descriptorpb.Default_FieldOptions_Lazy +const Default_FieldOptions_Deprecated = descriptorpb.Default_FieldOptions_Deprecated +const Default_FieldOptions_Weak = descriptorpb.Default_FieldOptions_Weak + +type OneofOptions = descriptorpb.OneofOptions +type EnumOptions = descriptorpb.EnumOptions + +const Default_EnumOptions_Deprecated = descriptorpb.Default_EnumOptions_Deprecated + +type EnumValueOptions = descriptorpb.EnumValueOptions + +const Default_EnumValueOptions_Deprecated = descriptorpb.Default_EnumValueOptions_Deprecated + +type ServiceOptions = descriptorpb.ServiceOptions + +const Default_ServiceOptions_Deprecated = descriptorpb.Default_ServiceOptions_Deprecated + +type MethodOptions = descriptorpb.MethodOptions + +const Default_MethodOptions_Deprecated = descriptorpb.Default_MethodOptions_Deprecated +const Default_MethodOptions_IdempotencyLevel = descriptorpb.Default_MethodOptions_IdempotencyLevel + +type UninterpretedOption = descriptorpb.UninterpretedOption +type SourceCodeInfo = descriptorpb.SourceCodeInfo +type GeneratedCodeInfo = descriptorpb.GeneratedCodeInfo +type DescriptorProto_ExtensionRange = descriptorpb.DescriptorProto_ExtensionRange +type DescriptorProto_ReservedRange = descriptorpb.DescriptorProto_ReservedRange +type EnumDescriptorProto_EnumReservedRange = descriptorpb.EnumDescriptorProto_EnumReservedRange +type UninterpretedOption_NamePart = descriptorpb.UninterpretedOption_NamePart +type SourceCodeInfo_Location = descriptorpb.SourceCodeInfo_Location +type GeneratedCodeInfo_Annotation = descriptorpb.GeneratedCodeInfo_Annotation + +var File_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto protoreflect.FileDescriptor + +var file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_rawDesc = []byte{ + 0x0a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, + 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, + 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x3b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x32, +} + +var file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_goTypes = []interface{}{} +var file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_init() } +func file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_init() { + if File_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_goTypes, + DependencyIndexes: file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_depIdxs, + }.Build() + File_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto = out.File + file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_rawDesc = nil + file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_goTypes = nil + file_github_com_golang_protobuf_protoc_gen_go_descriptor_descriptor_proto_depIdxs = nil +} diff --git a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go index 8fb1d9e0..179d6e8f 100644 --- a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go +++ b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go @@ -744,9 +744,6 @@ func (d decoder) skipValue() error { // Skip items. This will not validate whether skipped values are // of the same type or not, same behavior as C++ // TextFormat::Parser::AllowUnknownField(true) version 3.8.0. - if err := d.skipValue(); err != nil { - return err - } } } } diff --git a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go index a427f8b7..9c61112f 100644 --- a/vendor/google.golang.org/protobuf/encoding/protowire/wire.go +++ b/vendor/google.golang.org/protobuf/encoding/protowire/wire.go @@ -21,10 +21,11 @@ import ( type Number int32 const ( - MinValidNumber Number = 1 - FirstReservedNumber Number = 19000 - LastReservedNumber Number = 19999 - MaxValidNumber Number = 1<<29 - 1 + MinValidNumber Number = 1 + FirstReservedNumber Number = 19000 + LastReservedNumber Number = 19999 + MaxValidNumber Number = 1<<29 - 1 + DefaultRecursionLimit = 10000 ) // IsValid reports whether the field number is semantically valid. @@ -55,6 +56,7 @@ const ( errCodeOverflow errCodeReserved errCodeEndGroup + errCodeRecursionDepth ) var ( @@ -112,6 +114,10 @@ func ConsumeField(b []byte) (Number, Type, int) { // When parsing a group, the length includes the end group marker and // the end group is verified to match the starting field number. func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { + return consumeFieldValueD(num, typ, b, DefaultRecursionLimit) +} + +func consumeFieldValueD(num Number, typ Type, b []byte, depth int) (n int) { switch typ { case VarintType: _, n = ConsumeVarint(b) @@ -126,6 +132,9 @@ func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { _, n = ConsumeBytes(b) return n case StartGroupType: + if depth < 0 { + return errCodeRecursionDepth + } n0 := len(b) for { num2, typ2, n := ConsumeTag(b) @@ -140,7 +149,7 @@ func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) { return n0 - len(b) } - n = ConsumeFieldValue(num2, typ2, b) + n = consumeFieldValueD(num2, typ2, b, depth-1) if n < 0 { return n // forward error code } diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go index eb10ea10..37803773 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go @@ -381,7 +381,7 @@ func (d *Decoder) currentOpenKind() (Kind, byte) { case '[': return ListOpen, ']' } - panic(fmt.Sprintf("Decoder: openStack contains invalid byte %s", string(openCh))) + panic(fmt.Sprintf("Decoder: openStack contains invalid byte %c", openCh)) } func (d *Decoder) pushOpenStack(ch byte) { diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go index aa66bdd0..da289ccc 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go @@ -263,3 +263,8 @@ func (e *Encoder) Snapshot() encoderState { func (e *Encoder) Reset(es encoderState) { e.encoderState = es } + +// AppendString appends the escaped form of the input string to b. +func AppendString(b []byte, s string) []byte { + return appendString(b, s, false) +} diff --git a/vendor/google.golang.org/protobuf/internal/errors/is_go112.go b/vendor/google.golang.org/protobuf/internal/errors/is_go112.go index f90e909b..fbcd3492 100644 --- a/vendor/google.golang.org/protobuf/internal/errors/is_go112.go +++ b/vendor/google.golang.org/protobuf/internal/errors/is_go112.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.13 // +build !go1.13 package errors diff --git a/vendor/google.golang.org/protobuf/internal/errors/is_go113.go b/vendor/google.golang.org/protobuf/internal/errors/is_go113.go index dc05f419..5e72f1cd 100644 --- a/vendor/google.golang.org/protobuf/internal/errors/is_go113.go +++ b/vendor/google.golang.org/protobuf/internal/errors/is_go113.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.13 // +build go1.13 package errors diff --git a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go index a72995f0..bda8e8cf 100644 --- a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go +++ b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !protolegacy // +build !protolegacy package flags diff --git a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go index 772e2f0e..6d8d9bd6 100644 --- a/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go +++ b/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build protolegacy // +build protolegacy package flags diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go index 2706bb67..4b15493f 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !go1.12 // +build !go1.12 package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go index 1533ef60..0b31b66e 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build go1.12 // +build go1.12 package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go b/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go index 90705e3a..145c577b 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go b/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go index e118af1e..757642e2 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/decode.go b/vendor/google.golang.org/protobuf/internal/impl/decode.go index 949dc49a..c65b0325 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/decode.go +++ b/vendor/google.golang.org/protobuf/internal/impl/decode.go @@ -18,6 +18,7 @@ import ( ) var errDecode = errors.New("cannot parse invalid wire-format data") +var errRecursionDepth = errors.New("exceeded maximum recursion depth") type unmarshalOptions struct { flags protoiface.UnmarshalInputFlags @@ -25,6 +26,7 @@ type unmarshalOptions struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + depth int } func (o unmarshalOptions) Options() proto.UnmarshalOptions { @@ -44,6 +46,7 @@ func (o unmarshalOptions) IsDefault() bool { var lazyUnmarshalOptions = unmarshalOptions{ resolver: preg.GlobalTypes, + depth: protowire.DefaultRecursionLimit, } type unmarshalOutput struct { @@ -62,6 +65,7 @@ func (mi *MessageInfo) unmarshal(in piface.UnmarshalInput) (piface.UnmarshalOutp out, err := mi.unmarshalPointer(in.Buf, p, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) var flags piface.UnmarshalOutputFlags if out.initialized { @@ -82,6 +86,10 @@ var errUnknown = errors.New("unknown") func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() + opts.depth-- + if opts.depth < 0 { + return out, errRecursionDepth + } if flags.ProtoLegacy && mi.isMessageSet { return unmarshalMessageSet(mi, b, p, opts) } diff --git a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go index 3759b010..029feeef 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go +++ b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go @@ -440,6 +440,13 @@ func legacyMerge(in piface.MergeInput) piface.MergeOutput { if !ok { return piface.MergeOutput{} } + if !in.Source.IsValid() { + // Legacy Marshal methods may not function on nil messages. + // Check for a typed nil source only after we confirm that + // legacy Marshal/Unmarshal methods are present, for + // consistency. + return piface.MergeOutput{Flags: piface.MergeComplete} + } b, err := marshaler.Marshal() if err != nil { return piface.MergeOutput{} diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go index 9e3ed821..4c491bdf 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go index 9ecf23a8..ee0e0573 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package impl diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go b/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go index 85e074c9..a1f6f333 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package strs diff --git a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go index 2160c701..56a8a4ed 100644 --- a/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go +++ b/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package strs diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index 5879131d..3d40d524 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,7 +52,7 @@ import ( // 10. Send out the CL for review and submit it. const ( Major = 1 - Minor = 26 + Minor = 28 Patch = 0 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/proto/decode.go b/vendor/google.golang.org/protobuf/proto/decode.go index 49f9b8c8..11bf7173 100644 --- a/vendor/google.golang.org/protobuf/proto/decode.go +++ b/vendor/google.golang.org/protobuf/proto/decode.go @@ -42,18 +42,25 @@ type UnmarshalOptions struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + + // RecursionLimit limits how deeply messages may be nested. + // If zero, a default limit is applied. + RecursionLimit int } // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). func Unmarshal(b []byte, m Message) error { - _, err := UnmarshalOptions{}.unmarshal(b, m.ProtoReflect()) + _, err := UnmarshalOptions{RecursionLimit: protowire.DefaultRecursionLimit}.unmarshal(b, m.ProtoReflect()) return err } // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { + if o.RecursionLimit == 0 { + o.RecursionLimit = protowire.DefaultRecursionLimit + } _, err := o.unmarshal(b, m.ProtoReflect()) return err } @@ -63,6 +70,9 @@ func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { // This method permits fine-grained control over the unmarshaler. // Most users should use Unmarshal instead. func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + if o.RecursionLimit == 0 { + o.RecursionLimit = protowire.DefaultRecursionLimit + } return o.unmarshal(in.Buf, in.Message) } @@ -86,12 +96,17 @@ func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out proto Message: m, Buf: b, Resolver: o.Resolver, + Depth: o.RecursionLimit, } if o.DiscardUnknown { in.Flags |= protoiface.UnmarshalDiscardUnknown } out, err = methods.Unmarshal(in) } else { + o.RecursionLimit-- + if o.RecursionLimit < 0 { + return out, errors.New("exceeded max recursion depth") + } err = o.unmarshalMessageSlow(b, m) } if err != nil { diff --git a/vendor/google.golang.org/protobuf/proto/proto_methods.go b/vendor/google.golang.org/protobuf/proto/proto_methods.go index d8dd604f..465e057b 100644 --- a/vendor/google.golang.org/protobuf/proto/proto_methods.go +++ b/vendor/google.golang.org/protobuf/proto/proto_methods.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // The protoreflect build tag disables use of fast-path methods. +//go:build !protoreflect // +build !protoreflect package proto diff --git a/vendor/google.golang.org/protobuf/proto/proto_reflect.go b/vendor/google.golang.org/protobuf/proto/proto_reflect.go index b103d432..494d6cee 100644 --- a/vendor/google.golang.org/protobuf/proto/proto_reflect.go +++ b/vendor/google.golang.org/protobuf/proto/proto_reflect.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // The protoreflect build tag disables use of fast-path methods. +//go:build protoreflect // +build protoreflect package proto diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go index 6be5d16e..d5d5af6e 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go @@ -53,6 +53,7 @@ type ( FindExtensionByName(field FullName) (ExtensionType, error) FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error) } + Depth int } unmarshalOutput = struct { pragma.NoUnkeyedLiterals diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go index 918e685e..7ced876f 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build purego || appengine // +build purego appengine package protoreflect diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go index 5a341472..eb7764c3 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go @@ -41,6 +41,31 @@ import ( // Converting to/from a Value and a concrete Go value panics on type mismatch. // For example, ValueOf("hello").Int() panics because this attempts to // retrieve an int64 from a string. +// +// List, Map, and Message Values are called "composite" values. +// +// A composite Value may alias (reference) memory at some location, +// such that changes to the Value updates the that location. +// A composite value acquired with a Mutable method, such as Message.Mutable, +// always references the source object. +// +// For example: +// // Append a 0 to a "repeated int32" field. +// // Since the Value returned by Mutable is guaranteed to alias +// // the source message, modifying the Value modifies the message. +// message.Mutable(fieldDesc).(List).Append(protoreflect.ValueOfInt32(0)) +// +// // Assign [0] to a "repeated int32" field by creating a new Value, +// // modifying it, and assigning it. +// list := message.NewField(fieldDesc).(List) +// list.Append(protoreflect.ValueOfInt32(0)) +// message.Set(fieldDesc, list) +// // ERROR: Since it is not defined whether Set aliases the source, +// // appending to the List here may or may not modify the message. +// list.Append(protoreflect.ValueOfInt32(0)) +// +// Some operations, such as Message.Get, may return an "empty, read-only" +// composite Value. Modifying an empty, read-only value panics. type Value value // The protoreflect API uses a custom Value union type instead of interface{} diff --git a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go index c45debdc..702ddf22 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go +++ b/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !purego && !appengine // +build !purego,!appengine package protoreflect diff --git a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go index 66dcbcd0..59f024c4 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go +++ b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go @@ -94,7 +94,8 @@ type Files struct { // Note that enum values are in the top-level since that are in the same // scope as the parent enum. descsByName map[protoreflect.FullName]interface{} - filesByPath map[string]protoreflect.FileDescriptor + filesByPath map[string][]protoreflect.FileDescriptor + numFiles int } type packageDescriptor struct { @@ -117,17 +118,16 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { r.descsByName = map[protoreflect.FullName]interface{}{ "": &packageDescriptor{}, } - r.filesByPath = make(map[string]protoreflect.FileDescriptor) + r.filesByPath = make(map[string][]protoreflect.FileDescriptor) } path := file.Path() - if prev := r.filesByPath[path]; prev != nil { + if prev := r.filesByPath[path]; len(prev) > 0 { r.checkGenProtoConflict(path) err := errors.New("file %q is already registered", file.Path()) - err = amendErrorWithCaller(err, prev, file) - if r == GlobalFiles && ignoreConflict(file, err) { - err = nil + err = amendErrorWithCaller(err, prev[0], file) + if !(r == GlobalFiles && ignoreConflict(file, err)) { + return err } - return err } for name := file.Package(); name != ""; name = name.Parent() { @@ -168,7 +168,8 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { r.descsByName[d.FullName()] = d }) - r.filesByPath[path] = file + r.filesByPath[path] = append(r.filesByPath[path], file) + r.numFiles++ return nil } @@ -308,6 +309,7 @@ func (s *nameSuffix) Pop() (name protoreflect.Name) { // FindFileByPath looks up a file by the path. // // This returns (nil, NotFound) if not found. +// This returns an error if multiple files have the same path. func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { if r == nil { return nil, NotFound @@ -316,13 +318,19 @@ func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) globalMutex.RLock() defer globalMutex.RUnlock() } - if fd, ok := r.filesByPath[path]; ok { - return fd, nil + fds := r.filesByPath[path] + switch len(fds) { + case 0: + return nil, NotFound + case 1: + return fds[0], nil + default: + return nil, errors.New("multiple files named %q", path) } - return nil, NotFound } -// NumFiles reports the number of registered files. +// NumFiles reports the number of registered files, +// including duplicate files with the same name. func (r *Files) NumFiles() int { if r == nil { return 0 @@ -331,10 +339,11 @@ func (r *Files) NumFiles() int { globalMutex.RLock() defer globalMutex.RUnlock() } - return len(r.filesByPath) + return r.numFiles } // RangeFiles iterates over all registered files while f returns true. +// If multiple files have the same name, RangeFiles iterates over all of them. // The iteration order is undefined. func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { if r == nil { @@ -344,9 +353,11 @@ func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { globalMutex.RLock() defer globalMutex.RUnlock() } - for _, file := range r.filesByPath { - if !f(file) { - return + for _, files := range r.filesByPath { + for _, file := range files { + if !f(file) { + return + } } } } diff --git a/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go b/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go index 32c04f67..44cf467d 100644 --- a/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go +++ b/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go @@ -103,6 +103,7 @@ type UnmarshalInput = struct { FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) } + Depth int } // UnmarshalOutput is output from the Unmarshal method. diff --git a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index f77239fc..abe4ab51 100644 --- a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -43,7 +43,6 @@ package descriptorpb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoiface "google.golang.org/protobuf/runtime/protoiface" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" @@ -829,15 +828,6 @@ func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{3} } -var extRange_ExtensionRangeOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use ExtensionRangeOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*ExtensionRangeOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_ExtensionRangeOptions -} - func (x *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -1520,15 +1510,6 @@ func (*FileOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{10} } -var extRange_FileOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use FileOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*FileOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_FileOptions -} - func (x *FileOptions) GetJavaPackage() string { if x != nil && x.JavaPackage != nil { return *x.JavaPackage @@ -1776,15 +1757,6 @@ func (*MessageOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{11} } -var extRange_MessageOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use MessageOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*MessageOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_MessageOptions -} - func (x *MessageOptions) GetMessageSetWireFormat() bool { if x != nil && x.MessageSetWireFormat != nil { return *x.MessageSetWireFormat @@ -1930,15 +1902,6 @@ func (*FieldOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{12} } -var extRange_FieldOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use FieldOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*FieldOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_FieldOptions -} - func (x *FieldOptions) GetCtype() FieldOptions_CType { if x != nil && x.Ctype != nil { return *x.Ctype @@ -2030,15 +1993,6 @@ func (*OneofOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{13} } -var extRange_OneofOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use OneofOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*OneofOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_OneofOptions -} - func (x *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2101,15 +2055,6 @@ func (*EnumOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{14} } -var extRange_EnumOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use EnumOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*EnumOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_EnumOptions -} - func (x *EnumOptions) GetAllowAlias() bool { if x != nil && x.AllowAlias != nil { return *x.AllowAlias @@ -2183,15 +2128,6 @@ func (*EnumValueOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{15} } -var extRange_EnumValueOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use EnumValueOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*EnumValueOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_EnumValueOptions -} - func (x *EnumValueOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -2258,15 +2194,6 @@ func (*ServiceOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{16} } -var extRange_ServiceOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use ServiceOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*ServiceOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_ServiceOptions -} - func (x *ServiceOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -2335,15 +2262,6 @@ func (*MethodOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{17} } -var extRange_MethodOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use MethodOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*MethodOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_MethodOptions -} - func (x *MethodOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated diff --git a/vendor/modules.txt b/vendor/modules.txt index 846ffcb7..8f3587a2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -48,6 +48,7 @@ github.com/golang/groupcache/lru # github.com/golang/protobuf v1.5.2 ## explicit github.com/golang/protobuf/proto +github.com/golang/protobuf/protoc-gen-go/descriptor github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration @@ -231,7 +232,7 @@ google.golang.org/appengine/internal/modules google.golang.org/appengine/internal/remote_api google.golang.org/appengine/internal/urlfetch google.golang.org/appengine/urlfetch -# google.golang.org/protobuf v1.26.0 +# google.golang.org/protobuf v1.28.0 ## explicit google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire From 9aaa36a188028e463a53ba2dff608044c53aa2e6 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Tue, 24 May 2022 17:17:59 +0200 Subject: [PATCH 07/16] Fix action config ID parsing --- pkg/action/action.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index a278108b..c9f4e722 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -15,16 +15,13 @@ type ActionPayload interface{} // Action contains the details of action. type Action struct { + ConfigID string `json:"configurationId,omitempty"` Signature string `json:"signature,omitempty"` Body string `json:"body,omitempty"` ActionPayload ActionPayload } -type body struct { - ConfigID string `json:"configurationId,omitempty"` -} - // ActionHandler is a common interface of Action implementations. type ActionHandler interface { UnMarshal([]byte, *Action) error @@ -73,29 +70,23 @@ func (as *ActionService) Do(rawAction []byte, updateState func(interface{}) erro return } - as.logger.V(3).Info("Validate data integrity") + state.Meta.ConfigurationId = action.ConfigID + logger := as.logger.WithValues("config", action.ConfigID) + + logger.V(3).Info("Validate data integrity") err = utils.ValidateDataIntegrity(action.Signature, as.signatureKey, []byte(action.Body)) if err != nil { - as.logger.Error(err, "unable validate data integrity") + logger.Error(err, "unable validate data integrity") return } var rawBody []byte rawBody, err = base64.StdEncoding.DecodeString(action.Body) if err != nil { - as.logger.Error(err, "unable decode action body") + logger.Error(err, "unable decode action body") return } - body := body{} - if err = json.Unmarshal(rawBody, &body); err != nil { - as.logger.Error(err, "unable parse action body") - return - } - - state.Meta.ConfigurationId = body.ConfigID - logger := as.logger.WithValues("config", body.ConfigID) - for _, handler := range as.registeredActions { if err = handler.UnMarshal(rawBody, &action); err != nil { continue From 9e530cf0b9b372bd8d1fdb0f969bc9ce4aca3d68 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 10:38:29 +0200 Subject: [PATCH 08/16] Fix MQTT and StorageOS connections --- endpoints/portal.go | 31 ++++++------ endpoints/storageos.go | 6 +++ main.go | 6 ++- pkg/action/licence/licence.go | 4 +- pkg/publisher/sinks/gcp-iot/iot-core.go | 64 +++++++++++++++---------- pkg/storageos/client.go | 61 ++++++++++++----------- pkg/utils/http.go | 18 +++---- 7 files changed, 112 insertions(+), 78 deletions(-) diff --git a/endpoints/portal.go b/endpoints/portal.go index 535409cb..ddde77ea 100644 --- a/endpoints/portal.go +++ b/endpoints/portal.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math" + "net/http" "sync" "time" @@ -53,7 +54,7 @@ func (e *PortalEndpoint) RegisterDevice(pubKeyPem []byte) error { headers := utils.NewHeaders(utils.ContentType) headers["Authorization"] = fmt.Sprintf("Bearer %s", token.AccessToken) - _, err := utils.PostHttpContent(e.url+"/setup/register-cluster", req, headers) + _, _, err := utils.PostHttpContent(e.url+"/setup/register-cluster", req, headers) if err != nil { return errors.Wrap(err, "unable to send register request") } @@ -118,7 +119,7 @@ func (e *PortalEndpoint) GetConfig() (*v1alpha1.IOTCoreConfig, error) { headers := utils.NewHeaders(utils.ContentType) headers["Authorization"] = fmt.Sprintf("Bearer %s", e.token.AccessToken) - resp, err := utils.GetHttpContent(e.url+"/setup/configuration", headers) + resp, _, err := utils.GetHttpContent(e.url+"/setup/configuration", headers) if err != nil { return nil, err } @@ -129,19 +130,21 @@ func (e *PortalEndpoint) GetConfig() (*v1alpha1.IOTCoreConfig, error) { return nil, err } - resp, err = utils.GetHttpContent(e.url+"/setup/signature-key?clusterId="+e.clusterID, headers) - if err != nil { - return nil, err - } + var pubPEM []byte - signResp := &SignatureResponse{} - err = json.Unmarshal(resp, signResp) - if err != nil { - return nil, err - } - pubPEM, err := base64.StdEncoding.DecodeString(signResp.PublicSignKey) - if err != nil { + resp, code, err := utils.GetHttpContent(e.url+"/setup/signature-key?clusterId="+e.clusterID, headers) + if err != nil && code != http.StatusNotFound { return nil, err + } else if code != http.StatusNotFound { + signResp := &SignatureResponse{} + err = json.Unmarshal(resp, signResp) + if err != nil { + return nil, err + } + pubPEM, err = base64.StdEncoding.DecodeString(signResp.PublicSignKey) + if err != nil { + return nil, err + } } iotConfig := v1alpha1.IOTCoreConfig{ @@ -173,7 +176,7 @@ func (e *PortalEndpoint) refreshToken() (int, error) { Secret: e.password, } - resp, err := utils.PostHttpContent(e.url+"/auth/token", req, utils.NewHeaders(utils.ContentType)) + resp, _, err := utils.PostHttpContent(e.url+"/auth/token", req, utils.NewHeaders(utils.ContentType)) if err != nil { return -1, errors.Wrap(err, "unable to send token request") } diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 5977e294..4f5f67fa 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -1,6 +1,9 @@ package endpoints import ( + "context" + "time" + "github.com/go-logr/logr" "github.com/pkg/errors" stosapiv2 "github.com/storageos/go-api/v2" @@ -49,6 +52,9 @@ func (e *StorageOSEndpoint) Start() error { e.logger.V(3).Info("Storageos endpoint started", "ClusterID", e.clusterID) + // TODO would be nice to properly stop refresh. + go e.client.Refresh(context.Background(), nil, time.Minute) + return nil } diff --git a/main.go b/main.go index ee02c8df..3fdb3230 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,9 @@ const ( // stosPasswordKey is the key in the secret that holds the password value. stosPasswordKey = "password" + + // licenceUpdatePeriod is the frequency of licence status messages. + licenceUpdatePeriod = 5 * time.Minute ) var ( @@ -258,7 +261,8 @@ func main() { // Periodically send licence. go func() { - ticker := time.NewTicker(time.Minute) + // TODO would be nice to stop properly gorutine + ticker := time.NewTicker(licenceUpdatePeriod) for { currentLicence, err := stosEndpoint.GetLicence() if err != nil { diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index f5012817..b5cd3628 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -46,13 +46,13 @@ func (h *LicenceActionHandler) Do(payload action.ActionPayload, actionState *sta return errors.New("unable to cast payload to licence") } - h.logger.V(3).Info("Updating license") - licence, err := base64.StdEncoding.DecodeString(desiredLicence.Value) if err != nil { return errors.Wrap(err, "unable decode licence") } + h.logger.V(3).Info("Updating license") + currentLicence, err := h.storageOSEndpoint.UpdateLicence(string(licence)) if err != nil { return errors.Wrap(err, "unable update licence") diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index c04fdd3a..5900cbeb 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -22,13 +22,14 @@ const ( Name = "iotcore" // MQTT parameters - keyLifeTime = 20 //minutes - topicType = "events" // or "state" - qos = 1 // QoS 2 isn't supported in GCP - retain = false - username = "unused" // always this value in GCP - devicePrefix = "clusterId-" - publishStateWait = 2 * time.Second + keyLifeTime = 20 //minutes + topicType = "events" // or "state" + qos = 1 // QoS 2 isn't supported in GCP + retain = false + username = "unused" // always this value in GCP + devicePrefix = "clusterId-" + publishStateWait = 2 * time.Second + keepAliveDuration = 2 * time.Second ) var ( @@ -120,6 +121,14 @@ func (m *MQTT) Init() error { return errors.Wrap(err, "failed to get connection") } + m.conn.AddRoute(m.ConfigTopic(), func(_ mqtt.Client, msg mqtt.Message) { + m.logger.V(5).Info("Event received", "message", msg) + + m.deviceConfigCallback(msg.Payload(), func(cm interface{}) error { + return m.PublishState(context.Background(), cm) + }) + }) + return nil } @@ -144,7 +153,31 @@ func (m *MQTT) newClient() error { SetUsername(username). SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}). SetPassword(pass). + SetKeepAlive(keepAliveDuration). SetConnectRetry(true). + SetConnectRetryInterval(time.Second). + SetAutoReconnect(true). + SetResumeSubs(true). + SetCleanSession(false). + SetConnectionLostHandler(func(_ mqtt.Client, err error) { + m.logger.Error(err, "Connection has lost") + }). + SetOnConnectHandler(func(c mqtt.Client) { + m.logger.V(3).Info("Manage device config subscription...") + + for range make([]int, 3) { + token := c.Subscribe(m.ConfigTopic(), qos, nil) + if !token.WaitTimeout(time.Second) { + m.logger.V(2).Info("Subscribe timeout") + continue + } else if token.Error() != nil { + m.logger.V(2).Info("Subscribe failed") + continue + } + + break + } + }). SetProtocolVersion(4) // Use MQTT 3.1.1 m.conn = mqtt.NewClient(opts) @@ -175,23 +208,6 @@ func (m *MQTT) getConnection() (mqtt.Client, error) { if !m.conn.IsConnected() { m.logger.V(3).Info("Reconnecting...") m.attemptConnect() - - m.logger.V(3).Info("Manage device config subscription...") - - token := m.conn.Subscribe(m.ConfigTopic(), qos, func(_ mqtt.Client, msg mqtt.Message) { - m.logger.V(5).Info("Event received", "message", msg) - - m.deviceConfigCallback(msg.Payload(), func(cm interface{}) error { - return m.PublishState(context.Background(), cm) - }) - }) - if !token.WaitTimeout(time.Second) { - m.logger.V(2).Info("Subscribe timeout") - return m.conn, errors.Wrap(token.Error(), "subscribe timeout") - } else if token.Error() != nil { - m.logger.V(2).Info("Subscribe failed") - return m.conn, errors.Wrap(token.Error(), "subscribe failed") - } } return m.conn, nil diff --git a/pkg/storageos/client.go b/pkg/storageos/client.go index 27998119..c9122412 100644 --- a/pkg/storageos/client.go +++ b/pkg/storageos/client.go @@ -165,36 +165,41 @@ func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time for { select { case <-time.After(interval): - // Refresh api token before it expires. Default is 5 minute expiry. - // Refresh will fail if the token has already expired. - _, resp, err := c.api.RefreshJwt(c.ctx) - if err != nil { - c.logger.V(2).Info("Failed to refresh storageos api credentials, reauthenticating immediately", "error", err) - // Reset the api client directly on refresh errors. We can't - // send to the reset channel as it may cause a deadlock. We may - // also have a reset queued in the channel but that's ok, we'll - // just reset twice. - // - // We need to trigger here since the standby manager won't have - // controllers polling the api, so the token refresh is the only - // place we'll detect an error. - newCtx, err := c.resetClient() + func() { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + // Refresh api token before it expires. Default is 5 minute expiry. + // Refresh will fail if the token has already expired. + _, resp, err := c.api.RefreshJwt(ctx) if err != nil { - c.logger.Error(err, "failed to reset storageos api client") - continue + c.logger.V(2).Info("Failed to refresh storageos api credentials, reauthenticating immediately", "error", err) + // Reset the api client directly on refresh errors. We can't + // send to the reset channel as it may cause a deadlock. We may + // also have a reset queued in the channel but that's ok, we'll + // just reset twice. + // + // We need to trigger here since the standby manager won't have + // controllers polling the api, so the token refresh is the only + // place we'll detect an error. + newCtx, err := c.resetClient() + if err != nil { + c.logger.Error(err, "failed to reset storageos api client") + return + } + c.ctx = newCtx + c.logger.V(3).Info("Reset storageos api client") + return } - c.ctx = newCtx - c.logger.V(3).Info("Reset storageos api client") - continue - } - defer resp.Body.Close() - token := respAuthToken(resp) - if token == "" { - c.logger.V(2).Info("Failed to refresh storageos api credentials", "error", ErrNoAuthToken) - continue - } - c.ctx = context.WithValue(c.ctx, stosapiv2.ContextAccessToken, token) - c.logger.V(3).Info("Refreshed storageos api token") + defer resp.Body.Close() + token := respAuthToken(resp) + if token == "" { + c.logger.V(2).Info("Failed to refresh storageos api credentials", "error", ErrNoAuthToken) + return + } + c.ctx = context.WithValue(c.ctx, stosapiv2.ContextAccessToken, token) + c.logger.V(3).Info("Refreshed storageos api token") + }() case <-reset: c.logger.V(5).Info("Processing request to reset storageos api client") newCtx, err := c.resetClient() diff --git a/pkg/utils/http.go b/pkg/utils/http.go index 319b9f9e..1207fe60 100644 --- a/pkg/utils/http.go +++ b/pkg/utils/http.go @@ -17,23 +17,23 @@ import ( var httpLogger = ctrl.Log.WithName("http") // GetHttpContent downloads something from the given URL. -func GetHttpContent(url string, headers map[string]string) ([]byte, error) { +func GetHttpContent(url string, headers map[string]string) ([]byte, int, error) { return doHttpRequest(http.MethodGet, url, nil, headers) } // PostHttpContent sends something to the given URL. -func PostHttpContent(url string, data interface{}, headers map[string]string) ([]byte, error) { +func PostHttpContent(url string, data interface{}, headers map[string]string) ([]byte, int, error) { httpLogger.V(5).Info("Request", "method", http.Post, "url", url, "body", data) jsonData, err := json.Marshal(data) if err != nil { - return nil, errors.Wrap(err, "unable to marshal post body") + return nil, 0, errors.Wrap(err, "unable to marshal post body") } return doHttpRequest(http.MethodPost, url, strings.NewReader(string(jsonData)), headers) } -func doHttpRequest(method, url string, body io.Reader, headers map[string]string) ([]byte, error) { +func doHttpRequest(method, url string, body io.Reader, headers map[string]string) ([]byte, int, error) { logger := httpLogger.WithValues("method", method, "url", url) logger.V(3).Info("Connecting...") @@ -42,7 +42,7 @@ func doHttpRequest(method, url string, body io.Reader, headers map[string]string req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { - return nil, errors.Wrap(err, "unable to create new HTTP request") + return nil, 0, errors.Wrap(err, "unable to create new HTTP request") } for k, v := range headers { @@ -52,25 +52,25 @@ func doHttpRequest(method, url string, body io.Reader, headers map[string]string resp, err := http.DefaultClient.Do(req) if err != nil { logger.Error(err, "Failed to fetch content") - return nil, errors.Wrap(err, "unable to fetch HTTP content") + return nil, 0, errors.Wrap(err, "unable to fetch HTTP content") } logger.V(2).Info("Finished", "code", resp.StatusCode) if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusMultipleChoices { - return nil, fmt.Errorf("error fetching content of %s, status code: %d", url, resp.StatusCode) + return nil, resp.StatusCode, fmt.Errorf("error fetching content of %s, status code: %d", url, resp.StatusCode) } defer resp.Body.Close() content, err := ioutil.ReadAll(resp.Body) if err != nil { logger.Error(err, "Unable to read content") - return nil, errors.Wrap(err, "unable to read HTTP response") + return nil, resp.StatusCode, errors.Wrap(err, "unable to read HTTP response") } logger.V(5).Info("Response", "body", string(content)) - return content, nil + return content, resp.StatusCode, nil } const ( From 3b73ddd7117bb377fea086c54434fbeaa27e96a6 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 10:45:37 +0200 Subject: [PATCH 09/16] Fix error handling of storageos client refresh --- endpoints/storageos.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 4f5f67fa..48ce695d 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -53,7 +53,13 @@ func (e *StorageOSEndpoint) Start() error { e.logger.V(3).Info("Storageos endpoint started", "ClusterID", e.clusterID) // TODO would be nice to properly stop refresh. - go e.client.Refresh(context.Background(), nil, time.Minute) + go func() { + for { + if err := e.client.Refresh(context.Background(), nil, time.Minute); err != nil { + e.logger.Error(err, "Failed to start refresh") + } + } + }() return nil } From 32d1fc38c07e9b84caeecf3aa8948f18a79cb8ac Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 11:13:55 +0200 Subject: [PATCH 10/16] Fix IOT core logging name --- endpoints/storageos.go | 2 ++ pkg/publisher/publisher.go | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 48ce695d..6b37ab5f 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -58,6 +58,8 @@ func (e *StorageOSEndpoint) Start() error { if err := e.client.Refresh(context.Background(), nil, time.Minute); err != nil { e.logger.Error(err, "Failed to start refresh") } + + time.Sleep(time.Second) } }() diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 6755e059..4e3f2f91 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -25,6 +25,8 @@ type Publisher struct { } func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(interface{}) error), logger logr.Logger) (*Publisher, error) { + logger = logger.WithName("publisher") + sinks := make(map[string]Sink) if config.IOTCore != nil { sink, err := gcp.New(clusterID, privateKeyPem, config.IOTCore, deviceConfigCallback, logger) @@ -35,7 +37,7 @@ func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.Portal } return &Publisher{ sinks: sinks, - logger: logger.WithName("publisher"), + logger: logger, }, nil } From a005858718966dd042a898eb632dcb4046c91183 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 11:27:54 +0200 Subject: [PATCH 11/16] Make scripts in hack executable --- hack/generate-tests.sh | 0 hack/grep-iot-logs.sh | 0 hack/update-kind-nodes.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 hack/generate-tests.sh mode change 100644 => 100755 hack/grep-iot-logs.sh mode change 100644 => 100755 hack/update-kind-nodes.sh diff --git a/hack/generate-tests.sh b/hack/generate-tests.sh old mode 100644 new mode 100755 diff --git a/hack/grep-iot-logs.sh b/hack/grep-iot-logs.sh old mode 100644 new mode 100755 diff --git a/hack/update-kind-nodes.sh b/hack/update-kind-nodes.sh old mode 100644 new mode 100755 From 9b3156c189194705ff595f20f442be37defc7073 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 12:27:01 +0200 Subject: [PATCH 12/16] Retry action, Mutex for IOT subscribe, logging polish --- endpoints/storageos.go | 2 +- pkg/action/action.go | 13 ++++++++----- pkg/action/licence/licence.go | 3 +-- pkg/publisher/sinks/gcp-iot/iot-core.go | 9 +++++++-- pkg/storageos/client.go | 13 ++----------- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/endpoints/storageos.go b/endpoints/storageos.go index 6b37ab5f..cc122ac1 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -55,7 +55,7 @@ func (e *StorageOSEndpoint) Start() error { // TODO would be nice to properly stop refresh. go func() { for { - if err := e.client.Refresh(context.Background(), nil, time.Minute); err != nil { + if err := e.client.Refresh(context.Background(), time.Minute); err != nil { e.logger.Error(err, "Failed to start refresh") } diff --git a/pkg/action/action.go b/pkg/action/action.go index c9f4e722..1cb13e63 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -92,11 +92,14 @@ func (as *ActionService) Do(rawAction []byte, updateState func(interface{}) erro continue } - logger.V(3).Info("Executing action") - if err = handler.Do(action.ActionPayload, &state); err != nil { - logger.Error(err, "unable to execute action") - } else { - logger.V(2).Info("Action succeeded") + for i := 0; i < 5; i++ { + logger.V(3).Info("Executing action") + if err = handler.Do(action.ActionPayload, &state); err != nil { + logger.Error(err, "unable to execute action") + } else { + logger.V(2).Info("Action succeeded") + break + } } return diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index b5cd3628..adc9977f 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -12,8 +12,7 @@ import ( ) type wrapper struct { - Licence licence `json:"licence,omitempty"` - ConfigID string `json:"configurationId,omitempty"` + Licence licence `json:"licence,omitempty"` } type licence struct { diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index 5900cbeb..1c36e270 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -147,6 +147,8 @@ func (m *MQTT) newClient() error { return errors.Wrap(err, "failed to generate password") } + mu := sync.Mutex{} + opts := mqtt.NewClientOptions(). AddBroker(m.serverURL). SetClientID(m.ClientID()). @@ -160,12 +162,15 @@ func (m *MQTT) newClient() error { SetResumeSubs(true). SetCleanSession(false). SetConnectionLostHandler(func(_ mqtt.Client, err error) { - m.logger.Error(err, "Connection has lost") + m.logger.V(5).Info("Connection has lost", "error", err) }). SetOnConnectHandler(func(c mqtt.Client) { + mu.Lock() + defer mu.Unlock() + m.logger.V(3).Info("Manage device config subscription...") - for range make([]int, 3) { + for i := 0; i < 3; i++ { token := c.Subscribe(m.ConfigTopic(), qos, nil) if !token.WaitTimeout(time.Second) { m.logger.V(2).Info("Subscribe timeout") diff --git a/pkg/storageos/client.go b/pkg/storageos/client.go index c9122412..5970f21e 100644 --- a/pkg/storageos/client.go +++ b/pkg/storageos/client.go @@ -158,7 +158,7 @@ func (c *Client) initAuthenticatedClient() error { // Errors are currently logged at info level since they will be retried and // should be recoverable. Only a cancelled context will cause this to stop. Be // aware that any errors returned will trigger a process shutdown. -func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time.Duration) error { +func (c *Client) Refresh(ctx context.Context, interval time.Duration) error { if c.api == nil || c.transport == nil { return ErrNotInitialized } @@ -173,7 +173,7 @@ func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time // Refresh will fail if the token has already expired. _, resp, err := c.api.RefreshJwt(ctx) if err != nil { - c.logger.V(2).Info("Failed to refresh storageos api credentials, reauthenticating immediately", "error", err) + c.logger.V(5).Info("Failed to refresh storageos api credentials, reauthenticating immediately", "error", err) // Reset the api client directly on refresh errors. We can't // send to the reset channel as it may cause a deadlock. We may // also have a reset queued in the channel but that's ok, we'll @@ -200,15 +200,6 @@ func (c *Client) Refresh(ctx context.Context, reset chan struct{}, interval time c.ctx = context.WithValue(c.ctx, stosapiv2.ContextAccessToken, token) c.logger.V(3).Info("Refreshed storageos api token") }() - case <-reset: - c.logger.V(5).Info("Processing request to reset storageos api client") - newCtx, err := c.resetClient() - if err != nil { - c.logger.Error(err, "failed to reset storageos api client") - continue - } - c.ctx = newCtx - c.logger.V(5).Info("Reset storageos api client") case <-ctx.Done(): // Clean shutdown. return ctx.Err() From 29242ea4c6183ce3c275321042235a39483d798a Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Wed, 25 May 2022 15:13:40 +0200 Subject: [PATCH 13/16] Fix licence report error handling --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3fdb3230..57a277ed 100644 --- a/main.go +++ b/main.go @@ -267,7 +267,7 @@ func main() { currentLicence, err := stosEndpoint.GetLicence() if err != nil { setupLogger.Error(err, "unable to fetch licence") - return + continue } licenceState := state.ActionState{ From 9663577f938e1ad76eda7810cfe88cbe312c2218 Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Thu, 26 May 2022 11:54:05 +0200 Subject: [PATCH 14/16] Fix message broker connection leak --- pkg/publisher/sinks/gcp-iot/iot-core.go | 254 ++++++++++-------------- pkg/utils/jwt.go | 22 ++ 2 files changed, 124 insertions(+), 152 deletions(-) create mode 100644 pkg/utils/jwt.go diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index 1c36e270..9f137293 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -15,6 +15,7 @@ import ( "github.com/pkg/errors" storageosv1alpha1 "github.com/storageos/portal-manager/api/v1alpha1" "github.com/storageos/portal-manager/pkg/publisher/proto/portal" + "github.com/storageos/portal-manager/pkg/utils" "google.golang.org/protobuf/proto" ) @@ -29,7 +30,7 @@ const ( username = "unused" // always this value in GCP devicePrefix = "clusterId-" publishStateWait = 2 * time.Second - keepAliveDuration = 2 * time.Second + keepAliveDuration = 5 * time.Second ) var ( @@ -53,12 +54,12 @@ type MQTT struct { privateKeyPem []byte privateKey *rsa.PrivateKey - mu sync.Mutex - stateMu sync.Mutex - conn mqtt.Client - tokenExpired bool - logger logr.Logger - serverURL string + mu sync.Mutex + stateMu sync.Mutex + stopped bool + opts *mqtt.ClientOptions + conn mqtt.Client + logger logr.Logger } func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreConfig, deviceConfigCallback func([]byte, func(interface{}) error), logger logr.Logger) (*MQTT, error) { @@ -85,7 +86,7 @@ func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreC return nil, ErrPrivateKeyInvalid } - return &MQTT{ + m := &MQTT{ deviceConfigCallback: deviceConfigCallback, device: devicePrefix + device, project: config.Project, @@ -93,10 +94,22 @@ func New(device string, privateKeyPem []byte, config *storageosv1alpha1.IOTCoreC region: config.Region, privateKeyPem: privateKeyPem, privateKey: privateKey, - serverURL: config.ServerURL, - tokenExpired: true, logger: logger.WithName("iot"), - }, nil + } + + m.opts = mqtt.NewClientOptions(). + SetProtocolVersion(4). // Use MQTT 3.1.1 + AddBroker(config.ServerURL). + SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}). + SetKeepAlive(keepAliveDuration). + SetConnectRetry(true). + SetConnectRetryInterval(time.Second). + SetConnectionLostHandler(m.onConnectionLost). + SetClientID(m.ClientID()). + SetUsername(username). + SetOnConnectHandler(m.onConnectHandler) + + return m, nil } func (m *MQTT) ClientID() string { @@ -116,142 +129,31 @@ func (m *MQTT) ConfigTopic() string { } func (m *MQTT) Init() error { - if _, err := m.getConnection(); err != nil { - m.logger.V(2).Info("Failed to get connection") - return errors.Wrap(err, "failed to get connection") - } - - m.conn.AddRoute(m.ConfigTopic(), func(_ mqtt.Client, msg mqtt.Message) { - m.logger.V(5).Info("Event received", "message", msg) - - m.deviceConfigCallback(msg.Payload(), func(cm interface{}) error { - return m.PublishState(context.Background(), cm) - }) - }) - - return nil -} - -// newClient creates a new mqtt client -func (m *MQTT) newClient() error { - now := time.Now() - // IOT Core doesn't work with RegisteredClaims - // nolint:staticcheck - jwtToken := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), &jwt.StandardClaims{ - IssuedAt: now.Unix(), - ExpiresAt: now.Add(time.Minute * keyLifeTime).Unix(), - Audience: m.project, - }) - pass, err := jwtToken.SignedString(m.privateKey) - if err != nil { - return errors.Wrap(err, "failed to generate password") - } - - mu := sync.Mutex{} - - opts := mqtt.NewClientOptions(). - AddBroker(m.serverURL). - SetClientID(m.ClientID()). - SetUsername(username). - SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}). - SetPassword(pass). - SetKeepAlive(keepAliveDuration). - SetConnectRetry(true). - SetConnectRetryInterval(time.Second). - SetAutoReconnect(true). - SetResumeSubs(true). - SetCleanSession(false). - SetConnectionLostHandler(func(_ mqtt.Client, err error) { - m.logger.V(5).Info("Connection has lost", "error", err) - }). - SetOnConnectHandler(func(c mqtt.Client) { - mu.Lock() - defer mu.Unlock() - - m.logger.V(3).Info("Manage device config subscription...") - - for i := 0; i < 3; i++ { - token := c.Subscribe(m.ConfigTopic(), qos, nil) - if !token.WaitTimeout(time.Second) { - m.logger.V(2).Info("Subscribe timeout") - continue - } else if token.Error() != nil { - m.logger.V(2).Info("Subscribe failed") - continue - } - - break - } - }). - SetProtocolVersion(4) // Use MQTT 3.1.1 - - m.conn = mqtt.NewClient(opts) - - m.tokenExpired = false - time.AfterFunc(time.Minute*(keyLifeTime-1), func() { - m.mu.Lock() - defer m.mu.Unlock() - - m.logger.V(3).Info("Token has expired") - m.tokenExpired = true - }) + m.onConnectionLost(nil, nil) return nil } -func (m *MQTT) getConnection() (mqtt.Client, error) { +// Shutdown the sink. +func (m *MQTT) Shutdown() error { m.mu.Lock() defer m.mu.Unlock() - if m.tokenExpired { - m.logger.V(3).Info("Initializing...") - if err := m.newClient(); err != nil { - return m.conn, errors.Wrap(err, "failed to create new client") - } - } - - if !m.conn.IsConnected() { - m.logger.V(3).Info("Reconnecting...") - m.attemptConnect() - } - - return m.conn, nil -} + m.logger.V(3).Info("Shutdown...") -func (m *MQTT) attemptConnect() { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() + m.stopped = true - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - m.logger.V(3).Info("Iot sink connecting...") - token := m.conn.Connect() - if err := token.Error(); err != nil { - m.logger.V(3).Error(errors.Wrap(err, "sink connection failure"), "failed to connect") - continue - } - if !token.WaitTimeout(time.Second * 5) { - m.logger.V(3).Error(errors.New("sink connection timeout"), "connection timeout") - continue - } - - m.logger.V(3).Info("Iot sink initialized") - return - case <-ctx.Done(): - m.logger.V(3).Info("Disconnecting...") - - m.conn.Disconnect(0) - panic(errors.Wrap(ctx.Err(), "failed attempting to connect client")) - } + if m.conn != nil { + m.conn.Disconnect(0) } + return nil } // Publish will publish event. func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { + m.mu.Lock() + defer m.mu.Unlock() + logger := m.logger.WithValues("object", fmt.Sprintf("%s/%s", event.ObjectMeta.GetNamespace(), event.ObjectMeta.GetName()), "action", event.EventType) payload, err := proto.Marshal(event) @@ -265,12 +167,7 @@ func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() - conn, err := m.getConnection() - if err != nil { - return errors.Wrap(err, "failed to get connection") - } - - token := conn.Publish(m.PublishTopic(), qos, retain, payload) + token := m.conn.Publish(m.PublishTopic(), qos, retain, payload) select { case <-token.Done(): if token.Error() != nil { @@ -288,6 +185,9 @@ func (m *MQTT) Publish(ctx context.Context, event *portal.Event) error { // PublishState will publish state. func (m *MQTT) PublishState(ctx context.Context, state interface{}) error { + m.mu.Lock() + defer m.mu.Unlock() + m.stateMu.Lock() payload, err := json.Marshal(state) @@ -301,13 +201,7 @@ func (m *MQTT) PublishState(ctx context.Context, state interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() - conn, err := m.getConnection() - if err != nil { - m.stateMu.Unlock() - return errors.Wrap(err, "failed to get connection") - } - - token := conn.Publish(m.StateTopic(), qos, retain, payload) + token := m.conn.Publish(m.StateTopic(), qos, retain, payload) time.AfterFunc(publishStateWait, m.stateMu.Unlock) @@ -326,15 +220,71 @@ func (m *MQTT) PublishState(ctx context.Context, state interface{}) error { return nil } -// Shutdown the sink. -func (m *MQTT) Shutdown() error { +func (m *MQTT) onConnectionLost(conn mqtt.Client, err error) { m.mu.Lock() defer m.mu.Unlock() - m.logger.V(3).Info("Shutdown...") + if m.stopped { + return + } - if m.conn != nil { - m.conn.Disconnect(0) + if conn != nil { + m.logger.V(3).Info("Connection has lost", "error", err) + if conn.IsConnected() { + conn.Disconnect(0) + } } - return nil + + m.opts.Password, err = utils.GenerateJWTToken(time.Minute*keyLifeTime, m.project, m.privateKey) + if err != nil { + panic(errors.Wrap(err, "failed to generate password")) + } + + conn = mqtt.NewClient(m.opts) + + for i := 0; i < 3; i++ { + m.logger.V(3).Info("Iot sink connecting...") + token := conn.Connect() + if err := token.Error(); err != nil { + m.logger.V(3).Error(errors.Wrap(err, "sink connection failure"), "failed to connect") + continue + } + if !token.WaitTimeout(time.Second * 5) { + m.logger.V(3).Error(errors.New("sink connection timeout"), "connection timeout") + continue + } + + m.logger.V(3).Info("Iot sink initialized") + m.conn = conn + return + } + + panic(errors.New("Failed to connect Iot sink")) +} + +func (m *MQTT) onConnectHandler(conn mqtt.Client) { + m.logger.V(3).Info("Manage device config subscription...") + + conn.AddRoute(m.ConfigTopic(), func(_ mqtt.Client, msg mqtt.Message) { + m.logger.V(5).Info("Event received", "message", msg) + + m.deviceConfigCallback(msg.Payload(), func(cm interface{}) error { + return m.PublishState(context.Background(), cm) + }) + }) + + for i := 0; i < 3; i++ { + token := conn.Subscribe(m.ConfigTopic(), qos, nil) + if !token.WaitTimeout(time.Second) { + m.logger.V(2).Info("Subscribe timeout") + continue + } else if token.Error() != nil { + m.logger.V(2).Info("Subscribe failed") + continue + } + + return + } + + panic(errors.New("Failed to handle Iot sink connection")) } diff --git a/pkg/utils/jwt.go b/pkg/utils/jwt.go new file mode 100644 index 00000000..15cfe584 --- /dev/null +++ b/pkg/utils/jwt.go @@ -0,0 +1,22 @@ +package utils + +import ( + "time" + + "github.com/golang-jwt/jwt/v4" +) + +// GenerateJWTToken generates a JWT token. +func GenerateJWTToken(keyLifeTime time.Duration, project string, key interface{}) (string, error) { + now := time.Now() + + // IOT Core doesn't work with RegisteredClaims + // nolint:staticcheck + jwtToken := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), &jwt.StandardClaims{ + IssuedAt: now.Unix(), + ExpiresAt: now.Add(keyLifeTime).Unix(), + Audience: project, + }) + + return jwtToken.SignedString(key) +} From 0fc1273b1468660b4b0b2feaa7ca03cbf86dc08a Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Thu, 26 May 2022 15:09:27 +0200 Subject: [PATCH 15/16] Final polish --- api/v1alpha1/portalconfig_types.go | 15 +++ api/v1alpha1/storageosportal_types.go | 15 +++ controllers/config_controller.go | 95 ++++++++++++++ controllers/publish_controller.go | 15 +++ controllers/suite_test.go | 15 +++ controllers/watch_controller.go | 15 +++ endpoints/portal.go | 37 ++++-- endpoints/storageos.go | 15 +++ main.go | 168 ++++++++++-------------- managers/device.go | 19 ++- pkg/action/action.go | 28 +++- pkg/action/licence/licence.go | 15 +++ pkg/action/state/state.go | 15 +++ pkg/handler/event_queuer.go | 15 +++ pkg/publisher/publisher.go | 33 ++++- pkg/publisher/sinks/gcp-iot/iot-core.go | 17 ++- pkg/publisher/sinks/noop/noop.go | 23 +++- pkg/storageos/client.go | 15 +++ pkg/utils/cert.go | 15 +++ pkg/utils/cert_test.go | 15 +++ pkg/utils/file.go | 15 +++ pkg/utils/http.go | 15 +++ pkg/utils/http_test.go | 15 +++ pkg/utils/jwt.go | 15 +++ pkg/utils/signature.go | 15 +++ pkg/utils/signature_test.go | 15 +++ watchers/configmap.go | 110 ---------------- watchers/consts.go | 3 - watchers/secret.go | 102 -------------- 29 files changed, 568 insertions(+), 337 deletions(-) create mode 100644 controllers/config_controller.go delete mode 100644 watchers/configmap.go delete mode 100644 watchers/consts.go delete mode 100644 watchers/secret.go diff --git a/api/v1alpha1/portalconfig_types.go b/api/v1alpha1/portalconfig_types.go index 2085fc23..eee2096e 100644 --- a/api/v1alpha1/portalconfig_types.go +++ b/api/v1alpha1/portalconfig_types.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package v1alpha1 import ( diff --git a/api/v1alpha1/storageosportal_types.go b/api/v1alpha1/storageosportal_types.go index ddf4ebab..6b3d980d 100644 --- a/api/v1alpha1/storageosportal_types.go +++ b/api/v1alpha1/storageosportal_types.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package v1alpha1 import ( diff --git a/controllers/config_controller.go b/controllers/config_controller.go new file mode 100644 index 00000000..887d20d3 --- /dev/null +++ b/controllers/config_controller.go @@ -0,0 +1,95 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package controllers + +import ( + "context" + "fmt" + + storageosv1alpha1 "github.com/storageos/portal-manager/api/v1alpha1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/workqueue" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +// ConfigReconciler reconciles StorageOS related configs. +type ConfigReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// Reconcile changes of watched resources. +func (r *ConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return ctrl.Result{}, nil +} + +type configEventHandler struct { + namespace string +} + +func (eh configEventHandler) Create(event.CreateEvent, workqueue.RateLimitingInterface) {} + +// Update detects StorageOS related config changes. +func (eh configEventHandler) Update(e event.UpdateEvent, _ workqueue.RateLimitingInterface) { + if e.ObjectNew.GetNamespace() != eh.namespace { + return + } + + if app, ok := e.ObjectNew.GetLabels()["app"]; !ok || app != "storageos" { + return + } + + panic(fmt.Errorf("some config has changed: %s", e.ObjectNew.GetName())) +} + +func (eh configEventHandler) Delete(event.DeleteEvent, workqueue.RateLimitingInterface) {} + +func (eh configEventHandler) Generic(event.GenericEvent, workqueue.RateLimitingInterface) {} + +// SetupWithManager sets up the controller with the Manager. +func (r *ConfigReconciler) SetupWithManager(mgr ctrl.Manager, namespace string) error { + configEventHandler := configEventHandler{namespace: namespace} + + return ctrl.NewControllerManagedBy(mgr). + For(&storageosv1alpha1.StorageOSPortal{}). + Watches(&source.Kind{Type: &corev1.Secret{}}, configEventHandler). + Watches(&source.Kind{Type: &corev1.ConfigMap{}}, configEventHandler). + WithOptions(controller.Options{ + MaxConcurrentReconciles: 1, + }). + Complete(r) +} diff --git a/controllers/publish_controller.go b/controllers/publish_controller.go index a68774a8..70e22c00 100644 --- a/controllers/publish_controller.go +++ b/controllers/publish_controller.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package controllers import ( diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 3a747860..9b1222ef 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package controllers import ( diff --git a/controllers/watch_controller.go b/controllers/watch_controller.go index c9203153..26b931fc 100644 --- a/controllers/watch_controller.go +++ b/controllers/watch_controller.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package controllers import ( diff --git a/endpoints/portal.go b/endpoints/portal.go index ddde77ea..0ac1020a 100644 --- a/endpoints/portal.go +++ b/endpoints/portal.go @@ -1,6 +1,22 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package endpoints import ( + "context" "encoding/base64" "encoding/json" "fmt" @@ -33,7 +49,6 @@ type PortalEndpoint struct { refreshTokenDelay uint8 refreshTokenTimer *time.Timer - refreshStopchan chan bool logger logr.Logger } @@ -65,7 +80,7 @@ func (e *PortalEndpoint) RegisterDevice(pubKeyPem []byte) error { } // Start start endpoint service and token refresh. -func (e *PortalEndpoint) Start() { +func (e *PortalEndpoint) Start(ctx context.Context) { e.logger.V(3).Info("Start portal endpoint...") e.refreshTokenTimer = time.NewTimer(0) @@ -75,7 +90,6 @@ func (e *PortalEndpoint) Start() { wg.Add(1) go func() { - LOOP: for { select { case <-e.refreshTokenTimer.C: @@ -101,8 +115,12 @@ func (e *PortalEndpoint) Start() { e.logger.V(2).Info("Schedule token refresh retry", "seconds", e.refreshTokenDelay) e.refreshTokenTimer = time.NewTimer(time.Duration(e.refreshTokenDelay) * time.Second) - case <-e.refreshStopchan: - break LOOP + case <-ctx.Done(): + e.logger.V(3).Info("Stop portal endpoint") + + e.refreshTokenTimer.Stop() + + return } } }() @@ -160,14 +178,6 @@ func (e *PortalEndpoint) GetConfig() (*v1alpha1.IOTCoreConfig, error) { return &iotConfig, nil } -// Stop close endpoint and resources. -func (e *PortalEndpoint) Stop() { - e.logger.V(3).Info("Stop portal endpoint") - - e.refreshTokenTimer.Stop() - close(e.refreshStopchan) -} - func (e *PortalEndpoint) refreshToken() (int, error) { e.logger.V(2).Info("Refresh token...") @@ -205,7 +215,6 @@ func NewPortalEndpoint(clusterID, url string, clientID string, password string, password: password, tokenLock: make(chan bool, 1), refreshTokenDelay: refreshTokenDefaultDelay, - refreshStopchan: make(chan bool), logger: logger.WithName("portal_endpoint"), } } diff --git a/endpoints/storageos.go b/endpoints/storageos.go index cc122ac1..f790fa88 100644 --- a/endpoints/storageos.go +++ b/endpoints/storageos.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package endpoints import ( diff --git a/main.go b/main.go index 57a277ed..4d252e40 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,6 @@ import ( // to ensure that exec-entrypoint and run can make use of them. "go.uber.org/zap/zapcore" - "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" appsv1 "k8s.io/api/apps/v1" @@ -40,7 +39,6 @@ import ( "github.com/storageos/portal-manager/pkg/handler" "github.com/storageos/portal-manager/pkg/publisher" "github.com/storageos/portal-manager/pkg/utils" - "github.com/storageos/portal-manager/watchers" klog "k8s.io/klog/v2" //+kubebuilder:scaffold:imports ) @@ -74,13 +72,11 @@ const ( // stosPasswordKey is the key in the secret that holds the password value. stosPasswordKey = "password" - - // licenceUpdatePeriod is the frequency of licence status messages. - licenceUpdatePeriod = 5 * time.Minute ) var ( - scheme = runtime.NewScheme() + scheme = runtime.NewScheme() + shutDownperiod = time.Second * 5 ) func init() { @@ -110,6 +106,17 @@ func init() { // TODO some retries would be nice at every level. func main() { + ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) + defer func() { + if r := recover(); r != nil { + cancel() + + time.Sleep(shutDownperiod) + + os.Exit(1) + } + }() + var err error var configFile string flag.StringVar(&configFile, "config", "", @@ -138,8 +145,9 @@ func main() { currentNS := os.Getenv(podNamespace) if len(currentNS) == 0 { - setupLogger.Error(errors.New("current namespace not found"), "failed to get current namespace") - os.Exit(1) + err = errors.New("current namespace not found") + setupLogger.Error(err, "failed to get current namespace") + panic(err) } restConfig := ctrl.GetConfigOrDie() @@ -147,55 +155,53 @@ func main() { kubeClient, err := client.New(restConfig, client.Options{}) if err != nil { setupLogger.Error(err, "unable to build kubernetes client") - os.Exit(1) + panic(err) } // Setup StorageOS endpoint. stosUsername, err := utils.ReadFile(path.Join(stosSecretPath, stosUsernameKey)) if err != nil { setupLogger.Error(err, "unable to read username") - os.Exit(1) + panic(err) } stosPassword, err := utils.ReadFile(path.Join(stosSecretPath, stosPasswordKey)) if err != nil { setupLogger.Error(err, "unable to read password") - os.Exit(1) + panic(err) } stosService := fmt.Sprintf(stosServiceTemplate, currentNS) - // TODO watch api-secret change. stosEndpoint := endpoints.NewStorageOSEndpoint(stosUsername, stosPassword, stosService, logger) err = stosEndpoint.Start() if err != nil { setupLogger.Error(err, "unable to start storageos endpoint") - os.Exit(1) + panic(err) } // Setup Portal API. portalURL, ok := os.LookupEnv(portalURLEnv) if !ok { setupLogger.Error(errors.New("missing key"), portalURLEnv) - os.Exit(1) + panic(err) } portalClientID, ok := os.LookupEnv(clientIDEnv) if !ok { setupLogger.Error(errors.New("missing key"), clientIDEnv) - os.Exit(1) + panic(err) } portalPassword, ok := os.LookupEnv(passwordEnv) if !ok { setupLogger.Error(errors.New("missing key"), passwordEnv) - os.Exit(1) + panic(err) } portalEndpoint := endpoints.NewPortalEndpoint(stosEndpoint.GetClusterID(), portalURL, portalClientID, portalPassword, logger) - portalEndpoint.Start() - defer portalEndpoint.Stop() + portalEndpoint.Start(ctx) // Setup device manager. deviceManager := managers.NewDeviceManager(kubeClient, *portalEndpoint, currentNS, logger) - if err := deviceManager.Start(); err != nil { + if err := deviceManager.Init(); err != nil { setupLogger.Error(err, "failed to start device manager") - os.Exit(1) + panic(err) } var ctrlConfig storageosv1alpha1.PortalConfig @@ -203,7 +209,7 @@ func main() { iotConfig, err := portalEndpoint.GetConfig() if err != nil { setupLogger.Error(err, "unable to retrieve config from portal") - os.Exit(1) + panic(err) } ctrlConfig.IOTCore = iotConfig @@ -215,7 +221,7 @@ func main() { options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile).OfKind(&ctrlConfig)) if err != nil { setupLogger.Error(err, "unable to load the config file") - os.Exit(1) + panic(err) } } @@ -223,7 +229,7 @@ func main() { mgr, err := ctrl.NewManager(restConfig, options) if err != nil { setupLogger.Error(err, "unable to start manager") - os.Exit(1) + panic(err) } publishQueue := workqueue.NewRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(time.Second, time.Minute)) @@ -231,76 +237,76 @@ func main() { tenantID, ok := os.LookupEnv(tenantIDEnv) if !ok { setupLogger.Error(errors.New("missing key"), tenantIDEnv) - os.Exit(1) + panic(err) } // Start action service. actionService := action.NewActionService(logger, stosEndpoint.GetClusterID(), []byte(iotConfig.SignatureKey), licence.NewLicenceActionHandler(*stosEndpoint, logger)) - // Start publisher. + // Start message broker. sink, err := publisher.New(stosEndpoint.GetClusterID(), deviceManager.GetPrivateKeyPem(), ctrlConfig, actionService.Do, logger) if err != nil { setupLogger.Error(err, "unable to create sink") - os.Exit(1) + panic(err) } - if err := sink.Init(); err != nil { + if err := sink.Start(ctx); err != nil { setupLogger.Error(err, "unable to initialize sink") - os.Exit(1) + panic(err) } // Send acceptsConfiguration. go func() { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() if err := sink.PublishState(ctx, &map[string]string{"acceptsConfiguration": "true"}); err != nil { setupLogger.Error(err, "unable to send initial state") - return + panic(err) } - }() - // Periodically send licence. - go func() { - // TODO would be nice to stop properly gorutine - ticker := time.NewTicker(licenceUpdatePeriod) - for { - currentLicence, err := stosEndpoint.GetLicence() - if err != nil { - setupLogger.Error(err, "unable to fetch licence") - continue - } - - licenceState := state.ActionState{ - Meta: &state.ActionMeta{ - ClusterId: stosEndpoint.GetClusterID(), - Configurable: true, - Success: true, - }, - Licence: &state.Licence{ - ExpireDate: currentLicence.ExpiresAt, - LicenceType: currentLicence.Kind, - }, - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - if err := sink.PublishState(ctx, &licenceState); err != nil { - setupLogger.Error(err, "unable to send licence state") - } - cancel() + currentLicence, err := stosEndpoint.GetLicence() + if err != nil { + setupLogger.Error(err, "unable to fetch licence") + panic(err) + } - <-ticker.C + licenceState := state.ActionState{ + Meta: &state.ActionMeta{ + ClusterId: stosEndpoint.GetClusterID(), + Configurable: true, + Success: true, + }, + Licence: &state.Licence{ + ExpireDate: currentLicence.ExpiresAt, + LicenceType: currentLicence.Kind, + }, + } + + if err := sink.PublishState(ctx, &licenceState); err != nil { + setupLogger.Error(err, "unable to send licence state") + panic(err) } }() - // Start message broker. + // Start config change watcher. + configController := controllers.ConfigReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + } + if err = configController.SetupWithManager(mgr, currentNS); err != nil { + setupLogger.Error(err, "unable to initialize controller", "controller", "config") + panic(err) + } + + // Start publisher. publisher, err := controllers.NewPublisher(tenantID, stosEndpoint.GetClusterID(), sink, mgr.GetScheme(), publishQueue, logger) if err != nil { setupLogger.Error(err, "unable to create controller", "controller", "publisher") - os.Exit(1) + panic(err) } if err = publisher.SetupWithManager(mgr); err != nil { setupLogger.Error(err, "unable to initialize controller", "controller", "publisher") - os.Exit(1) + panic(err) } // TODO(sc): WatchTypes need to be configurable via the PortalConfig CR. @@ -326,50 +332,22 @@ func main() { EventHandler: handler.NewEventQueuer(publishQueue, logger), }).SetupWithManager(mgr); err != nil { setupLogger.Error(err, "unable to create controller", "controller", "portal") - os.Exit(1) + panic(err) } //+kubebuilder:scaffold:builder - kubeClientSet := kubernetes.NewForConfigOrDie(restConfig) - - ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) - - // Watch secrets and restart on change. - setupLogger.V(3).Info("Starting secret watcher...") - secretWatcher := watchers.NewSecretWatcher(kubeClientSet.CoreV1().Secrets(currentNS), logger) - if err := secretWatcher.Setup(ctx); err != nil { - setupLogger.Error(err, "unable to set secret watcher") - os.Exit(1) - } - go func() { - secretWatcher.Start(ctx) - cancel() - }() - - // Watch config maps and restart on change. - setupLogger.V(3).Info("Starting config map watcher...") - configWatcher := watchers.NewConfigMapWatcher(kubeClientSet.CoreV1().ConfigMaps(currentNS), logger) - if err := configWatcher.Setup(ctx); err != nil { - setupLogger.Error(err, "unable to set config map watcher") - os.Exit(1) - } - go func() { - configWatcher.Start(ctx) - cancel() - }() - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLogger.Error(err, "unable to set up health check") - os.Exit(1) + panic(err) } if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { setupLogger.Error(err, "unable to set up ready check") - os.Exit(1) + panic(err) } setupLogger.V(3).Info("Starting manager...") if err := mgr.Start(ctx); err != nil { setupLogger.Error(err, "problem running manager") - os.Exit(1) + panic(err) } } diff --git a/managers/device.go b/managers/device.go index 37c43e48..3bf847e5 100644 --- a/managers/device.go +++ b/managers/device.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package managers import ( @@ -31,8 +46,8 @@ type DeviceManager struct { logger logr.Logger } -// FetchKeys fetches keys stored as secret. -func (m *DeviceManager) Start() error { +// Init fetches keys stored as secret. +func (m *DeviceManager) Init() error { m.logger.V(3).Info("Starting device manager...") listCtx, listCancel := context.WithTimeout(context.Background(), time.Minute) diff --git a/pkg/action/action.go b/pkg/action/action.go index 1cb13e63..9b1a786d 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -1,9 +1,24 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package action import ( "encoding/base64" "encoding/json" - "errors" + "strings" "github.com/go-logr/logr" "github.com/storageos/portal-manager/pkg/action/state" @@ -52,10 +67,15 @@ func (as *ActionService) Do(rawAction []byte, updateState func(interface{}) erro } var err error + actionErrors := []string{} + defer func() { state.Meta.Success = err == nil if err != nil { state.Meta.Message = err.Error() + if len(actionErrors) > 0 { + state.Meta.Message += "Action errors: " + strings.Join(actionErrors, "#") + } } as.logger.V(3).Info("Updating state") @@ -102,10 +122,10 @@ func (as *ActionService) Do(rawAction []byte, updateState func(interface{}) erro } } - return + if err != nil { + actionErrors = append(actionErrors, err.Error()) + } } - - err = errors.New("action handler not found for request") } // NewActionService creates a service. diff --git a/pkg/action/licence/licence.go b/pkg/action/licence/licence.go index adc9977f..2c54b741 100644 --- a/pkg/action/licence/licence.go +++ b/pkg/action/licence/licence.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package licence import ( diff --git a/pkg/action/state/state.go b/pkg/action/state/state.go index c55378d4..51397d60 100644 --- a/pkg/action/state/state.go +++ b/pkg/action/state/state.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package state import "time" diff --git a/pkg/handler/event_queuer.go b/pkg/handler/event_queuer.go index e328d684..6f259bea 100644 --- a/pkg/handler/event_queuer.go +++ b/pkg/handler/event_queuer.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package handler import ( diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 4e3f2f91..f498f51e 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package publisher import ( @@ -13,7 +28,7 @@ import ( // Sink is implemented by the publisher and all supported backends. type Sink interface { - Init() error + Start(context.Context) error Publish(context.Context, *portal.Event) error PublishState(context.Context, interface{}) error Shutdown() error @@ -24,6 +39,7 @@ type Publisher struct { logger logr.Logger } +// New creates new publisher. func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.PortalConfig, deviceConfigCallback func([]byte, func(interface{}) error), logger logr.Logger) (*Publisher, error) { logger = logger.WithName("publisher") @@ -41,13 +57,22 @@ func New(clusterID string, privateKeyPem []byte, config storageosv1alpha1.Portal }, nil } -// Initialize the publisher. -func (p *Publisher) Init() error { +// Start the publisher. +func (p *Publisher) Start(ctx context.Context) error { for _, sink := range p.sinks { - if err := sink.Init(); err != nil { + if err := sink.Start(ctx); err != nil { return errors.Wrap(err, "unable to initialize new sink") } } + + go func() { + <-ctx.Done() + + if err := p.Shutdown(); err != nil { + p.logger.Error(err, "Failed to Shutdown publisher") + } + }() + return nil } diff --git a/pkg/publisher/sinks/gcp-iot/iot-core.go b/pkg/publisher/sinks/gcp-iot/iot-core.go index 9f137293..70c3b117 100644 --- a/pkg/publisher/sinks/gcp-iot/iot-core.go +++ b/pkg/publisher/sinks/gcp-iot/iot-core.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package gcpiot import ( @@ -128,7 +143,7 @@ func (m *MQTT) ConfigTopic() string { return fmt.Sprintf("/devices/%s/config", m.device) } -func (m *MQTT) Init() error { +func (m *MQTT) Start(context.Context) error { m.onConnectionLost(nil, nil) return nil diff --git a/pkg/publisher/sinks/noop/noop.go b/pkg/publisher/sinks/noop/noop.go index 59fbe741..d1c4d4a4 100644 --- a/pkg/publisher/sinks/noop/noop.go +++ b/pkg/publisher/sinks/noop/noop.go @@ -1,13 +1,32 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package noop -import "sigs.k8s.io/controller-runtime/pkg/reconcile" +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) type Noop struct { } // Init initializes the sink. // Do nothing for noop sink. -func (n *Noop) Init() error { +func (n *Noop) Start(context.Context) error { return nil } diff --git a/pkg/storageos/client.go b/pkg/storageos/client.go index 5970f21e..69515884 100644 --- a/pkg/storageos/client.go +++ b/pkg/storageos/client.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package storageos import ( diff --git a/pkg/utils/cert.go b/pkg/utils/cert.go index cc754a06..7c841d7c 100644 --- a/pkg/utils/cert.go +++ b/pkg/utils/cert.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/cert_test.go b/pkg/utils/cert_test.go index 05783606..ad5c0771 100644 --- a/pkg/utils/cert_test.go +++ b/pkg/utils/cert_test.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 418a5cd7..723e7ebc 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/http.go b/pkg/utils/http.go index 1207fe60..3aa57aa5 100644 --- a/pkg/utils/http.go +++ b/pkg/utils/http.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/http_test.go b/pkg/utils/http_test.go index f2163a61..77b63abd 100644 --- a/pkg/utils/http_test.go +++ b/pkg/utils/http_test.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/jwt.go b/pkg/utils/jwt.go index 15cfe584..c322f266 100644 --- a/pkg/utils/jwt.go +++ b/pkg/utils/jwt.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/signature.go b/pkg/utils/signature.go index 649f3116..6e1b37c7 100644 --- a/pkg/utils/signature.go +++ b/pkg/utils/signature.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import ( diff --git a/pkg/utils/signature_test.go b/pkg/utils/signature_test.go index 4e7dac5e..f4d9a475 100644 --- a/pkg/utils/signature_test.go +++ b/pkg/utils/signature_test.go @@ -1,3 +1,18 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package utils import "testing" diff --git a/watchers/configmap.go b/watchers/configmap.go deleted file mode 100644 index 95a00094..00000000 --- a/watchers/configmap.go +++ /dev/null @@ -1,110 +0,0 @@ -package watchers - -import ( - "context" - "time" - - "github.com/go-logr/logr" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - tcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -const maxWaitPeriod = 120 - -// ConfigMapWatcher watches StorageOS related config maps. -type ConfigMapWatcher struct { - client tcorev1.ConfigMapInterface - listOpt metav1.ListOptions - logger logr.Logger -} - -// Setup configures config map watcher with init values. -func (w *ConfigMapWatcher) Setup(ctx context.Context) error { - w.listOpt = metav1.ListOptions{ - LabelSelector: labelSelector, - } - - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - configMaps, err := w.client.List(ctx, w.listOpt) - if err != nil { - return errors.Wrap(err, "unable to list existing config maps") - } - - w.listOpt.Watch = true - w.listOpt.ResourceVersion = configMaps.ResourceVersion - - return nil -} - -// Start watching config maps until first change. -func (w *ConfigMapWatcher) Start(ctx context.Context) { - for waitPeriod := 0; ; waitPeriod = (waitPeriod + 1) * 2 { - if waitPeriod != 0 { - w.logger.V(2).Info("Wait before start watching", "period", waitPeriod) - time.Sleep(time.Second * time.Duration(waitPeriod)) - } - - err := w.watchChange(ctx, waitPeriod) - if err != nil { - if waitPeriod >= maxWaitPeriod { - w.logger.Error(err, "reached max wait period, time to give up watching") - break - } - - w.logger.Error(err, "Unable to start config map watcher") - // Increase wait period of reconnect. - continue - } - - // Something has changed. - break - } -} - -// watchChange consumes watch events and returns on change. -func (w *ConfigMapWatcher) watchChange(ctx context.Context, waitPeriod int) error { - watcher, err := w.client.Watch(ctx, w.listOpt) - if err != nil { - return errors.Wrap(err, "unable to watch config maps") - } - defer watcher.Stop() - - if waitPeriod != 0 { - w.logger.Info("Wait before reading channel", "period", waitPeriod) - time.Sleep(time.Second * time.Duration(waitPeriod)) - } - - for { - event, ok := <-watcher.ResultChan() - if !ok { - w.logger.V(3).Info("Watcher has closed") - return errors.New("watcher has closed") - } - - if event.Type == watch.Modified { - if cm, ok := event.Object.(*corev1.ConfigMap); ok { - w.logger.V(2).Info("Config map has updated", "name", cm.GetName(), "namespace", cm.GetNamespace()) - } else { - w.logger.V(2).Info("Config map has updated") - } - break - } - } - - return nil -} - -// NewConfigMapWatcher consructs a new config map watcher. -func NewConfigMapWatcher(client tcorev1.ConfigMapInterface, logger logr.Logger) *ConfigMapWatcher { - logger = logger.WithName("config_map_watcher") - - return &ConfigMapWatcher{ - client: client, - logger: logger, - } -} diff --git a/watchers/consts.go b/watchers/consts.go deleted file mode 100644 index 11d079f3..00000000 --- a/watchers/consts.go +++ /dev/null @@ -1,3 +0,0 @@ -package watchers - -const labelSelector = "app.kubernetes.io/component=portal-manager" diff --git a/watchers/secret.go b/watchers/secret.go deleted file mode 100644 index 0170ecc8..00000000 --- a/watchers/secret.go +++ /dev/null @@ -1,102 +0,0 @@ -package watchers - -import ( - "context" - "time" - - "github.com/go-logr/logr" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" - tcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" -) - -// SecretWatcher watches StorageOS related secrets. -type SecretWatcher struct { - client tcorev1.SecretInterface - listOpt metav1.ListOptions - logger logr.Logger -} - -// Setup configures secret watcher with init values. -func (w *SecretWatcher) Setup(ctx context.Context) error { - w.listOpt = metav1.ListOptions{ - LabelSelector: labelSelector, - } - - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() - - secrets, err := w.client.List(ctx, w.listOpt) - if err != nil { - return errors.Wrap(err, "unable to list existing secrets") - } - - w.listOpt.Watch = true - w.listOpt.ResourceVersion = secrets.ResourceVersion - - return nil -} - -// Start watching secrets until first change. -func (w *SecretWatcher) Start(ctx context.Context) { - for waitPeriod := 0; ; waitPeriod = (waitPeriod + 1) * 2 { - waitDuration := time.Second * time.Duration(waitPeriod) - if waitPeriod != 0 { - w.logger.V(2).Info("Wait before start watching", "period", waitPeriod) - time.Sleep(waitDuration) - } - - err := w.watchChange(ctx, waitDuration) - if err != nil { - w.logger.Error(err, "Unable to start secret watcher") - // Increase wait period of reconnect. - continue - } - - // Something has changed. - break - } -} - -// watchChange consumes watch events and returns on change. -func (w *SecretWatcher) watchChange(ctx context.Context, waitDuration time.Duration) error { - watcher, err := w.client.Watch(ctx, w.listOpt) - if err != nil { - return errors.Wrap(err, "unable to watch secrets") - } - defer watcher.Stop() - - if waitDuration != 0 { - w.logger.Info("Wait before reading channel", "period", waitDuration) - time.Sleep(waitDuration) - } - - for { - event, ok := <-watcher.ResultChan() - if !ok { - w.logger.V(3).Info("Watcher has closed") - return errors.New("watcher has closed") - } - - if event.Type == watch.Modified { - if secret, ok := event.Object.(*corev1.Secret); ok { - w.logger.V(2).Info("Secret has updated", "name", secret.GetName(), "namespace", secret.GetNamespace()) - } else { - w.logger.V(2).Info("Secret has updated") - } - break - } - } - - return nil -} - -// NewSecretWatcher consructs a new secret watcher. -func NewSecretWatcher(client tcorev1.SecretInterface, logger logr.Logger) *SecretWatcher { - return &SecretWatcher{ - client: client, - logger: logger.WithName("secret_watcher"), - } -} From f803bc6d0bb826e02616ff62fe6b72c26bbf2fbd Mon Sep 17 00:00:00 2001 From: Richard Kovacs Date: Thu, 26 May 2022 15:13:39 +0200 Subject: [PATCH 16/16] Fix docker build --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 29c49354..cba3e22f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,6 @@ COPY endpoints/ endpoints/ COPY managers/ managers/ COPY pkg/ pkg/ COPY vendor/ vendor/ -COPY watchers/ watchers/ # Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -mod=vendor -a -o manager main.go