Skip to content

Commit 4d5493a

Browse files
authored
Testing for the analyzer package + small refactoring (#246)
1 parent 938039f commit 4d5493a

File tree

4 files changed

+147
-36
lines changed

4 files changed

+147
-36
lines changed

pkg/analyzer/resources.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ SPDX-License-Identifier: Apache-2.0
77
package analyzer
88

99
import (
10-
"io"
10+
"bytes"
1111

1212
"k8s.io/apimachinery/pkg/util/yaml"
1313
)
1414

1515
const yamlParseBufferSize = 200
1616

17-
func parseResource[T interface{}](r io.Reader) *T {
18-
if r == nil {
17+
func parseResource[T interface{}](objDataBuf []byte) *T {
18+
reader := bytes.NewReader(objDataBuf)
19+
if reader == nil {
1920
return nil
2021
}
2122
var rc T
22-
err := yaml.NewYAMLOrJSONDecoder(r, yamlParseBufferSize).Decode(&rc)
23+
err := yaml.NewYAMLOrJSONDecoder(reader, yamlParseBufferSize).Decode(&rc)
2324
if err != nil {
2425
return nil
2526
}

pkg/analyzer/scan.go

+15-28
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ SPDX-License-Identifier: Apache-2.0
77
package analyzer
88

99
import (
10-
"bytes"
1110
"fmt"
1211
"net/url"
1312
"strconv"
@@ -32,27 +31,27 @@ func ScanK8sWorkloadObject(kind string, objDataBuf []byte) (*common.Resource, er
3231
resourceCtx.Resource.Kind = kind
3332
switch kind { // TODO: handle Pod
3433
case "ReplicaSet":
35-
obj := parseResource[appsv1.ReplicaSet](bytes.NewReader(objDataBuf))
34+
obj := parseResource[appsv1.ReplicaSet](objDataBuf)
3635
podSpecV1 = &obj.Spec.Template
3736
metaObj = obj
3837
case "ReplicationController":
39-
obj := parseResource[v1.ReplicationController](bytes.NewReader(objDataBuf))
38+
obj := parseResource[v1.ReplicationController](objDataBuf)
4039
podSpecV1 = obj.Spec.Template
4140
metaObj = obj
4241
case "Deployment":
43-
obj := parseResource[appsv1.Deployment](bytes.NewReader(objDataBuf))
42+
obj := parseResource[appsv1.Deployment](objDataBuf)
4443
podSpecV1 = &obj.Spec.Template
4544
metaObj = obj
4645
case "DaemonSet":
47-
obj := parseResource[appsv1.DaemonSet](bytes.NewReader(objDataBuf))
46+
obj := parseResource[appsv1.DaemonSet](objDataBuf)
4847
podSpecV1 = &obj.Spec.Template
4948
metaObj = obj
5049
case "StatefulSet":
51-
obj := parseResource[appsv1.StatefulSet](bytes.NewReader(objDataBuf))
50+
obj := parseResource[appsv1.StatefulSet](objDataBuf)
5251
podSpecV1 = &obj.Spec.Template
5352
metaObj = obj
5453
case "Job":
55-
obj := parseResource[batchv1.Job](bytes.NewReader(objDataBuf))
54+
obj := parseResource[batchv1.Job](objDataBuf)
5655
podSpecV1 = &obj.Spec.Template
5756
metaObj = obj
5857
default:
@@ -71,8 +70,8 @@ func matchLabelSelectorToStrLabels(labels map[string]string) []string {
7170
return res
7271
}
7372

74-
func ScanK8sConfigmapObject(kind string, objDataBuf []byte) (*common.CfgMap, error) {
75-
obj := parseResource[v1.ConfigMap](bytes.NewReader(objDataBuf))
73+
func ScanK8sConfigmapObject(objDataBuf []byte) (*common.CfgMap, error) {
74+
obj := parseResource[v1.ConfigMap](objDataBuf)
7675
if obj == nil {
7776
return nil, fmt.Errorf("unable to parse configmap")
7877
}
@@ -82,19 +81,15 @@ func ScanK8sConfigmapObject(kind string, objDataBuf []byte) (*common.CfgMap, err
8281
}
8382

8483
// Create a common.Service object from a k8s Service object
85-
func ScanK8sServiceObject(kind string, objDataBuf []byte) (*common.Service, error) {
86-
if kind != "Service" {
87-
return nil, fmt.Errorf("expected parsing a Service resource, but got `%s`", kind)
88-
}
89-
90-
svcObj := parseResource[v1.Service](bytes.NewReader(objDataBuf))
84+
func ScanK8sServiceObject(objDataBuf []byte) (*common.Service, error) {
85+
svcObj := parseResource[v1.Service](objDataBuf)
9186
if svcObj == nil {
9287
return nil, fmt.Errorf("failed to parse Service resource")
9388
}
9489
var serviceCtx common.Service
9590
serviceCtx.Resource.Name = svcObj.GetName()
9691
serviceCtx.Resource.Namespace = svcObj.Namespace
97-
serviceCtx.Resource.Kind = kind
92+
serviceCtx.Resource.Kind = svcObj.Kind
9893
serviceCtx.Resource.Type = svcObj.Spec.Type
9994
serviceCtx.Resource.Selectors = matchLabelSelectorToStrLabels(svcObj.Spec.Selector)
10095
serviceCtx.Resource.ExposeExternally = (svcObj.Spec.Type == v1.ServiceTypeLoadBalancer || svcObj.Spec.Type == v1.ServiceTypeNodePort)
@@ -109,12 +104,8 @@ func ScanK8sServiceObject(kind string, objDataBuf []byte) (*common.Service, erro
109104
}
110105

111106
// Scan an OpenShift Route object and mark the services it uses to be exposed inside the cluster
112-
func ScanOCRouteObject(kind string, objDataBuf []byte, servicesToExpose common.ServicesToExpose) error {
113-
if kind != "Route" {
114-
return fmt.Errorf("expected parsing a Route resource, but got `%s`", kind)
115-
}
116-
117-
routeObj := parseResource[ocroutev1.Route](bytes.NewReader(objDataBuf))
107+
func ScanOCRouteObject(objDataBuf []byte, servicesToExpose common.ServicesToExpose) error {
108+
routeObj := parseResource[ocroutev1.Route](objDataBuf)
118109
if routeObj == nil {
119110
return fmt.Errorf("failed to parse Route resource")
120111
}
@@ -133,12 +124,8 @@ func ScanOCRouteObject(kind string, objDataBuf []byte, servicesToExpose common.S
133124
}
134125

135126
// Scan an Ingress object and mark the services it uses to be exposed inside the cluster
136-
func ScanIngressObject(kind string, objDataBuf []byte, servicesToExpose common.ServicesToExpose) error {
137-
if kind != "Ingress" {
138-
return fmt.Errorf("expected parsing a Ingress resource, but got `%s`", kind)
139-
}
140-
141-
ingressObj := parseResource[networkv1.Ingress](bytes.NewReader(objDataBuf))
127+
func ScanIngressObject(objDataBuf []byte, servicesToExpose common.ServicesToExpose) error {
128+
ingressObj := parseResource[networkv1.Ingress](objDataBuf)
142129
if ingressObj == nil {
143130
return fmt.Errorf("failed to parse Ingress resource")
144131
}

pkg/analyzer/scan_test.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package analyzer
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/np-guard/cluster-topology-analyzer/pkg/common"
12+
)
13+
14+
func TestNetworkAddressValue(t *testing.T) {
15+
type strBoolPair struct {
16+
str string
17+
b bool
18+
}
19+
20+
valuesToCheck := map[string]strBoolPair{
21+
"svc": {"svc", true},
22+
"svc:500": {"svc:500", true},
23+
"http://svc:500": {"svc:500", true},
24+
"fttps://svc:500/something#abc": {"svc:500", true},
25+
strings.Repeat("abc", 500): {"", false},
26+
"not%a*url": {"", false},
27+
"123": {"", false},
28+
}
29+
30+
for val, expectedAnswer := range valuesToCheck {
31+
strRes, boolRes := NetworkAddressValue(val)
32+
require.Equal(t, expectedAnswer.b, boolRes)
33+
require.Equal(t, expectedAnswer.str, strRes)
34+
}
35+
}
36+
37+
func TestScanningSvc(t *testing.T) {
38+
resourceBuf, err := loadResourceAsByteArray([]string{"k8s_guestbook", "frontend-service.yaml"})
39+
require.Nil(t, err)
40+
res, err := ScanK8sServiceObject(resourceBuf)
41+
require.Nil(t, err)
42+
require.Equal(t, "frontend", res.Resource.Name)
43+
require.Len(t, res.Resource.Selectors, 2)
44+
require.Len(t, res.Resource.Network, 1)
45+
require.Equal(t, 80, res.Resource.Network[0].Port)
46+
}
47+
48+
func TestScanningDeploymentWithArgs(t *testing.T) {
49+
resourceBuf, err := loadResourceAsByteArray([]string{"sockshop", "manifests", "01-carts-dep.yaml"})
50+
require.Nil(t, err)
51+
res, err := ScanK8sWorkloadObject("Deployment", resourceBuf)
52+
require.Nil(t, err)
53+
require.Equal(t, "carts", res.Resource.Name)
54+
require.Len(t, res.Resource.NetworkAddrs, 1)
55+
require.Equal(t, "carts-db:27017", res.Resource.NetworkAddrs[0])
56+
require.Len(t, res.Resource.Labels, 1)
57+
require.Equal(t, "carts", res.Resource.Labels["name"])
58+
}
59+
60+
func TestScanningDeploymentWithEnvs(t *testing.T) {
61+
resourceBuf, err := loadResourceAsByteArray([]string{"k8s_guestbook", "frontend-deployment.yaml"})
62+
require.Nil(t, err)
63+
res, err := ScanK8sWorkloadObject("Deployment", resourceBuf)
64+
require.Nil(t, err)
65+
require.Equal(t, "frontend", res.Resource.Name)
66+
require.Len(t, res.Resource.NetworkAddrs, 4)
67+
require.Len(t, res.Resource.Labels, 2)
68+
}
69+
70+
func TestScanningDeploymentWithConfigMapRef(t *testing.T) {
71+
resourceBuf, err := loadResourceAsByteArray([]string{"acs-security-demos", "frontend", "webapp", "deployment.yaml"})
72+
require.Nil(t, err)
73+
res, err := ScanK8sWorkloadObject("Deployment", resourceBuf)
74+
require.Nil(t, err)
75+
require.Equal(t, "webapp", res.Resource.Name)
76+
require.Len(t, res.Resource.ConfigMapRefs, 1)
77+
require.Empty(t, res.Resource.NetworkAddrs) // extracting network addresses from configmaps happens later
78+
require.Len(t, res.Resource.Labels, 1)
79+
}
80+
81+
func TestScanningReplicaSet(t *testing.T) {
82+
resourceBuf, err := loadResourceAsByteArray([]string{"k8s_guestbook", "redis-leader-deployment.yaml"})
83+
require.Nil(t, err)
84+
res, err := ScanK8sWorkloadObject("ReplicaSet", resourceBuf)
85+
require.Nil(t, err)
86+
require.Equal(t, "redis-leader", res.Resource.Name)
87+
require.Len(t, res.Resource.NetworkAddrs, 0)
88+
require.Len(t, res.Resource.Labels, 3)
89+
}
90+
91+
func TestScanningConfigMap(t *testing.T) {
92+
resourceBuf, err := loadResourceAsByteArray([]string{"qotd", "qotd_usecase.yaml"})
93+
require.Nil(t, err)
94+
res, err := ScanK8sConfigmapObject(resourceBuf)
95+
require.Nil(t, err)
96+
require.Equal(t, res.FullName, "qotd-load/qotd-usecase-library")
97+
require.Len(t, res.Data, 5)
98+
}
99+
100+
func TestScanningIngress(t *testing.T) {
101+
resourceBuf, err := loadResourceAsByteArray([]string{"bookinfo", "bookinfo-ingress.yaml"})
102+
require.Nil(t, err)
103+
toExpose := common.ServicesToExpose{}
104+
err = ScanIngressObject(resourceBuf, toExpose)
105+
require.Nil(t, err)
106+
require.Len(t, toExpose, 1)
107+
}
108+
109+
func TestScanningRoute(t *testing.T) {
110+
resourceBuf, err := loadResourceAsByteArray([]string{"acs-security-demos", "frontend", "webapp", "route.yaml"})
111+
require.Nil(t, err)
112+
toExpose := common.ServicesToExpose{}
113+
err = ScanOCRouteObject(resourceBuf, toExpose)
114+
require.Nil(t, err)
115+
require.Len(t, toExpose, 1)
116+
}
117+
118+
func loadResourceAsByteArray(resourceDirs []string) ([]byte, error) {
119+
currentDir, _ := os.Getwd()
120+
resourceRelPath := filepath.Join(resourceDirs...)
121+
resourcePath := filepath.Join(currentDir, "..", "..", "tests", resourceRelPath)
122+
return os.ReadFile(resourcePath)
123+
}

pkg/controller/resource_finder.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -186,24 +186,24 @@ func (rf *resourceFinder) parseK8sYaml(mfp, relMfp string) []FileProcessingError
186186
func (rf *resourceFinder) parseResource(kind string, yamlDoc []byte, manifestFilePath string) error {
187187
switch kind {
188188
case service:
189-
res, err := analyzer.ScanK8sServiceObject(kind, yamlDoc)
189+
res, err := analyzer.ScanK8sServiceObject(yamlDoc)
190190
if err != nil {
191191
return err
192192
}
193193
res.Resource.FilePath = manifestFilePath
194194
rf.services = append(rf.services, res)
195195
case route:
196-
err := analyzer.ScanOCRouteObject(kind, yamlDoc, rf.servicesToExpose)
196+
err := analyzer.ScanOCRouteObject(yamlDoc, rf.servicesToExpose)
197197
if err != nil {
198198
return err
199199
}
200200
case ingress:
201-
err := analyzer.ScanIngressObject(kind, yamlDoc, rf.servicesToExpose)
201+
err := analyzer.ScanIngressObject(yamlDoc, rf.servicesToExpose)
202202
if err != nil {
203203
return err
204204
}
205205
case configmap:
206-
res, err := analyzer.ScanK8sConfigmapObject(kind, yamlDoc)
206+
res, err := analyzer.ScanK8sConfigmapObject(yamlDoc)
207207
if err != nil {
208208
return err
209209
}

0 commit comments

Comments
 (0)