Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using PATCH instead of PUT to update PipelineActivity #2309

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 36 additions & 35 deletions pkg/kube/activity.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package kube

import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"

"github.com/jenkins-x/jx/pkg/util"

"github.com/ghodss/yaml"
"github.com/jenkins-x/jx/pkg/apis/jenkins.io/v1"
typev1 "github.com/jenkins-x/jx/pkg/client/clientset/versioned/typed/jenkins.io/v1"
"github.com/jenkins-x/jx/pkg/gits"
"github.com/jenkins-x/jx/pkg/log"
"github.com/jenkins-x/jx/pkg/util"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

type PipelineActivityKey struct {
Expand Down Expand Up @@ -180,20 +180,19 @@ func (k *PipelineActivityKey) GetOrCreate(activities typev1.PipelineActivityInte
}
a, err := activities.Get(name, metav1.GetOptions{})
if err != nil {
create = true
a = defaultActivity
if errors.IsNotFound(err) {
create = true
a = defaultActivity
} else {
return defaultActivity, create, err
}
}
oldSpec := a.Spec
spec := &a.Spec
updateActivitySpec(k, spec)
if create {
answer, err := activities.Create(a)
return answer, true, err
} else {
if !reflect.DeepEqual(&a.Spec, &oldSpec) {
answer, err := activities.Update(a)
return answer, false, err
}
return a, false, nil
}
}
Expand Down Expand Up @@ -407,6 +406,28 @@ func (k *PromoteStepActivityKey) GetOrCreatePromoteUpdate(activities typev1.Pipe
return a, s, p, p.Update, created, err
}

// PatchRow is a json patch structure
type PatchRow struct {
Op string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value"`
}

// Almost making a full Update, but using Patch to work around problem with sometimes getting http status 409 back
func patchSpec(activities typev1.PipelineActivityInterface, activity *v1.PipelineActivity) (err error) {
things := make([]PatchRow, 1)
things[0].Value = activity.Spec
things[0].Op = "add"
things[0].Path = "/spec"

patchBytes, err := json.Marshal(things)
if err != nil {
return
}
_, err = activities.Patch(activity.Name, types.JSONPatchType, patchBytes)
return
}

func (k *PromoteStepActivityKey) OnPromotePullRequest(activities typev1.PipelineActivityInterface, fn PromotePullRequestFn) error {
if !k.IsValid() {
return nil
Expand All @@ -415,21 +436,16 @@ func (k *PromoteStepActivityKey) OnPromotePullRequest(activities typev1.Pipeline
log.Warn("Warning: no PipelineActivities client available!")
return nil
}
a, s, ps, p, added, err := k.GetOrCreatePromotePullRequest(activities)
a, s, ps, p, _, err := k.GetOrCreatePromotePullRequest(activities)
if err != nil {
return err
}
p1 := *p
err = fn(a, s, ps, p)
if err != nil {
return err
}
p2 := *p

if added || !reflect.DeepEqual(p1, p2) {
_, err = activities.Update(a)
}
return err
return patchSpec(activities, a)
}

func (k *PromoteStepActivityKey) OnPromoteUpdate(activities typev1.PipelineActivityInterface, fn PromoteUpdateFn) error {
Expand All @@ -440,11 +456,10 @@ func (k *PromoteStepActivityKey) OnPromoteUpdate(activities typev1.PipelineActiv
log.Warn("Warning: no PipelineActivities client available!")
return nil
}
a, s, ps, p, added, err := k.GetOrCreatePromoteUpdate(activities)
a, s, ps, p, _, err := k.GetOrCreatePromoteUpdate(activities)
if err != nil {
return err
}
p1 := asYaml(a)
if k.ApplicationURL != "" {
ps.ApplicationURL = k.ApplicationURL
}
Expand All @@ -455,21 +470,7 @@ func (k *PromoteStepActivityKey) OnPromoteUpdate(activities typev1.PipelineActiv
if k.ApplicationURL != "" {
ps.ApplicationURL = k.ApplicationURL
}
p2 := asYaml(a)

if added || p1 == "" || p1 != p2 {
_, err = activities.Update(a)
}
return err
}

func asYaml(activity *v1.PipelineActivity) string {
data, err := yaml.Marshal(activity)
if err == nil {
return string(data)
}
log.Warnf("Failed to marshal PipelineActivity to YAML %s: %s", activity.Name, err)
return ""
return patchSpec(activities, a)
}

func (k *PromoteStepActivityKey) matchesPreview(step *v1.PipelineActivityStep) bool {
Expand Down
24 changes: 6 additions & 18 deletions pkg/kube/activity_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,9 @@ func StartPromotionPullRequest(a *v1.PipelineActivity, s *v1.PipelineActivitySte
Time: time.Now(),
}
}
if a.Spec.WorkflowStatus != v1.ActivityStatusTypeRunning {
a.Spec.WorkflowStatus = v1.ActivityStatusTypeRunning
}
if a.Spec.Status != v1.ActivityStatusTypeRunning {
a.Spec.Status = v1.ActivityStatusTypeRunning
}
if p.Status != v1.ActivityStatusTypeRunning {
p.Status = v1.ActivityStatusTypeRunning
}
a.Spec.WorkflowStatus = v1.ActivityStatusTypeRunning
a.Spec.Status = v1.ActivityStatusTypeRunning
p.Status = v1.ActivityStatusTypeRunning
return nil
}

Expand All @@ -71,15 +65,9 @@ func StartPromotionUpdate(a *v1.PipelineActivity, s *v1.PipelineActivityStep, ps
Time: time.Now(),
}
}
if a.Spec.WorkflowStatus != v1.ActivityStatusTypeRunning {
a.Spec.WorkflowStatus = v1.ActivityStatusTypeRunning
}
if a.Spec.Status != v1.ActivityStatusTypeRunning {
a.Spec.Status = v1.ActivityStatusTypeRunning
}
if p.Status != v1.ActivityStatusTypeRunning {
p.Status = v1.ActivityStatusTypeRunning
}
a.Spec.WorkflowStatus = v1.ActivityStatusTypeRunning
a.Spec.Status = v1.ActivityStatusTypeRunning
p.Status = v1.ActivityStatusTypeRunning
return nil
}

Expand Down
23 changes: 22 additions & 1 deletion pkg/kube/activity_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package kube_test

import (
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"strconv"
"testing"
"time"
Expand Down Expand Up @@ -44,7 +47,7 @@ func (m *MockPipelineActivityInterface) Get(name string, options metav1.GetOptio
if ok {
return a, nil
}
return nil, fmt.Errorf("No such PipelineActivity %s", name)
return nil, errors.NewNotFound(schema.GroupResource{}, name)
}

func (m *MockPipelineActivityInterface) List(opts metav1.ListOptions) (*v1.PipelineActivityList, error) {
Expand All @@ -61,7 +64,25 @@ func (m *MockPipelineActivityInterface) Watch(opts metav1.ListOptions) (watch.In
return nil, fmt.Errorf("TODO")
}

// SpecPatchRow is a json patch structure
type SpecPatchRow struct {
Op string `json:"op"`
Path string `json:"path"`
Value v1.PipelineActivitySpec `json:"value"`
}

func (m *MockPipelineActivityInterface) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.PipelineActivity, err error) {
if pt == types.JSONPatchType {
patch := make([]SpecPatchRow, 1)
err := json.Unmarshal(data, &patch)
if err != nil {
return nil, err
}
if len(patch) == 1 && patch[0].Op == "add" && patch[0].Path == "/spec" {
m.Activities[name].Spec = patch[0].Value
return m.Activities[name], nil
}
}
return nil, fmt.Errorf("TODO")
}

Expand Down