restructure rcloud-base as a single base controller (#37)

* restructure rcloud-base as a single base controller
* updated master.rest
* moved sentry from internal to pkg as it is used by relay
* removing unused rpc and it's dependencies
* Fix usermgmt tests
* Don't redefine variables in rest file
Co-authored-by: Abin Simon <abin.simon@rafay.co>
This commit is contained in:
nirav-rafay
2022-03-03 17:59:06 +05:30
committed by GitHub
parent 4ff3d06ef7
commit c66bdc25cd
1176 changed files with 9472 additions and 88767 deletions
+218
View File
@@ -0,0 +1,218 @@
package runtime
import (
"bytes"
"errors"
"github.com/RafaySystems/rcloud-base/pkg/controller/scheme"
apiv2 "github.com/RafaySystems/rcloud-base/proto/types/controller"
rbacv1 "k8s.io/api/rbac/v1"
apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/RafaySystems/rcloud-base/pkg/controller/util"
)
var (
// ErrInvalidObject is returned for invalid object
ErrInvalidObject = errors.New("object interface not implemented")
)
// FromObject creates step object from runtime object
func FromObject(ro runtime.Object) (*apiv2.StepObject, error) {
var so apiv2.StepObject
var err error
bb := new(bytes.Buffer)
err = scheme.Serializer.Encode(ro, bb)
if err != nil {
return nil, err
}
// use step object accessor to get object gvk
// so.SetGroupVersionKind(ro.GetObjectKind().GroupVersionKind())
// if mo, ok := ro.(metav1.Object); ok {
// so.Name = mo.GetName()
// }
so.Raw = bb.Bytes()
// so.Raw, err = util.CleanPatch(so.Raw)
// if err != nil {
// return nil, err
// }
return &so, nil
}
// SetNamespace sets namespace for runtime object
func SetNamespace(ro runtime.Object, namespace string) error {
switch ro.(type) {
case *apixv1beta1.CustomResourceDefinition:
case *rbacv1.ClusterRole:
case *rbacv1.ClusterRoleBinding:
crb := ro.(*rbacv1.ClusterRoleBinding)
for i := range crb.Subjects {
if crb.Subjects[i].Kind == rbacv1.ServiceAccountKind &&
crb.Subjects[i].Namespace == "" {
crb.Subjects[i].Namespace = namespace
}
}
case *rbacv1.RoleBinding:
rb := ro.(*rbacv1.RoleBinding)
if rb.Namespace == "" {
rb.Namespace = namespace
}
for i := range rb.Subjects {
if rb.Subjects[i].Kind == rbacv1.ServiceAccountKind &&
rb.Subjects[i].Namespace == "" {
rb.Subjects[i].Namespace = namespace
}
}
case *rbacv1.Role:
rb := ro.(*rbacv1.Role)
if rb.Namespace == "" {
rb.Namespace = namespace
}
default:
if mo, ok := ro.(metav1.Object); ok {
mo.SetNamespace(namespace)
return nil
}
return ErrInvalidObject
}
return nil
}
// ToObject converts step object to runtime object
func ToObject(so *apiv2.StepObject) (o runtime.Object, gvk *schema.GroupVersionKind, err error) {
accessor, err := so.Accessor()
if err != nil {
return
}
eGVK, err := accessor.GroupVersionKind()
if err != nil {
return
}
if scheme.Scheme.Recognizes(eGVK) {
o, gvk, err = scheme.Serializer.Decode(so.Raw, nil, nil)
} else {
o, gvk, err = scheme.Serializer.Decode(so.Raw, nil, &unstructured.Unstructured{})
}
return o, gvk, err
}
// ToStructuredObject converts unstructured object to structured object
func ToStructuredObject(obj runtime.Object) runtime.Object {
if _, ok := obj.(*unstructured.Unstructured); ok {
bb := new(bytes.Buffer)
err := scheme.Serializer.Encode(obj, bb)
if err != nil {
return obj
}
o, _, err := scheme.Serializer.Decode(bb.Bytes(), nil, nil)
if err != nil {
return obj
}
return o
}
return obj
}
// ToUnstructuredObject converts step object to unstructured object,
// this is useful for preserving original json serialized input in step object.
// Note: while patching k8s resources, we should preserve the user input, as
// patching can remove fields; which are represented as null values in the patch
func ToUnstructuredObject(so *apiv2.StepObject) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
var o runtime.Object
var err error
var gvk *schema.GroupVersionKind
o, gvk, err = scheme.Serializer.Decode(so.Raw, nil, &unstructured.Unstructured{})
return o.(*unstructured.Unstructured), gvk, err
}
// PatchMeta is the metadata to be added while patching
type PatchMeta struct {
Annotations map[string]string
}
// PatchOption is the functional patch option
type PatchOption func(*PatchMeta)
// AddAnnotations adds annotations to object
func AddAnnotations(annotations map[string]string) PatchOption {
return func(m *PatchMeta) {
m.Annotations = annotations
}
}
// Patch patches the step object with give step object
func Patch(input *apiv2.StepObject, with *apiv2.StepObject, opts ...PatchOption) error {
pm := &PatchMeta{}
for _, opt := range opts {
opt(pm)
}
accessor, err := input.Accessor()
if err != nil {
return err
}
gvk, err := accessor.GroupVersionKind()
if err != nil {
return err
}
if util.IsStrategicMergePatch(gvk) {
pb, err := util.CreateStrategicMergePatch(gvk, nil, input.Raw, with.Raw)
if err != nil {
return err
}
fb, err := util.ApplyStrategicMergePatch(gvk, input.Raw, pb)
if err != nil {
return err
}
input.Raw = fb
} else {
pb, err := util.CreateJSONMergePatch(nil, input.Raw, with.Raw)
if err != nil {
return err
}
fb, err := util.ApplyJSONMergePatch(input.Raw, pb)
if err != nil {
return err
}
input.Raw = fb
}
if pm.Annotations != nil {
accessor, err := input.Accessor()
if err != nil {
return err
}
accessor.SetAnnotations(pm.Annotations)
input.Raw = accessor.Bytes()
}
return nil
}
+220
View File
@@ -0,0 +1,220 @@
package runtime
import (
"encoding/json"
"io/ioutil"
"os"
"reflect"
"testing"
apiv2 "github.com/RafaySystems/rcloud-base/proto/types/controller"
appsv1 "k8s.io/api/apps/v1"
"sigs.k8s.io/yaml"
)
func loadStepObject(path string) (*apiv2.StepObject, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
yb, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
jb, err := yaml.YAMLToJSON(yb)
if err != nil {
return nil, err
}
var so apiv2.StepObject
err = json.Unmarshal(jb, &so)
if err != nil {
return nil, err
}
return &so, nil
}
func TestConfigMap(t *testing.T) {
so, err := loadStepObject("./testdata/configmap.yaml")
if err != nil {
t.Error(err)
return
}
accessor, err := so.Accessor()
if err != nil {
t.Error(err)
return
}
apiVersion, err := accessor.APIVersion()
if err != nil {
t.Error(err)
return
}
kind, err := accessor.Kind()
if err != nil {
t.Error(err)
return
}
name, err := accessor.Name()
if err != nil {
t.Error(err)
return
}
if apiVersion != "v1" && kind != "ConfigMap" {
t.Error("expected ", "v1/ConfigMap")
return
}
if name != "example-config" {
t.Error("expected name example-config, got", so.Name)
}
_, _, err = ToObject(so)
if err != nil {
t.Error(err)
return
}
}
func TestUnstructured(t *testing.T) {
so, err := loadStepObject("./testdata/unstructured1.yaml")
if err != nil {
t.Error(err)
return
}
_, _, err = ToObject(so)
if err != nil {
t.Error(err)
return
}
}
func TestStructuredPatch(t *testing.T) {
so, err := loadStepObject("./testdata/deployment1.yaml")
if err != nil {
t.Error(err)
return
}
with, err := loadStepObject("./testdata/deployment2.yaml")
if err != nil {
t.Error(err)
return
}
err = Patch(so, with, AddAnnotations(map[string]string{"test": "test2"}))
if err != nil {
t.Error(err)
return
}
t.Log(string(so.Raw))
ro, _, err := ToObject(so)
if err != nil {
t.Error(err)
return
}
if d, ok := ro.(*appsv1.Deployment); ok {
if *d.Spec.Replicas != 2 {
t.Error("expected 2 replicas")
return
}
if !reflect.DeepEqual(d.Annotations, map[string]string{"test": "test2"}) {
t.Error("expected test: test2 got ", d.Annotations)
return
}
containers := d.Spec.Template.Spec.Containers
if len(containers) != 2 {
t.Error("expected 2 containers")
return
}
if containers[0].Name != "side-car" {
t.Error("expected side car container")
return
}
if containers[1].Name != "nginx" {
t.Error("expected nginx container")
return
}
if containers[1].Image != "nginx:1.7.10" {
t.Error("expected nginx image nginx:1.7.10")
return
}
} else {
t.Error("exptected appsv1.Deployment got, ", reflect.TypeOf(ro))
}
}
func TestUnstructuredPatch(t *testing.T) {
so, err := loadStepObject("./testdata/unstructured1.yaml")
if err != nil {
t.Error(err)
return
}
with, err := loadStepObject("./testdata/unstructured2.yaml")
if err != nil {
t.Error(err)
return
}
err = Patch(so, with)
if err != nil {
t.Error(err)
return
}
}
func TestPartialPath(t *testing.T) {
// so, err := loadStepObject("./testdata/etcd.yaml")
// if err != nil {
// t.Error(err)
// return
// }
// with, err := loadStepObject("./testdata/etcd-patch.yaml")
// if err != nil {
// t.Error(err)
// return
// }
// err = Patch(so, with, AddAnnotations(map[string]string{"test": "test"}))
// if err != nil {
// t.Error(err)
// return
// }
//t.Log(string(so.Raw))
so1, err := loadStepObject("./testdata/statefulset.yaml")
if err != nil {
t.Error(err)
return
}
with1, err := loadStepObject("./testdata/statefulset-patch.yaml")
if err != nil {
t.Error(err)
return
}
err = Patch(so1, with1, AddAnnotations(map[string]string{"test": "test"}))
if err != nil {
t.Error(err)
return
}
t.Log(string(so1.Raw))
}
+12
View File
@@ -0,0 +1,12 @@
kind: ConfigMap
apiVersion: v1
metadata:
name: example-config
namespace: default
data:
example.property.1: helloss
example.property.2: world
example.property.file: |-
property.1=value-1
property.2=value-2
property.3=value-3
+17
View File
@@ -0,0 +1,17 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
View File
+21
View File
@@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
+25
View File
@@ -0,0 +1,25 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
annotations:
test: test1
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: side-car
image: sidecar:latest
- name: nginx
image: nginx:1.7.10
ports:
- containerPort: 80
+10
View File
@@ -0,0 +1,10 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: etcd0
spec:
template:
spec:
containers:
- name: etcd0
image: quay.io/coreos/etcd:v3.4
+64
View File
@@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: etcd0
etcd_node: etcd0
name: etcd0
spec:
replicas: 1
selector:
matchLabels:
app: etcd0
etcd_node: etcd0
template:
metadata:
labels:
app: etcd0
etcd_node: etcd0
spec:
containers:
- command:
- /usr/local/bin/etcd
- --name
- etcd0
- --initial-advertise-peer-urls
- http://etcd0:2380
- --listen-peer-urls
- http://0.0.0.0:2380
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd0:2379
- --initial-cluster
- etcd0=http://etcd0:2380
- --initial-cluster-state
- new
env:
- name: ETCDCTL_API
value: "3"
image: quay.io/coreos/etcd:v3.3
name: etcd0
ports:
- containerPort: 2379
name: client
protocol: TCP
- containerPort: 2380
name: server
protocol: TCP
resources:
limits:
cpu: 100m
memory: 256Mi
requests:
cpu: 100m
memory: 256Mi
volumeMounts:
- mountPath: /etcd0.etcd
name: etcd-storage
hostname: etcd0
priorityClassName: rafay-cluster-critical
volumes:
- name: etcd-storage
persistentVolumeClaim:
claimName: etcd-pv-claim
View File
+8
View File
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
+15
View File
@@ -0,0 +1,15 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rafay-connector
spec:
template:
spec:
containers:
- name: connector
image: rafaysystems/rafay-connector:latest-1
imagePullSecrets:
- name: rcloud-registry-creds
command:
- test1
- ""
+29
View File
@@ -0,0 +1,29 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rafay-connector
namespace: rafay-system
spec:
selector:
matchLabels:
app: rafay-connector
serviceName: "connector"
replicas: 1
template:
metadata:
labels:
app: rafay-connector
spec:
priorityClassName: rafay-cluster-critical
terminationGracePeriodSeconds: 10
containers:
- name: connector
image: rafaysystems/rafay-connector:latest
imagePullPolicy: Always
volumeMounts:
- name: connector-config
mountPath: /etc/config
volumes:
- name: connector-config
configMap:
name: connector-config
+7
View File
@@ -0,0 +1,7 @@
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
+7
View File
@@ -0,0 +1,7 @@
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image2