diff --git a/pkg/apis/devops/v1alpha3/credential_types.go b/pkg/apis/devops/v1alpha3/credential_types.go index 707fe1323..1db8a26af 100644 --- a/pkg/apis/devops/v1alpha3/credential_types.go +++ b/pkg/apis/devops/v1alpha3/credential_types.go @@ -7,7 +7,7 @@ We use a special type of secret as a credential for DevOps. This file will not contain CRD, but the credential type constants and their fields. */ const ( - CredentialFinalizerName = "credential.finalizers.kubesphere.io" + CredentialFinalizerName = "finalizers.kubesphere.io/credential" DevOpsCredentialPrefix = "credential.devops.kubesphere.io/" // SecretTypeBasicAuth contains data needed for basic authentication. // diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 919a4957b..1abc186b3 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -34,6 +34,7 @@ import ( clusterkapisv1alpha1 "kubesphere.io/kubesphere/pkg/kapis/cluster/v1alpha1" configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2" devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha3" iamapi "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" @@ -179,6 +180,12 @@ func (s *APIServer) installKubeSphereAPIs() { s.SonarClient, s.KubernetesClient.KubeSphere(), s.S3Client)) + urlruntime.Must(devopsv1alpha3.AddToContainer(s.container, + s.DevopsClient, + s.KubernetesClient.Kubernetes(), + s.KubernetesClient.KubeSphere(), + s.InformerFactory.KubeSphereSharedInformerFactory(), + s.InformerFactory.KubernetesSharedInformerFactory())) urlruntime.Must(notificationv1.AddToContainer(s.container, s.Config.NotificationOptions.Endpoint)) urlruntime.Must(alertingv1.AddToContainer(s.container, s.Config.AlertingOptions.Endpoint)) } diff --git a/pkg/controller/devopscredential/devopscredential_controller.go b/pkg/controller/devopscredential/devopscredential_controller.go index 4206fd978..ad0195e1f 100644 --- a/pkg/controller/devopscredential/devopscredential_controller.go +++ b/pkg/controller/devopscredential/devopscredential_controller.go @@ -21,7 +21,6 @@ import ( devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" "reflect" "strings" "time" @@ -217,24 +216,15 @@ func (c *Controller) syncHandler(key string) error { copySecret := secret.DeepCopy() // DeletionTimestamp.IsZero() means copySecret has not been deleted. - if copySecret.ObjectMeta.DeletionTimestamp.IsZero() { + if secret.ObjectMeta.DeletionTimestamp.IsZero() { // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers - if !sliceutil.HasString(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) { + if !sliceutil.HasString(secret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) { copySecret.ObjectMeta.Finalizers = append(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) } // Check secret config exists, otherwise we will create it. // if secret exists, update config - _, err := c.devopsClient.GetCredentialInProject(nsName, secret.Name) - if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { - klog.Error(err, fmt.Sprintf("failed to get secret %s ", key)) - return err - } else if err != nil { - _, err := c.devopsClient.CreateCredentialInProject(nsName, copySecret) - if err != nil { - klog.Error(err, fmt.Sprintf("failed to create secret %s ", key)) - return err - } - } else { + _, err := c.devopsClient.CreateCredentialInProject(nsName, copySecret) + if err != nil { if _, ok := copySecret.Annotations[devopsv1alpha3.CredentialAutoSyncAnnoKey]; ok { _, err := c.devopsClient.UpdateCredentialInProject(nsName, copySecret) if err != nil { @@ -247,16 +237,9 @@ func (c *Controller) syncHandler(key string) error { } else { // Finalizers processing logic if sliceutil.HasString(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) { - _, err := c.devopsClient.GetCredentialInProject(nsName, secret.Name) - if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { - klog.Error(err, fmt.Sprintf("failed to get secret %s ", key)) + if _, err := c.devopsClient.DeleteCredentialInProject(nsName, secret.Name); err != nil { + klog.Error(err, fmt.Sprintf("failed to delete secret %s in devops", key)) return err - } else if err != nil && devopsClient.GetDevOpsStatusCode(err) == http.StatusNotFound { - } else { - if _, err := c.devopsClient.DeleteCredentialInProject(nsName, secret.Name); err != nil { - klog.Error(err, fmt.Sprintf("failed to delete secret %s in devops", key)) - return err - } } copySecret.ObjectMeta.Finalizers = sliceutil.RemoveString(copySecret.ObjectMeta.Finalizers, func(item string) bool { return item == devopsv1alpha3.CredentialFinalizerName diff --git a/pkg/controller/devopscredential/devopscredential_controller_test.go b/pkg/controller/devopscredential/devopscredential_controller_test.go index d2900c43d..235b4a0fd 100644 --- a/pkg/controller/devopscredential/devopscredential_controller_test.go +++ b/pkg/controller/devopscredential/devopscredential_controller_test.go @@ -1,13 +1,14 @@ package devopscredential import ( - v1 "k8s.io/api/core/v1" - "kubesphere.io/kubesphere/pkg/constants" - fakeDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" "reflect" "testing" "time" + v1 "k8s.io/api/core/v1" + "kubesphere.io/kubesphere/pkg/constants" + fakeDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -351,27 +352,6 @@ func TestDeleteCredential(t *testing.T) { f.run(getKey(secret, t)) } -func TestDeleteNotExistCredential(t *testing.T) { - f := newFixture(t) - nsName := "test-123" - pipelineName := "test" - projectName := "test_project" - - ns := newNamespace(nsName, projectName) - secret := newDeletingSecret(nsName, pipelineName) - - expectSecret := secret.DeepCopy() - expectSecret.Finalizers = []string{} - f.secretLister = append(f.secretLister, secret) - f.namespaceLister = append(f.namespaceLister, ns) - f.kubeobjects = append(f.kubeobjects, secret) - f.initDevOpsProject = nsName - f.initCredential = []*v1.Secret{} - f.expectCredential = []*v1.Secret{} - f.expectUpdateSecretAction(expectSecret) - f.run(getKey(secret, t)) -} - func TestUpdateCredential(t *testing.T) { f := newFixture(t) nsName := "test-123" diff --git a/pkg/controller/pipeline/pipeline_controller.go b/pkg/controller/pipeline/pipeline_controller.go index 4f6c94bdf..e8c0b563c 100644 --- a/pkg/controller/pipeline/pipeline_controller.go +++ b/pkg/controller/pipeline/pipeline_controller.go @@ -23,7 +23,6 @@ import ( devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" "reflect" "time" ) @@ -225,13 +224,10 @@ func (c *Controller) syncHandler(key string) error { return err } } - } else if devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { - klog.Error(err, fmt.Sprintf("failed to get copyPipeline %s ", key)) - return err } else { _, err := c.devopsClient.CreateProjectPipeline(nsName, copyPipeline) if err != nil { - klog.Error(err, fmt.Sprintf("failed to get copyPipeline %s ", key)) + klog.Error(err, fmt.Sprintf("failed to create copyPipeline %s ", key)) return err } } @@ -239,16 +235,8 @@ func (c *Controller) syncHandler(key string) error { } else { // Finalizers processing logic if sliceutil.HasString(copyPipeline.ObjectMeta.Finalizers, devopsv1alpha3.PipelineFinalizerName) { - _, err := c.devopsClient.GetProjectPipelineConfig(nsName, pipeline.Name) - if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { - klog.Error(err, fmt.Sprintf("failed to get pipeline %s ", key)) - return err - } else if err != nil && devopsClient.GetDevOpsStatusCode(err) == http.StatusNotFound { - } else { - if _, err := c.devopsClient.DeleteProjectPipeline(nsName, pipeline.Name); err != nil { - klog.Error(err, fmt.Sprintf("failed to delete pipeline %s in devops", key)) - return err - } + if _, err := c.devopsClient.DeleteProjectPipeline(nsName, pipeline.Name); err != nil { + klog.Error(err, fmt.Sprintf("failed to delete pipeline %s in devops", key)) } copyPipeline.ObjectMeta.Finalizers = sliceutil.RemoveString(copyPipeline.ObjectMeta.Finalizers, func(item string) bool { return item == devopsv1alpha3.PipelineFinalizerName diff --git a/pkg/controller/s2ibinary/s2ibinary_controller_test.go b/pkg/controller/s2ibinary/s2ibinary_controller_test.go index 343c0a490..42d4a7568 100644 --- a/pkg/controller/s2ibinary/s2ibinary_controller_test.go +++ b/pkg/controller/s2ibinary/s2ibinary_controller_test.go @@ -225,7 +225,7 @@ func TestDeleteS3Object(t *testing.T) { f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) f.objects = append(f.objects, s2iBinary) - f.initS3Objects = []*fakes3.Object{&fakes3.Object{ + f.initS3Objects = []*fakes3.Object{{ Key: "default-test", }} f.expectS3Objects = []*fakes3.Object{} diff --git a/pkg/kapis/devops/v1alpha2/handler.go b/pkg/kapis/devops/v1alpha2/handler.go index 39ebd4acb..0a53e7c6d 100644 --- a/pkg/kapis/devops/v1alpha2/handler.go +++ b/pkg/kapis/devops/v1alpha2/handler.go @@ -20,7 +20,7 @@ type PipelineSonarHandler struct { func NewProjectPipelineHandler(devopsClient devopsClient.Interface) ProjectPipelineHandler { return ProjectPipelineHandler{ - devopsOperator: devops.NewDevopsOperator(devopsClient), + devopsOperator: devops.NewDevopsOperator(devopsClient, nil, nil, nil, nil), projectCredentialGetter: devops.NewProjectCredentialOperator(devopsClient), } } diff --git a/pkg/kapis/devops/v1alpha3/handler.go b/pkg/kapis/devops/v1alpha3/handler.go new file mode 100644 index 000000000..8a6f9410d --- /dev/null +++ b/pkg/kapis/devops/v1alpha3/handler.go @@ -0,0 +1,385 @@ +/* + + 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" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/devops" + servererr "kubesphere.io/kubesphere/pkg/server/errors" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" +) + +type devopsHandler struct { + devops devops.DevopsOperator +} + +func newDevOpsHandler(devopsClient devopsClient.Interface, k8sclient kubernetes.Interface, ksclient kubesphere.Interface, + ksInformers externalversions.SharedInformerFactory, + k8sInformers informers.SharedInformerFactory) *devopsHandler { + + return &devopsHandler{ + devops: devops.NewDevopsOperator(devopsClient, k8sclient, ksclient, ksInformers, k8sInformers), + } +} + +// devopsproject handler about get/list/post/put/delete +func (h *devopsHandler) GetDevOpsProject(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + + project, err := h.devops.GetDevOpsProject(workspace, projectName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(project) +} + +func (h *devopsHandler) ListDevOpsProject(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + + projectList, err := h.devops.ListDevOpsProject(workspace) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(projectList) +} + +func (h *devopsHandler) CreateDevOpsProject(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + var devOpsProject v1alpha3.DevOpsProject + err := request.ReadEntity(&devOpsProject) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + created, err := h.devops.CreateDevOpsProject(workspace, &devOpsProject) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(created) +} + +func (h *devopsHandler) UpdateDevOpsProject(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + var devOpsProject v1alpha3.DevOpsProject + err := request.ReadEntity(&devOpsProject) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + project, err := h.devops.UpdateDevOpsProject(workspace, &devOpsProject) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(project) +} + +func (h *devopsHandler) DeleteDevOpsProject(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + + err := h.devops.DeleteDevOpsProject(workspace, projectName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(servererr.None) +} + +// pipeline handler about get/list/post/put/delete +func (h *devopsHandler) GetPipeline(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + pipelineName := request.PathParameter("pipelineName") + + obj, err := h.devops.GetPipelineObj(workspace, projectName, pipelineName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(obj) +} + +func (h *devopsHandler) ListPipeline(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + objs, err := h.devops.ListPipelineObj(workspace, projectName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(objs) +} + +func (h *devopsHandler) CreatePipeline(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + var pipeline v1alpha3.Pipeline + err := request.ReadEntity(&pipeline) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + created, err := h.devops.CreatePipelineObj(workspace, projectName, &pipeline) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(created) +} + +func (h *devopsHandler) UpdatePipeline(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + + var pipeline v1alpha3.Pipeline + err := request.ReadEntity(&pipeline) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + obj, err := h.devops.UpdatePipelineObj(workspace, projectName, &pipeline) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(obj) +} + +func (h *devopsHandler) DeletePipeline(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + pipelineName := request.PathParameter("pipelineName") + + err := h.devops.DeletePipelineObj(workspace, projectName, pipelineName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(servererr.None) +} + +//credential handler about get/list/post/put/delete +func (h *devopsHandler) GetCredential(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + credentialName := request.PathParameter("credentialName") + + obj, err := h.devops.GetCredentialObj(workspace, projectName, credentialName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(obj) +} + +func (h *devopsHandler) ListCredential(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + + objs, err := h.devops.ListCredentialObj(workspace, projectName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(objs) +} + +func (h *devopsHandler) CreateCredential(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + var obj v1.Secret + err := request.ReadEntity(&obj) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + created, err := h.devops.CreateCredentialObj(workspace, projectName, &obj) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(created) +} + +func (h *devopsHandler) UpdateCredential(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + var obj v1.Secret + err := request.ReadEntity(&obj) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + updated, err := h.devops.UpdateCredentialObj(workspace, projectName, &obj) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(updated) +} + +func (h *devopsHandler) DeleteCredential(request *restful.Request, response *restful.Response) { + workspace := request.PathParameter("workspace") + projectName := request.PathParameter("projectName") + credentialName := request.PathParameter("credentialName") + + err := h.devops.DeleteCredentialObj(workspace, projectName, credentialName) + + if err != nil { + klog.Error(err) + if errors.IsNotFound(err) { + api.HandleNotFound(response, request, err) + return + } + api.HandleBadRequest(response, request, err) + return + } + + response.WriteEntity(servererr.None) +} diff --git a/pkg/kapis/devops/v1alpha3/register.go b/pkg/kapis/devops/v1alpha3/register.go new file mode 100644 index 000000000..912c5ba29 --- /dev/null +++ b/pkg/kapis/devops/v1alpha3/register.go @@ -0,0 +1,183 @@ +/* + + 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" + restfulspec "github.com/emicklei/go-restful-openapi" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/constants" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" +) + +const ( + GroupName = "devops.kubesphere.io" + RespOK = "ok" +) + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"} + +func AddToContainer(container *restful.Container, devopsClient devopsClient.Interface, + k8sclient kubernetes.Interface, ksclient kubesphere.Interface, + ksInformers externalversions.SharedInformerFactory, + k8sInformers informers.SharedInformerFactory) error { + devopsEnable := devopsClient != nil + if devopsEnable { + ws := runtime.NewWebService(GroupVersion) + handler := newDevOpsHandler(devopsClient, k8sclient, ksclient, ksInformers, k8sInformers) + // credential + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/{projectName}/credential/"). + To(handler.ListCredential). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "devops name")). + Doc("list the credential of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.PipelineList{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.POST("/workspaces/{workspace}/devopsprojects/{projectName}/credential/"). + To(handler.CreateCredential). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "devops name")). + Doc("create the credential of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.Pipeline{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/{projectName}/credential/{credentialName}/"). + To(handler.GetCredential). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("credential", "pipeline name")). + Doc("get the credential of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1.Secret{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.PUT("/workspaces/{workspace}/devopsprojects/{projectName}/credential/{credentialName}/"). + To(handler.UpdateCredential). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("credentialName", "credential name")). + Doc("put the credential of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1.Secret{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.DELETE("/workspaces/{workspace}/devopsprojects/{projectName}/credential/{credentialName}/"). + To(handler.DeleteCredential). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("credentialName", "credential name")). + Doc("delete the credential of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1.Secret{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag})) + + // pipeline + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/{projectName}/pipelines/"). + To(handler.ListPipeline). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "devops name")). + Doc("list the pipeline of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.PipelineList{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.POST("/workspaces/{workspace}/devopsprojects/{projectName}/pipelines/"). + To(handler.CreatePipeline). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "devops name")). + Doc("create the pipeline of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.Pipeline{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/{projectName}/pipelines/{pipelineName}/"). + To(handler.GetPipeline). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("pipelineName", "pipeline name")). + Doc("get the pipeline of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.Pipeline{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.PUT("/workspaces/{workspace}/devopsprojects/{projectName}/pipelines/{pipelineName}/"). + To(handler.UpdatePipeline). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("pipelineName", "pipeline name")). + Doc("put the pipeline of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.Pipeline{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.DELETE("/workspaces/{workspace}/devopsprojects/{projectName}/pipelines/{pipelineName}/"). + To(handler.DeletePipeline). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Param(ws.PathParameter("pipelineName", "pipeline name")). + Doc("delete the pipeline of the specified devops for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.Pipeline{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag})) + + // devops + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/"). + To(handler.ListDevOpsProject). + Param(ws.PathParameter("workspace", "workspace name")). + Doc("List the devopsproject of the specified workspace for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.DevOpsProjectList{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.POST("/workspaces/{workspace}/devopsprojects/"). + To(handler.CreateDevOpsProject). + Param(ws.PathParameter("workspace", "workspace name")). + Doc("Create the devopsproject of the specified workspace for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.DevOpsProject{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.GET("/workspaces/{workspace}/devopsprojects/{projectName}/"). + To(handler.GetDevOpsProject). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Doc("Get the devopsproject of the specified workspace for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.DevOpsProject{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.PUT("/workspaces/{workspace}/devopsprojects/{projectName}/"). + To(handler.UpdateDevOpsProject). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Doc("Put the devopsproject of the specified workspace for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.DevOpsProject{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + ws.Route(ws.DELETE("/workspaces/{workspace}/devopsprojects/{projectName}/"). + To(handler.DeleteDevOpsProject). + Param(ws.PathParameter("workspace", "workspace name")). + Param(ws.PathParameter("projectName", "project name")). + Doc("Get the devopsproject of the specified workspace for the current user"). + Returns(http.StatusOK, api.StatusOK, []v1alpha3.DevOpsProject{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag})) + + container.Add(ws) + } + return nil +} diff --git a/pkg/models/devops/devops.go b/pkg/models/devops/devops.go index 94298915b..fe3e92e5f 100644 --- a/pkg/models/devops/devops.go +++ b/pkg/models/devops/devops.go @@ -23,7 +23,15 @@ import ( "fmt" "io" "io/ioutil" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" "sync" @@ -34,6 +42,24 @@ const ( ) type DevopsOperator interface { + CreateDevOpsProject(workspace string, project *v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) + GetDevOpsProject(workspace string, projectName string) (*v1alpha3.DevOpsProject, error) + DeleteDevOpsProject(workspace string, projectName string) error + UpdateDevOpsProject(workspace string, project *v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) + ListDevOpsProject(workspace string) (*v1alpha3.DevOpsProjectList, error) + + CreatePipelineObj(workspace string, projectName string, pipeline *v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) + GetPipelineObj(workspace string, projectName string, pipelineName string) (*v1alpha3.Pipeline, error) + DeletePipelineObj(workspace string, projectName string, pipelineName string) error + UpdatePipelineObj(workspace string, projectName string, pipeline *v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) + ListPipelineObj(workspace string, projectName string) (*v1alpha3.PipelineList, error) + + CreateCredentialObj(workspace string, projectName string, s *v1.Secret) (*v1.Secret, error) + GetCredentialObj(workspace string, projectName string, secretName string) (*v1.Secret, error) + DeleteCredentialObj(workspace string, projectName string, secretName string) error + UpdateCredentialObj(workspace string, projectName string, secret *v1.Secret) (*v1.Secret, error) + ListCredentialObj(workspace string, projectName string) (*v1.SecretList, error) + GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) ListPipelines(req *http.Request) (*devops.PipelineList, error) GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) (*devops.PipelineRun, error) @@ -85,10 +111,21 @@ type DevopsOperator interface { type devopsOperator struct { devopsClient devops.Interface + k8sclient kubernetes.Interface + ksclient kubesphere.Interface + ksInformers externalversions.SharedInformerFactory + k8sInformers informers.SharedInformerFactory } -func NewDevopsOperator(client devops.Interface) DevopsOperator { - return &devopsOperator{devopsClient: client} +func NewDevopsOperator(client devops.Interface, k8sclient kubernetes.Interface, ksclient kubesphere.Interface, + ksInformers externalversions.SharedInformerFactory, k8sInformers informers.SharedInformerFactory) DevopsOperator { + return &devopsOperator{ + devopsClient: client, + k8sclient: k8sclient, + ksclient: ksclient, + ksInformers: ksInformers, + k8sInformers: k8sInformers, + } } func convertToHttpParameters(req *http.Request) *devops.HttpParameters { @@ -104,6 +141,111 @@ func convertToHttpParameters(req *http.Request) *devops.HttpParameters { return &httpParameters } +func (d devopsOperator) CreateDevOpsProject(workspace string, project *v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) { + project.Annotations[tenantv1alpha1.WorkspaceLabel] = workspace + return d.ksclient.DevopsV1alpha3().DevOpsProjects().Create(project) +} + +func (d devopsOperator) GetDevOpsProject(workspace string, projectName string) (*v1alpha3.DevOpsProject, error) { + return d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) +} + +func (d devopsOperator) DeleteDevOpsProject(workspace string, projectName string) error { + return d.ksclient.DevopsV1alpha3().DevOpsProjects().Delete(projectName, metav1.NewDeleteOptions(0)) +} + +func (d devopsOperator) UpdateDevOpsProject(workspace string, project *v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) { + project.Annotations[tenantv1alpha1.WorkspaceLabel] = workspace + return d.ksclient.DevopsV1alpha3().DevOpsProjects().Update(project) +} + +func (d devopsOperator) ListDevOpsProject(workspace string) (*v1alpha3.DevOpsProjectList, error) { + return d.ksclient.DevopsV1alpha3().DevOpsProjects().List(metav1.ListOptions{}) +} + +// pipelineobj in crd +func (d devopsOperator) CreatePipelineObj(workspace string, projectName string, pipeline *v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.ksclient.DevopsV1alpha3().Pipelines(projectObj.Status.AdminNamespace).Create(pipeline) +} + +func (d devopsOperator) GetPipelineObj(workspace string, projectName string, pipelineName string) (*v1alpha3.Pipeline, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.ksclient.DevopsV1alpha3().Pipelines(projectObj.Status.AdminNamespace).Get(pipelineName, metav1.GetOptions{}) +} + +func (d devopsOperator) DeletePipelineObj(workspace string, projectName string, pipelineName string) error { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return err + } + return d.ksclient.DevopsV1alpha3().Pipelines(projectObj.Status.AdminNamespace).Delete(pipelineName, metav1.NewDeleteOptions(0)) +} + +func (d devopsOperator) UpdatePipelineObj(workspace string, projectName string, pipeline *v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.ksclient.DevopsV1alpha3().Pipelines(projectObj.Status.AdminNamespace).Update(pipeline) +} + +func (d devopsOperator) ListPipelineObj(workspace string, projectName string) (*v1alpha3.PipelineList, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.ksclient.DevopsV1alpha3().Pipelines(projectObj.Status.AdminNamespace).List(metav1.ListOptions{}) +} + +//credentialobj in crd +func (d devopsOperator) CreateCredentialObj(workspace string, projectName string, secret *v1.Secret) (*v1.Secret, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.k8sclient.CoreV1().Secrets(projectObj.Status.AdminNamespace).Create(secret) +} + +func (d devopsOperator) GetCredentialObj(workspace string, projectName string, secretName string) (*v1.Secret, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.k8sclient.CoreV1().Secrets(projectObj.Status.AdminNamespace).Get(secretName, metav1.GetOptions{}) +} + +func (d devopsOperator) DeleteCredentialObj(workspace string, projectName string, secret string) error { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return err + } + return d.k8sclient.CoreV1().Secrets(projectObj.Status.AdminNamespace).Delete(secret, metav1.NewDeleteOptions(0)) +} + +func (d devopsOperator) UpdateCredentialObj(workspace string, projectName string, secret *v1.Secret) (*v1.Secret, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.k8sclient.CoreV1().Secrets(projectObj.Status.AdminNamespace).Update(secret) +} + +func (d devopsOperator) ListCredentialObj(workspace string, projectName string) (*v1.SecretList, error) { + projectObj, err := d.ksclient.DevopsV1alpha3().DevOpsProjects().Get(projectName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return d.k8sclient.CoreV1().Secrets(projectObj.Status.AdminNamespace).List(metav1.ListOptions{}) +} + +// others func (d devopsOperator) GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) { res, err := d.devopsClient.GetPipeline(projectName, pipelineName, convertToHttpParameters(req)) diff --git a/pkg/models/devops/devops_test.go b/pkg/models/devops/devops_test.go index 4d82e5b41..b25c96516 100644 --- a/pkg/models/devops/devops_test.go +++ b/pkg/models/devops/devops_test.go @@ -44,7 +44,7 @@ func TestGetNodesDetail(t *testing.T) { devopsClient := fake.NewFakeDevops(fakeData) - devopsOperator := NewDevopsOperator(devopsClient) + devopsOperator := NewDevopsOperator(devopsClient, nil, nil, nil, nil) httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/runs/run1/nodesdetail/?limit=10000", nil) @@ -96,7 +96,7 @@ func TestGetBranchNodesDetail(t *testing.T) { devopsClient := fake.NewFakeDevops(fakeData) - devopsOperator := NewDevopsOperator(devopsClient) + devopsOperator := NewDevopsOperator(devopsClient, nil, nil, nil, nil) httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/branchs/branch1/runs/run1/nodesdetail/?limit=10000", nil) diff --git a/pkg/models/resources/v1alpha3/node/nodes_test.go b/pkg/models/resources/v1alpha3/node/nodes_test.go index f60fac035..c29021d56 100644 --- a/pkg/models/resources/v1alpha3/node/nodes_test.go +++ b/pkg/models/resources/v1alpha3/node/nodes_test.go @@ -44,7 +44,7 @@ var node = &corev1.Node{ } var pods = []*corev1.Pod{ - &corev1.Pod{ + { ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", Name: "pod-with-resources", @@ -69,7 +69,7 @@ var pods = []*corev1.Pod{ Phase: corev1.PodRunning, }, }, - &corev1.Pod{ + { ObjectMeta: metav1.ObjectMeta{ Namespace: "foo2", Name: "pod-with-resources", diff --git a/pkg/simple/client/devops/jenkins/credential.go b/pkg/simple/client/devops/jenkins/credential.go index b67c65774..85cc7fa68 100644 --- a/pkg/simple/client/devops/jenkins/credential.go +++ b/pkg/simple/client/devops/jenkins/credential.go @@ -233,6 +233,7 @@ func (j *Jenkins) CreateCredentialInProject(projectId string, credential *v1.Sec if err != nil { return "", err } + if response.StatusCode != http.StatusOK { return "", errors.New(strconv.Itoa(response.StatusCode)) } diff --git a/pkg/simple/client/devops/jenkins/jenkins.go b/pkg/simple/client/devops/jenkins/jenkins.go index c8ca746fd..cf4943063 100644 --- a/pkg/simple/client/devops/jenkins/jenkins.go +++ b/pkg/simple/client/devops/jenkins/jenkins.go @@ -407,7 +407,7 @@ func (j *Jenkins) ListPipelineRuns(projectName, pipelineName string, httpParamet PipelineOjb := &Pipeline{ HttpParameters: httpParameters, Jenkins: j, - Path: ListPipelineRunUrl + httpParameters.Url.RawQuery, + Path: fmt.Sprintf(ListPipelineRunUrl, projectName, pipelineName) + httpParameters.Url.RawQuery, } res, err := PipelineOjb.ListPipelineRuns() return res, err diff --git a/pkg/simple/client/devops/jenkins/pipeline.go b/pkg/simple/client/devops/jenkins/pipeline.go index 08122f46b..579df9398 100644 --- a/pkg/simple/client/devops/jenkins/pipeline.go +++ b/pkg/simple/client/devops/jenkins/pipeline.go @@ -145,7 +145,7 @@ func (p *Pipeline) ListPipelineRuns() (*devops.PipelineRunList, error) { } var pipelineRunList devops.PipelineRunList - err = json.Unmarshal(res, &pipelineRunList) + err = json.Unmarshal(res, &pipelineRunList.Items) if err != nil { klog.Error(err) return nil, err diff --git a/pkg/simple/client/devops/pipeline.go b/pkg/simple/client/devops/pipeline.go index 8043a50fe..3781af411 100644 --- a/pkg/simple/client/devops/pipeline.go +++ b/pkg/simple/client/devops/pipeline.go @@ -80,86 +80,7 @@ type Pipeline struct { // GetPipeBranchRun & SearchPipelineRuns type PipelineRunList struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - PrevRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"prevRun,omitempty"` - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Nodes struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nodes,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - NextRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nextRun,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description of resource"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline name"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"source type, such as \"WorkflowRun\""` - Branch struct { - IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` - Issues []interface{} `json:"issues,omitempty" description:"issues"` - URL string `json:"url,omitempty" description:"url"` - } `json:"branch,omitempty"` - CommitID string `json:"commitId,omitempty" description:"commit id"` - CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url "` - PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` + Items []PipelineRun `json:"items"` } // GetBranchPipeRunNodes @@ -462,7 +383,9 @@ type Artifacts struct { } // GetPipeBranch -type PipelineBranch struct { +type PipelineBranch []PipelineBranchItem + +type PipelineBranchItem struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { Self struct {