---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.2.5
+ creationTimestamp: null
+ name: cnfstatuses.batch.sdewan.akraino.org
+spec:
+ group: batch.sdewan.akraino.org
+ names:
+ kind: CNFStatus
+ listKind: CNFStatusList
+ plural: cnfstatuses
+ singular: cnfstatus
+ scope: Namespaced
+ subresources:
+ status: {}
+ validation:
+ openAPIV3Schema:
+ description: CNFStatus is the Schema for the cnfstatuses API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: CNFStatusSpec defines the desired state of CNFStatus
+ type: object
+ status:
+ description: CNFStatusStatus defines the observed state of CNFStatus
+ properties:
+ appliedGeneration:
+ description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file'
+ format: int64
+ type: integer
+ appliedTime:
+ format: date-time
+ type: string
+ information:
+ items:
+ description: CNFStatusInformation defines the runtime information of a CMF
+ properties:
+ ip:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ node:
+ type: string
+ purpose:
+ type: string
+ status:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ type: object
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
- group: batch
kind: SdewanApplication
version: v1alpha1
+- group: batch
+ kind: CNFStatus
+ version: v1alpha1
version: "2"
--- /dev/null
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
+
+// CNFStatusSpec defines the desired state of CNFStatus
+type CNFStatusSpec struct {
+ // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+ // Important: Run "make" to regenerate code after modifying this file
+}
+
+// CNFStatusInformation defines the runtime information of a CMF
+type CNFStatusInformation struct {
+ Name string `json:"name"`
+ NameSpace string `json:"namespace,omitempty"`
+ Node string `json:"node,omitempty"`
+ Purpose string `json:"purpose,omitempty"`
+ IP string `json:"ip,omitempty"`
+ Status string `json:"status,omitempty"`
+}
+
+// CNFStatusStatus defines the observed state of CNFStatus
+type CNFStatusStatus struct {
+ // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+ // Important: Run "make" to regenerate code after modifying this file
+ // +optional
+ AppliedGeneration int64 `json:"appliedGeneration,omitempty"`
+ // +optional
+ AppliedTime *metav1.Time `json:"appliedTime,omitempty"`
+ // +optional
+ Information []CNFStatusInformation `json:"information,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// CNFStatus is the Schema for the cnfstatuses API
+type CNFStatus struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec CNFStatusSpec `json:"spec,omitempty"`
+ Status CNFStatusStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// CNFStatusList contains a list of CNFStatus
+type CNFStatusList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []CNFStatus `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&CNFStatus{}, &CNFStatusList{})
+}
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CNFStatus) DeepCopyInto(out *CNFStatus) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+ in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNFStatus.
+func (in *CNFStatus) DeepCopy() *CNFStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(CNFStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *CNFStatus) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CNFStatusInformation) DeepCopyInto(out *CNFStatusInformation) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNFStatusInformation.
+func (in *CNFStatusInformation) DeepCopy() *CNFStatusInformation {
+ if in == nil {
+ return nil
+ }
+ out := new(CNFStatusInformation)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CNFStatusList) DeepCopyInto(out *CNFStatusList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]CNFStatus, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNFStatusList.
+func (in *CNFStatusList) DeepCopy() *CNFStatusList {
+ if in == nil {
+ return nil
+ }
+ out := new(CNFStatusList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *CNFStatusList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CNFStatusSpec) DeepCopyInto(out *CNFStatusSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNFStatusSpec.
+func (in *CNFStatusSpec) DeepCopy() *CNFStatusSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(CNFStatusSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CNFStatusStatus) DeepCopyInto(out *CNFStatusStatus) {
+ *out = *in
+ if in.AppliedTime != nil {
+ in, out := &in.AppliedTime, &out.AppliedTime
+ *out = (*in).DeepCopy()
+ }
+ if in.Information != nil {
+ in, out := &in.Information, &out.Information
+ *out = make([]CNFStatusInformation, len(*in))
+ copy(*out, *in)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNFStatusStatus.
+func (in *CNFStatusStatus) DeepCopy() *CNFStatusStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(CNFStatusStatus)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Connection) DeepCopyInto(out *Connection) {
*out = *in
--- /dev/null
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.2.5
+ creationTimestamp: null
+ name: cnfstatuses.batch.sdewan.akraino.org
+spec:
+ group: batch.sdewan.akraino.org
+ names:
+ kind: CNFStatus
+ listKind: CNFStatusList
+ plural: cnfstatuses
+ singular: cnfstatus
+ scope: Namespaced
+ subresources:
+ status: {}
+ validation:
+ openAPIV3Schema:
+ description: CNFStatus is the Schema for the cnfstatuses API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: CNFStatusSpec defines the desired state of CNFStatus
+ type: object
+ status:
+ description: CNFStatusStatus defines the observed state of CNFStatus
+ properties:
+ appliedGeneration:
+ description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
+ of cluster Important: Run "make" to regenerate code after modifying
+ this file'
+ format: int64
+ type: integer
+ appliedTime:
+ format: date-time
+ type: string
+ information:
+ items:
+ description: CNFStatusInformation defines the runtime information
+ of a CMF
+ properties:
+ ip:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ node:
+ type: string
+ purpose:
+ type: string
+ status:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ type: object
+ type: object
+ version: v1alpha1
+ versions:
+ - name: v1alpha1
+ served: true
+ storage: true
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
- bases/batch.sdewan.akraino.org_ipsecsites.yaml
- bases/batch.sdewan.akraino.org_cnfservices.yaml
- bases/batch.sdewan.akraino.org_sdewanapplications.yaml
+- bases/batch.sdewan.akraino.org_cnfstatuses.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
#- patches/webhook_in_ipsecsites.yaml
#- patches/webhook_in_cnfservices.yaml
#- patches/webhook_in_sdewanapplications.yaml
+#- patches/webhook_in_cnfstatuses.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
#- patches/cainjection_in_ipsecsites.yaml
#- patches/cainjection_in_cnfservices.yaml
#- patches/cainjection_in_sdewanapplications.yaml
+#- patches/cainjection_in_cnfstatuses.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
--- /dev/null
+# The following patch adds a directive for certmanager to inject CA into the CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+ name: cnfstatuses.batch.sdewan.akraino.org
--- /dev/null
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+ name: cnfstatuses.batch.sdewan.akraino.org
+spec:
+ conversion:
+ strategy: Webhook
+ webhookClientConfig:
+ # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
+ # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
+ caBundle: Cg==
+ service:
+ namespace: system
+ name: webhook-service
+ path: /convert
--- /dev/null
+# permissions for end users to edit cnfstatuses.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: cnfstatus-editor-role
+rules:
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - cnfstatuses
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - cnfstatuses/status
+ verbs:
+ - get
--- /dev/null
+# permissions for end users to view cnfstatuses.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: cnfstatus-viewer-role
+rules:
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - cnfstatuses
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch.sdewan.akraino.org
+ resources:
+ - cnfstatuses/status
+ verbs:
+ - get
--- /dev/null
+apiVersion: batch.sdewan.akraino.org/v1alpha1
+kind: CNFStatus
+metadata:
+ name: cnfstatus-sample
+spec:
+ # Add fields here
--- /dev/null
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controllers
+
+import (
+ "context"
+ "encoding/json"
+ "github.com/go-logr/logr"
+ "time"
+
+ corev1 "k8s.io/api/core/v1"
+ errs "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/util/wait"
+ batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
+ "sdewan.akraino.org/sdewan/openwrt"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sync"
+)
+
+var cnfCRNameSpace = "sdewan-system"
+var cnfCRName = "cnf-status"
+var inQueryStatus = false
+
+// SdewanCNFStatusController: query CNF status periodically
+type SdewanCNFStatusController struct {
+ client.Client
+ Log logr.Logger
+ CheckInterval time.Duration
+ mux sync.Mutex
+}
+
+func (r *SdewanCNFStatusController) SetupWithManager() error {
+ go wait.Until(r.SafeQuery, r.CheckInterval, wait.NeverStop)
+
+ return nil
+}
+
+func (r *SdewanCNFStatusController) GetInstance(ctx context.Context) (*batchv1alpha1.CNFStatus, error) {
+ instance := &batchv1alpha1.CNFStatus{}
+ err := r.Get(ctx, client.ObjectKey{
+ Namespace: cnfCRNameSpace,
+ Name: cnfCRName,
+ }, instance)
+
+ if errs.IsNotFound(err) {
+ // No instance, create the instance
+ r.Log.Info("Create New CNFStatus CR")
+ instance = &batchv1alpha1.CNFStatus{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: cnfCRName,
+ Namespace: cnfCRNameSpace,
+ },
+ Spec: batchv1alpha1.CNFStatusSpec{},
+ }
+
+ err = r.Create(ctx, instance)
+ if err != nil {
+ return &batchv1alpha1.CNFStatus{}, err
+ }
+ }
+
+ return instance, nil
+}
+
+// Query CNFStatus information
+func (r *SdewanCNFStatusController) SafeQuery() {
+ doQuery := true
+ r.mux.Lock()
+ if inQueryStatus == false {
+ inQueryStatus = true
+ } else {
+ doQuery = false
+ }
+ r.mux.Unlock()
+
+ if doQuery {
+ r.query()
+
+ r.mux.Lock()
+ inQueryStatus = false
+ r.mux.Unlock()
+ }
+}
+
+func (r *SdewanCNFStatusController) query() {
+ ctx := context.Background()
+
+ instance, err := r.GetInstance(ctx)
+ if err != nil {
+ r.Log.Info(err.Error())
+ } else {
+ r.Log.Info("Query CNFStatus CR Instance: " + instance.ObjectMeta.Name)
+ }
+
+ // Set Status infomration
+ instance.Status.AppliedGeneration = instance.Generation
+ instance.Status.AppliedTime = &metav1.Time{Time: time.Now()}
+ instance.Status.Information = []batchv1alpha1.CNFStatusInformation{}
+
+ cnfPodList := &corev1.PodList{}
+ err = r.List(ctx, cnfPodList, client.HasLabels{"sdewanPurpose"})
+ if err != nil {
+ r.Log.Info(err.Error())
+ return
+ }
+
+ for _, cnfPod := range cnfPodList.Items {
+ info := &batchv1alpha1.CNFStatusInformation{}
+ info.Name = cnfPod.ObjectMeta.Name
+ info.NameSpace = cnfPod.ObjectMeta.Namespace
+ info.Node = cnfPod.Spec.NodeName
+ info.Purpose = cnfPod.ObjectMeta.Labels["sdewanPurpose"]
+ info.IP = cnfPod.Status.PodIP
+
+ // Get CNF Status
+ clientInfo := &openwrt.OpenwrtClientInfo{Ip: info.IP, User: "root", Password: ""}
+ openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+ status_client := openwrt.StatusClient{OpenwrtClient: openwrtClient}
+ cnf_status, err := status_client.GetStatus()
+ if err != nil {
+ info.Status = "Not Available"
+ } else {
+ p_data, _ := json.Marshal(cnf_status)
+ info.Status = string(p_data)
+ }
+ instance.Status.Information = append(instance.Status.Information, *info)
+ }
+
+ // Update the CNFStatus CR
+ err = r.Status().Update(ctx, instance)
+ if err != nil {
+ r.Log.Info(err.Error())
+ }
+}
"flag"
"fmt"
"os"
+ "time"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
func main() {
var metricsAddr string
var enableLeaderElection bool
+ var checkInterval int
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
+ flag.IntVar(&checkInterval, "check-interval", 30,
+ "The check interval for query of CNF Status (seconds)")
flag.Parse()
ctrl.SetLogger(zap.New(func(o *zap.Options) {
}
// +kubebuilder:scaffold:builder
+ setupLog.Info("start CNFStatusController to query CNF status periodicly")
+ if err = (&controllers.SdewanCNFStatusController{
+ Client: mgr.GetClient(),
+ Log: ctrl.Log.WithName("controllers").WithName("SdewanCNFStatus"),
+ CheckInterval: time.Duration(checkInterval) * time.Second,
+ }).SetupWithManager(); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "SdewanCNFStatus")
+ os.Exit(1)
+ }
+
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
--- /dev/null
+package openwrt
+
+import (
+ "encoding/json"
+)
+
+const (
+ statusBaseURL = "sdewan/v1/"
+)
+
+type StatusClient struct {
+ OpenwrtClient *openwrtClient
+}
+
+// Status Info
+type SdewanModuleStatus struct {
+ Name string `json:"name"`
+ Status interface{} `json:"status"`
+}
+
+func (o *SdewanModuleStatus) GetName() string {
+ return o.Name
+}
+
+// Status APIs
+// get status
+func (m *StatusClient) GetStatus() (*[]SdewanModuleStatus, error) {
+ response, err := m.OpenwrtClient.Get(statusBaseURL + "status")
+ if err != nil {
+ return nil, err
+ }
+
+ var sdewanStatus []SdewanModuleStatus
+ err = json.Unmarshal([]byte(response), &sdewanStatus)
+ if err != nil {
+ return nil, err
+ }
+
+ return &sdewanStatus, nil
+}
+
+func (m *StatusClient) GetModuleStatus(mname string) (*SdewanModuleStatus, error) {
+ response, err := m.OpenwrtClient.Get(statusBaseURL + "status/" + mname)
+ if err != nil {
+ return nil, err
+ }
+
+ var sdewanModuleStatus SdewanModuleStatus
+ err = json.Unmarshal([]byte(response), &sdewanModuleStatus)
+ if err != nil {
+ return nil, err
+ }
+
+ return &sdewanModuleStatus, nil
+}