Skip to content

Commit 8ed61f9

Browse files
author
oluwole.fadeyi
committed
Add architecture and os checks when fetching tags
This updates the controller to check the architecture of the cluster nodes and select only tags for the current architecture. Signed-off-by: oluwole.fadeyi <[email protected]>
1 parent 9bfbe1d commit 8ed61f9

File tree

21 files changed

+637
-91
lines changed

21 files changed

+637
-91
lines changed

deploy/charts/version-checker/templates/clusterrole.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rules:
99
- ""
1010
resources:
1111
- "pods"
12+
- "nodes"
1213
verbs:
1314
- "get"
1415
- "list"

deploy/yaml/deploy.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ metadata:
6262
name: version-checker
6363
rules:
6464
- apiGroups: [""]
65-
resources: ["pods"]
65+
resources: ["pods","nodes"]
6666
verbs: ["get", "watch", "list"]
6767
---
6868
kind: ClusterRoleBinding

pkg/api/types.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ type Options struct {
5858
PinPatch *int64 `json:"pin-patch,omitempty"`
5959

6060
RegexMatcher *regexp.Regexp `json:"-"`
61+
62+
// Architecture and OS to search for
63+
Architecture *Architecture `json:"pin-architecture,omitempty"`
64+
OS *OS `json:"pin-os,omitempty"`
6165
}
6266

6367
// ImageTag describes a container image tag.
6468
type ImageTag struct {
65-
Tag string `json:"tag"`
66-
SHA string `json:"sha"`
67-
Timestamp time.Time `json:"timestamp"`
68-
Architecture string `json:"architecture,omitempty"`
69-
OS string `json:"os,omitempty"`
69+
Tag string `json:"tag"`
70+
SHA string `json:"sha"`
71+
Timestamp time.Time `json:"timestamp"`
72+
Architecture Architecture `json:"architecture,omitempty"`
73+
OS OS `json:"os,omitempty"`
7074
}
75+
76+
type Architecture string
77+
type OS string
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package architecture
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"sync"
7+
8+
corev1 "k8s.io/api/core/v1"
9+
10+
"github.com/jetstack/version-checker/pkg/api"
11+
)
12+
13+
// NodeMetadata metadata about a particular node
14+
type nodeMetadata struct {
15+
OS api.OS
16+
Architecture api.Architecture
17+
}
18+
19+
type NodeMap struct {
20+
mu sync.RWMutex
21+
nodes map[string]*nodeMetadata
22+
}
23+
24+
func New() *NodeMap {
25+
// might need to pass an initial map
26+
return &NodeMap{
27+
nodes: make(map[string]*nodeMetadata),
28+
}
29+
}
30+
31+
func (m *NodeMap) GetArchitecture(nodeName string) (*nodeMetadata, bool) {
32+
m.mu.RLock()
33+
defer m.mu.RUnlock()
34+
35+
meta, ok := m.nodes[nodeName]
36+
return meta, ok
37+
}
38+
39+
func (m *NodeMap) Add(node *corev1.Node) error {
40+
m.mu.Lock()
41+
defer m.mu.Unlock()
42+
if node == nil {
43+
return errors.New("passed node is nil")
44+
}
45+
46+
arch, ok := node.Labels[corev1.LabelArchStable]
47+
if !ok {
48+
return fmt.Errorf("missing %q label on node %q", corev1.LabelArchStable, node.Name)
49+
}
50+
51+
os, ok := node.Labels[corev1.LabelOSStable]
52+
if !ok {
53+
return fmt.Errorf("missing %q label on node %q", corev1.LabelOSStable, node.Name)
54+
}
55+
56+
m.nodes[node.Name] = &nodeMetadata{
57+
OS: api.OS(os),
58+
Architecture: api.Architecture(arch),
59+
}
60+
return nil
61+
}
62+
63+
func (m *NodeMap) Delete(nodeName string) {
64+
m.mu.Lock()
65+
defer m.mu.Unlock()
66+
delete(m.nodes, nodeName)
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package architecture
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
"testing"
8+
9+
corev1 "k8s.io/api/core/v1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
12+
utils "github.com/jetstack/version-checker/pkg/checker/internal/test"
13+
)
14+
15+
func TestAdd(t *testing.T) {
16+
tests := map[string]struct {
17+
input []*corev1.Node
18+
expOperationOutput error
19+
expResult map[string]*nodeMetadata
20+
}{
21+
"update valid node": {
22+
input: []*corev1.Node{
23+
utils.CreateNode("node1", utils.ArchAMD64, utils.OSLinux),
24+
utils.CreateNode("node1", utils.ArchARM, utils.OSLinux),
25+
},
26+
expOperationOutput: nil,
27+
expResult: map[string]*nodeMetadata{
28+
"node1": &nodeMetadata{
29+
Architecture: utils.ArchARM,
30+
OS: utils.OSLinux,
31+
},
32+
},
33+
},
34+
"add valid node": {
35+
input: []*corev1.Node{
36+
utils.CreateNode("node1", utils.ArchAMD64, utils.OSLinux),
37+
},
38+
expOperationOutput: nil,
39+
expResult: map[string]*nodeMetadata{
40+
"node1": &nodeMetadata{
41+
Architecture: utils.ArchAMD64,
42+
OS: utils.OSLinux,
43+
},
44+
},
45+
},
46+
"add nil node": {
47+
input: nil,
48+
expOperationOutput: errors.New("passed node is nil"),
49+
expResult: make(map[string]*nodeMetadata),
50+
},
51+
"add node with no architecture label": {
52+
input: []*corev1.Node{
53+
&corev1.Node{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Name: "node1",
56+
Labels: map[string]string{
57+
corev1.LabelOSStable: "linux",
58+
},
59+
},
60+
},
61+
},
62+
expOperationOutput: fmt.Errorf("missing \"kubernetes.io/arch\" label on node \"node1\""),
63+
expResult: make(map[string]*nodeMetadata),
64+
},
65+
"add node with no os label": {
66+
input: []*corev1.Node{
67+
&corev1.Node{
68+
ObjectMeta: metav1.ObjectMeta{
69+
Name: "node1",
70+
Labels: map[string]string{
71+
corev1.LabelArchStable: "amd64",
72+
},
73+
},
74+
},
75+
},
76+
expOperationOutput: fmt.Errorf("missing %q label on node \"node1\"", corev1.LabelOSStable),
77+
expResult: make(map[string]*nodeMetadata),
78+
},
79+
}
80+
81+
for name, test := range tests {
82+
t.Run(name, func(t *testing.T) {
83+
var nodeMap = New()
84+
for _, node := range test.input {
85+
err := nodeMap.Add(node)
86+
if !reflect.DeepEqual(test.expOperationOutput, err) {
87+
t.Errorf("got unexpected operation error, exp=%s act=%s", test.expOperationOutput, err)
88+
}
89+
}
90+
if !reflect.DeepEqual(nodeMap.nodes, test.expResult) {
91+
t.Errorf("got unexpected result, exp=%#+v got=%#+v", test.expResult, nodeMap.nodes)
92+
}
93+
94+
})
95+
}
96+
}
97+
98+
func TestDelete(t *testing.T) {
99+
tests := map[string]struct {
100+
mapInitialState []*corev1.Node
101+
input string
102+
expOperationOutput error
103+
expResult map[string]*nodeMetadata
104+
}{
105+
"delete valid node": {
106+
mapInitialState: []*corev1.Node{
107+
utils.CreateNode("node1", utils.ArchAMD64, utils.OSLinux),
108+
utils.CreateNode("node2", utils.ArchARM, utils.OSLinux),
109+
},
110+
input: "node1",
111+
expOperationOutput: nil,
112+
expResult: map[string]*nodeMetadata{
113+
"node2": &nodeMetadata{
114+
OS: utils.OSLinux,
115+
Architecture: utils.ArchARM,
116+
},
117+
},
118+
},
119+
"delete empty node name": {
120+
mapInitialState: []*corev1.Node{
121+
utils.CreateNode("node1", utils.ArchAMD64, utils.OSLinux),
122+
utils.CreateNode("node2", utils.ArchARM, utils.OSLinux),
123+
},
124+
input: "",
125+
expOperationOutput: nil,
126+
expResult: map[string]*nodeMetadata{
127+
"node1": &nodeMetadata{
128+
OS: utils.OSLinux,
129+
Architecture: utils.ArchAMD64,
130+
},
131+
"node2": &nodeMetadata{
132+
OS: utils.OSLinux,
133+
Architecture: utils.ArchARM,
134+
},
135+
},
136+
},
137+
}
138+
139+
for name, test := range tests {
140+
t.Run(name, func(t *testing.T) {
141+
var nodeMap = New()
142+
for _, node := range test.mapInitialState {
143+
err := nodeMap.Add(node)
144+
if err != nil {
145+
t.Errorf("unexpected error: %s", err)
146+
}
147+
}
148+
149+
nodeMap.Delete(test.input)
150+
if !reflect.DeepEqual(nodeMap.nodes, test.expResult) {
151+
t.Errorf("got unexpected result, exp=%#+v got=%#+v", test.expResult, nodeMap.nodes)
152+
}
153+
154+
})
155+
}
156+
}

pkg/controller/checker/checker.go renamed to pkg/checker/checker.go

+30-3
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,61 @@ package checker
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strings"
78

89
corev1 "k8s.io/api/core/v1"
910

1011
"github.com/jetstack/version-checker/pkg/api"
11-
"github.com/jetstack/version-checker/pkg/controller/search"
12-
"github.com/jetstack/version-checker/pkg/version/semver"
12+
"github.com/jetstack/version-checker/pkg/checker/architecture"
13+
"github.com/jetstack/version-checker/pkg/checker/search"
14+
"github.com/jetstack/version-checker/pkg/checker/version/semver"
1315
"github.com/sirupsen/logrus"
1416
)
1517

1618
type Checker struct {
1719
search search.Searcher
20+
nodes *architecture.NodeMap
1821
}
1922

2023
type Result struct {
2124
CurrentVersion string
2225
LatestVersion string
2326
IsLatest bool
2427
ImageURL string
28+
OS api.OS
29+
Architecture api.Architecture
2530
}
2631

27-
func New(search search.Searcher) *Checker {
32+
const (
33+
nodeMissingMetadata = "error fetching node architecture"
34+
)
35+
36+
func New(search search.Searcher, nodesArchInfo *architecture.NodeMap) *Checker {
2837
return &Checker{
2938
search: search,
39+
nodes: nodesArchInfo,
3040
}
3141
}
3242

3343
// Container will return the result of the given container's current version, compared to the latest upstream
3444
func (c *Checker) Container(ctx context.Context, log *logrus.Entry,
3545
pod *corev1.Pod, container *corev1.Container, opts *api.Options) (*Result, error) {
3646

47+
if opts == nil {
48+
// create new options if nil
49+
opts = new(api.Options)
50+
}
51+
// Get information about the pod node architecture
52+
arch, ok := c.nodes.GetArchitecture(pod.Spec.NodeName)
53+
if ok {
54+
opts.OS = &arch.OS
55+
opts.Architecture = &arch.Architecture
56+
} else {
57+
return nil, errors.New(nodeMissingMetadata)
58+
}
59+
3760
// If the container image SHA status is not ready yet, exit early
3861
statusSHA := containerStatusImageSHA(pod, container.Name)
3962
if len(statusSHA) == 0 {
@@ -90,6 +113,8 @@ func (c *Checker) Container(ctx context.Context, log *logrus.Entry,
90113
LatestVersion: latestVersion,
91114
IsLatest: isLatest,
92115
ImageURL: imageURL,
116+
OS: latestImage.OS,
117+
Architecture: latestImage.Architecture,
93118
}, nil
94119
}
95120

@@ -161,6 +186,8 @@ func (c *Checker) isLatestSHA(ctx context.Context, imageURL, currentSHA string,
161186
LatestVersion: latestVersion,
162187
IsLatest: isLatest,
163188
ImageURL: imageURL,
189+
OS: latestImage.OS,
190+
Architecture: latestImage.Architecture,
164191
}, nil
165192
}
166193

0 commit comments

Comments
 (0)