Skip to content

Commit

Permalink
Add converters package, use awserrors pkg for all errors
Browse files Browse the repository at this point in the history
Signed-off-by: Vince Prignano <[email protected]>
  • Loading branch information
vincepri committed Nov 19, 2018
1 parent f5899b4 commit cafa29b
Show file tree
Hide file tree
Showing 16 changed files with 261 additions and 188 deletions.
2 changes: 1 addition & 1 deletion pkg/cloud/aws/actuators/machine/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ go_library(
deps = [
"//pkg/apis/awsprovider/v1alpha1:go_default_library",
"//pkg/cloud/aws/services:go_default_library",
"//pkg/cloud/aws/services/ec2:go_default_library",
"//pkg/cloud/aws/services/awserrors:go_default_library",
"//pkg/deployer:go_default_library",
"//pkg/tokens:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
Expand Down
5 changes: 2 additions & 3 deletions pkg/cloud/aws/actuators/machine/actuator.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"sigs.k8s.io/cluster-api-provider-aws/pkg/apis/awsprovider/v1alpha1"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services"
service "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services"
ec2svc "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/ec2"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/awserrors"
"sigs.k8s.io/cluster-api-provider-aws/pkg/deployer"
"sigs.k8s.io/cluster-api-provider-aws/pkg/tokens"
clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1"
Expand Down Expand Up @@ -145,8 +145,7 @@ func (a *Actuator) Create(cluster *clusterv1.Cluster, machine *clusterv1.Machine

i, err := a.ec2(clusterConfig).CreateOrGetMachine(machine, status, config, clusterStatus, clusterConfig, cluster, bootstrapToken)
if err != nil {

if ec2svc.IsFailedDependency(errors.Cause(err)) {
if awserrors.IsFailedDependency(errors.Cause(err)) {
klog.Errorf("network not ready to launch instances yet: %s", err)
return &controllerError.RequeueAfterError{
RequeueAfter: time.Minute,
Expand Down
16 changes: 16 additions & 0 deletions pkg/cloud/aws/converters/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = [
"instance.go",
"tags.go",
],
importpath = "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/converters",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/awsprovider/v1alpha1:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
],
)
49 changes: 49 additions & 0 deletions pkg/cloud/aws/converters/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright © 2018 The Kubernetes Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package converters

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"sigs.k8s.io/cluster-api-provider-aws/pkg/apis/awsprovider/v1alpha1"
)

func SDKToInstance(v *ec2.Instance) *v1alpha1.Instance {
i := &v1alpha1.Instance{
ID: aws.StringValue(v.InstanceId),
State: v1alpha1.InstanceState(*v.State.Name),
Type: aws.StringValue(v.InstanceType),
SubnetID: aws.StringValue(v.SubnetId),
ImageID: aws.StringValue(v.ImageId),
KeyName: v.KeyName,
PrivateIP: v.PrivateIpAddress,
PublicIP: v.PublicIpAddress,
ENASupport: v.EnaSupport,
EBSOptimized: v.EbsOptimized,
}

for _, sg := range v.SecurityGroups {
i.SecurityGroupIDs = append(i.SecurityGroupIDs, *sg.GroupId)
}

// TODO: Handle returned IAM instance profile, since we are currently
// using a string representing the name, but the InstanceProfile returned
// from the sdk only returns ARN and ID.

if len(v.Tags) > 0 {
i.Tags = TagsToMap(v.Tags)
}

return i
}
37 changes: 37 additions & 0 deletions pkg/cloud/aws/converters/tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package converters

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
)

// TagsToMap converts a []*ec2.Tag into a map[string]string.
func TagsToMap(src []*ec2.Tag) map[string]string {
// Create an array of exactly the length we require to hopefully avoid some
// allocations while looping.
tags := make(map[string]string)

for _, t := range src {
tags[*t.Key] = *t.Value
}

return tags
}

// MapToTags converts a map[string]string to a []*ec2.Tag
func MapToTags(src map[string]string) []*ec2.Tag {
// Create an array of exactly the length we require to hopefully avoid some
// allocations while looping.
tags := make([]*ec2.Tag, 0, len(src))

for k, v := range src {
tag := &ec2.Tag{
Key: aws.String(k),
Value: aws.String(v),
}

tags = append(tags, tag)
}

return tags
}
106 changes: 106 additions & 0 deletions pkg/cloud/aws/services/awserrors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@
package awserrors

import (
"net/http"

"github.com/aws/aws-sdk-go/aws/awserr"
)

const (
AuthFailure = "AuthFailure"
InUseIPAddress = "InvalidIPAddress.InUse"
GroupNotFound = "InvalidGroup.NotFound"
PermissionNotFound = "InvalidPermission.NotFound"
)

var _ error = &EC2Error{}

// Code returns the AWS error code as a string
func Code(err error) (string, bool) {
if awserr, ok := err.(awserr.Error); ok {
Expand All @@ -32,3 +43,98 @@ func Message(err error) string {
}
return ""
}

// EC2Error is an error exposed to users of this library.
type EC2Error struct { //nolint
err error

Code int
}

// Error implements the Error interface.
func (e *EC2Error) Error() string {
return e.err.Error()
}

// NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
func NewNotFound(err error) error {
return &EC2Error{
err: err,
Code: http.StatusNotFound,
}
}

// NewConflict returns a new error which indicates that the request cannot be processed due to a conflict.
func NewConflict(err error) error {
return &EC2Error{
err: err,
Code: http.StatusConflict,
}
}

// NewFailedDependency returns a new error which indicates that a dependency failure status
func NewFailedDependency(err error) error {
return &EC2Error{
err: err,
Code: http.StatusFailedDependency,
}
}

// IsFailedDependency checks if the error is pf http.StatusFailedDependency
func IsFailedDependency(err error) bool {
if ReasonForError(err) == http.StatusFailedDependency {
return true
}
return false
}

// IsNotFound returns true if the error was created by NewNotFound.
func IsNotFound(err error) bool {
if ReasonForError(err) == http.StatusNotFound {
return true
}
return IsInvalidNotFoundError(err)
}

// IsConflict returns true if the error was created by NewConflict.
func IsConflict(err error) bool {
return ReasonForError(err) == http.StatusConflict
}

// IsSDKError returns true if the error is of type awserr.Error.
func IsSDKError(err error) (ok bool) {
_, ok = err.(awserr.Error)
return
}

// IsInvalidNotFoundError tests for common aws not found errors
func IsInvalidNotFoundError(err error) bool {
if code, ok := Code(err); ok {
switch code {
case "InvalidVpcID.NotFound":
return true
}
}
return false
}

// ReasonForError returns the HTTP status for a particular error.
func ReasonForError(err error) int {
switch t := err.(type) {
case *EC2Error:
return t.Code
}
return -1
}

func IsIgnorableSecurityGroupError(err error) error {
if code, ok := Code(err); ok {
switch code {
case GroupNotFound, PermissionNotFound:
return nil
default:
return err
}
}
return nil
}
4 changes: 2 additions & 2 deletions pkg/cloud/aws/services/ec2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ go_library(
"bastion.go",
"console.go",
"eips.go",
"errors.go",
"filters.go",
"gateways.go",
"instances.go",
Expand All @@ -25,11 +24,11 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/awsprovider/v1alpha1:go_default_library",
"//pkg/cloud/aws/converters:go_default_library",
"//pkg/cloud/aws/services/awserrors:go_default_library",
"//pkg/cloud/aws/services/userdata:go_default_library",
"//pkg/cloud/aws/services/wait:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2/ec2iface:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
Expand All @@ -51,6 +50,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/awsprovider/v1alpha1:go_default_library",
"//pkg/cloud/aws/services/awserrors:go_default_library",
"//pkg/cloud/aws/services/ec2/mock_ec2iface:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
Expand Down
10 changes: 6 additions & 4 deletions pkg/cloud/aws/services/ec2/bastion.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/pkg/errors"
"k8s.io/klog"
"sigs.k8s.io/cluster-api-provider-aws/pkg/apis/awsprovider/v1alpha1"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/converters"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/awserrors"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/userdata"
)

Expand Down Expand Up @@ -49,7 +51,7 @@ func (s *Service) ReconcileBastion(clusterName, keyName string, status *v1alpha1

// Describe bastion instance, if any.
instance, err := s.describeBastionInstance(clusterName, status)
if IsNotFound(err) {
if awserrors.IsNotFound(err) {
instance, err = s.runInstance(spec)
if err != nil {
return err
Expand All @@ -73,7 +75,7 @@ func (s *Service) ReconcileBastion(clusterName, keyName string, status *v1alpha1
func (s *Service) DeleteBastion(clusterName string, status *v1alpha1.AWSClusterProviderStatus) error {
instance, err := s.describeBastionInstance(clusterName, status)
if err != nil {
if IsNotFound(err) {
if awserrors.IsNotFound(err) {
klog.V(2).Info("bastion instance does not exist")
return nil
}
Expand Down Expand Up @@ -107,12 +109,12 @@ func (s *Service) describeBastionInstance(clusterName string, status *v1alpha1.A
for _, res := range out.Reservations {
for _, instance := range res.Instances {
if aws.StringValue(instance.State.Name) != ec2.InstanceStateNameTerminated {
return fromSDKTypeToInstance(instance), nil
return converters.SDKToInstance(instance), nil
}
}
}

return nil, NewNotFound(errors.New("bastion host not found"))
return nil, awserrors.NewNotFound(errors.New("bastion host not found"))
}

func (s *Service) getDefaultBastion(clusterName string, region string, network v1alpha1.Network, keyName string) *v1alpha1.Instance {
Expand Down
5 changes: 3 additions & 2 deletions pkg/cloud/aws/services/ec2/eips.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/pkg/errors"
"k8s.io/klog"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/awserrors"
"sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services/wait"
)

Expand Down Expand Up @@ -93,8 +94,8 @@ func (s *Service) releaseAddresses(clusterName string) error {
}

retryableErrors := []string{
errorAuthFailure,
errorInUseIPAddress,
awserrors.AuthFailure,
awserrors.InUseIPAddress,
}

err := wait.WaitForWithRetryable(wait.NewBackoff(), delete, retryableErrors)
Expand Down
Loading

0 comments on commit cafa29b

Please sign in to comment.