Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Operator: CAPTenantOutput introduced #120

Merged
merged 2 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions cmd/server/internal/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ type OAuthResponse struct {
AccessToken string `json:"access_token"`
}

type tenantInfo struct {
tenantId string
tenantSubDomain string
}

func (s *SubscriptionHandler) CreateTenant(req *http.Request) *Result {
util.LogInfo("Create Tenant triggered", TenantProvisioning, "CreateTenant", nil)
var created = false
Expand Down Expand Up @@ -181,7 +186,8 @@ func (s *SubscriptionHandler) CreateTenant(req *http.Request) *Result {

// TODO: consider retrying tenant creation if it is in Error state
if tenant != nil {
s.initializeCallback(tenant.Name, ca, saasData, req, reqType["subscribedSubdomain"].(string), true)
tenantIn := tenantInfo{tenantId: reqType["subscribedTenantId"].(string), tenantSubDomain: reqType["subscribedSubdomain"].(string)}
s.initializeCallback(tenant.Name, ca, saasData, req, tenantIn, true)
}

// Tenant created/exists
Expand Down Expand Up @@ -277,8 +283,8 @@ func (s *SubscriptionHandler) DeleteTenant(req *http.Request) *Result {
return &Result{Tenant: nil, Message: err.Error()}
}
}

s.initializeCallback(tenantName, ca, saasData, req, reqType["subscribedSubdomain"].(string), false)
tenantIn := tenantInfo{tenantId: reqType["subscribedTenantId"].(string), tenantSubDomain: reqType["subscribedSubdomain"].(string)}
s.initializeCallback(tenantName, ca, saasData, req, tenantIn, false)

return &Result{Tenant: tenant, Message: ResourceDeleted}
}
Expand Down Expand Up @@ -321,12 +327,12 @@ func (s *SubscriptionHandler) checkAuthorization(authHeader string, saasData *ut
return nil
}

func (s *SubscriptionHandler) initializeCallback(tenantName string, ca *v1alpha1.CAPApplication, saasData *util.SaasRegistryCredentials, req *http.Request, tenantSubDomain string, isProvisioning bool) {
func (s *SubscriptionHandler) initializeCallback(tenantName string, ca *v1alpha1.CAPApplication, saasData *util.SaasRegistryCredentials, req *http.Request, tenantIn tenantInfo, isProvisioning bool) {
subscriptionDomain := ca.Annotations[AnnotationSubscriptionDomain]
if subscriptionDomain == "" {
subscriptionDomain = ca.Spec.Domains.Primary
}
appUrl := "https://" + tenantSubDomain + "." + subscriptionDomain
appUrl := "https://" + tenantIn.tenantSubDomain + "." + subscriptionDomain
asyncCallbackPath := req.Header.Get("STATUS_CALLBACK")
util.LogInfo("Callback initialized", TenantProvisioning, ca, nil, "subscription URL", appUrl, "async callback path", asyncCallbackPath, "tenantName", tenantName)

Expand Down Expand Up @@ -356,6 +362,11 @@ func (s *SubscriptionHandler) initializeCallback(tenantName string, ca *v1alpha1
additionalOutput = nil
}
}
// Add tenant data to the additional output if it exists
err := s.enrichAdditionalOutput(ca.Namespace, tenantIn.tenantId, additionalOutput)
if err != nil {
util.LogError(err, "Error updating tenant data", step, ca, nil, "tenantId", tenantIn.tenantId)
}
} else {
additionalOutput = nil
}
Expand All @@ -365,6 +376,34 @@ func (s *SubscriptionHandler) initializeCallback(tenantName string, ca *v1alpha1
util.LogInfo("Waiting for async saas callback after checks...", step, ca, nil, "tenantName", tenantName)
}

func (s *SubscriptionHandler) enrichAdditionalOutput(namespace string, tenantId string, additionalOutput *map[string]any) error {
labelSelector, err := labels.ValidatedSelectorFromSet(map[string]string{
LabelTenantId: tenantId,
})
if err != nil {
return err
}

tenantDataList, err := s.Clientset.SmeV1alpha1().CAPTenantOutputs(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()})
if err != nil {
return err
}

for _, tenantData := range tenantDataList.Items {
// Update relevant data from each CAPTenantOutput to saas callback additional output
tenantDataOutput := &map[string]any{}
err = json.Unmarshal([]byte(tenantData.Spec.SubscriptionCallbackData), tenantDataOutput)
if err != nil {
return err
}
// merge tenant data output into additional output
for k, v := range *tenantDataOutput {
(*additionalOutput)[k] = v
}
}
return nil
}

func (s *SubscriptionHandler) checkCAPTenantStatus(ctx context.Context, tenantNamespace string, tenantName string, provisioning bool, callbackTimeoutMs string) bool {
asyncCallbackTimeout := 15 * time.Minute
if callbackTimeoutMs != "" {
Expand Down
39 changes: 30 additions & 9 deletions cmd/server/internal/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,17 @@ const (
tenantId = "012012012-1234-1234-123456"
)

func setup(ca *v1alpha1.CAPApplication, cat *v1alpha1.CAPTenant, client *http.Client) *SubscriptionHandler {
func setup(ca *v1alpha1.CAPApplication, cat *v1alpha1.CAPTenant, ctout *v1alpha1.CAPTenantOutput, client *http.Client) *SubscriptionHandler {
crdObjects := []runtime.Object{}
if ca != nil {
crdObjects = append(crdObjects, ca)
}
if cat != nil {
crdObjects = append(crdObjects, cat)
}
if ctout != nil {
crdObjects = append(crdObjects, ctout)
}

subHandler := NewSubscriptionHandler(fake.NewSimpleClientset(crdObjects...), k8sfake.NewSimpleClientset(createSecrets()...))
if client != nil {
Expand Down Expand Up @@ -221,7 +224,7 @@ func TestMain(m *testing.M) {
func Test_IncorrectMethod(t *testing.T) {
res := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodPatch, RequestPath, strings.NewReader(`{"foo": "bar"}`))
subHandler := setup(nil, nil, nil)
subHandler := setup(nil, nil, nil, nil)
subHandler.HandleRequest(res, req)
if res.Code != http.StatusMethodNotAllowed {
t.Errorf("Expected status '%d', received '%d'", http.StatusMethodNotAllowed, res.Code)
Expand Down Expand Up @@ -251,6 +254,7 @@ func Test_provisioning(t *testing.T) {
invalidAdditionalData bool
withSecretKey bool
existingTenant bool
existingTenantOutput bool
expectedStatusCode int
expectedResponse Result
}{
Expand Down Expand Up @@ -304,6 +308,19 @@ func Test_provisioning(t *testing.T) {
Message: ResourceCreated,
},
},
{
name: "Provisioning Request valid with additional data and existing tenant and existing tenant output",
method: http.MethodPut,
body: `{"subscriptionAppName":"` + appName + `","globalAccountGUID":"` + globalAccountId + `","subscribedTenantId":"` + tenantId + `","subscribedSubdomain":"` + subDomain + `"}`,
createCROs: true,
withAdditionalData: true,
existingTenant: true,
existingTenantOutput: true,
expectedStatusCode: http.StatusAccepted,
expectedResponse: Result{
Message: ResourceCreated,
},
},
{
name: "Provisioning Request valid with invalid additional data and existing tenant",
method: http.MethodPut,
Expand Down Expand Up @@ -334,6 +351,7 @@ func Test_provisioning(t *testing.T) {
t.Run(testData.name, func(t *testing.T) {
var ca *v1alpha1.CAPApplication
var cat *v1alpha1.CAPTenant
var ctout *v1alpha1.CAPTenantOutput
if testData.createCROs {
ca = createCA()
if testData.withAdditionalData {
Expand All @@ -347,11 +365,14 @@ func Test_provisioning(t *testing.T) {
if testData.existingTenant {
cat = createCAT(testData.withAdditionalData)
}
if testData.existingTenantOutput {
ctout = &v1alpha1.CAPTenantOutput{ObjectMeta: v1.ObjectMeta{Name: caName + "-provider", Namespace: v1.NamespaceDefault, Labels: map[string]string{LabelTenantId: tenantId}}, Spec: v1alpha1.CAPTenantOutputSpec{SubscriptionCallbackData: "{\"foo3\":\"bar3\"}"}}
}
client, tokenString, err := SetupValidTokenAndIssuerForSubscriptionTests("appname!b14")
if err != nil {
t.Fatal(err.Error())
}
subHandler := setup(ca, cat, client)
subHandler := setup(ca, cat, ctout, client)

res := httptest.NewRecorder()
req := httptest.NewRequest(testData.method, RequestPath, strings.NewReader(testData.body))
Expand Down Expand Up @@ -446,7 +467,7 @@ func Test_deprovisioning(t *testing.T) {
if err != nil {
t.Fatal(err.Error())
}
subHandler := setup(ca, cat, client)
subHandler := setup(ca, cat, nil, client)

res := httptest.NewRecorder()
req := httptest.NewRequest(testData.method, RequestPath, strings.NewReader(testData.body))
Expand Down Expand Up @@ -614,7 +635,7 @@ func TestAsyncCallback(t *testing.T) {
saasData.CredentialType = p.useCredentialType
t.Run(p.testName, func(t *testing.T) {
client := createCallbackTestServer(context.TODO(), t, &p)
subHandler := setup(nil, nil, client)
subHandler := setup(nil, nil, nil, client)
subHandler.handleAsyncCallback(
ctx,
saasData,
Expand All @@ -631,7 +652,7 @@ func TestAsyncCallback(t *testing.T) {
func TestCheckTenantStatusContextCancellationAsyncTimeout(t *testing.T) {
execTestsWithBLI(t, "Check Tenant Status Context Cancellation AsyncTimeout", []string{"ERP4SMEPREPWORKAPPPLAT-2240"}, func(t *testing.T) {
// test context cancellation (like deadline)
subHandler := setup(nil, nil, nil)
subHandler := setup(nil, nil, nil, nil)
notify := make(chan bool)
go func() {
r := subHandler.checkCAPTenantStatus(context.Background(), "default", "test-cat", true, "4000")
Expand All @@ -654,7 +675,7 @@ func TestCheckTenantStatusContextCancellationAsyncTimeout(t *testing.T) {
func TestCheckTenantStatusTenantReady(t *testing.T) {
// test context cancellation (like deadline)
cat := createCAT(true)
subHandler := setup(nil, cat, nil)
subHandler := setup(nil, cat, nil, nil)
r := subHandler.checkCAPTenantStatus(context.TODO(), cat.Namespace, cat.Name, true, "")

if r != true {
Expand All @@ -666,7 +687,7 @@ func TestCheckTenantStatusWithCallbacktimeout(t *testing.T) {
execTestsWithBLI(t, "Check Tenant Status With Callback timeout", []string{"ERP4SMEPREPWORKAPPPLAT-2240"}, func(t *testing.T) {
// test context cancellation (like deadline)
cat := createCAT(false)
subHandler := setup(nil, cat, nil)
subHandler := setup(nil, cat, nil, nil)
r := subHandler.checkCAPTenantStatus(context.TODO(), cat.Namespace, cat.Name, true, "4000")

if r != false {
Expand All @@ -680,7 +701,7 @@ func TestMultiXSUAA(t *testing.T) {
// CA without "sme.sap.com/primary-xsuaa" annotation
ca := createCA()

subHandler := setup(ca, nil, nil)
subHandler := setup(ca, nil, nil, nil)
uaaCreds := subHandler.getXSUAADetails(ca, "Test")

if uaaCreds.AuthUrl != "https://app-domain.auth.service.local" {
Expand Down
2 changes: 1 addition & 1 deletion crds/sme.sap.com_capapplications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
controller-gen.kubebuilder.io/version: v0.16.1
name: capapplications.sme.sap.com
spec:
group: sme.sap.com
Expand Down
Loading
Loading