...

Source file src/edge-infra.dev/pkg/sds/emergencyaccess/ea_integration/v2/ea_v2_integration_test.go

Documentation: edge-infra.dev/pkg/sds/emergencyaccess/ea_integration/v2

     1  package integrationv2
     2  
     3  import (
     4  	"context"
     5  	"embed"
     6  	"fmt"
     7  	"os"
     8  	"path"
     9  	"testing"
    10  
    11  	"cloud.google.com/go/pubsub"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"google.golang.org/api/option"
    15  	"google.golang.org/api/option/internaloption"
    16  	"google.golang.org/grpc"
    17  	"google.golang.org/grpc/credentials/insecure"
    18  
    19  	"edge-infra.dev/pkg/sds/emergencyaccess/emulatorsvc"
    20  	"edge-infra.dev/test/f2"
    21  	"edge-infra.dev/test/f2/fctx"
    22  	"edge-infra.dev/test/f2/integration"
    23  	"edge-infra.dev/test/f2/x/ktest"
    24  	"edge-infra.dev/test/f2/x/ktest/envtest"
    25  	"edge-infra.dev/test/f2/x/postgres"
    26  )
    27  
    28  var (
    29  	f          f2.Framework
    30  	smanifests map[string]string
    31  )
    32  
    33  //go:embed testdata
    34  var manifests embed.FS
    35  
    36  const (
    37  	// names to avoid typos in maps
    38  	userServiceName = "userService"
    39  	eaGatewayName   = "eaGateway"
    40  	rulesEngineName = "rulesEngine"
    41  	authServiceName = "authService"
    42  	authProxyName   = "authProxy"
    43  	mockBFFName     = "mockBFF"
    44  	pubsubName      = "pubsub"
    45  )
    46  
    47  func TestMain(m *testing.M) {
    48  	f = f2.New(
    49  		context.Background(),
    50  		f2.WithExtensions(
    51  			// Include ktest extension for access to a k8s cluster
    52  			ktest.New(
    53  				ktest.WithEnvtestOptions(
    54  					// Do not install any CRD into cluster
    55  					envtest.WithoutCRDs(),
    56  				),
    57  			),
    58  			postgres.New(
    59  				postgres.ApplySeedModel(),
    60  			),
    61  		),
    62  	).
    63  		Setup(func(ctx f2.Context) (f2.Context, error) {
    64  			// Test execution should end here unless -integration-level=2 is passed to test
    65  			if !integration.IsL2() {
    66  				return ctx, fmt.Errorf("%w: requires L2 integration test level", f2.ErrSkip)
    67  			}
    68  
    69  			return ctx, nil
    70  		}).
    71  		Setup(func(ctx f2.Context) (f2.Context, error) {
    72  			var err error
    73  			manifestPaths := map[string]string{
    74  				userServiceName: path.Join("testdata", "userservice_manifests.yaml"),
    75  				authServiceName: path.Join("testdata", "authservice_manifests.yaml"),
    76  				rulesEngineName: path.Join("testdata", "rulesengine_manifests.yaml"),
    77  				eaGatewayName:   path.Join("testdata", "eagateway_manifests.yaml"),
    78  				authProxyName:   path.Join("testdata", "auth_proxy_manifests.yaml"),
    79  				mockBFFName:     path.Join("testdata", "mockbffserver_manifests.yaml"),
    80  				pubsubName:      path.Join("testdata", "pubsub_manifests.yaml"),
    81  			}
    82  			smanifests = map[string]string{}
    83  			for name, path := range manifestPaths {
    84  				smanifests[name], err = getManifests(manifests, path)
    85  				if err != nil {
    86  					return ctx, err
    87  				}
    88  			}
    89  
    90  			return ctx, err
    91  		})
    92  	//Run the tests
    93  	os.Exit(f.Run(m))
    94  }
    95  
    96  func TestEA(t *testing.T) {
    97  	var (
    98  		namespace        string
    99  		pgConfigMapData  map[string]string
   100  		portforwardReng  = ktest.PortForward{}
   101  		portForwardProxy = ktest.PortForward{}
   102  		portForwardPSE   = ktest.PortForward{}
   103  		emsvc            *emulatorsvc.EmulatorService
   104  		sub              *pubsub.Subscription
   105  	)
   106  
   107  	feat := f2.NewFeature("Test Remote CLI services").
   108  		Setup("Get info for CM config", func(ctx f2.Context, t *testing.T) f2.Context {
   109  			k := ktest.FromContextT(ctx, t)
   110  			namespace = k.Namespace
   111  			pg := postgres.FromContextT(ctx, t)
   112  			pgConfigMapData = map[string]string{
   113  				"DATABASE_CONNECTION_NAME": pg.ConnectionName,
   114  				"DATABASE_HOST":            pg.K8SHost(),
   115  				"DATABASE_PORT":            fmt.Sprintf("%d", pg.Port),
   116  				"DATABASE_NAME":            pg.Database,
   117  				"DATABASE_USERNAME":        pg.User,
   118  				"DATABASE_PASSWORD":        pg.Password,
   119  				"DATABASE_SCHEMA":          pg.Schema(),
   120  			}
   121  
   122  			return ctx
   123  		}).
   124  		// auth proxy
   125  		Setup("Create auth proxy", createService(smanifests[authProxyName], processAuthProxyManifests(&namespace), addPostGresDetailsToCM("auth-proxy", &pgConfigMapData))).
   126  		// mock bff server
   127  		Setup("Create mock BFF Server", createService(smanifests[mockBFFName])).
   128  		// user service
   129  		Setup("Create user service", createService(smanifests[userServiceName], addPostGresDetailsToCM("userservice", &pgConfigMapData))).
   130  		// auth service
   131  		Setup("Create auth service", createService(smanifests[authServiceName], addPostGresDetailsToCM("authservice", &pgConfigMapData))).
   132  		Setup("Add data from seed", postgres.WithData(testOrgSeed)).
   133  		// rules engine
   134  		Setup("Create rules engine", createService(smanifests[rulesEngineName], addPostGresDetailsToCM("rulesengine", &pgConfigMapData))).
   135  		// EA Gateway
   136  		Setup("Create EAGateway", createService(smanifests[eaGatewayName], processEAGatewayCM(&namespace), addPostGresDetailsToCM("eagateway", &pgConfigMapData))).
   137  		// create + configure pubsub emulator
   138  		Setup("Deploy pubsub emulator", createService(smanifests[pubsubName])).
   139  		Setup("Wait for pubsub emulator", waitOn(smanifests[pubsubName])).
   140  		Setup("Port forward pubsub emulator", portForwardPSE.Forward("pubsub-service", 8085)).
   141  		Setup("Create topic and subscription", func(ctx f2.Context, t *testing.T) f2.Context {
   142  			// projectID needs to be ID not name
   143  			// Create a new client that connects to the pubsub emulator in the
   144  			// test namespace via the port forward. Manually set the same
   145  			// options that [pubsub.NewClient] would set when it detects the
   146  			// PUBSUB_EMULATOR_HOST env var as this means we don't need to set
   147  			// an environment variable for the test (env var's are process
   148  			// specific so would not allow running parallel tests against
   149  			// different addresses)
   150  			pseClient, err := pubsub.NewClient(ctx, "d56bf564-90f7-4036-9eab-9efd435e68fe", option.WithEndpoint(portForwardPSE.Retrieve(t)),
   151  				option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
   152  				option.WithoutAuthentication(),
   153  				option.WithTelemetryDisabled(),
   154  				internaloption.SkipDialSettingsValidation())
   155  			assert.NoError(t, err)
   156  			reqTopic, err := pseClient.CreateTopic(ctx, "topic.dsds-ea-request")
   157  			assert.NoError(t, err)
   158  			// this subscription isnt needed for this test, but is used to verify the message is sent correctly in a
   159  			// later step function.
   160  			sub, err = pseClient.CreateSubscription(ctx, "sub.dsds-ea-request", pubsub.SubscriptionConfig{
   161  				Topic: reqTopic,
   162  				//AckDeadline: 10 * time.Second,
   163  			})
   164  			assert.NoError(t, err)
   165  
   166  			topic, err := pseClient.CreateTopic(ctx, "topic.dsds-ea-response")
   167  			assert.NoError(t, err)
   168  			// subscription is sub.clusterEdgeID (as defined in seed).dsds-ea-response
   169  			_, err = pseClient.CreateSubscription(ctx, "sub.f983e403-b2fc-4359-83e5-53e2c993141b.dsds-ea-response", pubsub.SubscriptionConfig{
   170  				Topic: topic,
   171  			})
   172  			assert.NoError(t, err)
   173  			return ctx
   174  		}).
   175  
   176  		// Wait for remaining services to be ready
   177  		Setup("Wait for auth-proxy deployment", waitOn(smanifests[authProxyName])).
   178  		Setup("Wait for mock bff server deployment", waitOn(smanifests[mockBFFName])).
   179  		Setup("Wait for userservice deployment", waitOn(smanifests[userServiceName])).
   180  		Setup("Wait for authservice deployment", waitOn(smanifests[authServiceName])).
   181  		Setup("Wait for rulesengine deployment", waitOn(smanifests[rulesEngineName])).
   182  		Setup("Wait for EAGateway", waitOn(smanifests[eaGatewayName])).
   183  		// Port forwarding for rules engine
   184  		Setup("Rulesengine port forwarding", portforwardReng.Forward("rulesengine", 8080)).
   185  		Setup("Store rules engine port forward in ctx", func(ctx f2.Context, _ *testing.T) f2.Context {
   186  			return fctx.WithValue(ctx, rulesEnginePortForwardKey{}, portforwardReng)
   187  		}).
   188  		// configure rules engine
   189  		Setup("Add commands", addCommands([]string{"ls"})).
   190  		Setup("Add privileges", addPrivileges([]string{"ea-admin"})).
   191  		Setup("Add default rules", addDefaultRules([]defaultRule{
   192  			{"ls", []string{"ea-admin"}},
   193  		})).
   194  		Setup("Delete default rule", func(ctx f2.Context, _ *testing.T) f2.Context {
   195  			// Nothing for now
   196  			_ = deleteDefaultRule
   197  			return ctx
   198  		}).
   199  		Setup("Add role mapping", addRoleMapping(map[string][]string{"EDGE_ADMIN": {"ea-admin"}})).
   200  		Setup("Port forward auth proxy", portForwardProxy.Forward("auth-proxy", 9003)).
   201  		Test("Auth OK", func(ctx f2.Context, t *testing.T) f2.Context {
   202  			cfg := emulatorsvc.NewConfig("testdata", emulatorsvc.Profile{
   203  				Username:     "testValidUser1",
   204  				Password:     "abcd",
   205  				Organization: "testOrganization",
   206  				API:          "http://" + portForwardProxy.Retrieve(t) + "/api/v2",
   207  			})
   208  			var err error
   209  			emsvc, err = emulatorsvc.New(ctx, cfg)
   210  			require.NoError(t, err)
   211  			err = emsvc.RetrieveIdentity(ctx)
   212  			require.NoError(t, err)
   213  			return ctx
   214  		}).
   215  		Test("Test Valid Command", func(ctx f2.Context, t *testing.T) f2.Context {
   216  			// Connect with a valid session
   217  			// banner, store and terminalIDs are defined in the Seed, loaded during the WithData step for authservice
   218  			err := emsvc.Connect(ctx, "testOrganization", "testBanner1", "testStore1", "testTerminal1")
   219  			require.NoError(t, err)
   220  
   221  			// Send a valid command
   222  			commandID, err := emsvc.Send("ls")
   223  			require.NoError(t, err)
   224  
   225  			// Receive the message from the subscription
   226  			cctx, cancel := context.WithCancel(ctx)
   227  			defer cancel()
   228  			err = sub.Receive(cctx, func(_ context.Context, m *pubsub.Message) {
   229  				t.Log("Received message", "message", string(m.Data))
   230  				// check the message is as expected
   231  				assert.JSONEq(t, "{\"command\":\"ls\"}", string(m.Data))
   232  				assertPubSubMessageAttributesEqual(t, m, map[string]string{
   233  					"bannerId":   "3cf00a00-d06b-40c7-859c-fbddfdeb1177",
   234  					"storeId":    "f983e403-b2fc-4359-83e5-53e2c993141b",
   235  					"terminalId": "55c61200-d079-4b71-9e45-f5a81b7af86d",
   236  					"identity":   "testValidUser1",
   237  					"version":    "1.0",
   238  					"signature":  "",
   239  					"commandId":  commandID,
   240  				})
   241  				assertPubSubMessageAttributesNotNil(t, m, []string{"sessionId"})
   242  				m.Ack()
   243  				cancel() // stop receiving after the first message
   244  			})
   245  			assert.NoError(t, err)
   246  			return ctx
   247  		}).
   248  		Feature()
   249  	f.Test(t, feat)
   250  }
   251  

View as plain text