Skip to content

Commit

Permalink
Merge pull request #258 from KiptoonKipkurui/feat/kiptoonkipkurui/mes…
Browse files Browse the repository at this point in the history
…hsync_crd

Meshync Dynamic Configuration from Meshsync
  • Loading branch information
Mohd Uzair authored Nov 8, 2023
2 parents dbcef1e + 988de5b commit e1545b9
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 28 deletions.
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ require (
github.com/layer5io/meshkit v0.6.75
github.com/myntra/pipeline v0.0.0-20180618182531-2babf4864ce8
github.com/spf13/viper v1.17.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/net v0.17.0
gorm.io/gorm v1.25.5
k8s.io/api v0.28.2
k8s.io/apimachinery v0.28.2
k8s.io/client-go v0.28.2
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
k8s.io/kubectl v0.28.2
)

Expand Down Expand Up @@ -49,7 +50,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.14.1 // indirect
Expand Down Expand Up @@ -145,7 +146,6 @@ require (
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/oauth2 v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.13.0 // indirect
Expand All @@ -163,10 +163,10 @@ require (
gorm.io/driver/postgres v1.4.6 // indirect
gorm.io/driver/sqlite v1.4.4 // indirect
helm.sh/helm/v3 v3.12.3 // indirect
k8s.io/apiextensions-apiserver v0.27.3 // indirect
k8s.io/apiserver v0.27.3 // indirect
k8s.io/apiextensions-apiserver v0.28.3 // indirect
k8s.io/apiserver v0.28.3 // indirect
k8s.io/cli-runtime v0.28.2 // indirect
k8s.io/component-base v0.28.2 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
Expand Down
28 changes: 14 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down Expand Up @@ -1181,20 +1181,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw=
k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg=
k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4=
k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84=
k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ=
k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU=
k8s.io/apiserver v0.27.3 h1:AxLvq9JYtveYWK+D/Dz/uoPCfz8JC9asR5z7+I/bbQ4=
k8s.io/apiserver v0.27.3/go.mod h1:Y61+EaBMVWUBJtxD5//cZ48cHZbQD+yIyV/4iEBhhNA=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
k8s.io/apiserver v0.28.3 h1:8Ov47O1cMyeDzTXz0rwcfIIGAP/dP7L8rWbEljRcg5w=
k8s.io/apiserver v0.28.3/go.mod h1:YIpM+9wngNAv8Ctt0rHG4vQuX/I5rvkEMtZtsxW2rNM=
k8s.io/cli-runtime v0.28.2 h1:64meB2fDj10/ThIMEJLO29a1oujSm0GQmKzh1RtA/uk=
k8s.io/cli-runtime v0.28.2/go.mod h1:bTpGOvpdsPtDKoyfG4EG041WIyFZLV9qq4rPlkyYfDA=
k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY=
k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY=
k8s.io/component-base v0.28.2 h1:Yc1yU+6AQSlpJZyvehm/NkJBII72rzlEsd6MkBQ+G0E=
k8s.io/component-base v0.28.2/go.mod h1:4IuQPQviQCg3du4si8GpMrhAIegxpsgPngPRR/zWpzc=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI=
k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
Expand Down
163 changes: 163 additions & 0 deletions internal/config/crd_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package config

import (
"context"
"errors"

"github.com/layer5io/meshkit/utils"
"golang.org/x/exp/slices"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)

var (
namespace = "meshery" // Namespace for the Custom Resource
crName = "meshery-meshsync" // Name of the custom resource
version = "v1alpha1" // Version of the Custom Resource
group = "meshery.layer5.io" //Group for the Custom Resource
resource = "meshsyncs" //Name of the Resource
)

func GetMeshsyncCRDConfigs(dyClient dynamic.Interface) (*MeshsyncConfig, error) {
// initialize the group version resource to access the custom resource
gvr := schema.GroupVersionResource{Version: version, Group: group, Resource: resource}

// make a call to get the custom resource
crd, err := dyClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), crName, metav1.GetOptions{})

if err != nil {
return nil, ErrInitConfig(err)
}

if crd == nil {
return nil, ErrInitConfig(errors.New("Custom Resource is nil"))
}

spec := crd.Object["spec"]
specMap, ok := spec.(map[string]interface{})
if !ok {
return nil, ErrInitConfig(errors.New("Unable to convert spec to map"))
}
configObj := specMap["watch-list"]
if configObj == nil {
return nil, ErrInitConfig(errors.New("Custom Resource does not have Meshsync Configs"))
}
configStr, err := utils.Marshal(configObj)
if err != nil {
return nil, ErrInitConfig(err)
}

configMap := corev1.ConfigMap{}
err = utils.Unmarshal(string(configStr), &configMap)

if err != nil {
return nil, ErrInitConfig(err)
}

// populate the required configs
meshsyncConfig, err := PopulateConfigs(configMap)

if err != nil {
return nil, ErrInitConfig(err)
}
return meshsyncConfig, nil
}

// PopulateConfigs compares the default configs and the whitelist and blacklist
func PopulateConfigs(configMap corev1.ConfigMap) (*MeshsyncConfig, error) {
meshsyncConfig := &MeshsyncConfig{}

if _, ok := configMap.Data["blacklist"]; ok {
if len(configMap.Data["blacklist"]) > 0 {
err := utils.Unmarshal(configMap.Data["blacklist"], &meshsyncConfig.BlackList)
if err != nil {
return nil, ErrInitConfig(err)
}
}
}

if _, ok := configMap.Data["whitelist"]; ok {
if len(configMap.Data["whitelist"]) > 0 {
err := utils.Unmarshal(configMap.Data["whitelist"], &meshsyncConfig.WhiteList)
if err != nil {
return nil, ErrInitConfig(err)
}
}
}

// ensure that atleast one of whitelist or blacklist has been supplied
if len(meshsyncConfig.BlackList) == 0 && len(meshsyncConfig.WhiteList) == 0 {
return nil, ErrInitConfig(errors.New("Both whitelisted and blacklisted resources missing"))
}

// ensure that only one of whitelist or blacklist has been supplied
if len(meshsyncConfig.BlackList) != 0 && len(meshsyncConfig.WhiteList) != 0 {
return nil, ErrInitConfig(errors.New("Both whitelisted and blacklisted resources not currently supported"))
}

// Handle global resources
globalPipelines := make(PipelineConfigs, 0)
localPipelines := make(PipelineConfigs, 0)

if len(meshsyncConfig.WhiteList) != 0 {
for _, v := range Pipelines[GlobalResourceKey] {
if idx := slices.IndexFunc(meshsyncConfig.WhiteList, func(c ResourceConfig) bool { return c.Resource == v.Name }); idx != -1 {
config := meshsyncConfig.WhiteList[idx]
v.Events = config.Events
globalPipelines = append(globalPipelines, v)
}
}
if len(globalPipelines) > 0 {
meshsyncConfig.Pipelines = map[string]PipelineConfigs{}
meshsyncConfig.Pipelines[GlobalResourceKey] = globalPipelines
}

// Handle local resources
for _, v := range Pipelines[LocalResourceKey] {
if idx := slices.IndexFunc(meshsyncConfig.WhiteList, func(c ResourceConfig) bool { return c.Resource == v.Name }); idx != -1 {
config := meshsyncConfig.WhiteList[idx]
v.Events = config.Events
localPipelines = append(localPipelines, v)
}
}

if len(localPipelines) > 0 {
if meshsyncConfig.Pipelines == nil {
meshsyncConfig.Pipelines = make(map[string]PipelineConfigs)
}
meshsyncConfig.Pipelines[LocalResourceKey] = localPipelines
}

} else {

for _, v := range Pipelines[GlobalResourceKey] {
if idx := slices.IndexFunc(meshsyncConfig.BlackList, func(c string) bool { return c == v.Name }); idx == -1 {
v.Events = DefaultEvents
globalPipelines = append(globalPipelines, v)
}
}
if len(globalPipelines) > 0 {
meshsyncConfig.Pipelines = map[string]PipelineConfigs{}
meshsyncConfig.Pipelines[GlobalResourceKey] = globalPipelines
}

// Handle local resources
for _, v := range Pipelines[LocalResourceKey] {
if idx := slices.IndexFunc(meshsyncConfig.BlackList, func(c string) bool { return c == v.Name }); idx == -1 {
v.Events = DefaultEvents
localPipelines = append(localPipelines, v)
}
}

if len(localPipelines) > 0 {
if meshsyncConfig.Pipelines == nil {
meshsyncConfig.Pipelines = make(map[string]PipelineConfigs)
}
meshsyncConfig.Pipelines[LocalResourceKey] = localPipelines
}
}

return meshsyncConfig, nil
}
116 changes: 116 additions & 0 deletions internal/config/crd_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package config

// test for empty blacklist/whitelist
import (
"context"
"reflect"
"testing"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic/fake"
)

var (
Kind string = "MeshSync"
APIVersion string = "meshery.layer5.io/v1alpha1"
URL string = "https://layer5.io"
fakeDyClient *fake.FakeDynamicClient
ctx = context.Background()
)

func TestWhiteListResources(t *testing.T) {

// Create an instance of the custom resource.
watchList := corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1apha1",
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: "watch-list",
Namespace: "default",
},
Data: map[string]string{
"blacklist": "",
"whitelist": "[{\"Resource\":\"namespaces.v1.\",\"Events\":[\"ADDED\",\"DELETE\"]},{\"Resource\":\"replicasets.v1.apps\",\"Events\":[\"ADDED\",\"DELETE\"]},{\"Resource\":\"pods.v1.\",\"Events\":[\"MODIFIED\"]}]",
},
}

meshsyncConfig, err := PopulateConfigs(watchList)

if err != nil {
t.Errorf("Meshsync config not well deserialized got %s", err.Error())
}

if len(meshsyncConfig.WhiteList) == 0 {
t.Errorf("WhiteListed resources not correctly deserialized")
}
expectedWhiteList := []ResourceConfig{
{Resource: "namespaces.v1.", Events: []string{"ADDED", "DELETE"}},
{Resource: "replicasets.v1.apps", Events: []string{"ADDED", "DELETE"}},
{Resource: "pods.v1.", Events: []string{"MODIFIED"}},
}

if !reflect.DeepEqual(meshsyncConfig.WhiteList, expectedWhiteList) {
t.Error("WhiteListed resources not equal")
}

// now we assertain the global and local pipelines have been correctly configured
// global pipelines: namespaces
// local pipelines: pods, replicasets

if len(meshsyncConfig.Pipelines["global"]) != 1 {
t.Error("global pipelines not well configured expected 1")
}

if len(meshsyncConfig.Pipelines["local"]) != 2 {
t.Error("global pipelines not well configured expected 2")
}
}

func TestBlackListResources(t *testing.T) {

// Create an instance of the custom resource.
watchList := corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1apha1",
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: "watch-list",
Namespace: "default",
},
Data: map[string]string{
"blacklist": "[\"namespaces.v1.\",\"pods.v1.\"]",
"whitelist": "",
},
}

meshsyncConfig, err := PopulateConfigs(watchList)

if err != nil {
t.Errorf("Meshsync config not well deserialized got %s", err.Error())
}

if len(meshsyncConfig.BlackList) == 0 {
t.Errorf("WhiteListed resources")
}

expectedBlackList := []string{"namespaces.v1.", "pods.v1."}
if !reflect.DeepEqual(meshsyncConfig.BlackList, expectedBlackList) {
t.Error("WhiteListed resources not equal")
}

// now we assertain the global and local pipelines have been correctly configured
// excempted global pipelines: namespaces
// excempted local pipelines: pods, replicasets

if len(meshsyncConfig.Pipelines["global"]) != 5 {
t.Error("global pipelines not well configured expected 5")
}

if len(meshsyncConfig.Pipelines["local"]) != 14 {
t.Error("global pipelines not well configured expected 15")
}
}
2 changes: 2 additions & 0 deletions internal/config/default_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,6 @@ var (
SubscribeTo: "meshery.meshsync.request",
},
}

DefaultEvents = []string{"ADD", "UPDATE", "DELETE"}
)
Loading

0 comments on commit e1545b9

Please sign in to comment.