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

feat: Add spec.ordinals.start. #448

Merged
merged 44 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3d50513
feat: Add staringOrdinal
vimystic Nov 11, 2024
212e049
fix lint error
vimystic Nov 11, 2024
6c4f498
update build pods test to reflect startingordinals usage
vimystic Nov 12, 2024
8c55a1f
add retries for trivy to overcome the throttling
vimystic Nov 14, 2024
9dfdca0
Merge branch 'main' into vimystic/feat/445-add-starting-ordinals
vimystic Nov 18, 2024
62db355
Update: reorg so that we use a similar structure to stateful set inst…
vimystic Nov 19, 2024
b9ccbfa
optimize & use starting ordinal for pvcs too
vimystic Nov 20, 2024
804e076
Update to use starting ordinal in appropriate files
vimystic Nov 20, 2024
75dcd8a
Optimize tests
vimystic Nov 20, 2024
bfe1a51
fix test
vimystic Nov 20, 2024
e088656
fix test
vimystic Nov 20, 2024
0a52ff4
Use Ordinal within spec (duh))
vimystic Nov 20, 2024
91bded5
Remove redundant parameters
vimystic Nov 20, 2024
6bb42dd
Note: this is incorrect since PVC allocations start from 0, only the …
vimystic Nov 20, 2024
a8cc9e0
Attempt to find root cause of mapping issue crash
vimystic Nov 20, 2024
be87c7d
Adding spec.ordinal.start usage back to configmap_builder.go
vimystic Nov 20, 2024
e62a9a9
Adding spec.ordinal.start usage back to node_keybuilder.go
vimystic Nov 20, 2024
5905c9b
Adding spec.ordinal.start usage back to peer_collector.go
vimystic Nov 20, 2024
3b27cf1
Add spec.ordinal.start usage back to service_builder.go
vimystic Nov 20, 2024
65c1d33
service_builder.go
vimystic Nov 20, 2024
51747d4
Update pvc to use name from ordinals , but resource from 0
vimystic Nov 20, 2024
df5a242
revert previous commit
vimystic Nov 20, 2024
8be6661
Update pvc_contro.go to use ordinal start
vimystic Nov 20, 2024
dc23a15
name/label update
vimystic Nov 20, 2024
060d6cf
Attempting range check
vimystic Nov 20, 2024
4a11667
Revert previous
vimystic Nov 20, 2024
3c8739e
Update configmap , key peer and service builders to use correct ordin…
vimystic Nov 20, 2024
fcbcf8a
Merge branch 'main' into vimystic/feat/445-add-starting-ordinals
vimystic Nov 21, 2024
b124b07
Allow instanceoverrides for pvcs (example: changing storage) to wor…
vimystic Nov 27, 2024
ae29f8a
Use 'Ordinals' instead of 'Ordinals' as per review
vimystic Dec 3, 2024
212fd65
Update autogenerated yaml file with 'ordinals'
vimystic Dec 3, 2024
8a24b65
match the description for start with that of the stateful set
vimystic Dec 4, 2024
b1163e0
add happy path test with a non 0 starting ordinal
vimystic Dec 4, 2024
f27d4a3
add node keybuilder test
vimystic Dec 4, 2024
94afe93
test ofr peer collector
vimystic Dec 4, 2024
57019ed
pvc and service builder tests
vimystic Dec 4, 2024
e0afa9b
Ensure correct tags for goloang json
vimystic Dec 4, 2024
4595139
make for loops consistent across all builders
vimystic Dec 4, 2024
79e5c85
fix service builder loop. seperate index from ordinal name since both…
vimystic Dec 4, 2024
c8ba8ce
potential lint error fix (new lines between functions)
vimystic Dec 4, 2024
0bc73a8
remove unnecessary new line
vimystic Dec 4, 2024
ffdaafa
Merge branch 'main' into vimystic/feat/445-add-starting-ordinals
vimystic Dec 4, 2024
2749a79
Update the Ordinals description to match language similar to be simil…
vimystic Dec 5, 2024
1c27636
update with no starting ordinal defined. i.e: Default value 0
vimystic Dec 12, 2024
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
19 changes: 11 additions & 8 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ jobs:
build-args: VERSION=${{ steps.meta.outputs.version }}

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ fromJSON(steps.meta.outputs.json).tags[0] }}'
format: 'table'
exit-code: '1'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
run: |
for i in {1..3}; do
if docker run --rm aquasec/trivy:latest image --exit-code 0 --severity CRITICAL,HIGH --ignore-unfixed ${{ fromJSON(steps.meta.outputs.json).tags[0] }}; then
break
elif [ $i -lt 3 ]; then
echo "Retrying in 60 seconds..."
sleep 60
else
exit 1
fi
done
14 changes: 14 additions & 0 deletions api/v1/cosmosfullnode_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,25 @@ const CosmosFullNodeController = "CosmosFullNode"
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

type Ordinals struct {
// start is the number representing the first replica's index. It may be used to number replicas from an alternate index (eg: 1-indexed) over the default 0-indexed names,
// or to orchestrate progressive movement of replicas from one CosmosFullnode spec to another. If set, replica indices will be in the range:
// [.spec.ordinals.start, .spec.ordinals.start + .spec.replicas).
// If unset, defaults to 0. Replica indices will be in the range:
// [0, .spec.replicas).
// +kubebuilder:validation:Minimum:=0
Start int32 `json:"start,omitempty"`
vimystic marked this conversation as resolved.
Show resolved Hide resolved
}

// FullNodeSpec defines the desired state of CosmosFullNode
type FullNodeSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Ordinals controls the numbering of replica indices in a CosmosFullnode spec.
// The default ordinals behavior assigns a "0" index to the first replica and increments the index by one for each additional replica requested.
Ordinals Ordinals `json:"ordinals,omitempty"`

// Number of replicas to create.
// Individual replicas have a consistent identity.
// +kubebuilder:validation:Minimum:=0
Expand Down
16 changes: 16 additions & 0 deletions api/v1/zz_generated.deepcopy.go

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

16 changes: 16 additions & 0 deletions config/crd/bases/cosmos.strange.love_cosmosfullnodes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,22 @@ spec:
Example: cosmos-1
Used for debugging.
type: object
ordinals:
description: |-
Ordinals controls the numbering of replica indices in a CosmosFullnode spec.
The default ordinals behavior assigns a "0" index to the first replica and increments the index by one for each additional replica requested.
properties:
start:
description: |-
start is the number representing the first replica's index. It may be used to number replicas from an alternate index (eg: 1-indexed) over the default 0-indexed names,
or to orchestrate progressive movement of replicas from one CosmosFullnode spec to another. If set, replica indices will be in the range:
[.spec.ordinals.start, .spec.ordinals.start + .spec.replicas).
If unset, defaults to 0. Replica indices will be in the range:
[0, .spec.replicas).
format: int32
minimum: 0
type: integer
type: object
podTemplate:
description: |-
Template applied to all pods.
Expand Down
2 changes: 1 addition & 1 deletion internal/fullnode/build_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func BuildPods(crd *cosmosv1.CosmosFullNode, cksums ConfigChecksums) ([]diff.Res
pods []diff.Resource[*corev1.Pod]
)
candidates := podCandidates(crd)
for i := int32(0); i < crd.Spec.Replicas; i++ {
for i := crd.Spec.Ordinals.Start; i < crd.Spec.Ordinals.Start+crd.Spec.Replicas; i++ {
pod, err := builder.WithOrdinal(i).Build()
if err != nil {
return nil, err
Expand Down
137 changes: 129 additions & 8 deletions internal/fullnode/build_pods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,127 @@ import (
func TestBuildPods(t *testing.T) {
t.Parallel()

t.Run("happy path", func(t *testing.T) {
t.Run("happy path with starting ordinal", func(t *testing.T) {
crd := &cosmosv1.CosmosFullNode{
ObjectMeta: metav1.ObjectMeta{
Name: "agoric",
Namespace: "test",
},
Spec: cosmosv1.FullNodeSpec{
Replicas: 5,
ChainSpec: cosmosv1.ChainSpec{Network: "devnet"},
PodTemplate: cosmosv1.PodSpec{
Image: "busybox:latest",
},
InstanceOverrides: nil,
Ordinals: cosmosv1.Ordinals{
Start: 2,
},
},
}

cksums := make(ConfigChecksums)
for i := 0; i < int(crd.Spec.Replicas); i++ {
cksums[client.ObjectKey{Namespace: crd.Namespace, Name: fmt.Sprintf("agoric-%d", i+int(crd.Spec.Ordinals.Start))}] = strconv.Itoa(i + int(crd.Spec.Ordinals.Start))
}

pods, err := BuildPods(crd, cksums)
require.NoError(t, err)
require.Equal(t, 5, len(pods))

for i, r := range pods {
expectedOrdinal := crd.Spec.Ordinals.Start + int32(i)
require.Equal(t, int64(expectedOrdinal), r.Ordinal(), i)
require.NotEmpty(t, r.Revision(), i)
require.Equal(t, strconv.Itoa(int(expectedOrdinal)), r.Object().Annotations["cosmos.strange.love/config-checksum"])
}

want := lo.Map([]int{2, 3, 4, 5, 6}, func(i int, _ int) string {
return fmt.Sprintf("agoric-%d", i)
})
got := lo.Map(pods, func(pod diff.Resource[*corev1.Pod], _ int) string { return pod.Object().Name })
require.Equal(t, want, got)

pod, err := NewPodBuilder(crd).WithOrdinal(crd.Spec.Ordinals.Start).Build()
require.NoError(t, err)
require.Equal(t, pod.Spec, pods[0].Object().Spec)
})

t.Run("instance overrides with starting ordinal", func(t *testing.T) {
const (
image = "agoric:latest"
overrideImage = "some_image:custom"
overridePod = "agoric-7"
)
crd := &cosmosv1.CosmosFullNode{
ObjectMeta: metav1.ObjectMeta{
Name: "agoric",
},
Spec: cosmosv1.FullNodeSpec{
Replicas: 6,
PodTemplate: cosmosv1.PodSpec{
Image: image,
},
InstanceOverrides: map[string]cosmosv1.InstanceOverridesSpec{
"agoric-4": {DisableStrategy: ptr(cosmosv1.DisablePod)},
"agoric-6": {DisableStrategy: ptr(cosmosv1.DisableAll)},
overridePod: {Image: overrideImage},
},
Ordinals: cosmosv1.Ordinals{
Start: 2,
},
},
}

pods, err := BuildPods(crd, nil)
require.NoError(t, err)
require.Equal(t, 4, len(pods))

want := lo.Map([]int{2, 3, 5, 7}, func(i int, _ int) string {
return fmt.Sprintf("agoric-%d", i)
})
got := lo.Map(pods, func(pod diff.Resource[*corev1.Pod], _ int) string { return pod.Object().Name })
require.Equal(t, want, got)
for _, pod := range pods {
image := pod.Object().Spec.Containers[0].Image
if pod.Object().Name == overridePod {
require.Equal(t, overrideImage, image)
} else {
require.Equal(t, image, image)
}
}
})

t.Run("scheduled volume snapshot pod candidate with starting ordinal", func(t *testing.T) {
crd := &cosmosv1.CosmosFullNode{
ObjectMeta: metav1.ObjectMeta{
Name: "agoric",
},
Spec: cosmosv1.FullNodeSpec{
Replicas: 6,
Ordinals: cosmosv1.Ordinals{Start: 2},
},
Status: cosmosv1.FullNodeStatus{
ScheduledSnapshotStatus: map[string]cosmosv1.FullNodeSnapshotStatus{
"some.scheduled.snapshot.1": {PodCandidate: "agoric-3"},
"some.scheduled.snapshot.2": {PodCandidate: "agoric-4"},
"some.scheduled.snapshot.ignored": {PodCandidate: "agoric-99"},
},
},
}

pods, err := BuildPods(crd, nil)
require.NoError(t, err)
require.Equal(t, 4, len(pods))

want := lo.Map([]int{2, 5, 6, 7}, func(i int, _ int) string {
return fmt.Sprintf("agoric-%d", i)
})
got := lo.Map(pods, func(pod diff.Resource[*corev1.Pod], _ int) string { return pod.Object().Name })
require.Equal(t, want, got)
})

t.Run("happy path without starting ordinal", func(t *testing.T) {
crd := &cosmosv1.CosmosFullNode{
ObjectMeta: metav1.ObjectMeta{
Name: "agoric",
Expand All @@ -35,31 +155,32 @@ func TestBuildPods(t *testing.T) {

cksums := make(ConfigChecksums)
for i := 0; i < int(crd.Spec.Replicas); i++ {
cksums[client.ObjectKey{Namespace: crd.Namespace, Name: fmt.Sprintf("agoric-%d", i)}] = strconv.Itoa(i)
cksums[client.ObjectKey{Namespace: crd.Namespace, Name: fmt.Sprintf("agoric-%d", i+int(crd.Spec.Ordinals.Start))}] = strconv.Itoa(i + int(crd.Spec.Ordinals.Start))
}

pods, err := BuildPods(crd, cksums)
require.NoError(t, err)
require.Equal(t, 5, len(pods))

for i, r := range pods {
require.Equal(t, int64(i), r.Ordinal(), i)
expectedOrdinal := crd.Spec.Ordinals.Start + int32(i)
require.Equal(t, int64(expectedOrdinal), r.Ordinal(), i)
require.NotEmpty(t, r.Revision(), i)
require.Equal(t, strconv.Itoa(i), r.Object().Annotations["cosmos.strange.love/config-checksum"])
require.Equal(t, strconv.Itoa(int(expectedOrdinal)), r.Object().Annotations["cosmos.strange.love/config-checksum"])
}

want := lo.Map([]int{0, 1, 2, 3, 4}, func(_ int, i int) string {
want := lo.Map([]int{0, 1, 2, 3, 4}, func(i int, _ int) string {
return fmt.Sprintf("agoric-%d", i)
})
got := lo.Map(pods, func(pod diff.Resource[*corev1.Pod], _ int) string { return pod.Object().Name })
require.Equal(t, want, got)

pod, err := NewPodBuilder(crd).WithOrdinal(0).Build()
pod, err := NewPodBuilder(crd).WithOrdinal(crd.Spec.Ordinals.Start).Build()
require.NoError(t, err)
require.Equal(t, pod.Spec, pods[0].Object().Spec)
})

t.Run("instance overrides", func(t *testing.T) {
t.Run("instance overrides without starting ordinal", func(t *testing.T) {
const (
image = "agoric:latest"
overrideImage = "some_image:custom"
Expand Down Expand Up @@ -101,7 +222,7 @@ func TestBuildPods(t *testing.T) {
}
})

t.Run("scheduled volume snapshot pod candidate", func(t *testing.T) {
t.Run("scheduled volume snapshot pod candidate without starting ordinal", func(t *testing.T) {
crd := &cosmosv1.CosmosFullNode{
ObjectMeta: metav1.ObjectMeta{
Name: "agoric",
Expand Down
7 changes: 4 additions & 3 deletions internal/fullnode/configmap_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ const (
func BuildConfigMaps(crd *cosmosv1.CosmosFullNode, peers Peers) ([]diff.Resource[*corev1.ConfigMap], error) {
var (
buf = bufPool.Get().(*bytes.Buffer)
cms = make([]diff.Resource[*corev1.ConfigMap], crd.Spec.Replicas)
cms = make([]diff.Resource[*corev1.ConfigMap], 0, crd.Spec.Replicas)
)
defer bufPool.Put(buf)
defer buf.Reset()
startOrdinal := crd.Spec.Ordinals.Start

for i := int32(0); i < crd.Spec.Replicas; i++ {
for i := startOrdinal; i < startOrdinal+crd.Spec.Replicas; i++ {
data := make(map[string]string)
instance := instanceName(crd, i)
if err := addConfigToml(buf, data, crd, instance, peers); err != nil {
Expand Down Expand Up @@ -75,7 +76,7 @@ func BuildConfigMaps(crd *cosmosv1.CosmosFullNode, peers Peers) ([]diff.Resource
)
cm.Data = data
kube.NormalizeMetadata(&cm.ObjectMeta)
cms[i] = diff.Adapt(&cm, i)
cms = append(cms, diff.Adapt(&cm, int(i-startOrdinal)))
}

return cms, nil
Expand Down
58 changes: 53 additions & 5 deletions internal/fullnode/configmap_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,25 @@ func TestBuildConfigMaps(t *testing.T) {
crd.Namespace = "test"
crd.Spec.PodTemplate.Image = "agoric:v6.0.0"
crd.Spec.ChainSpec.Network = "testnet"
//Default starting ordinal is 0

cms, err := BuildConfigMaps(&crd, nil)
require.NoError(t, err)
require.Equal(t, 3, len(cms))
require.Equal(t, crd.Spec.Replicas, int32(len(cms)))

require.Equal(t, int64(0), cms[0].Ordinal())
require.Equal(t, crd.Spec.Ordinals.Start, int32(cms[0].Ordinal())+crd.Spec.Ordinals.Start)
require.NotEmpty(t, cms[0].Revision())

cm := cms[0].Object()
require.Equal(t, "agoric-0", cm.Name)
require.Equal(t, fmt.Sprintf("agoric-%d", crd.Spec.Ordinals.Start), cm.Name)
require.Equal(t, "test", cm.Namespace)
require.Nil(t, cm.Immutable)

wantLabels := map[string]string{
"app.kubernetes.io/created-by": "cosmos-operator",
"app.kubernetes.io/component": "CosmosFullNode",
"app.kubernetes.io/name": "agoric",
"app.kubernetes.io/instance": "agoric-0",
"app.kubernetes.io/instance": fmt.Sprintf("%s-%d", crd.Name, crd.Spec.Ordinals.Start),
"app.kubernetes.io/version": "v6.0.0",
"cosmos.strange.love/network": "testnet",
"cosmos.strange.love/type": "FullNode",
Expand All @@ -69,7 +70,54 @@ func TestBuildConfigMaps(t *testing.T) {
require.Equal(t, wantLabels, cm.Labels)

cm = cms[1].Object()
require.Equal(t, "agoric-1", cm.Name)
require.Equal(t, fmt.Sprintf("%s-%d", crd.Name, crd.Spec.Ordinals.Start+1), cm.Name)

require.NotEmpty(t, cms[0].Object().Data)
require.Equal(t, cms[0].Object().Data, cms[1].Object().Data)

crd.Spec.Type = cosmosv1.FullNode
cms2, err := BuildConfigMaps(&crd, nil)

require.NoError(t, err)
require.Equal(t, cms, cms2)
})

t.Run("happy path with non 0 starting ordinal", func(t *testing.T) {
crd := defaultCRD()
crd.Spec.Replicas = 3
crd.Name = "agoric"
crd.Namespace = "test"
crd.Spec.PodTemplate.Image = "agoric:v6.0.0"
crd.Spec.ChainSpec.Network = "testnet"
crd.Spec.Ordinals.Start = 2

cms, err := BuildConfigMaps(&crd, nil)
require.NoError(t, err)
require.Equal(t, crd.Spec.Replicas, int32(len(cms)))

require.Equal(t, crd.Spec.Ordinals.Start, int32(cms[0].Ordinal())+crd.Spec.Ordinals.Start)
require.NotEmpty(t, cms[0].Revision())

cm := cms[0].Object()
require.Equal(t, fmt.Sprintf("agoric-%d", crd.Spec.Ordinals.Start), cm.Name)
require.Equal(t, "test", cm.Namespace)
require.Nil(t, cm.Immutable)

wantLabels := map[string]string{
"app.kubernetes.io/created-by": "cosmos-operator",
"app.kubernetes.io/component": "CosmosFullNode",
"app.kubernetes.io/name": "agoric",
"app.kubernetes.io/instance": fmt.Sprintf("%s-%d", crd.Name, crd.Spec.Ordinals.Start),
"app.kubernetes.io/version": "v6.0.0",
"cosmos.strange.love/network": "testnet",
"cosmos.strange.love/type": "FullNode",
}
require.Empty(t, cm.Annotations)

require.Equal(t, wantLabels, cm.Labels)

cm = cms[1].Object()
require.Equal(t, fmt.Sprintf("%s-%d", crd.Name, crd.Spec.Ordinals.Start+1), cm.Name)

require.NotEmpty(t, cms[0].Object().Data)
require.Equal(t, cms[0].Object().Data, cms[1].Object().Data)
Expand Down
Loading
Loading