mirror of
https://github.com/paralus/paralus.git
synced 2026-08-24 15:47:19 +00:00
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:
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysecret
|
||||
type: Opaque
|
||||
data:
|
||||
username: YWRtaW4=
|
||||
password: MWYyZDFlMmU2N2Rm
|
||||
@@ -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
|
||||
- ""
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user