From 828f7726fd283955fe57152eb86cac5bd6573c8f Mon Sep 17 00:00:00 2001 From: Zhengyi Lai Date: Wed, 1 Apr 2020 23:18:57 +0800 Subject: [PATCH 1/2] Add network topology APIs Signed-off-by: Zhengyi Lai --- pkg/apiserver/apiserver.go | 2 + pkg/constants/constants.go | 1 + pkg/kapis/network/group.go | 18 ++ pkg/kapis/network/v1alpha2/handler.go | 77 ++++++++ pkg/kapis/network/v1alpha2/register.go | 56 ++++++ pkg/kapis/network/v1alpha2/swagger-doc.go | 218 ++++++++++++++++++++++ tools/cmd/doc-gen/main.go | 2 + 7 files changed, 374 insertions(+) create mode 100644 pkg/kapis/network/group.go create mode 100644 pkg/kapis/network/v1alpha2/handler.go create mode 100644 pkg/kapis/network/v1alpha2/register.go create mode 100644 pkg/kapis/network/v1alpha2/swagger-doc.go diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 329ee286a..bd8654567 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -30,6 +30,7 @@ import ( iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" + networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" "kubesphere.io/kubesphere/pkg/kapis/oauth" openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" @@ -139,6 +140,7 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient)) urlruntime.Must(monitoringv1alpha3.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient)) urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient)) + urlruntime.Must(networkv1alpha2.AddToContainer(s.container)) urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes())) urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory)) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 2f45dd696..cbe048064 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -59,6 +59,7 @@ const ( OpenpitrixTag = "Openpitrix Resources" VerificationTag = "Verification" RegistryTag = "Docker Registry" + NetworkTopologyTag = "Network Topology" UserResourcesTag = "User Resources" DevOpsProjectTag = "DevOps Project" DevOpsProjectCredentialTag = "DevOps Project Credential" diff --git a/pkg/kapis/network/group.go b/pkg/kapis/network/group.go new file mode 100644 index 000000000..71d348680 --- /dev/null +++ b/pkg/kapis/network/group.go @@ -0,0 +1,18 @@ +/* +Copyright 2020 The KubeSphere authors. + +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 network contains network API versions +package network diff --git a/pkg/kapis/network/v1alpha2/handler.go b/pkg/kapis/network/v1alpha2/handler.go new file mode 100644 index 000000000..e096acdc8 --- /dev/null +++ b/pkg/kapis/network/v1alpha2/handler.go @@ -0,0 +1,77 @@ +package v1alpha2 + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + + restful "github.com/emicklei/go-restful" +) + +const ScopeQueryUrl = "http://weave-scope-app.weave.svc/api/topology/services" + +func getNamespaceTopology(request *restful.Request, response *restful.Response) { + var query = url.Values{ + "namespace": []string{request.PathParameter("namespace")}, + "timestamp": request.QueryParameters("timestamp"), + } + var u = fmt.Sprintf("%s?%s", ScopeQueryUrl, query.Encode()) + + resp, err := http.Get(u) + + if err != nil { + log.Printf("query scope faile with err %v", err) + _ = response.WriteError(http.StatusInternalServerError, err) + return + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + log.Printf("read response error : %v", err) + _ = response.WriteError(http.StatusInternalServerError, err) + return + } + + // need to set header for proper response + response.Header().Set("Content-Type", "application/json") + _, err = response.Write(body) + + if err != nil { + log.Printf("write response failed %v", err) + } +} + +func getNamespaceNodeTopology(request *restful.Request, response *restful.Response) { + var query = url.Values{ + "namespace": []string{request.PathParameter("namespace")}, + "timestamp": request.QueryParameters("timestamp"), + } + var u = fmt.Sprintf("%s/%s?%s", ScopeQueryUrl, request.PathParameter("node_id"), query.Encode()) + + resp, err := http.Get(u) + + if err != nil { + log.Printf("query scope faile with err %v", err) + _ = response.WriteError(http.StatusInternalServerError, err) + return + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + log.Printf("read response error : %v", err) + _ = response.WriteError(http.StatusInternalServerError, err) + return + } + + // need to set header for proper response + response.Header().Set("Content-Type", "application/json") + _, err = response.Write(body) + + if err != nil { + log.Printf("write response failed %v", err) + } +} diff --git a/pkg/kapis/network/v1alpha2/register.go b/pkg/kapis/network/v1alpha2/register.go new file mode 100644 index 000000000..ac1c296c8 --- /dev/null +++ b/pkg/kapis/network/v1alpha2/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2020 The KubeSphere authors. + +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 v1alpha2 + +import ( + "net/http" + + restful "github.com/emicklei/go-restful" + restfulspec "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/constants" +) + +const GroupName = "network.kubesphere.io" + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} + +func AddToContainer(c *restful.Container) error { + webservice := runtime.NewWebService(GroupVersion) + + webservice.Route(webservice.GET("/namespaces/{namespace}/topology"). + To(getNamespaceTopology). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). + Doc("Get the topology with specifying a namespace"). + Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). + Returns(http.StatusOK, "ok", TopologyResponse{}). + Writes(TopologyResponse{})).Produces(restful.MIME_JSON) + + webservice.Route(webservice.GET("/namespaces/{namespace}/topology/{node_id}"). + To(getNamespaceNodeTopology). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). + Doc("Get the topology with specifying a node id in the whole topology and specifying a namespace"). + Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). + Param(webservice.PathParameter("node_id", "id of the node in the whole topology").Required(true)). + Returns(http.StatusOK, "ok", NodeResponse{}). + Writes(NodeResponse{})).Produces(restful.MIME_JSON) + + c.Add(webservice) + + return nil +} diff --git a/pkg/kapis/network/v1alpha2/swagger-doc.go b/pkg/kapis/network/v1alpha2/swagger-doc.go new file mode 100644 index 000000000..d8cdd4c0a --- /dev/null +++ b/pkg/kapis/network/v1alpha2/swagger-doc.go @@ -0,0 +1,218 @@ +/* +Copyright 2020 The KubeSphere authors. + +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 v1alpha2 + +import ( + "time" +) + +///////////////////// +// SWAGGER RESPONSES +///////////////////// + +// NoContent: the response is empty +type NoContent struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// BadRequestError: the client request is incorrect +type BadRequestError struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// NotFoundError is the error message that is generated when server could not find +// what was requested +type NotFoundError struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// copy from github.com/weaveworks/scope v1.12.0 + +// MetadataRow is a row for the metadata table. +type MetadataRow struct { + ID string `json:"id"` + Label string `json:"label"` + Value string `json:"value"` + Priority float64 `json:"priority,omitempty"` + Datatype string `json:"dataType,omitempty"` + Truncate int `json:"truncate,omitempty"` +} + +// BasicNodeSummary is basic summary information about a Node, +// sufficient for rendering links to the node. +type BasicNodeSummary struct { + ID string `json:"id"` + Label string `json:"label"` + LabelMinor string `json:"labelMinor"` + Rank string `json:"rank"` + Shape string `json:"shape,omitempty"` + Tag string `json:"tag,omitempty"` + Stack bool `json:"stack,omitempty"` + Pseudo bool `json:"pseudo,omitempty"` +} + +// Parent is the information needed to build a link to the parent of a Node. +type Parent struct { + ID string `json:"id"` + Label string `json:"label"` + TopologyID string `json:"topologyId"` +} + +// Metric is a list of timeseries data with some metadata. Clients must use the +// Add method to add values. Metrics are immutable. +type Metric struct { + Samples []Sample `json:"samples,omitempty"` + Min float64 `json:"min"` + Max float64 `json:"max"` +} + +func (m Metric) first() time.Time { return m.Samples[0].Timestamp } +func (m Metric) last() time.Time { return m.Samples[len(m.Samples)-1].Timestamp } + +// Sample is a single datapoint of a metric. +type Sample struct { + Timestamp time.Time `json:"date"` + Value float64 `json:"value"` +} + +// MetricRow is a tuple of data used to render a metric as a sparkline and +// accoutrements. +type MetricRow struct { + ID string + Label string + Format string + Group string + Value float64 + ValueEmpty bool + Priority float64 + URL string + Metric *Metric +} + +// NodeSummaryGroup is a topology-typed group of children for a Node. +type NodeSummaryGroup struct { + ID string `json:"id"` + Label string `json:"label"` + Nodes []NodeSummary `json:"nodes"` + TopologyID string `json:"topologyId"` + Columns []Column `json:"columns"` +} + +// Connection is a row in the connections table. +type Connection struct { + ID string `json:"id"` // ID of this element in the UI. Must be unique for a given ConnectionsSummary. + NodeID string `json:"nodeId"` // ID of a node in the topology. Optional, must be set if linkable is true. + Label string `json:"label"` + LabelMinor string `json:"labelMinor,omitempty"` + Metadata []MetadataRow `json:"metadata,omitempty"` +} + +// ConnectionsSummary is the table of connection to/form a node +type ConnectionsSummary struct { + ID string `json:"id"` + TopologyID string `json:"topologyId"` + Label string `json:"label"` + Columns []Column `json:"columns"` + Connections []Connection `json:"connections"` +} + +// Column is the type for multi-column tables in the UI. +type Column struct { + ID string `json:"id"` + Label string `json:"label"` + DataType string `json:"dataType"` +} + +// Row is the type that holds the table data for the UI. Entries map from column ID to cell value. +type Row struct { + ID string `json:"id"` + Entries map[string]string `json:"entries"` +} + +// Table is the type for a table in the UI. +type Table struct { + ID string `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + Columns []Column `json:"columns"` + Rows []Row `json:"rows"` + TruncationCount int `json:"truncationCount,omitempty"` +} + +// StringSet is a sorted set of unique strings. Clients must use the Add +// method to add strings. +type StringSet []string + +// IDList is a list of string IDs, which are always sorted and unique. +type IDList StringSet + +// NodeSummary is summary information about a Node. +type NodeSummary struct { + BasicNodeSummary + Metadata []MetadataRow `json:"metadata,omitempty"` + Parents []Parent `json:"parents,omitempty"` + Metrics []MetricRow `json:"metrics,omitempty"` + Tables []Table `json:"tables,omitempty"` + Adjacency IDList `json:"adjacency,omitempty"` +} + +type NodeSummaries map[string]NodeSummary + +type APITopology struct { + Nodes NodeSummaries `json:"nodes"` +} + +// A Control basically describes an RPC +type Control struct { + ID string `json:"id"` + Human string `json:"human"` + Icon string `json:"icon"` // from https://fortawesome.github.io/Font-Awesome/cheatsheet/ please + Confirmation string `json:"confirmation,omitempty"` + Rank int `json:"rank"` +} + +// ControlInstance contains a control description, and all the info +// needed to execute it. +type ControlInstance struct { + ProbeID string + NodeID string + Control Control +} + +// Node is the data type that's yielded to the JavaScript layer when +// we want deep information about an individual node. +type Node struct { + NodeSummary + Controls []ControlInstance `json:"controls"` + Children []NodeSummaryGroup `json:"children,omitempty"` + Connections []ConnectionsSummary `json:"connections,omitempty"` +} + +type APINode struct { + Node Node `json:"node"` +} + +type TopologyResponse struct { + APITopology +} + +type NodeResponse struct { + APINode +} diff --git a/tools/cmd/doc-gen/main.go b/tools/cmd/doc-gen/main.go index 6035b014c..9eb32821d 100644 --- a/tools/cmd/doc-gen/main.go +++ b/tools/cmd/doc-gen/main.go @@ -40,6 +40,7 @@ import ( iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" + networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" @@ -122,6 +123,7 @@ func generateSwaggerJson() []byte { urlruntime.Must(tenantv1alpha2.AddToContainer(container, clientsets, informerFactory)) urlruntime.Must(terminalv1alpha2.AddToContainer(container, clientsets.Kubernetes(), nil)) urlruntime.Must(metricsv1alpha2.AddToContainer(container)) + urlruntime.Must(networkv1alpha2.AddToContainer(container)) config := restfulspec.Config{ WebServices: container.RegisteredWebServices(), From 8ad17a464896bbb747d52bc660e982de4ab49aad Mon Sep 17 00:00:00 2001 From: Zhengyi Lai Date: Fri, 17 Apr 2020 23:27:57 +0800 Subject: [PATCH 2/2] Add network configuration to apiserver/config --- cmd/ks-apiserver/app/options/options.go | 3 ++ cmd/ks-apiserver/app/options/validation.go | 1 + pkg/apiserver/apiserver.go | 2 +- pkg/apiserver/config/config.go | 7 ++++ pkg/apiserver/config/config_test.go | 4 ++ pkg/kapis/network/v1alpha2/handler.go | 44 +++++++++++++--------- pkg/kapis/network/v1alpha2/register.go | 13 ++++--- pkg/simple/client/network/options.go | 32 ++++++++++++++++ tools/cmd/doc-gen/main.go | 2 +- 9 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 pkg/simple/client/network/options.go diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index fa7e608a4..855558ad3 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -17,6 +17,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/ldap" esclient "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/network" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/s3" fakes3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake" @@ -43,6 +44,7 @@ func NewServerRunOptions() *ServerRunOptions { DevopsOptions: jenkins.NewDevopsOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), + NetworkOptions: network.NewNetworkOptions(), MonitoringOptions: prometheus.NewPrometheusOptions(), S3Options: s3.NewS3Options(), OpenPitrixOptions: openpitrix.NewOptions(), @@ -68,6 +70,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { s.RedisOptions.AddFlags(fss.FlagSet("redis"), s.RedisOptions) s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options) s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions) + s.NetworkOptions.AddFlags(fss.FlagSet("network"), s.NetworkOptions) s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions) s.MonitoringOptions.AddFlags(fss.FlagSet("monitoring"), s.MonitoringOptions) s.LoggingOptions.AddFlags(fss.FlagSet("logging"), s.LoggingOptions) diff --git a/cmd/ks-apiserver/app/options/validation.go b/cmd/ks-apiserver/app/options/validation.go index ad85f5913..17b8c9fb5 100644 --- a/cmd/ks-apiserver/app/options/validation.go +++ b/cmd/ks-apiserver/app/options/validation.go @@ -13,6 +13,7 @@ func (s *ServerRunOptions) Validate() []error { errors = append(errors, s.SonarQubeOptions.Validate()...) errors = append(errors, s.S3Options.Validate()...) errors = append(errors, s.OpenPitrixOptions.Validate()...) + errors = append(errors, s.NetworkOptions.Validate()...) errors = append(errors, s.LoggingOptions.Validate()...) return errors diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index bd8654567..ea44d915b 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -140,7 +140,7 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient)) urlruntime.Must(monitoringv1alpha3.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient)) urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient)) - urlruntime.Must(networkv1alpha2.AddToContainer(s.container)) + urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost)) urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes())) urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.InformerFactory)) diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go index 75b8086f4..a9c4bd48a 100644 --- a/pkg/apiserver/config/config.go +++ b/pkg/apiserver/config/config.go @@ -11,6 +11,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/ldap" "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/network" "kubesphere.io/kubesphere/pkg/simple/client/notification" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/s3" @@ -61,6 +62,7 @@ type Config struct { SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` + NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` @@ -81,6 +83,7 @@ func New() *Config { SonarQubeOptions: sonarqube.NewSonarQubeOptions(), KubernetesOptions: k8s.NewKubernetesOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), + NetworkOptions: network.NewNetworkOptions(), LdapOptions: ldap.NewOptions(), RedisOptions: cache.NewRedisOptions(), S3Options: s3.NewS3Options(), @@ -175,6 +178,10 @@ func (conf *Config) stripEmptyOptions() { conf.OpenPitrixOptions = nil } + if conf.NetworkOptions != nil && conf.NetworkOptions.WeaveScopeHost == "" { + conf.NetworkOptions = nil + } + if conf.ServiceMeshOptions != nil && conf.ServiceMeshOptions.IstioPilotHost == "" && conf.ServiceMeshOptions.ServicemeshPrometheusHost == "" && conf.ServiceMeshOptions.JaegerQueryHost == "" { diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 3357acbb3..82f94a944 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -14,6 +14,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/ldap" "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/network" "kubesphere.io/kubesphere/pkg/simple/client/notification" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/s3" @@ -79,6 +80,9 @@ func newTestConfig() (*Config, error) { CategoryManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9113", AttachmentManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9122", }, + NetworkOptions: &network.Options{ + WeaveScopeHost: "weave-scope-app.weave.svc", + }, MonitoringOptions: &prometheus.Options{ Endpoint: "http://prometheus.kubesphere-monitoring-system.svc", SecondaryEndpoint: "http://prometheus.kubesphere-monitoring-system.svc", diff --git a/pkg/kapis/network/v1alpha2/handler.go b/pkg/kapis/network/v1alpha2/handler.go index e096acdc8..b7c5a3cc9 100644 --- a/pkg/kapis/network/v1alpha2/handler.go +++ b/pkg/kapis/network/v1alpha2/handler.go @@ -3,35 +3,45 @@ package v1alpha2 import ( "fmt" "io/ioutil" - "log" "net/http" "net/url" - restful "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful" + "k8s.io/klog" + + "kubesphere.io/kubesphere/pkg/api" ) -const ScopeQueryUrl = "http://weave-scope-app.weave.svc/api/topology/services" +const ScopeQueryUrl = "http://%s/api/topology/services" -func getNamespaceTopology(request *restful.Request, response *restful.Response) { +type handler struct { + weaveScopeHost string +} + +func (h *handler) getScopeUrl() string { + return fmt.Sprintf(ScopeQueryUrl, h.weaveScopeHost) +} + +func (h *handler) getNamespaceTopology(request *restful.Request, response *restful.Response) { var query = url.Values{ "namespace": []string{request.PathParameter("namespace")}, "timestamp": request.QueryParameters("timestamp"), } - var u = fmt.Sprintf("%s?%s", ScopeQueryUrl, query.Encode()) + var u = fmt.Sprintf("%s?%s", h.getScopeUrl(), query.Encode()) resp, err := http.Get(u) if err != nil { - log.Printf("query scope faile with err %v", err) - _ = response.WriteError(http.StatusInternalServerError, err) + klog.Errorf("query scope faile with err %v", err) + api.HandleInternalError(response, nil, err) return } body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { - log.Printf("read response error : %v", err) - _ = response.WriteError(http.StatusInternalServerError, err) + klog.Errorf("read response error : %v", err) + api.HandleInternalError(response, nil, err) return } @@ -40,30 +50,30 @@ func getNamespaceTopology(request *restful.Request, response *restful.Response) _, err = response.Write(body) if err != nil { - log.Printf("write response failed %v", err) + klog.Errorf("write response failed %v", err) } } -func getNamespaceNodeTopology(request *restful.Request, response *restful.Response) { +func (h *handler) getNamespaceNodeTopology(request *restful.Request, response *restful.Response) { var query = url.Values{ "namespace": []string{request.PathParameter("namespace")}, "timestamp": request.QueryParameters("timestamp"), } - var u = fmt.Sprintf("%s/%s?%s", ScopeQueryUrl, request.PathParameter("node_id"), query.Encode()) + var u = fmt.Sprintf("%s/%s?%s", h.getScopeUrl(), request.PathParameter("node_id"), query.Encode()) resp, err := http.Get(u) if err != nil { - log.Printf("query scope faile with err %v", err) - _ = response.WriteError(http.StatusInternalServerError, err) + klog.Errorf("query scope faile with err %v", err) + api.HandleInternalError(response, nil, err) return } body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { - log.Printf("read response error : %v", err) - _ = response.WriteError(http.StatusInternalServerError, err) + klog.Errorf("read response error : %v", err) + api.HandleInternalError(response, nil, err) return } @@ -72,6 +82,6 @@ func getNamespaceNodeTopology(request *restful.Request, response *restful.Respon _, err = response.Write(body) if err != nil { - log.Printf("write response failed %v", err) + klog.Errorf("write response failed %v", err) } } diff --git a/pkg/kapis/network/v1alpha2/register.go b/pkg/kapis/network/v1alpha2/register.go index ac1c296c8..300480308 100644 --- a/pkg/kapis/network/v1alpha2/register.go +++ b/pkg/kapis/network/v1alpha2/register.go @@ -30,25 +30,28 @@ const GroupName = "network.kubesphere.io" var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -func AddToContainer(c *restful.Container) error { +func AddToContainer(c *restful.Container, weaveScopeHost string) error { webservice := runtime.NewWebService(GroupVersion) + h := handler{weaveScopeHost: weaveScopeHost} webservice.Route(webservice.GET("/namespaces/{namespace}/topology"). - To(getNamespaceTopology). + To(h.getNamespaceTopology). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). Doc("Get the topology with specifying a namespace"). Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). Returns(http.StatusOK, "ok", TopologyResponse{}). - Writes(TopologyResponse{})).Produces(restful.MIME_JSON) + Writes(TopologyResponse{})). + Produces(restful.MIME_JSON) webservice.Route(webservice.GET("/namespaces/{namespace}/topology/{node_id}"). - To(getNamespaceNodeTopology). + To(h.getNamespaceNodeTopology). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). Doc("Get the topology with specifying a node id in the whole topology and specifying a namespace"). Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). Param(webservice.PathParameter("node_id", "id of the node in the whole topology").Required(true)). Returns(http.StatusOK, "ok", NodeResponse{}). - Writes(NodeResponse{})).Produces(restful.MIME_JSON) + Writes(NodeResponse{})). + Produces(restful.MIME_JSON) c.Add(webservice) diff --git a/pkg/simple/client/network/options.go b/pkg/simple/client/network/options.go new file mode 100644 index 000000000..3bc42e1d6 --- /dev/null +++ b/pkg/simple/client/network/options.go @@ -0,0 +1,32 @@ +package network + +import "github.com/spf13/pflag" + +type Options struct { + + // weave scope service host + WeaveScopeHost string `json:"weaveScopeHost,omitempty" yaml:"weaveScopeHost"` +} + +// NewNetworkOptions returns a `zero` instance +func NewNetworkOptions() *Options { + return &Options{ + WeaveScopeHost: "weave-scope-app.weave.svc", + } +} + +func (s *Options) Validate() []error { + var errors []error + return errors +} + +func (s *Options) ApplyTo(options *Options) { + if s.WeaveScopeHost != "" { + options.WeaveScopeHost = s.WeaveScopeHost + } +} + +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.WeaveScopeHost, "weave-scope-host", c.WeaveScopeHost, ""+ + "weave scope service host") +} diff --git a/tools/cmd/doc-gen/main.go b/tools/cmd/doc-gen/main.go index 9eb32821d..448eb203e 100644 --- a/tools/cmd/doc-gen/main.go +++ b/tools/cmd/doc-gen/main.go @@ -123,7 +123,7 @@ func generateSwaggerJson() []byte { urlruntime.Must(tenantv1alpha2.AddToContainer(container, clientsets, informerFactory)) urlruntime.Must(terminalv1alpha2.AddToContainer(container, clientsets.Kubernetes(), nil)) urlruntime.Must(metricsv1alpha2.AddToContainer(container)) - urlruntime.Must(networkv1alpha2.AddToContainer(container)) + urlruntime.Must(networkv1alpha2.AddToContainer(container, "")) config := restfulspec.Config{ WebServices: container.RegisteredWebServices(),