From 645c6a331cd00043fcf9f567f5f261a9db070918 Mon Sep 17 00:00:00 2001 From: Eric Multanen Date: Wed, 12 Aug 2020 15:33:12 -0700 Subject: Enhance the status query API This patch enhances the status query API. - The ResourceBundleState CRD is modified to just use the k8s Pod structure instead of a customized struct. - Status queries can either present results showing the rsync status of the composite app and resources or from information received from the cluster via the ResourceBundleState CR - Query parameters are provided to the API call to customize the query and response - Support for querying status of cluster network intents is added Issue-ID: MULTICLOUD-1042 Signed-off-by: Eric Multanen Change-Id: Icca4cdd901e2f2b446414fade256fc24d87594cd --- src/ncm/api/api.go | 3 + src/ncm/api/schedulerhandler.go | 111 ++++++++++++++++++++++++++++++++++++- src/ncm/go.mod | 13 +++-- src/ncm/pkg/scheduler/scheduler.go | 35 ++++++++++++ 4 files changed, 155 insertions(+), 7 deletions(-) (limited to 'src/ncm') diff --git a/src/ncm/api/api.go b/src/ncm/api/api.go index 6dd958a1..45551e6c 100644 --- a/src/ncm/api/api.go +++ b/src/ncm/api/api.go @@ -90,6 +90,9 @@ func NewRouter(testClient interface{}) *mux.Router { } router.HandleFunc("/cluster-providers/{cluster-provider}/clusters/{cluster}/apply", schedulerHandler.applySchedulerHandler).Methods("POST") router.HandleFunc("/cluster-providers/{cluster-provider}/clusters/{cluster}/terminate", schedulerHandler.terminateSchedulerHandler).Methods("POST") + router.HandleFunc("/cluster-providers/{cluster-provider}/clusters/{cluster}/status", schedulerHandler.statusSchedulerHandler).Methods("GET") + router.HandleFunc("/cluster-providers/{cluster-provider}/clusters/{cluster}/status", + schedulerHandler.statusSchedulerHandler).Queries("instance", "{instance}", "type", "{type}", "output", "{output}", "app", "{app}", "cluster", "{cluster}", "resource", "{resource}") return router } diff --git a/src/ncm/api/schedulerhandler.go b/src/ncm/api/schedulerhandler.go index d07d132d..9d8b4eff 100644 --- a/src/ncm/api/schedulerhandler.go +++ b/src/ncm/api/schedulerhandler.go @@ -17,9 +17,13 @@ package api import ( + "encoding/json" "net/http" + "net/url" + "strings" "github.com/onap/multicloud-k8s/src/ncm/pkg/scheduler" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/validation" "github.com/gorilla/mux" ) @@ -47,7 +51,7 @@ func (h schedulerHandler) applySchedulerHandler(w http.ResponseWriter, r *http.R w.WriteHeader(http.StatusNoContent) } -// terminateSchedulerHandler handles requests to apply network intents for a cluster +// terminateSchedulerHandler handles requests to terminate network intents for a cluster func (h schedulerHandler) terminateSchedulerHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) provider := vars["cluster-provider"] @@ -61,3 +65,108 @@ func (h schedulerHandler) terminateSchedulerHandler(w http.ResponseWriter, r *ht w.WriteHeader(http.StatusNoContent) } + +// statusSchedulerHandler handles requests to query status of network intents for a cluster +func (h schedulerHandler) statusSchedulerHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + provider := vars["cluster-provider"] + cluster := vars["cluster"] + + qParams, err := url.ParseQuery(r.URL.RawQuery) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var queryInstance string + if i, found := qParams["instance"]; found { + queryInstance = i[0] + } else { + queryInstance = "" // default type + } + + var queryType string + if t, found := qParams["type"]; found { + queryType = t[0] + if queryType != "cluster" && queryType != "rsync" { + http.Error(w, "Invalid query type", http.StatusBadRequest) + return + } + } else { + queryType = "rsync" // default type + } + + var queryOutput string + if o, found := qParams["output"]; found { + queryOutput = o[0] + if queryOutput != "summary" && queryOutput != "all" && queryOutput != "detail" { + http.Error(w, "Invalid query output", http.StatusBadRequest) + return + } + } else { + queryOutput = "all" // default output format + } + + var queryApps []string + if a, found := qParams["app"]; found { + queryApps = a + for _, app := range queryApps { + errs := validation.IsValidName(app) + if len(errs) > 0 { + http.Error(w, "Invalid app query", http.StatusBadRequest) + return + } + } + } else { + queryApps = make([]string, 0) + } + + var queryClusters []string + if c, found := qParams["cluster"]; found { + queryClusters = c + for _, cl := range queryClusters { + parts := strings.Split(cl, "+") + if len(parts) != 2 { + http.Error(w, "Invalid cluster query", http.StatusBadRequest) + return + } + for _, p := range parts { + errs := validation.IsValidName(p) + if len(errs) > 0 { + http.Error(w, "Invalid cluster query", http.StatusBadRequest) + return + } + } + } + } else { + queryClusters = make([]string, 0) + } + + var queryResources []string + if r, found := qParams["resource"]; found { + queryResources = r + for _, res := range queryResources { + errs := validation.IsValidName(res) + if len(errs) > 0 { + http.Error(w, "Invalid resources query", http.StatusBadRequest) + return + } + } + } else { + queryResources = make([]string, 0) + } + + status, iErr := h.client.NetworkIntentsStatus(provider, cluster, queryInstance, queryType, queryOutput, queryApps, queryClusters, queryResources) + if iErr != nil { + http.Error(w, iErr.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + iErr = json.NewEncoder(w).Encode(status) + if iErr != nil { + http.Error(w, iErr.Error(), http.StatusInternalServerError) + return + } +} diff --git a/src/ncm/go.mod b/src/ncm/go.mod index d3d2924a..84ecc4ec 100644 --- a/src/ncm/go.mod +++ b/src/ncm/go.mod @@ -6,19 +6,20 @@ require ( github.com/gorilla/mux v1.7.3 github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20200127152046-0ee521d56061 github.com/onap/multicloud-k8s/src/clm v0.0.0-00010101000000-000000000000 - github.com/onap/multicloud-k8s/src/orchestrator v0.0.0-20200601021239-7959bd4c6fd4 - github.com/pkg/errors v0.8.1 - google.golang.org/grpc v1.27.1 + github.com/onap/multicloud-k8s/src/orchestrator v0.0.0-20200721211210-783ed87fb39a + github.com/pkg/errors v0.9.1 + google.golang.org/grpc v1.28.0 gopkg.in/yaml.v2 v2.2.8 - k8s.io/api v0.0.0-20190831074750-7364b6bdad65 - k8s.io/apimachinery v0.0.0-20190831074630-461753078381 - k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible + k8s.io/api v0.18.2 + k8s.io/apimachinery v0.18.2 + k8s.io/client-go v12.0.0+incompatible k8s.io/kubernetes v1.14.1 k8s.io/utils v0.0.0-20200520001619-278ece378a50 // indirect ) replace ( github.com/onap/multicloud-k8s/src/clm => ../clm + github.com/onap/multicloud-k8s/src/monitor => ../monitor github.com/onap/multicloud-k8s/src/orchestrator => ../orchestrator github.com/onap/multicloud-k8s/src/rsync => ../rsync k8s.io/api => k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b diff --git a/src/ncm/pkg/scheduler/scheduler.go b/src/ncm/pkg/scheduler/scheduler.go index f2135974..516c0525 100644 --- a/src/ncm/pkg/scheduler/scheduler.go +++ b/src/ncm/pkg/scheduler/scheduler.go @@ -31,6 +31,7 @@ import ( log "github.com/onap/multicloud-k8s/src/orchestrator/pkg/infra/logutils" "github.com/onap/multicloud-k8s/src/orchestrator/pkg/module/controller" "github.com/onap/multicloud-k8s/src/orchestrator/pkg/state" + "github.com/onap/multicloud-k8s/src/orchestrator/pkg/status" pkgerrors "github.com/pkg/errors" ) @@ -41,6 +42,7 @@ const rsyncName = "rsync" // ClusterManager is an interface exposes the Cluster functionality type SchedulerManager interface { ApplyNetworkIntents(clusterProvider, cluster string) error + NetworkIntentsStatus(clusterProvider, cluster, qInstance, qType, qOutput string, qApps, qClusters, qResources []string) (ClusterStatus, error) TerminateNetworkIntents(clusterProvider, cluster string) error } @@ -63,6 +65,11 @@ func NewSchedulerClient() *SchedulerClient { } } +// ClusterStatus holds the status data prepared for cluster network intent status queries +type ClusterStatus struct { + status.StatusResult `json:",inline"` +} + func deleteAppContext(ac appcontext.AppContext) { err := ac.DeleteCompositeApp() if err != nil { @@ -303,3 +310,31 @@ func (v *SchedulerClient) TerminateNetworkIntents(clusterProvider, cluster strin return nil } + +/* +NetworkIntentsStatus takes in cluster provider, cluster and query parameters. +This method is responsible obtaining the status of +the cluster network intents, which is made available in the appcontext +*/ +func (c SchedulerClient) NetworkIntentsStatus(clusterProvider, cluster, qInstance, qType, qOutput string, qApps, qClusters, qResources []string) (ClusterStatus, error) { + + s, err := clusterPkg.NewClusterClient().GetClusterState(clusterProvider, cluster) + if err != nil { + return ClusterStatus{}, pkgerrors.Wrap(err, "cluster state not found") + } + + // Prepare the apps list (just one hardcoded value) + allApps := make([]string, 0) + allApps = append(allApps, nettypes.CONTEXT_CLUSTER_APP) + + statusResponse, err := status.PrepareStatusResult(s, allApps, qInstance, qType, qOutput, qApps, qClusters, qResources) + if err != nil { + return ClusterStatus{}, err + } + statusResponse.Name = clusterProvider + "+" + cluster + clStatus := ClusterStatus{ + StatusResult: statusResponse, + } + + return clStatus, nil +} -- cgit 1.2.3-korg