diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 3157eaff7..874c9bcf7 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -66,8 +66,7 @@ import ( clusterkapisv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/cluster/v1alpha1" configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2" crd "kubesphere.io/kubesphere/pkg/kapis/crd" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" - devopsv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha3" + kapisdevops "kubesphere.io/kubesphere/pkg/kapis/devops" edgeruntimev1alpha1 "kubesphere.io/kubesphere/pkg/kapis/edgeruntime/v1alpha1" gatewayv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/gateway/v1alpha1" iamapi "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" @@ -247,8 +246,7 @@ func (s *APIServer) installKubeSphereAPIs(stopCh <-chan struct{}) { s.Config.AuthenticationOptions)) urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.Config.ServiceMeshOptions, s.container, s.KubernetesClient.Kubernetes(), s.CacheClient)) urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost)) - urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.Config.DevopsOptions.Endpoint)) - urlruntime.Must(devopsv1alpha3.AddToContainer(s.container, s.Config.DevopsOptions.Endpoint)) + urlruntime.Must(kapisdevops.AddToContainer(s.container, s.Config.DevopsOptions.Endpoint)) urlruntime.Must(notificationv1.AddToContainer(s.container, s.Config.NotificationOptions.Endpoint)) urlruntime.Must(alertingv1.AddToContainer(s.container, s.Config.AlertingOptions.Endpoint)) urlruntime.Must(alertingv2alpha1.AddToContainer(s.container, s.InformerFactory, diff --git a/pkg/kapis/devops/group.go b/pkg/kapis/devops/group.go deleted file mode 100644 index 3324fdbdc..000000000 --- a/pkg/kapis/devops/group.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -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 devops diff --git a/pkg/kapis/devops/register.go b/pkg/kapis/devops/register.go new file mode 100644 index 000000000..d40f16b5f --- /dev/null +++ b/pkg/kapis/devops/register.go @@ -0,0 +1,50 @@ +/* +Copyright 2022 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 devops + +import ( + "strings" + + "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/runtime/schema" + + "kubesphere.io/kubesphere/pkg/kapis/generic" +) + +var devopsGroupVersions = []schema.GroupVersion{ + {Group: "devops.kubesphere.io", Version: "v1alpha2"}, + {Group: "devops.kubesphere.io", Version: "v1alpha3"}, + {Group: "gitops.kubesphere.io", Version: "v1alpha1"}, + // TODO Add other group versions here, like cd.devops.kubesphere.io +} + +// AddToContainer registers DevOps proxies to the container. +func AddToContainer(container *restful.Container, endpoint string) error { + endpoint = strings.TrimSuffix(endpoint, "/") + for _, groupVersion := range devopsGroupVersions { + // Ensure that we proxy with different group here due to trimming of "/kapis/group_name". + // TODO: We could add a flag to decide to trim "/kapis/group_name" or not when creating a new GenericProxy. + proxy, err := generic.NewGenericProxy(endpoint+"/kapis/"+groupVersion.Group, groupVersion.Group, groupVersion.Version) + if err != nil { + return err + } + if err = proxy.AddToContainer(container); err != nil { + return err + } + } + return nil +} diff --git a/pkg/kapis/devops/register_test.go b/pkg/kapis/devops/register_test.go new file mode 100644 index 000000000..7fa8106dd --- /dev/null +++ b/pkg/kapis/devops/register_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2022 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 devops + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/emicklei/go-restful" + "github.com/stretchr/testify/assert" +) + +func TestAddToContainer(t *testing.T) { + fakeResponse := "fake DevOps APIServer response" + notFoundResponse := "404 page not found\n" + type args struct { + target string + mockAPIPattern string + mockResponse string + } + tests := []struct { + name string + args args + wantErr bool + wantResponse string + }{{ + name: "Should proxy devops.kubesphere.io/v1alpha1 API properly", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha1/resources", + mockAPIPattern: "/kapis/devops.kubesphere.io/v1alpha1/resources", + mockResponse: fakeResponse, + }, + wantResponse: notFoundResponse, + }, { + name: "Should proxy devops.kubesphere.io/v1alpha2 API properly", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha2/resources", + mockAPIPattern: "/kapis/devops.kubesphere.io/v1alpha2/resources", + mockResponse: fakeResponse, + }, + wantResponse: fakeResponse, + }, { + name: "Should proxy devops.kubesphere.io/v1alpha3 API properly", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha3/resources", + mockAPIPattern: "/kapis/devops.kubesphere.io/v1alpha3/resources", + mockResponse: fakeResponse, + }, + wantResponse: fakeResponse, + }, { + name: "Should proxy gitops.kubesphere.io/v1alpha1 API properly", + args: args{ + target: "/kapis/gitops.kubesphere.io/v1alpha1/resources", + mockAPIPattern: "/kapis/gitops.kubesphere.io/v1alpha1/resources", + mockResponse: fakeResponse, + }, + wantResponse: fakeResponse, + }, { + name: "Should return 404 if versions miss match", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha3/resources", + mockAPIPattern: "/kapis/devops.kubesphere.io/v1alpha1/resources", + }, + wantResponse: notFoundResponse, + }, { + name: "Should return 404 if groups miss match", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha3/resources", + mockAPIPattern: "/kapis/gitops.kubesphere.io/v1alpha3/resources", + }, + wantResponse: notFoundResponse, + }, + { + name: "Should not proxy v1alpha123 API properly event if pattern matched", + args: args{ + target: "/kapis/devops.kubesphere.io/v1alpha123/resources", + mockAPIPattern: "/kapis/devops.kubesphere.io/v1alpha123/resources", + }, + wantResponse: notFoundResponse, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create a fresh mock DevOps APIServer. + server := mockDevOpsAPIServer(tt.args.mockAPIPattern, 200, tt.args.mockResponse) + defer server.Close() + + // mock to request DevOps API from KubeSphere APIServer + container := restful.NewContainer() + if err := AddToContainer(container, server.URL); (err != nil) != tt.wantErr { + t.Errorf("AddToContainer() error = %v, wantErr %v", err, tt.wantErr) + } + request := httptest.NewRequest(http.MethodGet, tt.args.target, nil) + recorder := &responseRecorder{*httptest.NewRecorder()} + container.ServeHTTP(restful.NewResponse(recorder), request) + + assert.Equal(t, tt.wantResponse, recorder.Body.String()) + }) + } +} + +func mockDevOpsAPIServer(pattern string, fakeCode int, fakeResp string) *httptest.Server { + mux := http.NewServeMux() + mux.HandleFunc(pattern, func(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(fakeCode) + _, _ = writer.Write([]byte(fakeResp)) + }) + return httptest.NewServer(mux) +} + +// responseRecorder extends httptest.ResponseRecorder and implements CloseNotifier interface for generic proxy. +type responseRecorder struct { + httptest.ResponseRecorder +} + +func (*responseRecorder) CloseNotify() <-chan bool { + return nil +} diff --git a/pkg/kapis/devops/v1alpha2/OWNERS b/pkg/kapis/devops/v1alpha2/OWNERS deleted file mode 100644 index d616f1051..000000000 --- a/pkg/kapis/devops/v1alpha2/OWNERS +++ /dev/null @@ -1,12 +0,0 @@ -approvers: - - shaowenchen - - linuxsuren - -reviewers: - - runzexia - - soulseen - - shaowenchen - - linuxsuren - -labels: - - area/devops diff --git a/pkg/kapis/devops/v1alpha2/register.go b/pkg/kapis/devops/v1alpha2/register.go deleted file mode 100644 index 3bc472f9c..000000000 --- a/pkg/kapis/devops/v1alpha2/register.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -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 ( - "github.com/emicklei/go-restful" - "k8s.io/apimachinery/pkg/runtime/schema" - - "kubesphere.io/kubesphere/pkg/kapis/generic" -) - -const ( - GroupName = "devops.kubesphere.io" -) - -var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} - -func AddToContainer(container *restful.Container, endpoint string) error { - proxy, err := generic.NewGenericProxy(endpoint, GroupVersion.Group, GroupVersion.Version) - if err != nil { - return err - } - - return proxy.AddToContainer(container) -} diff --git a/pkg/kapis/devops/v1alpha3/register.go b/pkg/kapis/devops/v1alpha3/register.go deleted file mode 100644 index f496be6ba..000000000 --- a/pkg/kapis/devops/v1alpha3/register.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - - 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 v1alpha3 - -import ( - "github.com/emicklei/go-restful" - "k8s.io/apimachinery/pkg/runtime/schema" - - "kubesphere.io/kubesphere/pkg/kapis/generic" -) - -const ( - GroupName = "devops.kubesphere.io" -) - -var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"} - -func AddToContainer(container *restful.Container, endpoint string) error { - proxy, err := generic.NewGenericProxy(endpoint, GroupVersion.Group, GroupVersion.Version) - if err != nil { - return err - } - - return proxy.AddToContainer(container) -} diff --git a/tools/cmd/doc-gen/main.go b/tools/cmd/doc-gen/main.go index 55b0e3d43..dc624562c 100644 --- a/tools/cmd/doc-gen/main.go +++ b/tools/cmd/doc-gen/main.go @@ -24,6 +24,8 @@ import ( "io/ioutil" "log" + kapisdevops "kubesphere.io/kubesphere/pkg/kapis/devops" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" "kubesphere.io/kubesphere/pkg/version" @@ -43,8 +45,6 @@ import ( "kubesphere.io/kubesphere/pkg/informers" alertingv2alpha1 "kubesphere.io/kubesphere/pkg/kapis/alerting/v2alpha1" clusterkapisv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/cluster/v1alpha1" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" - devopsv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha3" iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" @@ -120,8 +120,7 @@ func generateSwaggerJson() []byte { urlruntime.Must(oauth.AddToContainer(container, nil, nil, nil, nil, nil, nil)) urlruntime.Must(clusterkapisv1alpha1.AddToContainer(container, clientsets.KubeSphere(), informerFactory.KubernetesSharedInformerFactory(), informerFactory.KubeSphereSharedInformerFactory(), "", "", "")) - urlruntime.Must(devopsv1alpha2.AddToContainer(container, "")) - urlruntime.Must(devopsv1alpha3.AddToContainer(container, "")) + urlruntime.Must(kapisdevops.AddToContainer(container, "")) urlruntime.Must(iamv1alpha2.AddToContainer(container, nil, nil, group.New(informerFactory, clientsets.KubeSphere(), clientsets.Kubernetes()), nil)) urlruntime.Must(monitoringv1alpha3.AddToContainer(container, clientsets.Kubernetes(), nil, nil, informerFactory, nil, nil, nil, nil)) urlruntime.Must(openpitrixv1.AddToContainer(container, informerFactory, fake.NewSimpleClientset(), nil, nil))