Skip to content

Commit

Permalink
Hive SyncSet GenevaActions
Browse files Browse the repository at this point in the history
Implements:

1. Geneva Action to list selectorsyncsets for a given hive instance
2. Geneva Action to retrieve an individual selectorsyncset on a given hive instance
3. Geneva Action to list syncsets for a cluster defined by resource id in a hive instance
4. Geneva Action to retrieve an individual syncsets on a given hive instance

How to call:

List selector/syncsets - /admin/hivesyncset?namespace={namespace}?isSyncset
Get a selector/syncset   - /admin/hivesyncset/syncsetname/{syncsetname}?namespace={namespace}?isSyncSet

Notes:
1. namespace should be "" for selectorsyncsets
2. namespace cannot be "" for syncsets
3. isSyncSet can be true/false, setting it changes from using default selectorsyncset to syncset
  • Loading branch information
SrinivasAtmakuri committed Oct 1, 2024
1 parent 817438f commit 0ff61e8
Show file tree
Hide file tree
Showing 47 changed files with 745 additions and 48 deletions.
9 changes: 7 additions & 2 deletions cmd/aro/rp.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error {
if err != nil {
return err
}
hiveClusterManager, err := hive.NewFromEnv(ctx, log, _env)
hiveClusterManager, err := hive.NewFromEnvCLusterManager(ctx, log, _env)
if err != nil {
return err
}

hiveSyncSetManager, err := hive.NewFromEnvSyncSetManager(ctx, log, _env)
if err != nil {
return err
}
Expand All @@ -172,7 +177,7 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error {
WithPlatformWorkloadIdentityRoleSets(dbPlatformWorkloadIdentityRoleSets).
WithSubscriptions(dbSubscriptions)

f, err := frontend.NewFrontend(ctx, audit, log.WithField("component", "frontend"), _env, dbg, api.APIs, metrics, clusterm, feAead, hiveClusterManager, adminactions.NewKubeActions, adminactions.NewAzureActions, adminactions.NewAppLensActions, clusterdata.NewParallelEnricher(metrics, _env))
f, err := frontend.NewFrontend(ctx, audit, log.WithField("component", "frontend"), _env, dbg, api.APIs, metrics, clusterm, feAead, hiveClusterManager, hiveSyncSetManager, adminactions.NewKubeActions, adminactions.NewAzureActions, adminactions.NewAppLensActions, clusterdata.NewParallelEnricher(metrics, _env))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/backend/openshiftcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (ocb *openShiftClusterBackend) handle(ctx context.Context, log *logrus.Entr
if err != nil {
return fmt.Errorf("failed getting RESTConfig for Hive shard %d: %w", hiveShard, err)
}
hr, err = hive.NewFromConfig(log, ocb.env, hiveRestConfig)
hr, err = hive.NewFromConfigClusterManager(log, ocb.env, hiveRestConfig)
if err != nil {
return fmt.Errorf("failed creating HiveClusterManager: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/frontend/admin_hive_clusterdeployment_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ func Test_getAdminHiveClusterDeployment(t *testing.T) {
if tt.hiveEnabled {
clusterManager := mock_hive.NewMockClusterManager(controller)
clusterManager.EXPECT().GetClusterDeployment(gomock.Any(), gomock.Any()).Return(&clusterDeployment, nil).Times(tt.expectedGetClusterDeploymentCallCount)
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, clusterManager, nil, nil, nil, nil)
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, clusterManager, nil, nil, nil, nil, nil)
} else {
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil)
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil, nil)
}

if err != nil {
Expand Down
68 changes: 68 additions & 0 deletions pkg/frontend/admin_hive_syncset_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package frontend

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"context"
"net/http"
"reflect"

"github.com/go-chi/chi/v5"
hivev1 "github.com/openshift/hive/apis/hive/v1"
"github.com/sirupsen/logrus"
"github.com/ugorji/go/codec"

"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
)

func (f *frontend) getAdminHiveSyncSet(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
syncsetname := chi.URLParam(r, "syncsetname")
isSyncSet := r.URL.Query().Has("isSyncSet")
namespace := r.URL.Query().Get("namespace")
b, err := f._getAdminHiveSyncSet(ctx, namespace, syncsetname, isSyncSet)

if cloudErr, ok := err.(*api.CloudError); ok {
api.WriteCloudError(w, cloudErr)
return
}

adminReply(log, w, nil, b, err)
}

func (f *frontend) _getAdminHiveSyncSet(ctx context.Context, namespace string, syncsetname string, isSyncSet bool) ([]byte, error) {
// we have to check if the frontend has a valid syncSetManager since hive is not everywhere.
if f.hiveSyncSetManager == nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "hive is not enabled")
}

ssType := reflect.TypeOf(hivev1.SelectorSyncSet{})
if isSyncSet {
ssType = reflect.TypeOf(hivev1.SyncSet{})
}
if isSyncSet && namespace == "" {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "namespace cannot be null for getting a syncset")
}
if !isSyncSet && namespace != "" {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "namespace should be null for getting a selectorsyncset")
}
if syncsetname == "" {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "syncsetname cannot be null")
}

ss, err := f.hiveSyncSetManager.Get(ctx, namespace, syncsetname, ssType)
if err != nil {
return nil, err
}

var b []byte
err = codec.NewEncoderBytes(&b, &codec.JsonHandle{}).Encode(ss)
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "unable to marshal response")
}

return b, nil
}
153 changes: 153 additions & 0 deletions pkg/frontend/admin_hive_syncset_get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package frontend

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"context"
"reflect"
"strings"
"testing"

"github.com/golang/mock/gomock"
hivev1 "github.com/openshift/hive/apis/hive/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/metrics/noop"
mock_env "github.com/Azure/ARO-RP/pkg/util/mocks/env"
mock_hive "github.com/Azure/ARO-RP/pkg/util/mocks/hive"
)

func Test_getAdminHiveSyncSet(t *testing.T) {
fakeUUID := "00000000-0000-0000-0000-000000000000"
ctx := context.Background()
syncsetTest := hivev1.SyncSet{
ObjectMeta: metav1.ObjectMeta{
Name: "syncset-01",
},
}
selectorSyncSetTest := hivev1.SelectorSyncSet{
ObjectMeta: metav1.ObjectMeta{
Name: "selectorsyncset-01",
},
}
type test struct {
name string
namespace string
syncsetname string
isSyncSet bool
hiveEnabled bool
mocks func(*test, *mock_hive.MockSyncSetManager)
wantStatusCode int
wantResponse []byte
wantError string
}

for _, tt := range []*test{
{
name: "selectorSyncSets are not namespaced",
namespace: "aro-" + fakeUUID,
syncsetname: "syncsetTest",
isSyncSet: false,
hiveEnabled: true,
mocks: func(tt *test, s *mock_hive.MockSyncSetManager) {},
wantStatusCode: 400,
wantError: "400: InvalidRequestContent: : namespace should be null for getting a selectorsyncset",
},
{
name: "SyncSets must be namespaced",
namespace: "",
syncsetname: "syncsetTest",
isSyncSet: true,
hiveEnabled: true,
mocks: func(tt *test, s *mock_hive.MockSyncSetManager) {},
wantStatusCode: 400,
wantError: "400: InvalidRequestContent: : namespace cannot be null for getting a syncset",
},
{
name: "syncset name is must",
namespace: "",
syncsetname: "",
isSyncSet: false,
hiveEnabled: true,
mocks: func(tt *test, s *mock_hive.MockSyncSetManager) {},
wantStatusCode: 400,
wantError: "400: InvalidRequestContent: : syncsetname cannot be null",
},
{
name: "get a Selectorsyncset",
namespace: "",
syncsetname: "selectorSyncSetTest",
isSyncSet: false,
hiveEnabled: true,
mocks: func(tt *test, s *mock_hive.MockSyncSetManager) {
s.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Eq(reflect.TypeOf(hivev1.SelectorSyncSet{}))).
Return(&selectorSyncSetTest, nil).Times(1)
},
wantStatusCode: 200,
wantResponse: []byte(`{"metadata":{"name":"selectorsyncset-01"}}`),
wantError: "",
},
{
name: "get a syncset",
namespace: "aro-" + fakeUUID,
syncsetname: "syncSetTest",
isSyncSet: true,
hiveEnabled: true,
mocks: func(tt *test, s *mock_hive.MockSyncSetManager) {
s.EXPECT().
Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Eq(reflect.TypeOf(hivev1.SyncSet{}))).
Return(&syncsetTest, nil).Times(1)
},
wantStatusCode: 200,
wantResponse: []byte(`{"metadata":{"name":"syncset-01"}}`),
wantError: "",
},
{
name: "Hive is not enabled selector/syncsets",
namespace: "aro-" + fakeUUID,
syncsetname: "syncSetTest",
mocks: nil,
isSyncSet: false,
hiveEnabled: false,
wantStatusCode: 500,
wantError: "500: InternalServerError: : hive is not enabled",
},
} {
t.Run(tt.name, func(t *testing.T) {
ti := newTestInfra(t).WithOpenShiftClusters().WithSubscriptions()
defer ti.done()

_env := ti.env.(*mock_env.MockInterface)
var f *frontend
var err error
if tt.hiveEnabled {
s := mock_hive.NewMockSyncSetManager(ti.controller)
tt.mocks(tt, s)
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, s, nil, nil, nil, nil)
} else {
f, err = NewFrontend(ctx, ti.audit, ti.log, _env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil, nil)
}
if err != nil {
t.Fatal(err)
}

hiveSyncSet, err := f._getAdminHiveSyncSet(ctx, tt.namespace, tt.syncsetname, tt.isSyncSet)
cloudErr, isCloudErr := err.(*api.CloudError)
if tt.wantError != "" && isCloudErr && cloudErr != nil {
if tt.wantError != cloudErr.Error() {
t.Fatalf("got %q but wanted %q", cloudErr.Error(), tt.wantError)
}
if tt.wantStatusCode != 0 && tt.wantStatusCode != cloudErr.StatusCode {
t.Fatalf("got %q but wanted %q", cloudErr.Error(), tt.wantError)
}
}

if !strings.EqualFold(string(hiveSyncSet), string(tt.wantResponse)) {
t.Fatalf("got %q and expected %q", hiveSyncSet, tt.wantResponse)
}
})
}
}
65 changes: 65 additions & 0 deletions pkg/frontend/admin_hive_syncset_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package frontend

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"context"
"net/http"
"reflect"

hivev1 "github.com/openshift/hive/apis/hive/v1"
"github.com/sirupsen/logrus"
"github.com/ugorji/go/codec"

"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
)

func (f *frontend) listAdminHiveSyncSet(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
namespace := r.URL.Query().Get("namespace")
label := r.URL.Query().Get("lable")
isSyncSet := r.URL.Query().Has("isSyncSet")
b, err := f._listAdminHiveSyncSet(ctx, namespace, label, isSyncSet)

if cloudErr, ok := err.(*api.CloudError); ok {
api.WriteCloudError(w, cloudErr)
return
}

adminReply(log, w, nil, b, err)
}

func (f *frontend) _listAdminHiveSyncSet(ctx context.Context, namespace string, label string, isSyncSet bool) ([]byte, error) {
// we have to check if the frontend has a valid syncSetManager since hive is not everywhere.
if f.hiveSyncSetManager == nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "hive is not enabled")
}

// defaults to listing selectorSyncSets for an AKS instance
ssType := reflect.TypeOf(hivev1.SelectorSyncSetList{})
if isSyncSet {
ssType = reflect.TypeOf(hivev1.SyncSetList{})
}
if isSyncSet && namespace == "" {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "namespace cannot be null for listing syncsets")
}
if !isSyncSet && namespace != "" {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "namespace should be null for listing selectorsyncsets")
}

ss, err := f.hiveSyncSetManager.List(ctx, namespace, label, ssType)
if err != nil {
return nil, err
}

var b []byte
err = codec.NewEncoderBytes(&b, &codec.JsonHandle{}).Encode(ss)
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "unable to marshal response")
}

return b, nil
}
Loading

0 comments on commit 0ff61e8

Please sign in to comment.