Skip to content

Commit

Permalink
Allow version upgrades for specific containers and initContainers (#425)
Browse files Browse the repository at this point in the history
* Allow version upgrades for specific containers and initContainers

* improve test

* lint

* better check for changes

* Use go 1.22 for CI

* Fix manifests
  • Loading branch information
agouin authored Jun 18, 2024
1 parent 8eb22e7 commit 3c25c3d
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.2'
go-version: '>=1.22'
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
Expand All @@ -29,6 +29,6 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.2'
go-version: '>=1.22'
- name: unit tests
run: make test
7 changes: 3 additions & 4 deletions .github/workflows/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.20.2'
go-version: '>=1.22'
- run: make generate manifests

- uses: CatChen/check-git-status-action@v1
with:
fail-if-not-clean: true
- name: Ensure no changes
run: git diff --exit-code
8 changes: 8 additions & 0 deletions api/v1/cosmosfullnode_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,14 @@ type ChainVersion struct {
// The docker image for this version in "repository:tag" format. E.g. busybox:latest.
Image string `json:"image"`

// Version overrides for initContainers of the fullnode/sentry pods.
// +optional
InitContainers map[string]string `json:"initContainers"`

// Version overrides for containers of the fullnode/sentry pods.
// +optional
Containers map[string]string `json:"containers"`

// Determines if the node should forcefully halt at the upgrade height.
// +optional
SetHaltHeight bool `json:"setHaltHeight,omitempty"`
Expand Down
18 changes: 17 additions & 1 deletion api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions config/crd/bases/cosmos.strange.love_cosmosfullnodes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ spec:
If not provided, the operator will not upgrade the chain, and will use the image specified in the pod spec.
items:
properties:
containers:
additionalProperties:
type: string
description: Version overrides for containers of the fullnode/sentry
pods.
type: object
height:
description: The block height when this version should be
applied.
Expand All @@ -361,6 +367,12 @@ spec:
description: The docker image for this version in "repository:tag"
format. E.g. busybox:latest.
type: string
initContainers:
additionalProperties:
type: string
description: Version overrides for initContainers of the
fullnode/sentry pods.
type: object
setHaltHeight:
description: Determines if the node should forcefully halt
at the upgrade height.
Expand Down
23 changes: 23 additions & 0 deletions internal/fullnode/build_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,36 @@ func BuildPods(crd *cosmosv1.CosmosFullNode, cksums ConfigChecksums) ([]diff.Res
return pods, nil
}

func setChainContainerImages(pod *corev1.Pod, v *cosmosv1.ChainVersion) {
setChainContainerImage(pod, v.Image)

for name, image := range v.InitContainers {
for i := range pod.Spec.InitContainers {
if pod.Spec.InitContainers[i].Name == name {
pod.Spec.InitContainers[i].Image = image
break
}
}
}

for name, image := range v.Containers {
for i := range pod.Spec.Containers {
if pod.Spec.Containers[i].Name == name {
pod.Spec.Containers[i].Image = image
break
}
}
}
}

func setChainContainerImage(pod *corev1.Pod, image string) {
for i := range pod.Spec.Containers {
if pod.Spec.Containers[i].Name == mainContainer {
pod.Spec.Containers[i].Image = image
break
}
}

for i := range pod.Spec.InitContainers {
if pod.Spec.InitContainers[i].Name == chainInitContainer {
pod.Spec.InitContainers[i].Image = image
Expand Down
22 changes: 12 additions & 10 deletions internal/fullnode/pod_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,28 @@ func podReadinessProbes(crd *cosmosv1.CosmosFullNode) []*corev1.Probe {
func (b PodBuilder) Build() (*corev1.Pod, error) {
pod := b.pod.DeepCopy()

if err := kube.ApplyStrategicMergePatch(pod, podPatch(b.crd)); err != nil {
return nil, err
}

if len(b.crd.Spec.ChainSpec.Versions) > 0 {
instanceHeight := uint64(0)
if height, ok := b.crd.Status.Height[pod.Name]; ok {
instanceHeight = height
}
var image string
for _, version := range b.crd.Spec.ChainSpec.Versions {
if instanceHeight < version.UpgradeHeight {
var vrs *cosmosv1.ChainVersion
for _, v := range b.crd.Spec.ChainSpec.Versions {
v := v
if instanceHeight < v.UpgradeHeight {
break
}
image = version.Image
vrs = &v
}
if image != "" {
setChainContainerImage(pod, image)
if vrs != nil {
setChainContainerImages(pod, vrs)
}
}

if o, ok := b.crd.Spec.InstanceOverrides[pod.Name]; ok {
if o.DisableStrategy != nil {
return nil, nil
Expand All @@ -206,10 +212,6 @@ func (b PodBuilder) Build() (*corev1.Pod, error) {
}
}

if err := kube.ApplyStrategicMergePatch(pod, podPatch(b.crd)); err != nil {
return nil, err
}

kube.NormalizeMetadata(&pod.ObjectMeta)
return pod, nil
}
Expand Down
89 changes: 81 additions & 8 deletions internal/fullnode/pod_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,27 +606,100 @@ gaiad start --home /home/operator/cosmos`
}
crd.Spec.ChainSpec.Versions = []cosmosv1.ChainVersion{
{
UpgradeHeight: 1,
UpgradeHeight: 0,
Image: "image:v1.0.0",
},
{
UpgradeHeight: 100,
Image: "image:v2.0.0",
},
{
UpgradeHeight: 300,
Image: "image:v3.0.0",
InitContainers: map[string]string{
"chain-init": "chain-init:v3.0.0",
"new-init": "new-init:v3.0.0",
},
Containers: map[string]string{
"new-sidecar": "new-sidecar:v3.0.0",
},
},
{
UpgradeHeight: 400,
Image: "image:v4.0.0",
},
}

crd.Status.Height = map[string]uint64{
"osmosis-0": 1,
"osmosis-1": 150,
"osmosis-2": 300,
}

builder := NewPodBuilder(&crd)
pod, err := builder.WithOrdinal(0).Build()

pod0, err := builder.WithOrdinal(0).Build()
require.NoError(t, err)

containers := lo.SliceToMap(pod.Spec.Containers, func(c corev1.Container) (string, corev1.Container) { return c.Name, c })
containers := lo.SliceToMap(pod0.Spec.Containers, func(c corev1.Container) (string, corev1.Container) { return c.Name, c })
require.ElementsMatch(t, []string{"node", "new-sidecar", "healthcheck", "version-check-interval"}, lo.Keys(containers))
})

test.HasTypeLabel(t, func(crd cosmosv1.CosmosFullNode) []map[string]string {
builder := NewPodBuilder(&crd)
pod, _ := builder.WithOrdinal(5).Build()
return []map[string]string{pod.Labels}
initContainers := lo.SliceToMap(pod0.Spec.InitContainers, func(c corev1.Container) (string, corev1.Container) { return c.Name, c })
require.ElementsMatch(t, []string{"chain-init", "new-init", "genesis-init", "addrbook-init", "config-merge", "version-check", "clean-init"}, lo.Keys(initContainers))

require.Equal(t, "osmosis-0", pod0.Name)

require.Equal(t, "node", pod0.Spec.Containers[0].Name)
require.Equal(t, "image:v1.0.0", pod0.Spec.Containers[0].Image)

require.Equal(t, "chain-init", pod0.Spec.InitContainers[1].Name)
require.Equal(t, "image:v1.0.0", pod0.Spec.InitContainers[1].Image)

pod1, err := builder.WithOrdinal(1).Build()
require.NoError(t, err)

require.Equal(t, "osmosis-1", pod1.Name)

require.Equal(t, "node", pod1.Spec.Containers[0].Name)
require.Equal(t, "image:v2.0.0", pod1.Spec.Containers[0].Image)

require.Equal(t, "chain-init", pod1.Spec.InitContainers[1].Name)
require.Equal(t, "image:v2.0.0", pod1.Spec.InitContainers[1].Image)

pod2, err := builder.WithOrdinal(2).Build()
require.NoError(t, err)

require.Equal(t, "osmosis-2", pod2.Name)

require.Equal(t, "node", pod2.Spec.Containers[0].Name)
require.Equal(t, "image:v3.0.0", pod2.Spec.Containers[0].Image)

require.Equal(t, "new-sidecar", pod2.Spec.Containers[1].Name)
require.Equal(t, "new-sidecar:v3.0.0", pod2.Spec.Containers[1].Image)

require.Equal(t, "chain-init", pod2.Spec.InitContainers[1].Name)
require.Equal(t, "chain-init:v3.0.0", pod2.Spec.InitContainers[1].Image)

require.Equal(t, "new-init", pod2.Spec.InitContainers[2].Name)
require.Equal(t, "new-init:v3.0.0", pod2.Spec.InitContainers[2].Image)

crd.Status.Height["osmosis-2"] = 400
pod2, err = builder.WithOrdinal(2).Build()
require.NoError(t, err)

require.Equal(t, "osmosis-2", pod2.Name)

require.Equal(t, "node", pod2.Spec.Containers[0].Name)
require.Equal(t, "image:v4.0.0", pod2.Spec.Containers[0].Image)

require.Equal(t, "new-sidecar", pod2.Spec.Containers[1].Name)
require.Equal(t, "new-sidecar:latest", pod2.Spec.Containers[1].Image)

require.Equal(t, "chain-init", pod2.Spec.InitContainers[1].Name)
require.Equal(t, "image:v4.0.0", pod2.Spec.InitContainers[1].Image)

require.Equal(t, "new-init", pod2.Spec.InitContainers[2].Name)
require.Equal(t, "new-init:latest", pod2.Spec.InitContainers[2].Image)
})
}

Expand Down

0 comments on commit 3c25c3d

Please sign in to comment.