diff --git a/Makefile b/Makefile index afbe249..42eb394 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +export BASE_DIR=$(shell git rev-parse --show-toplevel) +export GOPATH=$(shell echo ${BASE_DIR}| sed 's@\(.*\)/src/github.com.*@\1@g') all: sdk-test sdk-test: diff --git a/cmd/sdk-test/sdk_test.go b/cmd/sdk-test/sdk_test.go index d6302f9..f853ac3 100644 --- a/cmd/sdk-test/sdk_test.go +++ b/cmd/sdk-test/sdk_test.go @@ -37,6 +37,7 @@ var ( cloudProviderConfigPath string sharedSecret string issuer string + junitfile string ) func init() { @@ -46,6 +47,7 @@ func init() { flag.StringVar(&cloudProviderConfigPath, prefix+"cpg", "", "Cloud Provider config file , optional") flag.StringVar(&sharedSecret, prefix+"sharedsecret", "", "Shared secret for auth, ownership, and role testing") flag.StringVar(&issuer, prefix+"issuer", "openstorage.io", "Issuer of token") + flag.StringVar(&junitfile, prefix+"junitfile", "report.xml", "XML test report") flag.Parse() } @@ -78,6 +80,7 @@ func TestSanity(t *testing.T) { SharedSecret: sharedSecret, Issuer: issuer, ProviderConfig: cfg, + JUnitFile: junitfile, }) } diff --git a/hack/demo.sh b/hack/demo.sh new file mode 100755 index 0000000..9aff67d --- /dev/null +++ b/hack/demo.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +fail() { + echo "$1" + exit 1 +} + +PORT="9020" +GWPORT="8181" +make install +./cmd/sdk-test/sdk-test --ginkgo.focus="Security" --ginkgo.v --sdk.endpoint=70.0.74.173:${PORT} --sdk.issuer="openstorage.io" --sdk.sharedsecret="Password1" diff --git a/pkg/common/common.go b/pkg/common/common.go new file mode 100644 index 0000000..84208fa --- /dev/null +++ b/pkg/common/common.go @@ -0,0 +1,70 @@ +package common + +import ( + "sync" +) + +type ConcMap struct { + l sync.RWMutex + KVMap map[interface{}]interface{} +} + +func NewConcMap() *ConcMap { + return &ConcMap{ + KVMap: make(map[interface{}]interface{}), + } +} + +func (c *ConcMap) Add(key interface{}, value interface{}) { + c.l.Lock() + defer c.l.Unlock() + c.KVMap[key] = value +} + +func (c *ConcMap) GetKeyValMap() map[interface{}]interface{} { + return c.KVMap +} + +type ConcStrToStrMap struct { + l sync.RWMutex + KVMap map[string]string +} + +func NewConcStrToStrMap() *ConcStrToStrMap { + return &ConcStrToStrMap{ + KVMap: make(map[string]string), + } +} + +func (c *ConcStrToStrMap) Add(key string, value string) { + c.l.Lock() + defer c.l.Unlock() + c.KVMap[key] = value +} + +func (c *ConcStrToStrMap) GetKeyValMap() map[string]string { + return c.KVMap +} + +type ConcStringErrChanMap struct { + l sync.RWMutex + StrErrChanMap map[string]chan (error) +} + +func NewConcStringErrChanMap() *ConcStringErrChanMap { + return &ConcStringErrChanMap{ + StrErrChanMap: make(map[string]chan (error)), + } +} + +func (c *ConcStringErrChanMap) Add(key string, value chan (error)) { + c.l.Lock() + defer c.l.Unlock() + c.StrErrChanMap[key] = value +} + +func (c *ConcStringErrChanMap) GetKeyValMap() map[string]chan (error) { + c.l.Lock() + defer c.l.Unlock() + return c.StrErrChanMap +} diff --git a/pkg/sanity/sanity.go b/pkg/sanity/sanity.go index 6c66bc9..4092455 100644 --- a/pkg/sanity/sanity.go +++ b/pkg/sanity/sanity.go @@ -29,6 +29,7 @@ import ( "google.golang.org/grpc/connectivity" . "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/reporters" . "github.com/onsi/gomega" ) @@ -52,6 +53,7 @@ type SanityConfiguration struct { SharedSecret string Issuer string ProviderConfig *CloudProviderConfig + JUnitFile string } // Test will test start the sanity tests @@ -61,7 +63,13 @@ func Test(t *testing.T, reqConfig *SanityConfiguration) { config = reqConfig RegisterFailHandler(Fail) - RunSpecs(t, "OpenStorage SDK Test Suite") + //RunSpecs(t, "OpenStorage SDK Test Suite") + var specReporters []Reporter + if reqConfig.JUnitFile != "" { + junitReporter := reporters.NewJUnitReporter(reqConfig.JUnitFile) + specReporters = append(specReporters, junitReporter) + } + RunSpecsWithDefaultAndCustomReporters(t, "OpenStorage SDK Test Suite", specReporters) } var _ = BeforeSuite(func() { diff --git a/pkg/sanity/securityScale.go b/pkg/sanity/securityScale.go new file mode 100644 index 0000000..59230a0 --- /dev/null +++ b/pkg/sanity/securityScale.go @@ -0,0 +1,165 @@ +package sanity + +import ( + "context" + "fmt" + "sync" + "time" + + api "github.com/libopenstorage/openstorage-sdk-clients/sdk/golang" + common "github.com/libopenstorage/sdk-test/pkg/common" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var scaleUsers map[string]string +var userVolumeMap *common.ConcMap + +type VolumeRequest struct { + VolID string + CreateRequest *api.SdkVolumeCreateRequest + Token string +} + +var _ = Describe("Security Scale", func() { + var ( + c api.OpenStorageVolumeClient + ic api.OpenStorageIdentityClient + ) + + BeforeEach(func() { + c = api.NewOpenStorageVolumeClient(conn) + ic = api.NewOpenStorageIdentityClient(conn) + isSupported := isCapabilitySupported( + ic, + api.SdkServiceCapability_OpenStorageService_VOLUME, + ) + if !isSupported { + Fail("Volume capability not supported , skipping related tests") + } + }) + + AfterEach(func() { + }) + + Describe("Security", func() { + + BeforeEach(func() { + }) + + It("Should be able to create the users", func() { + By("Creating users") + scaleUsers = createXUsersTokens("scaleUsers", 30) + }) + It("Should be able to create the volumes", func() { + By("Creating volumes") + err := createVolumesConcurrently(c) + Expect(err).NotTo(HaveOccurred()) + }) + It("Should be able to inspect the created Volume", func() { + By("Inspecting volumes") + err := inspectVolumesConcurrently(c) + Expect(err).NotTo(HaveOccurred()) + }) + It("Owner Should be able to delete its own Volume", func() { + By("Deleting volumes") + err := deleteVolumesConcurrently(c) + Expect(err).NotTo(HaveOccurred()) + }) + }) + +}) + +func createVolumesConcurrently(c api.OpenStorageVolumeClient) error { + //userVolumeMap is mapping user'name to volumes' ID + userVolumeMap = common.NewConcMap() + var volErrorMap = common.NewConcStringErrChanMap() + var wg sync.WaitGroup + for name, userToken := range scaleUsers { + userName := name + token := userToken + wg.Add(1) + go func(userName string, token string) { + defer wg.Done() + t := time.Now() + tstr := t.Format("20060102150405") + req := &api.SdkVolumeCreateRequest{ + Name: "sdk-vol-" + tstr + "-" + userName, + Spec: &api.VolumeSpec{ + Size: uint64(5 * GIGABYTE), + HaLevel: 2, + }, + } + createResponse, err := c.Create(setContextWithToken(context.Background(), token), req) + volID := createResponse.VolumeId + resp, err := c.Inspect( + setContextWithToken(context.Background(), token), + &api.SdkVolumeInspectRequest{ + VolumeId: volID, + }, + ) + printVolumeDetails(resp.Volume) + userVolumeMap.Add(userName, resp.GetVolume().GetId()) + errChan := make(chan (error), 1) + errChan <- err + volErrorMap.Add(volID, errChan) + }(userName, token) + } + wg.Wait() + for user, volID := range userVolumeMap.GetKeyValMap() { + fmt.Printf("\nuser %s createdvolume ->: %s", user, volID) + } + return summarizeErrorsFromStringErrorChanMap(volErrorMap.GetKeyValMap()) +} + +func inspectVolumesConcurrently(c api.OpenStorageVolumeClient) error { + var wg sync.WaitGroup + var volErrorMap = common.NewConcStringErrChanMap() + for user, id := range userVolumeMap.GetKeyValMap() { + userName := user.(string) + volID := id.(string) + fmt.Printf("\nNow user %s is going to inspect volume %s", userName, volID) + token := scaleUsers[userName] + wg.Add(1) + go func(userName string, volID string, token string) { + defer wg.Done() + _, err := c.Inspect( + setContextWithToken(context.Background(), token), + &api.SdkVolumeInspectRequest{ + VolumeId: volID, + }, + ) + errChan := make(chan (error), 1) + errChan <- err + volErrorMap.Add(volID, errChan) + }(userName, volID, token) + } + wg.Wait() + //Receiving all errors from channels + return summarizeErrorsFromStringErrorChanMap(volErrorMap.GetKeyValMap()) +} + +func deleteVolumesConcurrently(c api.OpenStorageVolumeClient) error { + var wg sync.WaitGroup + var volErrorMap = common.NewConcStringErrChanMap() + for user, id := range userVolumeMap.GetKeyValMap() { + userName := user.(string) + volID := id.(string) + token := scaleUsers[userName] + fmt.Printf("\nNow user %s is going to delete volume %s", userName, volID) + wg.Add(1) + go func(userName string, volID string, token string) { + defer wg.Done() + err := deleteVol( + setContextWithToken(context.Background(), token), + c, + volID, + ) + errChan := make(chan (error), 1) + errChan <- err + volErrorMap.Add(volID, errChan) + }(userName, volID, token) + } + wg.Wait() + return summarizeErrorsFromStringErrorChanMap(volErrorMap.GetKeyValMap()) +} diff --git a/pkg/sanity/utils.go b/pkg/sanity/utils.go index ae776d2..bb70f1f 100644 --- a/pkg/sanity/utils.go +++ b/pkg/sanity/utils.go @@ -19,10 +19,13 @@ package sanity import ( "context" "fmt" + "strings" + "sync" "time" api "github.com/libopenstorage/openstorage-sdk-clients/sdk/golang" - "github.com/libopenstorage/sdk-test/pkg/auth" + auth "github.com/libopenstorage/sdk-test/pkg/auth" + common "github.com/libopenstorage/sdk-test/pkg/common" "google.golang.org/grpc/metadata" . "github.com/onsi/gomega" @@ -102,6 +105,33 @@ func createUsersTokens() map[string]string { return users } +func createXUsersTokens(prefix string, amount int) map[string]string { + users := common.NewConcStrToStrMap() + if amount <= 0 { + return users.GetKeyValMap() + } + var wg sync.WaitGroup + for i := 0; i < amount; i++ { + name := fmt.Sprintf("%s%d", prefix, i) + wg.Add(1) + go func(name string) { + defer wg.Done() + user := createToken(&auth.Claims{ + Subject: name, + Name: name, + Email: fmt.Sprintf("%s@portworx.com", name), + Roles: []string{"system.user"}, + Groups: []string{"users"}, + }, &auth.Options{ + Expiration: time.Now().Add(1 * time.Hour).Unix(), + }, config.SharedSecret) + users.Add(name, user) + }(name) + } + wg.Wait() + return users.GetKeyValMap() +} + func setContextWithToken(ctx context.Context, token string) context.Context { md := metadata.New(map[string]string{ "authorization": "bearer " + token, @@ -395,3 +425,24 @@ func deleteVol(ctx context.Context, vc api.OpenStorageVolumeClient, volid string }) return err } + +func printVolumeDetails( + volume *api.Volume, +) { + fmt.Printf("volume ID: %s\n", volume.Id) +} + +func summarizeErrorsFromStringErrorChanMap(stringErrMap map[string]chan (error)) error { + var summarizedErrMsg string + for key, errChan := range stringErrMap { + err := <-errChan + if err != nil { + summarizedErrMsg = fmt.Sprintf("%s\nerror of %s: %v", summarizedErrMsg, key, err) + } + } + fmt.Printf("\nsummarizedErrMsg: %s", summarizedErrMsg) + if strings.TrimSpace(summarizedErrMsg) == "" { + return nil + } + return fmt.Errorf("%s", summarizedErrMsg) +}