Merge pull request #1199 from mjudeikis/enrich.cluster.service.principal

Add Cluster Service Principal enrich
This commit is contained in:
Jim Minter 2020-12-04 09:45:31 -06:00 коммит произвёл GitHub
Родитель eb2af5217e d4a80f1a52
Коммит 1bc0164f27
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 166 добавлений и 0 удалений

Просмотреть файл

@ -107,6 +107,7 @@ func (f *frontend) _putOrPatchOpenShiftCluster(ctx context.Context, r *http.Requ
}
}
// If Put or Patch is executed we will enrich document with cluster data.
if !isCreate {
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
@ -115,6 +116,9 @@ func (f *frontend) _putOrPatchOpenShiftCluster(ctx context.Context, r *http.Requ
var ext interface{}
switch r.Method {
// In case of PUT we will take customer request payload and store into database
// Our base structure for unmarshal is skeleton document with values we
// think is required. We expect payload to have everything else required.
case http.MethodPut:
ext = converter.ToExternal(&api.OpenShiftCluster{
ID: doc.OpenShiftCluster.ID,
@ -132,6 +136,10 @@ func (f *frontend) _putOrPatchOpenShiftCluster(ctx context.Context, r *http.Requ
},
})
// In case of PATCH we take current cluster document, which is enriched
// from the cluster and use it as base for unmarshal. So customer can
// provide single field json to be updated in the database.
// Patch should be used for updating individual fields of the document.
case http.MethodPatch:
ext = converter.ToExternal(doc.OpenShiftCluster)
}
@ -210,6 +218,8 @@ func (f *frontend) _putOrPatchOpenShiftCluster(ctx context.Context, r *http.Requ
return nil, err
}
// We remove sensitive data from document to prevent sensitive data being
// returned to the customer.
doc.OpenShiftCluster.Properties.ClusterProfile.PullSecret = ""
doc.OpenShiftCluster.Properties.ServicePrincipalProfile.ClientSecret = ""

Просмотреть файл

@ -0,0 +1,50 @@
package clusterdata
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/Azure/ARO-RP/pkg/api"
)
func newClusterServicePrincipalEnricherTask(log *logrus.Entry, restConfig *rest.Config, oc *api.OpenShiftCluster) (enricherTask, error) {
client, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return nil, err
}
return &clusterServicePrincipalEnricherTask{
log: log,
client: client,
oc: oc,
}, nil
}
type clusterServicePrincipalEnricherTask struct {
log *logrus.Entry
client kubernetes.Interface
oc *api.OpenShiftCluster
}
func (ef *clusterServicePrincipalEnricherTask) FetchData(ctx context.Context, callbacks chan<- func(), errs chan<- error) {
secret, err := ef.client.CoreV1().Secrets("kube-system").Get(ctx, "azure-credentials", metav1.GetOptions{})
if err != nil {
ef.log.Error(err)
errs <- err
return
}
callbacks <- func() {
ef.oc.Properties.ServicePrincipalProfile.ClientID = string(secret.Data["azure_client_id"])
ef.oc.Properties.ServicePrincipalProfile.ClientSecret = api.SecureString(secret.Data["azure_client_secret"])
}
}
func (ef *clusterServicePrincipalEnricherTask) SetDefaults() {}

Просмотреть файл

@ -0,0 +1,105 @@
package clusterdata
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"reflect"
"testing"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/test/util/cmp"
)
func TestClusterServidePrincipalEnricherTask(t *testing.T) {
log := logrus.NewEntry(logrus.StandardLogger())
name := "azure-credentials"
namespace := "kube-system"
for _, tt := range []struct {
name string
client kubernetes.Interface
wantOc *api.OpenShiftCluster
wantErr string
}{
{
name: "enrich worked",
client: fake.NewSimpleClientset(&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: map[string][]byte{
"azure_client_id": []byte("new-client-id"),
"azure_client_secret": []byte("new-client-secret"),
},
}),
wantOc: &api.OpenShiftCluster{
Properties: api.OpenShiftClusterProperties{
ServicePrincipalProfile: api.ServicePrincipalProfile{
ClientID: "new-client-id",
ClientSecret: api.SecureString("new-client-secret"),
},
},
},
},
{
name: "enrich failed - stale data",
client: fake.NewSimpleClientset(),
wantOc: &api.OpenShiftCluster{
Properties: api.OpenShiftClusterProperties{
ServicePrincipalProfile: api.ServicePrincipalProfile{
ClientID: "old-client-id",
ClientSecret: "old-client-secret",
},
},
},
wantErr: "secrets \"azure-credentials\" not found",
},
} {
t.Run(tt.name, func(t *testing.T) {
oc := &api.OpenShiftCluster{
Properties: api.OpenShiftClusterProperties{
ServicePrincipalProfile: api.ServicePrincipalProfile{
ClientID: "old-client-id",
ClientSecret: api.SecureString("old-client-secret"),
},
},
}
e := &clusterServicePrincipalEnricherTask{
log: log,
client: tt.client,
oc: oc,
}
e.SetDefaults()
callbacks := make(chan func())
errors := make(chan error)
go e.FetchData(context.Background(), callbacks, errors)
select {
case f := <-callbacks:
f()
if !reflect.DeepEqual(oc, tt.wantOc) {
t.Error(cmp.Diff(oc, tt.wantOc))
}
case err := <-errors:
if tt.wantErr != err.Error() {
t.Error(err)
}
// we want to make sure we see stale database data in case of failures
if !reflect.DeepEqual(oc, tt.wantOc) {
t.Error(cmp.Diff(oc, tt.wantOc))
}
}
})
}
}

Просмотреть файл

@ -43,6 +43,7 @@ func NewBestEffortEnricher(log *logrus.Entry, dialer proxy.Dialer, m metrics.Int
taskConstructors: []enricherTaskConstructor{
newClusterVersionEnricherTask,
newWorkerProfilesEnricherTask,
newClusterServicePrincipalEnricherTask,
},
}
}