Skip to content

Commit

Permalink
Onboard clients only if they are on same version as provider - 2
Browse files Browse the repository at this point in the history
- Operator major and minor version of client and provider should match
for onboarding process to begin
- After this commit older clients will fail to get onboarded

Minor: Rename package clientstatus to interfaces

Signed-off-by: Leela Venkaiah G <[email protected]>
  • Loading branch information
leelavg committed Dec 21, 2023
1 parent 5e04cc2 commit ef18be7
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 205 deletions.
17 changes: 9 additions & 8 deletions services/provider/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"time"

cs "github.com/red-hat-storage/ocs-operator/v4/services/provider/clientstatus"
ifaces "github.com/red-hat-storage/ocs-operator/v4/services/provider/interfaces"
pb "github.com/red-hat-storage/ocs-operator/v4/services/provider/pb"

"google.golang.org/grpc"
Expand Down Expand Up @@ -53,17 +53,18 @@ func (cc *OCSProviderClient) Close() {
cc.Client = nil
}

func NewOnboardConsumerRequest() ifaces.StorageClientOnboarding {
return &pb.OnboardConsumerRequest{}
}

// OnboardConsumer to validate the consumer and create StorageConsumer
// resource on the StorageProvider cluster
func (cc *OCSProviderClient) OnboardConsumer(ctx context.Context, ticket, name string) (*pb.OnboardConsumerResponse, error) {
func (cc *OCSProviderClient) OnboardConsumer(ctx context.Context, iface ifaces.StorageClientOnboarding) (*pb.OnboardConsumerResponse, error) {
if cc.Client == nil || cc.clientConn == nil {
return nil, fmt.Errorf("provider client is closed")
}

req := &pb.OnboardConsumerRequest{
OnboardingTicket: ticket,
ConsumerName: name,
}
req := iface.(*pb.OnboardConsumerRequest)

apiCtx, cancel := context.WithTimeout(ctx, cc.timeout)
defer cancel()
Expand Down Expand Up @@ -189,11 +190,11 @@ func (cc *OCSProviderClient) GetStorageClassClaimConfig(ctx context.Context, con
return cc.Client.GetStorageClassClaimConfig(apiCtx, req)
}

func NewStorageClientStatus() cs.StorageClientStatus {
func NewStorageClientStatus() ifaces.StorageClientStatus {
return &pb.ReportStatusRequest{}
}

func (cc *OCSProviderClient) ReportStatus(ctx context.Context, consumerUUID string, status cs.StorageClientStatus) (*pb.ReportStatusResponse, error) {
func (cc *OCSProviderClient) ReportStatus(ctx context.Context, consumerUUID string, status ifaces.StorageClientStatus) (*pb.ReportStatusResponse, error) {
if cc.Client == nil || cc.clientConn == nil {
return nil, fmt.Errorf("Provider client is closed")
}
Expand Down
9 changes: 0 additions & 9 deletions services/provider/clientstatus/status.go

This file was deleted.

23 changes: 23 additions & 0 deletions services/provider/interfaces/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package interfaces

type StorageClientStatus interface {
// TODO: it was mistake not using full name of the field and we are just
// doing indirection for getters, change this interface after ensuring
// no client is using it
GetPlatformVersion() string
GetOperatorVersion() string

SetPlatformVersion(string) StorageClientStatus
SetOperatorVersion(string) StorageClientStatus
}

type StorageClientOnboarding interface {
// getters for fields are already provided by protobuf messages
GetOnboardingTicket() string
GetConsumerName() string
GetClientOperatorVersion() string

SetOnboardingTicket(string) StorageClientOnboarding
SetConsumerName(string) StorageClientOnboarding
SetClientOperatorVersion(string) StorageClientOnboarding
}
323 changes: 168 additions & 155 deletions services/provider/pb/provider.pb.go

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions services/provider/pb/storageclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package providerpb

import (
ifaces "github.com/red-hat-storage/ocs-operator/v4/services/provider/interfaces"
)

// ensure ReportStatusRequest satisfies StorageClientStatus interface
var _ ifaces.StorageClientStatus = &ReportStatusRequest{}

func (r *ReportStatusRequest) GetPlatformVersion() string {
return r.GetClientPlatformVersion()
}

func (r *ReportStatusRequest) GetOperatorVersion() string {
return r.GetClientOperatorVersion()
}

func (r *ReportStatusRequest) SetPlatformVersion(version string) ifaces.StorageClientStatus {
r.ClientPlatformVersion = version
return r
}

func (r *ReportStatusRequest) SetOperatorVersion(version string) ifaces.StorageClientStatus {
r.ClientOperatorVersion = version
return r
}

// ensure OnboardConsumerRequest satisfies StorageClientOnboarding interface
var _ ifaces.StorageClientOnboarding = &OnboardConsumerRequest{}

func (o *OnboardConsumerRequest) SetOnboardingTicket(ticket string) ifaces.StorageClientOnboarding {
o.OnboardingTicket = ticket
return o
}

func (o *OnboardConsumerRequest) SetConsumerName(name string) ifaces.StorageClientOnboarding {
o.ConsumerName = name
return o
}

func (o *OnboardConsumerRequest) SetClientOperatorVersion(version string) ifaces.StorageClientOnboarding {
o.ClientOperatorVersion = version
return o
}
26 changes: 0 additions & 26 deletions services/provider/pb/storageclient_status.go

This file was deleted.

2 changes: 2 additions & 0 deletions services/provider/proto/provider.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ message OnboardConsumerRequest{
string onboardingTicket = 1;
// consumerName is the name of the consumer that is used to create the storageConsumer resource
string consumerName = 2;
// clientOperatorVersion is the semver version of ocs-client-operator
string clientOperatorVersion = 3;
}

// OnboardConsumerResponse holds the response for OnboardConsumer API request
Expand Down
13 changes: 10 additions & 3 deletions services/provider/server/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"sync"

ocsv1alpha1 "github.com/red-hat-storage/ocs-operator/api/v4/v1alpha1"
cs "github.com/red-hat-storage/ocs-operator/v4/services/provider/clientstatus"
ifaces "github.com/red-hat-storage/ocs-operator/v4/services/provider/interfaces"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -54,7 +54,9 @@ func newConsumerManager(ctx context.Context, cl client.Client, namespace string)
}

// Create creates a new storageConsumer resource, updates the consumer cache and returns the storageConsumer UID
func (c *ocsConsumerManager) Create(ctx context.Context, name, ticket string) (string, error) {
func (c *ocsConsumerManager) Create(ctx context.Context, onboard ifaces.StorageClientOnboarding) (string, error) {
ticket := onboard.GetOnboardingTicket()
name := onboard.GetConsumerName()
c.mutex.RLock()
if _, ok := c.nameByTicket[ticket]; ok {
c.mutex.RUnlock()
Expand All @@ -74,6 +76,11 @@ func (c *ocsConsumerManager) Create(ctx context.Context, name, ticket string) (s
Spec: ocsv1alpha1.StorageConsumerSpec{
Enable: false,
},
Status: ocsv1alpha1.StorageConsumerStatus{
Client: ocsv1alpha1.ClientStatus{
OperatorVersion: onboard.GetClientOperatorVersion(),
},
},
}

err := c.client.Create(ctx, consumerObj)
Expand Down Expand Up @@ -199,7 +206,7 @@ func (c *ocsConsumerManager) Get(ctx context.Context, id string) (*ocsv1alpha1.S
return consumerObj, nil
}

func (c *ocsConsumerManager) UpdateConsumerStatus(ctx context.Context, id string, status cs.StorageClientStatus) error {
func (c *ocsConsumerManager) UpdateConsumerStatus(ctx context.Context, id string, status ifaces.StorageClientStatus) error {
consumerObj, err := c.Get(ctx, id)
if err != nil {
return err
Expand Down
27 changes: 24 additions & 3 deletions services/provider/server/consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,41 @@ func TestCreateStorageConsumer(t *testing.T) {
assert.NoError(t, err)

// Create consumer should fail if consumer already exists
_, err = consumerManager.Create(ctx, "consumer1", "ticket1")
req := providerClient.NewOnboardConsumerRequest().
SetConsumerName("consumer1").
SetOnboardingTicket("ticket1")
_, err = consumerManager.Create(ctx, req)
assert.Error(t, err)

// Create consumer should fail if ticket is already used
_, err = consumerManager.Create(ctx, "consumer3", "ticket1")
req = providerClient.NewOnboardConsumerRequest().
SetConsumerName("consumer3").
SetOnboardingTicket("ticket1")
_, err = consumerManager.Create(ctx, req)
assert.Error(t, err)

// Create consumer successfully. (Can't validate the UID because fake client does not add UID)
assert.Equal(t, 1, len(consumerManager.nameByTicket))
_, err = consumerManager.Create(ctx, "consumer2", "ticket2")
req = providerClient.NewOnboardConsumerRequest().
SetConsumerName("consumer2").
SetOnboardingTicket("ticket2")
_, err = consumerManager.Create(ctx, req)
assert.NoError(t, err)
assert.Equal(t, 2, len(consumerManager.nameByTicket))
assert.Equal(t, "consumer1", consumerManager.nameByTicket["ticket1"])
assert.Equal(t, "consumer2", consumerManager.nameByTicket["ticket2"])

version := "4.15.1"
name := "consumer3"
req = providerClient.NewOnboardConsumerRequest().
SetConsumerName("consumer3").
SetOnboardingTicket("ticket3").
SetClientOperatorVersion(version)
_, err = consumerManager.Create(ctx, req)
assert.NoError(t, err)
consumerObject, err := consumerManager.GetByName(ctx, name)
assert.NoError(t, err)
assert.Equal(t, consumerObject.Status.Client.OperatorVersion, version)
}

func TestDeleteStorageConsumer(t *testing.T) {
Expand Down
14 changes: 13 additions & 1 deletion services/provider/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
ocsv1alpha1 "github.com/red-hat-storage/ocs-operator/api/v4/v1alpha1"
controllers "github.com/red-hat-storage/ocs-operator/v4/controllers/storageconsumer"
pb "github.com/red-hat-storage/ocs-operator/v4/services/provider/pb"
ocsVersion "github.com/red-hat-storage/ocs-operator/v4/version"
rookCephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"

"google.golang.org/grpc"
Expand Down Expand Up @@ -91,6 +92,17 @@ func NewOCSProviderServer(ctx context.Context, namespace string) (*OCSProviderSe
// OnboardConsumer RPC call to onboard a new OCS consumer cluster.
func (s *OCSProviderServer) OnboardConsumer(ctx context.Context, req *pb.OnboardConsumerRequest) (*pb.OnboardConsumerResponse, error) {

version, err := semver.FinalizeVersion(req.ClientOperatorVersion)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "malformed ClientOperatorVersion for client %q is provided. %v", req.ConsumerName, err)
}

serverVersion, _ := semver.Make(ocsVersion.Version)
clientVersion, _ := semver.Make(version)
if serverVersion.Major != clientVersion.Major || serverVersion.Minor != clientVersion.Minor {
return nil, status.Errorf(codes.FailedPrecondition, "both server and client %q operators major and minor versions should match for onboarding process", req.ConsumerName)
}

pubKey, err := s.getOnboardingValidationKey(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get public key to validate onboarding ticket for consumer %q. %v", req.ConsumerName, err)
Expand All @@ -101,7 +113,7 @@ func (s *OCSProviderServer) OnboardConsumer(ctx context.Context, req *pb.Onboard
return nil, status.Errorf(codes.InvalidArgument, "onboarding ticket is not valid. %v", err)
}

storageConsumerUUID, err := s.consumerManager.Create(ctx, req.ConsumerName, req.OnboardingTicket)
storageConsumerUUID, err := s.consumerManager.Create(ctx, req)
if err != nil {
if !kerrors.IsAlreadyExists(err) && err != errTicketAlreadyExists {
return nil, status.Errorf(codes.Internal, "failed to create storageConsumer %q. %v", req.ConsumerName, err)
Expand Down

0 comments on commit ef18be7

Please sign in to comment.