Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Add containers release specs to update-manifests
Browse files Browse the repository at this point in the history
This adds a new release spec format providing update requests on container
level.

Sample POST request body:
```
{
  "type": "containers",
  "spec": {
    "ContainerSpecs": {
      "default:deployment/nginx": [
        {
          "Container": "nginx",
          "Current": "nginx:1.14.0",
          "Target": "nginx:1.15.0"
        }
      ],
      "default:deployment/helloworld": [
        {
          "Container": "helloworld",
          "Current": "quay.io/weaveworks/helloworld:master-07a1b6b",
          "Target": "quay.io/weaveworks/helloworld:master-a000004"
        },
        {
          "Container": "sidecar",
          "Current": "quay.io/weaveworks/sidecar:master-a000001",
          "Target": "quay.io/weaveworks/sidecar:master-a000002"
        }
      ]
    },
    "Kind": "plan",
    "SkipMismatches": false
  }
}
```

The request will fail if any of the Current container image requirements
are not met. To have partial updates go through failures, one can pass
true for IgnoreMismatches. Note that this still leads to an error if
containers cannot be found.

Also, `Current` can be left empty to disable any precondition check.
  • Loading branch information
rndstr committed Jul 12, 2018
1 parent e6da896 commit 8c6b459
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 19 deletions.
2 changes: 2 additions & 0 deletions release/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func (rc *ReleaseContext) SelectServices(results update.Result, prefilters, post
return filteredUpdates, nil
}

// WorkloadsForUpdate collects all workloads defined in manifests and prepares a list of
// controller updates for each of them. It does not consider updatability.
func (rc *ReleaseContext) WorkloadsForUpdate() (map[flux.ResourceID]*update.ControllerUpdate, error) {
resources, err := rc.manifests.LoadManifests(rc.repo.Dir(), rc.repo.ManifestDir())
if err != nil {
Expand Down
169 changes: 163 additions & 6 deletions release/releaser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package release

import (
"encoding/json"
"fmt"
"reflect"
"testing"
"time"

"github.com/go-kit/kit/log"

"github.com/stretchr/testify/assert"
"github.com/weaveworks/flux"
"github.com/weaveworks/flux/cluster"
"github.com/weaveworks/flux/cluster/kubernetes"
Expand All @@ -23,6 +25,8 @@ var (
// This must match the value in cluster/kubernetes/testfiles/data.go
helloContainer = "greeter"
sidecarContainer = "sidecar"
lockedContainer = "locked-service"
testContainer = "test-service"

oldImage = "quay.io/weaveworks/helloworld:master-a000001"
oldRef, _ = image.ParseRef(oldImage)
Expand Down Expand Up @@ -52,27 +56,28 @@ var (
oldLockedRef, _ = image.ParseRef(oldLockedImg)

newLockedImg = "quay.io/weaveworks/locked-service:2"
newLockedID, _ = image.ParseRef(newLockedImg)
newLockedRef, _ = image.ParseRef(newLockedImg)
lockedSvcID, _ = flux.ParseResourceID("default:deployment/locked-service")
lockedSvcSpec, _ = update.ParseResourceSpec(lockedSvcID.String())
lockedSvc = cluster.Controller{
ID: lockedSvcID,
Containers: cluster.ContainersOrExcuse{
Containers: []resource.Container{
{
Name: "locked-service",
Name: lockedContainer,
Image: oldLockedRef,
},
},
},
}

testSvc = cluster.Controller{
ID: flux.MustParseResourceID("default:deployment/test-service"),
testSvcID = flux.MustParseResourceID("default:deployment/test-service")
testSvc = cluster.Controller{
ID: testSvcID,
Containers: cluster.ContainersOrExcuse{
Containers: []resource.Container{
{
Name: "test-service",
Name: testContainer,
Image: testServiceRef,
},
},
Expand Down Expand Up @@ -104,7 +109,7 @@ var (
CreatedAt: timeNow,
},
{
ID: newLockedID,
ID: newLockedRef,
CreatedAt: timeNow,
},
},
Expand Down Expand Up @@ -527,6 +532,158 @@ func Test_UpdateList(t *testing.T) {
}
}

func Test_UpdateContainers(t *testing.T) {
cluster := mockCluster(hwSvc, lockedSvc)
checkout, cleanup := setup(t)
defer cleanup()
ctx := &ReleaseContext{
cluster: cluster,
manifests: mockManifests,
repo: checkout,
registry: mockRegistry,
}
for _, tst := range []struct {
Name string
Spec []update.ContainerUpdate

// true|false for `SkipMismatches`
Commit map[bool]string
Expected map[bool]update.ControllerResult
Err map[bool]error
}{
{
Name: "multiple containers",
Spec: []update.ContainerUpdate{
{
Container: helloContainer,
Current: oldRef,
Target: newHwRef,
},
{
Container: sidecarContainer,
Current: sidecarRef,
Target: newSidecarRef,
},
},
Expected: map[bool]update.ControllerResult{
true: {
Status: update.ReleaseStatusSuccess,
PerContainer: []update.ContainerUpdate{{
Container: helloContainer,
Current: oldRef,
Target: newHwRef,
}, {
Container: sidecarContainer,
Current: sidecarRef,
Target: newSidecarRef,
}},
},
false: {
Status: update.ReleaseStatusSuccess,
PerContainer: []update.ContainerUpdate{{
Container: helloContainer,
Current: oldRef,
Target: newHwRef,
}, {
Container: sidecarContainer,
Current: sidecarRef,
Target: newSidecarRef,
}},
},
},
Commit: map[bool]string{
true: "Release containers\n\ndefault:deployment/helloworld\n- quay.io/weaveworks/helloworld:master-a000002\n- weaveworks/sidecar:master-a000002\n",
false: "Release containers\n\ndefault:deployment/helloworld\n- quay.io/weaveworks/helloworld:master-a000002\n- weaveworks/sidecar:master-a000002\n",
},
},
{
Name: "container tag mismatch",
Spec: []update.ContainerUpdate{
{
Container: helloContainer,
Current: newHwRef, // mismatch
Target: oldRef,
},
{
Container: sidecarContainer,
Current: sidecarRef,
Target: newSidecarRef,
},
},
Expected: map[bool]update.ControllerResult{
true: {
Status: update.ReleaseStatusSuccess,
Error: fmt.Sprintf(update.ContainerTagMismatch, helloContainer),
PerContainer: []update.ContainerUpdate{
{
Container: sidecarContainer,
Current: sidecarRef,
Target: newSidecarRef,
},
},
},
false: {}, // Errors.
},
Commit: map[bool]string{
true: "Release containers\n\ndefault:deployment/helloworld\n- weaveworks/sidecar:master-a000002\n",
},
},
{
Name: "container not found",
Spec: []update.ContainerUpdate{
{
Container: helloContainer,
Current: oldRef,
Target: newHwRef,
},
{
Container: "foo", // not found
Current: oldRef,
Target: newHwRef,
},
},
Expected: map[bool]update.ControllerResult{
true: {}, // Errors.
false: {}, // Errors.
},
},
{
Name: "no changes",
Spec: []update.ContainerUpdate{
{
Container: helloContainer,
Current: newHwRef, // mismatch
Target: newHwRef,
},
},
Expected: map[bool]update.ControllerResult{
true: {},
false: {},
},
},
} {
specs := update.ContainerSpecs{
ContainerSpecs: map[flux.ResourceID][]update.ContainerUpdate{hwSvcID: tst.Spec},
Kind: update.ReleaseKindExecute,
}

for _, ignoreMismatches := range []bool{true, false} {
name := tst.Name
if ignoreMismatches {
name += " (SkipMismatches)"
}
specs.SkipMismatches = ignoreMismatches
results, err := Release(ctx, specs, log.NewNopLogger())
if tst.Expected[ignoreMismatches].Status != "" {
assert.Equal(t, tst.Expected[ignoreMismatches], results[hwSvcID], name)
assert.Equal(t, tst.Commit[ignoreMismatches], specs.CommitMessage(results), name)
} else {
assert.Error(t, err, name)
}
}
}
}

func testRelease(t *testing.T, ctx *ReleaseContext, spec update.ReleaseSpec, expected update.Result) {
results, err := Release(ctx, spec, log.NewNopLogger())
if err != nil {
Expand Down
Loading

0 comments on commit 8c6b459

Please sign in to comment.