Skip to content

Commit

Permalink
#99: add scbe rest client map for authentication
Browse files Browse the repository at this point in the history
 #99: add scbe rest client headers sync management
 #99: add tests for scbe rest client multiple instances
  • Loading branch information
Lior Tamari committed Oct 26, 2017
1 parent ffb8896 commit 7357d5e
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 35 deletions.
4 changes: 3 additions & 1 deletion glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import:
version: ^1.2.0
- package: github.com/op/go-logging
version: 970db520ece77730c7e4724c61121037378659d9
- package: github.com/golang/sync/syncmap
version: f52d1811a62927559de87708c8913c1650ce4f26
testImport:
- package: github.com/onsi/ginkgo
version: bb93381d543b0e5725244abe752214a110791d01
- package: github.com/onsi/gomega
version: c463cd2a8578290d4be7a25cba69de81cf35785e
- package: github.com/jarcoal/httpmock
version: 4442edb3db31196622da56482fd8d0fa375fba4d
version: 4442edb3db31196622da56482fd8d0fa375fba4d
99 changes: 83 additions & 16 deletions local/scbe/scbe.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ import (
"strings"
"sync"
"github.com/IBM/ubiquity/database"
"github.com/golang/sync/syncmap"
)

type scbeLocalClient struct {
logger logs.Logger
dataModel ScbeDataModelWrapper
scbeRestClient ScbeRestClient
isActivated bool
config resources.ScbeConfig
activationLock *sync.RWMutex
locker utils.Locker
restClients *syncmap.Map
}

const (
Expand Down Expand Up @@ -70,33 +71,34 @@ func NewScbeLocalClientWithNewScbeRestClientAndDataModel(config resources.ScbeCo

client := &scbeLocalClient{
logger: logs.GetLogger(),
scbeRestClient: scbeRestClient, // TODO need to mock it in more advance way
dataModel: dataModel,
config: config,
activationLock: &sync.RWMutex{},
locker: utils.NewLocker(),
restClients: new(syncmap.Map),
}

if err := client.basicScbeLocalClientStartupAndValidation(); err != nil {
if err := client.basicScbeLocalClientStartupAndValidation(scbeRestClient); err != nil {
return &scbeLocalClient{}, err
}

return client, nil
}

// basicScbeLocalClientStartup validate config params, login to SCBE and validate default exist
func (s *scbeLocalClient) basicScbeLocalClientStartupAndValidation() error {
func (s *scbeLocalClient) basicScbeLocalClientStartupAndValidation(restClient ScbeRestClient) error {
defer s.logger.Trace(logs.DEBUG)()

// login
s.logger.Info("validate scbeRestClient.Login", logs.Args{{"SCBE", s.config.ConnectionInfo.ManagementIP}})
if err := s.scbeRestClient.Login(); err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.Login failed")
if err := restClient.Login(); err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.Login() failed")
}

s.restClients.Store(s.config.ConnectionInfo.CredentialInfo, restClient)

// service existence
s.logger.Info("validate scbeRestClient.ServiceExist", logs.Args{{"DefaultService", s.config.DefaultService}})
isExist, err := s.scbeRestClient.ServiceExist(s.config.DefaultService)
isExist, err := restClient.ServiceExist(s.config.DefaultService)
if err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.ServiceExist failed")
}
Expand All @@ -105,13 +107,13 @@ func (s *scbeLocalClient) basicScbeLocalClientStartupAndValidation() error {
}

// db volume
volumes, err := s.scbeRestClient.GetVolumes("")
volumes, err := restClient.GetVolumes("")
if err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.GetVolumes failed")
}
for _, volInfo := range volumes {
if database.IsDatabaseVolume(volInfo.Name) && s.isInstanceVolume(volInfo.Name) {
host, err := s.scbeRestClient.GetVolMapping(volInfo.Wwn)
host, err := restClient.GetVolMapping(volInfo.Wwn)
if err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.GetVolMapping failed")
}
Expand All @@ -130,6 +132,22 @@ func (s *scbeLocalClient) basicScbeLocalClientStartupAndValidation() error {
return nil
}

func (s *scbeLocalClient) getAuthenticatedScbeRestClient(credential resources.CredentialInfo) (ScbeRestClient, error) {
restClient, exists := s.restClients.Load(credential)
if !exists {
newClient, err := newScbeRestClientGen(resources.ConnectionInfo{
CredentialInfo: credential, Port: s.config.ConnectionInfo.Port, ManagementIP: s.config.ConnectionInfo.ManagementIP})
if err != nil {
return nil, s.logger.ErrorRet(err, "newScbeRestClientGen failed")
}
if err := newClient.Login(); err != nil {
return nil, s.logger.ErrorRet(err, "newClient.Login() failed")
}
restClient, _ = s.restClients.LoadOrStore(credential, newClient)
}
return restClient.(ScbeRestClient), nil
}

func (s *scbeLocalClient) isInstanceVolume(volName string) bool {
defer s.logger.Trace(logs.DEBUG)()
isInstanceVolume := strings.HasPrefix(volName, fmt.Sprintf(ComposeVolumeName, s.config.UbiquityInstanceName, ""))
Expand Down Expand Up @@ -170,7 +188,14 @@ func validateScbeConfig(config *resources.ScbeConfig) error {

func (s *scbeLocalClient) Activate(activateRequest resources.ActivateRequest) error {
defer s.logger.Trace(logs.DEBUG)()
s.activationLock.RLock()

// authenticate
_, err := s.getAuthenticatedScbeRestClient(activateRequest.CredentialInfo)
if err != nil {
return s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

s.activationLock.RLock()
if s.isActivated {
s.activationLock.RUnlock()
return nil
Expand All @@ -189,6 +214,12 @@ func (s *scbeLocalClient) Activate(activateRequest resources.ActivateRequest) er
func (s *scbeLocalClient) CreateVolume(createVolumeRequest resources.CreateVolumeRequest) (err error) {
defer s.logger.Trace(logs.DEBUG)()

// authenticate
scbeRestClient, err := s.getAuthenticatedScbeRestClient(createVolumeRequest.CredentialInfo)
if err != nil {
return s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

// verify volume does not exist
if _, err = s.dataModel.GetVolume(createVolumeRequest.Name, false); err != nil {
return s.logger.ErrorRet(err, "dataModel.GetVolume failed", logs.Args{{"name", createVolumeRequest.Name}})
Expand Down Expand Up @@ -242,7 +273,7 @@ func (s *scbeLocalClient) CreateVolume(createVolumeRequest resources.CreateVolum

// Provision the volume on SCBE service
volInfo := ScbeVolumeInfo{}
volInfo, err = s.scbeRestClient.CreateVolume(volNameToCreate, profile, size)
volInfo, err = scbeRestClient.CreateVolume(volNameToCreate, profile, size)
if err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.CreateVolume failed")
}
Expand All @@ -259,6 +290,12 @@ func (s *scbeLocalClient) CreateVolume(createVolumeRequest resources.CreateVolum
func (s *scbeLocalClient) RemoveVolume(removeVolumeRequest resources.RemoveVolumeRequest) (err error) {
defer s.logger.Trace(logs.DEBUG)()

// authenticate
scbeRestClient, err := s.getAuthenticatedScbeRestClient(removeVolumeRequest.CredentialInfo)
if err != nil {
return s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

existingVolume, err := s.dataModel.GetVolume(removeVolumeRequest.Name, true)
if err != nil {
return s.logger.ErrorRet(err, "dataModel.GetVolume failed")
Expand All @@ -268,7 +305,7 @@ func (s *scbeLocalClient) RemoveVolume(removeVolumeRequest resources.RemoveVolum
return s.logger.ErrorRet(&CannotDeleteVolWhichAttachedToHostError{removeVolumeRequest.Name, existingVolume.AttachTo}, "failed")
}

if err = s.scbeRestClient.DeleteVolume(existingVolume.WWN); err != nil {
if err = scbeRestClient.DeleteVolume(existingVolume.WWN); err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.DeleteVolume failed")
}

Expand All @@ -282,6 +319,12 @@ func (s *scbeLocalClient) RemoveVolume(removeVolumeRequest resources.RemoveVolum
func (s *scbeLocalClient) GetVolume(getVolumeRequest resources.GetVolumeRequest) (resources.Volume, error) {
defer s.logger.Trace(logs.DEBUG)()

// authenticate
_, err := s.getAuthenticatedScbeRestClient(getVolumeRequest.CredentialInfo)
if err != nil {
return resources.Volume{}, s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

existingVolume, err := s.dataModel.GetVolume(getVolumeRequest.Name, true)
if err != nil {
return resources.Volume{}, s.logger.ErrorRet(err, "dataModel.GetVolume failed")
Expand All @@ -296,14 +339,20 @@ func (s *scbeLocalClient) GetVolume(getVolumeRequest resources.GetVolumeRequest)
func (s *scbeLocalClient) GetVolumeConfig(getVolumeConfigRequest resources.GetVolumeConfigRequest) (map[string]interface{}, error) {
defer s.logger.Trace(logs.DEBUG)()

// authenticate
scbeRestClient, err := s.getAuthenticatedScbeRestClient(getVolumeConfigRequest.CredentialInfo)
if err != nil {
return nil, s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

// get volume wwn from name
scbeVolume, err := s.dataModel.GetVolume(getVolumeConfigRequest.Name, true)
if err != nil {
return nil, s.logger.ErrorRet(err, "dataModel.GetVolume failed")
}

// get volume full info from scbe
volumeInfo, err := s.scbeRestClient.GetVolumes(scbeVolume.WWN)
volumeInfo, err := scbeRestClient.GetVolumes(scbeVolume.WWN)
if err != nil {
return nil, s.logger.ErrorRet(err, "scbeRestClient.GetVolumes failed")
}
Expand Down Expand Up @@ -333,6 +382,12 @@ func (s *scbeLocalClient) GetVolumeConfig(getVolumeConfigRequest resources.GetVo
func (s *scbeLocalClient) Attach(attachRequest resources.AttachRequest) (string, error) {
defer s.logger.Trace(logs.DEBUG)()

// authenticate
scbeRestClient, err := s.getAuthenticatedScbeRestClient(attachRequest.CredentialInfo)
if err != nil {
return "", s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

if attachRequest.Host == EmptyHost {
return "", s.logger.ErrorRet(
&InValidRequestError{"attachRequest", "Host", attachRequest.Host, "none empty string"}, "failed")
Expand All @@ -359,7 +414,7 @@ func (s *scbeLocalClient) Attach(attachRequest resources.AttachRequest) (string,
// Lock will ensure no other caller attach a volume from the same host concurrently, Prevent SCBE race condition on get next available lun ID
s.locker.WriteLock(attachRequest.Host)
s.logger.Debug("Attaching", logs.Args{{"volume", existingVolume}})
if _, err = s.scbeRestClient.MapVolume(existingVolume.WWN, attachRequest.Host); err != nil {
if _, err = scbeRestClient.MapVolume(existingVolume.WWN, attachRequest.Host); err != nil {
s.locker.WriteUnlock(attachRequest.Host)
return "", s.logger.ErrorRet(err, "scbeRestClient.MapVolume failed")
}
Expand All @@ -377,6 +432,12 @@ func (s *scbeLocalClient) Detach(detachRequest resources.DetachRequest) (err err
defer s.logger.Trace(logs.DEBUG)()
host2detach := detachRequest.Host

// authenticate
scbeRestClient, err := s.getAuthenticatedScbeRestClient(detachRequest.CredentialInfo)
if err != nil {
return s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

existingVolume, err := s.dataModel.GetVolume(detachRequest.Name, true)
if err != nil {
return s.logger.ErrorRet(err, "dataModel.GetVolume failed")
Expand All @@ -388,7 +449,7 @@ func (s *scbeLocalClient) Detach(detachRequest resources.DetachRequest) (err err
}

s.logger.Debug("Detaching", logs.Args{{"volume", existingVolume}})
if err = s.scbeRestClient.UnmapVolume(existingVolume.WWN, host2detach); err != nil {
if err = scbeRestClient.UnmapVolume(existingVolume.WWN, host2detach); err != nil {
return s.logger.ErrorRet(err, "scbeRestClient.UnmapVolume failed")
}

Expand All @@ -403,6 +464,12 @@ func (s *scbeLocalClient) ListVolumes(listVolumesRequest resources.ListVolumesRe
defer s.logger.Trace(logs.DEBUG)()
var err error

// authenticate
_, err = s.getAuthenticatedScbeRestClient(listVolumesRequest.CredentialInfo)
if err != nil {
return nil, s.logger.ErrorRet(err, "getAuthenticatedScbeRestClient failed")
}

volumesInDb, err := s.dataModel.ListVolumes()
if err != nil {
return nil, s.logger.ErrorRet(err, "dataModel.ListVolumes failed")
Expand Down
40 changes: 40 additions & 0 deletions local/scbe/scbe_rest_client_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2017 IBM Corp.
*
* 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 scbe

import (
"github.com/IBM/ubiquity/resources"
)

type ScbeRestClientGen func(resources.ConnectionInfo) (ScbeRestClient, error)

var globalScbeRestClientGen ScbeRestClientGen = nil

func InitScbeRestClientGen(gen ScbeRestClientGen) func() {
if globalScbeRestClientGen != nil {
panic("globalScbeRestClientGen already initialized")
}
globalScbeRestClientGen = gen
return func() { globalScbeRestClientGen = nil }
}

func newScbeRestClientGen(conInfo resources.ConnectionInfo) (ScbeRestClient, error) {
if globalScbeRestClientGen != nil {
return globalScbeRestClientGen(conInfo)
}
return NewScbeRestClient(conInfo)
}

0 comments on commit 7357d5e

Please sign in to comment.