Skip to content

Commit 7fc195b

Browse files
authored
Explicit error handling by introducing NotFound error types, and replacing nil with NotFound with checks in the caller where-ever required. (#244)
1 parent 0827dce commit 7fc195b

File tree

17 files changed

+275
-72
lines changed

17 files changed

+275
-72
lines changed

.github/.codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ ignore:
2626
- "mocks/**/*"
2727
- "integration/shared/scenarios/**/*"
2828
- "pkg/common/logger.go"
29+
- "test/*"

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ build: test ## Build manager binary.
121121
go build -ldflags="-s -w -X ${PKG}.GitVersion=${GIT_TAG} -X ${PKG}.GitCommit=${GIT_COMMIT}" -o bin/manager main.go
122122

123123
run: test ## Run a controller from your host.
124-
go run -ldflags="-s -w -X ${PKG}.GitVersion=${GIT_TAG} -X ${PKG}.GitCommit=${GIT_COMMIT}" ./main.go --zap-devel=true $(ARGS)
124+
go run -ldflags="-s -w -X ${PKG}.GitVersion=${GIT_TAG} -X ${PKG}.GitCommit=${GIT_COMMIT}" ./main.go --zap-devel=true --zap-time-encoding=rfc3339 $(ARGS)
125125

126126
docker-build: test ## Build docker image with the manager.
127127
docker build --no-cache -t ${IMG} .

integration/janitor/janitor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77

88
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/cloudmap"
9+
910
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/model"
1011
"github.com/aws/aws-sdk-go-v2/aws"
1112
"github.com/aws/aws-sdk-go-v2/config"
@@ -81,7 +82,7 @@ func (j *cloudMapJanitor) deregisterInstances(ctx context.Context, nsName string
8182
model.ClusterSetIdAttr: j.clusterSetId,
8283
}
8384

84-
insts, err := j.sdApi.DiscoverInstances(ctx, nsName, svcName, &queryParameters)
85+
insts, err := j.sdApi.DiscoverInstances(ctx, nsName, svcName, queryParameters)
8586
j.checkOrFail(err,
8687
fmt.Sprintf("service has %d instances to clean", len(insts)),
8788
"could not list instances to cleanup")

integration/janitor/janitor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestCleanupHappyCase(t *testing.T) {
3232
Return(map[string]*model.Namespace{test.HttpNsName: test.GetTestHttpNamespace()}, nil)
3333
tj.mockApi.EXPECT().GetServiceIdMap(context.TODO(), test.HttpNsId).
3434
Return(map[string]string{test.SvcName: test.SvcId}, nil)
35-
tj.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, &map[string]string{
35+
tj.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, map[string]string{
3636
model.ClusterSetIdAttr: test.ClusterSet,
3737
}).
3838
Return([]types.HttpInstanceSummary{{InstanceId: aws.String(test.EndptId1)}}, nil)

integration/kind-test/scripts/run-tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ if ! endpts=$(./integration/shared/scripts/poll-endpoints.sh "$EXPECTED_ENDPOINT
2020
fi
2121

2222
mkdir -p "$LOGS"
23-
./bin/manager &> "$LOGS/ctl.log" &
23+
./bin/manager --zap-devel=true --zap-time-encoding=rfc3339 &> "$LOGS/ctl.log" &
2424
CTL_PID=$!
2525
echo "controller PID:$CTL_PID"
2626

integration/shared/scenarios/export_service.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/common"
11+
1012
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/cloudmap"
1113
"github.com/aws/aws-cloud-map-mcs-controller-for-k8s/pkg/model"
1214
"github.com/aws/aws-sdk-go-v2/aws"
@@ -86,16 +88,16 @@ func (e *exportServiceScenario) Run() error {
8688
return wait.Poll(defaultScenarioPollInterval, defaultScenarioPollTimeout, func() (done bool, err error) {
8789
fmt.Println("Polling service...")
8890
cmSvc, err := e.sdClient.GetService(context.TODO(), e.expectedSvc.Namespace, e.expectedSvc.Name)
89-
if err != nil {
91+
if common.IsUnknown(err) {
9092
return true, err
9193
}
9294

93-
if cmSvc == nil {
95+
if common.IsNotFound(err) {
9496
fmt.Println("Service not found.")
9597
return false, nil
9698
}
9799

98-
fmt.Printf("Found service: %v\n", cmSvc)
100+
fmt.Printf("Found service: %+v\n", cmSvc)
99101
return e.compareEndpoints(cmSvc.Endpoints), nil
100102
})
101103
}

pkg/cloudmap/api.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type ServiceDiscoveryApi interface {
3030
GetServiceIdMap(ctx context.Context, namespaceId string) (serviceIdMap map[string]string, err error)
3131

3232
// DiscoverInstances returns a list of service instances registered to a given service.
33-
DiscoverInstances(ctx context.Context, nsName string, svcName string, queryParameters *map[string]string) (insts []types.HttpInstanceSummary, err error)
33+
DiscoverInstances(ctx context.Context, nsName string, svcName string, queryParameters map[string]string) (insts []types.HttpInstanceSummary, err error)
3434

3535
// ListOperations returns a map of operations to their status matching a list of filters.
3636
ListOperations(ctx context.Context, opFilters []types.OperationFilter) (operationStatusMap map[string]types.OperationStatus, err error)
@@ -132,15 +132,15 @@ func (sdApi *serviceDiscoveryApi) GetServiceIdMap(ctx context.Context, nsId stri
132132
return serviceIdMap, nil
133133
}
134134

135-
func (sdApi *serviceDiscoveryApi) DiscoverInstances(ctx context.Context, nsName string, svcName string, queryParameters *map[string]string) (insts []types.HttpInstanceSummary, err error) {
135+
func (sdApi *serviceDiscoveryApi) DiscoverInstances(ctx context.Context, nsName string, svcName string, queryParameters map[string]string) (insts []types.HttpInstanceSummary, err error) {
136136
input := &sd.DiscoverInstancesInput{
137137
NamespaceName: aws.String(nsName),
138138
ServiceName: aws.String(svcName),
139139
HealthStatus: types.HealthStatusFilterAll,
140140
MaxResults: aws.Int32(1000),
141141
}
142142
if queryParameters != nil {
143-
input.QueryParameters = *queryParameters
143+
input.QueryParameters = queryParameters
144144
}
145145
out, err := sdApi.awsFacade.DiscoverInstances(ctx, input)
146146

pkg/cloudmap/api_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func TestServiceDiscoveryApi_DiscoverInstances_HappyCase(t *testing.T) {
124124
},
125125
}, nil)
126126

127-
insts, err := sdApi.DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, &map[string]string{model.ClusterSetIdAttr: test.ClusterSet})
127+
insts, err := sdApi.DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, map[string]string{model.ClusterSetIdAttr: test.ClusterSet})
128128
assert.Nil(t, err, "No error for happy case")
129129
assert.True(t, len(insts) == 2)
130130
assert.Equal(t, test.EndptId1, *insts[0].InstanceId)

pkg/cloudmap/client.go

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type serviceDiscoveryClient struct {
4040
// from a given AWS client config.
4141
func NewDefaultServiceDiscoveryClient(cfg *aws.Config, clusterUtils model.ClusterUtils) ServiceDiscoveryClient {
4242
return &serviceDiscoveryClient{
43-
log: common.NewLogger("cloudmap"),
43+
log: common.NewLogger("cloudmap", "client"),
4444
sdApi: NewServiceDiscoveryApiFromConfig(cfg),
4545
cache: NewDefaultServiceDiscoveryClientCache(),
4646
clusterUtils: clusterUtils,
@@ -49,7 +49,7 @@ func NewDefaultServiceDiscoveryClient(cfg *aws.Config, clusterUtils model.Cluste
4949

5050
func NewServiceDiscoveryClientWithCustomCache(cfg *aws.Config, cacheConfig *SdCacheConfig, clusterUtils model.ClusterUtils) ServiceDiscoveryClient {
5151
return &serviceDiscoveryClient{
52-
log: common.NewLogger("cloudmap"),
52+
log: common.NewLogger("cloudmap", "client"),
5353
sdApi: NewServiceDiscoveryApiFromConfig(cfg),
5454
cache: NewServiceDiscoveryClientCache(cacheConfig),
5555
clusterUtils: clusterUtils,
@@ -59,6 +59,10 @@ func NewServiceDiscoveryClientWithCustomCache(cfg *aws.Config, cacheConfig *SdCa
5959
func (sdc *serviceDiscoveryClient) ListServices(ctx context.Context, nsName string) (svcs []*model.Service, err error) {
6060
svcIdMap, err := sdc.getServiceIds(ctx, nsName)
6161
if err != nil {
62+
// Ignore resource not found error, as it will indicate deleted resources in CloudMap
63+
if common.IsNotFound(err) {
64+
return svcs, nil
65+
}
6266
return svcs, err
6367
}
6468

@@ -81,13 +85,13 @@ func (sdc *serviceDiscoveryClient) ListServices(ctx context.Context, nsName stri
8185
func (sdc *serviceDiscoveryClient) CreateService(ctx context.Context, nsName string, svcName string) error {
8286
sdc.log.Info("creating a new service", "namespace", nsName, "name", svcName)
8387

84-
nsMap, err := sdc.getNamespaces(ctx)
85-
if err != nil {
88+
namespace, err := sdc.getNamespace(ctx, nsName)
89+
if common.IsUnknown(err) {
8690
return err
8791
}
8892

89-
namespace := nsMap[nsName]
90-
if namespace == nil {
93+
if common.IsNotFound(err) {
94+
sdc.log.Info("namespace not found for service", "namespace", nsName, "service", svcName)
9195
// Create HttpNamespace if the namespace is not present in CloudMap
9296
namespace, err = sdc.createNamespace(ctx, nsName)
9397
if err != nil {
@@ -107,27 +111,20 @@ func (sdc *serviceDiscoveryClient) CreateService(ctx context.Context, nsName str
107111

108112
func (sdc *serviceDiscoveryClient) GetService(ctx context.Context, nsName string, svcName string) (svc *model.Service, err error) {
109113
sdc.log.Info("fetching a service", "namespace", nsName, "name", svcName)
110-
endpts, cacheHit := sdc.cache.GetEndpoints(nsName, svcName)
111-
112-
if cacheHit {
114+
if endpts, found := sdc.cache.GetEndpoints(nsName, svcName); found {
113115
return &model.Service{
114116
Namespace: nsName,
115117
Name: svcName,
116118
Endpoints: endpts,
117119
}, nil
118120
}
119121

120-
svcIdMap, err := sdc.getServiceIds(ctx, nsName)
122+
_, err = sdc.getServiceId(ctx, nsName, svcName)
121123
if err != nil {
122124
return nil, err
123125
}
124-
_, found := svcIdMap[svcName]
125-
if !found {
126-
return nil, nil
127-
}
128-
129-
endpts, err = sdc.getEndpoints(ctx, nsName, svcName)
130126

127+
endpts, err := sdc.getEndpoints(ctx, nsName, svcName)
131128
if err != nil {
132129
return nil, err
133130
}
@@ -147,14 +144,10 @@ func (sdc *serviceDiscoveryClient) RegisterEndpoints(ctx context.Context, nsName
147144

148145
sdc.log.Info("registering endpoints", "namespaceName", nsName, "serviceName", svcName, "endpoints", endpts)
149146

150-
svcIdMap, err := sdc.getServiceIds(ctx, nsName)
147+
svcId, err := sdc.getServiceId(ctx, nsName, svcName)
151148
if err != nil {
152149
return err
153150
}
154-
svcId, found := svcIdMap[svcName]
155-
if !found {
156-
return fmt.Errorf("service not found in Cloud Map: %s", svcName)
157-
}
158151

159152
opCollector := NewOperationCollector()
160153

@@ -190,14 +183,10 @@ func (sdc *serviceDiscoveryClient) DeleteEndpoints(ctx context.Context, nsName s
190183

191184
sdc.log.Info("deleting endpoints", "namespaceName", nsName, "serviceName", svcName, "endpoints", endpts)
192185

193-
svcIdMap, err := sdc.getServiceIds(ctx, nsName)
186+
svcId, err := sdc.getServiceId(ctx, nsName, svcName)
194187
if err != nil {
195188
return err
196189
}
197-
svcId, found := svcIdMap[svcName]
198-
if !found {
199-
return fmt.Errorf("service not found in Cloud Map: %s", svcName)
200-
}
201190

202191
opCollector := NewOperationCollector()
203192

@@ -225,8 +214,8 @@ func (sdc *serviceDiscoveryClient) DeleteEndpoints(ctx context.Context, nsName s
225214
}
226215

227216
func (sdc *serviceDiscoveryClient) getEndpoints(ctx context.Context, nsName string, svcName string) (endpts []*model.Endpoint, err error) {
228-
endpts, cacheHit := sdc.cache.GetEndpoints(nsName, svcName)
229-
if cacheHit {
217+
endpts, found := sdc.cache.GetEndpoints(nsName, svcName)
218+
if found {
230219
return endpts, nil
231220
}
232221

@@ -239,7 +228,7 @@ func (sdc *serviceDiscoveryClient) getEndpoints(ctx context.Context, nsName stri
239228
queryParameters := map[string]string{
240229
model.ClusterSetIdAttr: clusterProperties.ClusterSetId(),
241230
}
242-
insts, err := sdc.sdApi.DiscoverInstances(ctx, nsName, svcName, &queryParameters)
231+
insts, err := sdc.sdApi.DiscoverInstances(ctx, nsName, svcName, queryParameters)
243232
if err != nil {
244233
return nil, err
245234
}
@@ -257,10 +246,23 @@ func (sdc *serviceDiscoveryClient) getEndpoints(ctx context.Context, nsName stri
257246
return endpts, nil
258247
}
259248

260-
func (sdc *serviceDiscoveryClient) getNamespaces(ctx context.Context) (namespace map[string]*model.Namespace, err error) {
249+
func (sdc *serviceDiscoveryClient) getNamespace(ctx context.Context, nsName string) (namespace *model.Namespace, err error) {
250+
namespaces, err := sdc.getNamespaces(ctx)
251+
if err != nil {
252+
return nil, err
253+
}
254+
255+
if namespace, ok := namespaces[nsName]; ok {
256+
return namespace, nil
257+
}
258+
259+
return nil, common.NotFoundError(fmt.Sprintf("namespace: %s", nsName))
260+
}
261+
262+
func (sdc *serviceDiscoveryClient) getNamespaces(ctx context.Context) (namespaces map[string]*model.Namespace, err error) {
261263
// We are assuming a unique namespace name per account
262-
namespaces, cacheHit := sdc.cache.GetNamespaceMap()
263-
if cacheHit {
264+
namespaces, found := sdc.cache.GetNamespaceMap()
265+
if found {
264266
return namespaces, nil
265267
}
266268

@@ -273,15 +275,27 @@ func (sdc *serviceDiscoveryClient) getNamespaces(ctx context.Context) (namespace
273275
return namespaces, nil
274276
}
275277

278+
func (sdc *serviceDiscoveryClient) getServiceId(ctx context.Context, nsName string, svcName string) (svcId string, err error) {
279+
svcIdMap, err := sdc.getServiceIds(ctx, nsName)
280+
if err != nil {
281+
return "", err
282+
}
283+
284+
if svcId, ok := svcIdMap[svcName]; ok {
285+
return svcId, nil
286+
}
287+
288+
return "", common.NotFoundError(fmt.Sprintf("service: %s", svcName))
289+
}
290+
276291
func (sdc *serviceDiscoveryClient) getServiceIds(ctx context.Context, nsName string) (map[string]string, error) {
277-
serviceIdMap, cacheHit := sdc.cache.GetServiceIdMap(nsName)
278-
if cacheHit {
292+
serviceIdMap, found := sdc.cache.GetServiceIdMap(nsName)
293+
if found {
279294
return serviceIdMap, nil
280295
}
281296

282-
nsMap, err := sdc.getNamespaces(ctx)
283-
namespace := nsMap[nsName]
284-
if err != nil || namespace == nil {
297+
namespace, err := sdc.getNamespace(ctx, nsName)
298+
if err != nil {
285299
return nil, err
286300
}
287301

pkg/cloudmap/client_test.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func TestServiceDiscoveryClient_ListServices_HappyCase(t *testing.T) {
4848
tc.mockCache.EXPECT().CacheServiceIdMap(test.HttpNsName, getServiceIdMapForTest())
4949

5050
tc.mockCache.EXPECT().GetEndpoints(test.HttpNsName, test.SvcName).Return(nil, false)
51-
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, &map[string]string{
51+
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, map[string]string{
5252
model.ClusterSetIdAttr: test.ClusterSet,
5353
}).Return(getHttpInstanceSummaryForTest(), nil)
5454

@@ -117,7 +117,7 @@ func TestServiceDiscoveryClient_ListServices_InstanceError(t *testing.T) {
117117

118118
endptErr := errors.New("error listing endpoints")
119119
tc.mockCache.EXPECT().GetEndpoints(test.HttpNsName, test.SvcName).Return(nil, false)
120-
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, &map[string]string{
120+
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, map[string]string{
121121
model.ClusterSetIdAttr: test.ClusterSet,
122122
}).
123123
Return([]types.HttpInstanceSummary{}, endptErr)
@@ -180,6 +180,23 @@ func TestServiceDiscoveryClient_CreateService_NamespaceError(t *testing.T) {
180180
assert.Equal(t, nsErr, err)
181181
}
182182

183+
func TestServiceDiscoveryClient_CreateService_NamespaceNotFound(t *testing.T) {
184+
tc := getTestSdClient(t)
185+
defer tc.close()
186+
187+
tc.mockCache.EXPECT().GetNamespaceMap().Return(map[string]*model.Namespace{}, true)
188+
tc.mockApi.EXPECT().CreateHttpNamespace(context.TODO(), test.HttpNsName).Return(test.OpId1, nil)
189+
tc.mockApi.EXPECT().PollNamespaceOperation(context.TODO(), test.OpId1).Return(test.HttpNsId, nil)
190+
tc.mockCache.EXPECT().EvictNamespaceMap()
191+
192+
tc.mockApi.EXPECT().CreateService(context.TODO(), *test.GetTestHttpNamespace(), test.SvcName).
193+
Return(test.SvcId, nil)
194+
tc.mockCache.EXPECT().EvictServiceIdMap(test.HttpNsName)
195+
196+
err := tc.client.CreateService(context.TODO(), test.HttpNsName, test.SvcName)
197+
assert.Nil(t, err)
198+
}
199+
183200
func TestServiceDiscoveryClient_CreateService_CreateServiceError(t *testing.T) {
184201
tc := getTestSdClient(t)
185202
defer tc.close()
@@ -264,7 +281,7 @@ func TestServiceDiscoveryClient_GetService_HappyCase(t *testing.T) {
264281
tc.mockCache.EXPECT().CacheServiceIdMap(test.HttpNsName, getServiceIdMapForTest())
265282

266283
tc.mockCache.EXPECT().GetEndpoints(test.HttpNsName, test.SvcName).Return([]*model.Endpoint{}, false)
267-
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, &map[string]string{
284+
tc.mockApi.EXPECT().DiscoverInstances(context.TODO(), test.HttpNsName, test.SvcName, map[string]string{
268285
model.ClusterSetIdAttr: test.ClusterSet,
269286
}).
270287
Return(getHttpInstanceSummaryForTest(), nil)
@@ -288,6 +305,30 @@ func TestServiceDiscoveryClient_GetService_CachedValues(t *testing.T) {
288305
assert.Equal(t, test.GetTestService(), svc)
289306
}
290307

308+
func TestServiceDiscoveryClient_GetService_ServiceNotFound(t *testing.T) {
309+
tc := getTestSdClient(t)
310+
defer tc.close()
311+
312+
tc.mockCache.EXPECT().GetEndpoints(test.HttpNsName, test.SvcName).Return(nil, false)
313+
314+
tc.mockCache.EXPECT().GetServiceIdMap(test.HttpNsName).Return(nil, false)
315+
316+
tc.mockCache.EXPECT().GetNamespaceMap().Return(nil, false)
317+
tc.mockApi.EXPECT().GetNamespaceMap(context.TODO()).
318+
Return(getNamespaceMapForTest(), nil)
319+
tc.mockCache.EXPECT().CacheNamespaceMap(getNamespaceMapForTest())
320+
321+
// return empty list from CloudMap's api
322+
tc.mockApi.EXPECT().GetServiceIdMap(context.TODO(), test.HttpNsId).
323+
Return(map[string]string{}, nil)
324+
tc.mockCache.EXPECT().CacheServiceIdMap(test.HttpNsName, map[string]string{})
325+
326+
svc, err := tc.client.GetService(context.TODO(), test.HttpNsName, test.SvcName)
327+
assert.NotNil(t, err)
328+
assert.True(t, common.IsNotFound(err), svc)
329+
assert.Contains(t, err.Error(), test.SvcName)
330+
}
331+
291332
func TestServiceDiscoveryClient_RegisterEndpoints(t *testing.T) {
292333
tc := getTestSdClient(t)
293334
defer tc.close()

0 commit comments

Comments
 (0)