Skip to content

Commit de48356

Browse files
Map flavor name not found Internal error to the ResourceExhausted MCM error code (gardener#350)
* Map flavor name not found Internal error to the ResourceExhausted MCM error * Fix errors and extend tests * Run make generate * Fix errors and enhance tests * Add additional tests for error code mapping * Rename driver_test to utils_test --------- Co-authored-by: Prashant Tak <prashant.tak@sap.com>
1 parent 10a723c commit de48356

5 files changed

Lines changed: 105 additions & 1 deletion

File tree

pkg/driver/executor/errors.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,14 @@ var (
2424
// OpenStack resources. In case this case, where a unique ID could not be determined an ErrMultipleFound is returned.
2525
ErrMultipleFound = fmt.Errorf("multiple resources found")
2626
)
27+
28+
// ErrFlavorNotFound is returned when there is no flavor can be matched with the specified flavor name.
29+
// It can happen when certain flavor is not available in specified region and needs to be treated as ResourceExhausted
30+
// to allow fallback to other flavors.
31+
type ErrFlavorNotFound struct {
32+
Flavor string
33+
}
34+
35+
func (e ErrFlavorNotFound) Error() string {
36+
return fmt.Sprintf("Unable to find flavor with name %s", e.Flavor)
37+
}

pkg/driver/executor/executor.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"time"
1212

13+
"github.com/gophercloud/gophercloud/v2"
1314
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes"
1415
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs"
1516
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
@@ -276,7 +277,12 @@ func (ex *Executor) deployServer(ctx context.Context, machineName string, userDa
276277
}
277278
flavorRef, err := ex.Compute.FlavorIDFromName(ctx, flavorName)
278279
if err != nil {
279-
return nil, fmt.Errorf("error resolving flavor ID from flavor name %q: %v", imageName, err)
280+
switch err.(type) {
281+
case gophercloud.ErrResourceNotFound:
282+
return nil, fmt.Errorf("error resolving flavor ID from flavor name %q: %w", flavorName, ErrFlavorNotFound{Flavor: flavorName})
283+
default:
284+
return nil, fmt.Errorf("error resolving flavor ID from flavor name %q: %v", flavorName, err)
285+
}
280286
}
281287

282288
createOpts := &servers.CreateOpts{

pkg/driver/executor/executor_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,24 @@ var _ = Describe("Executor", func() {
209209
Expect(server.ProviderID).To(Equal(encodeProviderID(region, serverID)))
210210
})
211211

212+
It("should raise a ErrResourceNotFound error when called with a missing flavor", func() {
213+
ex := &Executor{
214+
Compute: compute,
215+
Network: network,
216+
Config: cfg,
217+
}
218+
219+
compute.EXPECT().ListServers(ctx, &servers.ListOpts{Name: machineName}).Return([]servers.Server{}, nil)
220+
compute.EXPECT().ImageIDFromName(ctx, imageName).Return(images.Image{ID: "imageID"}, nil)
221+
compute.EXPECT().FlavorIDFromName(ctx, flavorName).Return(flavorName, gophercloud.ErrResourceNotFound{Name: flavorName, ResourceType: "flavor"})
222+
compute.EXPECT().ListServers(ctx, &servers.ListOpts{Name: machineName}).Return([]servers.Server{}, nil)
223+
224+
_, err := ex.CreateMachine(ctx, machineName, nil)
225+
Expect(err).To(HaveOccurred())
226+
Expect(errors.Is(err, ErrFlavorNotFound{Flavor: "flavor"})).To(BeTrue())
227+
Expect(errors.As(err, &ErrFlavorNotFound{})).To(BeTrue())
228+
})
229+
212230
It("should delete the server on failure", func() {
213231
ex := &Executor{
214232
Compute: compute,

pkg/driver/utils.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ func mapErrorToCode(err error) codes.Code {
6363
return codes.PermissionDenied
6464
}
6565

66+
if errors.As(err, &executor.ErrFlavorNotFound{}) {
67+
return codes.ResourceExhausted
68+
}
69+
6670
return mapErrorMessageToCode(err)
6771
}
6872

pkg/driver/utils_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package driver
6+
7+
import (
8+
"fmt"
9+
10+
"github.com/gardener/machine-controller-manager/pkg/util/provider/machinecodes/codes"
11+
"github.com/gardener/machine-controller-manager/pkg/util/provider/machinecodes/status"
12+
"github.com/gophercloud/gophercloud/v2"
13+
. "github.com/onsi/ginkgo/v2"
14+
. "github.com/onsi/gomega"
15+
16+
"github.com/gardener/machine-controller-manager-provider-openstack/pkg/driver/executor"
17+
)
18+
19+
var _ = Describe("Utils", func() {
20+
21+
Context("mapErrorToCode", func() {
22+
It("should map executor.ErrFlavorNotFound error to ResourceExhausted error code", func() {
23+
err1 := executor.ErrFlavorNotFound{}
24+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
25+
Expect(err1).To(HaveOccurred())
26+
Expect(err2).To(HaveOccurred())
27+
Expect(mapErrorToCode(err1)).To(Equal(codes.ResourceExhausted))
28+
})
29+
It("should map executor.ErrFlavorNotFound error with specific flavor to ResourceExhausted error code", func() {
30+
err1 := executor.ErrFlavorNotFound{Flavor: "flavor"}
31+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
32+
Expect(err1).To(HaveOccurred())
33+
Expect(err2).To(HaveOccurred())
34+
Expect(mapErrorToCode(err1)).To(Equal(codes.ResourceExhausted))
35+
})
36+
It("should map executor.ErrNotFound error to NotFound error code", func() {
37+
err1 := executor.ErrNotFound
38+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
39+
Expect(err1).To(HaveOccurred())
40+
Expect(err2).To(HaveOccurred())
41+
Expect(mapErrorToCode(err1)).To(Equal(codes.NotFound))
42+
})
43+
It("should map gophercloud.ErrResourceNotFound error to Internal error code", func() {
44+
err1 := gophercloud.ErrResourceNotFound{}
45+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
46+
Expect(err1).To(HaveOccurred())
47+
Expect(err2).To(HaveOccurred())
48+
Expect(mapErrorToCode(err1)).To(Equal(codes.Internal))
49+
})
50+
It("should map error containing executor.NoValidHost to ResourceExhausted error code", func() {
51+
err1 := fmt.Errorf("error: %s", executor.NoValidHost)
52+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
53+
Expect(err1).To(HaveOccurred())
54+
Expect(err2).To(HaveOccurred())
55+
Expect(mapErrorToCode(err1)).To(Equal(codes.ResourceExhausted))
56+
})
57+
It("should map error containing random string to Internal error code", func() {
58+
err1 := fmt.Errorf("error: Random provider issue")
59+
err2 := status.Error(mapErrorToCode(err1), err1.Error())
60+
Expect(err1).To(HaveOccurred())
61+
Expect(err2).To(HaveOccurred())
62+
Expect(mapErrorToCode(err1)).To(Equal(codes.Internal))
63+
})
64+
})
65+
})

0 commit comments

Comments
 (0)