package awsat
import (
"fmt"
"reflect"
"strings"
"testing"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template"
)
type ATBuilder struct {
template string
cmdResult *string
expectCalls map[string]int
expectInput map[string]interface{}
ignoredInput map[string]struct{}
fillers map[string]string
expectRevert string
mock mock
graph *graph.Graph
}
func Template(template string) *ATBuilder {
return &ATBuilder{template: template,
expectCalls: make(map[string]int),
expectInput: make(map[string]interface{}),
ignoredInput: make(map[string]struct{}),
}
}
func (b *ATBuilder) ExpectCommandResult(key string) *ATBuilder {
b.cmdResult = &key
return b
}
func (b *ATBuilder) ExpectCalls(expects ...string) *ATBuilder {
for _, expect := range expects {
b.expectCalls[expect]++
}
return b
}
func (b *ATBuilder) ExpectInput(call string, input interface{}) *ATBuilder {
b.expectInput[call] = input
return b
}
func (b *ATBuilder) IgnoreInput(calls ...string) *ATBuilder {
for _, call := range calls {
b.ignoredInput[call] = struct{}{}
}
return b
}
func (b *ATBuilder) Graph(g *graph.Graph) *ATBuilder {
b.graph = g
return b
}
func (b *ATBuilder) Mock(i mock) *ATBuilder {
b.mock = i
return b
}
func (b *ATBuilder) Fillers(fillers map[string]string) *ATBuilder {
b.fillers = fillers
return b
}
func (b *ATBuilder) ExpectRevert(revert string) *ATBuilder {
b.expectRevert = revert
return b
}
func (b *ATBuilder) Run(t *testing.T, l ...*logger.Logger) {
t.Helper()
b.mock.SetInputs(b.expectInput)
b.mock.SetIgnored(b.ignoredInput)
b.mock.SetTesting(t)
tpl, err := template.Parse(b.template)
if err != nil {
t.Fatal(err)
}
if b.graph == nil {
b.graph = graph.NewGraph()
}
awsspec.CommandFactory = NewAcceptanceFactory(b.mock, b.graph, l...)
cenv := template.NewEnv().WithLookupCommandFunc(func(tokens ...string) interface{} {
return awsspec.CommandFactory.Build(strings.Join(tokens, ""))()
}).WithMissingHolesFunc(func(key string, paramPaths []string, isOptional bool) string {
return b.fillers[key]
}).Build()
compiled, cenv, err := template.Compile(tpl, cenv, template.NewRunnerCompileMode)
if err != nil {
t.Fatal(err)
}
ran, err := compiled.Run(template.NewRunEnv(cenv))
if err != nil {
t.Fatal(err)
}
if ran.HasErrors() {
for _, cmd := range ran.CommandNodesIterator() {
if cmd.Err() != nil {
t.Fatal(cmd.Err())
}
}
}
if len(b.expectCalls) > 0 {
if got, want := b.mock.Calls(), b.expectCalls; !reflect.DeepEqual(got, want) {
t.Fatalf("got %#v, want %#v", got, want)
}
}
if b.cmdResult != nil {
if got, want := fmt.Sprint(ran.CommandNodesIterator()[0].Result()), StringValue(b.cmdResult); got != want {
t.Fatalf("got %s, want %s", got, want)
}
}
if b.expectRevert != "" {
revert, err := ran.Revert()
if err != nil {
t.Fatal(err)
}
if got, want := revert.String(), b.expectRevert; got != want {
t.Fatalf("got\n%s\nwant\n%s", got, want)
}
}
}
func StringValue(v *string) string {
if v != nil {
return *v
}
return ""
}
func String(v string) *string {
return &v
}
func Int64(v int64) *int64 {
return &v
}
func Float64(v float64) *float64 {
return &v
}
func Int64AsIntValue(v *int64) int {
if v != nil {
return int(*v)
}
return 0
}
func Bool(v bool) *bool {
return &v
}
func BoolValue(v *bool) bool {
if v != nil {
return *v
}
return false
}
/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsat
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/wallix/awless/cloud"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/logger"
)
type AcceptanceFactory struct {
Mock interface{}
Logger *logger.Logger
Graph cloud.GraphAPI
}
func NewAcceptanceFactory(mock interface{}, g cloud.GraphAPI, l ...*logger.Logger) *AcceptanceFactory {
lg := logger.DiscardLogger
if len(l) > 0 {
lg = l[0]
}
return &AcceptanceFactory{Mock: mock, Graph:g, Logger: lg}
}
func (f *AcceptanceFactory) Build(key string) func() interface{} {
switch key {
case "attachalarm":
return func() interface{} {
cmd := awsspec.NewAttachAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachclassicloadbalancer":
return func() interface{} {
cmd := awsspec.NewAttachClassicLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachcontainertask":
return func() interface{} {
cmd := awsspec.NewAttachContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachelasticip":
return func() interface{} {
cmd := awsspec.NewAttachElasticip(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachinstance":
return func() interface{} {
cmd := awsspec.NewAttachInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachinstanceprofile":
return func() interface{} {
cmd := awsspec.NewAttachInstanceprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachinternetgateway":
return func() interface{} {
cmd := awsspec.NewAttachInternetgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachlistener":
return func() interface{} {
cmd := awsspec.NewAttachListener(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachmfadevice":
return func() interface{} {
cmd := awsspec.NewAttachMfadevice(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachnetworkinterface":
return func() interface{} {
cmd := awsspec.NewAttachNetworkinterface(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachpolicy":
return func() interface{} {
cmd := awsspec.NewAttachPolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachrole":
return func() interface{} {
cmd := awsspec.NewAttachRole(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachroutetable":
return func() interface{} {
cmd := awsspec.NewAttachRoutetable(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachsecuritygroup":
return func() interface{} {
cmd := awsspec.NewAttachSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachuser":
return func() interface{} {
cmd := awsspec.NewAttachUser(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "attachvolume":
return func() interface{} {
cmd := awsspec.NewAttachVolume(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "authenticateregistry":
return func() interface{} {
cmd := awsspec.NewAuthenticateRegistry(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkcertificate":
return func() interface{} {
cmd := awsspec.NewCheckCertificate(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkdatabase":
return func() interface{} {
cmd := awsspec.NewCheckDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkdistribution":
return func() interface{} {
cmd := awsspec.NewCheckDistribution(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkinstance":
return func() interface{} {
cmd := awsspec.NewCheckInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkloadbalancer":
return func() interface{} {
cmd := awsspec.NewCheckLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checknatgateway":
return func() interface{} {
cmd := awsspec.NewCheckNatgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checknetworkinterface":
return func() interface{} {
cmd := awsspec.NewCheckNetworkinterface(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkscalinggroup":
return func() interface{} {
cmd := awsspec.NewCheckScalinggroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checksecuritygroup":
return func() interface{} {
cmd := awsspec.NewCheckSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "checkvolume":
return func() interface{} {
cmd := awsspec.NewCheckVolume(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "copyimage":
return func() interface{} {
cmd := awsspec.NewCopyImage(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "copysnapshot":
return func() interface{} {
cmd := awsspec.NewCopySnapshot(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createaccesskey":
return func() interface{} {
cmd := awsspec.NewCreateAccesskey(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createalarm":
return func() interface{} {
cmd := awsspec.NewCreateAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createappscalingpolicy":
return func() interface{} {
cmd := awsspec.NewCreateAppscalingpolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createappscalingtarget":
return func() interface{} {
cmd := awsspec.NewCreateAppscalingtarget(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createbucket":
return func() interface{} {
cmd := awsspec.NewCreateBucket(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createcertificate":
return func() interface{} {
cmd := awsspec.NewCreateCertificate(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createclassicloadbalancer":
return func() interface{} {
cmd := awsspec.NewCreateClassicLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createcontainercluster":
return func() interface{} {
cmd := awsspec.NewCreateContainercluster(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createdatabase":
return func() interface{} {
cmd := awsspec.NewCreateDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createdbsubnetgroup":
return func() interface{} {
cmd := awsspec.NewCreateDbsubnetgroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createdistribution":
return func() interface{} {
cmd := awsspec.NewCreateDistribution(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createelasticip":
return func() interface{} {
cmd := awsspec.NewCreateElasticip(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createfunction":
return func() interface{} {
cmd := awsspec.NewCreateFunction(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "creategroup":
return func() interface{} {
cmd := awsspec.NewCreateGroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createimage":
return func() interface{} {
cmd := awsspec.NewCreateImage(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createinstance":
return func() interface{} {
cmd := awsspec.NewCreateInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createinstanceprofile":
return func() interface{} {
cmd := awsspec.NewCreateInstanceprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createinternetgateway":
return func() interface{} {
cmd := awsspec.NewCreateInternetgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createkeypair":
return func() interface{} {
cmd := awsspec.NewCreateKeypair(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createlaunchconfiguration":
return func() interface{} {
cmd := awsspec.NewCreateLaunchconfiguration(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createlistener":
return func() interface{} {
cmd := awsspec.NewCreateListener(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createloadbalancer":
return func() interface{} {
cmd := awsspec.NewCreateLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createloginprofile":
return func() interface{} {
cmd := awsspec.NewCreateLoginprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createmfadevice":
return func() interface{} {
cmd := awsspec.NewCreateMfadevice(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createnatgateway":
return func() interface{} {
cmd := awsspec.NewCreateNatgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createnetworkinterface":
return func() interface{} {
cmd := awsspec.NewCreateNetworkinterface(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createpolicy":
return func() interface{} {
cmd := awsspec.NewCreatePolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createqueue":
return func() interface{} {
cmd := awsspec.NewCreateQueue(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createrecord":
return func() interface{} {
cmd := awsspec.NewCreateRecord(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createrepository":
return func() interface{} {
cmd := awsspec.NewCreateRepository(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createrole":
return func() interface{} {
cmd := awsspec.NewCreateRole(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createroute":
return func() interface{} {
cmd := awsspec.NewCreateRoute(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createroutetable":
return func() interface{} {
cmd := awsspec.NewCreateRoutetable(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "creates3object":
return func() interface{} {
cmd := awsspec.NewCreateS3object(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createscalinggroup":
return func() interface{} {
cmd := awsspec.NewCreateScalinggroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createscalingpolicy":
return func() interface{} {
cmd := awsspec.NewCreateScalingpolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createsecuritygroup":
return func() interface{} {
cmd := awsspec.NewCreateSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createsnapshot":
return func() interface{} {
cmd := awsspec.NewCreateSnapshot(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createstack":
return func() interface{} {
cmd := awsspec.NewCreateStack(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createsubnet":
return func() interface{} {
cmd := awsspec.NewCreateSubnet(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createsubscription":
return func() interface{} {
cmd := awsspec.NewCreateSubscription(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createtag":
return func() interface{} {
cmd := awsspec.NewCreateTag(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createtargetgroup":
return func() interface{} {
cmd := awsspec.NewCreateTargetgroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createtopic":
return func() interface{} {
cmd := awsspec.NewCreateTopic(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createuser":
return func() interface{} {
cmd := awsspec.NewCreateUser(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createvolume":
return func() interface{} {
cmd := awsspec.NewCreateVolume(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createvpc":
return func() interface{} {
cmd := awsspec.NewCreateVpc(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "createzone":
return func() interface{} {
cmd := awsspec.NewCreateZone(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteaccesskey":
return func() interface{} {
cmd := awsspec.NewDeleteAccesskey(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletealarm":
return func() interface{} {
cmd := awsspec.NewDeleteAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteappscalingpolicy":
return func() interface{} {
cmd := awsspec.NewDeleteAppscalingpolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteappscalingtarget":
return func() interface{} {
cmd := awsspec.NewDeleteAppscalingtarget(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletebucket":
return func() interface{} {
cmd := awsspec.NewDeleteBucket(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletecertificate":
return func() interface{} {
cmd := awsspec.NewDeleteCertificate(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteclassicloadbalancer":
return func() interface{} {
cmd := awsspec.NewDeleteClassicLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletecontainercluster":
return func() interface{} {
cmd := awsspec.NewDeleteContainercluster(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletecontainertask":
return func() interface{} {
cmd := awsspec.NewDeleteContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletedatabase":
return func() interface{} {
cmd := awsspec.NewDeleteDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletedbsubnetgroup":
return func() interface{} {
cmd := awsspec.NewDeleteDbsubnetgroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletedistribution":
return func() interface{} {
cmd := awsspec.NewDeleteDistribution(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteelasticip":
return func() interface{} {
cmd := awsspec.NewDeleteElasticip(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletefunction":
return func() interface{} {
cmd := awsspec.NewDeleteFunction(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletegroup":
return func() interface{} {
cmd := awsspec.NewDeleteGroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteimage":
return func() interface{} {
cmd := awsspec.NewDeleteImage(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteinstance":
return func() interface{} {
cmd := awsspec.NewDeleteInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteinstanceprofile":
return func() interface{} {
cmd := awsspec.NewDeleteInstanceprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteinternetgateway":
return func() interface{} {
cmd := awsspec.NewDeleteInternetgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletekeypair":
return func() interface{} {
cmd := awsspec.NewDeleteKeypair(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletelaunchconfiguration":
return func() interface{} {
cmd := awsspec.NewDeleteLaunchconfiguration(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletelistener":
return func() interface{} {
cmd := awsspec.NewDeleteListener(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteloadbalancer":
return func() interface{} {
cmd := awsspec.NewDeleteLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteloginprofile":
return func() interface{} {
cmd := awsspec.NewDeleteLoginprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletemfadevice":
return func() interface{} {
cmd := awsspec.NewDeleteMfadevice(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletenatgateway":
return func() interface{} {
cmd := awsspec.NewDeleteNatgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletenetworkinterface":
return func() interface{} {
cmd := awsspec.NewDeleteNetworkinterface(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletepolicy":
return func() interface{} {
cmd := awsspec.NewDeletePolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletequeue":
return func() interface{} {
cmd := awsspec.NewDeleteQueue(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleterecord":
return func() interface{} {
cmd := awsspec.NewDeleteRecord(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleterepository":
return func() interface{} {
cmd := awsspec.NewDeleteRepository(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleterole":
return func() interface{} {
cmd := awsspec.NewDeleteRole(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteroute":
return func() interface{} {
cmd := awsspec.NewDeleteRoute(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteroutetable":
return func() interface{} {
cmd := awsspec.NewDeleteRoutetable(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletes3object":
return func() interface{} {
cmd := awsspec.NewDeleteS3object(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletescalinggroup":
return func() interface{} {
cmd := awsspec.NewDeleteScalinggroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletescalingpolicy":
return func() interface{} {
cmd := awsspec.NewDeleteScalingpolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletesecuritygroup":
return func() interface{} {
cmd := awsspec.NewDeleteSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletesnapshot":
return func() interface{} {
cmd := awsspec.NewDeleteSnapshot(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletestack":
return func() interface{} {
cmd := awsspec.NewDeleteStack(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletesubnet":
return func() interface{} {
cmd := awsspec.NewDeleteSubnet(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletesubscription":
return func() interface{} {
cmd := awsspec.NewDeleteSubscription(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletetag":
return func() interface{} {
cmd := awsspec.NewDeleteTag(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletetargetgroup":
return func() interface{} {
cmd := awsspec.NewDeleteTargetgroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletetopic":
return func() interface{} {
cmd := awsspec.NewDeleteTopic(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deleteuser":
return func() interface{} {
cmd := awsspec.NewDeleteUser(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletevolume":
return func() interface{} {
cmd := awsspec.NewDeleteVolume(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletevpc":
return func() interface{} {
cmd := awsspec.NewDeleteVpc(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "deletezone":
return func() interface{} {
cmd := awsspec.NewDeleteZone(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachalarm":
return func() interface{} {
cmd := awsspec.NewDetachAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachclassicloadbalancer":
return func() interface{} {
cmd := awsspec.NewDetachClassicLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachcontainertask":
return func() interface{} {
cmd := awsspec.NewDetachContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachelasticip":
return func() interface{} {
cmd := awsspec.NewDetachElasticip(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachinstance":
return func() interface{} {
cmd := awsspec.NewDetachInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachinstanceprofile":
return func() interface{} {
cmd := awsspec.NewDetachInstanceprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachinternetgateway":
return func() interface{} {
cmd := awsspec.NewDetachInternetgateway(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachmfadevice":
return func() interface{} {
cmd := awsspec.NewDetachMfadevice(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachnetworkinterface":
return func() interface{} {
cmd := awsspec.NewDetachNetworkinterface(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachpolicy":
return func() interface{} {
cmd := awsspec.NewDetachPolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachrole":
return func() interface{} {
cmd := awsspec.NewDetachRole(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachroutetable":
return func() interface{} {
cmd := awsspec.NewDetachRoutetable(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachsecuritygroup":
return func() interface{} {
cmd := awsspec.NewDetachSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachuser":
return func() interface{} {
cmd := awsspec.NewDetachUser(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "detachvolume":
return func() interface{} {
cmd := awsspec.NewDetachVolume(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "importimage":
return func() interface{} {
cmd := awsspec.NewImportImage(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "restartdatabase":
return func() interface{} {
cmd := awsspec.NewRestartDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "restartinstance":
return func() interface{} {
cmd := awsspec.NewRestartInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "startalarm":
return func() interface{} {
cmd := awsspec.NewStartAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "startcontainertask":
return func() interface{} {
cmd := awsspec.NewStartContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "startdatabase":
return func() interface{} {
cmd := awsspec.NewStartDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "startinstance":
return func() interface{} {
cmd := awsspec.NewStartInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "stopalarm":
return func() interface{} {
cmd := awsspec.NewStopAlarm(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "stopcontainertask":
return func() interface{} {
cmd := awsspec.NewStopContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "stopdatabase":
return func() interface{} {
cmd := awsspec.NewStopDatabase(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "stopinstance":
return func() interface{} {
cmd := awsspec.NewStopInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatebucket":
return func() interface{} {
cmd := awsspec.NewUpdateBucket(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updateclassicloadbalancer":
return func() interface{} {
cmd := awsspec.NewUpdateClassicLoadbalancer(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatecontainertask":
return func() interface{} {
cmd := awsspec.NewUpdateContainertask(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatedistribution":
return func() interface{} {
cmd := awsspec.NewUpdateDistribution(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updateimage":
return func() interface{} {
cmd := awsspec.NewUpdateImage(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updateinstance":
return func() interface{} {
cmd := awsspec.NewUpdateInstance(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updateloginprofile":
return func() interface{} {
cmd := awsspec.NewUpdateLoginprofile(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatepolicy":
return func() interface{} {
cmd := awsspec.NewUpdatePolicy(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updaterecord":
return func() interface{} {
cmd := awsspec.NewUpdateRecord(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updates3object":
return func() interface{} {
cmd := awsspec.NewUpdateS3object(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatescalinggroup":
return func() interface{} {
cmd := awsspec.NewUpdateScalinggroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatesecuritygroup":
return func() interface{} {
cmd := awsspec.NewUpdateSecuritygroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatestack":
return func() interface{} {
cmd := awsspec.NewUpdateStack(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatesubnet":
return func() interface{} {
cmd := awsspec.NewUpdateSubnet(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
case "updatetargetgroup":
return func() interface{} {
cmd := awsspec.NewUpdateTargetgroup(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
}
return nil
}
package awsat
import (
"reflect"
"testing"
)
type mock interface {
Calls() map[string]int
SetInputs(map[string]interface{})
SetIgnored(map[string]struct{})
SetTesting(*testing.T)
}
type basicMock struct { //nolint:unused // embedded in generated mock types
t *testing.T
calls map[string]int
inputs map[string]interface{}
ignored map[string]struct{}
}
func (m *basicMock) addCall(call string) { //nolint:unused
if m.calls == nil {
m.calls = make(map[string]int)
}
m.calls[call]++
}
func (m *basicMock) Calls() map[string]int { //nolint:unused
return m.calls
}
func (m *basicMock) SetTesting(t *testing.T) { //nolint:unused
m.t = t
}
func (m *basicMock) SetInputs(inputs map[string]interface{}) { //nolint:unused
m.inputs = inputs
}
func (m *basicMock) SetIgnored(ignored map[string]struct{}) { //nolint:unused
m.ignored = ignored
}
func (m *basicMock) verifyInput(call string, got interface{}) { //nolint:unused
if m.t == nil {
return
}
if _, ok := m.ignored[call]; ok {
return
}
if want, ok := m.inputs[call]; ok {
if !reflect.DeepEqual(got, want) {
m.t.Fatalf("got \n%#v\n\nwant \n%#v\n", got, want)
}
}
}
package awsconfig
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"text/tabwriter"
"github.com/chzyer/readline"
)
var AWSHomeDir = func() string {
var home string
if runtime.GOOS == "windows" { // Windows
home = os.Getenv("USERPROFILE")
} else {
home = os.Getenv("HOME")
}
return filepath.Join(home, ".aws")
}
func ParseRegion(i string) (interface{}, error) {
if !IsValidRegion(i) {
return i, fmt.Errorf("'%s' is not a valid region", i)
}
return i, nil
}
func ParseInstanceType(i string) (interface{}, error) {
if !isValidInstanceType(i) {
return i, fmt.Errorf("'%s' is not a valid instance type", i)
}
return i, nil
}
func StdinRegionSelector() string {
var regionItems []readline.PrefixCompleterInterface
for _, r := range allRegions() {
regionItems = append(regionItems, readline.PcItem(r))
}
var regionCompleter = readline.NewPrefixCompleter(regionItems...)
fmt.Println("Please enter one region: (Ctrl+C to quit, Tab for completion)")
var region string
rl, err := readline.NewEx(&readline.Config{
Prompt: "> ",
AutoComplete: regionCompleter,
})
if err != nil {
fmt.Fprintf(os.Stderr, "error while selecting region: %s", err)
return ""
}
defer rl.Close()
for !IsValidRegion(region) {
line, err := rl.Readline()
if err == readline.ErrInterrupt || err == io.EOF {
os.Exit(1)
} else if err != nil {
fmt.Fprintf(os.Stderr, "error while selecting region: %s", err)
return ""
}
region = strings.TrimSpace(line)
if !IsValidRegion(region) {
fmt.Fprintf(os.Stderr, "'%s' is not a valid region\n", region)
}
}
return region
}
func StdinInstanceTypeSelector() string {
fmt.Println("Please choose one instance type")
fmt.Println()
fmt.Println("Here are few examples:")
var instanceType string
t := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
fmt.Fprintln(t, "\tinstance type\tvCPU\tMemory (GiB)")
fmt.Fprintln(t, "\tt2.nano\t1\t0.5")
fmt.Fprintln(t, "\tt2.micro\t1\t1")
fmt.Fprintln(t, "\tt2.small\t1\t2")
fmt.Fprintln(t, "\tt2.medium\t2\t4")
fmt.Fprintln(t, "\tt2.large\t2\t8")
fmt.Fprintln(t, "\tt2.xlarge\t4\t16")
fmt.Fprintln(t, "\tt2.2xlarge\t8\t32")
fmt.Fprintln(t, "\tm4.large\t2\t8")
fmt.Fprintln(t, "\tm4.xlarge\t4\t16")
fmt.Fprintln(t, "\tc4.large\t2\t3.75")
fmt.Fprintln(t, "\tc4.xlarge\t4\t7.5")
fmt.Fprintln(t, "\t...")
t.Flush()
fmt.Println()
fmt.Print("Value ? > ")
fmt.Scan(&instanceType)
for !isValidInstanceType(instanceType) {
fmt.Printf("'%s' is not a valid instance type\n", instanceType)
fmt.Print("Value ? > ")
fmt.Scan(&instanceType)
}
return instanceType
}
func IsValidRegion(given string) bool {
reg, _ := regexp.Compile(`^(us|eu|ap|sa|ca|af|me|il|cn)\-\w+\-\d+$`)
regCompound, _ := regexp.Compile(`^(us\-gov|us\-iso|us\-isob|eu\-isoe)\-\w+\-\d+$`)
return reg.MatchString(given) || regCompound.MatchString(given)
}
func isValidInstanceType(given string) bool {
return regexp.MustCompile(`\w+\.\w+`).MatchString(given)
}
func allRegions() []string {
regions := sort.StringSlice{
"af-south-1",
"ap-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"cn-north-1",
"cn-northwest-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-south-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"il-central-1",
"me-central-1",
"me-south-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-gov-east-1",
"us-gov-west-1",
"us-west-1",
"us-west-2",
}
sort.Sort(regions)
return regions
}
func IsValidProfile(given string) bool {
return stringInSlice(given, AllProfiles())
}
var awsHomeFunc func() string = AWSHomeDir
var profileNameRegex = regexp.MustCompile(`\[(.*)\]`)
func AllProfiles() (profiles []string) {
awsHome := awsHomeFunc()
files := []string{filepath.Join(awsHome, "config"), filepath.Join(awsHome, "credentials")}
for _, f := range files {
if _, err := os.Stat(f); err != nil {
continue
}
out, err := os.ReadFile(f)
if err != nil {
continue
}
matches := profileNameRegex.FindAllSubmatch(out, -1)
for _, match := range matches {
profile := string(match[1])
profile = strings.TrimSpace(profile)
profile = strings.TrimPrefix(profile, "profile ")
profile = strings.TrimSpace(profile)
if profile != "" {
profiles = append(profiles, profile)
}
}
}
return profiles
}
func stringInSlice(s string, slice []string) bool {
for _, v := range slice {
if v == s {
return true
}
}
return false
}
/*
Copyright 2017 WALLIX
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 awsconv
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hash/adler32"
"net"
"net/url"
"reflect"
"sync"
"time"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
acmtypes "github.com/aws/aws-sdk-go-v2/service/acm/types"
apigatewayv2types "github.com/aws/aws-sdk-go-v2/service/apigatewayv2/types"
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
cloudfronttypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
cloudtrailtypes "github.com/aws/aws-sdk-go-v2/service/cloudtrail/types"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
efstypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
secretsmanagertypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
snstypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/graph"
)
func InitResource(source interface{}) (*graph.Resource, error) {
var res *graph.Resource
switch ss := source.(type) {
// EC2
case ec2types.Instance:
res = graph.InitResource(cloud.Instance, awssdk.ToString(ss.InstanceId))
case ec2types.Vpc:
res = graph.InitResource(cloud.Vpc, awssdk.ToString(ss.VpcId))
case ec2types.Subnet:
res = graph.InitResource(cloud.Subnet, awssdk.ToString(ss.SubnetId))
case ec2types.SecurityGroup:
res = graph.InitResource(cloud.SecurityGroup, awssdk.ToString(ss.GroupId))
case ec2types.KeyPairInfo:
res = graph.InitResource(cloud.Keypair, awssdk.ToString(ss.KeyName))
case ec2types.Volume:
res = graph.InitResource(cloud.Volume, awssdk.ToString(ss.VolumeId))
case ec2types.Image:
res = graph.InitResource(cloud.Image, awssdk.ToString(ss.ImageId))
case ec2types.ImportImageTask:
res = graph.InitResource(cloud.ImportImageTask, awssdk.ToString(ss.ImportTaskId))
case ec2types.InternetGateway:
res = graph.InitResource(cloud.InternetGateway, awssdk.ToString(ss.InternetGatewayId))
case ec2types.NatGateway:
res = graph.InitResource(cloud.NatGateway, awssdk.ToString(ss.NatGatewayId))
case ec2types.RouteTable:
res = graph.InitResource(cloud.RouteTable, awssdk.ToString(ss.RouteTableId))
case ec2types.AvailabilityZone:
res = graph.InitResource(cloud.AvailabilityZone, awssdk.ToString(ss.ZoneName))
case ec2types.Address:
if awssdk.ToString(ss.AllocationId) != "" {
res = graph.InitResource(cloud.ElasticIP, awssdk.ToString(ss.AllocationId))
} else {
res = graph.InitResource(cloud.ElasticIP, awssdk.ToString(ss.PublicIp))
}
case ec2types.Snapshot:
res = graph.InitResource(cloud.Snapshot, awssdk.ToString(ss.SnapshotId))
case ec2types.NetworkInterface:
res = graph.InitResource(cloud.NetworkInterface, awssdk.ToString(ss.NetworkInterfaceId))
// Loadbalancer
case elbtypes.LoadBalancerDescription:
res = graph.InitResource(cloud.ClassicLoadBalancer, awssdk.ToString(ss.LoadBalancerName))
case elbv2types.LoadBalancer:
res = graph.InitResource(cloud.LoadBalancer, awssdk.ToString(ss.LoadBalancerArn))
case elbv2types.TargetGroup:
res = graph.InitResource(cloud.TargetGroup, awssdk.ToString(ss.TargetGroupArn))
case elbv2types.Listener:
res = graph.InitResource(cloud.Listener, awssdk.ToString(ss.ListenerArn))
// Database
case rdstypes.DBInstance:
res = graph.InitResource(cloud.Database, awssdk.ToString(ss.DBInstanceIdentifier))
case rdstypes.DBSubnetGroup:
res = graph.InitResource(cloud.DbSubnetGroup, awssdk.ToString(ss.DBSubnetGroupArn))
// Autoscaling
case autoscalingtypes.LaunchConfiguration:
res = graph.InitResource(cloud.LaunchConfiguration, awssdk.ToString(ss.LaunchConfigurationARN))
case autoscalingtypes.AutoScalingGroup:
res = graph.InitResource(cloud.ScalingGroup, awssdk.ToString(ss.AutoScalingGroupARN))
case autoscalingtypes.ScalingPolicy:
res = graph.InitResource(cloud.ScalingPolicy, awssdk.ToString(ss.PolicyARN))
// Container
case ecrtypes.Repository:
res = graph.InitResource(cloud.Repository, awssdk.ToString(ss.RepositoryArn))
case ecstypes.Cluster:
res = graph.InitResource(cloud.ContainerCluster, awssdk.ToString(ss.ClusterArn))
case ecstypes.TaskDefinition:
res = graph.InitResource(cloud.ContainerTask, awssdk.ToString(ss.TaskDefinitionArn))
case ecstypes.Container:
res = graph.InitResource(cloud.Container, awssdk.ToString(ss.ContainerArn))
case ecstypes.ContainerInstance:
res = graph.InitResource(cloud.ContainerInstance, awssdk.ToString(ss.ContainerInstanceArn))
// ACM
case acmtypes.CertificateSummary:
res = graph.InitResource(cloud.Certificate, awssdk.ToString(ss.CertificateArn))
// IAM
case iamtypes.User:
res = graph.InitResource(cloud.User, awssdk.ToString(ss.UserId))
case iamtypes.UserDetail:
res = graph.InitResource(cloud.User, awssdk.ToString(ss.UserId))
case iamtypes.RoleDetail:
res = graph.InitResource(cloud.Role, awssdk.ToString(ss.RoleId))
case iamtypes.GroupDetail:
res = graph.InitResource(cloud.Group, awssdk.ToString(ss.GroupId))
case iamtypes.Policy:
res = graph.InitResource(cloud.Policy, awssdk.ToString(ss.PolicyId))
case iamtypes.ManagedPolicyDetail:
res = graph.InitResource(cloud.Policy, awssdk.ToString(ss.PolicyId))
case iamtypes.AccessKeyMetadata:
res = graph.InitResource(cloud.AccessKey, awssdk.ToString(ss.AccessKeyId))
case iamtypes.InstanceProfile:
res = graph.InitResource(cloud.InstanceProfile, awssdk.ToString(ss.InstanceProfileId))
case iamtypes.VirtualMFADevice:
res = graph.InitResource(cloud.MFADevice, awssdk.ToString(ss.SerialNumber))
// S3
case s3types.Bucket:
res = graph.InitResource(cloud.Bucket, awssdk.ToString(ss.Name))
case s3types.Object:
res = graph.InitResource(cloud.S3Object, awssdk.ToString(ss.Key))
//SNS
case snstypes.Subscription:
res = graph.InitResource(cloud.Subscription, awssdk.ToString(ss.Endpoint))
case snstypes.Topic:
res = graph.InitResource(cloud.Topic, awssdk.ToString(ss.TopicArn))
// DNS
case route53types.HostedZone:
res = graph.InitResource(cloud.Zone, awssdk.ToString(ss.Id))
case route53types.ResourceRecordSet:
id := HashFields(awssdk.ToString(ss.Name), string(ss.Type))
res = graph.InitResource(cloud.Record, id)
// Lambda
case lambdatypes.FunctionConfiguration:
res = graph.InitResource(cloud.Function, awssdk.ToString(ss.FunctionArn))
// Monitoring
case cloudwatchtypes.Metric:
id := HashFields(awssdk.ToString(ss.Namespace), awssdk.ToString(ss.MetricName))
res = graph.InitResource(cloud.Metric, id)
case cloudwatchtypes.MetricAlarm:
res = graph.InitResource(cloud.Alarm, awssdk.ToString(ss.AlarmArn))
// cdn
case cloudfronttypes.DistributionSummary:
res = graph.InitResource(cloud.Distribution, awssdk.ToString(ss.Id))
// cloudformation
case cloudformationtypes.Stack:
res = graph.InitResource(cloud.Stack, awssdk.ToString(ss.StackId))
// EKS
case ekstypes.Cluster:
res = graph.InitResource(cloud.EKSCluster, awssdk.ToString(ss.Name))
case ekstypes.Nodegroup:
res = graph.InitResource(cloud.EKSNodeGroup, awssdk.ToString(ss.NodegroupArn))
// DynamoDB
case dynamodbtypes.TableDescription:
res = graph.InitResource(cloud.DynamoDBTable, awssdk.ToString(ss.TableName))
// Secrets Manager
case secretsmanagertypes.SecretListEntry:
res = graph.InitResource(cloud.Secret, awssdk.ToString(ss.ARN))
// KMS
case kmstypes.KeyMetadata:
res = graph.InitResource(cloud.Key, awssdk.ToString(ss.KeyId))
// API Gateway
case apigatewayv2types.Api:
res = graph.InitResource(cloud.ApiGateway, awssdk.ToString(ss.ApiId))
case apigatewayv2types.Route:
res = graph.InitResource(cloud.ApiGatewayRoute, awssdk.ToString(ss.RouteId))
case apigatewayv2types.Stage:
res = graph.InitResource(cloud.ApiGatewayStage, awssdk.ToString(ss.StageName))
// SSM
case ssmtypes.ParameterMetadata:
res = graph.InitResource(cloud.SSMParameter, awssdk.ToString(ss.Name))
// EFS
case efstypes.FileSystemDescription:
res = graph.InitResource(cloud.FileSystem, awssdk.ToString(ss.FileSystemId))
case efstypes.MountTargetDescription:
res = graph.InitResource(cloud.MountTarget, awssdk.ToString(ss.MountTargetId))
// CloudTrail
case cloudtrailtypes.Trail:
res = graph.InitResource(cloud.Trail, awssdk.ToString(ss.TrailARN))
// CloudWatch Logs
case cloudwatchlogstypes.LogGroup:
res = graph.InitResource(cloud.LogGroup, awssdk.ToString(ss.LogGroupName))
default:
return nil, fmt.Errorf("Unknown type of resource %T", source)
}
return res, nil
}
func NewResource(source interface{}) (*graph.Resource, error) {
res, err := InitResource(source)
if err != nil {
return res, err
}
res.Properties()[properties.ID] = res.Id()
value := reflect.ValueOf(source)
if !value.IsValid() {
return nil, fmt.Errorf("can not fetch cloud resource. %v is not valid.", value)
}
var nodeV reflect.Value
if value.Kind() == reflect.Ptr {
if value.IsNil() {
return nil, fmt.Errorf("can not fetch cloud resource. %v is a nil pointer.", value)
}
nodeV = value.Elem()
} else if value.Kind() == reflect.Struct {
nodeV = value
} else {
return nil, fmt.Errorf("can not fetch cloud resource. %v is not a valid struct or pointer.", value)
}
type keyValResult struct {
key string
val interface{}
}
resultc := make(chan keyValResult)
errc := make(chan error)
var wg sync.WaitGroup
for prop, trans := range awsResourcesDef[res.Type()] {
wg.Add(1)
go func(p string, t *propertyTransform) {
defer wg.Done()
if t.transform != nil {
sourceField := nodeV.FieldByName(t.name)
if sourceField.IsValid() && !isNilValue(sourceField) {
val, err := t.transform(sourceField.Interface())
if err == ErrTagNotFound {
return
}
if err != nil {
errc <- fmt.Errorf("type [%s]: prop '%v': %s", res.Type(), p, err)
}
resultc <- keyValResult{p, val}
}
}
if t.fetch != nil {
val, err := t.fetch(source)
if err != nil {
errc <- fmt.Errorf("type [%s]: prop '%v': %s", res.Type(), p, err)
}
resultc <- keyValResult{p, val}
}
}(prop, trans)
}
go func() {
wg.Wait()
close(errc)
close(resultc)
}()
for {
select {
case e := <-errc:
if e != nil {
return res, e
}
case keyVal, ok := <-resultc:
if !ok {
return res, nil
}
res.Properties()[keyVal.key] = keyVal.val
}
}
}
var ErrTagNotFound = errors.New("aws tag key not found")
type propertyTransform struct {
name string
transform transformFn
fetch fetchFn
}
type transformFn func(i interface{}) (interface{}, error)
type fetchFn func(i interface{}) (interface{}, error)
var extractValueFn = func(i interface{}) (interface{}, error) {
iv := reflect.ValueOf(i)
if iv.Kind() == reflect.Ptr {
if iv.IsNil() {
return nil, nil
}
return iv.Elem().Interface(), nil
}
switch ii := i.(type) {
case []string:
return ii, nil
case []*string:
return awssdk.ToStringSlice(ii), nil
case string:
return ii, nil
case int32:
return ii, nil
case int64:
return ii, nil
case bool:
return ii, nil
}
// Handle typed enums (e.g. ec2types.InstanceStateName) which are underlying string/int types
if iv.Kind() == reflect.String {
return iv.String(), nil
}
return nil, fmt.Errorf("extract value: not a pointer or known type but a %T", i)
}
var extractValueAsStringFn = func(i interface{}) (interface{}, error) {
val, err := extractValueFn(i)
return fmt.Sprint(val), err
}
// Extract time forcing timezone to UTC (friendlier when running test in different timezones i.e. travis)
var extractTimeFn = func(i interface{}) (interface{}, error) {
t, ok := i.(*time.Time)
if ok {
return t.UTC(), nil
}
s, ok := i.(string)
if ok {
t, err := time.Parse("2006-01-02T15:04:05.000+0000", s)
if err != nil {
return nil, err
}
return t.UTC(), nil
}
sp, ok := i.(*string)
if ok {
t, err := time.Parse("2006-01-02T15:04:05.000+0000", awssdk.ToString(sp))
if err != nil {
return nil, err
}
return t.UTC(), nil
}
return nil, fmt.Errorf("extract time: expected time pointer, got: %T", i)
}
// Extract time from *int64 representing milliseconds since epoch (e.g., CloudWatch Logs CreationTime)
var extractMillisecondEpochTimeFn = func(i interface{}) (interface{}, error) {
p, ok := i.(*int64)
if ok {
if p == nil {
return nil, nil
}
return time.UnixMilli(*p).UTC(), nil
}
v, ok := i.(int64)
if ok {
return time.UnixMilli(v).UTC(), nil
}
return nil, fmt.Errorf("extract millis epoch time: expected *int64 or int64, got: %T", i)
}
// Extract time that have a Z directly after the time without a space which means UTC
// (https://en.wikipedia.org/wiki/ISO_8601#UTC)
var extractTimeWithZSuffixFn = func(i interface{}) (interface{}, error) {
t, ok := i.(*time.Time)
if ok {
return t.UTC(), nil
}
s, ok := i.(string)
if ok {
t, err := time.Parse("2006-01-02T15:04:05.000Z", s)
if err != nil {
return nil, err
}
return t, nil
}
sp, ok := i.(*string)
if ok {
t, err := time.Parse("2006-01-02T15:04:05.000Z", awssdk.ToString(sp))
if err != nil {
return nil, err
}
return t, nil
}
return nil, fmt.Errorf("extract time: expected time pointer, got: %T", i)
}
var extractIpPermissionSliceFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]ec2types.IpPermission); !ok {
return nil, fmt.Errorf("extract ip permission: not a permission slice but a %T", i)
}
var rules []*graph.FirewallRule
for _, ipPerm := range i.([]ec2types.IpPermission) {
rule := &graph.FirewallRule{}
protocol := awssdk.ToString(ipPerm.IpProtocol)
switch protocol {
case "-1":
rule.Protocol = "any"
rule.PortRange = graph.PortRange{Any: true}
case "tcp", "udp", "icmp", "58":
rule.Protocol = protocol
fromPort := int64(awssdk.ToInt32(ipPerm.FromPort))
toPort := int64(awssdk.ToInt32(ipPerm.ToPort))
if fromPort == -1 || toPort == -1 {
rule.PortRange = graph.PortRange{Any: true}
} else {
rule.PortRange = graph.PortRange{FromPort: fromPort, ToPort: toPort}
}
default:
rule.Protocol = protocol
rule.PortRange = graph.PortRange{Any: true}
}
for _, r := range ipPerm.IpRanges {
_, net, err := net.ParseCIDR(awssdk.ToString(r.CidrIp))
if err != nil {
return rules, err
}
rule.IPRanges = append(rule.IPRanges, net)
}
for _, r := range ipPerm.Ipv6Ranges {
_, net, err := net.ParseCIDR(awssdk.ToString(r.CidrIpv6))
if err != nil {
return rules, err
}
rule.IPRanges = append(rule.IPRanges, net)
}
for _, group := range ipPerm.UserIdGroupPairs {
rule.Sources = append(rule.Sources, awssdk.ToString(group.GroupId))
}
rules = append(rules, rule)
}
return rules, nil
}
var extractNameValueFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]cloudwatchtypes.Dimension); !ok {
return nil, fmt.Errorf("extract ip namevalue: not a dimension slice but a %T", i)
}
var nameValues []*graph.KeyValue
for _, dimension := range i.([]cloudwatchtypes.Dimension) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(dimension.Name), Value: awssdk.ToString(dimension.Value)}
nameValues = append(nameValues, keyval)
}
return nameValues, nil
}
var extractECSAttributesFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]ecstypes.Attribute); !ok {
return nil, fmt.Errorf("extract ECS attributes: not an attribute slice but a %T", i)
}
var keyVals []*graph.KeyValue
for _, attribute := range i.([]ecstypes.Attribute) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(attribute.Name), Value: awssdk.ToString(attribute.Value)}
keyVals = append(keyVals, keyval)
}
return keyVals, nil
}
var extractRouteTableAssociationsFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]ec2types.RouteTableAssociation); !ok {
return nil, fmt.Errorf("extract route table associations: not an association slice but a %T", i)
}
var keyVals []*graph.KeyValue
for _, assoc := range i.([]ec2types.RouteTableAssociation) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(assoc.RouteTableAssociationId), Value: awssdk.ToString(assoc.SubnetId)}
keyVals = append(keyVals, keyval)
}
return keyVals, nil
}
var extractFieldFn = func(field string) transformFn {
return func(i interface{}) (interface{}, error) {
value := reflect.ValueOf(i)
var struc reflect.Value
if value.Kind() == reflect.Ptr {
struc = value.Elem()
if struc.Kind() != reflect.Struct {
return nil, fmt.Errorf("extract field '%s': not a struct pointer but a %T", field, i)
}
} else if value.Kind() == reflect.Struct {
struc = value
} else {
return nil, fmt.Errorf("extract field '%s': not a pointer or struct but a %T", field, i)
}
structField := struc.FieldByName(field)
if !structField.IsValid() {
return nil, fmt.Errorf("extract field: field not found: %s", field)
}
return extractValueFn(structField.Interface())
}
}
var extractTagsFn = func(i interface{}) (interface{}, error) {
var out []string
switch tags := i.(type) {
case []ec2types.Tag:
for _, t := range tags {
out = append(out, fmt.Sprintf("%s=%s", awssdk.ToString(t.Key), awssdk.ToString(t.Value)))
}
case []autoscalingtypes.TagDescription:
for _, t := range tags {
out = append(out, fmt.Sprintf("%s=%s", awssdk.ToString(t.Key), awssdk.ToString(t.Value)))
}
default:
return nil, fmt.Errorf("extract tags: not a tag slice, but a %T", i)
}
return out, nil
}
var extractMapTagsFn = func(i interface{}) (interface{}, error) {
tags, ok := i.(map[string]string)
if !ok {
return nil, fmt.Errorf("extract map tags: not a map[string]string, but a %T", i)
}
var out []string
for k, v := range tags {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
return out, nil
}
var extractSecretsManagerTagsFn = func(i interface{}) (interface{}, error) {
tags, ok := i.([]secretsmanagertypes.Tag)
if !ok {
return nil, fmt.Errorf("extract secrets manager tags: not a tag slice, but a %T", i)
}
var out []string
for _, t := range tags {
out = append(out, fmt.Sprintf("%s=%s", awssdk.ToString(t.Key), awssdk.ToString(t.Value)))
}
return out, nil
}
var extractEFSTagsFn = func(i interface{}) (interface{}, error) {
tags, ok := i.([]efstypes.Tag)
if !ok {
return nil, fmt.Errorf("extract EFS tags: not a tag slice, but a %T", i)
}
var out []string
for _, t := range tags {
out = append(out, fmt.Sprintf("%s=%s", awssdk.ToString(t.Key), awssdk.ToString(t.Value)))
}
return out, nil
}
var extractEFSTagFn = func(key string) transformFn {
return func(i interface{}) (interface{}, error) {
tags, ok := i.([]efstypes.Tag)
if !ok {
return nil, fmt.Errorf("extract EFS tag: not a tag slice, but a %T", i)
}
for _, t := range tags {
if key == awssdk.ToString(t.Key) {
return awssdk.ToString(t.Value), nil
}
}
return nil, ErrTagNotFound
}
}
var extractDynamoDBKeySchemaFn = func(i interface{}) (interface{}, error) {
keys, ok := i.([]dynamodbtypes.KeySchemaElement)
if !ok {
return nil, fmt.Errorf("extract key schema: not a KeySchemaElement slice, but a %T", i)
}
var out []string
for _, k := range keys {
out = append(out, fmt.Sprintf("%s:%s", awssdk.ToString(k.AttributeName), k.KeyType))
}
return out, nil
}
var extractEKSScalingConfigFn = func(i interface{}) (interface{}, error) {
sc, ok := i.(*ekstypes.NodegroupScalingConfig)
if !ok {
return nil, fmt.Errorf("extract scaling config: not a NodegroupScalingConfig pointer, but a %T", i)
}
if sc == nil {
return nil, nil
}
return fmt.Sprintf("min:%d,max:%d,desired:%d", awssdk.ToInt32(sc.MinSize), awssdk.ToInt32(sc.MaxSize), awssdk.ToInt32(sc.DesiredSize)), nil
}
var extractTagFn = func(key string) transformFn {
return func(i interface{}) (interface{}, error) {
tags, ok := i.([]ec2types.Tag)
if !ok {
return nil, fmt.Errorf("extract tag: not a tag slice, but a %T", i)
}
for _, t := range tags {
if key == awssdk.ToString(t.Key) {
return awssdk.ToString(t.Value), nil
}
}
return nil, ErrTagNotFound
}
}
var extractStringPointerSliceValues = func(i interface{}) (interface{}, error) {
switch ss := i.(type) {
case []string:
return ss, nil
case []*string:
return awssdk.ToStringSlice(ss), nil
}
return nil, fmt.Errorf("extract string pointer: not a string slice but a %T", i)
}
var extractStringSliceValues = func(key string) transformFn {
return func(i interface{}) (interface{}, error) {
var res []string
value := reflect.ValueOf(i)
if value.Kind() != reflect.Slice {
return nil, fmt.Errorf("extract slice: not a slice but a %T", i)
}
for i := 0; i < value.Len(); i++ {
e, err := extractFieldFn(key)(value.Index(i).Interface())
if err != nil {
return nil, err
}
str, ok := e.(string)
if !ok {
return nil, fmt.Errorf("extract string slice: not a string but a %T", e)
}
res = append(res, str)
}
return res, nil
}
}
var extractClassicLoadbListenerDescriptionsFn = func(i interface{}) (interface{}, error) {
listeners, ok := i.([]elbtypes.ListenerDescription)
if !ok {
return nil, fmt.Errorf("extract classic loadb listener descriptions: unexpected type %T", i)
}
var out []string
for _, d := range listeners {
if list := d.Listener; list != nil {
out = append(out, fmt.Sprintf("%s:%d:%s:%d", awssdk.ToString(list.Protocol), list.LoadBalancerPort, awssdk.ToString(list.InstanceProtocol), awssdk.ToInt32(list.InstancePort)))
}
}
return out, nil
}
var extractRoutesSliceFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]ec2types.Route); !ok {
return nil, fmt.Errorf("extract route: not a route slice but a %T", i)
}
var routes []*graph.Route
for _, r := range i.([]ec2types.Route) {
route := &graph.Route{}
var err error
if notEmptyStr(r.DestinationCidrBlock) {
if _, route.Destination, err = net.ParseCIDR(awssdk.ToString(r.DestinationCidrBlock)); err != nil {
return nil, err
}
}
if notEmptyStr(r.DestinationIpv6CidrBlock) {
if _, route.DestinationIPv6, err = net.ParseCIDR(awssdk.ToString(r.DestinationIpv6CidrBlock)); err != nil {
return nil, err
}
}
if notEmptyStr(r.DestinationPrefixListId) {
route.DestinationPrefixListId = awssdk.ToString(r.DestinationPrefixListId)
}
if notEmptyStr(r.EgressOnlyInternetGatewayId) {
routeTarget := &graph.RouteTarget{Type: graph.EgressOnlyInternetGatewayTarget, Ref: awssdk.ToString(r.EgressOnlyInternetGatewayId)}
route.Targets = append(route.Targets, routeTarget)
}
if notEmptyStr(r.GatewayId) {
routeTarget := &graph.RouteTarget{Type: graph.GatewayTarget, Ref: awssdk.ToString(r.GatewayId)}
route.Targets = append(route.Targets, routeTarget)
}
if notEmptyStr(r.InstanceId) {
routeTarget := &graph.RouteTarget{Type: graph.InstanceTarget, Ref: awssdk.ToString(r.InstanceId), Owner: awssdk.ToString(r.InstanceOwnerId)}
route.Targets = append(route.Targets, routeTarget)
}
if notEmptyStr(r.NatGatewayId) {
routeTarget := &graph.RouteTarget{Type: graph.NatTarget, Ref: awssdk.ToString(r.NatGatewayId)}
route.Targets = append(route.Targets, routeTarget)
}
if notEmptyStr(r.NetworkInterfaceId) {
routeTarget := &graph.RouteTarget{Type: graph.NetworkInterfaceTarget, Ref: awssdk.ToString(r.NetworkInterfaceId)}
route.Targets = append(route.Targets, routeTarget)
}
if notEmptyStr(r.VpcPeeringConnectionId) {
routeTarget := &graph.RouteTarget{Type: graph.VpcPeeringConnectionTarget, Ref: awssdk.ToString(r.VpcPeeringConnectionId)}
route.Targets = append(route.Targets, routeTarget)
}
routes = append(routes, route)
}
return routes, nil
}
var extractHasATrueBoolInStructSliceFn = func(key string) transformFn {
return func(i interface{}) (interface{}, error) {
var res bool
value := reflect.ValueOf(i)
if value.Kind() != reflect.Slice {
return nil, fmt.Errorf("extract true bool: not a slice but a %T", i)
}
for i := 0; i < value.Len(); i++ {
e, err := extractFieldFn(key)(value.Index(i).Interface())
if err != nil {
return res, err
}
if e == nil {
continue //Empty field
}
b, ok := e.(bool)
if !ok {
return nil, fmt.Errorf("extract true bool: the field %s is not a boolean, but has type: %T", key, e)
}
if b {
res = true
}
}
return res, nil
}
}
var extractDistributionOriginFn = func(i interface{}) (interface{}, error) {
if _, ok := i.(*cloudfronttypes.Origins); !ok {
return nil, fmt.Errorf("extract origins: not a origins pointer but a %T", i)
}
var origins []*graph.DistributionOrigin
for _, o := range i.(*cloudfronttypes.Origins).Items {
origin := &graph.DistributionOrigin{
ID: awssdk.ToString(o.Id),
PublicDNS: awssdk.ToString(o.DomainName),
PathPrefix: awssdk.ToString(o.OriginPath),
}
if o.S3OriginConfig != nil && awssdk.ToString(o.S3OriginConfig.OriginAccessIdentity) != "" {
origin.OriginType = "s3"
origin.Config = awssdk.ToString(o.S3OriginConfig.OriginAccessIdentity)
}
origins = append(origins, origin)
}
return origins, nil
}
var extractStackOutputsFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]cloudformationtypes.Output); !ok {
return nil, fmt.Errorf("extract ouutputs not an output slice but a %T", i)
}
var keyVals []*graph.KeyValue
for _, out := range i.([]cloudformationtypes.Output) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(out.OutputKey), Value: awssdk.ToString(out.OutputValue)}
keyVals = append(keyVals, keyval)
}
return keyVals, nil
}
var extractStackParametersFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]cloudformationtypes.Parameter); !ok {
return nil, fmt.Errorf("extract parameters not a parameter slice but a %T", i)
}
var keyVals []*graph.KeyValue
for _, out := range i.([]cloudformationtypes.Parameter) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(out.ParameterKey), Value: awssdk.ToString(out.ParameterValue)}
keyVals = append(keyVals, keyval)
}
return keyVals, nil
}
var extractContainersImagesFn = func(i interface{}) (interface{}, error) {
if _, ok := i.([]ecstypes.ContainerDefinition); !ok {
return nil, fmt.Errorf("extract containers images, not a container definition slice but a %T", i)
}
var keyVals []*graph.KeyValue
for _, out := range i.([]ecstypes.ContainerDefinition) {
keyval := &graph.KeyValue{KeyName: awssdk.ToString(out.Name), Value: awssdk.ToString(out.Image)}
keyVals = append(keyVals, keyval)
}
return keyVals, nil
}
func extractDocumentDefaultVersion(i interface{}) (interface{}, error) {
if _, ok := i.([]iamtypes.PolicyVersion); !ok {
return nil, fmt.Errorf("extract default version of document, not a policy version slice but a %T", i)
}
for _, version := range i.([]iamtypes.PolicyVersion) {
if version.IsDefaultVersion {
docStr := awssdk.ToString(version.Document)
if str, err := url.QueryUnescape(docStr); err == nil {
var buff bytes.Buffer
err = json.Compact(&buff, []byte(str))
return buff.String(), err
}
return docStr, nil
}
}
return "", nil
}
func extractURLEncodedJson(i interface{}) (interface{}, error) {
var docStr string
switch v := i.(type) {
case *string:
docStr = awssdk.ToString(v)
case string:
docStr = v
default:
return nil, fmt.Errorf("extract URL-encoded JSON, not a string but a %T", i)
}
if str, err := url.QueryUnescape(docStr); err == nil {
var buff bytes.Buffer
err = json.Compact(&buff, []byte(str))
return buff.String(), err
}
return docStr, nil
}
func notEmptyStr(str *string) bool {
return str != nil && *str != ""
}
func isNilValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func:
return v.IsNil()
default:
return false
}
}
func HashFields(fields ...interface{}) string {
var buf bytes.Buffer
for _, field := range fields {
buf.WriteString(fmt.Sprint(field))
}
h := adler32.New()
buf.WriteTo(h)
return "awls-" + hex.EncodeToString(h.Sum(nil))
}
/*
Copyright 2017 WALLIX
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 awsconv
import (
"reflect"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
)
var awsResourcesDef = map[string]map[string]*propertyTransform{
//EC2
cloud.Instance: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Type: {name: "InstanceType", transform: extractValueFn},
properties.Subnet: {name: "SubnetId", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.PublicIP: {name: "PublicIpAddress", transform: extractValueFn},
properties.PrivateIP: {name: "PrivateIpAddress", transform: extractValueFn},
properties.Image: {name: "ImageId", transform: extractValueFn},
properties.Launched: {name: "LaunchTime", transform: extractValueFn},
properties.State: {name: "State", transform: extractFieldFn("Name")},
properties.KeyPair: {name: "KeyName", transform: extractValueFn},
properties.SecurityGroups: {name: "SecurityGroups", transform: extractStringSliceValues("GroupId")},
properties.Affinity: {name: "Placement", transform: extractFieldFn("Affinity")},
properties.AvailabilityZone: {name: "Placement", transform: extractFieldFn("AvailabilityZone")},
properties.PlacementGroup: {name: "Placement", transform: extractFieldFn("GroupName")},
properties.Host: {name: "Placement", transform: extractFieldFn("HostId")},
properties.Architecture: {name: "Architecture", transform: extractValueFn},
properties.Hypervisor: {name: "Hypervisor", transform: extractValueFn},
properties.Profile: {name: "IamInstanceProfile", transform: extractFieldFn("Arn")},
properties.Lifecycle: {fetch: func(i interface{}) (interface{}, error) {
val := reflect.ValueOf(i)
field := val.FieldByName("InstanceLifecycle")
if field.IsValid() {
s := field.String()
if s == "" {
return "on-demand", nil
}
return s, nil
}
return "on-demand", nil
}},
properties.PlatformDetails: {name: "PlatformDetails", transform: extractValueFn},
properties.NetworkInterfaces: {name: "NetworkInterfaces", transform: extractStringSliceValues("NetworkInterfaceId")},
properties.PublicDNS: {name: "PublicDnsName", transform: extractValueFn},
properties.RootDevice: {name: "RootDeviceName", transform: extractValueFn},
properties.RootDeviceType: {name: "RootDeviceType", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.Vpc: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Default: {name: "IsDefault", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.CIDR: {name: "CidrBlock", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.Subnet: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.Public: {name: "MapPublicIpOnLaunch", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.CIDR: {name: "CidrBlock", transform: extractValueFn},
properties.AvailabilityZone: {name: "AvailabilityZone", transform: extractValueFn},
properties.Default: {name: "DefaultForAz", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.SecurityGroup: {
properties.Name: {name: "GroupName", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.InboundRules: {name: "IpPermissions", transform: extractIpPermissionSliceFn},
properties.OutboundRules: {name: "IpPermissionsEgress", transform: extractIpPermissionSliceFn},
properties.Owner: {name: "OwnerId", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.Keypair: {
properties.Fingerprint: {name: "KeyFingerprint", transform: extractValueFn},
},
cloud.Volume: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Type: {name: "VolumeType", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.Size: {name: "Size", transform: extractValueFn},
properties.Encrypted: {name: "Encrypted", transform: extractValueFn},
properties.Created: {name: "CreateTime", transform: extractTimeFn},
properties.AvailabilityZone: {name: "AvailabilityZone", transform: extractValueFn},
properties.Instances: {name: "Attachments", transform: extractStringSliceValues("InstanceId")},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.Snapshot: {
properties.Description: {name: "Description", transform: extractValueFn},
properties.Encrypted: {name: "Encrypted", transform: extractValueFn},
properties.Owner: {name: "OwnerId", transform: extractValueFn},
properties.Progress: {name: "Progress", transform: extractValueFn},
properties.Created: {name: "StartTime", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.Size: {name: "VolumeSize", transform: extractValueFn},
properties.Volume: {name: "VolumeId", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.Image: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Architecture: {name: "Architecture", transform: extractValueFn},
properties.Hypervisor: {name: "Hypervisor", transform: extractValueFn},
properties.Location: {name: "ImageLocation", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Type: {name: "ImageType", transform: extractValueFn},
properties.Public: {name: "Public", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.Created: {name: "CreationDate", transform: extractTimeWithZSuffixFn},
properties.Virtualization: {name: "VirtualizationType", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.InstanceProfile: {
properties.Name: {name: "InstanceProfileName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractValueFn},
properties.Path: {name: "Path", transform: extractValueFn},
properties.Roles: {name: "Roles", transform: extractStringSliceValues("RoleId")},
},
cloud.ImportImageTask: {
properties.Architecture: {name: "Architecture", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Hypervisor: {name: "Hypervisor", transform: extractValueFn},
properties.Image: {name: "ImageId", transform: extractValueFn},
properties.Progress: {name: "Progress", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.StateMessage: {name: "StatusMessage", transform: extractValueFn},
},
cloud.InternetGateway: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Vpcs: {name: "Attachments", transform: extractStringSliceValues("VpcId")},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.NatGateway: {
properties.Created: {name: "CreateTime", transform: extractValueFn},
properties.Subnet: {name: "SubnetId", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
},
cloud.RouteTable: {
properties.Name: {name: "Tags", transform: extractTagFn("Name")},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.Routes: {name: "Routes", transform: extractRoutesSliceFn},
properties.Default: {name: "Associations", transform: extractHasATrueBoolInStructSliceFn("Main")},
properties.Associations: {name: "Associations", transform: extractRouteTableAssociationsFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.AvailabilityZone: {
properties.Name: {name: "ZoneName", transform: extractValueFn},
properties.State: {name: "State", transform: extractValueFn},
properties.Region: {name: "RegionName", transform: extractValueFn},
properties.Messages: {name: "Messages", transform: extractStringSliceValues("Message")},
},
cloud.ElasticIP: {
properties.Name: {name: "PublicIp", transform: extractValueFn},
properties.PublicIP: {name: "PublicIp", transform: extractValueFn},
properties.PrivateIP: {name: "PrivateIpAddress", transform: extractValueFn},
properties.Association: {name: "AssociationId", transform: extractValueFn},
},
cloud.NetworkInterface: {
properties.PublicIP: {name: "Association", transform: extractFieldFn("PublicIp")},
properties.PublicDNS: {name: "Association", transform: extractFieldFn("PublicDnsName")},
properties.Attachment: {name: "Attachment", transform: extractFieldFn("AttachmentId")},
properties.Instance: {name: "Attachment", transform: extractFieldFn("InstanceId")},
properties.InstanceOwner: {name: "Attachment", transform: extractFieldFn("InstanceOwnerId")},
properties.AvailabilityZone: {name: "AvailabilityZone", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.SecurityGroups: {name: "Groups", transform: extractStringSliceValues("GroupId")},
properties.Type: {name: "InterfaceType", transform: extractValueFn},
properties.IPv6Addresses: {name: "Ipv6Addresses", transform: extractStringSliceValues("Ipv6Address")},
properties.MACAddress: {name: "MacAddress", transform: extractValueFn},
properties.Owner: {name: "OwnerId", transform: extractValueFn},
properties.PrivateDNS: {name: "PrivateDnsName", transform: extractValueFn},
properties.PrivateIP: {name: "PrivateIpAddress", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.Subnet: {name: "SubnetId", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.Tags: {name: "TagSet", transform: extractTagsFn},
},
// LoadBalancer
cloud.LoadBalancer: {
properties.Name: {name: "LoadBalancerName", transform: extractValueFn},
properties.Arn: {name: "LoadBalancerArn", transform: extractValueFn},
properties.AvailabilityZones: {name: "AvailabilityZones", transform: extractStringSliceValues("ZoneName")},
properties.Subnets: {name: "AvailabilityZones", transform: extractStringSliceValues("SubnetId")},
properties.Zone: {name: "CanonicalHostedZoneId", transform: extractValueFn},
properties.Created: {name: "CreatedTime", transform: extractTimeFn},
properties.PublicDNS: {name: "DNSName", transform: extractValueFn},
properties.IPType: {name: "IpAddressType", transform: extractValueFn},
properties.Scheme: {name: "Scheme", transform: extractValueFn},
properties.State: {name: "State", transform: extractFieldFn("Code")},
properties.Type: {name: "Type", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
},
cloud.ClassicLoadBalancer: {
properties.Name: {name: "LoadBalancerName", transform: extractValueFn},
properties.AvailabilityZones: {name: "AvailabilityZones", transform: extractValueFn},
properties.Subnets: {name: "Subnets", transform: extractValueFn},
properties.Instances: {name: "Instances", transform: extractStringSliceValues("InstanceId")},
properties.Ports: {name: "ListenerDescriptions", transform: extractClassicLoadbListenerDescriptionsFn},
properties.Zone: {name: "CanonicalHostedZoneNameID", transform: extractValueFn},
properties.Created: {name: "CreatedTime", transform: extractTimeFn},
properties.PublicDNS: {name: "DNSName", transform: extractValueFn},
properties.Scheme: {name: "Scheme", transform: extractValueFn},
properties.Vpc: {name: "VPCId", transform: extractValueFn},
},
cloud.TargetGroup: {
properties.Name: {name: "TargetGroupName", transform: extractValueFn},
properties.Arn: {name: "TargetGroupArn", transform: extractValueFn},
properties.CheckInterval: {name: "HealthCheckIntervalSeconds", transform: extractValueFn},
properties.CheckPath: {name: "HealthCheckPath", transform: extractValueFn},
properties.CheckPort: {name: "HealthCheckPort", transform: extractValueFn},
properties.CheckProtocol: {name: "HealthCheckProtocol", transform: extractValueFn},
properties.CheckTimeout: {name: "HealthCheckTimeoutSeconds", transform: extractValueFn},
properties.CheckHTTPCode: {name: "Matcher", transform: extractFieldFn("HttpCode")},
properties.HealthyThresholdCount: {name: "HealthyThresholdCount", transform: extractValueFn},
properties.UnhealthyThresholdCount: {name: "UnhealthyThresholdCount", transform: extractValueFn},
properties.Port: {name: "Port", transform: extractValueFn},
properties.Protocol: {name: "Protocol", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
},
cloud.Listener: {
properties.Arn: {name: "ListenerArn", transform: extractValueFn},
properties.Certificates: {name: "Certificates", transform: extractStringSliceValues("CertificateArn")},
properties.Actions: {name: "DefaultActions", transform: extractStringSliceValues("Type")},
properties.TargetGroups: {name: "DefaultActions", transform: extractStringSliceValues("TargetGroupArn")},
properties.LoadBalancer: {name: "LoadBalancerArn", transform: extractValueFn},
properties.Port: {name: "Port", transform: extractValueFn},
properties.Protocol: {name: "Protocol", transform: extractValueFn},
properties.CipherSuite: {name: "SslPolicy", transform: extractValueFn},
},
//Database
cloud.Database: {
properties.Storage: {name: "AllocatedStorage", transform: extractValueFn},
properties.AutoUpgrade: {name: "AutoMinorVersionUpgrade", transform: extractValueFn},
properties.AvailabilityZone: {name: "AvailabilityZone", transform: extractValueFn},
properties.BackupRetentionPeriod: {name: "BackupRetentionPeriod", transform: extractValueFn},
properties.CertificateAuthority: {name: "CACertificateIdentifier", transform: extractValueFn},
properties.Charset: {name: "CharacterSetName", transform: extractValueFn},
properties.CopyTagsToSnapshot: {name: "CopyTagsToSnapshot", transform: extractValueFn},
properties.Cluster: {name: "DBClusterIdentifier", transform: extractValueFn},
properties.Arn: {name: "DBInstanceArn", transform: extractValueFn},
properties.Class: {name: "DBInstanceClass", transform: extractValueFn},
properties.State: {name: "DBInstanceStatus", transform: extractValueFn},
properties.Name: {name: "DBName", transform: extractValueFn},
properties.ParameterGroups: {name: "DBParameterGroups", transform: extractStringSliceValues("DBParameterGroupName")},
properties.DBSecurityGroups: {name: "DBSecurityGroups", transform: extractStringSliceValues("DBSecurityGroupName")},
properties.DBSubnetGroup: {name: "DBSubnetGroup", transform: extractFieldFn("DBSubnetGroupName")},
properties.Port: {name: "DbInstancePort", transform: extractValueFn},
properties.GlobalID: {name: "DbiResourceId", transform: extractValueFn},
properties.PublicDNS: {name: "Endpoint", transform: extractFieldFn("Address")},
properties.Zone: {name: "Endpoint", transform: extractFieldFn("HostedZoneId")},
properties.Engine: {name: "Engine", transform: extractValueFn},
properties.EngineVersion: {name: "EngineVersion", transform: extractValueFn},
properties.Created: {name: "InstanceCreateTime", transform: extractValueFn},
properties.IOPS: {name: "Iops", transform: extractValueFn},
properties.LatestRestorableTime: {name: "LatestRestorableTime", transform: extractValueFn},
properties.License: {name: "LicenseModel", transform: extractValueFn},
properties.Username: {name: "MasterUsername", transform: extractValueFn},
properties.MonitoringInterval: {name: "MonitoringInterval", transform: extractValueFn},
properties.MonitoringRole: {name: "MonitoringRoleArn", transform: extractValueFn},
properties.MultiAZ: {name: "MultiAZ", transform: extractValueFn},
properties.OptionGroups: {name: "OptionGroupMemberships", transform: extractStringSliceValues("OptionGroupName")},
properties.PreferredBackupDate: {name: "PreferredBackupWindow", transform: extractValueFn},
properties.PreferredMaintenanceDate: {name: "PreferredMaintenanceWindow", transform: extractValueFn},
properties.Public: {name: "PubliclyAccessible", transform: extractValueFn},
properties.SecondaryAvailabilityZone: {name: "SecondaryAvailabilityZone", transform: extractValueFn},
properties.Encrypted: {name: "StorageEncrypted", transform: extractValueFn},
properties.StorageType: {name: "StorageType", transform: extractValueFn},
properties.Timezone: {name: "Timezone", transform: extractValueFn},
properties.SecurityGroups: {name: "VpcSecurityGroups", transform: extractStringSliceValues("VpcSecurityGroupId")},
properties.ReplicaOf: {name: "ReadReplicaSourceDBInstanceIdentifier", transform: extractValueFn},
},
cloud.DbSubnetGroup: {
properties.Name: {name: "DBSubnetGroupName", transform: extractValueFn},
properties.Arn: {name: "DBSubnetGroupArn", transform: extractValueFn},
properties.Description: {name: "DBSubnetGroupDescription", transform: extractValueFn},
properties.State: {name: "SubnetGroupStatus", transform: extractValueFn},
properties.Subnets: {name: "Subnets", transform: extractStringSliceValues("SubnetIdentifier")},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
},
//Autoscaling
cloud.LaunchConfiguration: {
properties.Name: {name: "LaunchConfigurationName", transform: extractValueFn},
properties.Arn: {name: "LaunchConfigurationARN", transform: extractValueFn},
properties.Public: {name: "AssociatePublicIpAddress", transform: extractValueFn},
properties.Created: {name: "CreatedTime", transform: extractValueFn},
properties.Profile: {name: "IamInstanceProfile", transform: extractValueFn},
properties.Image: {name: "ImageId", transform: extractValueFn},
properties.Type: {name: "InstanceType", transform: extractValueFn},
properties.KeyPair: {name: "KeyName", transform: extractValueFn},
properties.SecurityGroups: {name: "SecurityGroups", transform: extractStringPointerSliceValues},
properties.SpotPrice: {name: "SpotPrice", transform: extractValueFn},
properties.UserData: {name: "Userdata", transform: extractValueFn},
},
cloud.ScalingGroup: {
properties.Name: {name: "AutoScalingGroupName", transform: extractValueFn},
properties.Arn: {name: "AutoScalingGroupARN", transform: extractValueFn},
properties.Created: {name: "CreatedTime", transform: extractValueFn},
properties.DefaultCooldown: {name: "DefaultCooldown", transform: extractValueFn},
properties.DesiredCapacity: {name: "DesiredCapacity", transform: extractValueFn},
properties.HealthCheckGracePeriod: {name: "HealthCheckGracePeriod", transform: extractValueFn},
properties.HealthCheckType: {name: "HealthCheckType", transform: extractValueFn},
properties.LaunchConfigurationName: {name: "LaunchConfigurationName", transform: extractValueFn},
properties.MaxSize: {name: "MaxSize", transform: extractValueFn},
properties.MinSize: {name: "MinSize", transform: extractValueFn},
properties.NewInstancesProtected: {name: "NewInstancesProtectedFromScaleIn", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractTagsFn},
},
cloud.ScalingPolicy: {
properties.Name: {name: "PolicyName", transform: extractValueFn},
properties.Arn: {name: "PolicyARN", transform: extractValueFn},
properties.AdjustmentType: {name: "AdjustmentType", transform: extractValueFn},
properties.AlarmNames: {name: "Alarms", transform: extractStringSliceValues("AlarmName")},
properties.ScalingGroupName: {name: "AutoScalingGroupName", transform: extractValueFn},
properties.Cooldown: {name: "AutoScalingGroupName", transform: extractValueFn},
properties.Type: {name: "PolicyType", transform: extractValueFn},
properties.ScalingAdjustment: {name: "ScalingAdjustment", transform: extractValueFn},
},
//Containers
cloud.Repository: {
properties.Name: {name: "RepositoryName", transform: extractValueFn},
properties.Arn: {name: "RepositoryArn", transform: extractValueFn},
properties.URI: {name: "RepositoryUri", transform: extractValueFn},
properties.Created: {name: "CreatedAt", transform: extractValueFn},
properties.Account: {name: "RegistryId", transform: extractValueFn},
},
cloud.ContainerCluster: {
properties.Name: {name: "ClusterName", transform: extractValueFn},
properties.Arn: {name: "ClusterArn", transform: extractValueFn},
properties.ActiveServicesCount: {name: "ActiveServicesCount", transform: extractValueFn},
properties.PendingTasksCount: {name: "PendingTasksCount", transform: extractValueFn},
properties.RegisteredContainerInstancesCount: {name: "RegisteredContainerInstancesCount", transform: extractValueFn},
properties.RunningTasksCount: {name: "RunningTasksCount", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
},
cloud.ContainerTask: {
properties.Name: {name: "Family", transform: extractValueFn},
properties.Arn: {name: "TaskDefinitionArn", transform: extractValueFn},
properties.ContainersImages: {name: "ContainerDefinitions", transform: extractContainersImagesFn},
properties.Version: {name: "Revision", transform: extractValueAsStringFn},
properties.Role: {name: "TaskRoleArn", transform: extractValueFn},
},
cloud.Container: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Arn: {name: "ContainerArn", transform: extractValueFn},
properties.ExitCode: {name: "ExitCode", transform: extractValueFn},
properties.State: {name: "LastStatus", transform: extractValueFn},
properties.StateMessage: {name: "Reason", transform: extractValueFn},
},
cloud.ContainerInstance: {
properties.Arn: {name: "ContainerInstanceArn", transform: extractValueFn},
properties.AgentConnected: {name: "AgentConnected", transform: extractValueFn},
properties.AgentState: {name: "AgentUpdateStatus", transform: extractValueFn},
properties.Attributes: {name: "Attributes", transform: extractECSAttributesFn},
properties.Instance: {name: "Ec2InstanceId", transform: extractValueFn},
properties.PendingTasksCount: {name: "PendingTasksCount", transform: extractValueFn},
properties.RunningTasksCount: {name: "RunningTasksCount", transform: extractValueFn},
properties.Created: {name: "RegisteredAt", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.Version: {name: "Version", transform: extractValueAsStringFn},
properties.AgentVersion: {name: "VersionInfo", transform: extractFieldFn("AgentVersion")},
properties.DockerVersion: {name: "VersionInfo", transform: extractFieldFn("DockerVersion")},
},
//ACM
cloud.Certificate: {
properties.Arn: {name: "CertificateArn", transform: extractValueFn},
properties.Name: {name: "DomainName", transform: extractValueFn},
},
//IAM
cloud.User: {
properties.Name: {name: "UserName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Path: {name: "Path", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractTimeFn},
properties.PasswordLastUsed: {name: "PasswordLastUsed", transform: extractTimeFn},
properties.InlinePolicies: {name: "UserPolicyList", transform: extractStringSliceValues("PolicyName")},
},
cloud.Role: {
properties.Name: {name: "RoleName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractTimeFn},
properties.Path: {name: "Path", transform: extractValueFn},
properties.InlinePolicies: {name: "RolePolicyList", transform: extractStringSliceValues("PolicyName")},
properties.TrustPolicy: {name: "AssumeRolePolicyDocument", transform: extractURLEncodedJson},
},
cloud.Group: {
properties.Name: {name: "GroupName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractTimeFn},
properties.Path: {name: "Path", transform: extractValueFn},
properties.InlinePolicies: {name: "GroupPolicyList", transform: extractStringSliceValues("PolicyName")},
},
cloud.Policy: {
properties.Name: {name: "PolicyName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractTimeFn},
properties.Updated: {name: "UpdateDate", transform: extractTimeFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Attachable: {name: "IsAttachable", transform: extractValueFn},
properties.Path: {name: "Path", transform: extractValueFn},
properties.Document: {name: "PolicyVersionList", transform: extractDocumentDefaultVersion},
},
cloud.AccessKey: {
properties.Username: {name: "UserName", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.Created: {name: "CreateDate", transform: extractTimeFn},
},
cloud.MFADevice: {
properties.AttachedAt: {name: "EnableDate", transform: extractTimeFn},
},
//S3
cloud.Bucket: {
properties.Created: {name: "CreationDate", transform: extractTimeFn},
},
cloud.S3Object: {
properties.Key: {name: "Key", transform: extractValueFn},
properties.Modified: {name: "LastModified", transform: extractTimeFn},
properties.Owner: {name: "Owner", transform: extractFieldFn("ID")},
properties.Size: {name: "Size", transform: extractValueFn},
properties.Class: {name: "StorageClass", transform: extractValueFn},
},
//Notification
cloud.Subscription: {
properties.Endpoint: {name: "Endpoint", transform: extractValueFn},
properties.Owner: {name: "Owner", transform: extractValueFn},
properties.Protocol: {name: "Protocol", transform: extractValueFn},
properties.Arn: {name: "SubscriptionArn", transform: extractValueFn},
properties.Topic: {name: "TopicArn", transform: extractValueFn},
},
cloud.Topic: {
properties.Arn: {name: "TopicArn", transform: extractValueFn},
},
// DNS
cloud.Zone: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Comment: {name: "Config", transform: extractFieldFn("Comment")},
properties.Private: {name: "Config", transform: extractFieldFn("PrivateZone")},
properties.CallerReference: {name: "CallerReference", transform: extractValueFn},
properties.RecordCount: {name: "ResourceRecordSetCount", transform: extractValueFn},
},
cloud.Record: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Zone: {name: "Zone"},
properties.Failover: {name: "Failover", transform: extractValueFn},
properties.Continent: {name: "GeoLocation", transform: extractFieldFn("ContinentCode")},
properties.Country: {name: "GeoLocation", transform: extractFieldFn("CountryCode")},
properties.HealthCheck: {name: "HealthCheckId", transform: extractValueFn},
properties.Region: {name: "Region", transform: extractValueFn},
properties.Records: {name: "ResourceRecords", transform: extractStringSliceValues("Value")},
properties.Alias: {name: "AliasTarget", transform: extractFieldFn("DNSName")},
properties.Set: {name: "SetIdentifier", transform: extractValueFn},
properties.TTL: {name: "TTL", transform: extractValueFn},
properties.TrafficPolicyInstance: {name: "TrafficPolicyInstanceId", transform: extractValueFn},
properties.Type: {name: "Type", transform: extractValueFn},
properties.Weight: {name: "Weight", transform: extractValueFn},
},
// Lambda
cloud.Function: {
properties.Arn: {name: "FunctionArn", transform: extractValueFn},
properties.Name: {name: "FunctionName", transform: extractValueFn},
properties.Hash: {name: "CodeSha256", transform: extractValueFn},
properties.Size: {name: "CodeSize", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Handler: {name: "Handler", transform: extractValueFn},
properties.Modified: {name: "LastModified", transform: extractTimeFn},
properties.Memory: {name: "MemorySize", transform: extractValueFn},
properties.Role: {name: "Role", transform: extractValueFn},
properties.Runtime: {name: "Runtime", transform: extractValueFn},
properties.Timeout: {name: "Timeout", transform: extractValueFn},
properties.Version: {name: "Version", transform: extractValueFn},
},
// Monitoring
cloud.Metric: {
properties.Name: {name: "MetricName", transform: extractValueFn},
properties.Namespace: {name: "Namespace", transform: extractValueFn},
properties.Dimensions: {name: "Dimensions", transform: extractNameValueFn},
},
cloud.Alarm: {
properties.Name: {name: "AlarmName", transform: extractValueFn},
properties.ActionsEnabled: {name: "ActionsEnabled", transform: extractValueFn},
properties.AlarmActions: {name: "AlarmActions", transform: extractStringPointerSliceValues},
properties.InsufficientDataActions: {name: "InsufficientDataActions", transform: extractStringPointerSliceValues},
properties.OKActions: {name: "OKActions", transform: extractStringPointerSliceValues},
properties.Arn: {name: "AlarmArn", transform: extractValueFn},
properties.Description: {name: "AlarmDescription", transform: extractValueFn},
properties.Dimensions: {name: "Dimensions", transform: extractNameValueFn},
properties.MetricName: {name: "MetricName", transform: extractValueFn},
properties.Namespace: {name: "Namespace", transform: extractValueFn},
properties.Updated: {name: "StateUpdatedTimestamp", transform: extractValueFn},
properties.State: {name: "StateValue", transform: extractValueFn},
},
// CDN
cloud.Distribution: {
properties.Arn: {name: "ARN", transform: extractValueFn},
properties.Aliases: {name: "Aliases", transform: extractFieldFn("Items")},
properties.Comment: {name: "Comment", transform: extractValueFn},
properties.ACMCertificate: {name: "ViewerCertificate", transform: extractFieldFn("ACMCertificateArn")},
properties.Certificate: {name: "ViewerCertificate", transform: extractFieldFn("Certificate")},
properties.TLSVersionRequired: {name: "ViewerCertificate", transform: extractFieldFn("MinimumProtocolVersion")},
properties.SSLSupportMethod: {name: "ViewerCertificate", transform: extractFieldFn("SSLSupportMethod")},
properties.Origins: {name: "Origins", transform: extractDistributionOriginFn},
properties.PublicDNS: {name: "DomainName", transform: extractValueFn},
properties.Enabled: {name: "Enabled", transform: extractValueFn},
properties.HTTPVersion: {name: "HttpVersion", transform: extractValueFn},
properties.IPv6Enabled: {name: "IsIPV6Enabled", transform: extractValueFn},
properties.Modified: {name: "LastModifiedTime", transform: extractValueFn},
properties.PriceClass: {name: "PriceClass", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.WebACL: {name: "WebACLId", transform: extractValueFn},
},
// Cloudformation
cloud.Stack: {
properties.Name: {name: "StackName", transform: extractValueFn},
properties.Capabilities: {name: "Capabilities", transform: extractStringPointerSliceValues},
properties.ChangeSet: {name: "ChangeSetId", transform: extractValueFn},
properties.Created: {name: "CreationTime", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.DisableRollback: {name: "DisableRollback", transform: extractValueFn},
properties.Modified: {name: "LastUpdatedTime", transform: extractValueFn},
properties.Notifications: {name: "NotificationARNs", transform: extractStringPointerSliceValues},
properties.Role: {name: "RoleARN", transform: extractValueFn},
properties.State: {name: "StackStatus", transform: extractValueFn},
properties.StateMessage: {name: "StackStatusReason", transform: extractValueFn},
properties.Parameters: {name: "Parameters", transform: extractStackParametersFn},
properties.Outputs: {name: "Outputs", transform: extractStackOutputsFn},
},
//Queue
cloud.Queue: {}, //Manually set
// EKS
cloud.EKSCluster: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.KubernetesVersion: {name: "Version", transform: extractValueFn},
properties.PlatformVersion: {name: "PlatformVersion", transform: extractValueFn},
properties.RoleArn: {name: "RoleArn", transform: extractValueFn},
properties.Endpoint: {name: "Endpoint", transform: extractValueFn},
properties.Created: {name: "CreatedAt", transform: extractTimeFn},
properties.Tags: {name: "Tags", transform: extractMapTagsFn},
},
cloud.EKSNodeGroup: {
properties.Name: {name: "NodegroupName", transform: extractValueFn},
properties.Arn: {name: "NodegroupArn", transform: extractValueFn},
properties.Cluster: {name: "ClusterName", transform: extractValueFn},
properties.State: {name: "Status", transform: extractValueFn},
properties.Created: {name: "CreatedAt", transform: extractTimeFn},
properties.Subnets: {name: "Subnets", transform: extractStringPointerSliceValues},
properties.Type: {name: "InstanceTypes", transform: extractStringPointerSliceValues},
properties.ScalingConfig: {name: "ScalingConfig", transform: extractEKSScalingConfigFn},
properties.Tags: {name: "Tags", transform: extractMapTagsFn},
},
// DynamoDB
cloud.DynamoDBTable: {
properties.Name: {name: "TableName", transform: extractValueFn},
properties.Arn: {name: "TableArn", transform: extractValueFn},
properties.State: {name: "TableStatus", transform: extractValueFn},
properties.Created: {name: "CreationDateTime", transform: extractTimeFn},
properties.ItemCount: {name: "ItemCount", transform: extractValueFn},
properties.SizeBytes: {name: "TableSizeBytes", transform: extractValueFn},
properties.KeySchema: {name: "KeySchema", transform: extractDynamoDBKeySchemaFn},
},
// Secrets Manager
cloud.Secret: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Arn: {name: "ARN", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Created: {name: "CreatedDate", transform: extractTimeFn},
properties.Modified: {name: "LastChangedDate", transform: extractTimeFn},
properties.LastAccessed: {name: "LastAccessedDate", transform: extractTimeFn},
properties.LastRotated: {name: "LastRotatedDate", transform: extractTimeFn},
properties.RotationEnabled: {name: "RotationEnabled", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractSecretsManagerTagsFn},
},
// KMS
cloud.Key: {
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.Name: {name: "Description", transform: extractValueFn},
properties.State: {name: "KeyState", transform: extractValueFn},
properties.KeyManager: {name: "KeyManager", transform: extractValueFn},
properties.KeyUsage: {name: "KeyUsage", transform: extractValueFn},
properties.Origin: {name: "Origin", transform: extractValueFn},
properties.Created: {name: "CreationDate", transform: extractTimeFn},
properties.Enabled: {name: "Enabled", transform: extractValueFn},
},
// API Gateway
cloud.ApiGateway: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.ApiProtocol: {name: "ProtocolType", transform: extractValueFn},
properties.Endpoint: {name: "ApiEndpoint", transform: extractValueFn},
properties.Created: {name: "CreatedDate", transform: extractTimeFn},
properties.Tags: {name: "Tags", transform: extractMapTagsFn},
},
cloud.ApiGatewayRoute: {
properties.RouteKey: {name: "RouteKey", transform: extractValueFn},
properties.Target: {name: "Target", transform: extractValueFn},
},
cloud.ApiGatewayStage: {
properties.Name: {name: "StageName", transform: extractValueFn},
properties.DeploymentID: {name: "DeploymentId", transform: extractValueFn},
properties.AutoDeploy: {name: "AutoDeploy", transform: extractValueFn},
properties.Created: {name: "CreatedDate", transform: extractTimeFn},
properties.Modified: {name: "LastUpdatedDate", transform: extractTimeFn},
properties.Tags: {name: "Tags", transform: extractMapTagsFn},
},
// SSM
cloud.SSMParameter: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.ParameterType: {name: "Type", transform: extractValueFn},
properties.DataType: {name: "DataType", transform: extractValueFn},
properties.Tier: {name: "Tier", transform: extractValueFn},
properties.Description: {name: "Description", transform: extractValueFn},
properties.Modified: {name: "LastModifiedDate", transform: extractTimeFn},
properties.Version: {name: "Version", transform: extractValueAsStringFn},
},
// EFS
cloud.FileSystem: {
properties.Name: {name: "Tags", transform: extractEFSTagFn("Name")},
properties.Arn: {name: "FileSystemArn", transform: extractValueFn},
properties.State: {name: "LifeCycleState", transform: extractValueFn},
properties.Created: {name: "CreationTime", transform: extractTimeFn},
properties.Encrypted: {name: "Encrypted", transform: extractValueFn},
properties.PerformanceMode: {name: "PerformanceMode", transform: extractValueFn},
properties.ThroughputMode: {name: "ThroughputMode", transform: extractValueFn},
properties.SizeBytes: {name: "SizeInBytes", transform: extractFieldFn("Value")},
properties.NumberOfMountTargets: {name: "NumberOfMountTargets", transform: extractValueFn},
properties.Tags: {name: "Tags", transform: extractEFSTagsFn},
},
cloud.MountTarget: {
properties.Subnet: {name: "SubnetId", transform: extractValueFn},
properties.Vpc: {name: "VpcId", transform: extractValueFn},
properties.IPAddress: {name: "IpAddress", transform: extractValueFn},
properties.LifecycleState: {name: "LifeCycleState", transform: extractValueFn},
},
// CloudTrail
cloud.Trail: {
properties.Name: {name: "Name", transform: extractValueFn},
properties.Arn: {name: "TrailARN", transform: extractValueFn},
properties.IsMultiRegion: {name: "IsMultiRegionTrail", transform: extractValueFn},
properties.S3BucketName: {name: "S3BucketName", transform: extractValueFn},
properties.HomeRegion: {name: "HomeRegion", transform: extractValueFn},
properties.HasCustomEventSelectors: {name: "HasCustomEventSelectors", transform: extractValueFn},
properties.HasInsightSelectors: {name: "HasInsightSelectors", transform: extractValueFn},
},
// CloudWatch Logs
cloud.LogGroup: {
properties.Name: {name: "LogGroupName", transform: extractValueFn},
properties.Arn: {name: "Arn", transform: extractValueFn},
properties.RetentionDays: {name: "RetentionInDays", transform: extractValueFn},
properties.StoredBytes: {name: "StoredBytes", transform: extractValueFn},
properties.Created: {name: "CreationTime", transform: extractMillisecondEpochTimeFn},
},
}
package awsdoc
import (
"bytes"
"fmt"
)
func AwlessCommandDefinitionsDoc(action, entity, fallbackDef string) string {
if v, ok := CommandDefinitionsDoc[action+"."+entity]; ok {
return v
}
return fallbackDef
}
var CommandDefinitionsDoc = map[string]string{
"copy.image": "Copy an EC2 image from given source region to current awless region",
"create.classicloadbalancer": "Create a ELB Classic Loadbalancer (recommended only for EC2 Classic instances).\n\nYou should favor newer AWS load balancers. See `awless create loadbalancer -h`.",
}
func AwlessExamplesDoc(action, entity string) string {
return exampleDoc(action + "." + entity)
}
func exampleDoc(key string) string {
examples, ok := cliExamplesDoc[key]
if ok {
var buf bytes.Buffer
for i, ex := range examples {
buf.WriteString(fmt.Sprintf(" %s", ex))
if i != len(examples)-1 {
buf.WriteByte('\n')
}
}
return buf.String()
}
return ""
}
var cliExamplesDoc = map[string][]string{
"attach.alarm": {},
"attach.containertask": {},
"attach.elasticip": {
"awless attach elasticip id=eipalloc-1c517b26 instance=@redis",
},
"attach.instance": {},
"attach.instanceprofile": {
"awless attach instanceprofile instance=@redis name=MyProfile replace=true",
},
"attach.internetgateway": {
"awless attach internetgateway id=igw-636c0504 vpc=vpc-1aba387c",
},
"attach.listener": {
"awless attach listener certificate=@www.mysite.com id=arn:aws:elasticloadbalancing:.../00683da53db92e54",
"awless attach listener certificate=arn:aws:acm:...a7b691c218 id=arn:aws:elasticloadbalancing:.../00683da53db92e54",
},
"attach.policy": {
"awless attach policy role=MyNewRole service=ec2 access=readonly",
"awless attach policy user=jsmith service=s3 access=readonly",
},
"attach.role": {
"awless attach role instanceprofile=MyProfile name=MyRole",
},
"attach.routetable": {
"awless attach routetable id=rtb-306da254 subnet=@my-subnet",
},
"attach.securitygroup": {
"awless attach securitygroup id=sg-0714247d instance=@redis",
},
"attach.user": {
"awless attach user name=jsmith group=AdminGroup",
},
"attach.volume": {
"awless attach volume id=vol-123oefwejf device=/dev/sdh instance=@redis",
},
"authenticate.registry": {
"awless authenticate registry",
},
"check.database": {
"awless check database id=@mydb state=available timeout=180",
},
"check.distribution": {
"awless check distribution id=@mydistr state=Deployed timeout=180",
},
"check.instance": {
"awless check instance id=@redis state=running timeout=180",
},
"check.loadbalancer": {
"awless check loadbalancer id=@myloadb state=active timeout=180",
},
"check.natgateway": {
"awless check natgateway id=@mynat state=active timeout=180",
},
"check.scalinggroup": {
"awless check scalinggroup name=MyAutoScalingGroup count=3 timeout=180",
},
"check.securitygroup": {
"awless check securitygroup id=@mysshsecgroup state=unused timeout=180",
},
"check.volume": {
"awless check volume id=vol-12r1o3rp state=available timeout=180",
},
"copy.image": {
"awless copy image name=my-ami-name source-id=ami-23or2or source-region=us-west-2",
},
"copy.snapshot": {
"awless copy snapshot source-id=efwqwdr2or source-region=us-west-2",
},
"create.accesskey": {
"awless create accesskey user=jsmith no-prompt=true",
},
"create.alarm": {
" awless create alarm namespace=AWS/EC2 dimensions=AutoScalingGroupName:instancesScalingGroup evaluation-periods=2 metric=CPUUtilization name=scaleinAlarm operator=GreaterThanOrEqualToThreshold period=300 statistic-function=Average threshold=75",
},
"create.appscalingpolicy": {
" awless create appscalingpolicy dimension=ecs:service:DesiredCount name=ScaleOutPolicy resource=service/my-ecs-cluster/my-service-deployment-name service-namespace=ecs stepscaling-adjustment-type=ChangeInCapacity stepscaling-adjustments=0::+1 type=StepScaling stepscaling-aggregation-type=Average stepscaling-cooldown=60",
},
"create.appscalingtarget": {
"awless create appscalingtarget dimension=ecs:service:DesiredCount min-capacity=2 max-capacity=10 resource=service/my-ecs-cluster/my-service-deployment-nameource role=arn:aws:iam::519101889238:role/ecsAutoscaleRole service-namespace=ecs",
},
"create.bucket": {
"awless create bucket name=my-bucket-name acl=public-read",
},
"create.containercluster": {
"awless create containercluster name=mycluster",
},
"create.classicloadbalancer": {
"create classicloadbalancer name=my-loadb subnets=[sub-123,sub-456] listeners=HTTPS:443:HTTP:80 securitygroups=sg-54321",
"create classicloadbalancer healthcheck-path=/health/ping listeners=TCP:80:TCP:8080 tags=Env:Test,Created:Awless",
"create classicloadbalancer listeners=[TCP:5000:TCP:5000,HTTPS:443:HTTP:80]",
},
"create.database": {
"awless create database engine=postgres id=mystartup-prod-db subnetgroup=@my-dbsubnetgroup password=notsafe dbname=mydb size=5 type=db.t2.small username=admin vpcsecuritygroups=@postgres_sg",
},
"create.dbsubnetgroup": {
"awless create dbsubnetgroup name=mydbsubnetgroup description=\"subnets for peps db\" subnets=[@my-firstsubnet, @my-secondsubnet]",
},
"create.distribution": {
"awless create distribution origin-domain=mybucket.s3.amazonaws.com",
},
"create.elasticip": {
"awless create elasticip domain=vpc",
},
"create.function": {},
"create.group": {
"awless create name=admins",
},
"create.image": {
"awless create image instance=@my-instance-name name=redis-image description='redis prod image'",
"awless create image instance=i-0ee436a45561c04df name=redis-image reboot=true",
"awless create image instance=@redis-prod name=redis-prod-image",
},
"create.instance": {
"awless create image=ami-123456 # Start to create instance from specific image",
"awless create instance keypair=jsmith type=t2.micro subnet=@my-subnet",
"awless create instance image=ami-123456 keypair=jsmith",
"awless create instance name=redis type=t2.nano keypair=jsmith userdata=/home/jsmith/data.sh",
"", // create empty line for clarity
"awless create instance distro=redhat type=t2.micro",
"awless create instance distro=coreos name=redis-prod",
"awless create instance distro=redhat::7.2 type=t2.micro",
"awless create instance distro=canonical:ubuntu role=MyInfraReadOnlyRole",
"awless create instance distro=debian:debian:jessie lock=true",
"awless create instance distro=amazonlinux securitygroup=@my-ssh-secgroup",
"awless create instance distro=amazonlinux:::::instance-store",
"awless create instance distro=amazonlinux:amzn2",
},
"create.instanceprofile": {},
"create.internetgateway": {},
"create.keypair": {},
"create.launchconfiguration": {},
"create.listener": {},
"create.loadbalancer": {},
"create.loginprofile": {},
"create.natgateway": {},
"create.policy": {},
"create.queue": {},
"create.record": {},
"create.repository": {},
"create.role": {},
"create.route": {},
"create.routetable": {},
"create.s3object": {},
"create.scalinggroup": {},
"create.scalingpolicy": {},
"create.securitygroup": {
"awless create securitygroup vpc=@myvpc name=ssh-only description=ssh-access",
"(... see more params at `awless update securitygroup -h`)",
},
"create.snapshot": {},
"create.stack": {},
"create.subnet": {},
"create.subscription": {},
"create.tag": {},
"create.targetgroup": {},
"create.topic": {},
"create.user": {},
"create.volume": {},
"create.vpc": {},
"create.zone": {},
"delete.accesskey": {},
"delete.alarm": {},
"delete.appscalingpolicy": {},
"delete.appscalingtarget": {},
"delete.bucket": {},
"delete.containercluster": {},
"delete.containertask": {},
"delete.database": {},
"delete.dbsubnetgroup": {},
"delete.distribution": {},
"delete.elasticip": {},
"delete.function": {},
"delete.group": {},
"delete.image": {},
"delete.instance": {},
"delete.instanceprofile": {},
"delete.internetgateway": {},
"delete.keypair": {},
"delete.launchconfiguration": {},
"delete.listener": {},
"delete.loadbalancer": {},
"delete.loginprofile": {},
"delete.natgateway": {},
"delete.policy": {},
"delete.queue": {},
"delete.record": {},
"delete.repository": {},
"delete.role": {},
"delete.route": {},
"delete.routetable": {},
"delete.s3object": {},
"delete.scalinggroup": {},
"delete.scalingpolicy": {},
"delete.securitygroup": {},
"delete.snapshot": {},
"delete.stack": {},
"delete.subnet": {},
"delete.subscription": {},
"delete.tag": {},
"delete.targetgroup": {},
"delete.topic": {},
"delete.user": {
"awless delete user name=john",
},
"delete.volume": {},
"delete.vpc": {},
"delete.zone": {},
"detach.alarm": {},
"detach.containertask": {},
"detach.elasticip": {},
"detach.instance": {},
"detach.instanceprofile": {},
"detach.internetgateway": {},
"detach.policy": {},
"detach.role": {},
"detach.routetable": {},
"detach.securitygroup": {},
"detach.user": {},
"detach.volume": {},
"import.image": {},
"start.alarm": {},
"start.containertask": {},
"start.instance": {},
"stop.alarm": {},
"stop.containertask": {},
"stop.instance": {},
"update.bucket": {},
"update.classicloadbalancer": {
"awless update classicloadbalancer name=my-loadb health-target=HTTP:80/health health-interval=30 health-timeout=5 healthy-threshold=10 unhealthy-threshold=2",
},
"update.containertask": {},
"update.distribution": {},
"update.instance": {},
"update.image": {
"awless update image id=@my-image description=new-description",
"awless update image id=ami-bd6bb2c5 groups=all operation=add # Make an AMI public",
"awless update image id=ami-bd6bb2c5 groups=all operation=remove # Make an AMI private",
"awless update image id=@my-image accounts=3456728198326 operation=add # Grants launch permission to an AWS account",
"awless update image id=@my-image accounts=[3456728198326,546371829387] operation=remove # Remove launch permission to multiple AWS accounts",
},
"update.loginprofile": {},
"update.policy": {},
"update.record": {},
"update.s3object": {},
"update.scalinggroup": {},
"update.securitygroup": {
"awless update securitygroup id=@ssh-only inbound=authorize protocol=tcp cidr=0.0.0.0/0 portrange=26257",
"awless update securitygroup id=@ssh-only inbound=authorize protocol=tcp securitygroup=sg-123457 portrange=8080",
},
"update.stack": {},
"update.subnet": {},
"update.targetgroup": {},
}
package awsdoc
import "strings"
func TemplateParamsDoc(action, entity, param string) (string, bool) {
doc, ok := paramsDoc[action+"."+entity][param]
return doc, ok
}
func TemplateParamsDocWithEnums(action, entity, param string) (string, bool) {
var suffix string
if enum, ok := EnumDoc[action+"."+entity+"."+param]; ok && len(enum) > 0 && strings.TrimSpace(enum[0]) != "" {
suffix = " (" + strings.Join(enum, " | ") + ")"
}
doc, ok := TemplateParamsDoc(action, entity, param)
return doc + suffix, ok
}
var paramsDoc = map[string]map[string]string{
"attach.alarm": {
"action-arn": "The Amazon Resource Name (ARN) of the action to execute when this alarm transitions to the ALARM state from any other state",
"name": "The Name of the Alarm to update",
},
"attach.classicloadbalancer": {
"instance": "The ID of the instance",
"name": "The name of the Classic load balancer",
},
"attach.containertask": {
"command": "The command that is passed to the container",
"container-name": "The name of a container",
"env": "The environment variables to pass to a container using this format: [key1:val1,key2:val2,...]",
"image": "The image used to start a container. Images in the Docker Hub registry are available by default. Other repositories are specified with repository-url/image:tag",
"memory-hard-limit": "The hard limit (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed",
"name": "The name of the new or existing task containing the container to attach",
"ports": "The list of port mappings for the container. Port mappings allow containers to access ports on the host container instance to send or receive traffic (format [host-port:]container-port[/protocol][,[host-port:]container-port[/protocol]])",
"privileged": "When this parameter is true, the container is given elevated privileges on the host container instance",
"workdir": "The working directory in which to run commands inside the container",
},
"attach.elasticip": {
"allow-reassociation": "Specify false to ensure the operation fails if the Elastic IP address is already associated with another resource",
"id": "The allocation ID",
"instance": "The ID of the instance",
"networkinterface": "The ID of the network interface",
"privateip": "The primary or secondary private IP address to associate with the Elastic IP address",
},
"attach.instance": {
"id": "The ID of the Instance",
"port": "The port on which the Instance is listenning",
"targetgroup": "The Amazon Resource Name (ARN) of the target group",
},
"attach.instanceprofile": {
"instance": "The ID of the Instance",
"name": "The name of the InstanceProfile to associate to the Instance",
"replace": "If 'true' will replace existing instance profile with provided one",
},
"attach.internetgateway": {
"id": "The ID of the Internet gateway",
"vpc": "The ID of the VPC",
},
"attach.listener": {
"certificate": "The awless alias of the certificate's name (ex: @www.mysite.com), or the full certificate's ARN",
"id": "The Amazon Resource Name (ARN) of the listener",
},
"attach.mfadevice": {
"id": "The serial number that uniquely identifies the MFA device",
"mfa-code-1": "An authentication code emitted by the device",
"mfa-code-2": "A subsequent authentication code emitted by the device",
"no-prompt": "Use 'true' to disable the prompt that asks to append the mfadevice to ~/.aws/config file",
"user": "The name of the IAM user for whom you want to enable the MFA device",
},
"attach.networkinterface": {
"device-index": "The index of the device for the network interface attachment",
"id": "The ID of the network interface",
"instance": "The ID of the instance",
},
"attach.policy": {
"access": "Type of access to retrieve an AWS policy",
"arn": "The Amazon Resource Name (ARN) of the IAM policy you want to attach",
"group": "The name (friendly name, not ARN) of the IAM group to attach the policy to",
"role": "The name (friendly name, not ARN) of the IAM role to attach the policy to",
"service": "Service string to retrieve an AWS policy",
"user": "The name (friendly name, not ARN) of the IAM user to attach the policy to",
},
"attach.role": {
"instanceprofile": "The name of the instance profile to update",
"name": "The name of the role to add",
},
"attach.routetable": {
"id": "The ID of the route table",
"subnet": "The ID of the subnet",
},
"attach.securitygroup": {
"id": "The ID of the Security Group to add to the instance",
"instance": "The ID of the Instance",
},
"attach.user": {
"group": "The name of the group to update",
"name": "The name of the user to add",
},
"attach.volume": {
"device": "The device name (for example, /dev/sdh or xvdh)",
"id": "The ID of the EBS volume",
"instance": "The ID of the instance",
},
"authenticate.registry": {
"accounts": "A list of AWS account IDs that are associated with the registries for which to authenticate",
"no-confirm": "Do not ask confirmation before effectively running `docker login` command",
"no-docker-login": "Set to 'true' to disable the prompt and automatic execution of `docker login` command",
},
"check.certificate": {
"arn": "The Amazon Resource Name (ARN) of the certificate to check",
"state": "The state of the certificate to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.database": {
"id": "The ID of the RDS Database to check",
"state": "The state of the RDS Database to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.distribution": {
"id": "The ID of the CloudFront Distribution to check",
"state": "The state of the CloudFront Distribution to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.instance": {
"id": "The ID of the EC2 Instance to check",
"state": "The state of the EC2 Instance to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.loadbalancer": {
"id": "The ID of the ELBv2 Loadbalancer to check",
"state": "The state of the ELBv2 Loadbalancer to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.natgateway": {
"id": "The ID of the NAT Gateway to check",
"state": "The state of the NAT Gateway to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.networkinterface": {
"id": "The ID of the Network Interface to check",
"state": "The state of the Network Interface to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.scalinggroup": {
"count": "The number of Instances + Loadbalancers + TargetGroups in the AutoScaling Group to reach",
"name": "The name of the AutoScaling Group to check",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.securitygroup": {
"id": "The ID of the EC2 Security Group to check",
"state": "The state of the EC2 Security Group to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"check.volume": {
"id": "The ID of the EC2 Volume to check",
"state": "The state of the EC2 Volume to reach",
"timeout": "The time (in seconds) after which the check is failed",
},
"copy.image": {
"description": "A description for the new AMI in the destination region",
"encrypted": "Specifies whether the destination snapshots of the copied image should be encrypted",
"name": "The name of the new AMI in the destination region",
"source-id": "The ID of the AMI to copy",
"source-region": "The name of the region that contains the AMI to copy",
},
"copy.snapshot": {
"description": "A description for the EBS snapshot",
"encrypted": "Specifies whether the destination snapshot should be encrypted",
"source-id": "The ID of the EBS snapshot to copy",
"source-region": "The ID of the region that contains the snapshot to be copied",
},
"create.accesskey": {
"no-prompt": "Deprecated - use the save param",
"save": "Use 'true' to save the access key in ~/.aws/credentials under 'user' profile; use 'false' to disable the prompt",
"user": "The name of the user for which the access key will be generated",
},
"create.alarm": {
"alarm-actions": "The actions to execute when this alarm transitions to the ALARM state from any other state",
"description": "The description for the alarm",
"dimensions": "The dimensions for the metric associated with the alarm",
"enabled": "Indicates whether actions should be executed during any changes to the alarm state",
"evaluation-periods": "The number of periods over which data is compared to the specified threshold",
"insufficientdata-actions": "The actions to execute when this alarm transitions to the INSUFFICIENT_DATA state from any other state",
"metric": "The name for the metric associated with the alarm",
"name": "The name for the alarm",
"namespace": "The namespace for the metric associated with the alarm",
"ok-actions": "The actions to execute when this alarm transitions to an OK state from any other state",
"operator": "The arithmetic operation to use when comparing the specified statistic and threshold",
"period": "The period, in seconds, over which the specified statistic is applied",
"statistic-function": "The statistic for the metric associated with the alarm, other than percentile",
"threshold": "The value against which the specified statistic is compared",
"unit": "The unit of measure for the statistic",
},
"create.appscalingpolicy": {
"dimension": "The scalable dimension associated with the scalable target",
"name": "The name of the scaling policy",
"resource": "The identifier of the resource associated with the scalable target (eg. for ECS: service/cluster-name/service-deployment-name, for EC2 spot-fleet: spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE, for EMR cluster: instancegroup/j-2EEZNYKUA1NTV/ig-1791Y4E1L8YI0, for AppStream 2.0 fleet: fleet/sample-fleet, for DynamoDB table: table/my-table, for DynamoDB global secondary index: table/my-table/index/my-table-index)",
"service-namespace": "The namespace of the AWS service",
"stepscaling-adjustment-type": "The scalable dimension",
"stepscaling-adjustments": "A set of adjustments that enable you to scale based on the size of the alarm breach using this format: [[from]:[to]:scaling-adjustment[,[from]:[to]:scaling-adjustment[,...]]]",
"stepscaling-aggregation-type": "The aggregation type for the CloudWatch metrics",
"stepscaling-cooldown": "The amount of time, in seconds, after a scaling activity completes where previous trigger-related scaling activities can influence future scaling events",
"stepscaling-min-adjustment-magnitude": "The minimum number to adjust your scalable dimension as a result of a scaling activity",
"type": "The policy type",
},
"create.appscalingtarget": {
"dimension": "The scalable dimension associated with the scalable target",
"max-capacity": "The maximum value to scale to in response to a scale out event",
"min-capacity": "The minimum value to scale to in response to a scale in event",
"resource": "The identifier of the resource associated with the scalable target (eg. for ECS: service/cluster-name/service-deployment-name, for EC2 spot-fleet: spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE, for EMR cluster: instancegroup/j-2EEZNYKUA1NTV/ig-1791Y4E1L8YI0, for AppStream 2.0 fleet: fleet/sample-fleet, for DynamoDB table: table/my-table, for DynamoDB global secondary index: table/my-table/index/my-table-index)",
"role": "The ARN of an IAM role that allows Application Auto Scaling to modify the scalable target on your behalf",
"service-namespace": "The namespace of the AWS service",
},
"create.bucket": {
"acl": "The canned ACL to apply to the bucket",
"name": "The name of bucket to create",
},
"create.certificate": {
"domains": "Main and Additional Fully qualified domain names (FQDNs) to be included in the Certificate name and Subject Alternative Name of the ACM Certificate",
"validation-domains": "The domain name that you want ACM to use to send you validation emails. This domain name is the suffix of the email addresses that you want ACM to use. This must be the same as the DomainName value or a superdomain of the domain value",
},
"create.classicloadbalancer": {
"healthcheck-path": "The healthcheck ping path only. Otherwise default to ping port 80 using TCP. Ex: home.html (protocol and port are derived from the first listener)",
"listeners": "The list of listeners. Format for a listener: LOADB_PROTO:LOADB_PORT:INST_PROTO:INST_PORT . Ex: [HTTPS:443:HTTP:8080,TCP:53:TCP:4567]",
"name": "The name of the Classic load balancer",
"scheme": "The nodes of an Internet-facing load balancer have public IP addresses",
"securitygroups": "[Application Load Balancers] The IDs of the security groups to assign to the load balancer",
"subnets": "The IDs of the subnets to attach to the load balancer",
"tags": "A list of tags to assign to the load balancer. Example: tags=Env:Prod,Country:US",
"zones": "At least 1 availability zone from the same region as the load balancer",
},
"create.containercluster": {
"name": "The name of your cluster",
},
"create.database": {
"autoupgrade": "Set to true to indicate that minor version patches are applied automatically",
"availabilityzone": "Specifies the name of the Availability Zone the DB instance is located in",
"backupretention": "Specifies the number of days for which automatic DB snapshots are retained",
"backupwindow": "Specifies the daily time range during which automated backups are created if automated backups are enabled, as determined by the BackupRetentionPeriod (format hh24:mi-hh24:mi)",
"cluster": "If the DB instance is a member of a DB cluster, contains the name of the DB cluster that the DB instance is a member of",
"copytagstosnapshot": "True to copy all tags from the READ replica to snapshots of the READ Replica. Default is false",
"dbname": "The name of the database to create when the DB instance is created",
"dbsecuritygroups": "A list of DB security groups to associate with this DB instance",
"domain": "Specify the Active Directory Domain to create the instance in",
"encrypted": "Specifies whether the DB instance is encrypted",
"engine": "The name of the database engine to be used for this DB instance (not every engine is available for every region)",
"iamrole": "Specify the name of the IAM role to be used when making API calls to the Directory Service",
"id": "Contains a user-supplied database identifier",
"iops": "Specifies the Provisioned IOPS (I/O operations per second) value",
"license": "License model information for this DB instance",
"maintenancewindow": "Specifies the weekly time range during which system maintenance can occur, in Universal Coordinated Time (UTC)",
"multiaz": "Specifies if the DB instance is a Multi-AZ deployment",
"optiongroup": "Indicates that the DB instance should be associated with the specified option group",
"parametergroup": "The name of the DB parameter group to associate with this DB instance",
"password": "The password for the master database user",
"port": "The port number on which the database accepts connections",
"public": "'true' specifies an Internet-facing instance with a publicly resolvable DNS name, which resolves to a public IP address. 'false' specifies an internal instance with a DNS name that resolves to a private IP address",
"replica": "The DB instance identifier of the READ replica",
"replica-source": "The identifier of the DB instance that will act as the source for the READ replica (each DB instance can have up to 5 Read replicas). Use the Amazon Resource Name (ARN) of the database if it is not in the same region",
"size": "Specifies the allocated storage size specified in gigabytes",
"storagetype": "Specifies the storage type associated with DB instance",
"subnetgroup": "A DB subnet group to associate with this DB instance",
"timezone": "The time zone of the DB instance",
"type": "Contains the name of the compute and memory capacity class of the DB instance",
"username": "Contains the master username for the DB instance",
"version": "Indicates the database engine version",
"vpcsecuritygroups": "A list of EC2 VPC security groups to associate with this DB instance",
},
"create.dbsubnetgroup": {
"description": "The description for the DB subnet group",
"name": "The name for the DB subnet group",
"subnets": "The EC2 Subnet IDs for the DB subnet group",
},
"create.distribution": {
"certificate": "The Amazon Resource Name (ARN) of the AWS Certificate Manager (ACM) certificate you want to use for TSL connection",
"comment": "Any comments you want to include about the distribution",
"default-file": "The object that you want CloudFront to request from your origin when a viewer requests the root URL for your distribution (http://www.example.com)",
"domain-aliases": "A list of CNAMEs (alternate domain names), if any, for this distribution",
"enable": "From this field, you can enable or disable the selected distribution",
"forward-cookies": "Specifies which cookies to forward to the origin for this cache behavior",
"forward-queries": "Indicates whether you want CloudFront to forward query strings to the origin that is associated with this cache behavior and cache based on the query string parameters",
"https-behaviour": "The protocol (HTTP or HTTPS) that viewers can use to access the files", //nolint:misspell
"min-ttl": "The minimum amount of time that you want objects to stay in CloudFront caches before CloudFront forwards another request to your origin to determine whether the object has been updated",
"origin-domain": "The DNS name of the Amazon S3 bucket from which you want CloudFront to get objects for this origin, for example, myawsbucket.s3.amazonaws.com",
"origin-path": "An optional element that causes CloudFront to request your content from a directory in your Amazon S3 bucket or your custom origin. When you include this element, specify the directory name, beginning with a /",
"price-class": "The price class that corresponds with the maximum price that you want to pay for CloudFront service. If you specify PriceClass_All, CloudFront responds to requests for your objects from all CloudFront edge locations",
},
"create.elasticip": {
"domain": "Set to vpc to allocate the address for use with instances in a VPC else the address is for use with instances in EC2-Classic",
},
"create.function": {
"bucket": "Amazon S3 bucket name where the .zip file containing your deployment package is stored. This bucket must reside in the same AWS region where you are creating the Lambda function",
"description": "A short, user-defined function description",
"handler": "The function within your code that Lambda calls to begin execution",
"memory": "The amount of memory, in MB, your Lambda function is given",
"name": "The name you want to assign to the function you are uploading",
"object": "The Amazon S3 object (the deployment package) key name you want to upload",
"objectversion": "The Amazon S3 object (the deployment package) version you want to upload",
"publish": "This boolean parameter can be used to request AWS Lambda to create the Lambda function and publish a version as an atomic operation",
"role": "The Amazon Resource Name (ARN) of the IAM role that Lambda assumes when it executes your function to access any other Amazon Web Services (AWS) resources",
"runtime": "The runtime environment for the Lambda function you are uploading",
"timeout": "The function execution time at which Lambda should terminate the function",
"zipfile": "The path toward the zip file containing your deployment package",
},
"create.group": {
"name": "The name of the group to create",
},
"create.image": {
"description": "A description for the new image",
"instance": "The ID of the instance",
"name": "A name for the new image",
"reboot": "True to shut down and reboot the instance before creating the image, otherwise no reboot and file system integrity on the created image cannot be guaranteed",
},
"create.instance": {
"associate-public-ip": "If true, a public IP address will be assigned to the instance. Requires a subnet to be specified",
"count": "The number of instances to launch",
"distro": "The distro query to resolve official community free bare distro AMI from current region. See above description from this help for specific queries. Default choices:",
"image": "The ID of an AMI for the instance to be launched",
"ip": "The primary IPv4 address",
"keypair": "The name of the key pair",
"lock": "If you set this parameter to true, you can't terminate the instance using the Amazon EC2 console, CLI, or API; otherwise, you can",
"ebs-optimized": "Indicates whether the instance is optimized for Amazon EBS I/O. Recommended for instances that benefit from high throughput to EBS",
"name": "The name of the instance to launch",
"role": "The name of the instance profile (role) to launch the instance with",
"securitygroup": "One or more security group IDs",
"subnet": "The ID of the subnet to launch the instance into",
"type": "The instance type",
"userdata": "The user data to make available to the instance",
},
"create.instanceprofile": {
"name": "The name of the instance profile to create",
},
"create.keypair": {
"encrypted": "Set to 'true' if you want to encrypt the keypair",
"name": "The name of the keypair to create (it will also be the name of the file stored in ~/.awless/keys)",
},
"create.launchconfiguration": {
"distro": "The distro query to resolve official community bare distro AMI from current region. See `awless search images -h`",
"image": "The ID of the Amazon Machine Image (AMI) to use to launch your EC2 instances",
"keypair": "The name of the key pair",
"name": "The name of the launch configuration",
"public": "Used for groups that launch instances into a virtual private cloud (VPC). Specifies whether to assign a public IP address to each instance",
"role": "The name or the Amazon Resource Name (ARN) of the instance profile associated with the IAM role for the instance",
"securitygroups": "One or more security groups with which to associate the instances",
"spotprice": "The maximum hourly price to be paid for any Spot Instance launched to fulfill the request",
"type": "The instance type of the EC2 instance",
"userdata": "The user data to make available to the launched EC2 instances",
},
"create.listener": {
"actiontype": "The type of action",
"certificate": "The Amazon Resource Name (ARN) of the certificate",
"loadbalancer": "The Amazon Resource Name (ARN) of the load balancer",
"port": "The port on which the load balancer is listening",
"protocol": "The protocol for connections from clients to the load balancer",
"sslpolicy": "The security policy that defines which ciphers and protocols are supported",
"targetgroup": "The Amazon Resource Name (ARN) of the target group",
},
"create.loadbalancer": {
"iptype": "[Application Load Balancers] The type of IP addresses used by the subnets for your load balancer",
"name": "The name of the load balancer",
"scheme": "The nodes of an Internet-facing load balancer have public IP addresses",
"securitygroups": "[Application Load Balancers] The IDs of the security groups to assign to the load balancer",
"subnet-mappings": "The IDs of the subnets to attach to the load balancer",
"subnets": "The IDs of the subnets to attach to the load balancer",
"type": "The type of load balancer to create",
},
"create.loginprofile": {
"password": "The new password for the user",
"password-reset": "Specifies whether the user is required to set a new password on next sign-in",
"username": "The name of the IAM user to create a password for",
},
"create.mfadevice": {
"name": "The name of the virtual MFA device",
},
"create.natgateway": {
"elasticip-id": "The allocation ID of an Elastic IP address to associate with the NAT gateway",
"subnet": "The subnet in which to create the NAT gateway",
},
"create.networkinterface": {
"description": "A description for the network interface",
"privateip": "The primary private IPv4 address of the network interface",
"securitygroups": "The IDs of one or more security groups",
"subnet": "The ID of the subnet to associate with the network interface",
},
"create.policy": {
"action": "The Action elements describing the actions that will be allowed or denied. You specify a value using a namespace that identifies a service followed by the name of the action to allow or deny (eg. sqs:SendMessage, s3:*). Use a list for multiple actions",
"conditions": "List of conditions necessary for the policy to be in effect (e.g. [aws:UserAgent!=My user agent,s3:prefix=~home/,aws:CurrentTime>=2013-06-30T00:00:00Z,aws:SourceIp!=203.0.113.0/24,aws:SourceArn==arn:aws:sns:eu-west-1:*:*])",
"description": "A friendly description of the policy",
"effect": "The Effect element is required and specifies whether the policy will result in an allow or an explicit deny",
"name": "The friendly name of the policy",
"resource": "The Amazon Resource Name (ARN) of the Resource element which specifies the object or objects that the policy covers",
},
"create.queue": {
"delay": "The length of time, in seconds, for which the delivery of all messages in the queue is delayed. Valid values: An integer from 0 to 900 seconds (15 minutes). The default is 0",
"max-msg-size": "The limit of how many bytes a message can contain before Amazon SQS rejects it. Valid values: An integer from 1024 bytes (1 KiB) to 262144 bytes (256 KiB). The default is 262144 (256 KiB)",
"msg-wait": "The length of time, in seconds, for which a ReceiveMessage action waits for a message to arrive. Valid values: An integer from 0 to 20 (seconds). The default is 0",
"name": "The name of the new queue",
"policy": "The queue's policy",
"redrive-policy": "The parameters for the dead letter queue functionality of the source queue",
"retention-period": "The length of time, in seconds, for which Amazon SQS retains a message. Valid values: An integer from 60 seconds (1 minute) to 1209600 seconds (14 days). The default is 345600 (4 days)",
"visibility-timeout": "The visibility timeout for the queue. Valid values: An integer from 0 to 43200 (12 hours). The default is 30",
},
"create.record": {
"comment": "Any comments you want to include about a change batch request",
"name": "The name of the domain you want to perform the action on. Enter a fully qualified domain name, for example, www.example.com. You can optionally include a trailing dot",
"ttl": "The resource record cache time to live (TTL), in seconds",
"type": "The DNS record type",
"value": "The new DNS record value",
"values": "The new DNS record value(s)",
"zone": "The ID of the hosted zone that contains the resource record sets that you want to change",
},
"create.repository": {
"name": "The name to use for the repository",
},
"create.role": {
"conditions": "List of conditions necessary for the policy to be in effect (e.g. [aws:UserAgent!=My user agent,s3:prefix=~home/,aws:CurrentTime>=2013-06-30T00:00:00Z,aws:SourceIp!=203.0.113.0/24,aws:SourceArn==arn:aws:sns:eu-west-1:*:*])",
"name": "The name of the role to create",
"principal-account": "The ID of the account that can perform actions and access resources of the role (you can know your account ID with `awless whoami`)",
"principal-service": "The AWS Service that can assume this role to perform actions and access resources of the role (e.g. 'ec2.amazonaws.com')",
"principal-user": "The Amazon Resource Name (ARN) of the user that can perform actions and access resources of the role",
"sleep-after": "The amount of time in seconds you want to wait after creating the role (usually used to be sure that the role creation has been propagated)",
},
"create.route": {
"cidr": "The IPv4 CIDR address block used for the destination match",
"gateway": "The ID of an Internet gateway or virtual private gateway attached to your VPC",
"table": "The ID of the route table for the route",
},
"create.routetable": {
"vpc": "The ID of the VPC",
},
"create.s3object": {
"acl": "The canned ACL to apply to the object",
"bucket": "Name of the bucket to which object will be added",
"file": "The path toward to file to upload",
"name": "The name of the Object to create (by default the file name is used)",
},
"create.scalinggroup": {
"cooldown": "The amount of time, in seconds, after a scaling activity completes before another scaling activity can start",
"desired-capacity": "The number of EC2 instances that should be running in the group",
"healthcheck-grace-period": "The amount of time, in seconds, that Auto Scaling waits before checking the health status of an EC2 instance that has come into service",
"healthcheck-type": "The service to use for the health checks",
"launchconfiguration": "The name of the launch configuration",
"max-size": "The maximum size of the group",
"min-size": "The minimum size of the group",
"name": "The name of the Auto Scaling group",
"new-instances-protected": "Indicates whether newly launched instances are protected from termination by Auto Scaling when scaling in",
"subnets": "A comma-separated list of subnet identifiers for your virtual private cloud (VPC)",
"targetgroups": "The Amazon Resource Names (ARN) of the target groups",
},
"create.scalingpolicy": {
"adjustment-magnitude": "The minimum number of instances to scale",
"adjustment-scaling": "The amount by which to scale, based on the specified adjustment type (e.g. '-2', '3')",
"adjustment-type": "The adjustment type",
"cooldown": "The amount of time, in seconds, after a scaling activity completes and before the next scaling activity can start",
"name": "The name of the scaling policy",
"scalinggroup": "The name of the Auto Scaling group",
},
"create.securitygroup": {
"description": "A description for the security group",
"name": "The name of the security group",
"vpc": "The ID of the VPC",
},
"create.snapshot": {
"description": "A description for the snapshot",
"volume": "The ID of the EBS volume",
},
"create.stack": {
"capabilities": "A list of values that you must specify before AWS CloudFormation can create certain stacks",
"disable-rollback": "Set to true to disable rollback of the stack if stack creation failed",
"name": "The name that is associated with the stack",
"notifications": "The Simple Notification Service (SNS) topic ARNs to publish stack related events",
"on-failure": "Determines what action will be taken if stack creation fails",
"parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]",
"policy-file": "The path to the file containing the stack policy body",
"resource-types": "The template resource types that you have permissions to work with for this create stack action, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance",
"role": "The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to create the stack",
"rollback-monitoring-min": "Time to monitor rollback-triggers during and after creation",
"rollback-triggers": "List of CloudWatch Alarm ARNs to monitor during and after creation",
"stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile",
"tags": "Key-value pairs to associate with this stack",
"template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes",
"timeout": "The amount of time that can pass before the stack status becomes CREATE_FAILED; if DisableRollback is not set or is set to false, the stack will be rolled back",
},
"create.subnet": {
"availabilityzone": "The Availability Zone for the subnet",
"cidr": "The IPv4 network range for the subnet, in CIDR notation",
"name": "The 'Name' Tag for the subnet to create",
"public": "A value (true) to indicate that network interfaces created in this subnet should be assigned a public IPv4 address (instances, etc.)",
"vpc": "The ID of the VPC",
},
"create.subscription": {
"endpoint": "The endpoint that you want to receive notifications. Endpoints vary by protocol: For the http or https protocol, the endpoint is a URL beginning with 'http://' or 'https://', for the email or email-json protocol, the endpoint is an email address, for the sms protocol, the endpoint is a phone number of an SMS-enabled, for the sqs protocol, the endpoint is the ARN of an Amazon SQS queue, for the application protocol, the endpoint is the EndpointArn of a mobile app and device, for the lambda protocol, the endpoint is the ARN of an AWS Lambda function",
"protocol": "The protocol you want to use",
"topic": "The ARN of the topic you want to subscribe to",
},
"create.tag": {
"key": "The Tag key",
"resource": "The ID of the resource on which you want to add a tag",
"value": "The Tag value",
},
"create.targetgroup": {
"healthcheckinterval": "The approximate amount of time, in seconds, between health checks of an individual target",
"healthcheckpath": "[HTTP/HTTPS health checks] The ping path that is the destination on the targets for health checks",
"healthcheckport": "The port the load balancer uses when performing health checks on targets",
"healthcheckprotocol": "The protocol the load balancer uses when performing health checks on targets",
"healthchecktimeout": "The amount of time, in seconds, during which no response from a target means a failed health check",
"healthythreshold": "The number of consecutive health checks successes required before considering an unhealthy target healthy",
"matcher": "The HTTP codes to use when checking for a successful response from a target",
"name": "The name of the target group",
"port": "The port on which the targets receive traffic",
"protocol": "The protocol to use for routing traffic to the targets",
"unhealthythreshold": "The number of consecutive health check failures required before considering a target unhealthy",
"vpc": "The identifier of the virtual private cloud (VPC)",
},
"create.topic": {
"name": "The name of the topic you want to create",
},
"create.user": {
"name": "The name of the user to create",
},
"create.volume": {
"availabilityzone": "The Availability Zone in which to create the volume",
"size": "The size of the volume, in GiBs",
},
"create.vpc": {
"cidr": "The IPv4 network range for the VPC, in CIDR notation",
"name": "The 'Name' Tag for the VPC to create",
},
"create.zone": {
"callerreference": "A unique string that identifies the request and that allows failed CreateHostedZone requests to be retried without the risk of executing the operation twice",
"comment": "Any comments that you want to include about the hosted zone",
"delegationsetid": "If you want to associate a reusable delegation set with this hosted zone, the ID that Amazon Route 53 assigned to the reusable delegation set when you created it",
"isprivate": "A value that indicates whether this is a private hosted zone",
"name": "The name of the domain",
"vpcid": "(Private hosted zones only) The ID of an Amazon VPC",
"vpcregion": "(Private hosted zones only) The region in which you created an Amazon VPC",
},
"delete.accesskey": {
"id": "The ID of the access key and secret access key you want to delete",
"user": "The name of the user whose access key pair you want to delete",
},
"delete.alarm": {
"name": "The name of the alarm(s) to be deleted",
},
"delete.appscalingpolicy": {
"dimension": "The scalable dimension",
"name": "The name of the scaling policy",
"resource": "The identifier of the resource associated with the scalable target",
"service-namespace": "The namespace of the AWS service",
},
"delete.appscalingtarget": {
"dimension": "The scalable dimension associated with the scalable target",
"resource": "The identifier of the resource associated with the scalable target",
"service-namespace": "The namespace of the AWS service",
},
"delete.bucket": {
"name": "The name of the bucket to be deleted",
},
"delete.certificate": {
"arn": "String that contains the ARN of the ACM Certificate to be deleted",
},
"delete.classicloadbalancer": {
"name": "The name of the Classic load balancer",
},
"delete.containercluster": {
"id": "The short name or full Amazon Resource Name (ARN) of the cluster to delete",
},
"delete.containertask": {
"all-versions": "Set to 'true' to delete all existing versions of the containertask to be deleted",
"name": "The name of the containertask to be deleted",
},
"delete.database": {
"id": "The ID of the database to be deleted",
"skip-snapshot": "Determines whether a final DB snapshot is created before the DB instance is deleted. If true is specified, no DBSnapshot is created. If false is specified, a DB snapshot is created before the DB instance is deleted",
"snapshot": "The ID of the new DBSnapshot created when skip-snapshot=false",
},
"delete.dbsubnetgroup": {
"name": "The name of the database subnet group to be deleted",
},
"delete.distribution": {
"id": "The ID of the distribution to be deleted",
},
"delete.elasticip": {
"id": "The allocation ID",
"ip": "The Elastic IP address",
},
"delete.function": {
"id": "The ID of the Lambda function to be deleted",
"version": "Using this optional parameter you can specify a function version (but not the $LATEST version) to direct AWS Lambda to delete a specific function version",
},
"delete.group": {
"name": "The name of the IAM group to delete",
},
"delete.image": {
"delete-snapshots": "Set to 'true' to also delete the snapshots created from this image",
"id": "The ID of the AMI to be deleted",
},
"delete.instance": {
"id": "The ID of the instance(s) to be deleted",
"ids": "The ID(s) of the instance(s) to be deleted",
},
"delete.instanceprofile": {
"name": "The name of the instance profile to delete",
},
"delete.internetgateway": {
"id": "The ID of the Internet gateway to be deleted",
},
"delete.keypair": {
"name": "The name of the key pair to be deleted",
},
"delete.launchconfiguration": {
"name": "The name of the launch configuration to be deleted",
},
"delete.listener": {
"id": "The Amazon Resource Name (ARN) of the listener",
},
"delete.loadbalancer": {
"id": "The Amazon Resource Name (ARN) of the load balancer",
},
"delete.loginprofile": {
"username": "The name of the user whose password you want to delete",
},
"delete.mfadevice": {
"id": "The serial number that uniquely identifies the MFA device",
},
"delete.natgateway": {
"id": "The ID of the NAT gateway",
},
"delete.networkinterface": {
"id": "The ID of the network interface",
},
"delete.policy": {
"all-versions": "Set to 'true' to delete all existing versions of the policy to be deleted",
"arn": "The Amazon Resource Name (ARN) of the IAM policy you want to delete",
},
"delete.queue": {
"url": "The URL of the Amazon SQS queue to delete",
},
"delete.record": {
"id": "The awless id (cf `awless list records`) of the record to delete",
"name": "The name of the domain you want to perform the action on. Enter a fully qualified domain name, for example, www.example.com. You can optionally include a trailing dot",
"ttl": "The resource record cache time to live (TTL), in seconds",
"type": "The DNS record type",
"value": "The DNS record value to delete",
"values": "The DNS record value(s) to delete",
"zone": "The ID of the hosted zone that contains the resource record sets that you want to delete",
},
"delete.repository": {
"account": "The AWS account ID associated with the registry that contains the repository to delete",
"force": "If a repository contains images, forces the deletion",
"name": "The name of the repository to delete",
},
"delete.role": {
"name": "The name of the role to be deleted",
},
"delete.route": {
"cidr": "The IPv4 CIDR range for the route",
"table": "The ID of the route table",
},
"delete.routetable": {
"id": "The ID of the route table",
},
"delete.s3object": {
"bucket": "The name of the bucket containing the object to be deleted",
"name": "The name (i.e. key) of the object to be deleted",
},
"delete.scalinggroup": {
"force": "Specifies that the group will be deleted along with all instances associated with the group, without waiting for all instances to be terminated",
"name": "The name of the Auto Scaling group",
},
"delete.scalingpolicy": {
"id": "The name or Amazon Resource Name (ARN) of the policy",
},
"delete.securitygroup": {
"id": "The ID of the security group",
},
"delete.snapshot": {
"id": "The ID of the EBS snapshot",
},
"delete.stack": {
"name": "The name or the unique stack ID that is associated with the stack",
"retain-resources": "For stacks in the DELETE_FAILED state, a list of resource logical IDs that are associated with the resources you want to retain",
},
"delete.subnet": {
"id": "The ID of the subnet",
},
"delete.subscription": {
"id": "The ARN of the subscription to be deleted",
},
"delete.tag": {
"key": "The Tag key",
"resource": "The ID of the resource on which you want to remove a tag",
"value": "The Tag value",
},
"delete.targetgroup": {
"id": "The Amazon Resource Name (ARN) of the target group",
},
"delete.topic": {
"id": "The ARN of the topic you want to delete",
},
"delete.user": {
"name": "The name of the user to delete",
},
"delete.volume": {
"id": "The ID of the volume",
},
"delete.vpc": {
"id": "The ID of the VPC",
},
"delete.zone": {
"id": "The ID of the hosted zone you want to delete",
},
"detach.alarm": {
"action-arn": "The Amazon Resource Name (ARN) to be detached of the ALARM actions",
"name": "The name of the alarm",
},
"detach.classicloadbalancer": {
"instance": "The ID of the instance",
"name": "The name of the Classic load balancer",
},
"detach.containertask": {
"container-name": "The name of the container to detach",
"name": "The name of the existing container task containing the container to detach",
},
"detach.elasticip": {
"association": "The association ID",
},
"detach.instance": {
"id": "The ID of the instance to be detached from target group",
"targetgroup": "The Amazon Resource Name (ARN) of the target group",
},
"detach.instanceprofile": {
"instance": "The ID of the Instance",
"name": "The name of the InstanceProfile to detach from the Instance",
},
"detach.internetgateway": {
"id": "The ID of the Internet gateway",
"vpc": "The ID of the VPC",
},
"detach.mfadevice": {
"id": "The serial number that uniquely identifies the MFA device",
"user": "The name of the user whose MFA device you want to deactivate",
},
"detach.networkinterface": {
"attachment": "The ID of the attachment",
"force": "Specifies whether to force a detachment",
"id": "The ID of the network interface",
"instance": "The ID of the instance this network interface is attached to",
},
"detach.policy": {
"access": "Type of access to retrieve an AWS policy",
"arn": "The Amazon Resource Name (ARN) of the IAM policy you want to detach",
"group": "The name (friendly name, not ARN) of the IAM group to detach the policy to",
"role": "The name (friendly name, not ARN) of the IAM role to detach the policy to",
"service": "Service string to retrieve an AWS policy",
"user": "The name (friendly name, not ARN) of the IAM user to detach the policy to",
},
"detach.role": {
"instanceprofile": "The name of the instance profile to update",
"name": "The name of the role to remove",
},
"detach.routetable": {
"association": "The association ID representing the current association between the route table and subnet",
},
"detach.securitygroup": {
"id": "The ID of the security group",
"instance": "The ID of the instance to be detached",
},
"detach.user": {
"group": "The name of the group to update",
"name": "The name of the user to remove",
},
"detach.volume": {
"device": "The device name",
"force": "Forces detachment if the previous detachment attempt did not occur cleanly (for example, logging into an instance, unmounting the volume, and detaching normally)",
"id": "The ID of the volume",
"instance": "The ID of the instance",
},
"import.image": {
"architecture": "The architecture of the virtual machine",
"bucket": "The name of the S3 bucket where the disk image is located",
"description": "A description string for the import image task",
"license": "The license type to be used for the Amazon Machine Image (AMI) after importing",
"platform": "The operating system of the virtual machine",
"role": "The name of the role to use when not using the default role, 'vmimport'",
"s3object": "The name of the S3 object where the disk image is located",
"snapshot": "The ID of the EBS snapshot to be used for importing the snapshot",
"url": "The URL to the Amazon S3-based disk image being imported. The URL can either be a https URL (https://..) or an Amazon S3 URL (s3://..)",
},
"restart.database": {
"id": "Contains a user-supplied database identifier",
"with-failover": "When true, the reboot is conducted through a MultiAZ failover",
},
"restart.instance": {
"id": "The ID of the instance to be restarted",
"ids": "One or more instance IDs",
},
"start.alarm": {
"names": "The names of the alarms",
},
"start.containertask": {
"cluster": "The short name or full Amazon Resource Name (ARN) of the cluster on which to run your task",
"deployment-name": "The deployment name of the service (e.g. prod, staging...)",
"desired-count": "The number of instantiations of the specified service to place and keep running on your cluster",
"loadbalancer.container-name": "The name of the container (as it appears in a container definition) to associate with the load balancer",
"loadbalancer.container-port": "The port on the container to associate with the load balancer",
"loadbalancer.targetgroup": "The full Amazon Resource Name (ARN) of the Elastic Load Balancing target group associated with a service",
"name": "The name of the container task to start",
"role": "The name or full Amazon Resource Name (ARN) of the IAM role that allows Amazon ECS to make calls to your load balancer on your behalf",
"type": "The type of task to launch",
},
"start.database": {
"id": "Contains a user-supplied database identifier",
},
"start.instance": {
"id": "The ID of the instance to be started",
"ids": "One or more instance IDs",
},
"stop.alarm": {
"names": "The names of the alarms",
},
"stop.containertask": {
"cluster": "The short name or full Amazon Resource Name (ARN) of the cluster on which to run your task",
"deployment-name": "The deployment name of the service (e.g. prod, staging...)",
"run-arn": "The ID or full Amazon Resource Name (ARN) entry of the run of the task to stop",
"type": "The type of task to launch",
},
"stop.database": {
"id": "Contains a user-supplied database identifier",
},
"stop.instance": {
"id": "The ID of the instance to be stopped",
"ids": "One or more instance IDs",
},
"update.bucket": {
"acl": "The canned ACL to apply to the bucket",
"enforce-https": "Use HTTPS rather than HTTP when redirecting requests",
"index-suffix": "A suffix that is appended to a request that is for a directory on the website endpoint",
"name": "The name of the bucket to update",
"public-website": "Set to 'true' if you want to publish the content of the bucket as a public HTTP website",
"redirect-hostname": "Hostname where HTTP requests will be redirected when publishing website",
},
"update.classicloadbalancer": {
"health-interval": "The approximate interval, in seconds, between health checks of an individual instance",
"health-target": "String with format PROTOCOL:PORT[PING_PATH]. For HTTP/HTTPS, you must include a ping path. Protocols: TCP, HTTP, HTTPS, or SSL. Ex: TCP:5000, or HTTP:80/weather/us/wa/seattle",
"health-timeout": "The amount of time, in seconds, during which no response means a failed health check. This value must be less than the Interval value",
"healthy-threshold": "The number of consecutive health checks successes required before moving the instance to the Healthy state",
"name": "The name of the load balancer",
"unhealthy-threshold": "The number of consecutive health check failures required before moving the instance to the Unhealthy state",
},
"update.containertask": {
"cluster": "The short name or full Amazon Resource Name (ARN) of the cluster that your service is running on",
"deployment-name": "The name of the service to update",
"desired-count": "The number of instantiations of the task to place and keep running in your service",
"name": "The family and revision (family:revision) or full ARN of the task definition to run in your service",
},
"update.distribution": {
"certificate": "The Amazon Resource Name (ARN) of the AWS Certificate Manager (ACM) certificate you want to use for TSL connection",
"comment": "Any comments you want to include about the distribution",
"default-file": "The object that you want CloudFront to request from your origin when a viewer requests the root URL for your distribution (http://www.example.com)",
"domain-aliases": "A list of CNAMEs (alternate domain names), if any, for this distribution",
"enable": "Enable/Disable the distribution",
"forward-cookies": "Specifies which cookies to forward to the origin for this cache behavior",
"forward-queries": "Indicates whether you want CloudFront to forward query strings to the origin that is associated with this cache behavior and cache based on the query string parameters (true | false)",
"https-behaviour": "The protocol (HTTP or HTTPS) that viewers can use to access the files", //nolint:misspell
"id": "The ID of the distribution to update",
"min-ttl": "The minimum amount of time that you want objects to stay in CloudFront caches before CloudFront forwards another request to your origin to determine whether the object has been updated",
"origin-domain": "The DNS name of the Amazon S3 bucket from which you want CloudFront to get objects for this origin, for example, myawsbucket.s3.amazonaws.com",
"origin-path": "An optional element that causes CloudFront to request your content from a directory in your Amazon S3 bucket or your custom origin. When you include this element, specify the directory name, beginning with a /",
"price-class": "The price class that corresponds with the maximum price that you want to pay for CloudFront service. If you specify PriceClass_All, CloudFront responds to requests for your objects from all CloudFront edge locations",
},
"update.image": {
"accounts": "List (one or more) AWS account IDs",
"description": "A new description for the AMI",
"groups": "List (one or more) user groups",
"id": "The ID of the AMI",
"operation": "The operation type for launch permissions",
"product-codes": "One or more DevPay product codes. After adding a product code, it cannot be removed",
},
"update.instance": {
"id": "The ID of the instance",
"lock": "If the value is true, you can't terminate the instance using the Amazon EC2 console, CLI, or API; otherwise, you can",
"type": "Changes the instance type to the specified value",
},
"update.loginprofile": {
"password": "The new password for the specified IAM user",
"password-reset": "Allows this new password to be used only once by requiring the specified IAM user to set a new password on next sign-in",
"username": "The name of the user whose password you want to update",
},
"update.policy": {
"action": "The Action elements describing the actions that will be allowed or denied. You specify a value using a namespace that identifies a service followed by the name of the action to allow or deny (eg. sqs:SendMessage, s3:*). Use a list for multiple actions",
"arn": "The Amazon Resource Name (ARN) of the IAM policy you want to attach",
"conditions": "List of conditions necessary for the policy to be in effect (e.g. [aws:UserAgent!=My user agent,s3:prefix=~home/,aws:CurrentTime>=2013-06-30T00:00:00Z,aws:SourceIp!=203.0.113.0/24,aws:SourceArn==arn:aws:sns:eu-west-1:*:*])",
"effect": "The Effect element is required and specifies whether the policy will result in an allow or an explicit deny",
"resource": "The Amazon Resource Name (ARN) of the Resource element which specifies the object or objects that the policy covers",
},
"update.record": {
"comment": "Any comments you want to include about a change batch request",
"name": "The name of the domain you want to perform the action on. Enter a fully qualified domain name, for example, www.example.com. You can optionally include a trailing dot",
"ttl": "The resource record cache time to live (TTL), in seconds",
"type": "The DNS record type",
"value": "The current or new DNS record value",
"values": "The current or new DNS record value(s)",
"zone": "The ID of the hosted zone that contains the resource record sets that you want to change",
},
"update.s3object": {
"acl": "The canned ACL to apply to the bucket",
"bucket": "The name of the bucket containing the object to be updated",
"name": "The name of the object to be updated",
"version": "Used to reference a specific version of the object",
},
"update.scalinggroup": {
"cooldown": "The amount of time, in seconds, after a scaling activity completes before another scaling activity can start",
"desired-capacity": "The number of EC2 instances that should be running in the Auto Scaling group",
"healthcheck-grace-period": "The amount of time, in seconds, that Auto Scaling waits before checking the health status of an EC2 instance that has come into service",
"healthcheck-type": "The service to use for the health checks",
"launchconfiguration": "The name of the launch configuration",
"max-size": "The maximum size of the Auto Scaling group",
"min-size": "The minimum size of the Auto Scaling group",
"name": "The name of the Auto Scaling group",
"new-instances-protected": "Indicates whether newly launched instances are protected from termination by Auto Scaling when scaling in",
"subnets": "The ID of the subnet, if you are launching into a VPC",
},
"update.securitygroup": {
"cidr": "The CIDR IPv4 address range",
"id": "The ID of the security group to be updated",
"inbound": "Set inbound to either authorize or revoke, to update the security group ingress rules",
"outbound": "Set outbound to either authorize or revoke, to update the security group egress rules",
"portrange": "The portrange for the rule to update: any, 80, 22-23...",
"protocol": "The IP protocol name or number",
"securitygroup": "The ID of the source security group. Cannot be used when using cidr param",
},
"update.stack": {
"capabilities": "A list of values that you must specify before AWS CloudFormation can update certain stacks",
"name": "The name or unique stack ID of the stack to update",
"notifications": "Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that AWS CloudFormation associates with the stack",
"parameters": "A list of Parameters that specify input parameters for the stack given using this format: [key1:val1,key2:val2,...]",
"policy-file": "The path to the file containing the stack policy body",
"policy-update-file": "The path to the file containing the temporary overriding stack policy",
"resource-types": "The template resource types that you have permissions to work with for this update stack action, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance",
"role": "The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to update the stack",
"rollback-monitoring-min": "Time to monitor rollback-triggers during and after update",
"rollback-triggers": "List of CloudWatch Alarm ARNs to monitor during and after update",
"stack-file": "The path to the file containing Parameters/Tags/StackPolices definition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html#w2ab2c13c15c15). Values passed via CLI has higher priority than ones defined in StackFile",
"tags": "Key-value pairs to associate with this stack",
"template-file": "The path to the file containing the template body with a minimum size of 1 byte and a maximum size of 51,200 bytes",
"use-previous-template": "Reuse the existing template that is associated with the stack that you are updating",
},
"update.subnet": {
"id": "The ID of the subnet",
"public": "Specify true to indicate that network interfaces created in the specified subnet should be assigned a public IPv4 address",
},
"update.targetgroup": {
"deregistrationdelay": "The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused. The range is 0-3600 seconds. The default value is 300 seconds",
"healthcheckinterval": "The approximate amount of time, in seconds, between health checks of an individual target",
"healthcheckpath": "The ping path that is the destination on the targets for health checks",
"healthcheckport": "The port the load balancer uses when performing health checks on targets",
"healthcheckprotocol": "The protocol the load balancer uses when performing health checks on targets",
"healthchecktimeout": "The amount of time, in seconds, during which no response from a target means a failed health check",
"healthythreshold": "The number of consecutive health checks successes required before considering an unhealthy target healthy",
"id": "The Amazon Resource Name (ARN) of the target group",
"matcher": "The HTTP codes to use when checking for a successful response from a target",
"name": "The name of the target group",
"port": "The port on which the targets receive traffic",
"protocol": "The protocol to use for routing traffic to the targets",
"stickiness": "Indicates whether sticky sessions (of type load balancer cookies) are enabled",
"stickinessduration": "The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). The default value is 1 day (86400 seconds)",
"unhealthythreshold": "The number of consecutive health check failures required before considering a target unhealthy",
},
}
package awsfetch
import (
"reflect"
"github.com/aws/aws-sdk-go-v2/service/acm"
"github.com/aws/aws-sdk-go-v2/service/apigatewayv2"
"github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
"github.com/aws/aws-sdk-go-v2/service/cloudtrail"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/aws/aws-sdk-go-v2/service/ecs"
"github.com/aws/aws-sdk-go-v2/service/efs"
"github.com/aws/aws-sdk-go-v2/service/eks"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/lambda"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/route53"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/wallix/awless/logger"
)
type AWSAPI struct {
Iam *iam.Client
Ec2 *ec2.Client
Elbv2 *elasticloadbalancingv2.Client
Elb *elasticloadbalancing.Client
Rds *rds.Client
Autoscaling *autoscaling.Client
Ecr *ecr.Client
Ecs *ecs.Client
Applicationautoscaling *applicationautoscaling.Client
Sts *sts.Client
S3 *s3.Client
Sns *sns.Client
Sqs *sqs.Client
Route53 *route53.Client
Lambda *lambda.Client
Cloudwatch *cloudwatch.Client
Cloudfront *cloudfront.Client
Cloudformation *cloudformation.Client
Acm *acm.Client
Eks *eks.Client
Dynamodb *dynamodb.Client
Secretsmanager *secretsmanager.Client
Kms *kms.Client
Apigatewayv2 *apigatewayv2.Client
Ssm *ssm.Client
Efs *efs.Client
Cloudtrail *cloudtrail.Client
Cloudwatchlogs *cloudwatchlogs.Client
}
type Config struct {
Log *logger.Logger
Extra map[string]interface{}
APIs *AWSAPI
}
func NewConfig(apis ...interface{}) *Config {
c := &Config{
Extra: make(map[string]interface{}),
Log: logger.DiscardLogger,
}
assignAPIs(c, apis...)
return c
}
func (c *Config) getBoolDefaultTrue(key string) bool {
if c.Extra == nil {
return true
}
if b, ok := c.Extra[key].(bool); ok {
return b
}
return true
}
func assignAPIs(c *Config, apis ...interface{}) {
c.APIs = new(AWSAPI)
val := reflect.ValueOf(c.APIs).Elem()
stru := val.Type()
for _, api := range apis {
if !reflect.ValueOf(api).IsValid() {
continue
}
apiType := reflect.TypeOf(api)
for i := 0; i < stru.NumField(); i++ {
fieldType := stru.Field(i).Type
if apiType.AssignableTo(fieldType) {
val.Field(i).Set(reflect.ValueOf(api))
break
}
}
}
}
package awsfetch
import (
"context"
"sync"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/wallix/awless/fetch"
)
func getClusterArns(ctx context.Context, cache fetch.Cache, api *ecs.Client) ([]string, error) {
var arns []string
if clusterName, hasFilter := getUserFiltersFromContext(ctx)["cluster"]; hasFilter {
out, err := api.DescribeClusters(ctx, &ecs.DescribeClustersInput{Clusters: []string{clusterName}})
if err != nil {
return arns, err
}
for _, c := range out.Clusters {
arns = append(arns, awssdk.ToString(c.ClusterArn))
}
} else {
if val, cerr := cache.Get("getClustersNames", func() (interface{}, error) {
paginator := ecs.NewListClustersPaginator(api, &ecs.ListClustersInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return arns, err
}
arns = append(arns, out.ClusterArns...)
}
return arns, nil
}); cerr != nil {
return arns, cerr
} else if v, ok := val.([]string); ok {
arns = v
}
}
return arns, nil
}
func getAllTasks(ctx context.Context, cache fetch.Cache, api *ecs.Client) (res []ecstypes.Task, err error) {
clusterArns, cerr := getClusterArns(ctx, cache, api)
if cerr != nil {
return res, cerr
}
type listTasksOutput struct {
err error
output *ecs.ListTasksOutput
cluster string
}
tasksNamesc := make(chan listTasksOutput)
var wg sync.WaitGroup
for _, cluster := range clusterArns {
wg.Add(1)
go func(cl string) {
defer wg.Done()
paginator := ecs.NewListTasksPaginator(api, &ecs.ListTasksInput{Cluster: &cl, DesiredStatus: ecstypes.DesiredStatusRunning})
for paginator.HasMorePages() {
out, er := paginator.NextPage(ctx)
if er != nil {
tasksNamesc <- listTasksOutput{err: er}
return
}
tasksNamesc <- listTasksOutput{output: out, cluster: cl}
}
}(cluster)
wg.Add(1)
go func(cl string) {
defer wg.Done()
paginator := ecs.NewListTasksPaginator(api, &ecs.ListTasksInput{Cluster: &cl, DesiredStatus: ecstypes.DesiredStatusStopped})
for paginator.HasMorePages() {
out, er := paginator.NextPage(ctx)
if er != nil {
tasksNamesc <- listTasksOutput{err: er}
return
}
tasksNamesc <- listTasksOutput{output: out, cluster: cl}
}
}(cluster)
}
type describeTasksOutput struct {
err error
output *ecs.DescribeTasksOutput
}
tasksc := make(chan describeTasksOutput)
var tasksWG sync.WaitGroup
tasksWG.Add(1)
go func() {
defer tasksWG.Done()
for r := range tasksNamesc {
if r.err != nil {
tasksc <- describeTasksOutput{err: r.err}
return
}
if len(r.output.TaskArns) == 0 {
continue
}
tasksWG.Add(1)
go func(taskArns []string, cluster string) {
defer tasksWG.Done()
tasksOut, er := api.DescribeTasks(ctx, &ecs.DescribeTasksInput{Cluster: &cluster, Tasks: taskArns})
tasksc <- describeTasksOutput{err: er, output: tasksOut}
}(r.output.TaskArns, r.cluster)
}
}()
go func() {
wg.Wait()
close(tasksNamesc)
tasksWG.Wait()
close(tasksc)
}()
for r := range tasksc {
if err = r.err; err != nil {
return
}
res = append(res, r.output.Tasks...)
}
return
}
// Auto generated implementation for the AWS cloud service
/*
Copyright 2017 WALLIX
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 awsfetch
// DO NOT EDIT - This file was automatically generated with go generate
import (
"context"
ec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
rds "github.com/aws/aws-sdk-go-v2/service/rds"
rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
autoscaling "github.com/aws/aws-sdk-go-v2/service/autoscaling"
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
ecr "github.com/aws/aws-sdk-go-v2/service/ecr"
ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
acm "github.com/aws/aws-sdk-go-v2/service/acm"
acmtypes "github.com/aws/aws-sdk-go-v2/service/acm/types"
iam "github.com/aws/aws-sdk-go-v2/service/iam"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
sns "github.com/aws/aws-sdk-go-v2/service/sns"
snstypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
route53 "github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
lambda "github.com/aws/aws-sdk-go-v2/service/lambda"
lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
cloudwatch "github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
cloudfront "github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudfronttypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
cloudformation "github.com/aws/aws-sdk-go-v2/service/cloudformation"
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
secretsmanager "github.com/aws/aws-sdk-go-v2/service/secretsmanager"
secretsmanagertypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
ssm "github.com/aws/aws-sdk-go-v2/service/ssm"
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
efs "github.com/aws/aws-sdk-go-v2/service/efs"
efstypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
cloudtrail "github.com/aws/aws-sdk-go-v2/service/cloudtrail"
cloudtrailtypes "github.com/aws/aws-sdk-go-v2/service/cloudtrail/types"
cloudwatchlogs "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/wallix/awless/fetch"
"github.com/wallix/awless/graph"
awsconv "github.com/wallix/awless/aws/conv"
)
func BuildInfraFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualInfraFetchFuncs(conf, funcs)
funcs["instance"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Instance
if !conf.getBoolDefaultTrue("aws.infra.instance.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[instance]")
return resources, objects, nil
}
paginator := ec2.NewDescribeInstancesPaginator(conf.APIs.Ec2, &ec2.DescribeInstancesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, all := range out.Reservations {
for _, output := range all.Instances {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
}
return resources, objects, nil
}
funcs["subnet"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Subnet
if !conf.getBoolDefaultTrue("aws.infra.subnet.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[subnet]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.Subnets {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["vpc"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Vpc
if !conf.getBoolDefaultTrue("aws.infra.vpc.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[vpc]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeVpcs(ctx, &ec2.DescribeVpcsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.Vpcs {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["keypair"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.KeyPairInfo
if !conf.getBoolDefaultTrue("aws.infra.keypair.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[keypair]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeKeyPairs(ctx, &ec2.DescribeKeyPairsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.KeyPairs {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["securitygroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.SecurityGroup
if !conf.getBoolDefaultTrue("aws.infra.securitygroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[securitygroup]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.SecurityGroups {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["volume"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Volume
if !conf.getBoolDefaultTrue("aws.infra.volume.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[volume]")
return resources, objects, nil
}
paginator := ec2.NewDescribeVolumesPaginator(conf.APIs.Ec2, &ec2.DescribeVolumesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Volumes {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["internetgateway"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.InternetGateway
if !conf.getBoolDefaultTrue("aws.infra.internetgateway.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[internetgateway]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeInternetGateways(ctx, &ec2.DescribeInternetGatewaysInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.InternetGateways {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["natgateway"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.NatGateway
if !conf.getBoolDefaultTrue("aws.infra.natgateway.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[natgateway]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeNatGateways(ctx, &ec2.DescribeNatGatewaysInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.NatGateways {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["routetable"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.RouteTable
if !conf.getBoolDefaultTrue("aws.infra.routetable.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[routetable]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeRouteTables(ctx, &ec2.DescribeRouteTablesInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.RouteTables {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["availabilityzone"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.AvailabilityZone
if !conf.getBoolDefaultTrue("aws.infra.availabilityzone.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[availabilityzone]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.AvailabilityZones {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["image"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Image
if !conf.getBoolDefaultTrue("aws.infra.image.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[image]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeImages(ctx, &ec2.DescribeImagesInput{Owners: []string{"self"}})
if err != nil {
return resources, objects, err
}
for _, output := range out.Images {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["importimagetask"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.ImportImageTask
if !conf.getBoolDefaultTrue("aws.infra.importimagetask.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[importimagetask]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeImportImageTasks(ctx, &ec2.DescribeImportImageTasksInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.ImportImageTasks {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["elasticip"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Address
if !conf.getBoolDefaultTrue("aws.infra.elasticip.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[elasticip]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeAddresses(ctx, &ec2.DescribeAddressesInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.Addresses {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["snapshot"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.Snapshot
if !conf.getBoolDefaultTrue("aws.infra.snapshot.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[snapshot]")
return resources, objects, nil
}
paginator := ec2.NewDescribeSnapshotsPaginator(conf.APIs.Ec2, &ec2.DescribeSnapshotsInput{OwnerIds: []string{"self"}})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Snapshots {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["networkinterface"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ec2types.NetworkInterface
if !conf.getBoolDefaultTrue("aws.infra.networkinterface.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[networkinterface]")
return resources, objects, nil
}
out, err := conf.APIs.Ec2.DescribeNetworkInterfaces(ctx, &ec2.DescribeNetworkInterfacesInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.NetworkInterfaces {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["classicloadbalancer"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []elbtypes.LoadBalancerDescription
if !conf.getBoolDefaultTrue("aws.infra.classicloadbalancer.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[classicloadbalancer]")
return resources, objects, nil
}
paginator := elb.NewDescribeLoadBalancersPaginator(conf.APIs.Elb, &elb.DescribeLoadBalancersInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.LoadBalancerDescriptions {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["loadbalancer"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []elbv2types.LoadBalancer
if !conf.getBoolDefaultTrue("aws.infra.loadbalancer.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[loadbalancer]")
return resources, objects, nil
}
paginator := elbv2.NewDescribeLoadBalancersPaginator(conf.APIs.Elbv2, &elbv2.DescribeLoadBalancersInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.LoadBalancers {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["targetgroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []elbv2types.TargetGroup
if !conf.getBoolDefaultTrue("aws.infra.targetgroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[targetgroup]")
return resources, objects, nil
}
out, err := conf.APIs.Elbv2.DescribeTargetGroups(ctx, &elbv2.DescribeTargetGroupsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.TargetGroups {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
funcs["database"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []rdstypes.DBInstance
if !conf.getBoolDefaultTrue("aws.infra.database.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[database]")
return resources, objects, nil
}
paginator := rds.NewDescribeDBInstancesPaginator(conf.APIs.Rds, &rds.DescribeDBInstancesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.DBInstances {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["dbsubnetgroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []rdstypes.DBSubnetGroup
if !conf.getBoolDefaultTrue("aws.infra.dbsubnetgroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[dbsubnetgroup]")
return resources, objects, nil
}
paginator := rds.NewDescribeDBSubnetGroupsPaginator(conf.APIs.Rds, &rds.DescribeDBSubnetGroupsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.DBSubnetGroups {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["launchconfiguration"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []autoscalingtypes.LaunchConfiguration
if !conf.getBoolDefaultTrue("aws.infra.launchconfiguration.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[launchconfiguration]")
return resources, objects, nil
}
paginator := autoscaling.NewDescribeLaunchConfigurationsPaginator(conf.APIs.Autoscaling, &autoscaling.DescribeLaunchConfigurationsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.LaunchConfigurations {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["scalinggroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []autoscalingtypes.AutoScalingGroup
if !conf.getBoolDefaultTrue("aws.infra.scalinggroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[scalinggroup]")
return resources, objects, nil
}
paginator := autoscaling.NewDescribeAutoScalingGroupsPaginator(conf.APIs.Autoscaling, &autoscaling.DescribeAutoScalingGroupsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.AutoScalingGroups {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["scalingpolicy"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []autoscalingtypes.ScalingPolicy
if !conf.getBoolDefaultTrue("aws.infra.scalingpolicy.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[scalingpolicy]")
return resources, objects, nil
}
paginator := autoscaling.NewDescribePoliciesPaginator(conf.APIs.Autoscaling, &autoscaling.DescribePoliciesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.ScalingPolicies {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["repository"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ecrtypes.Repository
if !conf.getBoolDefaultTrue("aws.infra.repository.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[repository]")
return resources, objects, nil
}
paginator := ecr.NewDescribeRepositoriesPaginator(conf.APIs.Ecr, &ecr.DescribeRepositoriesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Repositories {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["certificate"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []acmtypes.CertificateSummary
if !conf.getBoolDefaultTrue("aws.infra.certificate.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[certificate]")
return resources, objects, nil
}
paginator := acm.NewListCertificatesPaginator(conf.APIs.Acm, &acm.ListCertificatesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.CertificateSummaryList {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildAccessFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualAccessFetchFuncs(conf, funcs)
funcs["instanceprofile"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.InstanceProfile
if !conf.getBoolDefaultTrue("aws.access.instanceprofile.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[instanceprofile]")
return resources, objects, nil
}
paginator := iam.NewListInstanceProfilesPaginator(conf.APIs.Iam, &iam.ListInstanceProfilesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.InstanceProfiles {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["mfadevice"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.VirtualMFADevice
if !conf.getBoolDefaultTrue("aws.access.mfadevice.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[mfadevice]")
return resources, objects, nil
}
paginator := iam.NewListVirtualMFADevicesPaginator(conf.APIs.Iam, &iam.ListVirtualMFADevicesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.VirtualMFADevices {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildStorageFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualStorageFetchFuncs(conf, funcs)
return funcs
}
func BuildMessagingFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualMessagingFetchFuncs(conf, funcs)
funcs["subscription"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []snstypes.Subscription
if !conf.getBoolDefaultTrue("aws.messaging.subscription.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource messaging[subscription]")
return resources, objects, nil
}
paginator := sns.NewListSubscriptionsPaginator(conf.APIs.Sns, &sns.ListSubscriptionsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Subscriptions {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["topic"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []snstypes.Topic
if !conf.getBoolDefaultTrue("aws.messaging.topic.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource messaging[topic]")
return resources, objects, nil
}
paginator := sns.NewListTopicsPaginator(conf.APIs.Sns, &sns.ListTopicsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Topics {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildDnsFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualDnsFetchFuncs(conf, funcs)
funcs["zone"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []route53types.HostedZone
if !conf.getBoolDefaultTrue("aws.dns.zone.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource dns[zone]")
return resources, objects, nil
}
paginator := route53.NewListHostedZonesPaginator(conf.APIs.Route53, &route53.ListHostedZonesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.HostedZones {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildLambdaFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualLambdaFetchFuncs(conf, funcs)
funcs["function"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []lambdatypes.FunctionConfiguration
if !conf.getBoolDefaultTrue("aws.lambda.function.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource lambda[function]")
return resources, objects, nil
}
paginator := lambda.NewListFunctionsPaginator(conf.APIs.Lambda, &lambda.ListFunctionsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Functions {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildMonitoringFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualMonitoringFetchFuncs(conf, funcs)
funcs["metric"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudwatchtypes.Metric
if !conf.getBoolDefaultTrue("aws.monitoring.metric.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource monitoring[metric]")
return resources, objects, nil
}
paginator := cloudwatch.NewListMetricsPaginator(conf.APIs.Cloudwatch, &cloudwatch.ListMetricsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Metrics {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["alarm"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudwatchtypes.MetricAlarm
if !conf.getBoolDefaultTrue("aws.monitoring.alarm.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource monitoring[alarm]")
return resources, objects, nil
}
paginator := cloudwatch.NewDescribeAlarmsPaginator(conf.APIs.Cloudwatch, &cloudwatch.DescribeAlarmsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.MetricAlarms {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildCdnFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualCdnFetchFuncs(conf, funcs)
funcs["distribution"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudfronttypes.DistributionSummary
if !conf.getBoolDefaultTrue("aws.cdn.distribution.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource cdn[distribution]")
return resources, objects, nil
}
paginator := cloudfront.NewListDistributionsPaginator(conf.APIs.Cloudfront, &cloudfront.ListDistributionsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.DistributionList.Items {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildCloudformationFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualCloudformationFetchFuncs(conf, funcs)
funcs["stack"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudformationtypes.Stack
if !conf.getBoolDefaultTrue("aws.cloudformation.stack.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource cloudformation[stack]")
return resources, objects, nil
}
paginator := cloudformation.NewDescribeStacksPaginator(conf.APIs.Cloudformation, &cloudformation.DescribeStacksInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Stacks {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildEksFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualEksFetchFuncs(conf, funcs)
return funcs
}
func BuildDynamodbFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualDynamodbFetchFuncs(conf, funcs)
return funcs
}
func BuildSecretsmanagerFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualSecretsmanagerFetchFuncs(conf, funcs)
funcs["secret"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []secretsmanagertypes.SecretListEntry
if !conf.getBoolDefaultTrue("aws.secretsmanager.secret.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource secretsmanager[secret]")
return resources, objects, nil
}
paginator := secretsmanager.NewListSecretsPaginator(conf.APIs.Secretsmanager, &secretsmanager.ListSecretsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.SecretList {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildApigatewayFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualApigatewayFetchFuncs(conf, funcs)
return funcs
}
func BuildSsmFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualSsmFetchFuncs(conf, funcs)
funcs["ssmparameter"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ssmtypes.ParameterMetadata
if !conf.getBoolDefaultTrue("aws.ssm.ssmparameter.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource ssm[ssmparameter]")
return resources, objects, nil
}
paginator := ssm.NewDescribeParametersPaginator(conf.APIs.Ssm, &ssm.DescribeParametersInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.Parameters {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildEfsFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualEfsFetchFuncs(conf, funcs)
funcs["filesystem"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []efstypes.FileSystemDescription
if !conf.getBoolDefaultTrue("aws.efs.filesystem.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource efs[filesystem]")
return resources, objects, nil
}
paginator := efs.NewDescribeFileSystemsPaginator(conf.APIs.Efs, &efs.DescribeFileSystemsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.FileSystems {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
func BuildCloudtrailFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualCloudtrailFetchFuncs(conf, funcs)
funcs["trail"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudtrailtypes.Trail
if !conf.getBoolDefaultTrue("aws.cloudtrail.trail.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource cloudtrail[trail]")
return resources, objects, nil
}
out, err := conf.APIs.Cloudtrail.DescribeTrails(ctx, &cloudtrail.DescribeTrailsInput{})
if err != nil {
return resources, objects, err
}
for _, output := range out.TrailList {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil
}
return funcs
}
func BuildCloudwatchlogsFetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManualCloudwatchlogsFetchFuncs(conf, funcs)
funcs["loggroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []cloudwatchlogstypes.LogGroup
if !conf.getBoolDefaultTrue("aws.cloudwatchlogs.loggroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource cloudwatchlogs[loggroup]")
return resources, objects, nil
}
paginator := cloudwatchlogs.NewDescribeLogGroupsPaginator(conf.APIs.Cloudwatchlogs, &cloudwatchlogs.DescribeLogGroupsInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, output := range out.LogGroups {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
return funcs
}
package awsfetch
import (
"context"
"strings"
)
func getBoolFromContext(ctx context.Context, key string) bool {
v, ok := ctx.Value(key).(bool)
return v && ok
}
func getUserFiltersFromContext(ctx context.Context) map[string]string {
out := make(map[string]string)
arr, ok := ctx.Value("filters").([]string)
if ok {
for _, keyval := range arr {
if splits := strings.SplitN(keyval, "=", 2); len(splits) == 2 {
out[strings.ToLower(splits[0])] = splits[1]
}
}
}
return out
}
func sliceOfSlice(in []string, maxLength int) (res [][]string) {
if maxLength <= 0 {
return
}
if len(in) == 0 {
return
}
for i := 0; i < len(in); i += maxLength {
if i+maxLength < len(in) {
res = append(res, in[i:i+maxLength])
} else {
res = append(res, in[i:])
}
}
return
}
func appendIfNotInSlice(slice []string, s string) []string {
var found bool
for _, e := range slice {
if e == s {
found = true
}
}
if !found {
return append(slice, s)
}
return slice
}
func arnToName(arn string) string {
splits := strings.Split(arn, "/")
if len(splits) > 1 {
return splits[len(splits)-1]
}
return arn
}
func pluralizeIfNeeded(str string, n uint) string {
if n > 1 {
return str + "s"
}
return str
}
package awsfetch
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/service/iam"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/wallix/awless/fetch"
)
type AccountAuthorizationDetails struct {
Groups []iamtypes.GroupDetail
Policies []iamtypes.ManagedPolicyDetail
Roles []iamtypes.RoleDetail
Users []iamtypes.UserDetail
}
func getAccountAuthorizationDetails(ctx context.Context, cache fetch.Cache, api *iam.Client) (*AccountAuthorizationDetails, error) {
var entities []iamtypes.EntityType
var cacheKey string
resourceType, ok := fetch.IsFetchingByType(ctx)
if ok {
switch resourceType {
case "user":
cacheKey = "usersDetails"
entities = append(entities, iamtypes.EntityTypeUser)
case "group":
cacheKey = "groupsDetails"
entities = append(entities, iamtypes.EntityTypeGroup)
case "role":
cacheKey = "rolesDetails"
entities = append(entities, iamtypes.EntityTypeRole)
case "policy":
cacheKey = "policiesDetails"
entities = append(entities, iamtypes.EntityTypeLocalManagedPolicy, iamtypes.EntityTypeAWSManagedPolicy)
}
} else {
cacheKey = "accountDetails"
entities = append(entities, iamtypes.EntityTypeUser, iamtypes.EntityTypeGroup, iamtypes.EntityTypeRole)
entities = append(entities, iamtypes.EntityTypeLocalManagedPolicy, iamtypes.EntityTypeAWSManagedPolicy)
}
if val, err := cache.Get(cacheKey, func() (interface{}, error) {
return fetchAccountAuthorizationDetails(ctx, entities, api)
}); err != nil {
return nil, err
} else if v, ok := val.(*AccountAuthorizationDetails); ok {
return v, nil
} else {
return nil, fmt.Errorf("cannot get account details (val of type %T)", val)
}
}
func fetchAccountAuthorizationDetails(ctx context.Context, entities []iamtypes.EntityType, api *iam.Client) (*AccountAuthorizationDetails, error) {
details := new(AccountAuthorizationDetails)
paginator := iam.NewGetAccountAuthorizationDetailsPaginator(api, &iam.GetAccountAuthorizationDetailsInput{
Filter: entities,
})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return details, err
}
details.Users = append(details.Users, out.UserDetailList...)
details.Groups = append(details.Groups, out.GroupDetailList...)
details.Roles = append(details.Roles, out.RoleDetailList...)
details.Policies = append(details.Policies, out.Policies...)
}
return details, nil
}
package awsfetch
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/apigatewayv2"
apigatewayv2types "github.com/aws/aws-sdk-go-v2/service/apigatewayv2/types"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/aws/aws-sdk-go-v2/service/efs"
efstypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
"github.com/aws/aws-sdk-go-v2/service/eks"
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
"github.com/aws/aws-sdk-go-v2/service/iam"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/aws/aws-sdk-go-v2/service/kms"
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go-v2/service/sqs"
sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/aws/smithy-go"
awsconv "github.com/wallix/awless/aws/conv"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/fetch"
"github.com/wallix/awless/graph"
)
func addManualInfraFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["containerinstance"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []ecstypes.ContainerInstance
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.infra.containerinstance.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[containerinstance]")
return resources, objects, nil
}
clusterArns, err := getClusterArns(ctx, cache, conf.APIs.Ecs)
if err != nil {
return resources, objects, err
}
for _, cluster := range clusterArns {
paginator := ecs.NewListContainerInstancesPaginator(conf.APIs.Ecs, &ecs.ListContainerInstancesInput{Cluster: &cluster})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
if len(out.ContainerInstanceArns) == 0 {
continue
}
containerInstancesOut, err := conf.APIs.Ecs.DescribeContainerInstances(ctx, &ecs.DescribeContainerInstancesInput{Cluster: &cluster, ContainerInstances: out.ContainerInstanceArns})
if err != nil {
return resources, objects, err
}
for _, inst := range containerInstancesOut.ContainerInstances {
objects = append(objects, inst)
var res *graph.Resource
if res, err = awsconv.NewResource(inst); err != nil {
return resources, objects, err
}
res.Properties()[properties.Cluster] = cluster
resources = append(resources, res)
parent := graph.InitResource(cloud.ContainerCluster, cluster)
res.AddRelation(rdf.ChildrenOfRel, parent)
}
}
}
return resources, objects, nil
}
funcs["container"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []ecstypes.Container
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.infra.container.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[container]")
return resources, objects, nil
}
var tasks []ecstypes.Task
if val, e := cache.Get("getAllTasks", func() (interface{}, error) {
return getAllTasks(ctx, cache, conf.APIs.Ecs)
}); e != nil {
return resources, objects, e
} else if v, ok := val.([]ecstypes.Task); ok {
tasks = v
}
for _, task := range tasks {
for _, container := range task.Containers {
objects = append(objects, container)
res, err := awsconv.NewResource(container)
if err != nil {
return nil, nil, err
}
if task.ClusterArn != nil {
res.Properties()[properties.Cluster] = awssdk.ToString(task.ClusterArn)
}
if task.ContainerInstanceArn != nil {
res.Properties()[properties.ContainerInstance] = awssdk.ToString(task.ContainerInstanceArn)
}
if task.CreatedAt != nil {
res.Properties()[properties.Created] = *task.CreatedAt
}
if task.StartedAt != nil {
res.Properties()[properties.Launched] = *task.StartedAt
}
if task.StoppedAt != nil {
res.Properties()[properties.Stopped] = *task.StoppedAt
}
if task.TaskDefinitionArn != nil {
res.Properties()[properties.ContainerTask] = awssdk.ToString(task.TaskDefinitionArn)
}
if task.Group != nil {
res.Properties()[properties.DeploymentName] = awssdk.ToString(task.Group)
}
res.AddRelation(rdf.ChildrenOfRel, graph.InitResource(cloud.ContainerCluster, awssdk.ToString(task.ClusterArn)))
res.AddRelation(rdf.DependingOnRel, graph.InitResource(cloud.ContainerTask, awssdk.ToString(task.TaskDefinitionArn)))
res.AddRelation(rdf.DependingOnRel, graph.InitResource(cloud.ContainerInstance, awssdk.ToString(task.ContainerInstanceArn)))
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["containertask"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []ecstypes.TaskDefinition
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.infra.containertask.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[containertask]")
return resources, objects, nil
}
type resStruct struct {
res *ecstypes.TaskDefinition
err error
}
var wg sync.WaitGroup
resc := make(chan resStruct)
fetchDefinitionsInput := &ecs.ListTaskDefinitionsInput{}
if givenFamilyPrefix, hasFilter := getUserFiltersFromContext(ctx)["name"]; hasFilter {
fetchDefinitionsInput.FamilyPrefix = &givenFamilyPrefix
}
paginator := ecs.NewListTaskDefinitionsPaginator(conf.APIs.Ecs, fetchDefinitionsInput)
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, arn := range out.TaskDefinitionArns {
wg.Add(1)
go func(taskDefArn string) {
defer wg.Done()
tasksOut, err := conf.APIs.Ecs.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{TaskDefinition: &taskDefArn})
if err != nil {
resc <- resStruct{err: err}
return
}
resc <- resStruct{res: tasksOut.TaskDefinition}
}(arn)
}
}
go func() {
wg.Wait()
close(resc)
}()
var tasks []ecstypes.Task
if val, e := cache.Get("getAllTasks", func() (interface{}, error) {
return getAllTasks(ctx, cache, conf.APIs.Ecs)
}); e != nil {
return resources, objects, e
} else if v, ok := val.([]ecstypes.Task); ok {
tasks = v
}
var errs []string
var err error
for res := range resc {
if res.err != nil {
errs = appendIfNotInSlice(errs, res.err.Error())
continue
}
objects = append(objects, *res.res)
var graphres *graph.Resource
if graphres, err = awsconv.NewResource(res.res); err != nil {
errs = appendIfNotInSlice(errs, err.Error())
continue
}
var deployments []*graph.KeyValue
var runningServicesCount, stoppedServicesCount, runningTasksCount, stoppedTasksCount uint
for _, t := range tasks {
if awssdk.ToString(t.TaskDefinitionArn) == awssdk.ToString(res.res.TaskDefinitionArn) {
group := awssdk.ToString(t.Group)
state := strings.ToLower(awssdk.ToString(t.LastStatus))
clusterArn := awssdk.ToString(t.ClusterArn)
if strings.HasPrefix(group, "service:") {
switch state {
case "stopped":
stoppedServicesCount++
deployments = append(deployments, &graph.KeyValue{KeyName: arnToName(clusterArn), Value: group[len("service:"):] + " (stopped service)"})
case "running":
runningServicesCount++
deployments = append(deployments, &graph.KeyValue{KeyName: arnToName(clusterArn), Value: group[len("service:"):] + " (running service)"})
}
}
if strings.HasPrefix(group, "family:") {
switch state {
case "stopped":
deployments = append(deployments, &graph.KeyValue{KeyName: arnToName(clusterArn), Value: group[len("family:"):] + " (stopped task)"})
stoppedTasksCount++
case "running":
deployments = append(deployments, &graph.KeyValue{KeyName: arnToName(clusterArn), Value: group[len("family:"):] + " (running task)"})
runningTasksCount++
}
}
}
}
if len(deployments) > 0 {
graphres.Properties()[properties.Deployments] = deployments
}
switch {
case runningServicesCount+stoppedServicesCount+runningTasksCount+stoppedTasksCount == 0:
if state := strings.ToLower(string(res.res.Status)); state == "active" {
graphres.Properties()[properties.State] = "ready"
} else {
graphres.Properties()[properties.State] = state
}
default:
var stateSl []string
if runningServicesCount > 0 {
stateSl = append(stateSl, fmt.Sprintf("%d %s running", runningServicesCount, pluralizeIfNeeded("service", runningServicesCount)))
}
if stoppedServicesCount > 0 {
stateSl = append(stateSl, fmt.Sprintf("%d %s stopped", stoppedServicesCount, pluralizeIfNeeded("service", runningServicesCount)))
}
if runningTasksCount > 0 {
stateSl = append(stateSl, fmt.Sprintf("%d %s running", runningTasksCount, pluralizeIfNeeded("task", runningServicesCount)))
}
if stoppedTasksCount > 0 {
stateSl = append(stateSl, fmt.Sprintf("%d %s stopped", stoppedTasksCount, pluralizeIfNeeded("task", runningServicesCount)))
}
if len(stateSl) > 0 {
graphres.Properties()[properties.State] = strings.Join(stateSl, " ")
}
}
resources = append(resources, graphres)
}
if len(errs) > 0 {
err = fmt.Errorf("%s", strings.Join(errs, "; "))
}
return resources, objects, err
}
funcs["containercluster"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ecstypes.Cluster
if !conf.getBoolDefaultTrue("aws.infra.containercluster.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[containercluster]")
return resources, objects, nil
}
clusterNames, err := getClusterArns(ctx, cache, conf.APIs.Ecs)
if err != nil {
return resources, objects, nil
}
for _, clusterArns := range sliceOfSlice(clusterNames, 100) {
clustersOut, err := conf.APIs.Ecs.DescribeClusters(ctx, &ecs.DescribeClustersInput{Clusters: clusterArns})
if err != nil {
return resources, objects, err
}
for _, cluster := range clustersOut.Clusters {
objects = append(objects, cluster)
res, err := awsconv.NewResource(cluster)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["listener"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []elbv2types.Listener
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.infra.listener.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource infra[listener]")
return resources, objects, nil
}
errc := make(chan error)
resultc := make(chan elbv2types.Listener)
var wg sync.WaitGroup
lbPaginator := elbv2.NewDescribeLoadBalancersPaginator(conf.APIs.Elbv2, &elbv2.DescribeLoadBalancersInput{})
for lbPaginator.HasMorePages() {
out, err := lbPaginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, lb := range out.LoadBalancers {
wg.Add(1)
go func(lb elbv2types.LoadBalancer) {
defer wg.Done()
listenerPaginator := elbv2.NewDescribeListenersPaginator(conf.APIs.Elbv2, &elbv2.DescribeListenersInput{LoadBalancerArn: lb.LoadBalancerArn})
for listenerPaginator.HasMorePages() {
lout, lerr := listenerPaginator.NextPage(ctx)
if lerr != nil {
errc <- lerr
return
}
for _, listen := range lout.Listeners {
resultc <- listen
}
}
}(lb)
}
}
go func() {
wg.Wait()
close(resultc)
}()
for {
select {
case err := <-errc:
if err != nil {
return resources, objects, err
}
case listener, ok := <-resultc:
if !ok {
return resources, objects, nil
}
objects = append(objects, listener)
res, err := awsconv.NewResource(listener)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
}
}
func addManualAccessFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["user"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.UserDetail
if !conf.getBoolDefaultTrue("aws.access.user.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[user]")
return resources, objects, nil
}
var wg sync.WaitGroup
resourcesC := make(chan *graph.Resource)
objectsC := make(chan iamtypes.UserDetail)
errC := make(chan error)
wg.Add(1)
go func() {
defer wg.Done()
accountDetails, err := getAccountAuthorizationDetails(ctx, cache, conf.APIs.Iam)
if err != nil {
errC <- err
return
}
for _, output := range accountDetails.Users {
objectsC <- output
if res, e := awsconv.NewResource(output); e != nil {
errC <- e
return
} else {
resourcesC <- res
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
paginator := iam.NewListUsersPaginator(conf.APIs.Iam, &iam.ListUsersInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
errC <- err
return
}
for _, user := range page.Users {
res, e := awsconv.NewResource(user)
if e != nil {
errC <- e
return
}
resourcesC <- res
}
}
}()
go func() {
wg.Wait()
close(errC)
close(objectsC)
close(resourcesC)
}()
for {
select {
case e := <-errC:
if e != nil {
return resources, objects, e
}
case r, ok := <-resourcesC:
if !ok {
return resources, objects, nil
}
if r != nil {
resources = append(resources, r)
}
case o, ok := <-objectsC:
if !ok {
return resources, objects, nil
}
objects = append(objects, o)
}
}
}
funcs["group"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.GroupDetail
if !conf.getBoolDefaultTrue("aws.access.group.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[group]")
return resources, objects, nil
}
accountDetails, err := getAccountAuthorizationDetails(ctx, cache, conf.APIs.Iam)
if err != nil {
return resources, objects, err
}
for _, output := range accountDetails.Groups {
objects = append(objects, output)
if res, err := awsconv.NewResource(output); err != nil {
return resources, objects, err
} else {
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["role"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.RoleDetail
if !conf.getBoolDefaultTrue("aws.access.role.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[role]")
return resources, objects, nil
}
accountDetails, err := getAccountAuthorizationDetails(ctx, cache, conf.APIs.Iam)
if err != nil {
return resources, objects, err
}
for _, output := range accountDetails.Roles {
objects = append(objects, output)
if res, err := awsconv.NewResource(output); err != nil {
return resources, objects, err
} else {
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["policy"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.ManagedPolicyDetail
if !conf.getBoolDefaultTrue("aws.access.policy.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[policy]")
return resources, objects, nil
}
errC := make(chan error)
objectsC := make(chan iamtypes.ManagedPolicyDetail)
resourcesC := make(chan *graph.Resource)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
accountDetails, err := getAccountAuthorizationDetails(ctx, cache, conf.APIs.Iam)
if err != nil {
errC <- err
return
}
for _, p := range accountDetails.Policies {
res, e := awsconv.NewResource(p)
if e != nil {
errC <- e
return
}
if strings.HasPrefix(awssdk.ToString(p.Arn), "arn:aws:iam::aws:policy") {
res.Properties()[properties.Type] = "AWS Managed"
} else {
res.Properties()[properties.Type] = "Customer Managed"
}
res.Properties()[properties.Attached] = awssdk.ToInt32(p.AttachmentCount) > 0
resourcesC <- res
}
}()
go func() {
wg.Wait()
close(errC)
close(objectsC)
close(resourcesC)
}()
for {
select {
case err := <-errC:
if err != nil {
return resources, objects, err
}
case o, ok := <-objectsC:
if !ok {
return resources, objects, nil
}
objects = append(objects, o)
case r, ok := <-resourcesC:
if !ok {
return resources, objects, nil
}
resources = append(resources, r)
}
}
}
funcs["accesskey"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []iamtypes.AccessKeyMetadata
if !conf.getBoolDefaultTrue("aws.access.accesskey.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource access[accesskey]")
return resources, objects, nil
}
var wg sync.WaitGroup
resourcesC := make(chan *graph.Resource)
objectsC := make(chan iamtypes.AccessKeyMetadata)
errC := make(chan error)
var hasError bool
usersPaginator := iam.NewListUsersPaginator(conf.APIs.Iam, &iam.ListUsersInput{})
for usersPaginator.HasMorePages() && !hasError {
outUsers, err := usersPaginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, user := range outUsers.Users {
wg.Add(1)
go func(u iamtypes.User) {
userRes, err := awsconv.InitResource(u)
if err != nil {
hasError = true
errC <- err
return
}
defer wg.Done()
akPaginator := iam.NewListAccessKeysPaginator(conf.APIs.Iam, &iam.ListAccessKeysInput{UserName: u.UserName})
for akPaginator.HasMorePages() {
out, err := akPaginator.NextPage(ctx)
if err != nil {
hasError = true
errC <- err
return
}
for _, output := range out.AccessKeyMetadata {
objectsC <- output
res, e := awsconv.NewResource(output)
if e != nil {
errC <- e
hasError = true
return
}
res.AddRelation(rdf.ChildrenOfRel, userRes)
resourcesC <- res
}
}
}(user)
}
}
go func() {
wg.Wait()
close(errC)
close(objectsC)
close(resourcesC)
}()
for {
select {
case e := <-errC:
if e != nil {
return resources, objects, e
}
case r, ok := <-resourcesC:
if !ok {
return resources, objects, nil
}
if r != nil {
resources = append(resources, r)
}
case o, ok := <-objectsC:
if !ok {
return resources, objects, nil
}
objects = append(objects, o)
}
}
}
}
func addManualStorageFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["bucket"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []s3types.Bucket
if !conf.getBoolDefaultTrue("aws.storage.bucket.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource storage[bucket]")
return resources, objects, nil
}
bucketM := &sync.Mutex{}
err := forEachBucketParallel(ctx, cache, conf.APIs.S3, func(b s3types.Bucket) error {
bucketM.Lock()
objects = append(objects, b)
bucketM.Unlock()
res, err := awsconv.NewResource(b)
if err != nil {
return fmt.Errorf("build resource for bucket `%s`: %s", awssdk.ToString(b.Name), err)
}
grants, err := fetchAndExtractGrantsFn(ctx, conf.APIs.S3, awssdk.ToString(b.Name))
if err != nil {
return fmt.Errorf("fetching grants for bucket %s: %s", awssdk.ToString(b.Name), err)
}
res.Properties()[properties.Grants] = grants
bucketM.Lock()
resources = append(resources, res)
bucketM.Unlock()
return nil
})
return resources, objects, err
}
funcs["s3object"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []s3types.Object
var resources []*graph.Resource
resourcesC := make(chan *graph.Resource)
if !conf.getBoolDefaultTrue("aws.storage.s3object.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource storage[s3object]")
return resources, objects, nil
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for r := range resourcesC {
resources = append(resources, r)
}
}()
err := forEachBucketParallel(ctx, cache, conf.APIs.S3, func(b s3types.Bucket) error {
return fetchObjectsForBucket(ctx, conf.APIs.S3, b, resourcesC)
})
close(resourcesC)
wg.Wait()
return resources, objects, err
}
}
func addManualMessagingFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["queue"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []string
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.messaging.queue.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource messaging[queue]")
return resources, objects, nil
}
out, err := conf.APIs.Sqs.ListQueues(ctx, &sqs.ListQueuesInput{})
if err != nil {
return nil, objects, err
}
errC := make(chan error)
objectsC := make(chan string)
resourcesC := make(chan *graph.Resource)
var wg sync.WaitGroup
for _, output := range out.QueueUrls {
wg.Add(1)
go func(url string) {
defer wg.Done()
objectsC <- url
res := graph.InitResource(cloud.Queue, url)
res.Properties()[properties.ID] = url
attrs, err := conf.APIs.Sqs.GetQueueAttributes(ctx, &sqs.GetQueueAttributesInput{AttributeNames: []sqstypes.QueueAttributeName{sqstypes.QueueAttributeNameAll}, QueueUrl: &url})
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && (apiErr.ErrorCode() == "AWS.SimpleQueueService.NonExistentQueue" || apiErr.ErrorCode() == "AWS.SimpleQueueService.QueueDeletedRecently") {
return
}
errC <- err
return
}
for k, v := range attrs.Attributes {
switch k {
case "ApproximateNumberOfMessages":
count, err := strconv.Atoi(v)
if err != nil {
errC <- err
}
res.Properties()[properties.ApproximateMessageCount] = count
case "CreatedTimestamp":
if v != "" {
timestamp, err := strconv.ParseInt(v, 10, 64)
if err != nil {
errC <- err
}
res.Properties()[properties.Created] = time.Unix(timestamp, 0)
}
case "LastModifiedTimestamp":
if v != "" {
timestamp, err := strconv.ParseInt(v, 10, 64)
if err != nil {
errC <- err
}
res.Properties()[properties.Modified] = time.Unix(timestamp, 0)
}
case "QueueArn":
res.Properties()[properties.Arn] = v
case "DelaySeconds":
delay, err := strconv.Atoi(v)
if err != nil {
errC <- err
}
res.Properties()[properties.Delay] = delay
}
}
resourcesC <- res
}(output)
}
go func() {
wg.Wait()
close(errC)
close(objectsC)
close(resourcesC)
}()
for {
select {
case err := <-errC:
if err != nil {
return resources, objects, err
}
case o, ok := <-objectsC:
if !ok {
return resources, objects, nil
}
objects = append(objects, o)
case r, ok := <-resourcesC:
if !ok {
return resources, objects, nil
}
resources = append(resources, r)
}
}
}
}
func addManualDnsFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["record"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var objects []route53types.ResourceRecordSet
var resources []*graph.Resource
if !conf.getBoolDefaultTrue("aws.dns.record.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource dns[record]")
return resources, objects, nil
}
zoneName, hasZoneFilter := getUserFiltersFromContext(ctx)["zone"]
errC := make(chan error)
zoneC := make(chan route53types.HostedZone)
objectsC := make(chan route53types.ResourceRecordSet)
resourcesC := make(chan *graph.Resource)
go func() {
paginator := route53.NewListHostedZonesPaginator(conf.APIs.Route53, &route53.ListHostedZonesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
errC <- err
break
}
for _, output := range out.HostedZones {
if hasZoneFilter {
if strings.Contains(strings.ToLower(awssdk.ToString(output.Name)), strings.ToLower(zoneName)) {
zoneC <- output
}
} else {
zoneC <- output
}
}
}
close(zoneC)
}()
go func() {
var wg sync.WaitGroup
for zone := range zoneC {
wg.Add(1)
go func(z route53types.HostedZone) {
defer wg.Done()
input := &route53.ListResourceRecordSetsInput{HostedZoneId: z.Id}
for {
out, err := conf.APIs.Route53.ListResourceRecordSets(ctx, input)
if err != nil {
errC <- err
return
}
for _, output := range out.ResourceRecordSets {
objectsC <- output
res, err := awsconv.NewResource(output)
if err != nil {
errC <- err
return
}
res.Properties()[properties.Zone] = awssdk.ToString(z.Name)
parent, err := awsconv.InitResource(z)
if err != nil {
errC <- err
return
}
res.AddRelation(rdf.ChildrenOfRel, parent)
resourcesC <- res
}
if !out.IsTruncated {
break
}
input.StartRecordName = out.NextRecordName
input.StartRecordType = out.NextRecordType
input.StartRecordIdentifier = out.NextRecordIdentifier
}
}(zone)
}
go func() {
wg.Wait()
close(objectsC)
close(resourcesC)
}()
}()
for {
select {
case err := <-errC:
if err != nil {
return resources, objects, err
}
case o, ok := <-objectsC:
if !ok {
return resources, objects, nil
}
objects = append(objects, o)
case r, ok := <-resourcesC:
if !ok {
return resources, objects, nil
}
resources = append(resources, r)
}
}
}
}
func addManualLambdaFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualMonitoringFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualCdnFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualCloudformationFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualEksFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["ekscluster"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ekstypes.Cluster
if !conf.getBoolDefaultTrue("aws.eks.ekscluster.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource eks[ekscluster]")
return resources, objects, nil
}
paginator := eks.NewListClustersPaginator(conf.APIs.Eks, &eks.ListClustersInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, name := range out.Clusters {
descOut, err := conf.APIs.Eks.DescribeCluster(ctx, &eks.DescribeClusterInput{Name: &name})
if err != nil {
return resources, objects, err
}
if descOut.Cluster != nil {
objects = append(objects, *descOut.Cluster)
res, err := awsconv.NewResource(*descOut.Cluster)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
}
return resources, objects, nil
}
funcs["eksnodegroup"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []ekstypes.Nodegroup
if !conf.getBoolDefaultTrue("aws.eks.eksnodegroup.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource eks[eksnodegroup]")
return resources, objects, nil
}
clusterPaginator := eks.NewListClustersPaginator(conf.APIs.Eks, &eks.ListClustersInput{})
for clusterPaginator.HasMorePages() {
cOut, err := clusterPaginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, clusterName := range cOut.Clusters {
ngPaginator := eks.NewListNodegroupsPaginator(conf.APIs.Eks, &eks.ListNodegroupsInput{ClusterName: &clusterName})
for ngPaginator.HasMorePages() {
ngOut, err := ngPaginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, ngName := range ngOut.Nodegroups {
descOut, err := conf.APIs.Eks.DescribeNodegroup(ctx, &eks.DescribeNodegroupInput{ClusterName: &clusterName, NodegroupName: &ngName})
if err != nil {
return resources, objects, err
}
if descOut.Nodegroup != nil {
objects = append(objects, *descOut.Nodegroup)
res, err := awsconv.NewResource(*descOut.Nodegroup)
if err != nil {
return resources, objects, err
}
parent := graph.InitResource(cloud.EKSCluster, clusterName)
res.AddRelation(rdf.ChildrenOfRel, parent)
resources = append(resources, res)
}
}
}
}
}
return resources, objects, nil
}
}
func addManualDynamodbFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["dynamodbtable"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []dynamodbtypes.TableDescription
if !conf.getBoolDefaultTrue("aws.dynamodb.dynamodbtable.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource dynamodb[dynamodbtable]")
return resources, objects, nil
}
paginator := dynamodb.NewListTablesPaginator(conf.APIs.Dynamodb, &dynamodb.ListTablesInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, name := range out.TableNames {
descOut, err := conf.APIs.Dynamodb.DescribeTable(ctx, &dynamodb.DescribeTableInput{TableName: &name})
if err != nil {
return resources, objects, err
}
if descOut.Table != nil {
objects = append(objects, *descOut.Table)
res, err := awsconv.NewResource(*descOut.Table)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
}
return resources, objects, nil
}
}
func addManualSecretsmanagerFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["key"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []kmstypes.KeyMetadata
if !conf.getBoolDefaultTrue("aws.secretsmanager.key.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource secretsmanager[key]")
return resources, objects, nil
}
paginator := kms.NewListKeysPaginator(conf.APIs.Kms, &kms.ListKeysInput{})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
for _, keyEntry := range out.Keys {
descOut, err := conf.APIs.Kms.DescribeKey(ctx, &kms.DescribeKeyInput{KeyId: keyEntry.KeyId})
if err != nil {
return resources, objects, err
}
if descOut.KeyMetadata != nil {
objects = append(objects, *descOut.KeyMetadata)
res, err := awsconv.NewResource(*descOut.KeyMetadata)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
}
}
return resources, objects, nil
}
}
func addManualApigatewayFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["apigateway"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []apigatewayv2types.Api
if !conf.getBoolDefaultTrue("aws.apigateway.apigateway.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource apigateway[apigateway]")
return resources, objects, nil
}
var nextToken *string
for {
out, err := conf.APIs.Apigatewayv2.GetApis(ctx, &apigatewayv2.GetApisInput{NextToken: nextToken})
if err != nil {
return resources, objects, err
}
for _, api := range out.Items {
objects = append(objects, api)
res, err := awsconv.NewResource(api)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
if out.NextToken == nil {
break
}
nextToken = out.NextToken
}
return resources, objects, nil
}
funcs["apigatewayroute"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []apigatewayv2types.Route
if !conf.getBoolDefaultTrue("aws.apigateway.apigatewayroute.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource apigateway[apigatewayroute]")
return resources, objects, nil
}
apisOut, err := conf.APIs.Apigatewayv2.GetApis(ctx, &apigatewayv2.GetApisInput{})
if err != nil {
return resources, objects, err
}
for _, api := range apisOut.Items {
routesOut, err := conf.APIs.Apigatewayv2.GetRoutes(ctx, &apigatewayv2.GetRoutesInput{ApiId: api.ApiId})
if err != nil {
return resources, objects, err
}
for _, route := range routesOut.Items {
objects = append(objects, route)
res, err := awsconv.NewResource(route)
if err != nil {
return resources, objects, err
}
parent := graph.InitResource(cloud.ApiGateway, awssdk.ToString(api.ApiId))
res.AddRelation(rdf.ChildrenOfRel, parent)
resources = append(resources, res)
}
}
return resources, objects, nil
}
funcs["apigatewaystage"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []apigatewayv2types.Stage
if !conf.getBoolDefaultTrue("aws.apigateway.apigatewaystage.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource apigateway[apigatewaystage]")
return resources, objects, nil
}
apisOut, err := conf.APIs.Apigatewayv2.GetApis(ctx, &apigatewayv2.GetApisInput{})
if err != nil {
return resources, objects, err
}
for _, api := range apisOut.Items {
stagesOut, err := conf.APIs.Apigatewayv2.GetStages(ctx, &apigatewayv2.GetStagesInput{ApiId: api.ApiId})
if err != nil {
return resources, objects, err
}
for _, stage := range stagesOut.Items {
objects = append(objects, stage)
res, err := awsconv.NewResource(stage)
if err != nil {
return resources, objects, err
}
parent := graph.InitResource(cloud.ApiGateway, awssdk.ToString(api.ApiId))
res.AddRelation(rdf.ChildrenOfRel, parent)
resources = append(resources, res)
}
}
return resources, objects, nil
}
}
func addManualSsmFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualCloudtrailFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
func addManualEfsFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
funcs["mounttarget"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []efstypes.MountTargetDescription
if !conf.getBoolDefaultTrue("aws.efs.mounttarget.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource efs[mounttarget]")
return resources, objects, nil
}
fsOut, err := conf.APIs.Efs.DescribeFileSystems(ctx, &efs.DescribeFileSystemsInput{})
if err != nil {
return resources, objects, err
}
for _, fs := range fsOut.FileSystems {
mtOut, err := conf.APIs.Efs.DescribeMountTargets(ctx, &efs.DescribeMountTargetsInput{FileSystemId: fs.FileSystemId})
if err != nil {
return resources, objects, err
}
for _, mt := range mtOut.MountTargets {
objects = append(objects, mt)
res, err := awsconv.NewResource(mt)
if err != nil {
return resources, objects, err
}
parent := graph.InitResource(cloud.FileSystem, awssdk.ToString(fs.FileSystemId))
res.AddRelation(rdf.ChildrenOfRel, parent)
resources = append(resources, res)
}
}
return resources, objects, nil
}
}
func addManualCloudwatchlogsFetchFuncs(conf *Config, funcs map[string]fetch.Func) {
}
package awsfetch
import (
"context"
"sync"
"strings"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
awsconv "github.com/wallix/awless/aws/conv"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/fetch"
"github.com/wallix/awless/graph"
)
func forEachBucketParallel(ctx context.Context, cache fetch.Cache, api *s3.Client, f func(b s3types.Bucket) error) error {
var buckets []s3types.Bucket
if val, e := cache.Get("getBucketsPerRegion", func() (interface{}, error) {
return getBucketsPerRegion(ctx, api)
}); e != nil {
return e
} else if v, ok := val.([]s3types.Bucket); ok {
buckets = v
}
errc := make(chan error)
var wg sync.WaitGroup
for _, output := range buckets {
wg.Add(1)
go func(b s3types.Bucket) {
defer wg.Done()
if err := f(b); err != nil {
errc <- err
}
}(output)
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
return err
}
}
return nil
}
func fetchObjectsForBucket(ctx context.Context, api *s3.Client, bucket s3types.Bucket, resourcesC chan<- *graph.Resource) error {
objectc := make(chan []s3types.Object)
errc := make(chan error)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
paginator := s3.NewListObjectsV2Paginator(api, &s3.ListObjectsV2Input{Bucket: bucket.Name})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
errc <- err
return
}
objectc <- page.Contents
}
}()
processObjects := func(objs []s3types.Object) {
for _, output := range objs {
res, err := awsconv.NewResource(output)
if err != nil {
errc <- err
return
}
res.SetProperty("Bucket", awssdk.ToString(bucket.Name))
resourcesC <- res
parent, err := awsconv.InitResource(bucket)
if err != nil {
errc <- err
return
}
res.AddRelation(rdf.ChildrenOfRel, parent)
resourcesC <- parent
}
}
go func() {
wg.Wait()
close(objectc)
close(errc)
}()
for {
select {
case err := <-errc:
return err
case objects, ok := <-objectc:
if !ok {
return nil
}
processObjects(objects)
}
}
}
func getBucketsPerRegion(ctx context.Context, api *s3.Client) ([]s3types.Bucket, error) {
var buckets []s3types.Bucket
out, err := api.ListBuckets(ctx, &s3.ListBucketsInput{})
if err != nil {
return buckets, err
}
var userBucketName string
var hasBucketFilter bool
if id, hasID := getUserFiltersFromContext(ctx)["id"]; hasID {
userBucketName = id
hasBucketFilter = true
} else if buck, hasBucket := getUserFiltersFromContext(ctx)["bucket"]; hasBucket {
userBucketName = buck
hasBucketFilter = true
}
if hasBucketFilter {
for _, b := range out.Buckets {
if strings.Contains(strings.ToLower(awssdk.ToString(b.Name)), strings.ToLower(userBucketName)) {
buckets = append(buckets, b)
}
}
} else {
buckets = out.Buckets
}
bucketc := make(chan s3types.Bucket)
errc := make(chan error)
var wg sync.WaitGroup
for _, bucket := range buckets {
wg.Add(1)
go func(b s3types.Bucket) {
defer wg.Done()
loc, err := api.GetBucketLocation(ctx, &s3.GetBucketLocationInput{Bucket: b.Name})
if err != nil {
errc <- err
return
}
region, _ := ctx.Value("region").(string)
locConstraint := string(loc.LocationConstraint)
switch locConstraint {
case "":
if region == "us-east-1" {
bucketc <- b
}
case region:
bucketc <- b
}
}(bucket)
}
go func() {
wg.Wait()
close(bucketc)
}()
var bucketsInRegion []s3types.Bucket
for {
select {
case err := <-errc:
if err != nil {
return bucketsInRegion, err
}
case b, ok := <-bucketc:
if !ok {
return bucketsInRegion, nil
}
bucketsInRegion = append(bucketsInRegion, b)
}
}
}
func fetchAndExtractGrantsFn(ctx context.Context, api *s3.Client, bucketName string) ([]*graph.Grant, error) {
acls, err := api.GetBucketAcl(ctx, &s3.GetBucketAclInput{Bucket: awssdk.String(bucketName)})
if err != nil {
return nil, err
}
var grants []*graph.Grant
for _, acl := range acls.Grants {
displayName := awssdk.ToString(acl.Grantee.DisplayName)
granteeType := string(acl.Grantee.Type)
granteeId := awssdk.ToString(acl.Grantee.ID)
if awssdk.ToString(acl.Grantee.EmailAddress) != "" {
displayName += "<" + awssdk.ToString(acl.Grantee.EmailAddress) + ">"
}
if granteeType == "Group" {
granteeId += awssdk.ToString(acl.Grantee.URI)
}
grant := &graph.Grant{
Permission: string(acl.Permission),
Grantee: graph.Grantee{
GranteeID: granteeId,
GranteeType: granteeType,
GranteeDisplayName: displayName,
},
}
grants = append(grants, grant)
}
return grants, nil
}
/*
Copyright 2017 WALLIX
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 awsservices
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/wallix/awless/logger"
)
// stsCacheDuration is the duration for which STS credentials are cached.
// In AWS SDK v2, the default assumed role session duration is 15 minutes.
var stsCacheDuration = 15 * time.Minute
type cachedCredential struct {
aws.Credentials
Expiration time.Time
}
func (c *cachedCredential) isExpired() bool {
return c.Expiration.Before(time.Now().UTC())
}
type fileCacheProvider struct {
creds aws.CredentialsProvider
curr *cachedCredential
profile string
log *logger.Logger
}
func (f *fileCacheProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
awlessCache := os.Getenv("__AWLESS_CACHE")
if awlessCache == "" {
return f.creds.Retrieve(ctx)
}
credFolder := filepath.Join(awlessCache, "credentials")
fold := &folder{credFolder}
credFile := fmt.Sprintf("aws-profile-%s.json", f.profile)
if content, ok := fold.getFileContent(credFile); ok {
var cached *cachedCredential
if err := json.Unmarshal(content, &cached); err != nil {
return aws.Credentials{}, err
}
f.log.ExtraVerbosef("loading credentials from '%s'", filepath.Join(credFolder, credFile))
if !cached.isExpired() {
f.curr = cached
return cached.Credentials, nil
}
}
credValue, err := f.creds.Retrieve(ctx)
if err != nil {
f.log.Errorf("%s\n", err)
return credValue, err
}
switch credValue.Source {
case stscreds.ProviderName:
cred := &cachedCredential{credValue, time.Now().UTC().Add(stsCacheDuration)}
f.curr = cred
content, err := json.Marshal(cred)
if err != nil {
return credValue, err
}
if err = fold.putFileContent(credFile, content); err != nil {
return credValue, fmt.Errorf("error writing cache file: %s", err.Error())
}
f.log.ExtraVerbosef("credentials cached in '%s'", filepath.Join(credFolder, credFile))
return credValue, nil
}
return credValue, nil
}
type folder struct {
path string
}
func (f *folder) getFileContent(filename string) (content []byte, ok bool) {
if _, err := os.Stat(f.path); err != nil {
return
}
credPath := filepath.Join(f.path, filename)
if _, readerr := os.Stat(credPath); readerr != nil {
return
}
var err error
if content, err = os.ReadFile(credPath); err != nil {
return
}
ok = true
return
}
func (f *folder) putFileContent(filename string, content []byte) error {
if _, err := os.Stat(f.path); os.IsNotExist(err) {
os.MkdirAll(f.path, 0700)
}
return os.WriteFile(filepath.Join(f.path, filename), content, 0600)
}
// Auto generated implementation for the AWS cloud service
/*
Copyright 2017 WALLIX
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 awsservices
// DO NOT EDIT - This file was automatically generated with go generate
import (
"context"
"errors"
"sync"
"github.com/aws/aws-sdk-go-v2/aws"
ec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
rds "github.com/aws/aws-sdk-go-v2/service/rds"
rdstypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
autoscaling "github.com/aws/aws-sdk-go-v2/service/autoscaling"
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
ecr "github.com/aws/aws-sdk-go-v2/service/ecr"
ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types"
ecs "github.com/aws/aws-sdk-go-v2/service/ecs"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
applicationautoscaling "github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
acm "github.com/aws/aws-sdk-go-v2/service/acm"
acmtypes "github.com/aws/aws-sdk-go-v2/service/acm/types"
iam "github.com/aws/aws-sdk-go-v2/service/iam"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
sts "github.com/aws/aws-sdk-go-v2/service/sts"
s3 "github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
sns "github.com/aws/aws-sdk-go-v2/service/sns"
snstypes "github.com/aws/aws-sdk-go-v2/service/sns/types"
sqs "github.com/aws/aws-sdk-go-v2/service/sqs"
route53 "github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
lambda "github.com/aws/aws-sdk-go-v2/service/lambda"
lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types"
cloudwatch "github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
cloudfront "github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudfronttypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
cloudformation "github.com/aws/aws-sdk-go-v2/service/cloudformation"
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
eks "github.com/aws/aws-sdk-go-v2/service/eks"
ekstypes "github.com/aws/aws-sdk-go-v2/service/eks/types"
dynamodb "github.com/aws/aws-sdk-go-v2/service/dynamodb"
dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
secretsmanager "github.com/aws/aws-sdk-go-v2/service/secretsmanager"
secretsmanagertypes "github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
kms "github.com/aws/aws-sdk-go-v2/service/kms"
kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types"
apigatewayv2 "github.com/aws/aws-sdk-go-v2/service/apigatewayv2"
apigatewayv2types "github.com/aws/aws-sdk-go-v2/service/apigatewayv2/types"
ssm "github.com/aws/aws-sdk-go-v2/service/ssm"
ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
efs "github.com/aws/aws-sdk-go-v2/service/efs"
efstypes "github.com/aws/aws-sdk-go-v2/service/efs/types"
cloudtrail "github.com/aws/aws-sdk-go-v2/service/cloudtrail"
cloudtrailtypes "github.com/aws/aws-sdk-go-v2/service/cloudtrail/types"
cloudwatchlogs "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/fetch"
awsfetch "github.com/wallix/awless/aws/fetch"
tstore "github.com/wallix/triplestore"
)
const accessDenied = "Access Denied"
var ServiceNames = []string{
"infra",
"access",
"storage",
"messaging",
"dns",
"lambda",
"monitoring",
"cdn",
"cloudformation",
"eks",
"dynamodb",
"secretsmanager",
"apigateway",
"ssm",
"efs",
"cloudtrail",
"cloudwatchlogs",
}
var ResourceTypes = []string {
"instance",
"subnet",
"vpc",
"keypair",
"securitygroup",
"volume",
"internetgateway",
"natgateway",
"routetable",
"availabilityzone",
"image",
"importimagetask",
"elasticip",
"snapshot",
"networkinterface",
"classicloadbalancer",
"loadbalancer",
"targetgroup",
"listener",
"database",
"dbsubnetgroup",
"launchconfiguration",
"scalinggroup",
"scalingpolicy",
"repository",
"containercluster",
"containertask",
"container",
"containerinstance",
"certificate",
"user",
"group",
"role",
"policy",
"accesskey",
"instanceprofile",
"mfadevice",
"bucket",
"s3object",
"subscription",
"topic",
"queue",
"zone",
"record",
"function",
"metric",
"alarm",
"distribution",
"stack",
"ekscluster",
"eksnodegroup",
"dynamodbtable",
"secret",
"key",
"apigateway",
"apigatewayroute",
"apigatewaystage",
"ssmparameter",
"filesystem",
"mounttarget",
"trail",
"loggroup",
}
var ServicePerAPI = map[string]string {
"ec2": "infra",
"elbv2": "infra",
"elb": "infra",
"rds": "infra",
"autoscaling": "infra",
"ecr": "infra",
"ecs": "infra",
"applicationautoscaling": "infra",
"acm": "infra",
"iam": "access",
"sts": "access",
"s3": "storage",
"sns": "messaging",
"sqs": "messaging",
"route53": "dns",
"lambda": "lambda",
"cloudwatch": "monitoring",
"cloudfront": "cdn",
"cloudformation": "cloudformation",
"eks": "eks",
"dynamodb": "dynamodb",
"secretsmanager": "secretsmanager",
"kms": "secretsmanager",
"apigatewayv2": "apigateway",
"ssm": "ssm",
"efs": "efs",
"cloudtrail": "cloudtrail",
"cloudwatchlogs": "cloudwatchlogs",
}
var ServicePerResourceType = map[string]string {
"instance": "infra",
"subnet": "infra",
"vpc": "infra",
"keypair": "infra",
"securitygroup": "infra",
"volume": "infra",
"internetgateway": "infra",
"natgateway": "infra",
"routetable": "infra",
"availabilityzone": "infra",
"image": "infra",
"importimagetask": "infra",
"elasticip": "infra",
"snapshot": "infra",
"networkinterface": "infra",
"classicloadbalancer": "infra",
"loadbalancer": "infra",
"targetgroup": "infra",
"listener": "infra",
"database": "infra",
"dbsubnetgroup": "infra",
"launchconfiguration": "infra",
"scalinggroup": "infra",
"scalingpolicy": "infra",
"repository": "infra",
"containercluster": "infra",
"containertask": "infra",
"container": "infra",
"containerinstance": "infra",
"certificate": "infra",
"user": "access",
"group": "access",
"role": "access",
"policy": "access",
"accesskey": "access",
"instanceprofile": "access",
"mfadevice": "access",
"bucket": "storage",
"s3object": "storage",
"subscription": "messaging",
"topic": "messaging",
"queue": "messaging",
"zone": "dns",
"record": "dns",
"function": "lambda",
"metric": "monitoring",
"alarm": "monitoring",
"distribution": "cdn",
"stack": "cloudformation",
"ekscluster": "eks",
"eksnodegroup": "eks",
"dynamodbtable": "dynamodb",
"secret": "secretsmanager",
"key": "secretsmanager",
"apigateway": "apigateway",
"apigatewayroute": "apigateway",
"apigatewaystage": "apigateway",
"ssmparameter": "ssm",
"filesystem": "efs",
"mounttarget": "efs",
"trail": "cloudtrail",
"loggroup": "cloudwatchlogs",
}
var APIPerResourceType = map[string]string {
"instance": "ec2",
"subnet": "ec2",
"vpc": "ec2",
"keypair": "ec2",
"securitygroup": "ec2",
"volume": "ec2",
"internetgateway": "ec2",
"natgateway": "ec2",
"routetable": "ec2",
"availabilityzone": "ec2",
"image": "ec2",
"importimagetask": "ec2",
"elasticip": "ec2",
"snapshot": "ec2",
"networkinterface": "ec2",
"classicloadbalancer": "elb",
"loadbalancer": "elbv2",
"targetgroup": "elbv2",
"listener": "elbv2",
"database": "rds",
"dbsubnetgroup": "rds",
"launchconfiguration": "autoscaling",
"scalinggroup": "autoscaling",
"scalingpolicy": "autoscaling",
"repository": "ecr",
"containercluster": "ecs",
"containertask": "ecs",
"container": "ecs",
"containerinstance": "ecs",
"certificate": "acm",
"user": "iam",
"group": "iam",
"role": "iam",
"policy": "iam",
"accesskey": "iam",
"instanceprofile": "iam",
"mfadevice": "iam",
"bucket": "s3",
"s3object": "s3",
"subscription": "sns",
"topic": "sns",
"queue": "sqs",
"zone": "route53",
"record": "route53",
"function": "lambda",
"metric": "cloudwatch",
"alarm": "cloudwatch",
"distribution": "cloudfront",
"stack": "cloudformation",
"ekscluster": "eks",
"eksnodegroup": "eks",
"dynamodbtable": "dynamodb",
"secret": "secretsmanager",
"key": "kms",
"apigateway": "apigatewayv2",
"apigatewayroute": "apigatewayv2",
"apigatewaystage": "apigatewayv2",
"ssmparameter": "ssm",
"filesystem": "efs",
"mounttarget": "efs",
"trail": "cloudtrail",
"loggroup": "cloudwatchlogs",
}
type Infra struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
Ec2Client *ec2.Client
Elbv2Client *elbv2.Client
ElbClient *elb.Client
RdsClient *rds.Client
AutoscalingClient *autoscaling.Client
EcrClient *ecr.Client
EcsClient *ecs.Client
ApplicationautoscalingClient *applicationautoscaling.Client
AcmClient *acm.Client
}
func NewInfra(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
ec2Client := ec2.NewFromConfig(cfg)
elbv2Client := elbv2.NewFromConfig(cfg)
elbClient := elb.NewFromConfig(cfg)
rdsClient := rds.NewFromConfig(cfg)
autoscalingClient := autoscaling.NewFromConfig(cfg)
ecrClient := ecr.NewFromConfig(cfg)
ecsClient := ecs.NewFromConfig(cfg)
applicationautoscalingClient := applicationautoscaling.NewFromConfig(cfg)
acmClient := acm.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
ec2Client,
elbv2Client,
elbClient,
rdsClient,
autoscalingClient,
ecrClient,
ecsClient,
applicationautoscalingClient,
acmClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Infra{
Ec2Client: ec2Client,
Elbv2Client: elbv2Client,
ElbClient: elbClient,
RdsClient: rdsClient,
AutoscalingClient: autoscalingClient,
EcrClient: ecrClient,
EcsClient: ecsClient,
ApplicationautoscalingClient: applicationautoscalingClient,
AcmClient: acmClient,
fetcher: fetch.NewFetcher(awsfetch.BuildInfraFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Infra) Name() string {
return "infra"
}
func (s *Infra) Region() string {
return s.region
}
func (s *Infra) Profile() string {
return s.profile
}
func (s *Infra) ResourceTypes() []string {
return []string{
"instance",
"subnet",
"vpc",
"keypair",
"securitygroup",
"volume",
"internetgateway",
"natgateway",
"routetable",
"availabilityzone",
"image",
"importimagetask",
"elasticip",
"snapshot",
"networkinterface",
"classicloadbalancer",
"loadbalancer",
"targetgroup",
"listener",
"database",
"dbsubnetgroup",
"launchconfiguration",
"scalinggroup",
"scalingpolicy",
"repository",
"containercluster",
"containertask",
"container",
"containerinstance",
"certificate",
}
}
func (s *Infra) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.infra.instance.sync", true) {
list, err := s.fetcher.Get("instance_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Instance); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Instance' type from fetch context")
}
for _, r := range list.([]ec2types.Instance) {
for _, fn := range addParentsFns["instance"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Instance) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.subnet.sync", true) {
list, err := s.fetcher.Get("subnet_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Subnet); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Subnet' type from fetch context")
}
for _, r := range list.([]ec2types.Subnet) {
for _, fn := range addParentsFns["subnet"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Subnet) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.vpc.sync", true) {
list, err := s.fetcher.Get("vpc_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Vpc); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Vpc' type from fetch context")
}
for _, r := range list.([]ec2types.Vpc) {
for _, fn := range addParentsFns["vpc"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Vpc) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.keypair.sync", true) {
list, err := s.fetcher.Get("keypair_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.KeyPairInfo); !ok {
return gph, errors.New("cannot cast to '[]ec2types.KeyPairInfo' type from fetch context")
}
for _, r := range list.([]ec2types.KeyPairInfo) {
for _, fn := range addParentsFns["keypair"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.KeyPairInfo) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.securitygroup.sync", true) {
list, err := s.fetcher.Get("securitygroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.SecurityGroup); !ok {
return gph, errors.New("cannot cast to '[]ec2types.SecurityGroup' type from fetch context")
}
for _, r := range list.([]ec2types.SecurityGroup) {
for _, fn := range addParentsFns["securitygroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.SecurityGroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.volume.sync", true) {
list, err := s.fetcher.Get("volume_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Volume); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Volume' type from fetch context")
}
for _, r := range list.([]ec2types.Volume) {
for _, fn := range addParentsFns["volume"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Volume) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.internetgateway.sync", true) {
list, err := s.fetcher.Get("internetgateway_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.InternetGateway); !ok {
return gph, errors.New("cannot cast to '[]ec2types.InternetGateway' type from fetch context")
}
for _, r := range list.([]ec2types.InternetGateway) {
for _, fn := range addParentsFns["internetgateway"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.InternetGateway) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.natgateway.sync", true) {
list, err := s.fetcher.Get("natgateway_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.NatGateway); !ok {
return gph, errors.New("cannot cast to '[]ec2types.NatGateway' type from fetch context")
}
for _, r := range list.([]ec2types.NatGateway) {
for _, fn := range addParentsFns["natgateway"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.NatGateway) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.routetable.sync", true) {
list, err := s.fetcher.Get("routetable_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.RouteTable); !ok {
return gph, errors.New("cannot cast to '[]ec2types.RouteTable' type from fetch context")
}
for _, r := range list.([]ec2types.RouteTable) {
for _, fn := range addParentsFns["routetable"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.RouteTable) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.availabilityzone.sync", true) {
list, err := s.fetcher.Get("availabilityzone_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.AvailabilityZone); !ok {
return gph, errors.New("cannot cast to '[]ec2types.AvailabilityZone' type from fetch context")
}
for _, r := range list.([]ec2types.AvailabilityZone) {
for _, fn := range addParentsFns["availabilityzone"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.AvailabilityZone) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.image.sync", true) {
list, err := s.fetcher.Get("image_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Image); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Image' type from fetch context")
}
for _, r := range list.([]ec2types.Image) {
for _, fn := range addParentsFns["image"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Image) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.importimagetask.sync", true) {
list, err := s.fetcher.Get("importimagetask_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.ImportImageTask); !ok {
return gph, errors.New("cannot cast to '[]ec2types.ImportImageTask' type from fetch context")
}
for _, r := range list.([]ec2types.ImportImageTask) {
for _, fn := range addParentsFns["importimagetask"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.ImportImageTask) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.elasticip.sync", true) {
list, err := s.fetcher.Get("elasticip_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Address); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Address' type from fetch context")
}
for _, r := range list.([]ec2types.Address) {
for _, fn := range addParentsFns["elasticip"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Address) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.snapshot.sync", true) {
list, err := s.fetcher.Get("snapshot_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.Snapshot); !ok {
return gph, errors.New("cannot cast to '[]ec2types.Snapshot' type from fetch context")
}
for _, r := range list.([]ec2types.Snapshot) {
for _, fn := range addParentsFns["snapshot"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.Snapshot) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.networkinterface.sync", true) {
list, err := s.fetcher.Get("networkinterface_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ec2types.NetworkInterface); !ok {
return gph, errors.New("cannot cast to '[]ec2types.NetworkInterface' type from fetch context")
}
for _, r := range list.([]ec2types.NetworkInterface) {
for _, fn := range addParentsFns["networkinterface"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ec2types.NetworkInterface) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.classicloadbalancer.sync", true) {
list, err := s.fetcher.Get("classicloadbalancer_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]elbtypes.LoadBalancerDescription); !ok {
return gph, errors.New("cannot cast to '[]elbtypes.LoadBalancerDescription' type from fetch context")
}
for _, r := range list.([]elbtypes.LoadBalancerDescription) {
for _, fn := range addParentsFns["classicloadbalancer"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *elbtypes.LoadBalancerDescription) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.loadbalancer.sync", true) {
list, err := s.fetcher.Get("loadbalancer_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]elbv2types.LoadBalancer); !ok {
return gph, errors.New("cannot cast to '[]elbv2types.LoadBalancer' type from fetch context")
}
for _, r := range list.([]elbv2types.LoadBalancer) {
for _, fn := range addParentsFns["loadbalancer"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *elbv2types.LoadBalancer) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.targetgroup.sync", true) {
list, err := s.fetcher.Get("targetgroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]elbv2types.TargetGroup); !ok {
return gph, errors.New("cannot cast to '[]elbv2types.TargetGroup' type from fetch context")
}
for _, r := range list.([]elbv2types.TargetGroup) {
for _, fn := range addParentsFns["targetgroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *elbv2types.TargetGroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.listener.sync", true) {
list, err := s.fetcher.Get("listener_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]elbv2types.Listener); !ok {
return gph, errors.New("cannot cast to '[]elbv2types.Listener' type from fetch context")
}
for _, r := range list.([]elbv2types.Listener) {
for _, fn := range addParentsFns["listener"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *elbv2types.Listener) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.database.sync", true) {
list, err := s.fetcher.Get("database_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]rdstypes.DBInstance); !ok {
return gph, errors.New("cannot cast to '[]rdstypes.DBInstance' type from fetch context")
}
for _, r := range list.([]rdstypes.DBInstance) {
for _, fn := range addParentsFns["database"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *rdstypes.DBInstance) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.dbsubnetgroup.sync", true) {
list, err := s.fetcher.Get("dbsubnetgroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]rdstypes.DBSubnetGroup); !ok {
return gph, errors.New("cannot cast to '[]rdstypes.DBSubnetGroup' type from fetch context")
}
for _, r := range list.([]rdstypes.DBSubnetGroup) {
for _, fn := range addParentsFns["dbsubnetgroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *rdstypes.DBSubnetGroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.launchconfiguration.sync", true) {
list, err := s.fetcher.Get("launchconfiguration_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]autoscalingtypes.LaunchConfiguration); !ok {
return gph, errors.New("cannot cast to '[]autoscalingtypes.LaunchConfiguration' type from fetch context")
}
for _, r := range list.([]autoscalingtypes.LaunchConfiguration) {
for _, fn := range addParentsFns["launchconfiguration"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *autoscalingtypes.LaunchConfiguration) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.scalinggroup.sync", true) {
list, err := s.fetcher.Get("scalinggroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]autoscalingtypes.AutoScalingGroup); !ok {
return gph, errors.New("cannot cast to '[]autoscalingtypes.AutoScalingGroup' type from fetch context")
}
for _, r := range list.([]autoscalingtypes.AutoScalingGroup) {
for _, fn := range addParentsFns["scalinggroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *autoscalingtypes.AutoScalingGroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.scalingpolicy.sync", true) {
list, err := s.fetcher.Get("scalingpolicy_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]autoscalingtypes.ScalingPolicy); !ok {
return gph, errors.New("cannot cast to '[]autoscalingtypes.ScalingPolicy' type from fetch context")
}
for _, r := range list.([]autoscalingtypes.ScalingPolicy) {
for _, fn := range addParentsFns["scalingpolicy"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *autoscalingtypes.ScalingPolicy) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.repository.sync", true) {
list, err := s.fetcher.Get("repository_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ecrtypes.Repository); !ok {
return gph, errors.New("cannot cast to '[]ecrtypes.Repository' type from fetch context")
}
for _, r := range list.([]ecrtypes.Repository) {
for _, fn := range addParentsFns["repository"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ecrtypes.Repository) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.containercluster.sync", true) {
list, err := s.fetcher.Get("containercluster_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ecstypes.Cluster); !ok {
return gph, errors.New("cannot cast to '[]ecstypes.Cluster' type from fetch context")
}
for _, r := range list.([]ecstypes.Cluster) {
for _, fn := range addParentsFns["containercluster"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ecstypes.Cluster) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.containertask.sync", true) {
list, err := s.fetcher.Get("containertask_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ecstypes.TaskDefinition); !ok {
return gph, errors.New("cannot cast to '[]ecstypes.TaskDefinition' type from fetch context")
}
for _, r := range list.([]ecstypes.TaskDefinition) {
for _, fn := range addParentsFns["containertask"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ecstypes.TaskDefinition) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.container.sync", true) {
list, err := s.fetcher.Get("container_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ecstypes.Container); !ok {
return gph, errors.New("cannot cast to '[]ecstypes.Container' type from fetch context")
}
for _, r := range list.([]ecstypes.Container) {
for _, fn := range addParentsFns["container"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ecstypes.Container) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.containerinstance.sync", true) {
list, err := s.fetcher.Get("containerinstance_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ecstypes.ContainerInstance); !ok {
return gph, errors.New("cannot cast to '[]ecstypes.ContainerInstance' type from fetch context")
}
for _, r := range list.([]ecstypes.ContainerInstance) {
for _, fn := range addParentsFns["containerinstance"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ecstypes.ContainerInstance) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.infra.certificate.sync", true) {
list, err := s.fetcher.Get("certificate_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]acmtypes.CertificateSummary); !ok {
return gph, errors.New("cannot cast to '[]acmtypes.CertificateSummary' type from fetch context")
}
for _, r := range list.([]acmtypes.CertificateSummary) {
for _, fn := range addParentsFns["certificate"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *acmtypes.CertificateSummary) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Infra) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Infra) IsSyncDisabled() bool {
return !getBool(s.config, "aws.infra.sync", true)
}
type Access struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
IamClient *iam.Client
StsClient *sts.Client
}
func NewAccess(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := "global"
iamClient := iam.NewFromConfig(cfg)
stsClient := sts.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
iamClient,
stsClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Access{
IamClient: iamClient,
StsClient: stsClient,
fetcher: fetch.NewFetcher(awsfetch.BuildAccessFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Access) Name() string {
return "access"
}
func (s *Access) Region() string {
return s.region
}
func (s *Access) Profile() string {
return s.profile
}
func (s *Access) ResourceTypes() []string {
return []string{
"user",
"group",
"role",
"policy",
"accesskey",
"instanceprofile",
"mfadevice",
}
}
func (s *Access) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.access.user.sync", true) {
list, err := s.fetcher.Get("user_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.UserDetail); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.UserDetail' type from fetch context")
}
for _, r := range list.([]iamtypes.UserDetail) {
for _, fn := range addParentsFns["user"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.UserDetail) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.group.sync", true) {
list, err := s.fetcher.Get("group_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.GroupDetail); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.GroupDetail' type from fetch context")
}
for _, r := range list.([]iamtypes.GroupDetail) {
for _, fn := range addParentsFns["group"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.GroupDetail) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.role.sync", true) {
list, err := s.fetcher.Get("role_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.RoleDetail); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.RoleDetail' type from fetch context")
}
for _, r := range list.([]iamtypes.RoleDetail) {
for _, fn := range addParentsFns["role"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.RoleDetail) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.policy.sync", true) {
list, err := s.fetcher.Get("policy_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.Policy); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.Policy' type from fetch context")
}
for _, r := range list.([]iamtypes.Policy) {
for _, fn := range addParentsFns["policy"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.Policy) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.accesskey.sync", true) {
list, err := s.fetcher.Get("accesskey_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.AccessKeyMetadata); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.AccessKeyMetadata' type from fetch context")
}
for _, r := range list.([]iamtypes.AccessKeyMetadata) {
for _, fn := range addParentsFns["accesskey"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.AccessKeyMetadata) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.instanceprofile.sync", true) {
list, err := s.fetcher.Get("instanceprofile_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.InstanceProfile); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.InstanceProfile' type from fetch context")
}
for _, r := range list.([]iamtypes.InstanceProfile) {
for _, fn := range addParentsFns["instanceprofile"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.InstanceProfile) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.access.mfadevice.sync", true) {
list, err := s.fetcher.Get("mfadevice_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]iamtypes.VirtualMFADevice); !ok {
return gph, errors.New("cannot cast to '[]iamtypes.VirtualMFADevice' type from fetch context")
}
for _, r := range list.([]iamtypes.VirtualMFADevice) {
for _, fn := range addParentsFns["mfadevice"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *iamtypes.VirtualMFADevice) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Access) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Access) IsSyncDisabled() bool {
return !getBool(s.config, "aws.access.sync", true)
}
type Storage struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
S3Client *s3.Client
}
func NewStorage(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
s3Client := s3.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
s3Client,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Storage{
S3Client: s3Client,
fetcher: fetch.NewFetcher(awsfetch.BuildStorageFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Storage) Name() string {
return "storage"
}
func (s *Storage) Region() string {
return s.region
}
func (s *Storage) Profile() string {
return s.profile
}
func (s *Storage) ResourceTypes() []string {
return []string{
"bucket",
"s3object",
}
}
func (s *Storage) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.storage.bucket.sync", true) {
list, err := s.fetcher.Get("bucket_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]s3types.Bucket); !ok {
return gph, errors.New("cannot cast to '[]s3types.Bucket' type from fetch context")
}
for _, r := range list.([]s3types.Bucket) {
for _, fn := range addParentsFns["bucket"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *s3types.Bucket) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.storage.s3object.sync", true) {
list, err := s.fetcher.Get("s3object_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]s3types.Object); !ok {
return gph, errors.New("cannot cast to '[]s3types.Object' type from fetch context")
}
for _, r := range list.([]s3types.Object) {
for _, fn := range addParentsFns["s3object"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *s3types.Object) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Storage) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Storage) IsSyncDisabled() bool {
return !getBool(s.config, "aws.storage.sync", true)
}
type Messaging struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
SnsClient *sns.Client
SqsClient *sqs.Client
}
func NewMessaging(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
snsClient := sns.NewFromConfig(cfg)
sqsClient := sqs.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
snsClient,
sqsClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Messaging{
SnsClient: snsClient,
SqsClient: sqsClient,
fetcher: fetch.NewFetcher(awsfetch.BuildMessagingFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Messaging) Name() string {
return "messaging"
}
func (s *Messaging) Region() string {
return s.region
}
func (s *Messaging) Profile() string {
return s.profile
}
func (s *Messaging) ResourceTypes() []string {
return []string{
"subscription",
"topic",
"queue",
}
}
func (s *Messaging) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.messaging.subscription.sync", true) {
list, err := s.fetcher.Get("subscription_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]snstypes.Subscription); !ok {
return gph, errors.New("cannot cast to '[]snstypes.Subscription' type from fetch context")
}
for _, r := range list.([]snstypes.Subscription) {
for _, fn := range addParentsFns["subscription"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *snstypes.Subscription) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.messaging.topic.sync", true) {
list, err := s.fetcher.Get("topic_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]snstypes.Topic); !ok {
return gph, errors.New("cannot cast to '[]snstypes.Topic' type from fetch context")
}
for _, r := range list.([]snstypes.Topic) {
for _, fn := range addParentsFns["topic"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *snstypes.Topic) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.messaging.queue.sync", true) {
list, err := s.fetcher.Get("queue_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]string); !ok {
return gph, errors.New("cannot cast to '[]string' type from fetch context")
}
for _, r := range list.([]string) {
for _, fn := range addParentsFns["queue"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *string) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Messaging) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Messaging) IsSyncDisabled() bool {
return !getBool(s.config, "aws.messaging.sync", true)
}
type Dns struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
Route53Client *route53.Client
}
func NewDns(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := "global"
route53Client := route53.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
route53Client,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Dns{
Route53Client: route53Client,
fetcher: fetch.NewFetcher(awsfetch.BuildDnsFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Dns) Name() string {
return "dns"
}
func (s *Dns) Region() string {
return s.region
}
func (s *Dns) Profile() string {
return s.profile
}
func (s *Dns) ResourceTypes() []string {
return []string{
"zone",
"record",
}
}
func (s *Dns) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.dns.zone.sync", true) {
list, err := s.fetcher.Get("zone_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]route53types.HostedZone); !ok {
return gph, errors.New("cannot cast to '[]route53types.HostedZone' type from fetch context")
}
for _, r := range list.([]route53types.HostedZone) {
for _, fn := range addParentsFns["zone"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *route53types.HostedZone) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.dns.record.sync", true) {
list, err := s.fetcher.Get("record_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]route53types.ResourceRecordSet); !ok {
return gph, errors.New("cannot cast to '[]route53types.ResourceRecordSet' type from fetch context")
}
for _, r := range list.([]route53types.ResourceRecordSet) {
for _, fn := range addParentsFns["record"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *route53types.ResourceRecordSet) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Dns) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Dns) IsSyncDisabled() bool {
return !getBool(s.config, "aws.dns.sync", true)
}
type Lambda struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
LambdaClient *lambda.Client
}
func NewLambda(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
lambdaClient := lambda.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
lambdaClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Lambda{
LambdaClient: lambdaClient,
fetcher: fetch.NewFetcher(awsfetch.BuildLambdaFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Lambda) Name() string {
return "lambda"
}
func (s *Lambda) Region() string {
return s.region
}
func (s *Lambda) Profile() string {
return s.profile
}
func (s *Lambda) ResourceTypes() []string {
return []string{
"function",
}
}
func (s *Lambda) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.lambda.function.sync", true) {
list, err := s.fetcher.Get("function_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]lambdatypes.FunctionConfiguration); !ok {
return gph, errors.New("cannot cast to '[]lambdatypes.FunctionConfiguration' type from fetch context")
}
for _, r := range list.([]lambdatypes.FunctionConfiguration) {
for _, fn := range addParentsFns["function"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *lambdatypes.FunctionConfiguration) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Lambda) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Lambda) IsSyncDisabled() bool {
return !getBool(s.config, "aws.lambda.sync", true)
}
type Monitoring struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
CloudwatchClient *cloudwatch.Client
}
func NewMonitoring(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
cloudwatchClient := cloudwatch.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
cloudwatchClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Monitoring{
CloudwatchClient: cloudwatchClient,
fetcher: fetch.NewFetcher(awsfetch.BuildMonitoringFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Monitoring) Name() string {
return "monitoring"
}
func (s *Monitoring) Region() string {
return s.region
}
func (s *Monitoring) Profile() string {
return s.profile
}
func (s *Monitoring) ResourceTypes() []string {
return []string{
"metric",
"alarm",
}
}
func (s *Monitoring) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.monitoring.metric.sync", true) {
list, err := s.fetcher.Get("metric_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudwatchtypes.Metric); !ok {
return gph, errors.New("cannot cast to '[]cloudwatchtypes.Metric' type from fetch context")
}
for _, r := range list.([]cloudwatchtypes.Metric) {
for _, fn := range addParentsFns["metric"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudwatchtypes.Metric) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.monitoring.alarm.sync", true) {
list, err := s.fetcher.Get("alarm_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudwatchtypes.MetricAlarm); !ok {
return gph, errors.New("cannot cast to '[]cloudwatchtypes.MetricAlarm' type from fetch context")
}
for _, r := range list.([]cloudwatchtypes.MetricAlarm) {
for _, fn := range addParentsFns["alarm"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudwatchtypes.MetricAlarm) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Monitoring) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Monitoring) IsSyncDisabled() bool {
return !getBool(s.config, "aws.monitoring.sync", true)
}
type Cdn struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
CloudfrontClient *cloudfront.Client
}
func NewCdn(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := "global"
cloudfrontClient := cloudfront.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
cloudfrontClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Cdn{
CloudfrontClient: cloudfrontClient,
fetcher: fetch.NewFetcher(awsfetch.BuildCdnFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Cdn) Name() string {
return "cdn"
}
func (s *Cdn) Region() string {
return s.region
}
func (s *Cdn) Profile() string {
return s.profile
}
func (s *Cdn) ResourceTypes() []string {
return []string{
"distribution",
}
}
func (s *Cdn) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.cdn.distribution.sync", true) {
list, err := s.fetcher.Get("distribution_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudfronttypes.DistributionSummary); !ok {
return gph, errors.New("cannot cast to '[]cloudfronttypes.DistributionSummary' type from fetch context")
}
for _, r := range list.([]cloudfronttypes.DistributionSummary) {
for _, fn := range addParentsFns["distribution"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudfronttypes.DistributionSummary) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Cdn) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Cdn) IsSyncDisabled() bool {
return !getBool(s.config, "aws.cdn.sync", true)
}
type Cloudformation struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
CloudformationClient *cloudformation.Client
}
func NewCloudformation(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
cloudformationClient := cloudformation.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
cloudformationClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Cloudformation{
CloudformationClient: cloudformationClient,
fetcher: fetch.NewFetcher(awsfetch.BuildCloudformationFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Cloudformation) Name() string {
return "cloudformation"
}
func (s *Cloudformation) Region() string {
return s.region
}
func (s *Cloudformation) Profile() string {
return s.profile
}
func (s *Cloudformation) ResourceTypes() []string {
return []string{
"stack",
}
}
func (s *Cloudformation) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.cloudformation.stack.sync", true) {
list, err := s.fetcher.Get("stack_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudformationtypes.Stack); !ok {
return gph, errors.New("cannot cast to '[]cloudformationtypes.Stack' type from fetch context")
}
for _, r := range list.([]cloudformationtypes.Stack) {
for _, fn := range addParentsFns["stack"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudformationtypes.Stack) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Cloudformation) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Cloudformation) IsSyncDisabled() bool {
return !getBool(s.config, "aws.cloudformation.sync", true)
}
type Eks struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
EksClient *eks.Client
}
func NewEks(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
eksClient := eks.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
eksClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Eks{
EksClient: eksClient,
fetcher: fetch.NewFetcher(awsfetch.BuildEksFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Eks) Name() string {
return "eks"
}
func (s *Eks) Region() string {
return s.region
}
func (s *Eks) Profile() string {
return s.profile
}
func (s *Eks) ResourceTypes() []string {
return []string{
"ekscluster",
"eksnodegroup",
}
}
func (s *Eks) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.eks.ekscluster.sync", true) {
list, err := s.fetcher.Get("ekscluster_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ekstypes.Cluster); !ok {
return gph, errors.New("cannot cast to '[]ekstypes.Cluster' type from fetch context")
}
for _, r := range list.([]ekstypes.Cluster) {
for _, fn := range addParentsFns["ekscluster"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ekstypes.Cluster) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.eks.eksnodegroup.sync", true) {
list, err := s.fetcher.Get("eksnodegroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ekstypes.Nodegroup); !ok {
return gph, errors.New("cannot cast to '[]ekstypes.Nodegroup' type from fetch context")
}
for _, r := range list.([]ekstypes.Nodegroup) {
for _, fn := range addParentsFns["eksnodegroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ekstypes.Nodegroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Eks) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Eks) IsSyncDisabled() bool {
return !getBool(s.config, "aws.eks.sync", true)
}
type Dynamodb struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
DynamodbClient *dynamodb.Client
}
func NewDynamodb(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
dynamodbClient := dynamodb.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
dynamodbClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Dynamodb{
DynamodbClient: dynamodbClient,
fetcher: fetch.NewFetcher(awsfetch.BuildDynamodbFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Dynamodb) Name() string {
return "dynamodb"
}
func (s *Dynamodb) Region() string {
return s.region
}
func (s *Dynamodb) Profile() string {
return s.profile
}
func (s *Dynamodb) ResourceTypes() []string {
return []string{
"dynamodbtable",
}
}
func (s *Dynamodb) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.dynamodb.dynamodbtable.sync", true) {
list, err := s.fetcher.Get("dynamodbtable_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]dynamodbtypes.TableDescription); !ok {
return gph, errors.New("cannot cast to '[]dynamodbtypes.TableDescription' type from fetch context")
}
for _, r := range list.([]dynamodbtypes.TableDescription) {
for _, fn := range addParentsFns["dynamodbtable"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *dynamodbtypes.TableDescription) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Dynamodb) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Dynamodb) IsSyncDisabled() bool {
return !getBool(s.config, "aws.dynamodb.sync", true)
}
type Secretsmanager struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
SecretsmanagerClient *secretsmanager.Client
KmsClient *kms.Client
}
func NewSecretsmanager(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
secretsmanagerClient := secretsmanager.NewFromConfig(cfg)
kmsClient := kms.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
secretsmanagerClient,
kmsClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Secretsmanager{
SecretsmanagerClient: secretsmanagerClient,
KmsClient: kmsClient,
fetcher: fetch.NewFetcher(awsfetch.BuildSecretsmanagerFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Secretsmanager) Name() string {
return "secretsmanager"
}
func (s *Secretsmanager) Region() string {
return s.region
}
func (s *Secretsmanager) Profile() string {
return s.profile
}
func (s *Secretsmanager) ResourceTypes() []string {
return []string{
"secret",
"key",
}
}
func (s *Secretsmanager) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.secretsmanager.secret.sync", true) {
list, err := s.fetcher.Get("secret_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]secretsmanagertypes.SecretListEntry); !ok {
return gph, errors.New("cannot cast to '[]secretsmanagertypes.SecretListEntry' type from fetch context")
}
for _, r := range list.([]secretsmanagertypes.SecretListEntry) {
for _, fn := range addParentsFns["secret"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *secretsmanagertypes.SecretListEntry) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.secretsmanager.key.sync", true) {
list, err := s.fetcher.Get("key_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]kmstypes.KeyMetadata); !ok {
return gph, errors.New("cannot cast to '[]kmstypes.KeyMetadata' type from fetch context")
}
for _, r := range list.([]kmstypes.KeyMetadata) {
for _, fn := range addParentsFns["key"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *kmstypes.KeyMetadata) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Secretsmanager) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Secretsmanager) IsSyncDisabled() bool {
return !getBool(s.config, "aws.secretsmanager.sync", true)
}
type Apigateway struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
Apigatewayv2Client *apigatewayv2.Client
}
func NewApigateway(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
apigatewayv2Client := apigatewayv2.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
apigatewayv2Client,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Apigateway{
Apigatewayv2Client: apigatewayv2Client,
fetcher: fetch.NewFetcher(awsfetch.BuildApigatewayFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Apigateway) Name() string {
return "apigateway"
}
func (s *Apigateway) Region() string {
return s.region
}
func (s *Apigateway) Profile() string {
return s.profile
}
func (s *Apigateway) ResourceTypes() []string {
return []string{
"apigateway",
"apigatewayroute",
"apigatewaystage",
}
}
func (s *Apigateway) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.apigateway.apigateway.sync", true) {
list, err := s.fetcher.Get("apigateway_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]apigatewayv2types.Api); !ok {
return gph, errors.New("cannot cast to '[]apigatewayv2types.Api' type from fetch context")
}
for _, r := range list.([]apigatewayv2types.Api) {
for _, fn := range addParentsFns["apigateway"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *apigatewayv2types.Api) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.apigateway.apigatewayroute.sync", true) {
list, err := s.fetcher.Get("apigatewayroute_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]apigatewayv2types.Route); !ok {
return gph, errors.New("cannot cast to '[]apigatewayv2types.Route' type from fetch context")
}
for _, r := range list.([]apigatewayv2types.Route) {
for _, fn := range addParentsFns["apigatewayroute"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *apigatewayv2types.Route) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.apigateway.apigatewaystage.sync", true) {
list, err := s.fetcher.Get("apigatewaystage_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]apigatewayv2types.Stage); !ok {
return gph, errors.New("cannot cast to '[]apigatewayv2types.Stage' type from fetch context")
}
for _, r := range list.([]apigatewayv2types.Stage) {
for _, fn := range addParentsFns["apigatewaystage"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *apigatewayv2types.Stage) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Apigateway) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Apigateway) IsSyncDisabled() bool {
return !getBool(s.config, "aws.apigateway.sync", true)
}
type Ssm struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
SsmClient *ssm.Client
}
func NewSsm(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
ssmClient := ssm.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
ssmClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Ssm{
SsmClient: ssmClient,
fetcher: fetch.NewFetcher(awsfetch.BuildSsmFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Ssm) Name() string {
return "ssm"
}
func (s *Ssm) Region() string {
return s.region
}
func (s *Ssm) Profile() string {
return s.profile
}
func (s *Ssm) ResourceTypes() []string {
return []string{
"ssmparameter",
}
}
func (s *Ssm) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.ssm.ssmparameter.sync", true) {
list, err := s.fetcher.Get("ssmparameter_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]ssmtypes.ParameterMetadata); !ok {
return gph, errors.New("cannot cast to '[]ssmtypes.ParameterMetadata' type from fetch context")
}
for _, r := range list.([]ssmtypes.ParameterMetadata) {
for _, fn := range addParentsFns["ssmparameter"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *ssmtypes.ParameterMetadata) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Ssm) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Ssm) IsSyncDisabled() bool {
return !getBool(s.config, "aws.ssm.sync", true)
}
type Efs struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
EfsClient *efs.Client
}
func NewEfs(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
efsClient := efs.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
efsClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Efs{
EfsClient: efsClient,
fetcher: fetch.NewFetcher(awsfetch.BuildEfsFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Efs) Name() string {
return "efs"
}
func (s *Efs) Region() string {
return s.region
}
func (s *Efs) Profile() string {
return s.profile
}
func (s *Efs) ResourceTypes() []string {
return []string{
"filesystem",
"mounttarget",
}
}
func (s *Efs) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.efs.filesystem.sync", true) {
list, err := s.fetcher.Get("filesystem_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]efstypes.FileSystemDescription); !ok {
return gph, errors.New("cannot cast to '[]efstypes.FileSystemDescription' type from fetch context")
}
for _, r := range list.([]efstypes.FileSystemDescription) {
for _, fn := range addParentsFns["filesystem"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *efstypes.FileSystemDescription) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
if getBool(s.config, "aws.efs.mounttarget.sync", true) {
list, err := s.fetcher.Get("mounttarget_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]efstypes.MountTargetDescription); !ok {
return gph, errors.New("cannot cast to '[]efstypes.MountTargetDescription' type from fetch context")
}
for _, r := range list.([]efstypes.MountTargetDescription) {
for _, fn := range addParentsFns["mounttarget"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *efstypes.MountTargetDescription) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Efs) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Efs) IsSyncDisabled() bool {
return !getBool(s.config, "aws.efs.sync", true)
}
type Cloudtrail struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
CloudtrailClient *cloudtrail.Client
}
func NewCloudtrail(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
cloudtrailClient := cloudtrail.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
cloudtrailClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Cloudtrail{
CloudtrailClient: cloudtrailClient,
fetcher: fetch.NewFetcher(awsfetch.BuildCloudtrailFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Cloudtrail) Name() string {
return "cloudtrail"
}
func (s *Cloudtrail) Region() string {
return s.region
}
func (s *Cloudtrail) Profile() string {
return s.profile
}
func (s *Cloudtrail) ResourceTypes() []string {
return []string{
"trail",
}
}
func (s *Cloudtrail) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.cloudtrail.trail.sync", true) {
list, err := s.fetcher.Get("trail_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudtrailtypes.Trail); !ok {
return gph, errors.New("cannot cast to '[]cloudtrailtypes.Trail' type from fetch context")
}
for _, r := range list.([]cloudtrailtypes.Trail) {
for _, fn := range addParentsFns["trail"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudtrailtypes.Trail) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Cloudtrail) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Cloudtrail) IsSyncDisabled() bool {
return !getBool(s.config, "aws.cloudtrail.sync", true)
}
type Cloudwatchlogs struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
CloudwatchlogsClient *cloudwatchlogs.Client
}
func NewCloudwatchlogs(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
region := cfg.Region
cloudwatchlogsClient := cloudwatchlogs.NewFromConfig(cfg)
fetchConfig := awsfetch.NewConfig(
cloudwatchlogsClient,
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &Cloudwatchlogs{
CloudwatchlogsClient: cloudwatchlogsClient,
fetcher: fetch.NewFetcher(awsfetch.BuildCloudwatchlogsFetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *Cloudwatchlogs) Name() string {
return "cloudwatchlogs"
}
func (s *Cloudwatchlogs) Region() string {
return s.region
}
func (s *Cloudwatchlogs) Profile() string {
return s.profile
}
func (s *Cloudwatchlogs) ResourceTypes() []string {
return []string{
"loggroup",
}
}
func (s *Cloudwatchlogs) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
if getBool(s.config, "aws.cloudwatchlogs.loggroup.sync", true) {
list, err := s.fetcher.Get("loggroup_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]cloudwatchlogstypes.LogGroup); !ok {
return gph, errors.New("cannot cast to '[]cloudwatchlogstypes.LogGroup' type from fetch context")
}
for _, r := range list.([]cloudwatchlogstypes.LogGroup) {
for _, fn := range addParentsFns["loggroup"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *cloudwatchlogstypes.LogGroup) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *Cloudwatchlogs) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *Cloudwatchlogs) IsSyncDisabled() bool {
return !getBool(s.config, "aws.cloudwatchlogs.sync", true)
}
/*
Copyright 2017 WALLIX
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 awsservices
import (
"errors"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
var (
AccessService, InfraService, StorageService, MessagingService, DnsService, LambdaService, MonitoringService, CdnService, CloudformationService cloud.Service
EksService, DynamodbService, SecretsmanagerService, ApigatewayService, SsmService, EfsService, CloudtrailService, CloudwatchlogsService cloud.Service
)
func Init(profile, region string, extraConf map[string]interface{}, log *logger.Logger, profileSetterCallback func(val string) error, enableNetworkMonitor bool) error {
if region == "" {
return errors.New("empty AWS region. Set it with `awless config set aws.region`")
}
sb := newConfigResolver().withRegion(region).withProfile(profile).withNetworkMonitor(enableNetworkMonitor)
sb = sb.withProfileSetter(profileSetterCallback).withLogger(log).withCredentialResolvers()
cfg, err := sb.resolve()
if err != nil {
return err
}
AccessService = NewAccess(cfg, profile, extraConf, log)
InfraService = NewInfra(cfg, profile, extraConf, log)
StorageService = NewStorage(cfg, profile, extraConf, log)
MessagingService = NewMessaging(cfg, profile, extraConf, log)
DnsService = NewDns(cfg, profile, extraConf, log)
LambdaService = NewLambda(cfg, profile, extraConf, log)
MonitoringService = NewMonitoring(cfg, profile, extraConf, log)
CdnService = NewCdn(cfg, profile, extraConf, log)
CloudformationService = NewCloudformation(cfg, profile, extraConf, log)
EksService = NewEks(cfg, profile, extraConf, log)
DynamodbService = NewDynamodb(cfg, profile, extraConf, log)
SecretsmanagerService = NewSecretsmanager(cfg, profile, extraConf, log)
ApigatewayService = NewApigateway(cfg, profile, extraConf, log)
SsmService = NewSsm(cfg, profile, extraConf, log)
EfsService = NewEfs(cfg, profile, extraConf, log)
CloudtrailService = NewCloudtrail(cfg, profile, extraConf, log)
CloudwatchlogsService = NewCloudwatchlogs(cfg, profile, extraConf, log)
cloud.ServiceRegistry[InfraService.Name()] = InfraService
cloud.ServiceRegistry[AccessService.Name()] = AccessService
cloud.ServiceRegistry[StorageService.Name()] = StorageService
cloud.ServiceRegistry[MessagingService.Name()] = MessagingService
cloud.ServiceRegistry[DnsService.Name()] = DnsService
cloud.ServiceRegistry[LambdaService.Name()] = LambdaService
cloud.ServiceRegistry[MonitoringService.Name()] = MonitoringService
cloud.ServiceRegistry[CdnService.Name()] = CdnService
cloud.ServiceRegistry[CloudformationService.Name()] = CloudformationService
cloud.ServiceRegistry[EksService.Name()] = EksService
cloud.ServiceRegistry[DynamodbService.Name()] = DynamodbService
cloud.ServiceRegistry[SecretsmanagerService.Name()] = SecretsmanagerService
cloud.ServiceRegistry[ApigatewayService.Name()] = ApigatewayService
cloud.ServiceRegistry[SsmService.Name()] = SsmService
cloud.ServiceRegistry[EfsService.Name()] = EfsService
cloud.ServiceRegistry[CloudtrailService.Name()] = CloudtrailService
cloud.ServiceRegistry[CloudwatchlogsService.Name()] = CloudwatchlogsService
awsspec.CommandFactory = &awsspec.AWSFactory{
Log: log,
Cfg: cfg,
Graph: &cloud.LazyGraph{LoadingFunc: func() cloud.GraphAPI {
g, err := sync.LoadLocalGraphs(profile, region)
if err != nil {
g = graph.NewGraph()
}
return g
}},
}
return nil
}
func getBool(m map[string]interface{}, key string, def bool) bool {
if b, ok := m[key].(bool); ok {
return b
}
return def
}
/*
Copyright 2017 WALLIX
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 awsservices
import (
"context"
"regexp"
"strings"
"sync"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/wallix/awless/cloud"
)
func GetCloudServicesForAPIs(apis ...string) (services []cloud.Service) {
unique := make(map[string]struct{})
for _, api := range apis {
if name, ok := ServicePerAPI[api]; ok {
if service, exists := cloud.ServiceRegistry[name]; exists {
if _, done := unique[name]; !done {
unique[name] = struct{}{}
services = append(services, service)
}
}
}
}
return
}
func GetCloudServicesForTypes(types ...string) (services []cloud.Service) {
unique := make(map[string]struct{})
for _, typ := range types {
if name, ok := ServicePerResourceType[typ]; ok {
if service, exists := cloud.ServiceRegistry[name]; exists {
if _, done := unique[name]; !done {
unique[name] = struct{}{}
services = append(services, service)
}
}
}
}
return
}
func ResourceTypesPerServiceName() map[string][]string {
out := make(map[string][]string)
for rT, s := range ServicePerResourceType {
out[s] = append(out[s], rT)
}
return out
}
var arnResourceInfoRegex = regexp.MustCompile(`(root)|([\w-.]*)/([\w-./]*)`)
type Identity struct {
Account, Arn, UserId, ResourceType, ResourcePath, Resource string
}
func (i *Identity) IsRoot() bool {
return i.Resource == "root"
}
func (i *Identity) IsUserType() bool {
return i.ResourceType == "user"
}
func (s *Access) GetIdentity() (*Identity, error) {
resp, err := s.StsClient.GetCallerIdentity(context.Background(), &sts.GetCallerIdentityInput{})
if err != nil {
return nil, err
}
ident := &Identity{
Account: aws.ToString(resp.Account),
Arn: aws.ToString(resp.Arn),
UserId: aws.ToString(resp.UserId),
}
splits := strings.Split(ident.Arn, ":")
if l := len(splits); l > 0 {
ident.ResourcePath = splits[l-1]
matches := arnResourceInfoRegex.FindStringSubmatch(ident.ResourcePath)
if len(matches) == 4 {
if matches[1] == "root" {
ident.Resource = "root"
ident.ResourceType = "user"
} else {
ident.ResourceType = matches[2]
ident.Resource = matches[3]
}
}
}
return ident, nil
}
type UserPolicies struct {
Username string
Inlined []string
Attached []string
ByGroup map[string][]string
}
func (s *Access) GetUserPolicies(username string) (*UserPolicies, error) {
var wg sync.WaitGroup
all := &UserPolicies{
Username: username,
ByGroup: make(map[string][]string),
}
errc := make(chan error, 4)
wg.Add(1)
go func() {
defer wg.Done()
policies, err := s.IamClient.ListUserPolicies(context.Background(), &iam.ListUserPoliciesInput{
UserName: aws.String(username),
})
if err != nil {
errc <- err
return
}
all.Inlined = append(all.Inlined, policies.PolicyNames...)
}()
wg.Add(1)
go func() {
defer wg.Done()
attached, err := s.IamClient.ListAttachedUserPolicies(context.Background(), &iam.ListAttachedUserPoliciesInput{
UserName: aws.String(username),
})
if err != nil {
errc <- err
return
}
for _, pol := range attached.AttachedPolicies {
all.Attached = append(all.Attached, aws.ToString(pol.PolicyName))
}
}()
wg.Add(1)
go func() {
defer wg.Done()
groups, err := s.IamClient.ListGroupsForUser(context.Background(), &iam.ListGroupsForUserInput{
UserName: aws.String(username),
})
if err != nil {
errc <- err
return
}
type result struct {
group, policy string
}
resultC := make(chan result)
var wgg sync.WaitGroup
for _, group := range groups.Groups {
wgg.Add(1)
go func(name string) {
defer wgg.Done()
output, err := s.IamClient.ListAttachedGroupPolicies(context.Background(), &iam.ListAttachedGroupPoliciesInput{
GroupName: aws.String(name),
})
if err != nil {
errc <- err
return
}
for _, pol := range output.AttachedPolicies {
resultC <- result{group: name, policy: aws.ToString(pol.PolicyName)}
}
}(aws.ToString(group.GroupName))
}
go func() {
wgg.Wait()
close(resultC)
}()
for res := range resultC {
all.ByGroup[res.group] = append(all.ByGroup[res.group], res.policy)
}
}()
go func() {
wg.Wait()
close(errc)
}()
for e := range errc {
return all, e
}
return all, nil
}
package awsservices
import (
"fmt"
"io"
"os"
"sort"
"strings"
"sync"
"time"
"github.com/wallix/awless/console"
)
var DefaultNetworkMonitor = &NetworkMonitor{requests: make(map[string]*req)}
type NetworkMonitor struct {
requests map[string]*req
l sync.Mutex
}
type req struct {
name string
from time.Time
to time.Time
retries []time.Time
}
func (n *NetworkMonitor) DisplayStats(w io.Writer) {
fmt.Fprintf(w, "\n%d requests sent:\n", len(n.requests))
var sorted []*req
var min, max time.Time
var maxFunctionNameLength int
for _, r := range n.requests {
if min.IsZero() || r.from.Before(min) {
min = r.from
}
if max.IsZero() || r.to.After(max) {
max = r.to
}
if len(r.name) > maxFunctionNameLength {
maxFunctionNameLength = len(r.name)
}
sorted = append(sorted, r)
}
sort.Slice(sorted, func(i int, j int) bool {
if sorted[i].from.Equal(sorted[j].from) {
return sorted[i].to.Before(sorted[j].to)
}
return sorted[i].from.Before(sorted[j].from)
})
maxwidth := uint(console.GetTerminalWidth() - maxFunctionNameLength - 11) // 11 = '['+']'+' '+'('+4+'m'+'s'+')'
maxDuration := max.Sub(min)
for _, r := range sorted {
if len(r.retries) > 0 {
drawRequest(w, r.name, min, r.from, r.retries[0], maxwidth, maxDuration, "[", "X")
for i := 0; i < len(r.retries)-1; i++ {
drawRequest(w, r.name, min, r.retries[i], r.retries[i+1], maxwidth, maxDuration, "o", "X")
}
drawRequest(w, r.name, min, r.retries[len(r.retries)-1], r.to, maxwidth, maxDuration, "o", "]")
} else {
drawRequest(w, r.name, min, r.from, r.to, maxwidth, maxDuration, "[", "]")
}
}
}
func drawRequest(w io.Writer, name string, min, from, to time.Time, maxwidth uint, maxduration time.Duration, startChar, stopChar string) {
duration := to.Sub(from)
width := uint(duration) * maxwidth / uint(maxduration)
before := uint(from.Sub(min)) * maxwidth / uint(maxduration)
after := maxwidth - width - before
fmt.Fprintf(w, "%s%s%s%s%s %s(%dms)\n", strings.Repeat(" ", int(before)), startChar, strings.Repeat("-", int(width)), stopChar, strings.Repeat(" ", int(after)), name, duration/(1000*1000))
}
func (n *NetworkMonitor) AddRequest(name string) {
n.l.Lock()
defer n.l.Unlock()
if r, ok := n.requests[name]; ok {
r.retries = append(r.retries, time.Now().UTC())
} else {
n.requests[name] = &req{name: name, from: time.Now().UTC()}
}
}
func (n *NetworkMonitor) SetRequestEnd(name string) {
n.l.Lock()
defer n.l.Unlock()
r, ok := n.requests[name]
if !ok {
fmt.Fprintf(os.Stderr, "request '%s' not found\n", name)
return
}
r.to = time.Now().UTC()
}
/*
Copyright 2017 WALLIX
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 awsservices
import (
"context"
"errors"
"fmt"
"os"
"reflect"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
tstore "github.com/wallix/triplestore"
awsconv "github.com/wallix/awless/aws/conv"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
)
const (
PARENT_OF = iota // default
APPLIES_ON
DEPENDING_ON
)
type funcBuilder struct {
parent string
fieldName, listName, stringListName string
relation int
}
type addParentFn func(*graph.Graph, tstore.RDFGraph, string, interface{}) error
var addParentsFns = map[string][]addParentFn{
// Infra
cloud.Subnet: {
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
},
cloud.Instance: {
funcBuilder{parent: cloud.Subnet, fieldName: "SubnetId"}.build(),
funcBuilder{parent: cloud.SecurityGroup, fieldName: "GroupId", listName: "SecurityGroups", relation: APPLIES_ON}.build(),
funcBuilder{parent: cloud.Keypair, fieldName: "KeyName", relation: APPLIES_ON}.build(),
},
cloud.SecurityGroup: {
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
},
cloud.InternetGateway: {
addRegionParent,
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId", listName: "Attachments", relation: DEPENDING_ON}.build(),
},
cloud.NatGateway: {
addRegionParent,
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
funcBuilder{parent: cloud.Subnet, fieldName: "SubnetId", relation: DEPENDING_ON}.build(),
},
cloud.RouteTable: {
funcBuilder{parent: cloud.Subnet, fieldName: "SubnetId", listName: "Associations", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
},
cloud.Volume: {
funcBuilder{parent: cloud.AvailabilityZone, fieldName: "AvailabilityZone"}.build(),
funcBuilder{parent: cloud.Instance, fieldName: "InstanceId", listName: "Attachments", relation: DEPENDING_ON}.build(),
},
cloud.ElasticIP: {
addRegionParent,
funcBuilder{parent: cloud.Instance, fieldName: "InstanceId", relation: DEPENDING_ON}.build(),
},
cloud.Snapshot: {
addRegionParent,
funcBuilder{parent: cloud.Volume, fieldName: "VolumeId", relation: DEPENDING_ON}.build(),
},
cloud.NetworkInterface: {
funcBuilder{parent: cloud.Subnet, fieldName: "SubnetId", relation: PARENT_OF}.build(),
funcBuilder{parent: cloud.SecurityGroup, fieldName: "GroupId", listName: "Groups", relation: APPLIES_ON}.build(),
funcBuilder{parent: cloud.Instance, fieldName: "Attachment.InstanceId", relation: DEPENDING_ON}.build(),
},
// Loadbalancer
cloud.LoadBalancer: {
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
funcBuilder{parent: cloud.Subnet, fieldName: "SubnetId", listName: "AvailabilityZones", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.AvailabilityZone, fieldName: "ZoneName", listName: "AvailabilityZones", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.SecurityGroup, stringListName: "SecurityGroups", relation: APPLIES_ON}.build(),
},
cloud.ClassicLoadBalancer: {
funcBuilder{parent: cloud.Vpc, fieldName: "VPCId"}.build(),
funcBuilder{parent: cloud.Subnet, stringListName: "Subnets", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.AvailabilityZone, stringListName: "AvailabilityZones", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.SecurityGroup, stringListName: "SecurityGroups", relation: APPLIES_ON}.build(),
},
cloud.Listener: {
funcBuilder{parent: cloud.LoadBalancer, fieldName: "LoadBalancerArn"}.build(),
},
cloud.TargetGroup: {
funcBuilder{parent: cloud.Vpc, fieldName: "VpcId"}.build(),
funcBuilder{parent: cloud.LoadBalancer, stringListName: "LoadBalancerArns", relation: APPLIES_ON}.build(),
fetchTargetsAndAddRelations,
},
// Database
cloud.Database: {
funcBuilder{parent: cloud.AvailabilityZone, fieldName: "AvailabilityZone"}.build(),
funcBuilder{parent: cloud.SecurityGroup, listName: "VpcSecurityGroups", fieldName: "VpcSecurityGroupId", relation: APPLIES_ON}.build(),
},
// Autoscaling
cloud.LaunchConfiguration: {
addRegionParent,
funcBuilder{parent: cloud.Keypair, fieldName: "KeyName", relation: APPLIES_ON}.build(),
},
cloud.ScalingGroup: {
addRegionParent,
funcBuilder{parent: cloud.AvailabilityZone, stringListName: "AvailabilityZones", relation: APPLIES_ON}.build(),
funcBuilder{parent: cloud.Instance, fieldName: "InstanceId", listName: "Instances", relation: DEPENDING_ON}.build(),
funcBuilder{parent: cloud.TargetGroup, stringListName: "TargetGroupARNs", relation: DEPENDING_ON}.build(),
addScalingGroupSubnets,
},
// Container
cloud.ContainerInstance: {
funcBuilder{parent: cloud.Instance, fieldName: "Ec2InstanceId", relation: APPLIES_ON}.build(),
},
cloud.Subscription: {
funcBuilder{parent: cloud.Topic, fieldName: "TopicArn"}.build(),
},
cloud.Vpc: {addRegionParent},
cloud.AvailabilityZone: {addRegionParent},
cloud.Keypair: {addRegionParent},
cloud.Image: {addRegionParent},
cloud.Repository: {addRegionParent},
cloud.ContainerCluster: {addRegionParent},
cloud.ContainerTask: {addRegionParent},
cloud.Certificate: {addRegionParent},
cloud.User: {userAddGroupsRelations, addManagedPoliciesRelations},
cloud.Role: {addManagedPoliciesRelations},
cloud.Group: {addManagedPoliciesRelations},
cloud.Bucket: {addRegionParent},
cloud.Function: {addRegionParent},
cloud.Topic: {addRegionParent},
cloud.Alarm: {addRegionParent, addAlarmMetric},
cloud.Metric: {addRegionParent},
cloud.Stack: {addRegionParent},
cloud.MFADevice: {
funcBuilder{parent: cloud.User, fieldName: "User.UserId", relation: DEPENDING_ON}.build(),
},
}
func (fb funcBuilder) build() addParentFn {
switch {
case fb.listName != "":
return fb.addRelationListWithField()
case fb.stringListName != "":
return fb.addRelationListWithStringField()
default:
return fb.addRelationWithField()
}
}
func (fb funcBuilder) addRelationWithField() addParentFn {
return func(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
val, err := valueAtPath(i, fb.fieldName)
if err != nil {
return err
}
if val == nil {
return nil
}
var strVal string
switch s := val.(type) {
case *string:
strVal = aws.ToString(s)
case string:
strVal = s
default:
return fmt.Errorf("add parent to %s: %T not a string", fb.fieldName, val)
}
if strVal == "" {
return nil
}
res, err := awsconv.InitResource(i)
if err != nil {
return err
}
parent := graph.InitResource(fb.parent, strVal)
return addRelation(g, parent, res, fb.relation)
}
}
func (fb funcBuilder) addRelationListWithStringField() addParentFn {
return func(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
structField, err := verifyValidStructField(i, fb.stringListName)
if err != nil {
return err
}
res, err := awsconv.InitResource(i)
if err != nil {
return err
}
if !structField.IsValid() || structField.Kind() != reflect.Slice {
return fmt.Errorf("add parent to %s: field not a slice: %T", res.Id(), structField.Kind())
}
for i := 0; i < structField.Len(); i++ {
elem := structField.Index(i).Interface()
var strVal string
switch s := elem.(type) {
case *string:
strVal = aws.ToString(s)
case string:
strVal = s
default:
return fmt.Errorf("add parent to %s: not a string: %T", res.Id(), elem)
}
if strVal == "" {
continue
}
parent := graph.InitResource(fb.parent, strVal)
if err = addRelation(g, parent, res, fb.relation); err != nil {
return err
}
}
return nil
}
}
func (fb funcBuilder) addRelationListWithField() addParentFn {
return func(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
structField, err := verifyValidStructField(i, fb.listName)
if err != nil {
return err
}
res, err := awsconv.InitResource(i)
if err != nil {
return err
}
if !structField.IsValid() || structField.Kind() != reflect.Slice {
return fmt.Errorf("add parent to %s: field not a slice: %T", res.Id(), structField.Kind())
}
for i := 0; i < structField.Len(); i++ {
listValue := structField.Index(i)
var listStruc reflect.Value
switch listValue.Kind() {
case reflect.Ptr:
listStruc = listValue.Elem()
case reflect.Struct:
listStruc = listValue
default:
return fmt.Errorf("add parent to %s: not a struct or pointer: %s", res.Id(), listValue.Kind())
}
if listStruc.Kind() != reflect.Struct {
return fmt.Errorf("add parent to %s: not a struct: %s", res.Id(), listStruc.Kind())
}
listStructField := listStruc.FieldByName(fb.fieldName)
if !listStructField.IsValid() {
return fmt.Errorf("add parent to %s: unknown field %s in %d", res.Id(), listStructField, i)
}
var strVal string
switch s := listStructField.Interface().(type) {
case *string:
strVal = aws.ToString(s)
case string:
strVal = s
default:
return fmt.Errorf("add parent to %s: %T is not a string", listStructField, listStructField.Interface())
}
if strVal == "" {
continue
}
parent := graph.InitResource(fb.parent, strVal)
if err = addRelation(g, parent, res, fb.relation); err != nil {
return err
}
}
return nil
}
}
func verifyValidStructField(i interface{}, name string) (reflect.Value, error) {
value := reflect.ValueOf(i)
if value.Kind() != reflect.Ptr {
return reflect.Value{}, fmt.Errorf("%T not a pointer", i)
}
struc := value.Elem()
if struc.Kind() != reflect.Struct {
return reflect.Value{}, fmt.Errorf("%T not a stuct pointer", i)
}
structField := struc.FieldByName(name)
if !structField.IsValid() {
return reflect.Value{}, fmt.Errorf("invalid field %s: ", name)
}
return structField, nil
}
func addRelation(g *graph.Graph, first, other *graph.Resource, relation int) error {
switch relation {
case PARENT_OF:
g.AddParentRelation(first, other)
case APPLIES_ON:
g.AddAppliesOnRelation(first, other)
case DEPENDING_ON:
g.AddAppliesOnRelation(other, first)
default:
return errors.New("unknown relation type")
}
return nil
}
func addRegionParent(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
res, err := awsconv.InitResource(i)
if err != nil {
return err
}
g.AddParentRelation(graph.InitResource(cloud.Region, region), res)
return nil
}
func addManagedPoliciesRelations(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
res, err := awsconv.InitResource(i)
if err != nil {
return err
}
value := reflect.ValueOf(i)
if value.Kind() != reflect.Ptr {
return fmt.Errorf("add parent to %s: unknown type %T", res.Id(), i)
}
struc := value.Elem()
if struc.Kind() != reflect.Struct {
return fmt.Errorf("add parent to %s: unknown type %T", res.Id(), i)
}
structField := struc.FieldByName("AttachedManagedPolicies")
if !structField.IsValid() {
return fmt.Errorf("add parent to %s: unknown field %s in %d", res.Id(), structField, i)
}
policies, ok := structField.Interface().([]iamtypes.AttachedPolicy)
if !ok {
return fmt.Errorf("add parent to %s: not a valid attached policy list: %T", res.Id(), structField.Interface())
}
for _, policy := range policies {
policies, err := graph.ResolveResourcesWithProp(snap, cloud.Policy, "Name", aws.ToString(policy.PolicyName))
if err != nil {
return err
}
if len(policies) != 1 {
fmt.Fprintf(os.Stderr, "add parent to '%s/%s': unknown policy named '%s'. Ignoring it.\n", res.Type(), res.Id(), aws.ToString(policy.PolicyName))
return nil
}
g.AddAppliesOnRelation(policies[0], res)
}
return nil
}
func userAddGroupsRelations(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
user, ok := i.(*iamtypes.UserDetail)
if !ok {
return fmt.Errorf("aws fetch: not a user, but a %T", i)
}
n, err := awsconv.InitResource(user)
if err != nil {
return err
}
for _, group := range user.GroupList {
groupName := group
resources, err := graph.ResolveResourcesWithProp(snap, cloud.Group, "Name", groupName)
if err != nil {
return err
}
switch len(resources) {
case 0:
fmt.Fprintf(os.Stderr, "no group with name %s found for user %s\n", groupName, n.Id())
case 1:
g.AddAppliesOnRelation(resources[0], n)
default:
fmt.Fprintf(os.Stderr, "multiple groups with name %s found for user %s:%v\n", groupName, n.Id(), resources)
}
}
return nil
}
func fetchTargetsAndAddRelations(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
group, ok := i.(*elbv2types.TargetGroup)
if !ok {
return fmt.Errorf("add targets relation: not a target group, but a %T", i)
}
parent, err := awsconv.InitResource(group)
if err != nil {
return err
}
targets, err := InfraService.(*Infra).Elbv2Client.DescribeTargetHealth(context.Background(), &elbv2.DescribeTargetHealthInput{TargetGroupArn: group.TargetGroupArn})
if err != nil {
return err
}
for _, t := range targets.TargetHealthDescriptions {
n := graph.InitResource(cloud.Instance, aws.ToString(t.Target.Id))
err = g.AddAppliesOnRelation(parent, n)
if err != nil {
return err
}
}
return nil
}
func addScalingGroupSubnets(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
group, ok := i.(*autoscalingtypes.AutoScalingGroup)
if !ok {
return fmt.Errorf("add autoscaling group relation: not a autoscaling group, but a %T", i)
}
parent, err := awsconv.InitResource(group)
if err != nil {
return err
}
if subnets := aws.ToString(group.VPCZoneIdentifier); subnets != "" {
splits := strings.Split(subnets, ",")
for _, split := range splits {
n := graph.InitResource(cloud.Subnet, split)
err = g.AddAppliesOnRelation(parent, n)
if err != nil {
return err
}
}
}
return nil
}
// valueAtPath navigates a dot-separated field path through struct fields,
// returning the value found. Replaces awsutil.ValuesAtPath from SDK v1.
func valueAtPath(i interface{}, path string) (interface{}, error) {
parts := strings.Split(path, ".")
v := reflect.ValueOf(i)
for _, part := range parts {
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
if v.IsNil() {
return nil, nil
}
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected struct at path segment '%s', got %s", part, v.Kind())
}
v = v.FieldByName(part)
if !v.IsValid() {
return nil, fmt.Errorf("field '%s' not found", part)
}
}
return v.Interface(), nil
}
func addAlarmMetric(g *graph.Graph, snap tstore.RDFGraph, region string, i interface{}) error {
alarm, ok := i.(*cloudwatchtypes.MetricAlarm)
if !ok {
return fmt.Errorf("add alarm metric relation: not a alarm, but a %T", i)
}
parent, err := awsconv.InitResource(alarm)
if err != nil {
return err
}
if namespace, metric := aws.ToString(alarm.Namespace), aws.ToString(alarm.MetricName); namespace != "" && metric != "" {
id := awsconv.HashFields(namespace, metric)
n := graph.InitResource(cloud.Metric, id)
err = g.AddAppliesOnRelation(parent, n)
if err != nil {
return err
}
}
return nil
}
/*
Copyright 2017 WALLIX
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 awsservices
import (
"context"
"fmt"
"net/http"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
awsconfig "github.com/wallix/awless/aws/config"
"github.com/wallix/awless/logger"
)
func ResolveRegionFromEnv() (region string) {
cfg, err := newConfigResolver().resolve()
if err == nil {
region = cfg.Region
}
if awsconfig.IsValidRegion(region) {
fmt.Fprintf(os.Stderr, "Found existing AWS region '%s'. Setting it as your default region.\n", region)
} else {
client := imds.NewFromConfig(cfg)
output, imdsErr := client.GetRegion(context.Background(), &imds.GetRegionInput{})
if imdsErr == nil {
fmt.Fprintf(os.Stderr, "Found AWS region '%s' from local EC2 instance metadata. Setting it as your default region.\n", output.Region)
region = output.Region
}
}
if !awsconfig.IsValidRegion(region) {
region = awsconfig.StdinRegionSelector()
fmt.Println()
}
return
}
type configResolver struct {
region, profile string
profileSetterCallback func(val string) error
httpClient *http.Client
credentialHTTPClient *http.Client
logger *logger.Logger
enableCredentialResolvers bool
}
func newConfigResolver() *configResolver {
return &configResolver{
credentialHTTPClient: &http.Client{Timeout: 1 * time.Second},
httpClient: http.DefaultClient,
profileSetterCallback: func(val string) error { return nil },
logger: logger.DiscardLogger,
}
}
func (s *configResolver) withRegion(region string) *configResolver {
s.region = region
return s
}
func (s *configResolver) withProfile(profile string) *configResolver {
s.profile = profile
return s
}
func (s *configResolver) withCredentialResolvers() *configResolver {
s.enableCredentialResolvers = true
return s
}
func (s *configResolver) withProfileSetter(f func(val string) error) *configResolver {
s.profileSetterCallback = f
return s
}
func (s *configResolver) withLogger(l *logger.Logger) *configResolver {
s.logger = l
return s
}
func (s *configResolver) withNetworkMonitor(enableNetworkMonitor bool) *configResolver {
// Network monitor request handlers are not supported in SDK v2.
// This method is retained for API compatibility.
return s
}
func (s *configResolver) resolve() (aws.Config, error) {
var opts []func(*config.LoadOptions) error
if s.region != "" {
opts = append(opts, config.WithRegion(s.region))
}
if s.profile != "" {
opts = append(opts, config.WithSharedConfigProfile(s.profile))
}
opts = append(opts, config.WithHTTPClient(s.httpClient))
cfg, err := config.LoadDefaultConfig(context.Background(), opts...)
if err != nil {
return aws.Config{}, err
}
if s.enableCredentialResolvers {
// TODO: Migrate fileCacheProvider and credentialsPrompterProvider
// to implement aws.CredentialsProvider (v2) instead of the v1
// credentials.Provider interface, then wire them back in here.
//
// For now, rely on the default credential chain from
// config.LoadDefaultConfig which covers environment variables,
// shared credentials/config files, and IAM roles.
if _, err = cfg.Credentials.Retrieve(context.Background()); err != nil {
return cfg, err
}
}
return cfg, nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/wallix/awless/cloud/match"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
awsconfig "github.com/wallix/awless/aws/config"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/logger"
)
type CreateAccesskey struct {
_ string `action:"create" entity:"accesskey" awsAPI:"iam" awsCall:"CreateAccessKey" awsInput:"iam.CreateAccessKeyInput" awsOutput:"iam.CreateAccessKeyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
Save *bool `templateName:"save"`
}
func (cmd *CreateAccesskey) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(params.Key("user"),
params.Opt("save", "no-prompt"),
))
builder.AddReducer(
func(values map[string]interface{}) (map[string]interface{}, error) {
if noPrompt, hasNoPrompt := values["no-prompt"]; hasNoPrompt {
b, err := castBool(noPrompt)
if err != nil {
return nil, fmt.Errorf("no-prompt: %s", err)
}
return map[string]interface{}{"save": !b}, nil
} else {
return nil, nil
}
},
"no-prompt",
)
return builder.Done()
}
func (cmd *CreateAccesskey) AfterRun(renv env.Running, output interface{}) error {
accessKey := output.(*iam.CreateAccessKeyOutput).AccessKey
if !BoolValue(cmd.Save) {
cmd.logger.Infof("Access key created. Here are the crendentials for user %s:", aws.ToString(accessKey.UserName))
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, strings.Repeat("*", 64))
fmt.Fprintf(os.Stderr, "aws_access_key_id = %s\n", aws.ToString(accessKey.AccessKeyId))
fmt.Fprintf(os.Stderr, "aws_secret_access_key = %s\n", aws.ToString(accessKey.SecretAccessKey))
fmt.Fprintln(os.Stderr, strings.Repeat("*", 64))
fmt.Fprintln(os.Stderr)
cmd.logger.Warning("This is your only opportunity to view the secret access keys.")
cmd.logger.Warning("Save the user's new access key ID and secret access key in a safe and secure place.")
cmd.logger.Warning("You will not have access to the secret keys again after this step.\n")
}
if cmd.Save != nil && !BoolValue(cmd.Save) {
return nil
}
profile := StringValue(cmd.User)
if !BoolValue(cmd.Save) {
if !promptConfirm("Do you want to save these access keys in %s?", AWSCredFilepath) {
return nil
}
profile = promptStringWithDefault("Entry profile name: ("+StringValue(cmd.User)+") ", profile)
}
creds := NewCredsPrompter(profile)
creds.Val.AccessKeyID = aws.ToString(accessKey.AccessKeyId)
creds.Val.SecretAccessKey = aws.ToString(accessKey.SecretAccessKey)
created, err := creds.Store()
if err != nil {
logger.Errorf("cannot store access keys: %s", err)
} else {
if created {
fmt.Fprintf(os.Stderr, "\n\u2713 %s created", AWSCredFilepath)
}
fmt.Fprintf(os.Stderr, "\n\u2713 Credentials for profile '%s' stored successfully in %s\n\n", creds.Profile, AWSCredFilepath)
}
return nil
}
func (cmd *CreateAccesskey) ExtractResult(i interface{}) string {
return StringValue(i.(*iam.CreateAccessKeyOutput).AccessKey.AccessKeyId)
}
type DeleteAccesskey struct {
_ string `action:"delete" entity:"accesskey" awsAPI:"iam" awsCall:"DeleteAccessKey" awsInput:"iam.DeleteAccessKeyInput" awsOutput:"iam.DeleteAccessKeyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Id *string `awsName:"AccessKeyId" awsType:"awsstr" templateName:"id"`
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
}
func (cmd *DeleteAccesskey) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AtLeastOneOf(params.Key("id"), params.Key("user")))
builder.AddReducer(
func(values map[string]interface{}) (map[string]interface{}, error) {
user, hasUser := values["user"].(string)
id, hasId := values["id"].(string)
if !hasUser && hasId {
r, err := cmd.graph.FindOne(cloud.NewQuery(cloud.AccessKey).Match(match.Property(properties.ID, id)))
if err != nil || r == nil {
return values, nil
}
if keyUser, ok := r.Property(properties.Username); ok {
values["user"] = keyUser
}
} else if hasUser && !hasId {
keys, err := cmd.api.ListAccessKeys(context.Background(), &iam.ListAccessKeysInput{
UserName: String(user),
})
if err != nil {
return values, fmt.Errorf("can not find access key for %s: %s", user, err)
}
switch len(keys.AccessKeyMetadata) {
case 0:
return values, fmt.Errorf("no access key found for %s:", user)
case 1:
values["id"] = StringValue(keys.AccessKeyMetadata[0].AccessKeyId)
default:
var keysStr []string
for _, k := range keys.AccessKeyMetadata {
keysStr = append(keysStr, fmt.Sprintf("%s (created on %s)", StringValue(k.AccessKeyId), aws.ToTime(k.CreateDate).Format("2006/01/02 15:04:05")))
}
return values, fmt.Errorf("multiple access keys found for %s: %s", user, strings.Join(keysStr, ", "))
}
}
return values, nil
},
"user", "id",
)
return builder.Done()
}
var (
AWSCredFilepath = filepath.Join(awsconfig.AWSHomeDir(), "credentials")
)
type credentialsPrompter struct {
Profile string
Val aws.Credentials
ProfileSetterCallback func(string) error
}
func NewCredsPrompter(profile string) *credentialsPrompter {
return &credentialsPrompter{Profile: profile, ProfileSetterCallback: func(string) error { return nil }}
}
func (c *credentialsPrompter) Prompt() error {
token := "and choose a profile name"
if c.HasProfile() {
token = fmt.Sprintf("for profile '%s'", c.Profile)
}
fmt.Printf("\nPlease enter access keys %s (stored at %s):\n", token, AWSCredFilepath)
promptUntilNonEmpty("AWS Access Key ID? ", &c.Val.AccessKeyID)
promptUntilNonEmpty("AWS Secret Access Key? ", &c.Val.SecretAccessKey)
if c.HasProfile() {
promptToOverride(fmt.Sprintf("Change your profile name (or just press Enter to keep '%s')? ", c.Profile), &c.Profile)
} else {
c.Profile = "default"
promptToOverride("Choose a profile name (or just press Enter to have AWS 'default')? ", &c.Profile)
}
if c.ProfileSetterCallback != nil {
c.ProfileSetterCallback(c.Profile)
}
return nil
}
var accessKeysRegex = regexp.MustCompile("^[a-zA-Z0-9/+=]{20,60}$")
func (c *credentialsPrompter) Store() (bool, error) {
var created bool
if c.Val.SecretAccessKey == "" {
return created, errors.New("given empty secret access key")
}
if !accessKeysRegex.MatchString(c.Val.SecretAccessKey) {
return created, errors.New("given invalid secret access key")
}
if c.Val.AccessKeyID == "" {
return created, errors.New("given empty access key")
}
if !accessKeysRegex.MatchString(c.Val.AccessKeyID) {
return created, errors.New("given invalid access key")
}
return appendToAwsFile(
fmt.Sprintf("\n[%s]\naws_access_key_id = %s\naws_secret_access_key = %s\n", c.Profile, c.Val.AccessKeyID, c.Val.SecretAccessKey),
AWSCredFilepath,
)
}
func appendToAwsFile(content string, awsFilePath string) (bool, error) {
var created bool
if awsHomeDirMissing() {
if err := os.MkdirAll(awsconfig.AWSHomeDir(), 0700); err != nil {
return created, fmt.Errorf("creating '%s' : %s", awsconfig.AWSHomeDir(), err)
}
created = true
}
f, err := os.OpenFile(awsFilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return created, fmt.Errorf("appending to '%s': %s", awsFilePath, err)
}
if _, err := fmt.Fprintf(f, "%s", content); err != nil {
return created, err
}
return created, nil
}
func promptConfirm(msg string, a ...interface{}) bool {
var yesorno string
fmt.Fprintf(os.Stderr, "%s [y/N] ", fmt.Sprintf(msg, a...))
fmt.Scanln(&yesorno)
if y := strings.TrimSpace(strings.ToLower(yesorno)); y == "y" || y == "yes" {
return true
}
return false
}
func (c *credentialsPrompter) HasProfile() bool {
return strings.TrimSpace(c.Profile) != ""
}
func promptToOverride(question string, v *string) {
fmt.Print(question)
var override string
fmt.Scanln(&override)
if strings.TrimSpace(override) != "" {
*v = override
return
}
}
func promptUntilNonEmpty(question string, v *string) {
ask := func(v *string) bool {
fmt.Print(question)
_, err := fmt.Scanln(v)
if err == nil && strings.TrimSpace(*v) != "" {
return false
}
if err != nil {
fmt.Printf("Error: %s. Retry please...\n", err)
}
return true
}
for ask(v) {
}
}
func awsHomeDirMissing() bool {
_, err := os.Stat(awsconfig.AWSHomeDir())
return os.IsNotExist(err)
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/wallix/awless/logger"
)
type CreateAlarm struct {
_ string `action:"create" entity:"alarm" awsAPI:"cloudwatch" awsCall:"PutMetricAlarm" awsInput:"cloudwatch.PutMetricAlarmInput" awsOutput:"cloudwatch.PutMetricAlarmOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Name *string `awsName:"AlarmName" awsType:"awsstr" templateName:"name"`
Operator *string `awsName:"ComparisonOperator" awsType:"awsstr" templateName:"operator"`
Metric *string `awsName:"MetricName" awsType:"awsstr" templateName:"metric"`
Namespace *string `awsName:"Namespace" awsType:"awsstr" templateName:"namespace"`
EvaluationPeriods *int64 `awsName:"EvaluationPeriods" awsType:"awsint64" templateName:"evaluation-periods"`
Period *int64 `awsName:"Period" awsType:"awsint64" templateName:"period"`
StatisticFunction *string `awsName:"Statistic" awsType:"awsstr" templateName:"statistic-function"`
Threshold *float64 `awsName:"Threshold" awsType:"awsfloat" templateName:"threshold"`
Enabled *bool `awsName:"ActionsEnabled" awsType:"awsbool" templateName:"enabled"`
AlarmActions []*string `awsName:"AlarmActions" awsType:"awsstringslice" templateName:"alarm-actions"`
InsufficientdataActions []*string `awsName:"InsufficientDataActions" awsType:"awsstringslice" templateName:"insufficientdata-actions"`
OkActions []*string `awsName:"OKActions" awsType:"awsstringslice" templateName:"ok-actions"`
Description *string `awsName:"AlarmDescription" awsType:"awsstr" templateName:"description"`
Dimensions []*string `awsName:"Dimensions" awsType:"awsdimensionslice" templateName:"dimensions"`
Unit *string `awsName:"Unit" awsType:"awsstr" templateName:"unit"`
}
func (cmd *CreateAlarm) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("evaluation-periods"), params.Key("metric"), params.Key("name"), params.Key("namespace"), params.Key("operator"), params.Key("period"), params.Key("statistic-function"), params.Key("threshold"),
params.Opt("alarm-actions", "description", "dimensions", "enabled", "insufficientdata-actions", "ok-actions", "unit"),
),
params.Validators{
"operator": params.IsInEnumIgnoreCase("GreaterThanThreshold", "LessThanThreshold", "LessThanOrEqualToThreshold", "GreaterThanOrEqualToThreshold"),
})
}
func (cmd *CreateAlarm) ExtractResult(i interface{}) string {
return StringValue(cmd.Name)
}
type DeleteAlarm struct {
_ string `action:"delete" entity:"alarm" awsAPI:"cloudwatch" awsCall:"DeleteAlarms" awsInput:"cloudwatch.DeleteAlarmsInput" awsOutput:"cloudwatch.DeleteAlarmsOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Name []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"name"`
}
func (cmd *DeleteAlarm) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
type StartAlarm struct {
_ string `action:"start" entity:"alarm" awsAPI:"cloudwatch" awsCall:"EnableAlarmActions" awsInput:"cloudwatch.EnableAlarmActionsInput" awsOutput:"cloudwatch.EnableAlarmActionsOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Names []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"names"`
}
func (cmd *StartAlarm) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("names")))
}
type StopAlarm struct {
_ string `action:"stop" entity:"alarm" awsAPI:"cloudwatch" awsCall:"DisableAlarmActions" awsInput:"cloudwatch.DisableAlarmActionsInput" awsOutput:"cloudwatch.DisableAlarmActionsOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Names []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"names"`
}
func (cmd *StopAlarm) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("names")))
}
type AttachAlarm struct {
_ string `action:"attach" entity:"alarm" awsAPI:"cloudwatch"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Name *string `templateName:"name"`
ActionArn *string `templateName:"action-arn"`
}
func (cmd *AttachAlarm) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("action-arn"), params.Key("name")))
}
func (cmd *AttachAlarm) ManualRun(renv env.Running) (interface{}, error) {
alarm, err := getAlarm(cmd.api, cmd.Name)
if err != nil {
return nil, err
}
alarm.AlarmActions = append(alarm.AlarmActions, aws.ToString(cmd.ActionArn))
return cmd.api.PutMetricAlarm(context.Background(), &cloudwatch.PutMetricAlarmInput{
ActionsEnabled: alarm.ActionsEnabled,
AlarmActions: alarm.AlarmActions,
AlarmDescription: alarm.AlarmDescription,
AlarmName: alarm.AlarmName,
ComparisonOperator: alarm.ComparisonOperator,
Dimensions: alarm.Dimensions,
EvaluateLowSampleCountPercentile: alarm.EvaluateLowSampleCountPercentile,
EvaluationPeriods: alarm.EvaluationPeriods,
ExtendedStatistic: alarm.ExtendedStatistic,
InsufficientDataActions: alarm.InsufficientDataActions,
MetricName: alarm.MetricName,
Namespace: alarm.Namespace,
OKActions: alarm.OKActions,
Period: alarm.Period,
Statistic: alarm.Statistic,
Threshold: alarm.Threshold,
TreatMissingData: alarm.TreatMissingData,
Unit: alarm.Unit,
})
}
type DetachAlarm struct {
_ string `action:"detach" entity:"alarm" awsAPI:"cloudwatch"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudwatch.Client
Name *string `templateName:"name"`
ActionArn *string `templateName:"action-arn"`
}
func (cmd *DetachAlarm) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("action-arn"), params.Key("name")))
}
func (cmd *DetachAlarm) ManualRun(renv env.Running) (interface{}, error) {
alarm, err := getAlarm(cmd.api, cmd.Name)
if err != nil {
return nil, err
}
actionArn := aws.ToString(cmd.ActionArn)
var found bool
var updatedActions []string
for _, action := range alarm.AlarmActions {
if action == actionArn {
found = true
} else {
updatedActions = append(updatedActions, action)
}
}
if !found {
return nil, fmt.Errorf("detach alarm: action '%s' is not attached to alarm actions of alarm %s", actionArn, aws.ToString(alarm.AlarmName))
}
return cmd.api.PutMetricAlarm(context.Background(), &cloudwatch.PutMetricAlarmInput{
ActionsEnabled: alarm.ActionsEnabled,
AlarmActions: updatedActions,
AlarmDescription: alarm.AlarmDescription,
AlarmName: alarm.AlarmName,
ComparisonOperator: alarm.ComparisonOperator,
Dimensions: alarm.Dimensions,
EvaluateLowSampleCountPercentile: alarm.EvaluateLowSampleCountPercentile,
EvaluationPeriods: alarm.EvaluationPeriods,
ExtendedStatistic: alarm.ExtendedStatistic,
InsufficientDataActions: alarm.InsufficientDataActions,
MetricName: alarm.MetricName,
Namespace: alarm.Namespace,
OKActions: alarm.OKActions,
Period: alarm.Period,
Statistic: alarm.Statistic,
Threshold: alarm.Threshold,
TreatMissingData: alarm.TreatMissingData,
Unit: alarm.Unit,
})
}
func getAlarm(api *cloudwatch.Client, name *string) (*cloudwatchtypes.MetricAlarm, error) {
if name == nil {
return nil, errors.New("missing required params 'name'")
}
out, err := api.DescribeAlarms(context.Background(), &cloudwatch.DescribeAlarmsInput{AlarmNames: []string{*name}})
if err != nil {
return nil, err
}
if l := len(out.MetricAlarms); l == 0 {
return nil, fmt.Errorf("alarm '%s' not found", StringValue(name))
} else if l > 1 {
return nil, fmt.Errorf("%d alarms found with name '%s'", l, StringValue(name))
}
return &out.MetricAlarms[0], nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateAppscalingpolicy struct {
_ string `action:"create" entity:"appscalingpolicy" awsAPI:"applicationautoscaling" awsCall:"PutScalingPolicy" awsInput:"applicationautoscaling.PutScalingPolicyInput" awsOutput:"applicationautoscaling.PutScalingPolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *applicationautoscaling.Client
Name *string `awsName:"PolicyName" awsType:"awsstr" templateName:"name"`
Type *string `awsName:"PolicyType" awsType:"awsstr" templateName:"type"`
Resource *string `awsName:"ResourceId" awsType:"awsstr" templateName:"resource"`
Dimension *string `awsName:"ScalableDimension" awsType:"awsstr" templateName:"dimension"`
ServiceNamespace *string `awsName:"ServiceNamespace" awsType:"awsstr" templateName:"service-namespace"`
StepscalingAdjustmentType *string `awsName:"StepScalingPolicyConfiguration.AdjustmentType" awsType:"awsstr" templateName:"stepscaling-adjustment-type"`
StepscalingAdjustments []*string `awsName:"StepScalingPolicyConfiguration.StepAdjustments" awsType:"awsstepadjustments" templateName:"stepscaling-adjustments"`
StepscalingCooldown *int64 `awsName:"StepScalingPolicyConfiguration.Cooldown" awsType:"awsint64" templateName:"stepscaling-cooldown"`
StepscalingAggregationType *string `awsName:"StepScalingPolicyConfiguration.MetricAggregationType" awsType:"awsstr" templateName:"stepscaling-aggregation-type"`
StepscalingMinAdjustmentMagnitude *int64 `awsName:"StepScalingPolicyConfiguration.MinAdjustmentMagnitude" awsType:"awsint64" templateName:"stepscaling-min-adjustment-magnitude"`
}
func (cmd *CreateAppscalingpolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("dimension"), params.Key("name"), params.Key("resource"), params.Key("service-namespace"), params.Key("stepscaling-adjustment-type"), params.Key("stepscaling-adjustments"), params.Key("type"),
params.Opt("stepscaling-aggregation-type", "stepscaling-cooldown", "stepscaling-min-adjustment-magnitude"),
))
}
func (cmd *CreateAppscalingpolicy) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*applicationautoscaling.PutScalingPolicyOutput).PolicyARN)
}
type DeleteAppscalingpolicy struct {
_ string `action:"delete" entity:"appscalingpolicy" awsAPI:"applicationautoscaling" awsCall:"DeleteScalingPolicy" awsInput:"applicationautoscaling.DeleteScalingPolicyInput" awsOutput:"applicationautoscaling.DeleteScalingPolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *applicationautoscaling.Client
Name *string `awsName:"PolicyName" awsType:"awsstr" templateName:"name"`
Resource *string `awsName:"ResourceId" awsType:"awsstr" templateName:"resource"`
Dimension *string `awsName:"ScalableDimension" awsType:"awsstr" templateName:"dimension"`
ServiceNamespace *string `awsName:"ServiceNamespace" awsType:"awsstr" templateName:"service-namespace"`
}
func (cmd *DeleteAppscalingpolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("dimension"), params.Key("name"), params.Key("resource"), params.Key("service-namespace")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateAppscalingtarget struct {
_ string `action:"create" entity:"appscalingtarget" awsAPI:"applicationautoscaling" awsCall:"RegisterScalableTarget" awsInput:"applicationautoscaling.RegisterScalableTargetInput" awsOutput:"applicationautoscaling.RegisterScalableTargetOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *applicationautoscaling.Client
MaxCapacity *int64 `awsName:"MaxCapacity" awsType:"awsint64" templateName:"max-capacity"`
MinCapacity *int64 `awsName:"MinCapacity" awsType:"awsint64" templateName:"min-capacity"`
Resource *string `awsName:"ResourceId" awsType:"awsstr" templateName:"resource"`
Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"`
Dimension *string `awsName:"ScalableDimension" awsType:"awsstr" templateName:"dimension"`
ServiceNamespace *string `awsName:"ServiceNamespace" awsType:"awsstr" templateName:"service-namespace"`
}
func (cmd *CreateAppscalingtarget) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("dimension"), params.Key("max-capacity"), params.Key("min-capacity"), params.Key("resource"), params.Key("role"), params.Key("service-namespace")))
}
type DeleteAppscalingtarget struct {
_ string `action:"delete" entity:"appscalingtarget" awsAPI:"applicationautoscaling" awsCall:"DeregisterScalableTarget" awsInput:"applicationautoscaling.DeregisterScalableTargetInput" awsOutput:"applicationautoscaling.DeregisterScalableTargetOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *applicationautoscaling.Client
Resource *string `awsName:"ResourceId" awsType:"awsstr" templateName:"resource"`
Dimension *string `awsName:"ScalableDimension" awsType:"awsstr" templateName:"dimension"`
ServiceNamespace *string `awsName:"ServiceNamespace" awsType:"awsstr" templateName:"service-namespace"`
}
func (cmd *DeleteAppscalingtarget) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("dimension"), params.Key("resource"), params.Key("service-namespace")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/wallix/awless/logger"
)
type CreateBucket struct {
_ string `action:"create" entity:"bucket" awsAPI:"s3" awsCall:"CreateBucket" awsInput:"s3.CreateBucketInput" awsOutput:"s3.CreateBucketOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Name *string `awsName:"Bucket" awsType:"awsstr" templateName:"name"`
Acl *string `awsName:"ACL" awsType:"awsstr" templateName:"acl"`
}
func (cmd *CreateBucket) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("acl"),
))
}
func (cmd *CreateBucket) ExtractResult(i interface{}) string {
return StringValue(cmd.Name)
}
type UpdateBucket struct {
_ string `action:"update" entity:"bucket" awsAPI:"s3"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Name *string `templateName:"name"`
Acl *string `templateName:"acl"`
PublicWebsite *bool `templateName:"public-website"`
RedirectHostname *string `templateName:"redirect-hostname"`
IndexSuffix *string `templateName:"index-suffix"`
EnforceHttps *bool `templateName:"enforce-https"`
}
func (cmd *UpdateBucket) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("acl", "enforce-https", "index-suffix", "public-website", "redirect-hostname"),
))
}
func (cmd *UpdateBucket) ManualRun(renv env.Running) (interface{}, error) {
start := time.Now()
if cmd.Acl != nil { // Update the canned ACL to apply to the bucket
input := &s3.PutBucketAclInput{
Bucket: cmd.Name,
}
if err := setFieldWithType(cmd.Acl, input, "ACL", awsstr); err != nil {
return nil, err
}
if _, err := cmd.api.PutBucketAcl(context.Background(), input); err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("s3.PutBucketAcl call took %s", time.Since(start))
return nil, nil
}
if cmd.PublicWebsite != nil { // Set/Unset this bucket as a public website
if BoolValue(cmd.PublicWebsite) {
input := &s3.PutBucketWebsiteInput{
Bucket: cmd.Name,
WebsiteConfiguration: &s3types.WebsiteConfiguration{},
}
if cmd.RedirectHostname != nil {
input.WebsiteConfiguration.RedirectAllRequestsTo = &s3types.RedirectAllRequestsTo{HostName: cmd.RedirectHostname}
if BoolValue(cmd.EnforceHttps) {
input.WebsiteConfiguration.RedirectAllRequestsTo.Protocol = s3types.ProtocolHttps
}
} else if cmd.IndexSuffix != nil {
input.WebsiteConfiguration.IndexDocument = &s3types.IndexDocument{Suffix: cmd.IndexSuffix}
} else {
input.WebsiteConfiguration.IndexDocument = &s3types.IndexDocument{Suffix: aws.String("index.html")}
}
if _, err := cmd.api.PutBucketWebsite(context.Background(), input); err != nil {
return nil, err
}
} else {
if _, err := cmd.api.DeleteBucketWebsite(context.Background(), &s3.DeleteBucketWebsiteInput{Bucket: cmd.Name}); err != nil {
return nil, err
}
}
cmd.logger.ExtraVerbosef("s3.PutBucketWebsite call took %s", time.Since(start))
}
return nil, nil
}
type DeleteBucket struct {
_ string `action:"delete" entity:"bucket" awsAPI:"s3" awsCall:"DeleteBucket" awsInput:"s3.DeleteBucketInput" awsOutput:"s3.DeleteBucketOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Name *string `awsName:"Bucket" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteBucket) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"bytes"
"fmt"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"context"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/acm"
acmtypes "github.com/aws/aws-sdk-go-v2/service/acm/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateCertificate struct {
_ string `action:"create" entity:"certificate" awsAPI:"acm"`
logger *logger.Logger
graph cloud.GraphAPI
api *acm.Client
Domains []*string `templateName:"domains"`
ValidationDomains []*string `templateName:"validation-domains"`
}
func (cmd *CreateCertificate) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("domains"),
params.Opt("validation-domains"),
))
}
func (cmd *CreateCertificate) ManualRun(renv env.Running) (interface{}, error) {
input := &acm.RequestCertificateInput{}
domains := awssdk.ToStringSlice(cmd.Domains)
if len(domains) == 0 {
return nil, fmt.Errorf("'domains' must contain at least one element")
}
// Required params
err := setFieldWithType(domains[0], input, "DomainName", awsstr, renv.Context())
if err != nil {
return nil, err
}
if len(domains) > 1 {
if err = setFieldWithType(domains[1:], input, "SubjectAlternativeNames", awsstringslice, renv.Context()); err != nil {
return nil, err
}
}
domainsToValidate := make(map[string]string)
// Extra params
if len(cmd.ValidationDomains) > 0 {
var validationOptions []acmtypes.DomainValidationOption
validation := awssdk.ToStringSlice(cmd.ValidationDomains)
for i, validationDomain := range validation {
if i >= len(domains) {
return nil, fmt.Errorf("there is more validation-domains than certificate domains: %v", validation)
}
domainsToValidate[domains[i]] = validationDomain
validationOptions = append(validationOptions, acmtypes.DomainValidationOption{DomainName: String(domains[i]), ValidationDomain: String(validationDomain)})
}
input.DomainValidationOptions = validationOptions
}
if len(domainsToValidate) < len(domains) {
for i := len(domainsToValidate); i < len(domains); i++ {
domainsToValidate[domains[i]] = domains[i]
}
}
start := time.Now()
var output *acm.RequestCertificateOutput
output, err = cmd.api.RequestCertificate(context.Background(), input)
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("acm.RequestCertificate call took %s", time.Since(start))
if len(domainsToValidate) > 0 {
var helpMsg bytes.Buffer
for domain, validationDomain := range domainsToValidate {
helpMsg.WriteString(fmt.Sprintf("\n\t-> %s: {admin/administrator/hostmaster/postmaster/webmaster}@%s", domain, validationDomain))
}
cmd.logger.Warningf("validate your certificates by following the instructions sent by email to %s", helpMsg.String())
}
return output, nil
}
func (cmd *CreateCertificate) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*acm.RequestCertificateOutput).CertificateArn)
}
type DeleteCertificate struct {
_ string `action:"delete" entity:"certificate" awsAPI:"acm" awsCall:"DeleteCertificate" awsInput:"acm.DeleteCertificateInput" awsOutput:"acm.DeleteCertificateOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *acm.Client
Arn *string `awsName:"CertificateArn" awsType:"awsstr" templateName:"arn"`
}
func (cmd *DeleteCertificate) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("arn")))
}
type CheckCertificate struct {
_ string `action:"check" entity:"certificate" awsAPI:"acm"`
logger *logger.Logger
graph cloud.GraphAPI
api *acm.Client
Arn *string `templateName:"arn"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckCertificate) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("arn"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("issued", "pending_validation", notFoundState),
})
}
func (cmd *CheckCertificate) ManualRun(renv env.Running) (interface{}, error) {
input := &acm.DescribeCertificateInput{
CertificateArn: cmd.Arn,
}
c := &checker{
description: fmt.Sprintf("certificate %s", StringValue(cmd.Arn)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeCertificate(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "CertificateNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
}
if output.Certificate == nil {
return notFoundState, nil
}
return string(output.Certificate.Status), nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"strings"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
)
type CreateClassicLoadbalancer struct {
_ string `action:"create" entity:"classicloadbalancer" awsAPI:"elb" awsCall:"CreateLoadBalancer" awsInput:"elb.CreateLoadBalancerInput" awsOutput:"elb.CreateLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elb.Client
Name *string `awsName:"LoadBalancerName" awsType:"awsstr" templateName:"name"`
AvailabilityZones []*string `awsName:"AvailabilityZones" awsType:"awsstringslice" templateName:"zones"`
Listeners []*string `awsName:"Listeners" awsType:"awsclassicloadblisteners" templateName:"listeners"`
Subnets []*string `awsName:"Subnets" awsType:"awsstringslice" templateName:"subnets"`
Securitygroups []*string `awsName:"SecurityGroups" awsType:"awsstringslice" templateName:"securitygroups"`
Scheme *string `awsName:"Scheme" awsType:"awsstr" templateName:"scheme"`
Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"`
HealthCheckPathToPing string `awsType:"awsstr" templateName:"healthcheck-path"`
}
func (cmd *CreateClassicLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(
params.Key("name"), params.Key("listeners"),
params.AtLeastOneOf(params.Key("subnets"), params.Key("zones")),
params.Opt("scheme", "securitygroups", "tags", "healthcheck-path"),
))
}
func (cmd *CreateClassicLoadbalancer) ExtractResult(i interface{}) string {
return awssdk.ToString(cmd.Name)
}
func (cmd *CreateClassicLoadbalancer) AfterRun(renv env.Running, output interface{}) (err error) {
var target string
if len(cmd.Listeners) > 0 {
splits := strings.SplitN(*cmd.Listeners[0], ":", 3) // format validated on previous command
target = splits[len(splits)-1]
}
var path string
if strings.Contains(target, "HTTP") {
path = "/"
if p := cmd.HealthCheckPathToPing; len(p) > 0 {
if strings.HasPrefix(p, "/") {
path = p
} else {
path = "/" + p
}
}
}
target = target + path
updateClassic := CommandFactory.Build("updateclassicloadbalancer")().(*UpdateClassicLoadbalancer)
entries := map[string]interface{}{
"name": *cmd.Name,
"healthy-threshold": 10,
"unhealthy-threshold": 2,
"health-timeout": 5,
"health-interval": 30,
"health-target": target,
}
if err := params.Validate(updateClassic.ParamsSpec().Validators(), entries); err != nil {
return err
}
_, err = updateClassic.Run(renv, entries)
return
}
type UpdateClassicLoadbalancer struct {
_ string `action:"update" entity:"classicloadbalancer" awsAPI:"elb" awsCall:"ConfigureHealthCheck" awsInput:"elb.ConfigureHealthCheckInput" awsOutput:"elb.ConfigureHealthCheckOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elb.Client
Name *string `awsName:"LoadBalancerName" awsType:"awsstr" templateName:"name"`
HealthcheckHealthyThreshold *int64 `awsName:"Healthcheck.HealthyThreshold" awsType:"awsint64" templateName:"healthy-threshold"`
HealthcheckUnhealthyThreshold *int64 `awsName:"Healthcheck.UnhealthyThreshold" awsType:"awsint64" templateName:"unhealthy-threshold"`
HealthcheckInterval *int64 `awsName:"Healthcheck.Interval" awsType:"awsint64" templateName:"health-interval"`
HealthcheckTimeout *int64 `awsName:"Healthcheck.Timeout" awsType:"awsint64" templateName:"health-timeout"`
HealthcheckTarget *string `awsName:"Healthcheck.Target" awsType:"awsstr" templateName:"health-target"`
}
func (cmd *UpdateClassicLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(
params.Key("name"), params.Key("healthy-threshold"), params.Key("unhealthy-threshold"),
params.Key("health-interval"), params.Key("health-timeout"), params.Key("health-target")),
)
}
type DeleteClassicLoadbalancer struct {
_ string `action:"delete" entity:"classicloadbalancer" awsAPI:"elb" awsCall:"DeleteLoadBalancer" awsInput:"elb.DeleteLoadBalancerInput" awsOutput:"elb.DeleteLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elb.Client
Name *string `awsName:"LoadBalancerName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteClassicLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
type AttachClassicLoadbalancer struct {
_ string `action:"attach" entity:"classicloadbalancer" awsAPI:"elb" awsCall:"RegisterInstancesWithLoadBalancer" awsInput:"elb.RegisterInstancesWithLoadBalancerInput" awsOutput:"elb.RegisterInstancesWithLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elb.Client
Name *string `awsName:"LoadBalancerName" awsType:"awsstr" templateName:"name"`
Instance *string `awsName:"Instances[0]InstanceId" awsType:"awsslicestruct" templateName:"instance"`
}
func (cmd *AttachClassicLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"), params.Key("instance")))
}
func (cmd *AttachClassicLoadbalancer) ExtractResult(interface{}) string {
return awssdk.ToString(cmd.Instance)
}
type DetachClassicLoadbalancer struct {
_ string `action:"detach" entity:"classicloadbalancer" awsAPI:"elb" awsCall:"DeregisterInstancesFromLoadBalancer" awsInput:"elb.DeregisterInstancesFromLoadBalancerInput" awsOutput:"elb.DeregisterInstancesFromLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elb.Client
Name *string `awsName:"LoadBalancerName" awsType:"awsstr" templateName:"name"`
Instance *string `awsName:"Instances[0]InstanceId" awsType:"awsslicestruct" templateName:"instance"`
}
func (cmd *DetachClassicLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"), params.Key("instance")))
}
func (cmd *DetachClassicLoadbalancer) ExtractResult(interface{}) string {
return awssdk.ToString(cmd.Instance)
}
package awsspec
import "github.com/wallix/awless/template/params"
type Definition struct {
Action, Entity, Api string
Params params.Rule
}
func AWSLookupDefinitions(key string) (t Definition, ok bool) {
t, ok = AWSTemplatesDefinitions[key]
return
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateContainercluster struct {
_ string `action:"create" entity:"containercluster" awsAPI:"ecs" awsCall:"CreateCluster" awsInput:"ecs.CreateClusterInput" awsOutput:"ecs.CreateClusterOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Name *string `awsName:"ClusterName" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateContainercluster) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateContainercluster) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ecs.CreateClusterOutput).Cluster.ClusterArn)
}
type DeleteContainercluster struct {
_ string `action:"delete" entity:"containercluster" awsAPI:"ecs" awsCall:"DeleteCluster" awsInput:"ecs.DeleteClusterInput" awsOutput:"ecs.DeleteClusterOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Id *string `awsName:"Cluster" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteContainercluster) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type StartContainertask struct {
_ string `action:"start" entity:"containertask" awsAPI:"ecs"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Cluster *string `templateName:"cluster"`
DesiredCount *int64 `templateName:"desired-count"`
Name *string `templateName:"name"`
Type *string `templateName:"type"`
Role *string `templateName:"role"`
DeploymentName *string `templateName:"deployment-name"`
LoadBalancerContainerName *string `templateName:"loadbalancer.container-name"`
LoadBalancerContainerPort *int64 `templateName:"loadbalancer.container-port"`
LoadBalancerTargetgroup *string `templateName:"loadbalancer.targetgroup"`
}
func (cmd *StartContainertask) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cluster"), params.Key("desired-count"), params.Key("name"), params.Key("type"), params.Opt("deployment-name", "loadbalancer.container-name", "loadbalancer.container-port", "loadbalancer.targetgroup", "role")),
params.Validators{
"type": func(i interface{}, others map[string]interface{}) error {
typ := fmt.Sprint(i)
if typ != "task" && typ != "service" {
return fmt.Errorf("expected any of [task service] but got %s", typ)
}
_, hasDepName := others["deployment-name"]
if typ == "service" && !hasDepName {
return errors.New("missing required param 'deployment-name' when type=service")
}
return nil
},
})
}
func (cmd *StartContainertask) ManualRun(renv env.Running) (interface{}, error) {
switch StringValue(cmd.Type) {
case "service":
setters := []setter{
{val: cmd.Cluster, fieldPath: "Cluster", fieldType: awsstr},
{val: cmd.Name, fieldPath: "TaskDefinition", fieldType: awsstr},
{val: cmd.DeploymentName, fieldPath: "ServiceName", fieldType: awsstr},
{val: cmd.DesiredCount, fieldPath: "DesiredCount", fieldType: awsint64},
}
if cmd.Role != nil {
setters = append(setters, setter{val: cmd.Role, fieldPath: "Role", fieldType: awsstr})
}
if cmd.LoadBalancerContainerName != nil {
setters = append(setters, setter{val: cmd.LoadBalancerContainerName, fieldPath: "LoadBalancers[0]ContainerName", fieldType: awsslicestruct})
}
if cmd.LoadBalancerContainerPort != nil {
setters = append(setters, setter{val: cmd.LoadBalancerContainerPort, fieldPath: "LoadBalancers[0]ContainerPort", fieldType: awsslicestructint64})
}
if cmd.LoadBalancerTargetgroup != nil {
setters = append(setters, setter{val: cmd.LoadBalancerTargetgroup, fieldPath: "LoadBalancers[0]TargetGroupArn", fieldType: awsslicestruct})
}
call := &awsCall{
fnName: "ecs.CreateService",
fn: cmd.api.CreateService,
logger: cmd.logger,
setters: setters,
}
return call.execute(&ecs.CreateServiceInput{})
case "task":
call := &awsCall{
fnName: "ecs.RunTask",
fn: cmd.api.RunTask,
logger: cmd.logger,
setters: []setter{
{val: cmd.Cluster, fieldPath: "Cluster", fieldType: awsstr},
{val: cmd.Name, fieldPath: "TaskDefinition", fieldType: awsstr},
{val: cmd.DesiredCount, fieldPath: "Count", fieldType: awsint64},
},
}
output, err := call.execute(&ecs.RunTaskInput{})
if err != nil {
return nil, err
}
if len(output.(*ecs.RunTaskOutput).Failures) > 0 {
return nil, fmt.Errorf("fail to run task: %s", aws.ToString(output.(*ecs.RunTaskOutput).Failures[0].Reason))
}
if len(output.(*ecs.RunTaskOutput).Tasks) > 0 {
return output, nil
}
return nil, fmt.Errorf("no task started successfully")
}
return nil, fmt.Errorf("invalid type '%s'", StringValue(cmd.Type))
}
func (cmd *StartContainertask) ExtractResult(i interface{}) string {
switch ii := i.(type) {
case *ecs.CreateServiceOutput:
return StringValue(ii.Service.ServiceArn)
case *ecs.RunTaskOutput:
return StringValue(ii.Tasks[0].TaskArn)
default:
return ""
}
}
type StopContainertask struct {
_ string `action:"stop" entity:"containertask" awsAPI:"ecs"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Cluster *string `templateName:"cluster"`
Type *string `templateName:"type"`
DeploymentName *string `templateName:"deployment-name"`
RunArn *string `templateName:"run-arn"`
}
func (cmd *StopContainertask) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cluster"), params.Key("type"), params.Opt("deployment-name", "run-arn")),
params.Validators{
"type": func(i interface{}, others map[string]interface{}) error {
typ := fmt.Sprint(i)
if typ != "task" && typ != "service" {
return fmt.Errorf("expected any of [task service] but got %s", typ)
}
_, hasDepName := others["deployment-name"]
if typ == "service" && !hasDepName {
return errors.New("missing required param 'deployment-name' when type=service")
}
_, hasRunARN := others["run-arn"]
if typ == "task" && !hasRunARN {
return errors.New("missing required param 'run-arn' when type=task")
}
return nil
},
})
}
func (cmd *StopContainertask) ManualRun(renv env.Running) (interface{}, error) {
switch StringValue(cmd.Type) {
case "service":
call := &awsCall{
fnName: "ecs.DeleteService",
fn: cmd.api.DeleteService,
logger: cmd.logger,
setters: []setter{
{val: cmd.Cluster, fieldPath: "Cluster", fieldType: awsstr},
{val: cmd.DeploymentName, fieldPath: "Service", fieldType: awsstr},
},
}
return call.execute(&ecs.DeleteServiceInput{})
case "task":
call := &awsCall{
fnName: "ecs.StopTask",
fn: cmd.api.StopTask,
logger: cmd.logger,
setters: []setter{
{val: cmd.Cluster, fieldPath: "Cluster", fieldType: awsstr},
{val: cmd.RunArn, fieldPath: "Task", fieldType: awsstr},
},
}
return call.execute(&ecs.StopTaskInput{})
}
return nil, fmt.Errorf("invalid type '%s'", StringValue(cmd.Type))
}
type UpdateContainertask struct {
_ string `action:"update" entity:"containertask" awsAPI:"ecs" awsCall:"UpdateService" awsInput:"ecs.UpdateServiceInput" awsOutput:"ecs.UpdateServiceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Cluster *string `awsName:"Cluster" awsType:"awsstr" templateName:"cluster"`
DeploymentName *string `awsName:"Service" awsType:"awsstr" templateName:"deployment-name"`
DesiredCount *int64 `awsName:"DesiredCount" awsType:"awsint64" templateName:"desired-count"`
Name *string `awsName:"TaskDefinition" awsType:"awsstr" templateName:"name"`
}
func (cmd *UpdateContainertask) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("cluster"), params.Key("deployment-name"),
params.Opt("desired-count", "name"),
))
}
type AttachContainertask struct {
_ string `action:"attach" entity:"containertask" awsAPI:"ecs"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Name *string `templateName:"name"`
ContainerName *string `templateName:"container-name"`
Image *string `templateName:"image"`
MemoryHardLimit *int64 `templateName:"memory-hard-limit"`
Commands []*string `templateName:"command"`
Env []*string `templateName:"env"`
Privileged *bool `templateName:"privileged"`
Workdir *string `templateName:"workdir"`
Ports []*string `templateName:"ports"`
}
func (cmd *AttachContainertask) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("container-name"), params.Key("image"), params.Key("memory-hard-limit"), params.Key("name"),
params.Opt("command", "env", "ports", "privileged", "workdir"),
))
}
func (cmd *AttachContainertask) ManualRun(renv env.Running) (interface{}, error) {
var taskDefinitionInput *ecs.RegisterTaskDefinitionInput
taskDefinitionName := StringValue(cmd.Name)
taskdefOutput, err := cmd.api.DescribeTaskDefinition(context.Background(), &ecs.DescribeTaskDefinitionInput{
TaskDefinition: cmd.Name,
})
if awserr, ok := err.(smithy.APIError); err != nil && ok {
if awserr.ErrorCode() == "ClientException" && strings.Contains(strings.ToLower(awserr.ErrorMessage()), "unable to describe task definition") {
cmd.logger.Verbosef("service %s does not exist: creating service", taskDefinitionName)
taskDefinitionInput = &ecs.RegisterTaskDefinitionInput{
Family: aws.String(taskDefinitionName),
}
} else {
return nil, err
}
} else if err != nil {
return nil, err
} else {
taskDefinitionInput = &ecs.RegisterTaskDefinitionInput{
ContainerDefinitions: taskdefOutput.TaskDefinition.ContainerDefinitions,
Family: taskdefOutput.TaskDefinition.Family,
NetworkMode: taskdefOutput.TaskDefinition.NetworkMode,
PlacementConstraints: taskdefOutput.TaskDefinition.PlacementConstraints,
TaskRoleArn: taskdefOutput.TaskDefinition.TaskRoleArn,
Volumes: taskdefOutput.TaskDefinition.Volumes,
}
}
container := &ecstypes.ContainerDefinition{}
if err = setFieldWithType(cmd.ContainerName, container, "Name", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType(cmd.Image, container, "Image", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType(cmd.MemoryHardLimit, container, "Memory", awsint64); err != nil {
return nil, err
}
if cmd.Commands != nil {
switch len(cmd.Commands) {
case 1:
if err = setFieldWithType(strings.Split(StringValue(cmd.Commands[0]), " "), container, "Command", awsstringslice); err != nil {
return nil, err
}
default:
if err = setFieldWithType(cmd.Commands, container, "Command", awsstringslice); err != nil {
return nil, err
}
}
}
if len(cmd.Env) > 0 {
if err = setFieldWithType(cmd.Env, container, "Environment", awsecskeyvalue); err != nil {
return nil, err
}
}
if BoolValue(cmd.Privileged) {
if err = setFieldWithType(true, container, "Privileged", awsbool); err != nil {
return nil, err
}
}
if cmd.Workdir != nil {
if err = setFieldWithType(cmd.Workdir, container, "WorkingDirectory", awsstr); err != nil {
return nil, err
}
}
if len(cmd.Ports) > 0 {
if err = setFieldWithType(cmd.Ports, container, "PortMappings", awsportmappings); err != nil {
return nil, err
}
}
taskDefinitionInput.ContainerDefinitions = append(taskDefinitionInput.ContainerDefinitions, *container)
start := time.Now()
taskDefOutput, err := cmd.api.RegisterTaskDefinition(context.Background(), taskDefinitionInput)
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ecs.RegisterTaskDefinitionOutput call took %s", time.Since(start))
return taskDefOutput, nil
}
func (cmd *AttachContainertask) ExtractResult(i interface{}) string {
return StringValue(i.(*ecs.RegisterTaskDefinitionOutput).TaskDefinition.TaskDefinitionArn)
}
type DetachContainertask struct {
_ string `action:"detach" entity:"containertask" awsAPI:"ecs"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Name *string `templateName:"name"`
ContainerName *string `templateName:"container-name"`
}
func (cmd *DetachContainertask) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("container-name"), params.Key("name")))
}
func (cmd *DetachContainertask) ManualRun(renv env.Running) (interface{}, error) {
taskdefOutput, err := cmd.api.DescribeTaskDefinition(context.Background(), &ecs.DescribeTaskDefinitionInput{
TaskDefinition: cmd.Name,
})
if err != nil {
return nil, err
}
var containerDefinitions []ecstypes.ContainerDefinition
var found bool
var containerNames []string
for _, def := range taskdefOutput.TaskDefinition.ContainerDefinitions {
name := aws.ToString(def.Name)
containerNames = append(containerNames, name)
if name == StringValue(cmd.ContainerName) || aws.ToString(def.Image) == StringValue(cmd.ContainerName) {
found = true
} else {
containerDefinitions = append(containerDefinitions, def)
}
}
if !found {
return nil, fmt.Errorf("did not find any container called '%s': found: '%s'", StringValue(cmd.ContainerName), strings.Join(containerNames, "','"))
}
if len(containerDefinitions) > 0 { //At least one container remaining
taskDefinitionInput := &ecs.RegisterTaskDefinitionInput{
ContainerDefinitions: containerDefinitions,
Family: taskdefOutput.TaskDefinition.Family,
NetworkMode: taskdefOutput.TaskDefinition.NetworkMode,
PlacementConstraints: taskdefOutput.TaskDefinition.PlacementConstraints,
TaskRoleArn: taskdefOutput.TaskDefinition.TaskRoleArn,
Volumes: taskdefOutput.TaskDefinition.Volumes,
}
start := time.Now()
if _, err := cmd.api.RegisterTaskDefinition(context.Background(), taskDefinitionInput); err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ecs.RegisterTaskDefinition call took %s", time.Since(start))
} else {
cmd.logger.Verbosef("no container remaining in service %s: deleting service", StringValue(cmd.Name))
taskDefinitionInput := &ecs.DeregisterTaskDefinitionInput{
TaskDefinition: taskdefOutput.TaskDefinition.TaskDefinitionArn,
}
start := time.Now()
if _, err := cmd.api.DeregisterTaskDefinition(context.Background(), taskDefinitionInput); err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ecs.DeregisterTaskDefinition call took %s", time.Since(start))
}
return taskdefOutput, nil
}
type DeleteContainertask struct {
_ string `action:"delete" entity:"containertask" awsAPI:"ecs" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecs.Client
Name *string `templateName:"name"`
AllVersions *bool `templateName:"all-versions"`
}
func (cmd *DeleteContainertask) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("all-versions"),
))
}
func (cmd *DeleteContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
taskDefinitionName := StringValue(cmd.Name)
taskDefOutput, err := cmd.api.ListTaskDefinitions(context.Background(), &ecs.ListTaskDefinitionsInput{
FamilyPrefix: cmd.Name,
})
if err != nil {
return nil, err
}
switch len(taskDefOutput.TaskDefinitionArns) {
case 0:
return nil, fmt.Errorf("no containertask found with name '%s'", taskDefinitionName)
case 1:
cmd.logger.Verbosef("only one version found for containertask '%s', will delete '%s'.", taskDefinitionName, taskDefOutput.TaskDefinitionArns[0])
default:
if BoolValue(cmd.AllVersions) {
cmd.logger.Warningf("multiple versions found for containertask '%s'. Will delete '%s'", taskDefinitionName, strings.Join(taskDefOutput.TaskDefinitionArns, "','"))
} else {
cmd.logger.Infof("multiple versions found for containertask '%s'", taskDefinitionName)
cmd.logger.Warningf("will delete only latest version: '%s'. Add param `all-versions=true` to delete all versions", taskDefOutput.TaskDefinitionArns[len(taskDefOutput.TaskDefinitionArns)-1])
}
}
return nil, nil
}
func (cmd *DeleteContainertask) ManualRun(renv env.Running) (interface{}, error) {
taskDefinitionName := StringValue(cmd.Name)
if BoolValue(cmd.AllVersions) {
taskDefOutput, err := cmd.api.ListTaskDefinitions(context.Background(), &ecs.ListTaskDefinitionsInput{
FamilyPrefix: aws.String(taskDefinitionName),
})
if err != nil {
return nil, err
}
for _, task := range taskDefOutput.TaskDefinitionArns {
cmd.logger.ExtraVerbosef("deleting '%s'", task)
start := time.Now()
if _, err := cmd.api.DeregisterTaskDefinition(context.Background(), &ecs.DeregisterTaskDefinitionInput{TaskDefinition: aws.String(task)}); err != nil {
return nil, fmt.Errorf("deregister task definition: %s", err)
}
cmd.logger.ExtraVerbosef("ecs.DeregisterTaskDefinition call took %s", time.Since(start))
}
} else {
taskDefOutput, err := cmd.api.DescribeTaskDefinition(context.Background(), &ecs.DescribeTaskDefinitionInput{
TaskDefinition: aws.String(taskDefinitionName),
})
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("deleting '%s'", aws.ToString(taskDefOutput.TaskDefinition.TaskDefinitionArn))
start := time.Now()
if _, err := cmd.api.DeregisterTaskDefinition(context.Background(), &ecs.DeregisterTaskDefinitionInput{TaskDefinition: taskDefOutput.TaskDefinition.TaskDefinitionArn}); err != nil {
return nil, fmt.Errorf("deregister task definition: %s", err)
}
cmd.logger.ExtraVerbosef("ecs.DeregisterTaskDefinition call took %s", time.Since(start))
}
return nil, nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateDatabase struct {
_ string `action:"create" entity:"database" awsAPI:"rds"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
// Required for DB
Type *string `awsName:"DBInstanceClass" awsType:"awsstr" templateName:"type"`
Id *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"id"`
Engine *string `awsName:"Engine" awsType:"awsstr" templateName:"engine"`
Password *string `awsName:"MasterUserPassword" awsType:"awsstr" templateName:"password"`
Username *string `awsName:"MasterUsername" awsType:"awsstr" templateName:"username"`
Size *int64 `awsName:"AllocatedStorage" awsType:"awsint64" templateName:"size"`
// Required for read replica DB
ReadReplicaSourceDB *string `awsName:"SourceDBInstanceIdentifier" awsType:"awsstr" templateName:"replica-source"`
ReadReplicaIdentifier *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"replica"`
// Extras common to both replica DB and source DB
Autoupgrade *bool `awsName:"AutoMinorVersionUpgrade" awsType:"awsbool" templateName:"autoupgrade"`
Availabilityzone *string `awsName:"AvailabilityZone" awsType:"awsstr" templateName:"availabilityzone"`
Subnetgroup *string `awsName:"DBSubnetGroupName" awsType:"awsstr" templateName:"subnetgroup"`
Iops *int64 `awsName:"Iops" awsType:"awsint64" templateName:"iops"`
Optiongroup *string `awsName:"OptionGroupName" awsType:"awsstr" templateName:"optiongroup"`
Port *int64 `awsName:"Port" awsType:"awsint64" templateName:"port"`
Public *bool `awsName:"PubliclyAccessible" awsType:"awsbool" templateName:"public"`
Storagetype *string `awsName:"StorageType" awsType:"awsstr" templateName:"storagetype"`
// Extra only for DB
Backupretention *int64 `awsName:"BackupRetentionPeriod" awsType:"awsint64" templateName:"backupretention"`
Backupwindow *string `awsName:"PreferredBackupWindow" awsType:"awsstr" templateName:"backupwindow"`
Cluster *string `awsName:"DBClusterIdentifier" awsType:"awsstr" templateName:"cluster"`
Dbname *string `awsName:"DBName" awsType:"awsstr" templateName:"dbname"`
Dbsecuritygroups []*string `awsName:"DBSecurityGroups" awsType:"awsstringslice" templateName:"dbsecuritygroups"`
Domain *string `awsName:"Domain" awsType:"awsstr" templateName:"domain"`
Encrypted *bool `awsName:"StorageEncrypted" awsType:"awsbool" templateName:"encrypted"`
Iamrole *string `awsName:"DomainIAMRoleName" awsType:"awsstr" templateName:"iamrole"`
License *string `awsName:"LicenseModel" awsType:"awsstr" templateName:"license"`
Maintenancewindow *string `awsName:"PreferredMaintenanceWindow" awsType:"awsstr" templateName:"maintenancewindow"`
Multiaz *bool `awsName:"MultiAZ" awsType:"awsbool" templateName:"multiaz"`
Parametergroup *string `awsName:"DBParameterGroupName" awsType:"awsstr" templateName:"parametergroup"`
Timezone *string `awsName:"Timezone" awsType:"awsstr" templateName:"timezone"`
Vpcsecuritygroups []*string `awsName:"VpcSecurityGroupIds" awsType:"awsstringslice" templateName:"vpcsecuritygroups"`
Version *string `awsName:"EngineVersion" awsType:"awsstr" templateName:"version"`
// Extra only for replica DB
CopyTagsToSnapshot *string `awsName:"CopyTagsToSnapshot" awsType:"awsbool" templateName:"copytagstosnapshot"`
}
func (cmd *CreateDatabase) ParamsSpec() params.Spec {
return params.NewSpec(params.OnlyOneOf(
params.AllOf(params.Key("type"), params.Key("id"), params.Key("engine"), params.Key("password"), params.Key("username"), params.Key("size")),
params.AllOf(params.Key("replica"), params.Key("replica-source")),
params.Opt("autoupgrade", "availabilityzone", "backupretention", "cluster", "dbname", "parametergroup",
"dbsecuritygroups", "subnetgroup", "domain", "iamrole", "version", "iops", "license", "multiaz", "optiongroup",
"port", "backupwindow", "maintenancewindow", "public", "encrypted", "storagetype", "timezone", "vpcsecuritygroups")),
params.Validators{
"password": params.MinLengthOf(8),
"replica": func(i interface{}, others map[string]interface{}) error {
msg := "param not allowed in replica (either not applicable or directly inherited from the source DB)"
if _, ok := others["backupretention"]; ok {
return fmt.Errorf("'backupretention' %s", msg)
}
if _, ok := others["backupwindow"]; ok {
return fmt.Errorf("'backupwindow' %s", msg)
}
if _, ok := others["cluster"]; ok {
return fmt.Errorf("'cluster' %s", msg)
}
if _, ok := others["dbname"]; ok {
return fmt.Errorf("'dbname' %s", msg)
}
if _, ok := others["dbsecuritygroups"]; ok {
return fmt.Errorf("'dbsecuritygroups' %s", msg)
}
if _, ok := others["domain"]; ok {
return fmt.Errorf("'domain' %s", msg)
}
if _, ok := others["encrypted"]; ok {
return fmt.Errorf("'encrypted' %s", msg)
}
if _, ok := others["iamrole"]; ok {
return fmt.Errorf("'iamrole' %s", msg)
}
if _, ok := others["license"]; ok {
return fmt.Errorf("'license' %s", msg)
}
if _, ok := others["maintenancewindow"]; ok {
return fmt.Errorf("'maintenancewindow' %s", msg)
}
if _, ok := others["multiaz"]; ok {
return fmt.Errorf("'multiaz' %s", msg)
}
if _, ok := others["parametergroup"]; ok {
return fmt.Errorf("'parametergroup' %s", msg)
}
if _, ok := others["timezone"]; ok {
return fmt.Errorf("'timezone' %s", msg)
}
if _, ok := others["vpcsecuritygroups"]; ok {
return fmt.Errorf("'vpcsecuritygroups' %s", msg)
}
if _, ok := others["version"]; ok {
return fmt.Errorf("'version' %s", msg)
}
return nil
},
},
)
}
func (cmd *CreateDatabase) ManualRun(renv env.Running) (output interface{}, err error) {
if replica := cmd.ReadReplicaIdentifier; replica != nil {
input := &rds.CreateDBInstanceReadReplicaInput{}
if ierr := structInjector(cmd, input, renv.Context()); ierr != nil {
return nil, fmt.Errorf("cannot inject in rds.CreateDBInstanceReadReplicaInput: %s", ierr)
}
start := time.Now()
output, err = cmd.api.CreateDBInstanceReadReplica(context.Background(), input)
cmd.logger.ExtraVerbosef("rds.CreateDBInstanceReadReplica call took %s", time.Since(start))
} else {
input := &rds.CreateDBInstanceInput{}
if ierr := structInjector(cmd, input, renv.Context()); ierr != nil {
return nil, fmt.Errorf("cannot inject in rds.CreateDBInstanceInput: %s", ierr)
}
start := time.Now()
output, err = cmd.api.CreateDBInstance(context.Background(), input)
cmd.logger.ExtraVerbosef("rds.CreateDBInstance call took %s", time.Since(start))
}
if err != nil {
return output, err
}
return output, nil
}
func (cmd *CreateDatabase) ExtractResult(i interface{}) string {
switch v := i.(type) {
case *rds.CreateDBInstanceOutput:
return awssdk.ToString(v.DBInstance.DBInstanceIdentifier)
case *rds.CreateDBInstanceReadReplicaOutput:
return awssdk.ToString(v.DBInstance.DBInstanceIdentifier)
default:
logger.Errorf("unexpected interface type %T", i)
return ""
}
}
type DeleteDatabase struct {
_ string `action:"delete" entity:"database" awsAPI:"rds" awsCall:"DeleteDBInstance" awsInput:"rds.DeleteDBInstanceInput" awsOutput:"rds.DeleteDBInstanceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Id *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"id"`
SkipSnapshot *bool `awsName:"SkipFinalSnapshot" awsType:"awsbool" templateName:"skip-snapshot"`
Snapshot *string `awsName:"FinalDBSnapshotIdentifier" awsType:"awsstr" templateName:"snapshot"`
}
func (cmd *DeleteDatabase) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("skip-snapshot", "snapshot"),
))
}
type CheckDatabase struct {
_ string `action:"check" entity:"database" awsAPI:"rds"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckDatabase) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("available",
"backing-up", "creating", "deleting", "failed", "maintenance", "modifying",
"rebooting", "renaming", "resetting-master-credentials", "restore-error",
"storage-full", "upgrading", notFoundState),
},
)
}
func (cmd *CheckDatabase) ManualRun(renv env.Running) (interface{}, error) {
input := &rds.DescribeDBInstancesInput{
DBInstanceIdentifier: cmd.Id,
}
c := &checker{
description: fmt.Sprintf("database %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeDBInstances(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "DatabaseNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
if res := output.DBInstances; len(res) > 0 {
for _, dbinst := range res {
if StringValue(dbinst.DBInstanceIdentifier) == StringValue(cmd.Id) {
return StringValue(dbinst.DBInstanceStatus), nil
}
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
type StartDatabase struct {
_ string `action:"start" entity:"database" awsAPI:"rds" awsCall:"StartDBInstance" awsInput:"rds.StartDBInstanceInput" awsOutput:"rds.StartDBInstanceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Id *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"id"`
}
func (cmd *StartDatabase) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type StopDatabase struct {
_ string `action:"stop" entity:"database" awsAPI:"rds" awsCall:"StopDBInstance" awsInput:"rds.StopDBInstanceInput" awsOutput:"rds.StopDBInstanceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Id *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"id"`
}
func (cmd *StopDatabase) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type RestartDatabase struct {
_ string `action:"restart" entity:"database" awsAPI:"rds" awsCall:"RebootDBInstance" awsInput:"rds.RebootDBInstanceInput" awsOutput:"rds.RebootDBInstanceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Id *string `awsName:"DBInstanceIdentifier" awsType:"awsstr" templateName:"id"`
WithFailover *bool `awsName:"ForceFailover" awsType:"awsbool" templateName:"with-failover"`
}
func (cmd *RestartDatabase) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Opt("with-failover")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateDbsubnetgroup struct {
_ string `action:"create" entity:"dbsubnetgroup" awsAPI:"rds" awsCall:"CreateDBSubnetGroup" awsInput:"rds.CreateDBSubnetGroupInput" awsOutput:"rds.CreateDBSubnetGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Name *string `awsName:"DBSubnetGroupName" awsType:"awsstr" templateName:"name"`
Description *string `awsName:"DBSubnetGroupDescription" awsType:"awsstr" templateName:"description"`
Subnets []*string `awsName:"SubnetIds" awsType:"awsstringslice" templateName:"subnets"`
}
func (cmd *CreateDbsubnetgroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("description"), params.Key("name"), params.Key("subnets")))
}
func (cmd *CreateDbsubnetgroup) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*rds.CreateDBSubnetGroupOutput).DBSubnetGroup.DBSubnetGroupName)
}
type DeleteDbsubnetgroup struct {
_ string `action:"delete" entity:"dbsubnetgroup" awsAPI:"rds" awsCall:"DeleteDBSubnetGroup" awsInput:"rds.DeleteDBSubnetGroupInput" awsOutput:"rds.DeleteDBSubnetGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *rds.Client
Name *string `awsName:"DBSubnetGroupName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteDbsubnetgroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"fmt"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudfronttypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
var CallerReferenceFunc = func() string {
return fmt.Sprint(time.Now().UTC().Unix())
}
type CreateDistribution struct {
_ string `action:"create" entity:"distribution" awsAPI:"cloudfront"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudfront.Client
OriginDomain *string `templateName:"origin-domain"`
Certificate *string `templateName:"certificate"`
Comment *string `templateName:"comment"`
DefaultFile *string `templateName:"default-file"`
DomainAliases []*string `templateName:"domain-aliases"`
Enable *bool `templateName:"enable"`
ForwardCookies *string `templateName:"forward-cookies"`
ForwardQueries *bool `templateName:"forward-queries"`
HttpsBehaviour *string `templateName:"https-behaviour"` //nolint:misspell
OriginPath *string `templateName:"origin-path"`
PriceClass *string `templateName:"price-class"`
MinTtl *int64 `templateName:"min-ttl"`
}
func (cmd *CreateDistribution) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("origin-domain"),
params.Opt("certificate", "comment", "default-file", "domain-aliases", "enable", "forward-cookies", "forward-queries", "https-behaviour", "min-ttl", "origin-path", "price-class"), //nolint:misspell
))
}
func (cmd *CreateDistribution) ManualRun(renv env.Running) (interface{}, error) {
originId := "orig_1"
input := &cloudfront.CreateDistributionInput{
DistributionConfig: &cloudfronttypes.DistributionConfig{
CallerReference: aws.String(CallerReferenceFunc()),
Comment: cmd.OriginDomain,
DefaultCacheBehavior: &cloudfronttypes.DefaultCacheBehavior{
MinTTL: aws.Int64(0),
ForwardedValues: &cloudfronttypes.ForwardedValues{
Cookies: &cloudfronttypes.CookiePreference{Forward: cloudfronttypes.ItemSelectionAll},
QueryString: aws.Bool(true),
},
TrustedSigners: &cloudfronttypes.TrustedSigners{
Enabled: aws.Bool(false),
Quantity: aws.Int32(0),
},
TargetOriginId: aws.String(originId),
ViewerProtocolPolicy: cloudfronttypes.ViewerProtocolPolicyAllowAll,
},
Enabled: aws.Bool(true),
Origins: &cloudfronttypes.Origins{
Quantity: aws.Int32(1),
Items: []cloudfronttypes.Origin{
{Id: aws.String(originId)},
},
},
},
}
if domain := StringValue(cmd.OriginDomain); strings.HasSuffix(domain, ".s3.amazonaws.com") || (strings.HasSuffix(domain, ".amazonaws.com") && strings.Contains(domain, ".s3-website-")) {
input.DistributionConfig.Origins.Items[0].S3OriginConfig = &cloudfronttypes.S3OriginConfig{OriginAccessIdentity: aws.String("")}
}
call := &awsCall{
fnName: "cloudfront.CreateDistribution",
fn: cmd.api.CreateDistribution,
logger: cmd.logger,
setters: []setter{
{val: cmd.OriginDomain, fieldPath: "DistributionConfig.Origins.Items[0].DomainName", fieldType: awsstr},
},
}
if cmd.Certificate != nil {
call.setters = append(call.setters, setter{val: cmd.Certificate, fieldPath: "DistributionConfig.ViewerCertificate.ACMCertificateArn", fieldType: awsstr})
call.setters = append(call.setters, setter{val: "sni-only", fieldPath: "DistributionConfig.ViewerCertificate.SSLSupportMethod", fieldType: awsstr})
}
if cmd.Comment != nil {
call.setters = append(call.setters, setter{val: cmd.Comment, fieldPath: "DistributionConfig.Comment", fieldType: awsstr})
}
if cmd.DefaultFile != nil {
call.setters = append(call.setters, setter{val: cmd.DefaultFile, fieldPath: "DistributionConfig.DefaultRootObject", fieldType: awsstr})
}
if cmd.DomainAliases != nil {
call.setters = append(call.setters, setter{val: cmd.DomainAliases, fieldPath: "DistributionConfig.Aliases.Items", fieldType: awsstringslice})
call.setters = append(call.setters, setter{val: len(cmd.DomainAliases), fieldPath: "DistributionConfig.Aliases.Quantity", fieldType: awsint64})
}
if cmd.Enable != nil {
call.setters = append(call.setters, setter{val: cmd.Enable, fieldPath: "DistributionConfig.Enabled", fieldType: awsbool})
}
if cmd.ForwardCookies != nil {
call.setters = append(call.setters, setter{val: cmd.ForwardCookies, fieldPath: "DistributionConfig.DefaultCacheBehavior.ForwardedValues.Cookies.Forward", fieldType: awsstr})
}
if cmd.ForwardQueries != nil {
call.setters = append(call.setters, setter{val: cmd.ForwardQueries, fieldPath: "DistributionConfig.DefaultCacheBehavior.ForwardedValues.QueryString", fieldType: awsbool})
}
if cmd.HttpsBehaviour != nil {
call.setters = append(call.setters, setter{val: cmd.HttpsBehaviour, fieldPath: "DistributionConfig.DefaultCacheBehavior.ViewerProtocolPolicy", fieldType: awsstr})
}
if cmd.MinTtl != nil {
call.setters = append(call.setters, setter{val: cmd.MinTtl, fieldPath: "DistributionConfig.DefaultCacheBehavior.MinTTL", fieldType: awsint64})
}
if cmd.OriginPath != nil {
call.setters = append(call.setters, setter{val: cmd.OriginPath, fieldPath: "DistributionConfig.Origins.Items[0].OriginPath", fieldType: awsstr})
}
if cmd.PriceClass != nil {
call.setters = append(call.setters, setter{val: cmd.PriceClass, fieldPath: "DistributionConfig.PriceClass", fieldType: awsstr})
}
return call.execute(input)
}
func (cmd *CreateDistribution) ExtractResult(i interface{}) string {
return StringValue(i.(*cloudfront.CreateDistributionOutput).Distribution.Id)
}
type CheckDistribution struct {
_ string `action:"check" entity:"distribution" awsAPI:"cloudfront"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudfront.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckDistribution) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("deployed", "inprogress", notFoundState),
})
}
func (cmd *CheckDistribution) ManualRun(renv env.Running) (interface{}, error) {
input := &cloudfront.GetDistributionInput{
Id: cmd.Id,
}
c := &checker{
description: fmt.Sprintf("distribution %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.GetDistribution(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "NoSuchDistribution" {
return notFoundState, nil
}
return "", awserr
} else {
return "", err
}
} else {
return aws.ToString(output.Distribution.Status), nil
}
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
type UpdateDistribution struct {
_ string `action:"update" entity:"distribution" awsAPI:"cloudfront"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudfront.Client
Id *string `awsName:"Id" awsType:"awsstr" templateName:"id"`
OriginDomain *string `templateName:"origin-domain"`
Certificate *string `templateName:"certificate"`
Comment *string `templateName:"comment"`
DefaultFile *string `templateName:"default-file"`
DomainAliases []*string `templateName:"domain-aliases"`
Enable *bool `templateName:"enable"`
ForwardCookies *string `templateName:"forward-cookies"`
ForwardQueries *bool `templateName:"forward-queries"`
HttpsBehaviour *string `templateName:"https-behaviour"` //nolint:misspell
OriginPath *string `templateName:"origin-path"`
PriceClass *string `templateName:"price-class"`
MinTtl *int64 `templateName:"min-ttl"`
}
func (cmd *UpdateDistribution) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("certificate", "comment", "default-file", "domain-aliases", "enable", "forward-cookies", "forward-queries", "https-behaviour", "min-ttl", "origin-domain", "origin-path", "price-class"), //nolint:misspell
))
}
func (cmd *UpdateDistribution) ManualRun(renv env.Running) (interface{}, error) {
distribOutput, err := cmd.api.GetDistribution(context.Background(), &cloudfront.GetDistributionInput{
Id: cmd.Id,
})
if err != nil {
return nil, err
}
distriToUpdate := distribOutput.Distribution
configToUpdate := distriToUpdate.DistributionConfig
etag := distribOutput.ETag
beforeUpdate := fmt.Sprintf("%v", distribOutput.Distribution.DistributionConfig)
input := &cloudfront.UpdateDistributionInput{
IfMatch: etag,
DistributionConfig: distriToUpdate.DistributionConfig,
}
if err = setFieldWithType(cmd.Id, input, "Id", awsstr); err != nil {
return nil, err
}
if cmd.Enable != nil && BoolValue(cmd.Enable) != BoolValue(distriToUpdate.DistributionConfig.Enabled) {
if err = setFieldWithType(cmd.Enable, input, "DistributionConfig.Enabled", awsbool); err != nil {
return nil, err
}
}
if cmd.OriginDomain != nil || cmd.OriginPath != nil {
if configToUpdate.Origins == nil || len(configToUpdate.Origins.Items) == 0 {
configToUpdate.Origins = &cloudfronttypes.Origins{
Quantity: aws.Int32(1),
Items: []cloudfronttypes.Origin{
{Id: aws.String("orig_1")},
},
}
}
if cmd.OriginDomain != nil {
if err = setFieldWithType(cmd.OriginDomain, input, "DistributionConfig.Origins.Items[0].DomainName", awsstr); err != nil {
return nil, err
}
if domain := aws.ToString(input.DistributionConfig.Origins.Items[0].DomainName); strings.HasSuffix(domain, ".s3.amazonaws.com") || (strings.HasSuffix(domain, ".amazonaws.com") && strings.Contains(domain, ".s3-website-")) {
input.DistributionConfig.Origins.Items[0].S3OriginConfig = &cloudfronttypes.S3OriginConfig{OriginAccessIdentity: aws.String("")}
}
}
if cmd.OriginPath != nil {
if err = setFieldWithType(cmd.OriginPath, input, "DistributionConfig.Origins.Items[0].OriginPath", awsstr); err != nil {
return nil, err
}
}
}
if cmd.Certificate != nil {
if err = setFieldWithType(cmd.Certificate, input, "DistributionConfig.ViewerCertificate.ACMCertificateArn", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType("sni-only", input, "DistributionConfig.ViewerCertificate.SSLSupportMethod", awsstr); err != nil {
return nil, err
}
}
if cmd.Comment != nil {
if err = setFieldWithType(cmd.Comment, input, "DistributionConfig.Comment", awsstr); err != nil {
return nil, err
}
}
if cmd.DefaultFile != nil {
if err = setFieldWithType(cmd.DefaultFile, input, "DistributionConfig.DefaultRootObject", awsstr); err != nil {
return nil, err
}
}
if cmd.DomainAliases != nil {
if err = setFieldWithType(cmd.DomainAliases, input, "DistributionConfig.Aliases.Items", awsstringslice); err != nil {
return nil, err
}
}
if cmd.Enable != nil {
if err = setFieldWithType(cmd.Enable, input, "DistributionConfig.Enabled", awsbool); err != nil {
return nil, err
}
}
if cmd.ForwardCookies != nil {
if err = setFieldWithType(cmd.ForwardCookies, input, "DistributionConfig.DefaultCacheBehavior.ForwardedValues.Cookies.Forward", awsstr); err != nil {
return nil, err
}
}
if cmd.ForwardQueries != nil {
if err = setFieldWithType(cmd.ForwardQueries, input, "DistributionConfig.DefaultCacheBehavior.ForwardedValues.QueryString", awsbool); err != nil {
return nil, err
}
}
if cmd.HttpsBehaviour != nil {
if err = setFieldWithType(cmd.HttpsBehaviour, input, "DistributionConfig.DefaultCacheBehavior.ViewerProtocolPolicy", awsstr); err != nil {
return nil, err
}
}
if cmd.MinTtl != nil {
if err = setFieldWithType(cmd.MinTtl, input, "DistributionConfig.DefaultCacheBehavior.MinTTL", awsint64); err != nil {
return nil, err
}
}
if cmd.PriceClass != nil {
if err = setFieldWithType(cmd.PriceClass, input, "DistributionConfig.PriceClass", awsstr); err != nil {
return nil, err
}
}
if aliases := input.DistributionConfig.Aliases; aliases != nil {
aliases.Quantity = aws.Int32(int32(len(aliases.Items)))
}
if beforeUpdate == fmt.Sprintf("%v", input.DistributionConfig) {
cmd.logger.Infof("no property has been changed to distribution '%s'", StringValue(cmd.Id))
return distribOutput, nil
}
start := time.Now()
var output *cloudfront.UpdateDistributionOutput
output, err = cmd.api.UpdateDistribution(context.Background(), input)
cmd.logger.ExtraVerbosef("cloudfront.UpdateDistribution call took %s", time.Since(start))
return output, err
}
func (cmd *UpdateDistribution) ExtractResult(i interface{}) string {
switch ii := i.(type) {
case *cloudfront.GetDistributionOutput:
return StringValue(ii.ETag)
case *cloudfront.UpdateDistributionOutput:
return StringValue(ii.ETag)
default:
return ""
}
}
type DeleteDistribution struct {
_ string `action:"delete" entity:"distribution" awsAPI:"cloudfront"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudfront.Client
Id *string `awsName:"Id" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteDistribution) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
func (cmd *DeleteDistribution) ManualRun(renv env.Running) (interface{}, error) {
cmd.logger.Info("disabling distribution")
updateDistribution := CommandFactory.Build("updatedistribution")().(*UpdateDistribution)
entries := map[string]interface{}{
"id": cmd.Id,
"enable": false,
}
if err := params.Validate(updateDistribution.ParamsSpec().Validators(), entries); err != nil {
return nil, err
}
var etag string
if out, err := updateDistribution.Run(renv, entries); err != nil {
return nil, err
} else if str, ok := out.(string); ok {
etag = str
}
cmd.logger.Info("check distribution disabling has been propagated")
checkDistribution := CommandFactory.Build("checkdistribution")().(*CheckDistribution)
entries = map[string]interface{}{
"id": cmd.Id,
"state": "Deployed",
"timeout": 1800,
}
if err := params.Validate(checkDistribution.ParamsSpec().Validators(), entries); err != nil {
return nil, err
}
if _, err := checkDistribution.Run(renv, entries); err != nil {
return nil, err
}
input := &cloudfront.DeleteDistributionInput{IfMatch: aws.String(fmt.Sprint(etag))}
if err := setFieldWithType(cmd.Id, input, "Id", awsstr); err != nil {
return nil, err
}
start := time.Now()
output, err := cmd.api.DeleteDistribution(context.Background(), input)
cmd.logger.ExtraVerbosef("cloudfront.DeleteDistribution call took %s", time.Since(start))
return output, err
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateElasticip struct {
_ string `action:"create" entity:"elasticip" awsAPI:"ec2" awsCall:"AllocateAddress" awsInput:"ec2.AllocateAddressInput" awsOutput:"ec2.AllocateAddressOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Domain *string `awsName:"Domain" awsType:"awsstr" templateName:"domain"`
}
func (cmd *CreateElasticip) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("domain")))
}
func (cmd *CreateElasticip) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.AllocateAddressOutput).AllocationId)
}
type DeleteElasticip struct {
_ string `action:"delete" entity:"elasticip" awsAPI:"ec2" awsCall:"ReleaseAddress" awsInput:"ec2.ReleaseAddressInput" awsOutput:"ec2.ReleaseAddressOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"AllocationId" awsType:"awsstr" templateName:"id"`
Ip *string `awsName:"PublicIp" awsType:"awsstr" templateName:"ip"`
}
func (cmd *DeleteElasticip) ParamsSpec() params.Spec {
return params.NewSpec(
params.OnlyOneOf(params.Key("id"), params.Key("ip")),
params.Validators{"ip": params.IsIP},
)
}
type AttachElasticip struct {
_ string `action:"attach" entity:"elasticip" awsAPI:"ec2" awsCall:"AssociateAddress" awsInput:"ec2.AssociateAddressInput" awsOutput:"ec2.AssociateAddressOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"AllocationId" awsType:"awsstr" templateName:"id"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
Networkinterface *string `awsName:"NetworkInterfaceId" awsType:"awsstr" templateName:"networkinterface"`
Privateip *string `awsName:"PrivateIpAddress" awsType:"awsstr" templateName:"privateip"`
AllowReassociation *bool `awsName:"AllowReassociation" awsType:"awsbool" templateName:"allow-reassociation"`
}
func (cmd *AttachElasticip) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Opt("allow-reassociation", "instance", "networkinterface", "privateip")),
params.Validators{"privateip": params.IsIP},
)
}
func (cmd *AttachElasticip) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.AssociateAddressOutput).AssociationId)
}
type DetachElasticip struct {
_ string `action:"detach" entity:"elasticip" awsAPI:"ec2" awsCall:"DisassociateAddress" awsInput:"ec2.DisassociateAddressInput" awsOutput:"ec2.DisassociateAddressOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Association *string `awsName:"AssociationId" awsType:"awsstr" templateName:"association"`
}
func (cmd *DetachElasticip) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("association")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/lambda"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateFunction struct {
_ string `action:"create" entity:"function" awsAPI:"lambda" awsCall:"CreateFunction" awsInput:"lambda.CreateFunctionInput" awsOutput:"lambda.FunctionConfiguration"`
logger *logger.Logger
graph cloud.GraphAPI
api *lambda.Client
Name *string `awsName:"FunctionName" awsType:"awsstr" templateName:"name"`
Handler *string `awsName:"Handler" awsType:"awsstr" templateName:"handler"`
Role *string `awsName:"Role" awsType:"awsstr" templateName:"role"`
Runtime *string `awsName:"Runtime" awsType:"awsstr" templateName:"runtime"`
Bucket *string `awsName:"Code.S3Bucket" awsType:"awsstr" templateName:"bucket"`
Object *string `awsName:"Code.S3Key" awsType:"awsstr" templateName:"object"`
Objectversion *string `awsName:"Code.S3ObjectVersion" awsType:"awsstr" templateName:"objectversion"`
Zipfile *string `awsName:"Code.ZipFile" awsType:"awsfiletobyteslice" templateName:"zipfile"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
Memory *int64 `awsName:"MemorySize" awsType:"awsint64" templateName:"memory"`
Publish *bool `awsName:"Publish" awsType:"awsbool" templateName:"publish"`
Timeout *int64 `awsName:"Timeout" awsType:"awsint64" templateName:"timeout"`
}
func (cmd *CreateFunction) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("handler"), params.Key("name"), params.Key("role"), params.Key("runtime"),
params.Opt("bucket", "description", "memory", "object", "objectversion", "publish", "timeout", "zipfile"),
))
}
func (cmd *CreateFunction) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*lambda.CreateFunctionOutput).FunctionArn)
}
type DeleteFunction struct {
_ string `action:"delete" entity:"function" awsAPI:"lambda" awsCall:"DeleteFunction" awsInput:"lambda.DeleteFunctionInput" awsOutput:"lambda.DeleteFunctionOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *lambda.Client
Id *string `awsName:"FunctionName" awsType:"awsstr" templateName:"id"`
Version *string `awsName:"Qualifier" awsType:"awsstr" templateName:"version"`
}
func (cmd *DeleteFunction) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("version"),
))
}
/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsspec
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
)
type Factory interface {
Build(key string) func() interface{}
}
var CommandFactory Factory
var MockAWSSessionFactory = &AWSFactory{
Log: logger.DiscardLogger,
Cfg: aws.Config{},
}
type AWSFactory struct {
Log *logger.Logger
Cfg aws.Config
Graph cloud.GraphAPI
}
func (f *AWSFactory) Build(key string) func() interface{} {
switch key {
case "attachalarm":
return func() interface{} { return NewAttachAlarm(f.Cfg, f.Graph, f.Log) }
case "attachclassicloadbalancer":
return func() interface{} { return NewAttachClassicLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "attachcontainertask":
return func() interface{} { return NewAttachContainertask(f.Cfg, f.Graph, f.Log) }
case "attachelasticip":
return func() interface{} { return NewAttachElasticip(f.Cfg, f.Graph, f.Log) }
case "attachinstance":
return func() interface{} { return NewAttachInstance(f.Cfg, f.Graph, f.Log) }
case "attachinstanceprofile":
return func() interface{} { return NewAttachInstanceprofile(f.Cfg, f.Graph, f.Log) }
case "attachinternetgateway":
return func() interface{} { return NewAttachInternetgateway(f.Cfg, f.Graph, f.Log) }
case "attachlistener":
return func() interface{} { return NewAttachListener(f.Cfg, f.Graph, f.Log) }
case "attachmfadevice":
return func() interface{} { return NewAttachMfadevice(f.Cfg, f.Graph, f.Log) }
case "attachnetworkinterface":
return func() interface{} { return NewAttachNetworkinterface(f.Cfg, f.Graph, f.Log) }
case "attachpolicy":
return func() interface{} { return NewAttachPolicy(f.Cfg, f.Graph, f.Log) }
case "attachrole":
return func() interface{} { return NewAttachRole(f.Cfg, f.Graph, f.Log) }
case "attachroutetable":
return func() interface{} { return NewAttachRoutetable(f.Cfg, f.Graph, f.Log) }
case "attachsecuritygroup":
return func() interface{} { return NewAttachSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "attachuser":
return func() interface{} { return NewAttachUser(f.Cfg, f.Graph, f.Log) }
case "attachvolume":
return func() interface{} { return NewAttachVolume(f.Cfg, f.Graph, f.Log) }
case "authenticateregistry":
return func() interface{} { return NewAuthenticateRegistry(f.Cfg, f.Graph, f.Log) }
case "checkcertificate":
return func() interface{} { return NewCheckCertificate(f.Cfg, f.Graph, f.Log) }
case "checkdatabase":
return func() interface{} { return NewCheckDatabase(f.Cfg, f.Graph, f.Log) }
case "checkdistribution":
return func() interface{} { return NewCheckDistribution(f.Cfg, f.Graph, f.Log) }
case "checkinstance":
return func() interface{} { return NewCheckInstance(f.Cfg, f.Graph, f.Log) }
case "checkloadbalancer":
return func() interface{} { return NewCheckLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "checknatgateway":
return func() interface{} { return NewCheckNatgateway(f.Cfg, f.Graph, f.Log) }
case "checknetworkinterface":
return func() interface{} { return NewCheckNetworkinterface(f.Cfg, f.Graph, f.Log) }
case "checkscalinggroup":
return func() interface{} { return NewCheckScalinggroup(f.Cfg, f.Graph, f.Log) }
case "checksecuritygroup":
return func() interface{} { return NewCheckSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "checkvolume":
return func() interface{} { return NewCheckVolume(f.Cfg, f.Graph, f.Log) }
case "copyimage":
return func() interface{} { return NewCopyImage(f.Cfg, f.Graph, f.Log) }
case "copysnapshot":
return func() interface{} { return NewCopySnapshot(f.Cfg, f.Graph, f.Log) }
case "createaccesskey":
return func() interface{} { return NewCreateAccesskey(f.Cfg, f.Graph, f.Log) }
case "createalarm":
return func() interface{} { return NewCreateAlarm(f.Cfg, f.Graph, f.Log) }
case "createappscalingpolicy":
return func() interface{} { return NewCreateAppscalingpolicy(f.Cfg, f.Graph, f.Log) }
case "createappscalingtarget":
return func() interface{} { return NewCreateAppscalingtarget(f.Cfg, f.Graph, f.Log) }
case "createbucket":
return func() interface{} { return NewCreateBucket(f.Cfg, f.Graph, f.Log) }
case "createcertificate":
return func() interface{} { return NewCreateCertificate(f.Cfg, f.Graph, f.Log) }
case "createclassicloadbalancer":
return func() interface{} { return NewCreateClassicLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "createcontainercluster":
return func() interface{} { return NewCreateContainercluster(f.Cfg, f.Graph, f.Log) }
case "createdatabase":
return func() interface{} { return NewCreateDatabase(f.Cfg, f.Graph, f.Log) }
case "createdbsubnetgroup":
return func() interface{} { return NewCreateDbsubnetgroup(f.Cfg, f.Graph, f.Log) }
case "createdistribution":
return func() interface{} { return NewCreateDistribution(f.Cfg, f.Graph, f.Log) }
case "createelasticip":
return func() interface{} { return NewCreateElasticip(f.Cfg, f.Graph, f.Log) }
case "createfunction":
return func() interface{} { return NewCreateFunction(f.Cfg, f.Graph, f.Log) }
case "creategroup":
return func() interface{} { return NewCreateGroup(f.Cfg, f.Graph, f.Log) }
case "createimage":
return func() interface{} { return NewCreateImage(f.Cfg, f.Graph, f.Log) }
case "createinstance":
return func() interface{} { return NewCreateInstance(f.Cfg, f.Graph, f.Log) }
case "createinstanceprofile":
return func() interface{} { return NewCreateInstanceprofile(f.Cfg, f.Graph, f.Log) }
case "createinternetgateway":
return func() interface{} { return NewCreateInternetgateway(f.Cfg, f.Graph, f.Log) }
case "createkeypair":
return func() interface{} { return NewCreateKeypair(f.Cfg, f.Graph, f.Log) }
case "createlaunchconfiguration":
return func() interface{} { return NewCreateLaunchconfiguration(f.Cfg, f.Graph, f.Log) }
case "createlistener":
return func() interface{} { return NewCreateListener(f.Cfg, f.Graph, f.Log) }
case "createloadbalancer":
return func() interface{} { return NewCreateLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "createloginprofile":
return func() interface{} { return NewCreateLoginprofile(f.Cfg, f.Graph, f.Log) }
case "createmfadevice":
return func() interface{} { return NewCreateMfadevice(f.Cfg, f.Graph, f.Log) }
case "createnatgateway":
return func() interface{} { return NewCreateNatgateway(f.Cfg, f.Graph, f.Log) }
case "createnetworkinterface":
return func() interface{} { return NewCreateNetworkinterface(f.Cfg, f.Graph, f.Log) }
case "createpolicy":
return func() interface{} { return NewCreatePolicy(f.Cfg, f.Graph, f.Log) }
case "createqueue":
return func() interface{} { return NewCreateQueue(f.Cfg, f.Graph, f.Log) }
case "createrecord":
return func() interface{} { return NewCreateRecord(f.Cfg, f.Graph, f.Log) }
case "createrepository":
return func() interface{} { return NewCreateRepository(f.Cfg, f.Graph, f.Log) }
case "createrole":
return func() interface{} { return NewCreateRole(f.Cfg, f.Graph, f.Log) }
case "createroute":
return func() interface{} { return NewCreateRoute(f.Cfg, f.Graph, f.Log) }
case "createroutetable":
return func() interface{} { return NewCreateRoutetable(f.Cfg, f.Graph, f.Log) }
case "creates3object":
return func() interface{} { return NewCreateS3object(f.Cfg, f.Graph, f.Log) }
case "createscalinggroup":
return func() interface{} { return NewCreateScalinggroup(f.Cfg, f.Graph, f.Log) }
case "createscalingpolicy":
return func() interface{} { return NewCreateScalingpolicy(f.Cfg, f.Graph, f.Log) }
case "createsecuritygroup":
return func() interface{} { return NewCreateSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "createsnapshot":
return func() interface{} { return NewCreateSnapshot(f.Cfg, f.Graph, f.Log) }
case "createstack":
return func() interface{} { return NewCreateStack(f.Cfg, f.Graph, f.Log) }
case "createsubnet":
return func() interface{} { return NewCreateSubnet(f.Cfg, f.Graph, f.Log) }
case "createsubscription":
return func() interface{} { return NewCreateSubscription(f.Cfg, f.Graph, f.Log) }
case "createtag":
return func() interface{} { return NewCreateTag(f.Cfg, f.Graph, f.Log) }
case "createtargetgroup":
return func() interface{} { return NewCreateTargetgroup(f.Cfg, f.Graph, f.Log) }
case "createtopic":
return func() interface{} { return NewCreateTopic(f.Cfg, f.Graph, f.Log) }
case "createuser":
return func() interface{} { return NewCreateUser(f.Cfg, f.Graph, f.Log) }
case "createvolume":
return func() interface{} { return NewCreateVolume(f.Cfg, f.Graph, f.Log) }
case "createvpc":
return func() interface{} { return NewCreateVpc(f.Cfg, f.Graph, f.Log) }
case "createzone":
return func() interface{} { return NewCreateZone(f.Cfg, f.Graph, f.Log) }
case "deleteaccesskey":
return func() interface{} { return NewDeleteAccesskey(f.Cfg, f.Graph, f.Log) }
case "deletealarm":
return func() interface{} { return NewDeleteAlarm(f.Cfg, f.Graph, f.Log) }
case "deleteappscalingpolicy":
return func() interface{} { return NewDeleteAppscalingpolicy(f.Cfg, f.Graph, f.Log) }
case "deleteappscalingtarget":
return func() interface{} { return NewDeleteAppscalingtarget(f.Cfg, f.Graph, f.Log) }
case "deletebucket":
return func() interface{} { return NewDeleteBucket(f.Cfg, f.Graph, f.Log) }
case "deletecertificate":
return func() interface{} { return NewDeleteCertificate(f.Cfg, f.Graph, f.Log) }
case "deleteclassicloadbalancer":
return func() interface{} { return NewDeleteClassicLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "deletecontainercluster":
return func() interface{} { return NewDeleteContainercluster(f.Cfg, f.Graph, f.Log) }
case "deletecontainertask":
return func() interface{} { return NewDeleteContainertask(f.Cfg, f.Graph, f.Log) }
case "deletedatabase":
return func() interface{} { return NewDeleteDatabase(f.Cfg, f.Graph, f.Log) }
case "deletedbsubnetgroup":
return func() interface{} { return NewDeleteDbsubnetgroup(f.Cfg, f.Graph, f.Log) }
case "deletedistribution":
return func() interface{} { return NewDeleteDistribution(f.Cfg, f.Graph, f.Log) }
case "deleteelasticip":
return func() interface{} { return NewDeleteElasticip(f.Cfg, f.Graph, f.Log) }
case "deletefunction":
return func() interface{} { return NewDeleteFunction(f.Cfg, f.Graph, f.Log) }
case "deletegroup":
return func() interface{} { return NewDeleteGroup(f.Cfg, f.Graph, f.Log) }
case "deleteimage":
return func() interface{} { return NewDeleteImage(f.Cfg, f.Graph, f.Log) }
case "deleteinstance":
return func() interface{} { return NewDeleteInstance(f.Cfg, f.Graph, f.Log) }
case "deleteinstanceprofile":
return func() interface{} { return NewDeleteInstanceprofile(f.Cfg, f.Graph, f.Log) }
case "deleteinternetgateway":
return func() interface{} { return NewDeleteInternetgateway(f.Cfg, f.Graph, f.Log) }
case "deletekeypair":
return func() interface{} { return NewDeleteKeypair(f.Cfg, f.Graph, f.Log) }
case "deletelaunchconfiguration":
return func() interface{} { return NewDeleteLaunchconfiguration(f.Cfg, f.Graph, f.Log) }
case "deletelistener":
return func() interface{} { return NewDeleteListener(f.Cfg, f.Graph, f.Log) }
case "deleteloadbalancer":
return func() interface{} { return NewDeleteLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "deleteloginprofile":
return func() interface{} { return NewDeleteLoginprofile(f.Cfg, f.Graph, f.Log) }
case "deletemfadevice":
return func() interface{} { return NewDeleteMfadevice(f.Cfg, f.Graph, f.Log) }
case "deletenatgateway":
return func() interface{} { return NewDeleteNatgateway(f.Cfg, f.Graph, f.Log) }
case "deletenetworkinterface":
return func() interface{} { return NewDeleteNetworkinterface(f.Cfg, f.Graph, f.Log) }
case "deletepolicy":
return func() interface{} { return NewDeletePolicy(f.Cfg, f.Graph, f.Log) }
case "deletequeue":
return func() interface{} { return NewDeleteQueue(f.Cfg, f.Graph, f.Log) }
case "deleterecord":
return func() interface{} { return NewDeleteRecord(f.Cfg, f.Graph, f.Log) }
case "deleterepository":
return func() interface{} { return NewDeleteRepository(f.Cfg, f.Graph, f.Log) }
case "deleterole":
return func() interface{} { return NewDeleteRole(f.Cfg, f.Graph, f.Log) }
case "deleteroute":
return func() interface{} { return NewDeleteRoute(f.Cfg, f.Graph, f.Log) }
case "deleteroutetable":
return func() interface{} { return NewDeleteRoutetable(f.Cfg, f.Graph, f.Log) }
case "deletes3object":
return func() interface{} { return NewDeleteS3object(f.Cfg, f.Graph, f.Log) }
case "deletescalinggroup":
return func() interface{} { return NewDeleteScalinggroup(f.Cfg, f.Graph, f.Log) }
case "deletescalingpolicy":
return func() interface{} { return NewDeleteScalingpolicy(f.Cfg, f.Graph, f.Log) }
case "deletesecuritygroup":
return func() interface{} { return NewDeleteSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "deletesnapshot":
return func() interface{} { return NewDeleteSnapshot(f.Cfg, f.Graph, f.Log) }
case "deletestack":
return func() interface{} { return NewDeleteStack(f.Cfg, f.Graph, f.Log) }
case "deletesubnet":
return func() interface{} { return NewDeleteSubnet(f.Cfg, f.Graph, f.Log) }
case "deletesubscription":
return func() interface{} { return NewDeleteSubscription(f.Cfg, f.Graph, f.Log) }
case "deletetag":
return func() interface{} { return NewDeleteTag(f.Cfg, f.Graph, f.Log) }
case "deletetargetgroup":
return func() interface{} { return NewDeleteTargetgroup(f.Cfg, f.Graph, f.Log) }
case "deletetopic":
return func() interface{} { return NewDeleteTopic(f.Cfg, f.Graph, f.Log) }
case "deleteuser":
return func() interface{} { return NewDeleteUser(f.Cfg, f.Graph, f.Log) }
case "deletevolume":
return func() interface{} { return NewDeleteVolume(f.Cfg, f.Graph, f.Log) }
case "deletevpc":
return func() interface{} { return NewDeleteVpc(f.Cfg, f.Graph, f.Log) }
case "deletezone":
return func() interface{} { return NewDeleteZone(f.Cfg, f.Graph, f.Log) }
case "detachalarm":
return func() interface{} { return NewDetachAlarm(f.Cfg, f.Graph, f.Log) }
case "detachclassicloadbalancer":
return func() interface{} { return NewDetachClassicLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "detachcontainertask":
return func() interface{} { return NewDetachContainertask(f.Cfg, f.Graph, f.Log) }
case "detachelasticip":
return func() interface{} { return NewDetachElasticip(f.Cfg, f.Graph, f.Log) }
case "detachinstance":
return func() interface{} { return NewDetachInstance(f.Cfg, f.Graph, f.Log) }
case "detachinstanceprofile":
return func() interface{} { return NewDetachInstanceprofile(f.Cfg, f.Graph, f.Log) }
case "detachinternetgateway":
return func() interface{} { return NewDetachInternetgateway(f.Cfg, f.Graph, f.Log) }
case "detachmfadevice":
return func() interface{} { return NewDetachMfadevice(f.Cfg, f.Graph, f.Log) }
case "detachnetworkinterface":
return func() interface{} { return NewDetachNetworkinterface(f.Cfg, f.Graph, f.Log) }
case "detachpolicy":
return func() interface{} { return NewDetachPolicy(f.Cfg, f.Graph, f.Log) }
case "detachrole":
return func() interface{} { return NewDetachRole(f.Cfg, f.Graph, f.Log) }
case "detachroutetable":
return func() interface{} { return NewDetachRoutetable(f.Cfg, f.Graph, f.Log) }
case "detachsecuritygroup":
return func() interface{} { return NewDetachSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "detachuser":
return func() interface{} { return NewDetachUser(f.Cfg, f.Graph, f.Log) }
case "detachvolume":
return func() interface{} { return NewDetachVolume(f.Cfg, f.Graph, f.Log) }
case "importimage":
return func() interface{} { return NewImportImage(f.Cfg, f.Graph, f.Log) }
case "restartdatabase":
return func() interface{} { return NewRestartDatabase(f.Cfg, f.Graph, f.Log) }
case "restartinstance":
return func() interface{} { return NewRestartInstance(f.Cfg, f.Graph, f.Log) }
case "startalarm":
return func() interface{} { return NewStartAlarm(f.Cfg, f.Graph, f.Log) }
case "startcontainertask":
return func() interface{} { return NewStartContainertask(f.Cfg, f.Graph, f.Log) }
case "startdatabase":
return func() interface{} { return NewStartDatabase(f.Cfg, f.Graph, f.Log) }
case "startinstance":
return func() interface{} { return NewStartInstance(f.Cfg, f.Graph, f.Log) }
case "stopalarm":
return func() interface{} { return NewStopAlarm(f.Cfg, f.Graph, f.Log) }
case "stopcontainertask":
return func() interface{} { return NewStopContainertask(f.Cfg, f.Graph, f.Log) }
case "stopdatabase":
return func() interface{} { return NewStopDatabase(f.Cfg, f.Graph, f.Log) }
case "stopinstance":
return func() interface{} { return NewStopInstance(f.Cfg, f.Graph, f.Log) }
case "updatebucket":
return func() interface{} { return NewUpdateBucket(f.Cfg, f.Graph, f.Log) }
case "updateclassicloadbalancer":
return func() interface{} { return NewUpdateClassicLoadbalancer(f.Cfg, f.Graph, f.Log) }
case "updatecontainertask":
return func() interface{} { return NewUpdateContainertask(f.Cfg, f.Graph, f.Log) }
case "updatedistribution":
return func() interface{} { return NewUpdateDistribution(f.Cfg, f.Graph, f.Log) }
case "updateimage":
return func() interface{} { return NewUpdateImage(f.Cfg, f.Graph, f.Log) }
case "updateinstance":
return func() interface{} { return NewUpdateInstance(f.Cfg, f.Graph, f.Log) }
case "updateloginprofile":
return func() interface{} { return NewUpdateLoginprofile(f.Cfg, f.Graph, f.Log) }
case "updatepolicy":
return func() interface{} { return NewUpdatePolicy(f.Cfg, f.Graph, f.Log) }
case "updaterecord":
return func() interface{} { return NewUpdateRecord(f.Cfg, f.Graph, f.Log) }
case "updates3object":
return func() interface{} { return NewUpdateS3object(f.Cfg, f.Graph, f.Log) }
case "updatescalinggroup":
return func() interface{} { return NewUpdateScalinggroup(f.Cfg, f.Graph, f.Log) }
case "updatesecuritygroup":
return func() interface{} { return NewUpdateSecuritygroup(f.Cfg, f.Graph, f.Log) }
case "updatestack":
return func() interface{} { return NewUpdateStack(f.Cfg, f.Graph, f.Log) }
case "updatesubnet":
return func() interface{} { return NewUpdateSubnet(f.Cfg, f.Graph, f.Log) }
case "updatetargetgroup":
return func() interface{} { return NewUpdateTargetgroup(f.Cfg, f.Graph, f.Log) }
}
return nil
}
var (
_ command = &AttachAlarm{}
_ command = &AttachClassicLoadbalancer{}
_ command = &AttachContainertask{}
_ command = &AttachElasticip{}
_ command = &AttachInstance{}
_ command = &AttachInstanceprofile{}
_ command = &AttachInternetgateway{}
_ command = &AttachListener{}
_ command = &AttachMfadevice{}
_ command = &AttachNetworkinterface{}
_ command = &AttachPolicy{}
_ command = &AttachRole{}
_ command = &AttachRoutetable{}
_ command = &AttachSecuritygroup{}
_ command = &AttachUser{}
_ command = &AttachVolume{}
_ command = &AuthenticateRegistry{}
_ command = &CheckCertificate{}
_ command = &CheckDatabase{}
_ command = &CheckDistribution{}
_ command = &CheckInstance{}
_ command = &CheckLoadbalancer{}
_ command = &CheckNatgateway{}
_ command = &CheckNetworkinterface{}
_ command = &CheckScalinggroup{}
_ command = &CheckSecuritygroup{}
_ command = &CheckVolume{}
_ command = &CopyImage{}
_ command = &CopySnapshot{}
_ command = &CreateAccesskey{}
_ command = &CreateAlarm{}
_ command = &CreateAppscalingpolicy{}
_ command = &CreateAppscalingtarget{}
_ command = &CreateBucket{}
_ command = &CreateCertificate{}
_ command = &CreateClassicLoadbalancer{}
_ command = &CreateContainercluster{}
_ command = &CreateDatabase{}
_ command = &CreateDbsubnetgroup{}
_ command = &CreateDistribution{}
_ command = &CreateElasticip{}
_ command = &CreateFunction{}
_ command = &CreateGroup{}
_ command = &CreateImage{}
_ command = &CreateInstance{}
_ command = &CreateInstanceprofile{}
_ command = &CreateInternetgateway{}
_ command = &CreateKeypair{}
_ command = &CreateLaunchconfiguration{}
_ command = &CreateListener{}
_ command = &CreateLoadbalancer{}
_ command = &CreateLoginprofile{}
_ command = &CreateMfadevice{}
_ command = &CreateNatgateway{}
_ command = &CreateNetworkinterface{}
_ command = &CreatePolicy{}
_ command = &CreateQueue{}
_ command = &CreateRecord{}
_ command = &CreateRepository{}
_ command = &CreateRole{}
_ command = &CreateRoute{}
_ command = &CreateRoutetable{}
_ command = &CreateS3object{}
_ command = &CreateScalinggroup{}
_ command = &CreateScalingpolicy{}
_ command = &CreateSecuritygroup{}
_ command = &CreateSnapshot{}
_ command = &CreateStack{}
_ command = &CreateSubnet{}
_ command = &CreateSubscription{}
_ command = &CreateTag{}
_ command = &CreateTargetgroup{}
_ command = &CreateTopic{}
_ command = &CreateUser{}
_ command = &CreateVolume{}
_ command = &CreateVpc{}
_ command = &CreateZone{}
_ command = &DeleteAccesskey{}
_ command = &DeleteAlarm{}
_ command = &DeleteAppscalingpolicy{}
_ command = &DeleteAppscalingtarget{}
_ command = &DeleteBucket{}
_ command = &DeleteCertificate{}
_ command = &DeleteClassicLoadbalancer{}
_ command = &DeleteContainercluster{}
_ command = &DeleteContainertask{}
_ command = &DeleteDatabase{}
_ command = &DeleteDbsubnetgroup{}
_ command = &DeleteDistribution{}
_ command = &DeleteElasticip{}
_ command = &DeleteFunction{}
_ command = &DeleteGroup{}
_ command = &DeleteImage{}
_ command = &DeleteInstance{}
_ command = &DeleteInstanceprofile{}
_ command = &DeleteInternetgateway{}
_ command = &DeleteKeypair{}
_ command = &DeleteLaunchconfiguration{}
_ command = &DeleteListener{}
_ command = &DeleteLoadbalancer{}
_ command = &DeleteLoginprofile{}
_ command = &DeleteMfadevice{}
_ command = &DeleteNatgateway{}
_ command = &DeleteNetworkinterface{}
_ command = &DeletePolicy{}
_ command = &DeleteQueue{}
_ command = &DeleteRecord{}
_ command = &DeleteRepository{}
_ command = &DeleteRole{}
_ command = &DeleteRoute{}
_ command = &DeleteRoutetable{}
_ command = &DeleteS3object{}
_ command = &DeleteScalinggroup{}
_ command = &DeleteScalingpolicy{}
_ command = &DeleteSecuritygroup{}
_ command = &DeleteSnapshot{}
_ command = &DeleteStack{}
_ command = &DeleteSubnet{}
_ command = &DeleteSubscription{}
_ command = &DeleteTag{}
_ command = &DeleteTargetgroup{}
_ command = &DeleteTopic{}
_ command = &DeleteUser{}
_ command = &DeleteVolume{}
_ command = &DeleteVpc{}
_ command = &DeleteZone{}
_ command = &DetachAlarm{}
_ command = &DetachClassicLoadbalancer{}
_ command = &DetachContainertask{}
_ command = &DetachElasticip{}
_ command = &DetachInstance{}
_ command = &DetachInstanceprofile{}
_ command = &DetachInternetgateway{}
_ command = &DetachMfadevice{}
_ command = &DetachNetworkinterface{}
_ command = &DetachPolicy{}
_ command = &DetachRole{}
_ command = &DetachRoutetable{}
_ command = &DetachSecuritygroup{}
_ command = &DetachUser{}
_ command = &DetachVolume{}
_ command = &ImportImage{}
_ command = &RestartDatabase{}
_ command = &RestartInstance{}
_ command = &StartAlarm{}
_ command = &StartContainertask{}
_ command = &StartDatabase{}
_ command = &StartInstance{}
_ command = &StopAlarm{}
_ command = &StopContainertask{}
_ command = &StopDatabase{}
_ command = &StopInstance{}
_ command = &UpdateBucket{}
_ command = &UpdateClassicLoadbalancer{}
_ command = &UpdateContainertask{}
_ command = &UpdateDistribution{}
_ command = &UpdateImage{}
_ command = &UpdateInstance{}
_ command = &UpdateLoginprofile{}
_ command = &UpdatePolicy{}
_ command = &UpdateRecord{}
_ command = &UpdateS3object{}
_ command = &UpdateScalinggroup{}
_ command = &UpdateSecuritygroup{}
_ command = &UpdateStack{}
_ command = &UpdateSubnet{}
_ command = &UpdateTargetgroup{}
)
/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsspec
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
acm "github.com/aws/aws-sdk-go-v2/service/acm"
applicationautoscaling "github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
autoscaling "github.com/aws/aws-sdk-go-v2/service/autoscaling"
cloudformation "github.com/aws/aws-sdk-go-v2/service/cloudformation"
cloudfront "github.com/aws/aws-sdk-go-v2/service/cloudfront"
cloudwatch "github.com/aws/aws-sdk-go-v2/service/cloudwatch"
ec2 "github.com/aws/aws-sdk-go-v2/service/ec2"
ecr "github.com/aws/aws-sdk-go-v2/service/ecr"
ecs "github.com/aws/aws-sdk-go-v2/service/ecs"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
iam "github.com/aws/aws-sdk-go-v2/service/iam"
lambda "github.com/aws/aws-sdk-go-v2/service/lambda"
rds "github.com/aws/aws-sdk-go-v2/service/rds"
route53 "github.com/aws/aws-sdk-go-v2/service/route53"
s3 "github.com/aws/aws-sdk-go-v2/service/s3"
sns "github.com/aws/aws-sdk-go-v2/service/sns"
sqs "github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
)
func NewAttachAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachAlarm {
cmd := new(AttachAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *AttachAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach alarm '%s' done", extracted)
} else {
renv.Log().Verbose("attach alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *AttachAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachClassicLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachClassicLoadbalancer {
cmd := new(AttachClassicLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elb.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachClassicLoadbalancer) SetApi(api *elb.Client) {
cmd.api = api
}
func (cmd *AttachClassicLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachClassicLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elb.RegisterInstancesWithLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elb.RegisterInstancesWithLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RegisterInstancesWithLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elb.RegisterInstancesWithLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach classicloadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach classicloadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("attach classicloadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachClassicLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("classicloadbalancer"), nil
}
func (cmd *AttachClassicLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachContainertask {
cmd := new(AttachContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *AttachContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach containertask '%s' done", extracted)
} else {
renv.Log().Verbose("attach containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containertask"), nil
}
func (cmd *AttachContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachElasticip(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachElasticip {
cmd := new(AttachElasticip)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachElasticip) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachElasticip) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachElasticip) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AssociateAddressInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AssociateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AssociateAddress(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AssociateAddress call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach elasticip: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach elasticip '%s' done", extracted)
} else {
renv.Log().Verbose("attach elasticip done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachElasticip) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AssociateAddressInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AssociateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AssociateAddress(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AssociateAddress call took %s", time.Since(start))
renv.Log().Verbose("dry run: attach elasticip ok")
return fakeDryRunId("elasticip"), nil
}
}
return nil, err
}
func (cmd *AttachElasticip) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachInstance {
cmd := new(AttachInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachInstance) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *AttachInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.RegisterTargetsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.RegisterTargetsInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RegisterTargets(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.RegisterTargets call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach instance '%s' done", extracted)
} else {
renv.Log().Verbose("attach instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instance"), nil
}
func (cmd *AttachInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachInstanceprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachInstanceprofile {
cmd := new(AttachInstanceprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachInstanceprofile) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachInstanceprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachInstanceprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach instanceprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach instanceprofile '%s' done", extracted)
} else {
renv.Log().Verbose("attach instanceprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachInstanceprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachInternetgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachInternetgateway {
cmd := new(AttachInternetgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachInternetgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachInternetgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachInternetgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AttachInternetGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AttachInternetGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AttachInternetGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach internetgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach internetgateway '%s' done", extracted)
} else {
renv.Log().Verbose("attach internetgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachInternetgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AttachInternetGatewayInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AttachInternetGateway(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AttachInternetGateway call took %s", time.Since(start))
renv.Log().Verbose("dry run: attach internetgateway ok")
return fakeDryRunId("internetgateway"), nil
}
}
return nil, err
}
func (cmd *AttachInternetgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachListener(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachListener {
cmd := new(AttachListener)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachListener) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *AttachListener) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachListener) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.AddListenerCertificatesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.AddListenerCertificatesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AddListenerCertificates(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.AddListenerCertificates call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach listener: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach listener '%s' done", extracted)
} else {
renv.Log().Verbose("attach listener done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachListener) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("listener"), nil
}
func (cmd *AttachListener) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachMfadevice(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachMfadevice {
cmd := new(AttachMfadevice)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachMfadevice) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *AttachMfadevice) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachMfadevice) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.EnableMFADeviceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.EnableMFADeviceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.EnableMFADevice(context.Background(), input)
renv.Log().ExtraVerbosef("iam.EnableMFADevice call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach mfadevice: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach mfadevice '%s' done", extracted)
} else {
renv.Log().Verbose("attach mfadevice done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachMfadevice) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("mfadevice"), nil
}
func (cmd *AttachMfadevice) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachNetworkinterface(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachNetworkinterface {
cmd := new(AttachNetworkinterface)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachNetworkinterface) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachNetworkinterface) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachNetworkinterface) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AttachNetworkInterfaceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AttachNetworkInterface(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AttachNetworkInterface call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach networkinterface: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach networkinterface '%s' done", extracted)
} else {
renv.Log().Verbose("attach networkinterface done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachNetworkinterface) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AttachNetworkInterfaceInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AttachNetworkInterface(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AttachNetworkInterface call took %s", time.Since(start))
renv.Log().Verbose("dry run: attach networkinterface ok")
return fakeDryRunId("networkinterface"), nil
}
}
return nil, err
}
func (cmd *AttachNetworkinterface) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachPolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachPolicy {
cmd := new(AttachPolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachPolicy) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *AttachPolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachPolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach policy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach policy '%s' done", extracted)
} else {
renv.Log().Verbose("attach policy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachPolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("policy"), nil
}
func (cmd *AttachPolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachRole(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachRole {
cmd := new(AttachRole)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachRole) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *AttachRole) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachRole) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.AddRoleToInstanceProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.AddRoleToInstanceProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AddRoleToInstanceProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.AddRoleToInstanceProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach role: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach role '%s' done", extracted)
} else {
renv.Log().Verbose("attach role done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachRole) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("role"), nil
}
func (cmd *AttachRole) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachRoutetable(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachRoutetable {
cmd := new(AttachRoutetable)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachRoutetable) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachRoutetable) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachRoutetable) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AssociateRouteTableInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AssociateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AssociateRouteTable(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AssociateRouteTable call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach routetable: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach routetable '%s' done", extracted)
} else {
renv.Log().Verbose("attach routetable done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachRoutetable) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AssociateRouteTableInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AssociateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AssociateRouteTable(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AssociateRouteTable call took %s", time.Since(start))
renv.Log().Verbose("dry run: attach routetable ok")
return fakeDryRunId("routetable"), nil
}
}
return nil, err
}
func (cmd *AttachRoutetable) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachSecuritygroup {
cmd := new(AttachSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("attach securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("securitygroup"), nil
}
func (cmd *AttachSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachUser(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachUser {
cmd := new(AttachUser)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachUser) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *AttachUser) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachUser) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.AddUserToGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.AddUserToGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AddUserToGroup(context.Background(), input)
renv.Log().ExtraVerbosef("iam.AddUserToGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach user: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach user '%s' done", extracted)
} else {
renv.Log().Verbose("attach user done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachUser) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("user"), nil
}
func (cmd *AttachUser) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAttachVolume(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AttachVolume {
cmd := new(AttachVolume)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AttachVolume) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *AttachVolume) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AttachVolume) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AttachVolumeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AttachVolume(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AttachVolume call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("attach volume: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("attach volume '%s' done", extracted)
} else {
renv.Log().Verbose("attach volume done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AttachVolume) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AttachVolumeInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AttachVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AttachVolume(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AttachVolume call took %s", time.Since(start))
renv.Log().Verbose("dry run: attach volume ok")
return fakeDryRunId("volume"), nil
}
}
return nil, err
}
func (cmd *AttachVolume) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewAuthenticateRegistry(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *AuthenticateRegistry {
cmd := new(AuthenticateRegistry)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecr.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *AuthenticateRegistry) SetApi(api *ecr.Client) {
cmd.api = api
}
func (cmd *AuthenticateRegistry) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *AuthenticateRegistry) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("authenticate registry: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("authenticate registry '%s' done", extracted)
} else {
renv.Log().Verbose("authenticate registry done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *AuthenticateRegistry) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("registry"), nil
}
func (cmd *AuthenticateRegistry) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckCertificate(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckCertificate {
cmd := new(CheckCertificate)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = acm.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckCertificate) SetApi(api *acm.Client) {
cmd.api = api
}
func (cmd *CheckCertificate) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckCertificate) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check certificate: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check certificate '%s' done", extracted)
} else {
renv.Log().Verbose("check certificate done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckCertificate) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("certificate"), nil
}
func (cmd *CheckCertificate) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckDatabase {
cmd := new(CheckDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *CheckDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check database '%s' done", extracted)
} else {
renv.Log().Verbose("check database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *CheckDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckDistribution(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckDistribution {
cmd := new(CheckDistribution)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudfront.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckDistribution) SetApi(api *cloudfront.Client) {
cmd.api = api
}
func (cmd *CheckDistribution) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckDistribution) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check distribution: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check distribution '%s' done", extracted)
} else {
renv.Log().Verbose("check distribution done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckDistribution) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("distribution"), nil
}
func (cmd *CheckDistribution) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckInstance {
cmd := new(CheckInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CheckInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check instance '%s' done", extracted)
} else {
renv.Log().Verbose("check instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instance"), nil
}
func (cmd *CheckInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckLoadbalancer {
cmd := new(CheckLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckLoadbalancer) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *CheckLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check loadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check loadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("check loadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loadbalancer"), nil
}
func (cmd *CheckLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckNatgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckNatgateway {
cmd := new(CheckNatgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckNatgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CheckNatgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckNatgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check natgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check natgateway '%s' done", extracted)
} else {
renv.Log().Verbose("check natgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckNatgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("natgateway"), nil
}
func (cmd *CheckNatgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckNetworkinterface(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckNetworkinterface {
cmd := new(CheckNetworkinterface)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckNetworkinterface) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CheckNetworkinterface) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckNetworkinterface) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check networkinterface: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check networkinterface '%s' done", extracted)
} else {
renv.Log().Verbose("check networkinterface done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckNetworkinterface) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("networkinterface"), nil
}
func (cmd *CheckNetworkinterface) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckScalinggroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckScalinggroup {
cmd := new(CheckScalinggroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckScalinggroup) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *CheckScalinggroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckScalinggroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check scalinggroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check scalinggroup '%s' done", extracted)
} else {
renv.Log().Verbose("check scalinggroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckScalinggroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalinggroup"), nil
}
func (cmd *CheckScalinggroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckSecuritygroup {
cmd := new(CheckSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CheckSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("check securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("securitygroup"), nil
}
func (cmd *CheckSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCheckVolume(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CheckVolume {
cmd := new(CheckVolume)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CheckVolume) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CheckVolume) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CheckVolume) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("check volume: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("check volume '%s' done", extracted)
} else {
renv.Log().Verbose("check volume done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CheckVolume) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("volume"), nil
}
func (cmd *CheckVolume) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCopyImage(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CopyImage {
cmd := new(CopyImage)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CopyImage) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CopyImage) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CopyImage) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CopyImageInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CopyImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CopyImage(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CopyImage call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("copy image: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("copy image '%s' done", extracted)
} else {
renv.Log().Verbose("copy image done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CopyImage) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CopyImageInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CopyImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CopyImage(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CopyImage call took %s", time.Since(start))
renv.Log().Verbose("dry run: copy image ok")
return fakeDryRunId("image"), nil
}
}
return nil, err
}
func (cmd *CopyImage) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCopySnapshot(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CopySnapshot {
cmd := new(CopySnapshot)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CopySnapshot) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CopySnapshot) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CopySnapshot) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CopySnapshotInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CopySnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CopySnapshot(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CopySnapshot call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("copy snapshot: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("copy snapshot '%s' done", extracted)
} else {
renv.Log().Verbose("copy snapshot done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CopySnapshot) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CopySnapshotInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CopySnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CopySnapshot(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CopySnapshot call took %s", time.Since(start))
renv.Log().Verbose("dry run: copy snapshot ok")
return fakeDryRunId("snapshot"), nil
}
}
return nil, err
}
func (cmd *CopySnapshot) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateAccesskey(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateAccesskey {
cmd := new(CreateAccesskey)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateAccesskey) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateAccesskey) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateAccesskey) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreateAccessKeyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreateAccessKeyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateAccessKey(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreateAccessKey call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create accesskey: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create accesskey '%s' done", extracted)
} else {
renv.Log().Verbose("create accesskey done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateAccesskey) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("accesskey"), nil
}
func (cmd *CreateAccesskey) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateAlarm {
cmd := new(CreateAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *CreateAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudwatch.PutMetricAlarmInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudwatch.PutMetricAlarmInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.PutMetricAlarm(context.Background(), input)
renv.Log().ExtraVerbosef("cloudwatch.PutMetricAlarm call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create alarm '%s' done", extracted)
} else {
renv.Log().Verbose("create alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *CreateAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateAppscalingpolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateAppscalingpolicy {
cmd := new(CreateAppscalingpolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = applicationautoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateAppscalingpolicy) SetApi(api *applicationautoscaling.Client) {
cmd.api = api
}
func (cmd *CreateAppscalingpolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateAppscalingpolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &applicationautoscaling.PutScalingPolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in applicationautoscaling.PutScalingPolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.PutScalingPolicy(context.Background(), input)
renv.Log().ExtraVerbosef("applicationautoscaling.PutScalingPolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create appscalingpolicy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create appscalingpolicy '%s' done", extracted)
} else {
renv.Log().Verbose("create appscalingpolicy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateAppscalingpolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("appscalingpolicy"), nil
}
func (cmd *CreateAppscalingpolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateAppscalingtarget(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateAppscalingtarget {
cmd := new(CreateAppscalingtarget)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = applicationautoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateAppscalingtarget) SetApi(api *applicationautoscaling.Client) {
cmd.api = api
}
func (cmd *CreateAppscalingtarget) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateAppscalingtarget) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &applicationautoscaling.RegisterScalableTargetInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in applicationautoscaling.RegisterScalableTargetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RegisterScalableTarget(context.Background(), input)
renv.Log().ExtraVerbosef("applicationautoscaling.RegisterScalableTarget call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create appscalingtarget: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create appscalingtarget '%s' done", extracted)
} else {
renv.Log().Verbose("create appscalingtarget done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateAppscalingtarget) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("appscalingtarget"), nil
}
func (cmd *CreateAppscalingtarget) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateBucket(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateBucket {
cmd := new(CreateBucket)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateBucket) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *CreateBucket) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateBucket) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &s3.CreateBucketInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in s3.CreateBucketInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateBucket(context.Background(), input)
renv.Log().ExtraVerbosef("s3.CreateBucket call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create bucket: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create bucket '%s' done", extracted)
} else {
renv.Log().Verbose("create bucket done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateBucket) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("bucket"), nil
}
func (cmd *CreateBucket) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateCertificate(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateCertificate {
cmd := new(CreateCertificate)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = acm.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateCertificate) SetApi(api *acm.Client) {
cmd.api = api
}
func (cmd *CreateCertificate) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateCertificate) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create certificate: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create certificate '%s' done", extracted)
} else {
renv.Log().Verbose("create certificate done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateCertificate) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("certificate"), nil
}
func (cmd *CreateCertificate) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateClassicLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateClassicLoadbalancer {
cmd := new(CreateClassicLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elb.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateClassicLoadbalancer) SetApi(api *elb.Client) {
cmd.api = api
}
func (cmd *CreateClassicLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateClassicLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elb.CreateLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elb.CreateLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elb.CreateLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create classicloadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create classicloadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("create classicloadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateClassicLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("classicloadbalancer"), nil
}
func (cmd *CreateClassicLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateContainercluster(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateContainercluster {
cmd := new(CreateContainercluster)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateContainercluster) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *CreateContainercluster) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateContainercluster) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ecs.CreateClusterInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ecs.CreateClusterInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateCluster(context.Background(), input)
renv.Log().ExtraVerbosef("ecs.CreateCluster call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create containercluster: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create containercluster '%s' done", extracted)
} else {
renv.Log().Verbose("create containercluster done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateContainercluster) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containercluster"), nil
}
func (cmd *CreateContainercluster) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateDatabase {
cmd := new(CreateDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *CreateDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create database '%s' done", extracted)
} else {
renv.Log().Verbose("create database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *CreateDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateDbsubnetgroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateDbsubnetgroup {
cmd := new(CreateDbsubnetgroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateDbsubnetgroup) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *CreateDbsubnetgroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateDbsubnetgroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.CreateDBSubnetGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.CreateDBSubnetGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateDBSubnetGroup(context.Background(), input)
renv.Log().ExtraVerbosef("rds.CreateDBSubnetGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create dbsubnetgroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create dbsubnetgroup '%s' done", extracted)
} else {
renv.Log().Verbose("create dbsubnetgroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateDbsubnetgroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("dbsubnetgroup"), nil
}
func (cmd *CreateDbsubnetgroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateDistribution(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateDistribution {
cmd := new(CreateDistribution)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudfront.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateDistribution) SetApi(api *cloudfront.Client) {
cmd.api = api
}
func (cmd *CreateDistribution) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateDistribution) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create distribution: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create distribution '%s' done", extracted)
} else {
renv.Log().Verbose("create distribution done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateDistribution) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("distribution"), nil
}
func (cmd *CreateDistribution) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateElasticip(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateElasticip {
cmd := new(CreateElasticip)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateElasticip) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateElasticip) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateElasticip) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.AllocateAddressInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AllocateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.AllocateAddress(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.AllocateAddress call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create elasticip: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create elasticip '%s' done", extracted)
} else {
renv.Log().Verbose("create elasticip done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateElasticip) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.AllocateAddressInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.AllocateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.AllocateAddress(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.AllocateAddress call took %s", time.Since(start))
renv.Log().Verbose("dry run: create elasticip ok")
return fakeDryRunId("elasticip"), nil
}
}
return nil, err
}
func (cmd *CreateElasticip) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateFunction(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateFunction {
cmd := new(CreateFunction)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = lambda.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateFunction) SetApi(api *lambda.Client) {
cmd.api = api
}
func (cmd *CreateFunction) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateFunction) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &lambda.CreateFunctionInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in lambda.CreateFunctionInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateFunction(context.Background(), input)
renv.Log().ExtraVerbosef("lambda.CreateFunction call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create function: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create function '%s' done", extracted)
} else {
renv.Log().Verbose("create function done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateFunction) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("function"), nil
}
func (cmd *CreateFunction) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateGroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateGroup {
cmd := new(CreateGroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateGroup) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateGroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateGroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreateGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreateGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateGroup(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreateGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create group: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create group '%s' done", extracted)
} else {
renv.Log().Verbose("create group done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateGroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("group"), nil
}
func (cmd *CreateGroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateImage(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateImage {
cmd := new(CreateImage)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateImage) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateImage) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateImage) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateImageInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateImage(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateImage call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create image: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create image '%s' done", extracted)
} else {
renv.Log().Verbose("create image done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateImage) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateImageInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateImage(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateImage call took %s", time.Since(start))
renv.Log().Verbose("dry run: create image ok")
return fakeDryRunId("image"), nil
}
}
return nil, err
}
func (cmd *CreateImage) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateInstance {
cmd := new(CreateInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.RunInstancesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.RunInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RunInstances(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.RunInstances call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create instance '%s' done", extracted)
} else {
renv.Log().Verbose("create instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.RunInstancesInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.RunInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.RunInstances(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.RunInstances call took %s", time.Since(start))
renv.Log().Verbose("dry run: create instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *CreateInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateInstanceprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateInstanceprofile {
cmd := new(CreateInstanceprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateInstanceprofile) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateInstanceprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateInstanceprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreateInstanceProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreateInstanceProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateInstanceProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreateInstanceProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create instanceprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create instanceprofile '%s' done", extracted)
} else {
renv.Log().Verbose("create instanceprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateInstanceprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instanceprofile"), nil
}
func (cmd *CreateInstanceprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateInternetgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateInternetgateway {
cmd := new(CreateInternetgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateInternetgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateInternetgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateInternetgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateInternetGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateInternetGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateInternetGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create internetgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create internetgateway '%s' done", extracted)
} else {
renv.Log().Verbose("create internetgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateInternetgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateInternetGatewayInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateInternetGateway(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateInternetGateway call took %s", time.Since(start))
renv.Log().Verbose("dry run: create internetgateway ok")
return fakeDryRunId("internetgateway"), nil
}
}
return nil, err
}
func (cmd *CreateInternetgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateKeypair(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateKeypair {
cmd := new(CreateKeypair)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateKeypair) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateKeypair) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateKeypair) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.ImportKeyPairInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ImportKeyPairInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ImportKeyPair(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.ImportKeyPair call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create keypair: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create keypair '%s' done", extracted)
} else {
renv.Log().Verbose("create keypair done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateKeypair) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("keypair"), nil
}
func (cmd *CreateKeypair) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateLaunchconfiguration(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateLaunchconfiguration {
cmd := new(CreateLaunchconfiguration)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateLaunchconfiguration) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *CreateLaunchconfiguration) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateLaunchconfiguration) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.CreateLaunchConfigurationInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.CreateLaunchConfigurationInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateLaunchConfiguration(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.CreateLaunchConfiguration call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create launchconfiguration: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create launchconfiguration '%s' done", extracted)
} else {
renv.Log().Verbose("create launchconfiguration done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateLaunchconfiguration) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("launchconfiguration"), nil
}
func (cmd *CreateLaunchconfiguration) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateListener(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateListener {
cmd := new(CreateListener)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateListener) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *CreateListener) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateListener) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.CreateListenerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.CreateListenerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateListener(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.CreateListener call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create listener: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create listener '%s' done", extracted)
} else {
renv.Log().Verbose("create listener done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateListener) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("listener"), nil
}
func (cmd *CreateListener) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateLoadbalancer {
cmd := new(CreateLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateLoadbalancer) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *CreateLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.CreateLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.CreateLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.CreateLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create loadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create loadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("create loadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loadbalancer"), nil
}
func (cmd *CreateLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateLoginprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateLoginprofile {
cmd := new(CreateLoginprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateLoginprofile) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateLoginprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateLoginprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreateLoginProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreateLoginProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateLoginProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreateLoginProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create loginprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create loginprofile '%s' done", extracted)
} else {
renv.Log().Verbose("create loginprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateLoginprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loginprofile"), nil
}
func (cmd *CreateLoginprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateMfadevice(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateMfadevice {
cmd := new(CreateMfadevice)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateMfadevice) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateMfadevice) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateMfadevice) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create mfadevice: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create mfadevice '%s' done", extracted)
} else {
renv.Log().Verbose("create mfadevice done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateMfadevice) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("mfadevice"), nil
}
func (cmd *CreateMfadevice) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateNatgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateNatgateway {
cmd := new(CreateNatgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateNatgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateNatgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateNatgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateNatGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateNatGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateNatGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateNatGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create natgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create natgateway '%s' done", extracted)
} else {
renv.Log().Verbose("create natgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateNatgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("natgateway"), nil
}
func (cmd *CreateNatgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateNetworkinterface(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateNetworkinterface {
cmd := new(CreateNetworkinterface)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateNetworkinterface) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateNetworkinterface) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateNetworkinterface) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateNetworkInterfaceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateNetworkInterface(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateNetworkInterface call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create networkinterface: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create networkinterface '%s' done", extracted)
} else {
renv.Log().Verbose("create networkinterface done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateNetworkinterface) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateNetworkInterfaceInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateNetworkInterface(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateNetworkInterface call took %s", time.Since(start))
renv.Log().Verbose("dry run: create networkinterface ok")
return fakeDryRunId("networkinterface"), nil
}
}
return nil, err
}
func (cmd *CreateNetworkinterface) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreatePolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreatePolicy {
cmd := new(CreatePolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreatePolicy) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreatePolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreatePolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreatePolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreatePolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreatePolicy(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreatePolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create policy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create policy '%s' done", extracted)
} else {
renv.Log().Verbose("create policy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreatePolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("policy"), nil
}
func (cmd *CreatePolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateQueue(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateQueue {
cmd := new(CreateQueue)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sqs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateQueue) SetApi(api *sqs.Client) {
cmd.api = api
}
func (cmd *CreateQueue) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateQueue) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sqs.CreateQueueInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sqs.CreateQueueInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateQueue(context.Background(), input)
renv.Log().ExtraVerbosef("sqs.CreateQueue call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create queue: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create queue '%s' done", extracted)
} else {
renv.Log().Verbose("create queue done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateQueue) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("queue"), nil
}
func (cmd *CreateQueue) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateRecord(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateRecord {
cmd := new(CreateRecord)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = route53.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateRecord) SetApi(api *route53.Client) {
cmd.api = api
}
func (cmd *CreateRecord) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateRecord) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create record: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create record '%s' done", extracted)
} else {
renv.Log().Verbose("create record done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateRecord) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("record"), nil
}
func (cmd *CreateRecord) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateRepository(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateRepository {
cmd := new(CreateRepository)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecr.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateRepository) SetApi(api *ecr.Client) {
cmd.api = api
}
func (cmd *CreateRepository) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateRepository) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ecr.CreateRepositoryInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ecr.CreateRepositoryInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateRepository(context.Background(), input)
renv.Log().ExtraVerbosef("ecr.CreateRepository call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create repository: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create repository '%s' done", extracted)
} else {
renv.Log().Verbose("create repository done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateRepository) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("repository"), nil
}
func (cmd *CreateRepository) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateRole(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateRole {
cmd := new(CreateRole)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateRole) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateRole) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateRole) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create role: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create role '%s' done", extracted)
} else {
renv.Log().Verbose("create role done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateRole) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("role"), nil
}
func (cmd *CreateRole) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateRoute(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateRoute {
cmd := new(CreateRoute)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateRoute) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateRoute) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateRoute) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateRouteInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateRouteInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateRoute(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateRoute call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create route: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create route '%s' done", extracted)
} else {
renv.Log().Verbose("create route done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateRoute) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateRouteInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateRouteInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateRoute(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateRoute call took %s", time.Since(start))
renv.Log().Verbose("dry run: create route ok")
return fakeDryRunId("route"), nil
}
}
return nil, err
}
func (cmd *CreateRoute) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateRoutetable(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateRoutetable {
cmd := new(CreateRoutetable)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateRoutetable) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateRoutetable) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateRoutetable) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateRouteTableInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateRouteTable(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateRouteTable call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create routetable: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create routetable '%s' done", extracted)
} else {
renv.Log().Verbose("create routetable done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateRoutetable) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateRouteTableInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateRouteTable(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateRouteTable call took %s", time.Since(start))
renv.Log().Verbose("dry run: create routetable ok")
return fakeDryRunId("routetable"), nil
}
}
return nil, err
}
func (cmd *CreateRoutetable) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateS3object(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateS3object {
cmd := new(CreateS3object)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateS3object) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *CreateS3object) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateS3object) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create s3object: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create s3object '%s' done", extracted)
} else {
renv.Log().Verbose("create s3object done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateS3object) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("s3object"), nil
}
func (cmd *CreateS3object) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateScalinggroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateScalinggroup {
cmd := new(CreateScalinggroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateScalinggroup) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *CreateScalinggroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateScalinggroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.CreateAutoScalingGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.CreateAutoScalingGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateAutoScalingGroup(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.CreateAutoScalingGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create scalinggroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create scalinggroup '%s' done", extracted)
} else {
renv.Log().Verbose("create scalinggroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateScalinggroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalinggroup"), nil
}
func (cmd *CreateScalinggroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateScalingpolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateScalingpolicy {
cmd := new(CreateScalingpolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateScalingpolicy) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *CreateScalingpolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateScalingpolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.PutScalingPolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.PutScalingPolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.PutScalingPolicy(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.PutScalingPolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create scalingpolicy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create scalingpolicy '%s' done", extracted)
} else {
renv.Log().Verbose("create scalingpolicy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateScalingpolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalingpolicy"), nil
}
func (cmd *CreateScalingpolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateSecuritygroup {
cmd := new(CreateSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateSecurityGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSecurityGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateSecurityGroup(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateSecurityGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("create securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateSecurityGroupInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSecurityGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateSecurityGroup(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateSecurityGroup call took %s", time.Since(start))
renv.Log().Verbose("dry run: create securitygroup ok")
return fakeDryRunId("securitygroup"), nil
}
}
return nil, err
}
func (cmd *CreateSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateSnapshot(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateSnapshot {
cmd := new(CreateSnapshot)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateSnapshot) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateSnapshot) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateSnapshot) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateSnapshotInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateSnapshot(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateSnapshot call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create snapshot: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create snapshot '%s' done", extracted)
} else {
renv.Log().Verbose("create snapshot done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateSnapshot) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateSnapshotInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateSnapshot(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateSnapshot call took %s", time.Since(start))
renv.Log().Verbose("dry run: create snapshot ok")
return fakeDryRunId("snapshot"), nil
}
}
return nil, err
}
func (cmd *CreateSnapshot) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateStack(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateStack {
cmd := new(CreateStack)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudformation.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateStack) SetApi(api *cloudformation.Client) {
cmd.api = api
}
func (cmd *CreateStack) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateStack) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudformation.CreateStackInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudformation.CreateStackInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateStack(context.Background(), input)
renv.Log().ExtraVerbosef("cloudformation.CreateStack call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create stack: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create stack '%s' done", extracted)
} else {
renv.Log().Verbose("create stack done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateStack) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("stack"), nil
}
func (cmd *CreateStack) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateSubnet(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateSubnet {
cmd := new(CreateSubnet)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateSubnet) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateSubnet) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateSubnet) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateSubnetInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSubnetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateSubnet(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateSubnet call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create subnet: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create subnet '%s' done", extracted)
} else {
renv.Log().Verbose("create subnet done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateSubnet) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateSubnetInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateSubnetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateSubnet(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateSubnet call took %s", time.Since(start))
renv.Log().Verbose("dry run: create subnet ok")
return fakeDryRunId("subnet"), nil
}
}
return nil, err
}
func (cmd *CreateSubnet) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateSubscription(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateSubscription {
cmd := new(CreateSubscription)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sns.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateSubscription) SetApi(api *sns.Client) {
cmd.api = api
}
func (cmd *CreateSubscription) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateSubscription) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sns.SubscribeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sns.SubscribeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.Subscribe(context.Background(), input)
renv.Log().ExtraVerbosef("sns.Subscribe call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create subscription: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create subscription '%s' done", extracted)
} else {
renv.Log().Verbose("create subscription done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateSubscription) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("subscription"), nil
}
func (cmd *CreateSubscription) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateTag(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateTag {
cmd := new(CreateTag)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateTag) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateTag) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateTag) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create tag: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create tag '%s' done", extracted)
} else {
renv.Log().Verbose("create tag done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateTag) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateTargetgroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateTargetgroup {
cmd := new(CreateTargetgroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateTargetgroup) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *CreateTargetgroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateTargetgroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.CreateTargetGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.CreateTargetGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateTargetGroup(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.CreateTargetGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create targetgroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create targetgroup '%s' done", extracted)
} else {
renv.Log().Verbose("create targetgroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateTargetgroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("targetgroup"), nil
}
func (cmd *CreateTargetgroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateTopic(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateTopic {
cmd := new(CreateTopic)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sns.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateTopic) SetApi(api *sns.Client) {
cmd.api = api
}
func (cmd *CreateTopic) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateTopic) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sns.CreateTopicInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sns.CreateTopicInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateTopic(context.Background(), input)
renv.Log().ExtraVerbosef("sns.CreateTopic call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create topic: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create topic '%s' done", extracted)
} else {
renv.Log().Verbose("create topic done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateTopic) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("topic"), nil
}
func (cmd *CreateTopic) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateUser(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateUser {
cmd := new(CreateUser)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateUser) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *CreateUser) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateUser) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreateUserInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreateUserInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateUser(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreateUser call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create user: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create user '%s' done", extracted)
} else {
renv.Log().Verbose("create user done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateUser) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("user"), nil
}
func (cmd *CreateUser) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateVolume(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateVolume {
cmd := new(CreateVolume)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateVolume) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateVolume) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateVolume) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateVolumeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateVolume(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateVolume call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create volume: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create volume '%s' done", extracted)
} else {
renv.Log().Verbose("create volume done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateVolume) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateVolumeInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateVolume(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateVolume call took %s", time.Since(start))
renv.Log().Verbose("dry run: create volume ok")
return fakeDryRunId("volume"), nil
}
}
return nil, err
}
func (cmd *CreateVolume) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateVpc(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateVpc {
cmd := new(CreateVpc)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateVpc) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *CreateVpc) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateVpc) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.CreateVpcInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateVpcInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateVpc(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.CreateVpc call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create vpc: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create vpc '%s' done", extracted)
} else {
renv.Log().Verbose("create vpc done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateVpc) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.CreateVpcInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateVpcInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.CreateVpc(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.CreateVpc call took %s", time.Since(start))
renv.Log().Verbose("dry run: create vpc ok")
return fakeDryRunId("vpc"), nil
}
}
return nil, err
}
func (cmd *CreateVpc) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewCreateZone(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *CreateZone {
cmd := new(CreateZone)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = route53.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *CreateZone) SetApi(api *route53.Client) {
cmd.api = api
}
func (cmd *CreateZone) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *CreateZone) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &route53.CreateHostedZoneInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in route53.CreateHostedZoneInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreateHostedZone(context.Background(), input)
renv.Log().ExtraVerbosef("route53.CreateHostedZone call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("create zone: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("create zone '%s' done", extracted)
} else {
renv.Log().Verbose("create zone done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *CreateZone) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("zone"), nil
}
func (cmd *CreateZone) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteAccesskey(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteAccesskey {
cmd := new(DeleteAccesskey)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteAccesskey) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteAccesskey) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteAccesskey) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteAccessKeyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteAccessKeyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteAccessKey(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteAccessKey call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete accesskey: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete accesskey '%s' done", extracted)
} else {
renv.Log().Verbose("delete accesskey done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteAccesskey) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("accesskey"), nil
}
func (cmd *DeleteAccesskey) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteAlarm {
cmd := new(DeleteAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *DeleteAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudwatch.DeleteAlarmsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudwatch.DeleteAlarmsInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteAlarms(context.Background(), input)
renv.Log().ExtraVerbosef("cloudwatch.DeleteAlarms call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete alarm '%s' done", extracted)
} else {
renv.Log().Verbose("delete alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *DeleteAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteAppscalingpolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteAppscalingpolicy {
cmd := new(DeleteAppscalingpolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = applicationautoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteAppscalingpolicy) SetApi(api *applicationautoscaling.Client) {
cmd.api = api
}
func (cmd *DeleteAppscalingpolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteAppscalingpolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &applicationautoscaling.DeleteScalingPolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in applicationautoscaling.DeleteScalingPolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteScalingPolicy(context.Background(), input)
renv.Log().ExtraVerbosef("applicationautoscaling.DeleteScalingPolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete appscalingpolicy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete appscalingpolicy '%s' done", extracted)
} else {
renv.Log().Verbose("delete appscalingpolicy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteAppscalingpolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("appscalingpolicy"), nil
}
func (cmd *DeleteAppscalingpolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteAppscalingtarget(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteAppscalingtarget {
cmd := new(DeleteAppscalingtarget)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = applicationautoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteAppscalingtarget) SetApi(api *applicationautoscaling.Client) {
cmd.api = api
}
func (cmd *DeleteAppscalingtarget) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteAppscalingtarget) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &applicationautoscaling.DeregisterScalableTargetInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in applicationautoscaling.DeregisterScalableTargetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeregisterScalableTarget(context.Background(), input)
renv.Log().ExtraVerbosef("applicationautoscaling.DeregisterScalableTarget call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete appscalingtarget: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete appscalingtarget '%s' done", extracted)
} else {
renv.Log().Verbose("delete appscalingtarget done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteAppscalingtarget) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("appscalingtarget"), nil
}
func (cmd *DeleteAppscalingtarget) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteBucket(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteBucket {
cmd := new(DeleteBucket)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteBucket) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *DeleteBucket) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteBucket) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &s3.DeleteBucketInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in s3.DeleteBucketInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteBucket(context.Background(), input)
renv.Log().ExtraVerbosef("s3.DeleteBucket call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete bucket: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete bucket '%s' done", extracted)
} else {
renv.Log().Verbose("delete bucket done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteBucket) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("bucket"), nil
}
func (cmd *DeleteBucket) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteCertificate(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteCertificate {
cmd := new(DeleteCertificate)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = acm.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteCertificate) SetApi(api *acm.Client) {
cmd.api = api
}
func (cmd *DeleteCertificate) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteCertificate) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &acm.DeleteCertificateInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in acm.DeleteCertificateInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteCertificate(context.Background(), input)
renv.Log().ExtraVerbosef("acm.DeleteCertificate call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete certificate: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete certificate '%s' done", extracted)
} else {
renv.Log().Verbose("delete certificate done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteCertificate) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("certificate"), nil
}
func (cmd *DeleteCertificate) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteClassicLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteClassicLoadbalancer {
cmd := new(DeleteClassicLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elb.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteClassicLoadbalancer) SetApi(api *elb.Client) {
cmd.api = api
}
func (cmd *DeleteClassicLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteClassicLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elb.DeleteLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elb.DeleteLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elb.DeleteLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete classicloadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete classicloadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("delete classicloadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteClassicLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("classicloadbalancer"), nil
}
func (cmd *DeleteClassicLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteContainercluster(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteContainercluster {
cmd := new(DeleteContainercluster)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteContainercluster) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *DeleteContainercluster) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteContainercluster) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ecs.DeleteClusterInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ecs.DeleteClusterInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteCluster(context.Background(), input)
renv.Log().ExtraVerbosef("ecs.DeleteCluster call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete containercluster: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete containercluster '%s' done", extracted)
} else {
renv.Log().Verbose("delete containercluster done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteContainercluster) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containercluster"), nil
}
func (cmd *DeleteContainercluster) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteContainertask {
cmd := new(DeleteContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *DeleteContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete containertask '%s' done", extracted)
} else {
renv.Log().Verbose("delete containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteDatabase {
cmd := new(DeleteDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *DeleteDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.DeleteDBInstanceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.DeleteDBInstanceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteDBInstance(context.Background(), input)
renv.Log().ExtraVerbosef("rds.DeleteDBInstance call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete database '%s' done", extracted)
} else {
renv.Log().Verbose("delete database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *DeleteDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteDbsubnetgroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteDbsubnetgroup {
cmd := new(DeleteDbsubnetgroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteDbsubnetgroup) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *DeleteDbsubnetgroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteDbsubnetgroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.DeleteDBSubnetGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.DeleteDBSubnetGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteDBSubnetGroup(context.Background(), input)
renv.Log().ExtraVerbosef("rds.DeleteDBSubnetGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete dbsubnetgroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete dbsubnetgroup '%s' done", extracted)
} else {
renv.Log().Verbose("delete dbsubnetgroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteDbsubnetgroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("dbsubnetgroup"), nil
}
func (cmd *DeleteDbsubnetgroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteDistribution(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteDistribution {
cmd := new(DeleteDistribution)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudfront.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteDistribution) SetApi(api *cloudfront.Client) {
cmd.api = api
}
func (cmd *DeleteDistribution) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteDistribution) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete distribution: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete distribution '%s' done", extracted)
} else {
renv.Log().Verbose("delete distribution done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteDistribution) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("distribution"), nil
}
func (cmd *DeleteDistribution) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteElasticip(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteElasticip {
cmd := new(DeleteElasticip)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteElasticip) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteElasticip) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteElasticip) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.ReleaseAddressInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ReleaseAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ReleaseAddress(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.ReleaseAddress call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete elasticip: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete elasticip '%s' done", extracted)
} else {
renv.Log().Verbose("delete elasticip done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteElasticip) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.ReleaseAddressInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ReleaseAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.ReleaseAddress(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.ReleaseAddress call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete elasticip ok")
return fakeDryRunId("elasticip"), nil
}
}
return nil, err
}
func (cmd *DeleteElasticip) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteFunction(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteFunction {
cmd := new(DeleteFunction)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = lambda.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteFunction) SetApi(api *lambda.Client) {
cmd.api = api
}
func (cmd *DeleteFunction) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteFunction) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &lambda.DeleteFunctionInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in lambda.DeleteFunctionInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteFunction(context.Background(), input)
renv.Log().ExtraVerbosef("lambda.DeleteFunction call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete function: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete function '%s' done", extracted)
} else {
renv.Log().Verbose("delete function done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteFunction) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("function"), nil
}
func (cmd *DeleteFunction) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteGroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteGroup {
cmd := new(DeleteGroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteGroup) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteGroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteGroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteGroup(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete group: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete group '%s' done", extracted)
} else {
renv.Log().Verbose("delete group done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteGroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("group"), nil
}
func (cmd *DeleteGroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteImage(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteImage {
cmd := new(DeleteImage)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteImage) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteImage) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteImage) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete image: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete image '%s' done", extracted)
} else {
renv.Log().Verbose("delete image done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteImage) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteInstance {
cmd := new(DeleteInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.TerminateInstancesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.TerminateInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.TerminateInstances(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.TerminateInstances call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete instance '%s' done", extracted)
} else {
renv.Log().Verbose("delete instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.TerminateInstancesInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.TerminateInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.TerminateInstances(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.TerminateInstances call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *DeleteInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteInstanceprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteInstanceprofile {
cmd := new(DeleteInstanceprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteInstanceprofile) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteInstanceprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteInstanceprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteInstanceProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteInstanceProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteInstanceProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteInstanceProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete instanceprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete instanceprofile '%s' done", extracted)
} else {
renv.Log().Verbose("delete instanceprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteInstanceprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instanceprofile"), nil
}
func (cmd *DeleteInstanceprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteInternetgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteInternetgateway {
cmd := new(DeleteInternetgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteInternetgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteInternetgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteInternetgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteInternetGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteInternetGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteInternetGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete internetgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete internetgateway '%s' done", extracted)
} else {
renv.Log().Verbose("delete internetgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteInternetgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteInternetGatewayInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteInternetGateway(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteInternetGateway call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete internetgateway ok")
return fakeDryRunId("internetgateway"), nil
}
}
return nil, err
}
func (cmd *DeleteInternetgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteKeypair(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteKeypair {
cmd := new(DeleteKeypair)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteKeypair) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteKeypair) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteKeypair) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteKeyPairInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteKeyPairInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteKeyPair(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteKeyPair call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete keypair: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete keypair '%s' done", extracted)
} else {
renv.Log().Verbose("delete keypair done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteKeypair) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteKeyPairInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteKeyPairInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteKeyPair(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteKeyPair call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete keypair ok")
return fakeDryRunId("keypair"), nil
}
}
return nil, err
}
func (cmd *DeleteKeypair) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteLaunchconfiguration(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteLaunchconfiguration {
cmd := new(DeleteLaunchconfiguration)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteLaunchconfiguration) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *DeleteLaunchconfiguration) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteLaunchconfiguration) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.DeleteLaunchConfigurationInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.DeleteLaunchConfigurationInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteLaunchConfiguration(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.DeleteLaunchConfiguration call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete launchconfiguration: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete launchconfiguration '%s' done", extracted)
} else {
renv.Log().Verbose("delete launchconfiguration done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteLaunchconfiguration) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("launchconfiguration"), nil
}
func (cmd *DeleteLaunchconfiguration) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteListener(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteListener {
cmd := new(DeleteListener)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteListener) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *DeleteListener) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteListener) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.DeleteListenerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.DeleteListenerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteListener(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.DeleteListener call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete listener: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete listener '%s' done", extracted)
} else {
renv.Log().Verbose("delete listener done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteListener) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("listener"), nil
}
func (cmd *DeleteListener) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteLoadbalancer {
cmd := new(DeleteLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteLoadbalancer) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *DeleteLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.DeleteLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.DeleteLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.DeleteLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete loadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete loadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("delete loadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loadbalancer"), nil
}
func (cmd *DeleteLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteLoginprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteLoginprofile {
cmd := new(DeleteLoginprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteLoginprofile) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteLoginprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteLoginprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteLoginProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteLoginProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteLoginProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteLoginProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete loginprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete loginprofile '%s' done", extracted)
} else {
renv.Log().Verbose("delete loginprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteLoginprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loginprofile"), nil
}
func (cmd *DeleteLoginprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteMfadevice(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteMfadevice {
cmd := new(DeleteMfadevice)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteMfadevice) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteMfadevice) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteMfadevice) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteVirtualMFADeviceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteVirtualMFADeviceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteVirtualMFADevice(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteVirtualMFADevice call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete mfadevice: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete mfadevice '%s' done", extracted)
} else {
renv.Log().Verbose("delete mfadevice done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteMfadevice) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("mfadevice"), nil
}
func (cmd *DeleteMfadevice) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteNatgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteNatgateway {
cmd := new(DeleteNatgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteNatgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteNatgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteNatgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteNatGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteNatGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteNatGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteNatGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete natgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete natgateway '%s' done", extracted)
} else {
renv.Log().Verbose("delete natgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteNatgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("natgateway"), nil
}
func (cmd *DeleteNatgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteNetworkinterface(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteNetworkinterface {
cmd := new(DeleteNetworkinterface)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteNetworkinterface) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteNetworkinterface) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteNetworkinterface) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteNetworkInterfaceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteNetworkInterface(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteNetworkInterface call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete networkinterface: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete networkinterface '%s' done", extracted)
} else {
renv.Log().Verbose("delete networkinterface done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteNetworkinterface) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteNetworkInterfaceInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteNetworkInterfaceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteNetworkInterface(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteNetworkInterface call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete networkinterface ok")
return fakeDryRunId("networkinterface"), nil
}
}
return nil, err
}
func (cmd *DeleteNetworkinterface) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeletePolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeletePolicy {
cmd := new(DeletePolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeletePolicy) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeletePolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeletePolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeletePolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeletePolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeletePolicy(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeletePolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete policy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete policy '%s' done", extracted)
} else {
renv.Log().Verbose("delete policy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeletePolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("policy"), nil
}
func (cmd *DeletePolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteQueue(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteQueue {
cmd := new(DeleteQueue)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sqs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteQueue) SetApi(api *sqs.Client) {
cmd.api = api
}
func (cmd *DeleteQueue) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteQueue) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sqs.DeleteQueueInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sqs.DeleteQueueInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteQueue(context.Background(), input)
renv.Log().ExtraVerbosef("sqs.DeleteQueue call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete queue: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete queue '%s' done", extracted)
} else {
renv.Log().Verbose("delete queue done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteQueue) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("queue"), nil
}
func (cmd *DeleteQueue) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteRecord(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteRecord {
cmd := new(DeleteRecord)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = route53.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteRecord) SetApi(api *route53.Client) {
cmd.api = api
}
func (cmd *DeleteRecord) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteRecord) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete record: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete record '%s' done", extracted)
} else {
renv.Log().Verbose("delete record done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteRecord) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("record"), nil
}
func (cmd *DeleteRecord) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteRepository(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteRepository {
cmd := new(DeleteRepository)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecr.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteRepository) SetApi(api *ecr.Client) {
cmd.api = api
}
func (cmd *DeleteRepository) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteRepository) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ecr.DeleteRepositoryInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ecr.DeleteRepositoryInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteRepository(context.Background(), input)
renv.Log().ExtraVerbosef("ecr.DeleteRepository call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete repository: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete repository '%s' done", extracted)
} else {
renv.Log().Verbose("delete repository done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteRepository) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("repository"), nil
}
func (cmd *DeleteRepository) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteRole(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteRole {
cmd := new(DeleteRole)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteRole) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteRole) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteRole) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete role: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete role '%s' done", extracted)
} else {
renv.Log().Verbose("delete role done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteRole) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("role"), nil
}
func (cmd *DeleteRole) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteRoute(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteRoute {
cmd := new(DeleteRoute)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteRoute) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteRoute) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteRoute) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteRouteInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteRouteInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteRoute(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteRoute call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete route: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete route '%s' done", extracted)
} else {
renv.Log().Verbose("delete route done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteRoute) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteRouteInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteRouteInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteRoute(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteRoute call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete route ok")
return fakeDryRunId("route"), nil
}
}
return nil, err
}
func (cmd *DeleteRoute) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteRoutetable(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteRoutetable {
cmd := new(DeleteRoutetable)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteRoutetable) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteRoutetable) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteRoutetable) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteRouteTableInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteRouteTable(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteRouteTable call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete routetable: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete routetable '%s' done", extracted)
} else {
renv.Log().Verbose("delete routetable done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteRoutetable) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteRouteTableInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteRouteTable(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteRouteTable call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete routetable ok")
return fakeDryRunId("routetable"), nil
}
}
return nil, err
}
func (cmd *DeleteRoutetable) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteS3object(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteS3object {
cmd := new(DeleteS3object)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteS3object) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *DeleteS3object) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteS3object) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &s3.DeleteObjectInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in s3.DeleteObjectInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteObject(context.Background(), input)
renv.Log().ExtraVerbosef("s3.DeleteObject call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete s3object: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete s3object '%s' done", extracted)
} else {
renv.Log().Verbose("delete s3object done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteS3object) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("s3object"), nil
}
func (cmd *DeleteS3object) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteScalinggroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteScalinggroup {
cmd := new(DeleteScalinggroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteScalinggroup) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *DeleteScalinggroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteScalinggroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.DeleteAutoScalingGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.DeleteAutoScalingGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteAutoScalingGroup(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.DeleteAutoScalingGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete scalinggroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete scalinggroup '%s' done", extracted)
} else {
renv.Log().Verbose("delete scalinggroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteScalinggroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalinggroup"), nil
}
func (cmd *DeleteScalinggroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteScalingpolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteScalingpolicy {
cmd := new(DeleteScalingpolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteScalingpolicy) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *DeleteScalingpolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteScalingpolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.DeletePolicyInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.DeletePolicyInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeletePolicy(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.DeletePolicy call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete scalingpolicy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete scalingpolicy '%s' done", extracted)
} else {
renv.Log().Verbose("delete scalingpolicy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteScalingpolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalingpolicy"), nil
}
func (cmd *DeleteScalingpolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteSecuritygroup {
cmd := new(DeleteSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteSecurityGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSecurityGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteSecurityGroup(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteSecurityGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("delete securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteSecurityGroupInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSecurityGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteSecurityGroup(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteSecurityGroup call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete securitygroup ok")
return fakeDryRunId("securitygroup"), nil
}
}
return nil, err
}
func (cmd *DeleteSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteSnapshot(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteSnapshot {
cmd := new(DeleteSnapshot)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteSnapshot) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteSnapshot) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteSnapshot) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteSnapshotInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteSnapshot(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteSnapshot call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete snapshot: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete snapshot '%s' done", extracted)
} else {
renv.Log().Verbose("delete snapshot done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteSnapshot) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteSnapshotInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSnapshotInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteSnapshot(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteSnapshot call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete snapshot ok")
return fakeDryRunId("snapshot"), nil
}
}
return nil, err
}
func (cmd *DeleteSnapshot) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteStack(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteStack {
cmd := new(DeleteStack)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudformation.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteStack) SetApi(api *cloudformation.Client) {
cmd.api = api
}
func (cmd *DeleteStack) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteStack) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudformation.DeleteStackInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudformation.DeleteStackInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteStack(context.Background(), input)
renv.Log().ExtraVerbosef("cloudformation.DeleteStack call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete stack: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete stack '%s' done", extracted)
} else {
renv.Log().Verbose("delete stack done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteStack) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("stack"), nil
}
func (cmd *DeleteStack) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteSubnet(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteSubnet {
cmd := new(DeleteSubnet)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteSubnet) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteSubnet) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteSubnet) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteSubnetInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSubnetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteSubnet(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteSubnet call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete subnet: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete subnet '%s' done", extracted)
} else {
renv.Log().Verbose("delete subnet done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteSubnet) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteSubnetInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteSubnetInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteSubnet(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteSubnet call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete subnet ok")
return fakeDryRunId("subnet"), nil
}
}
return nil, err
}
func (cmd *DeleteSubnet) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteSubscription(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteSubscription {
cmd := new(DeleteSubscription)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sns.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteSubscription) SetApi(api *sns.Client) {
cmd.api = api
}
func (cmd *DeleteSubscription) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteSubscription) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sns.UnsubscribeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sns.UnsubscribeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.Unsubscribe(context.Background(), input)
renv.Log().ExtraVerbosef("sns.Unsubscribe call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete subscription: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete subscription '%s' done", extracted)
} else {
renv.Log().Verbose("delete subscription done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteSubscription) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("subscription"), nil
}
func (cmd *DeleteSubscription) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteTag(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteTag {
cmd := new(DeleteTag)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteTag) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteTag) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteTag) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete tag: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete tag '%s' done", extracted)
} else {
renv.Log().Verbose("delete tag done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteTag) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteTargetgroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteTargetgroup {
cmd := new(DeleteTargetgroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteTargetgroup) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *DeleteTargetgroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteTargetgroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.DeleteTargetGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.DeleteTargetGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteTargetGroup(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.DeleteTargetGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete targetgroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete targetgroup '%s' done", extracted)
} else {
renv.Log().Verbose("delete targetgroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteTargetgroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("targetgroup"), nil
}
func (cmd *DeleteTargetgroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteTopic(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteTopic {
cmd := new(DeleteTopic)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = sns.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteTopic) SetApi(api *sns.Client) {
cmd.api = api
}
func (cmd *DeleteTopic) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteTopic) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &sns.DeleteTopicInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in sns.DeleteTopicInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteTopic(context.Background(), input)
renv.Log().ExtraVerbosef("sns.DeleteTopic call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete topic: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete topic '%s' done", extracted)
} else {
renv.Log().Verbose("delete topic done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteTopic) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("topic"), nil
}
func (cmd *DeleteTopic) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteUser(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteUser {
cmd := new(DeleteUser)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteUser) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DeleteUser) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteUser) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeleteUserInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeleteUserInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteUser(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeleteUser call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete user: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete user '%s' done", extracted)
} else {
renv.Log().Verbose("delete user done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteUser) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("user"), nil
}
func (cmd *DeleteUser) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteVolume(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteVolume {
cmd := new(DeleteVolume)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteVolume) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteVolume) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteVolume) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteVolumeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteVolume(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteVolume call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete volume: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete volume '%s' done", extracted)
} else {
renv.Log().Verbose("delete volume done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteVolume) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteVolumeInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteVolume(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteVolume call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete volume ok")
return fakeDryRunId("volume"), nil
}
}
return nil, err
}
func (cmd *DeleteVolume) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteVpc(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteVpc {
cmd := new(DeleteVpc)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteVpc) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DeleteVpc) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteVpc) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DeleteVpcInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteVpcInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteVpc(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DeleteVpc call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete vpc: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete vpc '%s' done", extracted)
} else {
renv.Log().Verbose("delete vpc done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteVpc) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteVpcInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteVpcInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DeleteVpc(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DeleteVpc call took %s", time.Since(start))
renv.Log().Verbose("dry run: delete vpc ok")
return fakeDryRunId("vpc"), nil
}
}
return nil, err
}
func (cmd *DeleteVpc) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDeleteZone(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DeleteZone {
cmd := new(DeleteZone)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = route53.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DeleteZone) SetApi(api *route53.Client) {
cmd.api = api
}
func (cmd *DeleteZone) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DeleteZone) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &route53.DeleteHostedZoneInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in route53.DeleteHostedZoneInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeleteHostedZone(context.Background(), input)
renv.Log().ExtraVerbosef("route53.DeleteHostedZone call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("delete zone: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("delete zone '%s' done", extracted)
} else {
renv.Log().Verbose("delete zone done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DeleteZone) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("zone"), nil
}
func (cmd *DeleteZone) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachAlarm {
cmd := new(DetachAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *DetachAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach alarm '%s' done", extracted)
} else {
renv.Log().Verbose("detach alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *DetachAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachClassicLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachClassicLoadbalancer {
cmd := new(DetachClassicLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elb.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachClassicLoadbalancer) SetApi(api *elb.Client) {
cmd.api = api
}
func (cmd *DetachClassicLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachClassicLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elb.DeregisterInstancesFromLoadBalancerInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elb.DeregisterInstancesFromLoadBalancerInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeregisterInstancesFromLoadBalancer(context.Background(), input)
renv.Log().ExtraVerbosef("elb.DeregisterInstancesFromLoadBalancer call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach classicloadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach classicloadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("detach classicloadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachClassicLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("classicloadbalancer"), nil
}
func (cmd *DetachClassicLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachContainertask {
cmd := new(DetachContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *DetachContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach containertask '%s' done", extracted)
} else {
renv.Log().Verbose("detach containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containertask"), nil
}
func (cmd *DetachContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachElasticip(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachElasticip {
cmd := new(DetachElasticip)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachElasticip) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachElasticip) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachElasticip) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DisassociateAddressInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DisassociateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DisassociateAddress(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DisassociateAddress call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach elasticip: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach elasticip '%s' done", extracted)
} else {
renv.Log().Verbose("detach elasticip done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachElasticip) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DisassociateAddressInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DisassociateAddressInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DisassociateAddress(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DisassociateAddress call took %s", time.Since(start))
renv.Log().Verbose("dry run: detach elasticip ok")
return fakeDryRunId("elasticip"), nil
}
}
return nil, err
}
func (cmd *DetachElasticip) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachInstance {
cmd := new(DetachInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachInstance) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *DetachInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elbv2.DeregisterTargetsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elbv2.DeregisterTargetsInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeregisterTargets(context.Background(), input)
renv.Log().ExtraVerbosef("elbv2.DeregisterTargets call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach instance '%s' done", extracted)
} else {
renv.Log().Verbose("detach instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instance"), nil
}
func (cmd *DetachInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachInstanceprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachInstanceprofile {
cmd := new(DetachInstanceprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachInstanceprofile) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachInstanceprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachInstanceprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach instanceprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach instanceprofile '%s' done", extracted)
} else {
renv.Log().Verbose("detach instanceprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachInstanceprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("instanceprofile"), nil
}
func (cmd *DetachInstanceprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachInternetgateway(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachInternetgateway {
cmd := new(DetachInternetgateway)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachInternetgateway) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachInternetgateway) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachInternetgateway) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DetachInternetGatewayInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DetachInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DetachInternetGateway(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DetachInternetGateway call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach internetgateway: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach internetgateway '%s' done", extracted)
} else {
renv.Log().Verbose("detach internetgateway done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachInternetgateway) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DetachInternetGatewayInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DetachInternetGatewayInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DetachInternetGateway(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DetachInternetGateway call took %s", time.Since(start))
renv.Log().Verbose("dry run: detach internetgateway ok")
return fakeDryRunId("internetgateway"), nil
}
}
return nil, err
}
func (cmd *DetachInternetgateway) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachMfadevice(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachMfadevice {
cmd := new(DetachMfadevice)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachMfadevice) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DetachMfadevice) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachMfadevice) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.DeactivateMFADeviceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.DeactivateMFADeviceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DeactivateMFADevice(context.Background(), input)
renv.Log().ExtraVerbosef("iam.DeactivateMFADevice call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach mfadevice: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach mfadevice '%s' done", extracted)
} else {
renv.Log().Verbose("detach mfadevice done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachMfadevice) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("mfadevice"), nil
}
func (cmd *DetachMfadevice) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachNetworkinterface(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachNetworkinterface {
cmd := new(DetachNetworkinterface)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachNetworkinterface) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachNetworkinterface) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachNetworkinterface) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach networkinterface: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach networkinterface '%s' done", extracted)
} else {
renv.Log().Verbose("detach networkinterface done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachNetworkinterface) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachPolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachPolicy {
cmd := new(DetachPolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachPolicy) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DetachPolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachPolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach policy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach policy '%s' done", extracted)
} else {
renv.Log().Verbose("detach policy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachPolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("policy"), nil
}
func (cmd *DetachPolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachRole(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachRole {
cmd := new(DetachRole)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachRole) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DetachRole) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachRole) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.RemoveRoleFromInstanceProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.RemoveRoleFromInstanceProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RemoveRoleFromInstanceProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.RemoveRoleFromInstanceProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach role: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach role '%s' done", extracted)
} else {
renv.Log().Verbose("detach role done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachRole) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("role"), nil
}
func (cmd *DetachRole) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachRoutetable(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachRoutetable {
cmd := new(DetachRoutetable)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachRoutetable) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachRoutetable) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachRoutetable) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DisassociateRouteTableInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DisassociateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DisassociateRouteTable(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DisassociateRouteTable call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach routetable: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach routetable '%s' done", extracted)
} else {
renv.Log().Verbose("detach routetable done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachRoutetable) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DisassociateRouteTableInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DisassociateRouteTableInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DisassociateRouteTable(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DisassociateRouteTable call took %s", time.Since(start))
renv.Log().Verbose("dry run: detach routetable ok")
return fakeDryRunId("routetable"), nil
}
}
return nil, err
}
func (cmd *DetachRoutetable) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachSecuritygroup {
cmd := new(DetachSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("detach securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("securitygroup"), nil
}
func (cmd *DetachSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachUser(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachUser {
cmd := new(DetachUser)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachUser) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *DetachUser) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachUser) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.RemoveUserFromGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.RemoveUserFromGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RemoveUserFromGroup(context.Background(), input)
renv.Log().ExtraVerbosef("iam.RemoveUserFromGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach user: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach user '%s' done", extracted)
} else {
renv.Log().Verbose("detach user done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachUser) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("user"), nil
}
func (cmd *DetachUser) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewDetachVolume(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *DetachVolume {
cmd := new(DetachVolume)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *DetachVolume) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *DetachVolume) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *DetachVolume) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.DetachVolumeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DetachVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DetachVolume(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.DetachVolume call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("detach volume: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("detach volume '%s' done", extracted)
} else {
renv.Log().Verbose("detach volume done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *DetachVolume) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DetachVolumeInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DetachVolumeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.DetachVolume(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.DetachVolume call took %s", time.Since(start))
renv.Log().Verbose("dry run: detach volume ok")
return fakeDryRunId("volume"), nil
}
}
return nil, err
}
func (cmd *DetachVolume) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewImportImage(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *ImportImage {
cmd := new(ImportImage)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *ImportImage) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *ImportImage) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *ImportImage) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.ImportImageInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ImportImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ImportImage(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.ImportImage call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("import image: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("import image '%s' done", extracted)
} else {
renv.Log().Verbose("import image done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *ImportImage) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.ImportImageInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ImportImageInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.ImportImage(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.ImportImage call took %s", time.Since(start))
renv.Log().Verbose("dry run: import image ok")
return fakeDryRunId("image"), nil
}
}
return nil, err
}
func (cmd *ImportImage) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewRestartDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *RestartDatabase {
cmd := new(RestartDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *RestartDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *RestartDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *RestartDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.RebootDBInstanceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.RebootDBInstanceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RebootDBInstance(context.Background(), input)
renv.Log().ExtraVerbosef("rds.RebootDBInstance call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("restart database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("restart database '%s' done", extracted)
} else {
renv.Log().Verbose("restart database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *RestartDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *RestartDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewRestartInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *RestartInstance {
cmd := new(RestartInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *RestartInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *RestartInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *RestartInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.RebootInstancesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.RebootInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.RebootInstances(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.RebootInstances call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("restart instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("restart instance '%s' done", extracted)
} else {
renv.Log().Verbose("restart instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *RestartInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.RebootInstancesInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.RebootInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.RebootInstances(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.RebootInstances call took %s", time.Since(start))
renv.Log().Verbose("dry run: restart instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *RestartInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStartAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StartAlarm {
cmd := new(StartAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StartAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *StartAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StartAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudwatch.EnableAlarmActionsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudwatch.EnableAlarmActionsInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.EnableAlarmActions(context.Background(), input)
renv.Log().ExtraVerbosef("cloudwatch.EnableAlarmActions call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("start alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("start alarm '%s' done", extracted)
} else {
renv.Log().Verbose("start alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StartAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *StartAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStartContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StartContainertask {
cmd := new(StartContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StartContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *StartContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StartContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("start containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("start containertask '%s' done", extracted)
} else {
renv.Log().Verbose("start containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StartContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containertask"), nil
}
func (cmd *StartContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStartDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StartDatabase {
cmd := new(StartDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StartDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *StartDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StartDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.StartDBInstanceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.StartDBInstanceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.StartDBInstance(context.Background(), input)
renv.Log().ExtraVerbosef("rds.StartDBInstance call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("start database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("start database '%s' done", extracted)
} else {
renv.Log().Verbose("start database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StartDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *StartDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStartInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StartInstance {
cmd := new(StartInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StartInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *StartInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StartInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.StartInstancesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.StartInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.StartInstances(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.StartInstances call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("start instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("start instance '%s' done", extracted)
} else {
renv.Log().Verbose("start instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StartInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.StartInstancesInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.StartInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.StartInstances(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.StartInstances call took %s", time.Since(start))
renv.Log().Verbose("dry run: start instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *StartInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStopAlarm(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StopAlarm {
cmd := new(StopAlarm)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudwatch.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StopAlarm) SetApi(api *cloudwatch.Client) {
cmd.api = api
}
func (cmd *StopAlarm) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StopAlarm) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudwatch.DisableAlarmActionsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudwatch.DisableAlarmActionsInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.DisableAlarmActions(context.Background(), input)
renv.Log().ExtraVerbosef("cloudwatch.DisableAlarmActions call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("stop alarm: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("stop alarm '%s' done", extracted)
} else {
renv.Log().Verbose("stop alarm done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StopAlarm) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("alarm"), nil
}
func (cmd *StopAlarm) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStopContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StopContainertask {
cmd := new(StopContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StopContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *StopContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StopContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("stop containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("stop containertask '%s' done", extracted)
} else {
renv.Log().Verbose("stop containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StopContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containertask"), nil
}
func (cmd *StopContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStopDatabase(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StopDatabase {
cmd := new(StopDatabase)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = rds.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StopDatabase) SetApi(api *rds.Client) {
cmd.api = api
}
func (cmd *StopDatabase) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StopDatabase) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &rds.StopDBInstanceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in rds.StopDBInstanceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.StopDBInstance(context.Background(), input)
renv.Log().ExtraVerbosef("rds.StopDBInstance call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("stop database: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("stop database '%s' done", extracted)
} else {
renv.Log().Verbose("stop database done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StopDatabase) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("database"), nil
}
func (cmd *StopDatabase) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewStopInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *StopInstance {
cmd := new(StopInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *StopInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *StopInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *StopInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.StopInstancesInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.StopInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.StopInstances(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.StopInstances call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("stop instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("stop instance '%s' done", extracted)
} else {
renv.Log().Verbose("stop instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *StopInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.StopInstancesInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.StopInstancesInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.StopInstances(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.StopInstances call took %s", time.Since(start))
renv.Log().Verbose("dry run: stop instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *StopInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateBucket(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateBucket {
cmd := new(UpdateBucket)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateBucket) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *UpdateBucket) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateBucket) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update bucket: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update bucket '%s' done", extracted)
} else {
renv.Log().Verbose("update bucket done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateBucket) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("bucket"), nil
}
func (cmd *UpdateBucket) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateClassicLoadbalancer(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateClassicLoadbalancer {
cmd := new(UpdateClassicLoadbalancer)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elb.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateClassicLoadbalancer) SetApi(api *elb.Client) {
cmd.api = api
}
func (cmd *UpdateClassicLoadbalancer) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateClassicLoadbalancer) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &elb.ConfigureHealthCheckInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in elb.ConfigureHealthCheckInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ConfigureHealthCheck(context.Background(), input)
renv.Log().ExtraVerbosef("elb.ConfigureHealthCheck call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update classicloadbalancer: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update classicloadbalancer '%s' done", extracted)
} else {
renv.Log().Verbose("update classicloadbalancer done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateClassicLoadbalancer) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("classicloadbalancer"), nil
}
func (cmd *UpdateClassicLoadbalancer) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateContainertask(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateContainertask {
cmd := new(UpdateContainertask)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ecs.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateContainertask) SetApi(api *ecs.Client) {
cmd.api = api
}
func (cmd *UpdateContainertask) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateContainertask) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ecs.UpdateServiceInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ecs.UpdateServiceInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.UpdateService(context.Background(), input)
renv.Log().ExtraVerbosef("ecs.UpdateService call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update containertask: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update containertask '%s' done", extracted)
} else {
renv.Log().Verbose("update containertask done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateContainertask) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("containertask"), nil
}
func (cmd *UpdateContainertask) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateDistribution(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateDistribution {
cmd := new(UpdateDistribution)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudfront.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateDistribution) SetApi(api *cloudfront.Client) {
cmd.api = api
}
func (cmd *UpdateDistribution) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateDistribution) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update distribution: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update distribution '%s' done", extracted)
} else {
renv.Log().Verbose("update distribution done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateDistribution) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("distribution"), nil
}
func (cmd *UpdateDistribution) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateImage(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateImage {
cmd := new(UpdateImage)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateImage) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *UpdateImage) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateImage) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update image: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update image '%s' done", extracted)
} else {
renv.Log().Verbose("update image done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateImage) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateInstance(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateInstance {
cmd := new(UpdateInstance)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateInstance) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *UpdateInstance) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateInstance) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.ModifyInstanceAttributeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ModifyInstanceAttributeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ModifyInstanceAttribute(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.ModifyInstanceAttribute call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update instance: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update instance '%s' done", extracted)
} else {
renv.Log().Verbose("update instance done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateInstance) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.ModifyInstanceAttributeInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ModifyInstanceAttributeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.ModifyInstanceAttribute(context.Background(), input)
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: ec2.ModifyInstanceAttribute call took %s", time.Since(start))
renv.Log().Verbose("dry run: update instance ok")
return fakeDryRunId("instance"), nil
}
}
return nil, err
}
func (cmd *UpdateInstance) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateLoginprofile(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateLoginprofile {
cmd := new(UpdateLoginprofile)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateLoginprofile) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *UpdateLoginprofile) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateLoginprofile) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.UpdateLoginProfileInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.UpdateLoginProfileInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.UpdateLoginProfile(context.Background(), input)
renv.Log().ExtraVerbosef("iam.UpdateLoginProfile call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update loginprofile: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update loginprofile '%s' done", extracted)
} else {
renv.Log().Verbose("update loginprofile done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateLoginprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("loginprofile"), nil
}
func (cmd *UpdateLoginprofile) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdatePolicy(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdatePolicy {
cmd := new(UpdatePolicy)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = iam.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdatePolicy) SetApi(api *iam.Client) {
cmd.api = api
}
func (cmd *UpdatePolicy) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdatePolicy) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &iam.CreatePolicyVersionInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in iam.CreatePolicyVersionInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.CreatePolicyVersion(context.Background(), input)
renv.Log().ExtraVerbosef("iam.CreatePolicyVersion call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update policy: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update policy '%s' done", extracted)
} else {
renv.Log().Verbose("update policy done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdatePolicy) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("policy"), nil
}
func (cmd *UpdatePolicy) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateRecord(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateRecord {
cmd := new(UpdateRecord)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = route53.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateRecord) SetApi(api *route53.Client) {
cmd.api = api
}
func (cmd *UpdateRecord) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateRecord) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update record: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update record '%s' done", extracted)
} else {
renv.Log().Verbose("update record done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateRecord) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("record"), nil
}
func (cmd *UpdateRecord) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateS3object(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateS3object {
cmd := new(UpdateS3object)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = s3.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateS3object) SetApi(api *s3.Client) {
cmd.api = api
}
func (cmd *UpdateS3object) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateS3object) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &s3.PutObjectAclInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in s3.PutObjectAclInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.PutObjectAcl(context.Background(), input)
renv.Log().ExtraVerbosef("s3.PutObjectAcl call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update s3object: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update s3object '%s' done", extracted)
} else {
renv.Log().Verbose("update s3object done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateS3object) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("s3object"), nil
}
func (cmd *UpdateS3object) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateScalinggroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateScalinggroup {
cmd := new(UpdateScalinggroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = autoscaling.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateScalinggroup) SetApi(api *autoscaling.Client) {
cmd.api = api
}
func (cmd *UpdateScalinggroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateScalinggroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &autoscaling.UpdateAutoScalingGroupInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in autoscaling.UpdateAutoScalingGroupInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.UpdateAutoScalingGroup(context.Background(), input)
renv.Log().ExtraVerbosef("autoscaling.UpdateAutoScalingGroup call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update scalinggroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update scalinggroup '%s' done", extracted)
} else {
renv.Log().Verbose("update scalinggroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateScalinggroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("scalinggroup"), nil
}
func (cmd *UpdateScalinggroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateSecuritygroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateSecuritygroup {
cmd := new(UpdateSecuritygroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateSecuritygroup) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *UpdateSecuritygroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateSecuritygroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update securitygroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update securitygroup '%s' done", extracted)
} else {
renv.Log().Verbose("update securitygroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateSecuritygroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateStack(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateStack {
cmd := new(UpdateStack)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = cloudformation.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateStack) SetApi(api *cloudformation.Client) {
cmd.api = api
}
func (cmd *UpdateStack) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateStack) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &cloudformation.UpdateStackInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in cloudformation.UpdateStackInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.UpdateStack(context.Background(), input)
renv.Log().ExtraVerbosef("cloudformation.UpdateStack call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update stack: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update stack '%s' done", extracted)
} else {
renv.Log().Verbose("update stack done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateStack) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("stack"), nil
}
func (cmd *UpdateStack) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateSubnet(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateSubnet {
cmd := new(UpdateSubnet)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = ec2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateSubnet) SetApi(api *ec2.Client) {
cmd.api = api
}
func (cmd *UpdateSubnet) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateSubnet) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
input := &ec2.ModifySubnetAttributeInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ModifySubnetAttributeInput: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.ModifySubnetAttribute(context.Background(), input)
renv.Log().ExtraVerbosef("ec2.ModifySubnetAttribute call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update subnet: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update subnet '%s' done", extracted)
} else {
renv.Log().Verbose("update subnet done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateSubnet) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("subnet"), nil
}
func (cmd *UpdateSubnet) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
func NewUpdateTargetgroup(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *UpdateTargetgroup {
cmd := new(UpdateTargetgroup)
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = elbv2.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *UpdateTargetgroup) SetApi(api *elbv2.Client) {
cmd.api = api
}
func (cmd *UpdateTargetgroup) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *UpdateTargetgroup) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("update targetgroup: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("update targetgroup '%s' done", extracted)
} else {
renv.Log().Verbose("update targetgroup done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
func (cmd *UpdateTargetgroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("targetgroup"), nil
}
func (cmd *UpdateTargetgroup) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateGroup struct {
_ string `action:"create" entity:"group" awsAPI:"iam" awsCall:"CreateGroup" awsInput:"iam.CreateGroupInput" awsOutput:"iam.CreateGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"GroupName" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateGroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateGroup) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*iam.CreateGroupOutput).Group.GroupId)
}
type DeleteGroup struct {
_ string `action:"delete" entity:"group" awsAPI:"iam" awsCall:"DeleteGroup" awsInput:"iam.DeleteGroupInput" awsOutput:"iam.DeleteGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"GroupName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteGroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateImage struct {
_ string `action:"create" entity:"image" awsAPI:"ec2" awsCall:"CreateImage" awsInput:"ec2.CreateImageInput" awsOutput:"ec2.CreateImageOutput" awsDryRun:"true"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
Reboot *bool `awsName:"NoReboot" awsType:"awsbool" templateName:"reboot"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
}
func (cmd *CreateImage) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("instance"), params.Key("name"), params.Opt("description", "reboot")),
params.Validators{
"name": params.MinLengthOf(3),
})
}
func (cmd *CreateImage) BeforeRun(renv env.Running) error {
if reboot := cmd.Reboot; reboot != nil && *reboot {
cmd.Reboot = nil
} else {
cmd.Reboot = Bool(true) // so that ec2.CreateImageInput.NoReboot = true and therefore by default no reboot from AWS
}
return nil
}
func (cmd *CreateImage) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateImageOutput).ImageId)
}
type UpdateImage struct {
_ string `action:"update" entity:"image" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"ImageId" awsType:"awsstr" templateName:"id"`
Groups []*string `awsName:"UserGroups" awsType:"awsstringslice" templateName:"groups"`
Accounts []*string `awsName:"UserIds" awsType:"awsstringslice" templateName:"accounts"`
Operation *string `awsName:"OperationType" awsType:"awsstr" templateName:"operation"`
ProductCodes []*string `awsName:"ProductCodes" awsType:"awsstringslice" templateName:"product-codes"`
Description *string `awsName:"Description" awsType:"awsstringattribute" templateName:"description"`
}
func (cmd *UpdateImage) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("accounts", "description", "groups", "operation", "product-codes"),
))
}
func (cmd *UpdateImage) prepareImageAttributeInput(ctx map[string]interface{}) (*ec2.ModifyImageAttributeInput, error) {
input := &ec2.ModifyImageAttributeInput{}
if err := structInjector(cmd, input, ctx); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ModifyImageAttributeInput: %s", err)
}
if cmd.Accounts != nil || cmd.Groups != nil {
input.Attribute = awssdk.String("launchPermission")
}
if cmd.ProductCodes != nil {
input.Attribute = awssdk.String("productCodes")
}
if cmd.Description != nil {
input.Attribute = awssdk.String("description")
}
return input, nil
}
func (cmd *UpdateImage) ManualRun(renv env.Running) (interface{}, error) {
input, err := cmd.prepareImageAttributeInput(renv.Context())
if err != nil {
return nil, err
}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.ModifyImageAttributeInput: %s", err)
}
start := time.Now()
output, err := cmd.api.ModifyImageAttribute(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.ModifyImageAttributeInput call took %s", time.Since(start))
return output, err
}
func (cmd *UpdateImage) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("dry run: cannot set params on command struct: %s", err)
}
input, err := cmd.prepareImageAttributeInput(renv.Context())
if err != nil {
return nil, err
}
input.DryRun = awssdk.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("dry run: cannot inject in ec2.ModifyImageAttributeInput: %s", err)
}
start := time.Now()
_, err = cmd.api.ModifyImageAttribute(context.Background(), input)
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(awsErr.ErrorMessage(), "Invalid IAM Instance Profile name"):
cmd.logger.ExtraVerbosef("dry run: ec2.ec2.ModifyImageAttribute call took %s", time.Since(start))
cmd.logger.Verbose("dry run: update image ok")
return fakeDryRunId("image"), nil
}
}
return nil, fmt.Errorf("dry run: %s", err)
}
type CopyImage struct {
_ string `action:"copy" entity:"image" awsAPI:"ec2" awsCall:"CopyImage" awsInput:"ec2.CopyImageInput" awsOutput:"ec2.CopyImageOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
SourceId *string `awsName:"SourceImageId" awsType:"awsstr" templateName:"source-id"`
SourceRegion *string `awsName:"SourceRegion" awsType:"awsstr" templateName:"source-region"`
Encrypted *bool `awsName:"Encrypted" awsType:"awsbool" templateName:"encrypted"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
}
func (cmd *CopyImage) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"), params.Key("source-id"), params.Key("source-region"),
params.Opt("description", "encrypted"),
))
}
func (cmd *CopyImage) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CopyImageOutput).ImageId)
}
type ImportImage struct {
_ string `action:"import" entity:"image" awsAPI:"ec2" awsCall:"ImportImage" awsInput:"ec2.ImportImageInput" awsOutput:"ec2.ImportImageOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Architecture *string `awsName:"Architecture" awsType:"awsstr" templateName:"architecture"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
License *string `awsName:"LicenseType" awsType:"awsstr" templateName:"license"`
Platform *string `awsName:"Platform" awsType:"awsstr" templateName:"platform"`
Role *string `awsName:"RoleName" awsType:"awsstr" templateName:"role"`
Snapshot *string `awsName:"DiskContainers[0]SnapshotId" awsType:"awsslicestruct" templateName:"snapshot"`
Url *string `awsName:"DiskContainers[0]Url" awsType:"awsslicestruct" templateName:"url"`
Bucket *string `awsName:"DiskContainers[0]UserBucket.S3Bucket" awsType:"awsslicestruct" templateName:"bucket"`
S3object *string `awsName:"DiskContainers[0]UserBucket.S3Key" awsType:"awsslicestruct" templateName:"s3object"`
}
func (cmd *ImportImage) ParamsSpec() params.Spec {
return params.NewSpec(params.OnlyOneOf(
params.Key("snapshot"), params.Key("url"),
params.AllOf(params.Key("bucket"), params.Key("s3object")),
params.Opt("architecture", "description", "license", "platform", "role"),
))
}
func (cmd *ImportImage) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.ImportImageOutput).ImportTaskId)
}
type DeleteImage struct {
_ string `action:"delete" entity:"image" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
DeleteSnapshots *bool `templateName:"delete-snapshots"`
}
func (cmd *DeleteImage) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("delete-snapshots"),
))
}
func (cmd *DeleteImage) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeregisterImageInput{}
input.DryRun = Bool(true)
if err := setFieldWithType(cmd.Id, input, "ImageId", awsstr); err != nil {
return nil, err
}
if BoolValue(cmd.DeleteSnapshots) {
var snaps []string
var err error
if snaps, err = cmd.imageSnapshots(StringValue(input.ImageId)); err != nil {
return nil, err
}
if len(snaps) > 0 {
renv.Log().Warningf("deleting image will also delete snapshot %s (prevent that by appending `delete-snapshots=false`)", strings.Join(snaps, ", "))
}
}
_, err := cmd.api.DeregisterImage(context.Background(), input)
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound):
id := fakeDryRunId("image")
cmd.logger.Verbose("dry run: delete image ok")
return id, nil
}
}
return nil, err
}
func (cmd *DeleteImage) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DeregisterImageInput{}
if err := setFieldWithType(cmd.Id, input, "ImageId", awsstr); err != nil {
return nil, err
}
var snaps []string
var err error
if BoolValue(cmd.DeleteSnapshots) {
if snaps, err = cmd.imageSnapshots(StringValue(input.ImageId)); err != nil {
return nil, err
}
}
start := time.Now()
var output *ec2.DeregisterImageOutput
if output, err = cmd.api.DeregisterImage(context.Background(), input); err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ec2.DeregisterImage call took %s", time.Since(start))
if BoolValue(cmd.DeleteSnapshots) {
for _, snap := range snaps {
deleteSnapshot := CommandFactory.Build("deletesnapshot")().(*DeleteSnapshot)
entries := map[string]interface{}{
"id": snap,
}
if err := params.Validate(deleteSnapshot.ParamsSpec().Validators(), entries); err != nil {
return nil, err
}
if _, err := deleteSnapshot.Run(renv, entries); err != nil {
return nil, fmt.Errorf("delete snapshot %s: %s", snap, err)
}
}
}
return output, nil
}
func (cmd *DeleteImage) imageSnapshots(id string) ([]string, error) {
var snapshots []string
imgs, err := cmd.api.DescribeImages(context.Background(), &ec2.DescribeImagesInput{ImageIds: []string{id}})
if err != nil {
return snapshots, err
}
if len(imgs.Images) == 0 {
return snapshots, fmt.Errorf("no image found with id '%s'", id)
}
if len(imgs.Images) > 1 {
return snapshots, fmt.Errorf("multiple images found with id '%s'", id)
}
for _, dev := range imgs.Images[0].BlockDeviceMappings {
if dev.Ebs == nil {
continue
}
if snapshot := StringValue(dev.Ebs.SnapshotId); snapshot != "" {
snapshots = append(snapshots, snapshot)
}
}
return snapshots, nil
}
package awsspec
import (
"context"
"fmt"
"sort"
"strings"
"time"
"sync"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
awsdoc "github.com/wallix/awless/aws/doc"
)
// Image resolving allows to find AWS AMIs identifiers specifying what you want instead
// of an id that is specific to a region. The ami query string specification is as follows:
//
// owner:distro:variant:arch:virtualization:store
//
// Everything optional expect for the owner.
//
// As for now only the main specific owner are taken into account
// and we deal with bares machines only distribution. Here are some examples:
//
// - canonical:ubuntu:trusty
//
// - redhat:rhel:6.8
//
// - redhat::6.8
//
// - amazonlinux
//
// - suselinux:sles-12
//
// - canonical:::i386
//
// - redhat::::instance-store
//
// - centos:centos
//
// The default values are: Arch="x86_64", Virt="hvm", Store="ebs"
type ImageResolver func(context.Context, *ec2.DescribeImagesInput, ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error)
func EC2ImageResolver() ImageResolver {
factory := CommandFactory.Build("createinstance")
return ImageResolver(factory().(*CreateInstance).api.DescribeImages)
}
var DefaultImageResolverCache = new(ImageResolverCache)
type ImageResolverCache struct {
mu sync.Mutex
cache map[string][]*AwsImage
}
func (r *ImageResolverCache) Store(key string, images []*AwsImage) {
r.mu.Lock()
defer r.mu.Unlock()
if r.cache == nil {
r.cache = make(map[string][]*AwsImage)
}
r.cache[key] = images
}
func (r *ImageResolverCache) Get(key string) ([]*AwsImage, bool) {
r.mu.Lock()
defer r.mu.Unlock()
if r.cache == nil {
r.cache = make(map[string][]*AwsImage)
}
images, ok := r.cache[key]
return images, ok
}
const ImageQuerySpec = "owner:distro:variant:arch:virtualization:store"
type AwsImage struct {
Id string
Owner string
Location string
Type string
Architecture string
VirtualizationType string
Name string
Created time.Time
Hypervisor string
Store string
}
type ImageQuery struct {
Platform Platform
Distro Distro
}
func (q ImageQuery) String() string {
var all []string
all = append(all, q.Platform.Name)
all = append(all, q.Distro.Name)
all = append(all, q.Distro.Variant)
all = append(all, q.Distro.Arch)
all = append(all, q.Distro.Virt)
all = append(all, q.Distro.Store)
return strings.Join(all, ":")
}
type Distro struct {
Name, Variant, Arch, Virt, Store string
}
var (
validArchs = []string{"i386", "x86_64"}
validVirts = []string{"paravirtual", "hvm"}
validStores = []string{"ebs", "instance-store"}
)
type Platform struct {
Name string
Id string
DistroName string
LatestVariant string
MatchFunc func(s string, d Distro) bool
}
func (resolv ImageResolver) Resolve(q ImageQuery) ([]*AwsImage, bool, error) {
images, found := DefaultImageResolverCache.Get(q.String())
if found {
return images, true, nil
}
results := make([]*AwsImage, 0) // json empty array friendly
var filters []ec2types.Filter
filters = append(filters,
ec2types.Filter{
Name: awssdk.String("state"),
Values: []string{"available"},
},
ec2types.Filter{
Name: awssdk.String("is-public"),
Values: []string{"true"},
},
)
filters = append(filters,
ec2types.Filter{
Name: awssdk.String("owner-id"),
Values: []string{q.Platform.Id},
},
)
filters = append(filters,
ec2types.Filter{
Name: awssdk.String("virtualization-type"),
Values: []string{q.Distro.Virt},
},
)
filters = append(filters,
ec2types.Filter{
Name: awssdk.String("architecture"),
Values: []string{q.Distro.Arch},
},
)
filters = append(filters,
ec2types.Filter{
Name: awssdk.String("root-device-type"),
Values: []string{q.Distro.Store},
},
)
params := &ec2.DescribeImagesInput{
ExecutableUsers: []string{"all"},
Filters: filters,
}
amis, err := resolv(context.Background(), params)
if err != nil {
return results, false, err
}
for _, ami := range amis.Images {
if !q.Platform.MatchFunc(strings.ToLower(awssdk.ToString(ami.Name)), q.Distro) {
continue
}
img := &AwsImage{
Id: awssdk.ToString(ami.ImageId),
Owner: q.Platform.Id,
Location: awssdk.ToString(ami.ImageLocation),
Type: string(ami.ImageType),
Architecture: string(ami.Architecture),
VirtualizationType: string(ami.VirtualizationType),
Name: awssdk.ToString(ami.Name),
Hypervisor: string(ami.Hypervisor),
Store: string(ami.RootDeviceType),
}
img.Created, _ = time.Parse(time.RFC3339, awssdk.ToString(ami.CreationDate))
results = append(results, img)
}
sort.Slice(results, func(i, j int) bool { return results[i].Created.After(results[j].Created) })
DefaultImageResolverCache.Store(q.String(), results)
return results, false, nil
}
var (
Platforms = map[string]Platform{
"canonical": Canonical,
"redhat": RedHat,
"debian": Debian,
"amazonlinux": AmazonLinux,
"coreos": CoreOS,
"centos": CentOS,
"suselinux": SuseLinux,
"windows": Windows,
}
Canonical = Platform{
Name: "canonical", Id: "099720109477", DistroName: "ubuntu", LatestVariant: "xenial",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, fmt.Sprintf("%s/images/%s-ssd/%s-%s", d.Name, d.Virt, d.Name, d.Variant))
},
}
RedHat = Platform{
Name: "redhat", Id: "309956199498", DistroName: "rhel", LatestVariant: "7.5",
MatchFunc: func(s string, d Distro) bool {
return strings.Contains(s, fmt.Sprintf("%s-%s", d.Name, d.Variant))
},
}
Debian = Platform{
Name: "debian", Id: "379101102735", DistroName: "debian", LatestVariant: "stretch",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, fmt.Sprintf("%s-%s", d.Name, d.Variant))
},
}
CoreOS = Platform{
Name: "coreos", Id: "595879546273", DistroName: "coreos", LatestVariant: "1688",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, strings.ToLower(fmt.Sprintf("%s-stable-%s", d.Name, d.Variant)))
},
}
CentOS = Platform{
Name: "centos", Id: "679593333241", DistroName: "centos", LatestVariant: "7",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, strings.ToLower(fmt.Sprintf("%s Linux %s", d.Name, d.Variant)))
},
}
AmazonLinux = Platform{
Name: "amazonlinux", Id: "137112412989", DistroName: "amzn", LatestVariant: "hvm",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, fmt.Sprintf("%s-ami-%s", d.Name, d.Variant))
},
}
SuseLinux = Platform{
Name: "suselinux", Id: "013907871322", LatestVariant: "sles-12",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, fmt.Sprintf("suse-%s", d.Variant))
},
}
Windows = Platform{
Name: "windows", Id: "801119661308", DistroName: "server", LatestVariant: "2016",
MatchFunc: func(s string, d Distro) bool {
return strings.HasPrefix(s, strings.ToLower(fmt.Sprintf("windows_%s-%s-english", d.Name, d.Variant)))
},
}
defaultArch = "x86_64"
defaultVirt = "hvm"
defaultStore = "ebs"
SupportedAMIOwners []string
)
func init() {
for name := range Platforms {
SupportedAMIOwners = append(SupportedAMIOwners, name)
}
awsdoc.CommandDefinitionsDoc["create.instance"] = fmt.Sprintf("Create an EC2 instance.\n\nThe `distro` param allows to resolve from the current region the official community free bare AMI according to an awless specific bare distro query format, ordering by latest first. The query string specification is the following column separated format:\n\n\t\t%s\n\nIn this query format, everything is optional expect for the 'owner'. Supported owners: %s", ImageQuerySpec, strings.Join(SupportedAMIOwners, ", "))
}
func ParseImageQuery(s string) (ImageQuery, error) {
supported := strings.Join(SupportedAMIOwners, ", ")
splits := strings.Split(s, ":")
splitsCount := len(splits)
q := ImageQuery{}
if splitsCount < 1 {
return q, fmt.Errorf("malformed image query '%s': missing at least one supported owner name: %s", s, supported)
}
if splitsCount > 6 {
return q, fmt.Errorf("malformed image query '%s': to many tokens, expecting format: %s", s, ImageQuerySpec)
}
for i, s := range splits {
splits[i] = strings.ToLower(s)
}
plat, ok := Platforms[splits[0]]
if !ok {
return q, fmt.Errorf("unsupported owner '%s'. Expecting: %s (see awless search images -h for more)", splits[0], supported)
}
q.Platform = plat
if splitsCount > 1 && strings.TrimSpace(splits[1]) != "" {
q.Distro.Name = splits[1]
} else {
q.Distro.Name = q.Platform.DistroName
}
if splitsCount > 2 && strings.TrimSpace(splits[2]) != "" {
q.Distro.Variant = splits[2]
} else {
q.Distro.Variant = q.Platform.LatestVariant
}
if splitsCount > 3 && strings.TrimSpace(splits[3]) != "" {
if !contains(validArchs, splits[3]) {
return q, fmt.Errorf("image query: invalid architecture '%s' (expecting: %s)", splits[3], strings.Join(validArchs, ", "))
}
q.Distro.Arch = splits[3]
} else {
q.Distro.Arch = defaultArch
}
if splitsCount > 4 && strings.TrimSpace(splits[4]) != "" {
if !contains(validVirts, splits[4]) {
return q, fmt.Errorf("image query: invalid virtualization '%s' (expecting: %s)", splits[4], strings.Join(validVirts, ", "))
}
q.Distro.Virt = splits[4]
} else {
q.Distro.Virt = defaultVirt
}
if splitsCount > 5 && strings.TrimSpace(splits[5]) != "" {
if !contains(validStores, splits[5]) {
return q, fmt.Errorf("image query: invalid store '%s' (expecting: %s)", splits[5], strings.Join(validStores, ", "))
}
q.Distro.Store = splits[5]
} else {
q.Distro.Store = defaultStore
}
return q, nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"fmt"
"time"
"context"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
)
type CreateInstance struct {
_ string `action:"create" entity:"instance" awsAPI:"ec2" awsCall:"RunInstances" awsInput:"ec2.RunInstancesInput" awsOutput:"ec2.Reservation" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Image *string `awsName:"ImageId" awsType:"awsstr" templateName:"image"`
Count *int64 `awsName:"MaxCount,MinCount" awsType:"awsin64" templateName:"count"`
Type *string `awsName:"InstanceType" awsType:"awsstr" templateName:"type"`
Name *string `templateName:"name"`
Subnet *string `awsName:"SubnetId" awsType:"awsstr" templateName:"subnet"`
Keypair *string `awsName:"KeyName" awsType:"awsstr" templateName:"keypair"`
PrivateIP *string `awsName:"PrivateIpAddress" awsType:"awsstr" templateName:"ip"`
UserData *string `awsName:"UserData" awsType:"awsuserdatatobase64" templateName:"userdata"`
SecurityGroups []*string `awsName:"SecurityGroupIds" awsType:"awsstringslice" templateName:"securitygroup"`
Lock *bool `awsName:"DisableApiTermination" awsType:"awsbool" templateName:"lock"`
EbsOptimized *bool `awsName:"EbsOptimized" awsType:"awsbool" templateName:"ebs-optimized"`
Role *string `awsName:"IamInstanceProfile.Name" awsType:"awsstr" templateName:"role"`
DistroQuery *string `awsType:"awsstr" templateName:"distro"`
AssociatePublicIP *bool `templateName:"associate-public-ip"`
}
func (cmd *CreateInstance) ParamsSpec() params.Spec {
builder := params.SpecBuilder(
params.AllOf(params.OnlyOneOf(params.Key("distro"), params.Key("image")),
params.Key("count"), params.Key("type"), params.Key("name"), params.Key("subnet"),
params.Opt(params.Suggested("keypair", "securitygroup"), "ip", "userdata", "lock", "ebs-optimized", "role", "associate-public-ip"),
),
params.Validators{"ip": params.IsIP},
)
builder.AddReducer(cmd.convertDistroToAMI, "distro")
return builder.Done()
}
// PostProcessInput adjusts the RunInstancesInput when associate-public-ip is
// requested. Because AssociatePublicIpAddress must live inside
// NetworkInterfaces[0], SecurityGroupIds and SubnetId must also be moved there
// (AWS rejects mixing top-level and NetworkInterfaces for these fields).
func (cmd *CreateInstance) PostProcessInput(iface interface{}) {
input, ok := iface.(*ec2.RunInstancesInput)
if !ok || cmd.AssociatePublicIP == nil {
return
}
nic := ec2types.InstanceNetworkInterfaceSpecification{
AssociatePublicIpAddress: cmd.AssociatePublicIP,
DeviceIndex: awssdk.Int32(0),
}
if len(input.SecurityGroupIds) > 0 {
nic.Groups = input.SecurityGroupIds
input.SecurityGroupIds = nil
}
if input.SubnetId != nil {
nic.SubnetId = input.SubnetId
input.SubnetId = nil
}
input.NetworkInterfaces = []ec2types.InstanceNetworkInterfaceSpecification{nic}
}
func (cmd *CreateInstance) convertDistroToAMI(values map[string]interface{}) (map[string]interface{}, error) {
if distro, ok := values["distro"].(string); ok {
query, err := ParseImageQuery(distro)
if err != nil {
return nil, fmt.Errorf("distro: %s", err)
}
resolver := ImageResolver(cmd.api.DescribeImages)
cmd.logger.Verbosef("Searching for bare community distro: '%s' expanded to '%s'", distro, query)
images, fromCache, err := resolver.Resolve(query)
if err != nil {
return nil, fmt.Errorf("distro: %s", err)
}
if len(images) > 0 {
var caching string
if fromCache {
caching = " from cache"
}
cmd.logger.Infof("Image %s resolved%s for distro '%s' (expanded to '%s')", images[0].Id, caching, distro, query)
return map[string]interface{}{"image": images[0].Id}, nil
} else {
return nil, fmt.Errorf("distro: no image id found for query '%s'", query)
}
}
return nil, nil
}
func (cmd *CreateInstance) ExtractResult(i interface{}) string {
return StringValue(i.(*ec2.RunInstancesOutput).Instances[0].InstanceId)
}
func (cmd *CreateInstance) AfterRun(renv env.Running, output interface{}) error {
return createNameTag(String(cmd.ExtractResult(output)), cmd.Name, renv)
}
type UpdateInstance struct {
_ string `action:"update" entity:"instance" awsAPI:"ec2" awsCall:"ModifyInstanceAttribute" awsInput:"ec2.ModifyInstanceAttributeInput" awsOutput:"ec2.ModifyInstanceAttributeOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"InstanceId" awsType:"awsstr" templateName:"id"`
Type *string `awsName:"InstanceType.Value" awsType:"awsstr" templateName:"type"`
Lock *bool `awsName:"DisableApiTermination" awsType:"awsboolattribute" templateName:"lock"`
}
func (cmd *UpdateInstance) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Opt("lock", "type")))
}
type DeleteInstance struct {
_ string `action:"delete" entity:"instance" awsAPI:"ec2" awsCall:"TerminateInstances" awsInput:"ec2.TerminateInstancesInput" awsOutput:"ec2.TerminateInstancesOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
IDs []*string `awsName:"InstanceIds" awsType:"awsstringslice" templateName:"ids"`
}
func (cmd *DeleteInstance) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.OnlyOneOf(params.Key("ids"), params.Key("id")))
builder.AddReducer(idToIds, "id")
return builder.Done()
}
type StartInstance struct {
_ string `action:"start" entity:"instance" awsAPI:"ec2" awsCall:"StartInstances" awsInput:"ec2.StartInstancesInput" awsOutput:"ec2.StartInstancesOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id []*string `awsName:"InstanceIds" awsType:"awsstringslice" templateName:"ids"`
}
func (cmd *StartInstance) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.OnlyOneOf(params.Key("ids"), params.Key("id")))
builder.AddReducer(idToIds, "id")
return builder.Done()
}
func (cmd *StartInstance) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.StartInstancesOutput).StartingInstances[0].InstanceId)
}
type StopInstance struct {
_ string `action:"stop" entity:"instance" awsAPI:"ec2" awsCall:"StopInstances" awsInput:"ec2.StopInstancesInput" awsOutput:"ec2.StopInstancesOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id []*string `awsName:"InstanceIds" awsType:"awsstringslice" templateName:"ids"`
}
func (cmd *StopInstance) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.OnlyOneOf(params.Key("ids"), params.Key("id")))
builder.AddReducer(idToIds, "id")
return builder.Done()
}
func (cmd *StopInstance) ExtractResult(i interface{}) string {
return StringValue(i.(*ec2.StopInstancesOutput).StoppingInstances[0].InstanceId)
}
type RestartInstance struct {
_ string `action:"restart" entity:"instance" awsAPI:"ec2" awsCall:"RebootInstances" awsInput:"ec2.RebootInstancesInput" awsOutput:"ec2.RebootInstancesOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id []*string `awsName:"InstanceIds" awsType:"awsstringslice" templateName:"ids"`
}
func (cmd *RestartInstance) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.OnlyOneOf(params.Key("ids"), params.Key("id")))
builder.AddReducer(idToIds, "id")
return builder.Done()
}
const (
notFoundState = "not-found"
)
type CheckInstance struct {
_ string `action:"check" entity:"instance" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckInstance) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("pending", "running", "shutting-down", "terminated", "stopping", "stopped", notFoundState),
},
)
}
func (cmd *CheckInstance) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DescribeInstancesInput{
InstanceIds: []string{awssdk.ToString(cmd.Id)},
}
c := &checker{
description: fmt.Sprintf("instance %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeInstances(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "InstanceNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
if res := output.Reservations; len(res) > 0 {
if instances := output.Reservations[0].Instances; len(instances) > 0 {
for _, inst := range instances {
if StringValue(inst.InstanceId) == StringValue(cmd.Id) {
return string(inst.State.Name), nil
}
}
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
type AttachInstance struct {
_ string `action:"attach" entity:"instance" awsAPI:"elbv2" awsCall:"RegisterTargets" awsInput:"elbv2.RegisterTargetsInput" awsOutput:"elbv2.RegisterTargetsOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Targetgroup *string `awsName:"TargetGroupArn" awsType:"awsstr" templateName:"targetgroup"`
Id *string `awsName:"Targets[0]Id" awsType:"awsslicestruct" templateName:"id"`
Port *int64 `awsName:"Targets[0]Port" awsType:"awsslicestructint64" templateName:"port"`
}
func (cmd *AttachInstance) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("targetgroup"),
params.Opt("port"),
))
}
type DetachInstance struct {
_ string `action:"detach" entity:"instance" awsAPI:"elbv2" awsCall:"DeregisterTargets" awsInput:"elbv2.DeregisterTargetsInput" awsOutput:"elbv2.DeregisterTargetsOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Targetgroup *string `awsName:"TargetGroupArn" awsType:"awsstr" templateName:"targetgroup"`
Id *string `awsName:"Targets[0]Id" awsType:"awsslicestruct" templateName:"id"`
}
func (cmd *DetachInstance) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("targetgroup")))
}
func idToIds(values map[string]interface{}) (map[string]interface{}, error) {
if id, hasID := values["id"]; hasID {
return map[string]interface{}{"ids": id}, nil
} else {
return nil, nil
}
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"strings"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"time"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/wallix/awless/logger"
)
type CreateInstanceprofile struct {
_ string `action:"create" entity:"instanceprofile" awsAPI:"iam" awsCall:"CreateInstanceProfile" awsInput:"iam.CreateInstanceProfileInput" awsOutput:"iam.CreateInstanceProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"InstanceProfileName" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateInstanceprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
type DeleteInstanceprofile struct {
_ string `action:"delete" entity:"instanceprofile" awsAPI:"iam" awsCall:"DeleteInstanceProfile" awsInput:"iam.DeleteInstanceProfileInput" awsOutput:"iam.DeleteInstanceProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"InstanceProfileName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteInstanceprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
type AttachInstanceprofile struct {
_ string `action:"attach" entity:"instanceprofile" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
Name *string `awsName:"IamInstanceProfile.Name" awsType:"awsstr" templateName:"name"`
Replace *bool `templateName:"replace"`
}
func (cmd *AttachInstanceprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("instance"), params.Key("name"),
params.Opt("replace"),
))
}
func (cmd *AttachInstanceprofile) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if BoolValue(cmd.Replace) {
in := &ec2.DescribeIamInstanceProfileAssociationsInput{
Filters: []ec2types.Filter{
{Name: String("instance-id"), Values: []string{StringValue(cmd.Instance)}},
},
}
out, err := cmd.api.DescribeIamInstanceProfileAssociations(context.Background(), in)
if err != nil {
return nil, fmt.Errorf("replace mode on: cannot get: %s", err)
}
if assocs := out.IamInstanceProfileAssociations; len(assocs) > 0 {
for _, ass := range assocs {
cmd.logger.ExtraVerbosef("dry run: attach instanceprofile: existing instanceprofile %s (state: %s) on instance %s", StringValue(ass.IamInstanceProfile.Id), string(ass.State), StringValue(cmd.Instance))
}
}
}
cmd.logger.Verbose("params dry run: attach instanceprofile ok")
return fakeDryRunId("instanceprofile"), nil
}
func (cmd *AttachInstanceprofile) ManualRun(renv env.Running) (interface{}, error) {
instanceId := StringValue(cmd.Instance)
profileName := StringValue(cmd.Name)
if BoolValue(cmd.Replace) {
out, err := cmd.api.DescribeIamInstanceProfileAssociations(context.Background(),
&ec2.DescribeIamInstanceProfileAssociationsInput{
Filters: []ec2types.Filter{
{Name: String("instance-id"), Values: []string{instanceId}},
{Name: String("state"), Values: []string{"associated"}},
},
})
if err != nil {
logger.Warningf("attach instanceprofile: replace mode on: dry run was ok but now cannot get instance profile association")
}
assoc := out.IamInstanceProfileAssociations
if len(assoc) > 0 {
start := time.Now()
assocId := StringValue(assoc[0].AssociationId)
assocInstId := StringValue(assoc[0].InstanceId)
oldProfileArn := StringValue(assoc[0].IamInstanceProfile.Arn)
cmd.logger.ExtraVerbosef("attach profile: found existing profile to replace with %s", profileName)
if assocInstId == instanceId {
out, err := cmd.api.ReplaceIamInstanceProfileAssociation(context.Background(),
&ec2.ReplaceIamInstanceProfileAssociationInput{
AssociationId: String(assocId),
IamInstanceProfile: &ec2types.IamInstanceProfileSpecification{
Name: String(profileName),
},
})
if err != nil {
return nil, fmt.Errorf("attach instanceprofile: replace mode on: cannot replace with new instance profile: %s", err)
}
cmd.logger.Verbosef("attach profile: replaced profile '%s' with '%s' on instance %s", oldProfileArn, profileName, instanceId)
cmd.logger.ExtraVerbosef("ec2.ReplaceIamInstanceProfileAssociation call took %s", time.Since(start))
return out, nil
}
}
}
input := &ec2.AssociateIamInstanceProfileInput{}
if err := setFieldWithType(instanceId, input, "InstanceId", awsstr, renv.Context()); err != nil {
return nil, err
}
if err := setFieldWithType(profileName, input, "IamInstanceProfile.Name", awsstr, renv.Context()); err != nil {
return nil, err
}
start := time.Now()
output, err := cmd.api.AssociateIamInstanceProfile(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.AssociateIamInstanceProfile call took %s", time.Since(start))
return output, err
}
type DetachInstanceprofile struct {
_ string `action:"detach" entity:"instanceprofile" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Instance *string `templateName:"instance"`
Name *string `templateName:"name"`
}
func (cmd *DetachInstanceprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("instance"), params.Key("name")))
}
func (cmd *DetachInstanceprofile) ManualRun(renv env.Running) (interface{}, error) {
instanceId := StringValue(cmd.Instance)
profileName := StringValue(cmd.Name)
out, err := cmd.api.DescribeIamInstanceProfileAssociations(context.Background(),
&ec2.DescribeIamInstanceProfileAssociationsInput{
Filters: []ec2types.Filter{
{Name: String("instance-id"), Values: []string{instanceId}},
},
})
if err != nil {
return nil, fmt.Errorf("cannot list profile on instance %s: %s", instanceId, err)
}
assocs := out.IamInstanceProfileAssociations
if len(assocs) < 1 {
cmd.logger.Infof("detach instanceprofile: nothing to be detached on instance %s", instanceId)
return nil, nil
}
var lastId string
for _, ass := range assocs {
if strings.Contains(StringValue(ass.IamInstanceProfile.Arn), profileName) {
input := &ec2.DisassociateIamInstanceProfileInput{
AssociationId: ass.AssociationId,
}
start := time.Now()
output, err := cmd.api.DisassociateIamInstanceProfile(context.Background(), input)
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ec2.DisassociateIamInstanceProfile call took %s", time.Since(start))
id := StringValue(output.IamInstanceProfileAssociation.IamInstanceProfile.Id)
lastId = id
}
}
return lastId, nil
}
func (cmd *DetachInstanceprofile) ExtractResult(i interface{}) string {
if i != nil {
return fmt.Sprint(i)
}
return ""
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateInternetgateway struct {
_ string `action:"create" entity:"internetgateway" awsAPI:"ec2" awsCall:"CreateInternetGateway" awsInput:"ec2.CreateInternetGatewayInput" awsOutput:"ec2.CreateInternetGatewayOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
}
func (cmd *CreateInternetgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.None())
}
func (cmd *CreateInternetgateway) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateInternetGatewayOutput).InternetGateway.InternetGatewayId)
}
type DeleteInternetgateway struct {
_ string `action:"delete" entity:"internetgateway" awsAPI:"ec2" awsCall:"DeleteInternetGateway" awsInput:"ec2.DeleteInternetGatewayInput" awsOutput:"ec2.DeleteInternetGatewayOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"InternetGatewayId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteInternetgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type AttachInternetgateway struct {
_ string `action:"attach" entity:"internetgateway" awsAPI:"ec2" awsCall:"AttachInternetGateway" awsInput:"ec2.AttachInternetGatewayInput" awsOutput:"ec2.AttachInternetGatewayOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"InternetGatewayId" awsType:"awsstr" templateName:"id"`
Vpc *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
}
func (cmd *AttachInternetgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("vpc")))
}
type DetachInternetgateway struct {
_ string `action:"detach" entity:"internetgateway" awsAPI:"ec2" awsCall:"DetachInternetGateway" awsInput:"ec2.DetachInternetGatewayInput" awsOutput:"ec2.DetachInternetGatewayOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"InternetGatewayId" awsType:"awsstr" templateName:"id"`
Vpc *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
}
func (cmd *DetachInternetgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("vpc")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/console"
"github.com/wallix/awless/logger"
)
const keyDirEnv = "__AWLESS_KEYS_DIR"
type CreateKeypair struct {
_ string `action:"create" entity:"keypair" awsAPI:"ec2" awsCall:"ImportKeyPair" awsInput:"ec2.ImportKeyPairInput" awsOutput:"ec2.ImportKeyPairOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Name *string `awsName:"KeyName" awsType:"awsstr" templateName:"name"`
Encrypted *bool `templateName:"encrypted"`
PublicKeyMaterial []byte `awsName:"PublicKeyMaterial" awsType:"awsbyteslice"`
}
func (cmd *CreateKeypair) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("name"), params.Opt("encrypted")),
params.Validators{
"name": func(i interface{}, others map[string]interface{}) error {
keyDir := os.Getenv(keyDirEnv)
if keyDir == "" {
return fmt.Errorf("empty env var '%s'", keyDirEnv)
}
privKeyPath := filepath.Join(keyDir, fmt.Sprint(i)+".pem")
if _, err := os.Stat(privKeyPath); err == nil {
return fmt.Errorf("file already exists at path: %s", privKeyPath)
}
return nil
},
})
}
func (cmd *CreateKeypair) BeforeRun(renv env.Running) error {
var encryptedMsg string
var encrypted bool
if BoolValue(cmd.Encrypted) {
encrypted = true
encryptedMsg = " encrypted"
}
privKeyPath := filepath.Join(os.Getenv(keyDirEnv), StringValue(cmd.Name)+".pem")
if _, err := os.Stat(privKeyPath); err == nil {
return fmt.Errorf("saving private key: file already exists at path: %s", privKeyPath)
}
cmd.logger.Infof("Generating locally%s 4096 RSA at %s", encryptedMsg, privKeyPath)
start := time.Now()
pub, priv, err := console.GenerateSSHKeyPair(4096, encrypted)
cmd.logger.ExtraVerbosef("4096 bits key generation took %s", time.Since(start))
if err != nil {
return fmt.Errorf("generating key: %s", err)
}
if err = os.WriteFile(privKeyPath, priv, 0400); err != nil {
return fmt.Errorf("saving private key: %s", err)
}
cmd.PublicKeyMaterial = pub
return nil
}
func (cmd *CreateKeypair) ExtractResult(i interface{}) string {
return StringValue(i.(*ec2.ImportKeyPairOutput).KeyName)
}
type DeleteKeypair struct {
_ string `action:"delete" entity:"keypair" awsAPI:"ec2" awsCall:"DeleteKeyPair" awsInput:"ec2.DeleteKeyPairInput" awsOutput:"ec2.DeleteKeyPairOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Name *string `awsName:"KeyName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteKeypair) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateLaunchconfiguration struct {
_ string `action:"create" entity:"launchconfiguration" awsAPI:"autoscaling" awsCall:"CreateLaunchConfiguration" awsInput:"autoscaling.CreateLaunchConfigurationInput" awsOutput:"autoscaling.CreateLaunchConfigurationOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Image *string `awsName:"ImageId" awsType:"awsstr" templateName:"image"`
Type *string `awsName:"InstanceType" awsType:"awsstr" templateName:"type"`
Name *string `awsName:"LaunchConfigurationName" awsType:"awsstr" templateName:"name"`
Public *bool `awsName:"AssociatePublicIpAddress" awsType:"awsbool" templateName:"public"`
Keypair *string `awsName:"KeyName" awsType:"awsstr" templateName:"keypair"`
Userdata *string `awsName:"UserData" awsType:"awsuserdatatobase64" templateName:"userdata"`
Securitygroups []*string `awsName:"SecurityGroups" awsType:"awsstringslice" templateName:"securitygroups"`
Role *string `awsName:"IamInstanceProfile" awsType:"awsstr" templateName:"role"`
Spotprice *string `awsName:"SpotPrice" awsType:"awsstr" templateName:"spotprice"`
DistroQuery *string `awsType:"awsstr" templateName:"distro"`
}
func (cmd *CreateLaunchconfiguration) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(
params.OnlyOneOf(params.Key("distro"), params.Key("image")),
params.Key("name"), params.Key("type"),
params.Opt("keypair", "public", "role", "securitygroups", "spotprice", "userdata"),
))
builder.AddReducer(func(values map[string]interface{}) (map[string]interface{}, error) {
fn := CommandFactory.Build("createinstance")().(*CreateInstance).convertDistroToAMI
return fn(values)
}, "distro")
return builder.Done()
}
func (cmd *CreateLaunchconfiguration) ExtractResult(i interface{}) string {
return StringValue(cmd.Name)
}
type DeleteLaunchconfiguration struct {
_ string `action:"delete" entity:"launchconfiguration" awsAPI:"autoscaling" awsCall:"DeleteLaunchConfiguration" awsInput:"autoscaling.DeleteLaunchConfigurationInput" awsOutput:"autoscaling.DeleteLaunchConfigurationOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Name *string `awsName:"LaunchConfigurationName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteLaunchconfiguration) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateListener struct {
_ string `action:"create" entity:"listener" awsAPI:"elbv2" awsCall:"CreateListener" awsInput:"elbv2.CreateListenerInput" awsOutput:"elbv2.CreateListenerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Actiontype *string `awsName:"DefaultActions[0]Type" awsType:"awsslicestruct" templateName:"actiontype"`
Targetgroup *string `awsName:"DefaultActions[0]TargetGroupArn" awsType:"awsslicestruct" templateName:"targetgroup"`
Loadbalancer *string `awsName:"LoadBalancerArn" awsType:"awsstr" templateName:"loadbalancer"`
Port *int64 `awsName:"Port" awsType:"awsint64" templateName:"port"`
Protocol *string `awsName:"Protocol" awsType:"awsstr" templateName:"protocol"`
Certificate *string `awsName:"Certificates[0]CertificateArn" awsType:"awsslicestruct" templateName:"certificate"`
Sslpolicy *string `awsName:"SslPolicy" awsType:"awsstr" templateName:"sslpolicy"`
}
func (cmd *CreateListener) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("actiontype"), params.Key("loadbalancer"), params.Key("port"), params.Key("protocol"), params.Key("targetgroup"),
params.Opt("certificate", "sslpolicy"),
))
}
func (cmd *CreateListener) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*elbv2.CreateListenerOutput).Listeners[0].ListenerArn)
}
type AttachListener struct {
_ string `action:"attach" entity:"listener" awsAPI:"elbv2" awsCall:"AddListenerCertificates" awsInput:"elbv2.AddListenerCertificatesInput" awsOutput:"elbv2.AddListenerCertificatesOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `awsName:"ListenerArn" awsType:"awsstr" templateName:"id"`
Certificate *string `awsName:"Certificates[0]CertificateArn" awsType:"awsslicestruct" templateName:"certificate"`
}
func (cmd *AttachListener) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("certificate")))
}
type DeleteListener struct {
_ string `action:"delete" entity:"listener" awsAPI:"elbv2" awsCall:"DeleteListener" awsInput:"elbv2.DeleteListenerInput" awsOutput:"elbv2.DeleteListenerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `awsName:"ListenerArn" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteListener) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
)
type CreateLoadbalancer struct {
_ string `action:"create" entity:"loadbalancer" awsAPI:"elbv2" awsCall:"CreateLoadBalancer" awsInput:"elbv2.CreateLoadBalancerInput" awsOutput:"elbv2.CreateLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
Subnets []*string `awsName:"Subnets" awsType:"awsstringslice" templateName:"subnets"`
SubnetMappings []*string `awsName:"SubnetMappings" awsType:"awssubnetmappings" templateName:"subnet-mappings"`
Iptype *string `awsName:"IpAddressType" awsType:"awsstr" templateName:"iptype"`
Scheme *string `awsName:"Scheme" awsType:"awsstr" templateName:"scheme"`
Securitygroups []*string `awsName:"SecurityGroups" awsType:"awsstringslice" templateName:"securitygroups"`
Type *string `awsName:"Type" awsType:"awsstr" templateName:"type"`
}
func (cmd *CreateLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(
params.Key("name"), params.Key("subnets"),
params.Opt("subnet-mappings", "iptype", "scheme", "securitygroups", "type"),
))
}
func (cmd *CreateLoadbalancer) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*elbv2.CreateLoadBalancerOutput).LoadBalancers[0].LoadBalancerArn)
}
type DeleteLoadbalancer struct {
_ string `action:"delete" entity:"loadbalancer" awsAPI:"elbv2" awsCall:"DeleteLoadBalancer" awsInput:"elbv2.DeleteLoadBalancerInput" awsOutput:"elbv2.DeleteLoadBalancerOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `awsName:"LoadBalancerArn" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type CheckLoadbalancer struct {
_ string `action:"check" entity:"loadbalancer" awsAPI:"elbv2"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckLoadbalancer) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("provisioning", "active", "failed", notFoundState),
})
}
func (cmd *CheckLoadbalancer) ManualRun(renv env.Running) (interface{}, error) {
input := &elbv2.DescribeLoadBalancersInput{
LoadBalancerArns: []string{awssdk.ToString(cmd.Id)},
}
c := &checker{
description: fmt.Sprintf("loadbalancer %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeLoadBalancers(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "LoadBalancerNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
for _, lb := range output.LoadBalancers {
if StringValue(lb.LoadBalancerArn) == StringValue(cmd.Id) {
return string(lb.State.Code), nil
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateLoginprofile struct {
_ string `action:"create" entity:"loginprofile" awsAPI:"iam" awsCall:"CreateLoginProfile" awsInput:"iam.CreateLoginProfileInput" awsOutput:"iam.CreateLoginProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Username *string `awsName:"UserName" awsType:"awsstr" templateName:"username"`
Password *string `awsName:"Password" awsType:"awsstr" templateName:"password"`
PasswordReset *bool `awsName:"PasswordResetRequired" awsType:"awsbool" templateName:"password-reset"`
}
func (cmd *CreateLoginprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("password"), params.Key("username"),
params.Opt("password-reset"),
))
}
func (cmd *CreateLoginprofile) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*iam.CreateLoginProfileOutput).LoginProfile.UserName)
}
type UpdateLoginprofile struct {
_ string `action:"update" entity:"loginprofile" awsAPI:"iam" awsCall:"UpdateLoginProfile" awsInput:"iam.UpdateLoginProfileInput" awsOutput:"iam.UpdateLoginProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Username *string `awsName:"UserName" awsType:"awsstr" templateName:"username"`
Password *string `awsName:"Password" awsType:"awsstr" templateName:"password"`
PasswordReset *bool `awsName:"PasswordResetRequired" awsType:"awsbool" templateName:"password-reset"`
}
func (cmd *UpdateLoginprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("password"), params.Key("username"),
params.Opt("password-reset"),
))
}
type DeleteLoginprofile struct {
_ string `action:"delete" entity:"loginprofile" awsAPI:"iam" awsCall:"DeleteLoginProfile" awsInput:"iam.DeleteLoginProfileInput" awsOutput:"iam.DeleteLoginProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Username *string `awsName:"UserName" awsType:"awsstr" templateName:"username"`
}
func (cmd *DeleteLoginprofile) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("username")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
awsconfig "github.com/wallix/awless/aws/config"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
"github.com/chzyer/readline"
"github.com/fatih/color"
"github.com/wallix/awless/logger"
)
type CreateMfadevice struct {
_ string `action:"create" entity:"mfadevice" awsAPI:"iam"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `templateName:"name"`
}
func (cmd *CreateMfadevice) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateMfadevice) ManualRun(renv env.Running) (interface{}, error) {
name := StringValue(cmd.Name)
input := &iam.CreateVirtualMFADeviceInput{
VirtualMFADeviceName: cmd.Name,
}
var err error
start := time.Now()
var output *iam.CreateVirtualMFADeviceOutput
output, err = cmd.api.CreateVirtualMFADevice(context.Background(), input)
if err != nil {
return nil, fmt.Errorf("%s", err)
}
cmd.logger.ExtraVerbosef("iam.CreateVirtualMFADevice call took %s", time.Since(start))
cmd.logger.Infof("MFA virtual device created. Here is the secret to create virtual device: %s.", string(output.VirtualMFADevice.Base32StringSeed))
cmd.logger.Infof("You can also use this QRCode:")
qrCodeURI := fmt.Sprintf("otpauth://totp/AWS:%s?secret=%s", name, string(output.VirtualMFADevice.Base32StringSeed))
qrcode, err := qr.Encode(qrCodeURI, qr.L, qr.Auto)
if err != nil {
return nil, fmt.Errorf("encode qrcode: %s", err)
}
qrCodeDisplaySize := 40
qrcode, err = barcode.Scale(qrcode, qrCodeDisplaySize, qrCodeDisplaySize)
if err != nil {
return nil, fmt.Errorf("scale qrcode: %s", err)
}
displayQRCode(os.Stderr, qrcode)
cmd.logger.Warning("This is your only opportunity to view the secret. You will not have access to the secret again after this step.\n")
return output, nil
}
func (cmd *CreateMfadevice) ExtractResult(i interface{}) string {
return StringValue(i.(*iam.CreateVirtualMFADeviceOutput).VirtualMFADevice.SerialNumber)
}
type DeleteMfadevice struct {
_ string `action:"delete" entity:"mfadevice" awsAPI:"iam" awsCall:"DeleteVirtualMFADevice" awsInput:"iam.DeleteVirtualMFADeviceInput" awsOutput:"iam.DeleteVirtualMFADeviceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Id *string `awsName:"SerialNumber" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteMfadevice) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
var (
awsConfigFilepath = filepath.Join(awsconfig.AWSHomeDir(), "config")
)
type AttachMfadevice struct {
_ string `action:"attach" entity:"mfadevice" awsAPI:"iam" awsCall:"EnableMFADevice" awsInput:"iam.EnableMFADeviceInput" awsOutput:"iam.EnableMFADeviceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Id *string `awsName:"SerialNumber" awsType:"awsstr" templateName:"id"`
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
MfaCode1 *string `awsName:"AuthenticationCode1" awsType:"aws6digitsstring" templateName:"mfa-code-1"`
MfaCode2 *string `awsName:"AuthenticationCode2" awsType:"aws6digitsstring" templateName:"mfa-code-2"`
NoPrompt *bool `templateName:"no-prompt"`
}
func (cmd *AttachMfadevice) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("mfa-code-1"), params.Key("mfa-code-2"), params.Key("user"),
params.Opt("no-prompt"),
))
}
func (cmd *AttachMfadevice) AfterRun(renv env.Running, output interface{}) error {
if !BoolValue(cmd.NoPrompt) {
if promptConfirm("\nDo you want to create a profile for this MFA device in %s?", awsConfigFilepath) {
roleArn, err := promptRole(cmd.api)
for err != nil {
if !promptConfirm("\nDo you want to create a profile for this MFA device in %s?", awsConfigFilepath) {
return nil
}
roleArn, err = promptRole(cmd.api)
}
fmt.Fprintln(os.Stderr)
srcProfile := promptStringWithDefault("Enter source profile used to assume role: (default) ", "default")
mfaProfile := promptStringWithDefault("Enter new MFA profile name: (mfa) ", "mfa")
config := fmt.Sprintf("\n[%s]\n"+
"source_profile = %s\n"+
"mfa_serial = %s\n"+
"role_arn = %s", mfaProfile, srcProfile, StringValue(cmd.Id), roleArn)
if promptConfirm("\n%s\n\nAppend this to '%s'?", config, awsConfigFilepath) {
created, err := appendToAwsFile(config, awsConfigFilepath)
if err != nil {
cmd.logger.Error(err)
} else {
if created {
fmt.Fprintf(os.Stderr, "\n\u2713 %s created", awsConfigFilepath)
}
fmt.Fprintf(os.Stderr, "\n\u2713 New profile '%s' for MFA device stored successfully in '%s'\n\n", mfaProfile, awsConfigFilepath)
return nil
}
}
fmt.Fprintf(os.Stderr, "Canceled modification of '%s'.\n\n", awsConfigFilepath)
return nil
}
fmt.Fprintf(os.Stderr, "Canceled adding profile for MFA device to '%s'.\n\n", awsConfigFilepath)
return nil
}
return nil
}
type DetachMfadevice struct {
_ string `action:"detach" entity:"mfadevice" awsAPI:"iam" awsCall:"DeactivateMFADevice" awsInput:"iam.DeactivateMFADeviceInput" awsOutput:"iam.DeactivateMFADeviceOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Id *string `awsName:"SerialNumber" awsType:"awsstr" templateName:"id"`
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
}
func (cmd *DetachMfadevice) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("user")))
}
func displayQRCode(w io.Writer, qrCode barcode.Barcode) {
white := color.New(color.BgWhite)
black := color.New(color.BgBlack)
for x := 0; x < qrCode.Bounds().Dx(); x++ {
for y := 0; y < qrCode.Bounds().Dy(); y++ {
r32, g32, b32, _ := qrCode.At(x, y).RGBA()
r, g, b := int(r32>>8), int(g32>>8), int(b32>>8)
if (r+g+b)/3 > 180 {
white.Fprint(w, " ")
} else {
black.Fprint(w, " ")
}
}
fmt.Fprintln(w)
}
}
func promptStringWithDefault(msg, def string) (res string) {
fmt.Fprintf(os.Stderr, "%s", msg)
fmt.Scanln(&res)
res = strings.TrimSpace(res)
if res == "" {
res = def
}
return
}
func promptRole(api *iam.Client) (string, error) {
rolesNameToArn := make(map[string]string)
paginator := iam.NewListRolesPaginator(api, &iam.ListRolesInput{})
var err error
for paginator.HasMorePages() {
out, e := paginator.NextPage(context.Background())
if e != nil {
err = e
break
}
for _, role := range out.Roles {
rolesNameToArn[StringValue(role.RoleName)] = StringValue(role.Arn)
}
}
if err == nil && len(rolesNameToArn) > 0 {
var roles []readline.PrefixCompleterInterface
for name, arn := range rolesNameToArn {
roles = append(roles, readline.PcItem(name))
roles = append(roles, readline.PcItem(arn))
}
var roleCompleter = readline.NewPrefixCompleter(roles...)
fmt.Fprint(os.Stderr, "Please specify the role (name or ARN) to assume with this MFA device: (Tab for completion) \n")
rl, err := readline.NewEx(&readline.Config{
Prompt: "> ",
AutoComplete: roleCompleter,
})
if err != nil {
return "", fmt.Errorf("error while selecting role: %s", err)
}
defer rl.Close()
role, err := rl.Readline()
if err != nil {
return "", fmt.Errorf("error while selecting role: %s", err)
}
if arn, isName := rolesNameToArn[strings.TrimSpace(role)]; isName {
return arn, nil
}
return role, nil
}
//No permission to list roles:
var roleArn string
fmt.Fprint(os.Stderr, "Please specify the role ARN to assume with this MFA device:")
fmt.Scanln(&roleArn)
roleArn = strings.TrimSpace(roleArn)
if roleArn == "" {
return roleArn, errors.New("Role cannot be empty")
}
return roleArn, nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateNatgateway struct {
_ string `action:"create" entity:"natgateway" awsAPI:"ec2" awsCall:"CreateNatGateway" awsInput:"ec2.CreateNatGatewayInput" awsOutput:"ec2.CreateNatGatewayOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
ElasticipId *string `awsName:"AllocationId" awsType:"awsstr" templateName:"elasticip-id"`
Subnet *string `awsName:"SubnetId" awsType:"awsstr" templateName:"subnet"`
}
func (cmd *CreateNatgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("elasticip-id"), params.Key("subnet")))
}
func (cmd *CreateNatgateway) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateNatGatewayOutput).NatGateway.NatGatewayId)
}
type DeleteNatgateway struct {
_ string `action:"delete" entity:"natgateway" awsAPI:"ec2" awsCall:"DeleteNatGateway" awsInput:"ec2.DeleteNatGatewayInput" awsOutput:"ec2.DeleteNatGatewayOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"NatGatewayId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteNatgateway) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type CheckNatgateway struct {
_ string `action:"check" entity:"natgateway" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckNatgateway) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("pending", "failed", "available", "deleting", "deleted", notFoundState),
})
}
func (cmd *CheckNatgateway) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DescribeNatGatewaysInput{
NatGatewayIds: []string{awssdk.ToString(cmd.Id)},
}
c := &checker{
description: fmt.Sprintf("natgateway %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeNatGateways(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "NatGatewayNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
for _, nat := range output.NatGateways {
if StringValue(nat.NatGatewayId) == StringValue(cmd.Id) {
return string(nat.State), nil
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateNetworkinterface struct {
_ string `action:"create" entity:"networkinterface" awsAPI:"ec2" awsCall:"CreateNetworkInterface" awsInput:"ec2.CreateNetworkInterfaceInput" awsOutput:"ec2.CreateNetworkInterfaceOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Subnet *string `awsName:"SubnetId" awsType:"awsstr" templateName:"subnet"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
Securitygroups []*string `awsName:"Groups" awsType:"awsstringslice" templateName:"securitygroups"`
Privateip *string `awsName:"PrivateIpAddress" awsType:"awsstr" templateName:"privateip"`
}
func (cmd *CreateNetworkinterface) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("subnet"), params.Opt("description", "privateip", "securitygroups")),
params.Validators{"privateip": params.IsIP},
)
}
func (cmd *CreateNetworkinterface) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateNetworkInterfaceOutput).NetworkInterface.NetworkInterfaceId)
}
type DeleteNetworkinterface struct {
_ string `action:"delete" entity:"networkinterface" awsAPI:"ec2" awsCall:"DeleteNetworkInterface" awsInput:"ec2.DeleteNetworkInterfaceInput" awsOutput:"ec2.DeleteNetworkInterfaceOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"NetworkInterfaceId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteNetworkinterface) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type AttachNetworkinterface struct {
_ string `action:"attach" entity:"networkinterface" awsAPI:"ec2" awsCall:"AttachNetworkInterface" awsInput:"ec2.AttachNetworkInterfaceInput" awsOutput:"ec2.AttachNetworkInterfaceOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"NetworkInterfaceId" awsType:"awsstr" templateName:"id"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
DeviceIndex *int64 `awsName:"DeviceIndex" awsType:"awsint64" templateName:"device-index"`
}
func (cmd *AttachNetworkinterface) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("device-index"), params.Key("id"), params.Key("instance")))
}
func (cmd *AttachNetworkinterface) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.AttachNetworkInterfaceOutput).AttachmentId)
}
type DetachNetworkinterface struct {
_ string `action:"detach" entity:"networkinterface" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Attachment *string `awsName:"AttachmentId" awsType:"awsstr" templateName:"attachment"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
Id *string `awsName:"NetworkInterfaceId" awsType:"awsstr" templateName:"id"`
Force *bool `awsName:"Force" awsType:"awsbool" templateName:"force"`
}
func (cmd *DetachNetworkinterface) ParamsSpec() params.Spec {
return params.NewSpec(params.OnlyOneOf(
params.AllOf(params.Key("instance"), params.Key("id")),
params.Key("attachment"),
params.Opt("force"),
))
}
func (cmd *DetachNetworkinterface) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DetachNetworkInterfaceInput{}
input.DryRun = Bool(true)
if cmd.Attachment != nil {
if err := setFieldWithType(cmd.Attachment, input, "AttachmentId", awsstr, renv.Context()); err != nil {
return nil, err
}
} else if cmd.Instance != nil && cmd.Id != nil {
attachId, err := cmd.findAttachmentBetweenInstanceAndNetworkInterface(cmd.Instance, cmd.Id)
if err == nil && attachId != "" {
input.AttachmentId = awssdk.String(attachId)
} else {
return nil, err
}
} else {
return nil, errors.New("either required 'attachment' or ('instance' and 'id')")
}
if cmd.Force != nil {
if err := setFieldWithType(cmd.Force, input, "Force", awsbool, renv.Context()); err != nil {
return nil, err
}
}
_, err := cmd.api.DetachNetworkInterface(context.Background(), input)
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound):
id := fakeDryRunId("networkinterface")
cmd.logger.Verbose("dry run: detach networkinterface ok")
return id, nil
}
}
return nil, err
}
func (cmd *DetachNetworkinterface) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DetachNetworkInterfaceInput{}
if cmd.Attachment != nil {
if err := setFieldWithType(cmd.Attachment, input, "AttachmentId", awsstr, renv.Context()); err != nil {
return nil, err
}
} else if cmd.Instance != nil && cmd.Id != nil {
attachId, err := cmd.findAttachmentBetweenInstanceAndNetworkInterface(cmd.Instance, cmd.Id)
if err == nil && attachId != "" {
input.AttachmentId = awssdk.String(attachId)
} else {
return nil, err
}
} else {
return nil, errors.New("detach networkinterface: either required 'attachment' or ('instance' and 'id')")
}
if cmd.Force != nil {
if err := setFieldWithType(cmd.Force, input, "Force", awsbool, renv.Context()); err != nil {
return nil, err
}
}
start := time.Now()
output, err := cmd.api.DetachNetworkInterface(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.DetachNetworkInterface call took %s", time.Since(start))
return output, err
}
type CheckNetworkinterface struct {
_ string `action:"check" entity:"networkinterface" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckNetworkinterface) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("available", "attaching", "detaching", "in-use", notFoundState),
})
}
func (cmd *CheckNetworkinterface) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DescribeNetworkInterfacesInput{
NetworkInterfaceIds: []string{awssdk.ToString(cmd.Id)},
}
c := &checker{
description: fmt.Sprintf("network interface %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeNetworkInterfaces(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "NetworkInterfaceNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
for _, neti := range output.NetworkInterfaces {
if StringValue(neti.NetworkInterfaceId) == StringValue(cmd.Id) {
return string(neti.Status), nil
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
func (cmd *DetachNetworkinterface) findAttachmentBetweenInstanceAndNetworkInterface(instanceId, netInterfaceId *string) (string, error) {
filters := &ec2.DescribeInstancesInput{
Filters: []ec2types.Filter{
{Name: String("network-interface.network-interface-id"), Values: []string{StringValue(netInterfaceId)}},
{Name: String("instance-id"), Values: []string{StringValue(instanceId)}},
},
}
if out, err := cmd.api.DescribeInstances(context.Background(), filters); err != nil {
return "", err
} else if reserv := out.Reservations; len(reserv) == 1 && len(reserv[0].Instances) == 1 {
for _, neti := range reserv[0].Instances[0].NetworkInterfaces {
if StringValue(netInterfaceId) == StringValue(neti.NetworkInterfaceId) {
return StringValue(neti.Attachment.AttachmentId), nil
}
}
}
return "", fmt.Errorf("not found: attachment between instance '%s' and network interface '%s'", StringValue(instanceId), StringValue(netInterfaceId))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/wallix/awless/logger"
)
type CreatePolicy struct {
_ string `action:"create" entity:"policy" awsAPI:"iam" awsCall:"CreatePolicy" awsInput:"iam.CreatePolicyInput" awsOutput:"iam.CreatePolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"PolicyName" awsType:"awsstr" templateName:"name"`
Effect *string `templateName:"effect"`
Action []*string `templateName:"action"`
Resource []*string `templateName:"resource"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
Document *string `awsName:"PolicyDocument" awsType:"awsstr"`
Conditions []*string `templateName:"conditions"`
}
func (cmd *CreatePolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("action"), params.Key("effect"), params.Key("name"), params.Key("resource"),
params.Opt("conditions", "description"),
))
}
func (cmd *CreatePolicy) BeforeRun(renv env.Running) error {
stat, err := buildStatementFromParams(cmd.Effect, cmd.Resource, cmd.Action, cmd.Conditions)
if err != nil {
return err
}
policy := &policyBody{
Version: "2012-10-17",
Statement: []*policyStatement{stat},
}
b, err := json.MarshalIndent(policy, "", " ")
if err != nil {
return fmt.Errorf("cannot marshal policy document: %s", err)
}
cmd.Document = String(string(b))
cmd.logger.ExtraVerbosef("policy document json:\n%s\n", string(b))
return nil
}
func (cmd *CreatePolicy) ExtractResult(i interface{}) string {
return StringValue(i.(*iam.CreatePolicyOutput).Policy.Arn)
}
type UpdatePolicy struct {
_ string `action:"update" entity:"policy" awsAPI:"iam" awsCall:"CreatePolicyVersion" awsInput:"iam.CreatePolicyVersionInput" awsOutput:"iam.CreatePolicyVersionOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Arn *string `awsName:"PolicyArn" awsType:"awsstr" templateName:"arn"`
Effect *string `templateName:"effect"`
Action []*string `templateName:"action"`
Resource []*string `templateName:"resource"`
Conditions []*string `templateName:"conditions"`
Document *string `awsName:"PolicyDocument" awsType:"awsstr"`
DefaultVersion *bool `awsName:"SetAsDefault" awsType:"awsbool"`
}
func (cmd *UpdatePolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("action"), params.Key("arn"), params.Key("effect"), params.Key("resource"),
params.Opt("conditions"),
))
}
func (cmd *UpdatePolicy) BeforeRun(renv env.Running) error {
document, err := cmd.getPolicyLastVersionDocument(cmd.Arn)
if err != nil {
return err
}
var defaultPolicyDocument *struct {
Version string `json:",omitempty"`
ID string `json:"Id,omitempty"`
Statements []*json.RawMessage `json:"Statement,omitempty"`
}
if err = json.Unmarshal([]byte(document), &defaultPolicyDocument); err != nil {
return err
}
stat, err := buildStatementFromParams(cmd.Effect, cmd.Resource, cmd.Action, cmd.Conditions)
if err != nil {
return err
}
var newStatement json.RawMessage
if newStatement, err = json.Marshal(stat); err != nil {
return err
}
defaultPolicyDocument.Statements = append(defaultPolicyDocument.Statements, &newStatement)
b, err := json.MarshalIndent(defaultPolicyDocument, "", " ")
if err != nil {
return fmt.Errorf("cannot marshal policy document: %s", err)
}
cmd.Document = String(string(b))
cmd.DefaultVersion = aws.Bool(true)
cmd.logger.ExtraVerbosef("policy document json:\n%s\n", string(b))
return nil
}
func (cmd *UpdatePolicy) getPolicyLastVersionDocument(arn *string) (string, error) {
listVersionsInput := &iam.ListPolicyVersionsInput{
PolicyArn: arn,
}
listVersionsOut, err := cmd.api.ListPolicyVersions(context.Background(), listVersionsInput)
if err != nil {
return "", err
}
var defaultVersion *iamtypes.PolicyVersion
for _, version := range listVersionsOut.Versions {
if version.IsDefaultVersion {
policyDetailInput := &iam.GetPolicyVersionInput{
VersionId: version.VersionId,
PolicyArn: arn,
}
var policyDetailOutput *iam.GetPolicyVersionOutput
if policyDetailOutput, err = cmd.api.GetPolicyVersion(context.Background(), policyDetailInput); err != nil {
return "", err
}
defaultVersion = policyDetailOutput.PolicyVersion
}
}
if defaultVersion == nil {
return "", fmt.Errorf("update policy: can not find default version for policy with arn '%s'", StringValue(arn))
}
document, err := url.QueryUnescape(aws.ToString(defaultVersion.Document))
if err != nil {
return "", fmt.Errorf("decoding policy document: %s", err)
}
return document, nil
}
type DeletePolicy struct {
_ string `action:"delete" entity:"policy" awsAPI:"iam" awsCall:"DeletePolicy" awsInput:"iam.DeletePolicyInput" awsOutput:"iam.DeletePolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Arn *string `awsName:"PolicyArn" awsType:"awsstr" templateName:"arn"`
AllVersions *bool `templateName:"all-versions"`
}
func (cmd *DeletePolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("arn"),
params.Opt("all-versions"),
))
}
func (cmd *DeletePolicy) BeforeRun(renv env.Running) error {
if BoolValue(cmd.AllVersions) {
list, err := cmd.api.ListPolicyVersions(context.Background(), &iam.ListPolicyVersionsInput{PolicyArn: cmd.Arn})
if err != nil {
return fmt.Errorf("list all policy versions: %s", err)
}
for _, v := range list.Versions {
if !v.IsDefaultVersion {
cmd.logger.Verbosef("deleting version '%s' of policy '%s'", aws.ToString(v.VersionId), StringValue(cmd.Arn))
if _, err := cmd.api.DeletePolicyVersion(context.Background(), &iam.DeletePolicyVersionInput{PolicyArn: cmd.Arn, VersionId: v.VersionId}); err != nil {
return fmt.Errorf("delete version %s: %s", aws.ToString(v.VersionId), err)
}
}
}
}
return nil
}
type AttachPolicy struct {
_ string `action:"attach" entity:"policy" awsAPI:"iam"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Arn *string `awsName:"PolicyArn" awsType:"awsstr" templateName:"arn"`
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
Group *string `awsName:"GroupName" awsType:"awsstr" templateName:"group"`
Role *string `awsName:"RoleName" awsType:"awsstr" templateName:"role"`
Service *string `templateName:"service"`
Access *string `templateName:"access"`
}
func (cmd *AttachPolicy) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(
params.OnlyOneOf(params.Key("user"), params.Key("role"), params.Key("group")),
params.OnlyOneOf(params.Key("arn"), params.AllOf(params.Key("access"), params.Key("service"))),
))
builder.AddReducer(transformAccessServiceToARN, "access", "service")
return builder.Done()
}
func transformAccessServiceToARN(values map[string]interface{}) (map[string]interface{}, error) {
service, hasService := values["service"].(string)
access, hasAccess := values["access"].(string)
if hasService && hasAccess {
pol, err := lookupAWSPolicy(service, access)
if err != nil {
return values, err
}
return map[string]interface{}{"arn": pol.Arn}, nil
} else {
return nil, nil
}
}
func (cmd *AttachPolicy) ManualRun(renv env.Running) (interface{}, error) {
start := time.Now()
switch {
case cmd.User != nil:
input := &iam.AttachUserPolicyInput{}
input.PolicyArn = cmd.Arn
input.UserName = cmd.User
output, err := cmd.api.AttachUserPolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.AttachUserPolicy call took %s", time.Since(start))
return output, err
case cmd.Group != nil:
input := &iam.AttachGroupPolicyInput{}
input.PolicyArn = cmd.Arn
input.GroupName = cmd.Group
output, err := cmd.api.AttachGroupPolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.AttachGroupPolicy call took %s", time.Since(start))
return output, err
case cmd.Role != nil:
input := &iam.AttachRolePolicyInput{}
input.PolicyArn = cmd.Arn
input.RoleName = cmd.Role
output, err := cmd.api.AttachRolePolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.AttachRolePolicy call took %s", time.Since(start))
return output, err
default:
return nil, errors.New("missing one of 'user, group, role' param")
}
}
type DetachPolicy struct {
_ string `action:"detach" entity:"policy" awsAPI:"iam"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Arn *string `awsName:"PolicyArn" awsType:"awsstr" templateName:"arn"`
User *string `awsName:"UserName" awsType:"awsstr" templateName:"user"`
Group *string `awsName:"GroupName" awsType:"awsstr" templateName:"group"`
Role *string `awsName:"RoleName" awsType:"awsstr" templateName:"role"`
}
func (cmd *DetachPolicy) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(
params.OnlyOneOf(params.Key("user"), params.Key("role"), params.Key("group")),
params.OnlyOneOf(params.Key("arn"), params.AllOf(params.Key("access"), params.Key("service"))),
))
builder.AddReducer(transformAccessServiceToARN, "access", "service")
return builder.Done()
}
func (cmd *DetachPolicy) ManualRun(renv env.Running) (interface{}, error) {
start := time.Now()
switch {
case cmd.User != nil:
input := &iam.DetachUserPolicyInput{}
input.PolicyArn = cmd.Arn
input.UserName = cmd.User
output, err := cmd.api.DetachUserPolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.DetachUserPolicy call took %s", time.Since(start))
return output, err
case cmd.Group != nil:
input := &iam.DetachGroupPolicyInput{}
input.PolicyArn = cmd.Arn
input.GroupName = cmd.Group
output, err := cmd.api.DetachGroupPolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.DetachGroupPolicy call took %s", time.Since(start))
return output, err
case cmd.Role != nil:
input := &iam.DetachRolePolicyInput{}
input.PolicyArn = cmd.Arn
input.RoleName = cmd.Role
output, err := cmd.api.DetachRolePolicy(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.DetachRolePolicy call took %s", time.Since(start))
return output, err
default:
return nil, errors.New("missing one of 'user, group, role' param")
}
}
type policyBody struct {
Version string
Statement []*policyStatement
}
type policyStatement struct {
Effect string `json:",omitempty"`
Actions []string `json:"Action,omitempty"`
Resources []string `json:"Resource,omitempty"`
Principal *principal `json:",omitempty"`
Conditions policyConditions `json:"Condition,omitempty"`
}
type principal struct {
AWS interface{} `json:",omitempty"`
Service interface{} `json:",omitempty"`
}
type policyCondition struct {
Type string
Key string
Value string
}
func buildStatementFromParams(effect *string, resource, action, condition []*string) (*policyStatement, error) {
stat := &policyStatement{Effect: strings.Title(StringValue(effect))}
if resource != nil {
res := castStringSlice(resource)
if len(res) == 1 && res[0] == "all" {
res[0] = "*"
}
stat.Resources = res
}
if action != nil {
stat.Actions = castStringSlice(action)
}
if condition != nil {
condStr := castStringSlice(condition)
for _, str := range condStr {
cond, err := parseCondition(str)
if err != nil {
return stat, err
}
stat.Conditions = append(stat.Conditions, cond)
}
}
return stat, nil
}
type policyConditions []*policyCondition
func (c *policyConditions) MarshalJSON() ([]byte, error) {
if c == nil {
return []byte("\"\""), nil
}
var buff bytes.Buffer
buff.WriteRune('{')
for i, cond := range *c {
buff.WriteString(fmt.Sprintf("\"%s\":{\"%s\":\"%s\"}", cond.Type, cond.Key, cond.Value))
if i < len(*c)-1 {
buff.WriteRune(',')
}
}
buff.WriteRune('}')
return buff.Bytes(), nil
}
var conditionRegex = regexp.MustCompile(`^([a-zA-Z0-9:_\-\[\]\*]+)(==|!=|=~|!~|<=|>=|<|>)(.*)$`)
func parseCondition(condition string) (*policyCondition, error) {
matches := conditionRegex.FindStringSubmatch(condition)
if len(matches) < 4 {
return nil, fmt.Errorf("invalid condition '%s'", condition)
}
key, operator, value := matches[1], matches[2], matches[3]
key = strings.TrimSpace(key)
value = strings.TrimSpace(value)
if strings.HasPrefix(value, "'") && strings.HasSuffix(value, "'") && len(value) >= 2 {
value = value[1 : len(value)-1]
}
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") && len(value) >= 2 {
value = value[1 : len(value)-1]
}
if strings.ToLower(value) == "null" {
switch operator {
case "==":
return &policyCondition{Type: "Null", Key: key, Value: "true"}, nil
case "!=":
return &policyCondition{Type: "Null", Key: key, Value: "false"}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for null value '%s', expected either '==' or '!='", operator, value)
}
} else if strings.HasPrefix(value, "arn:") {
switch operator {
case "==":
return &policyCondition{Type: "ArnEquals", Key: key, Value: value}, nil
case "!=":
return &policyCondition{Type: "ArnNotEquals", Key: key, Value: value}, nil
case "=~":
return &policyCondition{Type: "ArnLike", Key: key, Value: value}, nil
case "!~":
return &policyCondition{Type: "ArnNotLike", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for arn value '%s', expected either '==', '!=', '=~' or '!~'", operator, value)
}
} else if _, _, cidrErr := net.ParseCIDR(value); cidrErr == nil || net.ParseIP(value) != nil {
switch operator {
case "==":
return &policyCondition{Type: "IpAddress", Key: key, Value: value}, nil
case "!=":
return &policyCondition{Type: "NotIpAddress", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for IP value '%s', expected either '==' or '!='", operator, value)
}
} else if _, err := time.Parse("2006-01-02T15:04:05Z", value); err == nil {
switch operator {
case "==":
return &policyCondition{Type: "DateEquals", Key: key, Value: value}, nil
case "!=":
return &policyCondition{Type: "DateNotEquals", Key: key, Value: value}, nil
case "<":
return &policyCondition{Type: "DateLessThan", Key: key, Value: value}, nil
case "<=":
return &policyCondition{Type: "DateLessThanEquals", Key: key, Value: value}, nil
case ">":
return &policyCondition{Type: "DateGreaterThan", Key: key, Value: value}, nil
case ">=":
return &policyCondition{Type: "DateGreaterThanEquals", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for date value '%s', expected either '==', '!=', '>', '>=', '<' or '<='", operator, value)
}
} else if _, err := strconv.Atoi(value); err == nil {
switch operator {
case "==":
return &policyCondition{Type: "NumericEquals", Key: key, Value: value}, nil
case "!=":
return &policyCondition{Type: "NumericNotEquals", Key: key, Value: value}, nil
case "<":
return &policyCondition{Type: "NumericLessThan", Key: key, Value: value}, nil
case "<=":
return &policyCondition{Type: "NumericLessThanEquals", Key: key, Value: value}, nil
case ">":
return &policyCondition{Type: "NumericGreaterThan", Key: key, Value: value}, nil
case ">=":
return &policyCondition{Type: "NumericGreaterThanEquals", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for int value '%s', expected either '==', '!=', '>', '>=', '<' or '<='", operator, value)
}
} else if b, err := strconv.ParseBool(value); err == nil {
switch operator {
case "==":
return &policyCondition{Type: "Bool", Key: key, Value: fmt.Sprint(b)}, nil
case "!=":
return &policyCondition{Type: "Bool", Key: key, Value: fmt.Sprint(!b)}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for bool value '%s', expected either '==' or '!='", operator, value)
}
} else if _, err := base64.StdEncoding.DecodeString(value); value != "" && err == nil {
switch operator {
case "==":
return &policyCondition{Type: "BinaryEquals", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for binary value '%s', expected '=='", operator, value)
}
} else {
switch operator {
case "==":
return &policyCondition{Type: "StringEquals", Key: key, Value: value}, nil
case "!=":
return &policyCondition{Type: "StringNotEquals", Key: key, Value: value}, nil
case "=~":
return &policyCondition{Type: "StringLike", Key: key, Value: value}, nil
case "!~":
return &policyCondition{Type: "StringNotLike", Key: key, Value: value}, nil
default:
return nil, fmt.Errorf("invalid operator '%s' for string value '%s', expected either '==', '!=', '=~' or '!~'", operator, value)
}
}
}
func lookupAWSPolicy(service, access string) (*policy, error) {
if access != "readonly" && access != "full" {
return nil, errors.New("looking up AWS policies: access value can only be 'readonly' or 'full'")
}
var suggestions []string
for _, p := range awsPolicies {
name := strings.ToLower(p.Name)
match := fmt.Sprintf("%s%s", strings.ToLower(service), strings.ToLower(access))
if strings.Contains(name, match) {
return p, nil
}
if strings.Contains(name, strings.ToLower(service)) {
suggestions = append(suggestions, fmt.Sprintf("\t\tarn=%s", p.Arn))
}
}
errBuff := bytes.NewBufferString(fmt.Sprintf("No AWS policy matching service '%s' and access '%s'", service, access))
if len(suggestions) > 0 {
errBuff.WriteString(". Try using the full ARN of those potential matches:\n")
errBuff.WriteString(strings.Join(suggestions, "\n"))
}
return nil, errors.New(errBuff.String())
}
type policy struct {
Name string `json:"PolicyName"`
Id string `json:"PolicyId"`
Arn string `json:"Arn"`
}
var awsPolicies = []*policy{
{
Name: "AWSDirectConnectReadOnlyAccess",
Id: "ANPAI23HZ27SI6FQMGNQ2",
Arn: "arn:aws:iam::aws:policy/AWSDirectConnectReadOnlyAccess",
},
{
Name: "AmazonGlacierReadOnlyAccess",
Id: "ANPAI2D5NJKMU274MET4E",
Arn: "arn:aws:iam::aws:policy/AmazonGlacierReadOnlyAccess",
},
{
Name: "AWSMarketplaceFullAccess",
Id: "ANPAI2DV5ULJSO2FYVPYG",
Arn: "arn:aws:iam::aws:policy/AWSMarketplaceFullAccess",
},
{
Name: "AutoScalingConsoleReadOnlyAccess",
Id: "ANPAI3A7GDXOYQV3VUQMK",
Arn: "arn:aws:iam::aws:policy/AutoScalingConsoleReadOnlyAccess",
},
{
Name: "AmazonDMSRedshiftS3Role",
Id: "ANPAI3CCUQ4U5WNC5F6B6",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonDMSRedshiftS3Role",
},
{
Name: "AWSQuickSightListIAM",
Id: "ANPAI3CH5UUWZN4EKGILO",
Arn: "arn:aws:iam::aws:policy/service-role/AWSQuickSightListIAM",
},
{
Name: "AWSHealthFullAccess",
Id: "ANPAI3CUMPCPEUPCSXC4Y",
Arn: "arn:aws:iam::aws:policy/AWSHealthFullAccess",
},
{
Name: "AmazonRDSFullAccess",
Id: "ANPAI3R4QMOG6Q5A4VWVG",
Arn: "arn:aws:iam::aws:policy/AmazonRDSFullAccess",
},
{
Name: "SupportUser",
Id: "ANPAI3V4GSSN5SJY3P2RO",
Arn: "arn:aws:iam::aws:policy/job-function/SupportUser",
},
{
Name: "AmazonEC2FullAccess",
Id: "ANPAI3VAJF5ZCRZ7MCQE6",
Arn: "arn:aws:iam::aws:policy/AmazonEC2FullAccess",
},
{
Name: "AWSElasticBeanstalkReadOnlyAccess",
Id: "ANPAI47KNGXDAXFD4SDHG",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkReadOnlyAccess",
},
{
Name: "AWSCertificateManagerReadOnly",
Id: "ANPAI4GSWX6S4MESJ3EWC",
Arn: "arn:aws:iam::aws:policy/AWSCertificateManagerReadOnly",
},
{
Name: "AWSQuicksightAthenaAccess",
Id: "ANPAI4JB77JXFQXDWNRPM",
Arn: "arn:aws:iam::aws:policy/service-role/AWSQuicksightAthenaAccess",
},
{
Name: "AWSCodeCommitPowerUser",
Id: "ANPAI4UIINUVGB5SEC57G",
Arn: "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser",
},
{
Name: "AWSCodeCommitFullAccess",
Id: "ANPAI4VCZ3XPIZLQ5NZV2",
Arn: "arn:aws:iam::aws:policy/AWSCodeCommitFullAccess",
},
{
Name: "IAMSelfManageServiceSpecificCredentials",
Id: "ANPAI4VT74EMXK2PMQJM2",
Arn: "arn:aws:iam::aws:policy/IAMSelfManageServiceSpecificCredentials",
},
{
Name: "AmazonSQSFullAccess",
Id: "ANPAI65L554VRJ33ECQS6",
Arn: "arn:aws:iam::aws:policy/AmazonSQSFullAccess",
},
{
Name: "AWSLambdaFullAccess",
Id: "ANPAI6E2CYYMI4XI7AA5K",
Arn: "arn:aws:iam::aws:policy/AWSLambdaFullAccess",
},
{
Name: "AWSIoTLogging",
Id: "ANPAI6R6Z2FHHGS454W7W",
Arn: "arn:aws:iam::aws:policy/service-role/AWSIoTLogging",
},
{
Name: "AmazonEC2RoleforSSM",
Id: "ANPAI6TL3SMY22S4KMMX6",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM",
},
{
Name: "AWSCloudHSMRole",
Id: "ANPAI7QIUU4GC66SF26WE",
Arn: "arn:aws:iam::aws:policy/service-role/AWSCloudHSMRole",
},
{
Name: "IAMFullAccess",
Id: "ANPAI7XKCFMBPM3QQRRVQ",
Arn: "arn:aws:iam::aws:policy/IAMFullAccess",
},
{
Name: "AmazonInspectorFullAccess",
Id: "ANPAI7Y6NTA27NWNA5U5E",
Arn: "arn:aws:iam::aws:policy/AmazonInspectorFullAccess",
},
{
Name: "AmazonElastiCacheFullAccess",
Id: "ANPAIA2V44CPHAUAAECKG",
Arn: "arn:aws:iam::aws:policy/AmazonElastiCacheFullAccess",
},
{
Name: "AWSAgentlessDiscoveryService",
Id: "ANPAIA3DIL7BYQ35ISM4K",
Arn: "arn:aws:iam::aws:policy/AWSAgentlessDiscoveryService",
},
{
Name: "AWSXrayWriteOnlyAccess",
Id: "ANPAIAACM4LMYSRGBCTM6",
Arn: "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess",
},
{
Name: "AutoScalingReadOnlyAccess",
Id: "ANPAIAFWUVLC2LPLSFTFG",
Arn: "arn:aws:iam::aws:policy/AutoScalingReadOnlyAccess",
},
{
Name: "AutoScalingFullAccess",
Id: "ANPAIAWRCSJDDXDXGPCFU",
Arn: "arn:aws:iam::aws:policy/AutoScalingFullAccess",
},
{
Name: "AmazonEC2RoleforAWSCodeDeploy",
Id: "ANPAIAZKXZ27TAJ4PVWGK",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforAWSCodeDeploy",
},
{
Name: "AWSMobileHub_ReadOnly",
Id: "ANPAIBXVYVL3PWQFBZFGW",
Arn: "arn:aws:iam::aws:policy/AWSMobileHub_ReadOnly",
},
{
Name: "CloudWatchEventsBuiltInTargetExecutionAccess",
Id: "ANPAIC5AQ5DATYSNF4AUM",
Arn: "arn:aws:iam::aws:policy/service-role/CloudWatchEventsBuiltInTargetExecutionAccess",
},
{
Name: "AmazonCloudDirectoryReadOnlyAccess",
Id: "ANPAICMSZQGR3O62KMD6M",
Arn: "arn:aws:iam::aws:policy/AmazonCloudDirectoryReadOnlyAccess",
},
{
Name: "AWSOpsWorksFullAccess",
Id: "ANPAICN26VXMXASXKOQCG",
Arn: "arn:aws:iam::aws:policy/AWSOpsWorksFullAccess",
},
{
Name: "AWSOpsWorksCMInstanceProfileRole",
Id: "ANPAICSU3OSHCURP2WIZW",
Arn: "arn:aws:iam::aws:policy/AWSOpsWorksCMInstanceProfileRole",
},
{
Name: "AWSCodePipelineApproverAccess",
Id: "ANPAICXNWK42SQ6LMDXM2",
Arn: "arn:aws:iam::aws:policy/AWSCodePipelineApproverAccess",
},
{
Name: "AWSApplicationDiscoveryAgentAccess",
Id: "ANPAICZIOVAGC6JPF3WHC",
Arn: "arn:aws:iam::aws:policy/AWSApplicationDiscoveryAgentAccess",
},
{
Name: "ViewOnlyAccess",
Id: "ANPAID22R6XPJATWOFDK6",
Arn: "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess",
},
{
Name: "AmazonElasticMapReduceRole",
Id: "ANPAIDI2BQT2LKXZG36TW",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole",
},
{
Name: "AmazonRoute53DomainsReadOnlyAccess",
Id: "ANPAIDRINP6PPTRXYVQCI",
Arn: "arn:aws:iam::aws:policy/AmazonRoute53DomainsReadOnlyAccess",
},
{
Name: "AWSOpsWorksRole",
Id: "ANPAIDUTMOKHJFAPJV45W",
Arn: "arn:aws:iam::aws:policy/service-role/AWSOpsWorksRole",
},
{
Name: "ApplicationAutoScalingForAmazonAppStreamAccess",
Id: "ANPAIEL3HJCCWFVHA6KPG",
Arn: "arn:aws:iam::aws:policy/service-role/ApplicationAutoScalingForAmazonAppStreamAccess",
},
{
Name: "AmazonEC2ContainerRegistryFullAccess",
Id: "ANPAIESRL7KD7IIVF6V4W",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess",
},
{
Name: "SimpleWorkflowFullAccess",
Id: "ANPAIFE3AV6VE7EANYBVM",
Arn: "arn:aws:iam::aws:policy/SimpleWorkflowFullAccess",
},
{
Name: "AmazonS3FullAccess",
Id: "ANPAIFIR6V6BVTRAHWINE",
Arn: "arn:aws:iam::aws:policy/AmazonS3FullAccess",
},
{
Name: "AWSStorageGatewayReadOnlyAccess",
Id: "ANPAIFKCTUVOPD5NICXJK",
Arn: "arn:aws:iam::aws:policy/AWSStorageGatewayReadOnlyAccess",
},
{
Name: "Billing",
Id: "ANPAIFTHXT6FFMIRT7ZEA",
Arn: "arn:aws:iam::aws:policy/job-function/Billing",
},
{
Name: "QuickSightAccessForS3StorageManagementAnalyticsReadOnly",
Id: "ANPAIFWG3L3WDMR4I7ZJW",
Arn: "arn:aws:iam::aws:policy/service-role/QuickSightAccessForS3StorageManagementAnalyticsReadOnly",
},
{
Name: "AmazonEC2ContainerRegistryReadOnly",
Id: "ANPAIFYZPA37OOHVIH7KQ",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
},
{
Name: "AmazonElasticMapReduceforEC2Role",
Id: "ANPAIGALS5RCDLZLB3PGS",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role",
},
{
Name: "DatabaseAdministrator",
Id: "ANPAIGBMAW4VUQKOQNVT6",
Arn: "arn:aws:iam::aws:policy/job-function/DatabaseAdministrator",
},
{
Name: "AmazonRedshiftReadOnlyAccess",
Id: "ANPAIGD46KSON64QBSEZM",
Arn: "arn:aws:iam::aws:policy/AmazonRedshiftReadOnlyAccess",
},
{
Name: "AmazonEC2ReadOnlyAccess",
Id: "ANPAIGDT4SV4GSETWTBZK",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess",
},
{
Name: "AWSXrayReadOnlyAccess",
Id: "ANPAIH4OFXWPS6ZX6OPGQ",
Arn: "arn:aws:iam::aws:policy/AWSXrayReadOnlyAccess",
},
{
Name: "AWSElasticBeanstalkEnhancedHealth",
Id: "ANPAIH5EFJNMOGUUTKLFE",
Arn: "arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth",
},
{
Name: "AmazonElasticMapReduceReadOnlyAccess",
Id: "ANPAIHP6NH2S6GYFCOINC",
Arn: "arn:aws:iam::aws:policy/AmazonElasticMapReduceReadOnlyAccess",
},
{
Name: "AWSDirectoryServiceReadOnlyAccess",
Id: "ANPAIHWYO6WSDNCG64M2W",
Arn: "arn:aws:iam::aws:policy/AWSDirectoryServiceReadOnlyAccess",
},
{
Name: "AmazonVPCReadOnlyAccess",
Id: "ANPAIICZJNOJN36GTG6CM",
Arn: "arn:aws:iam::aws:policy/AmazonVPCReadOnlyAccess",
},
{
Name: "CloudWatchEventsReadOnlyAccess",
Id: "ANPAIILJPXXA6F7GYLYBS",
Arn: "arn:aws:iam::aws:policy/CloudWatchEventsReadOnlyAccess",
},
{
Name: "AmazonAPIGatewayInvokeFullAccess",
Id: "ANPAIIWAX2NOOQJ4AIEQ6",
Arn: "arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess",
},
{
Name: "AmazonKinesisAnalyticsReadOnly",
Id: "ANPAIJIEXZAFUK43U7ARK",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisAnalyticsReadOnly",
},
{
Name: "AmazonMobileAnalyticsFullAccess",
Id: "ANPAIJIKLU2IJ7WJ6DZFG",
Arn: "arn:aws:iam::aws:policy/AmazonMobileAnalyticsFullAccess",
},
{
Name: "AWSMobileHub_FullAccess",
Id: "ANPAIJLU43R6AGRBK76DM",
Arn: "arn:aws:iam::aws:policy/AWSMobileHub_FullAccess",
},
{
Name: "AmazonAPIGatewayPushToCloudWatchLogs",
Id: "ANPAIK4GFO7HLKYN64ASK",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs",
},
{
Name: "AWSDataPipelineRole",
Id: "ANPAIKCP6XS3ESGF4GLO2",
Arn: "arn:aws:iam::aws:policy/service-role/AWSDataPipelineRole",
},
{
Name: "CloudWatchFullAccess",
Id: "ANPAIKEABORKUXN6DEAZU",
Arn: "arn:aws:iam::aws:policy/CloudWatchFullAccess",
},
{
Name: "ServiceCatalogAdminFullAccess",
Id: "ANPAIKTX42IAS75B7B7BY",
Arn: "arn:aws:iam::aws:policy/ServiceCatalogAdminFullAccess",
},
{
Name: "AmazonRDSDirectoryServiceAccess",
Id: "ANPAIL4KBY57XWMYUHKUU",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonRDSDirectoryServiceAccess",
},
{
Name: "AWSCodePipelineReadOnlyAccess",
Id: "ANPAILFKZXIBOTNC5TO2Q",
Arn: "arn:aws:iam::aws:policy/AWSCodePipelineReadOnlyAccess",
},
{
Name: "ReadOnlyAccess",
Id: "ANPAILL3HVNFSB6DCOWYQ",
Arn: "arn:aws:iam::aws:policy/ReadOnlyAccess",
},
{
Name: "AmazonMachineLearningBatchPredictionsAccess",
Id: "ANPAILOI4HTQSFTF3GQSC",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningBatchPredictionsAccess",
},
{
Name: "AmazonRekognitionReadOnlyAccess",
Id: "ANPAILWSUHXUY4ES43SA4",
Arn: "arn:aws:iam::aws:policy/AmazonRekognitionReadOnlyAccess",
},
{
Name: "AWSCodeDeployReadOnlyAccess",
Id: "ANPAILZHHKCKB4NE7XOIQ",
Arn: "arn:aws:iam::aws:policy/AWSCodeDeployReadOnlyAccess",
},
{
Name: "CloudSearchFullAccess",
Id: "ANPAIM6OOWKQ7L7VBOZOC",
Arn: "arn:aws:iam::aws:policy/CloudSearchFullAccess",
},
{
Name: "AWSCloudHSMFullAccess",
Id: "ANPAIMBQYQZM7F63DA2UU",
Arn: "arn:aws:iam::aws:policy/AWSCloudHSMFullAccess",
},
{
Name: "AmazonEC2SpotFleetAutoscaleRole",
Id: "ANPAIMFFRMIOBGDP2TAVE",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetAutoscaleRole",
},
{
Name: "AWSCodeBuildDeveloperAccess",
Id: "ANPAIMKTMR34XSBQW45HS",
Arn: "arn:aws:iam::aws:policy/AWSCodeBuildDeveloperAccess",
},
{
Name: "AmazonEC2SpotFleetRole",
Id: "ANPAIMRTKHWK7ESSNETSW",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole",
},
{
Name: "AWSDataPipeline_PowerUser",
Id: "ANPAIMXGLVY6DVR24VTYS",
Arn: "arn:aws:iam::aws:policy/AWSDataPipeline_PowerUser",
},
{
Name: "AmazonElasticTranscoderJobsSubmitter",
Id: "ANPAIN5WGARIKZ3E2UQOU",
Arn: "arn:aws:iam::aws:policy/AmazonElasticTranscoderJobsSubmitter",
},
{
Name: "AWSCodeStarServiceRole",
Id: "ANPAIN6D4M2KD3NBOC4M4",
Arn: "arn:aws:iam::aws:policy/service-role/AWSCodeStarServiceRole",
},
{
Name: "AWSDirectoryServiceFullAccess",
Id: "ANPAINAW5ANUWTH3R4ANI",
Arn: "arn:aws:iam::aws:policy/AWSDirectoryServiceFullAccess",
},
{
Name: "AmazonDynamoDBFullAccess",
Id: "ANPAINUGF2JSOSUY76KYA",
Arn: "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess",
},
{
Name: "AmazonSESReadOnlyAccess",
Id: "ANPAINV2XPFRMWJJNSCGI",
Arn: "arn:aws:iam::aws:policy/AmazonSESReadOnlyAccess",
},
{
Name: "AWSWAFReadOnlyAccess",
Id: "ANPAINZVDMX2SBF7EU2OC",
Arn: "arn:aws:iam::aws:policy/AWSWAFReadOnlyAccess",
},
{
Name: "AutoScalingNotificationAccessRole",
Id: "ANPAIO2VMUPGDC5PZVXVA",
Arn: "arn:aws:iam::aws:policy/service-role/AutoScalingNotificationAccessRole",
},
{
Name: "AmazonMechanicalTurkReadOnly",
Id: "ANPAIO5IY3G3WXSX5PPRM",
Arn: "arn:aws:iam::aws:policy/AmazonMechanicalTurkReadOnly",
},
{
Name: "AmazonKinesisReadOnlyAccess",
Id: "ANPAIOCMTDT5RLKZ2CAJO",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisReadOnlyAccess",
},
{
Name: "AWSCodeDeployFullAccess",
Id: "ANPAIONKN3TJZUKXCHXWC",
Arn: "arn:aws:iam::aws:policy/AWSCodeDeployFullAccess",
},
{
Name: "CloudWatchActionsEC2Access",
Id: "ANPAIOWD4E3FVSORSZTGU",
Arn: "arn:aws:iam::aws:policy/CloudWatchActionsEC2Access",
},
{
Name: "AWSLambdaDynamoDBExecutionRole",
Id: "ANPAIP7WNAGMIPYNW4WQG",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole",
},
{
Name: "AmazonRoute53DomainsFullAccess",
Id: "ANPAIPAFBMIYUILMOKL6G",
Arn: "arn:aws:iam::aws:policy/AmazonRoute53DomainsFullAccess",
},
{
Name: "AmazonElastiCacheReadOnlyAccess",
Id: "ANPAIPDACSNQHSENWAKM2",
Arn: "arn:aws:iam::aws:policy/AmazonElastiCacheReadOnlyAccess",
},
{
Name: "AmazonAthenaFullAccess",
Id: "ANPAIPJMLMD4C7RYZ6XCK",
Arn: "arn:aws:iam::aws:policy/AmazonAthenaFullAccess",
},
{
Name: "AmazonElasticFileSystemReadOnlyAccess",
Id: "ANPAIPN5S4NE5JJOKVC4Y",
Arn: "arn:aws:iam::aws:policy/AmazonElasticFileSystemReadOnlyAccess",
},
{
Name: "CloudFrontFullAccess",
Id: "ANPAIPRV52SH6HDCCFY6U",
Arn: "arn:aws:iam::aws:policy/CloudFrontFullAccess",
},
{
Name: "AmazonMachineLearningRoleforRedshiftDataSource",
Id: "ANPAIQ5UDYYMNN42BM4AK",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonMachineLearningRoleforRedshiftDataSource",
},
{
Name: "AmazonMobileAnalyticsNon-financialReportAccess",
Id: "ANPAIQLKQ4RXPUBBVVRDE",
Arn: "arn:aws:iam::aws:policy/AmazonMobileAnalyticsNon-financialReportAccess",
},
{
Name: "AWSCloudTrailFullAccess",
Id: "ANPAIQNUJTQYDRJPC3BNK",
Arn: "arn:aws:iam::aws:policy/AWSCloudTrailFullAccess",
},
{
Name: "AmazonCognitoDeveloperAuthenticatedIdentities",
Id: "ANPAIQOKZ5BGKLCMTXH4W",
Arn: "arn:aws:iam::aws:policy/AmazonCognitoDeveloperAuthenticatedIdentities",
},
{
Name: "AWSConfigRole",
Id: "ANPAIQRXRDRGJUA33ELIO",
Arn: "arn:aws:iam::aws:policy/service-role/AWSConfigRole",
},
{
Name: "AmazonAppStreamServiceAccess",
Id: "ANPAISBRZ7LMMCBYEF3SE",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonAppStreamServiceAccess",
},
{
Name: "AmazonRedshiftFullAccess",
Id: "ANPAISEKCHH4YDB46B5ZO",
Arn: "arn:aws:iam::aws:policy/AmazonRedshiftFullAccess",
},
{
Name: "AmazonZocaloReadOnlyAccess",
Id: "ANPAISRCSSJNS3QPKZJPM",
Arn: "arn:aws:iam::aws:policy/AmazonZocaloReadOnlyAccess",
},
{
Name: "AWSCloudHSMReadOnlyAccess",
Id: "ANPAISVCBSY7YDBOT67KE",
Arn: "arn:aws:iam::aws:policy/AWSCloudHSMReadOnlyAccess",
},
{
Name: "SystemAdministrator",
Id: "ANPAITJPEZXCYCBXANDSW",
Arn: "arn:aws:iam::aws:policy/job-function/SystemAdministrator",
},
{
Name: "AmazonEC2ContainerServiceEventsRole",
Id: "ANPAITKFNIUAG27VSYNZ4",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole",
},
{
Name: "AmazonRoute53ReadOnlyAccess",
Id: "ANPAITOYK2ZAOQFXV2JNC",
Arn: "arn:aws:iam::aws:policy/AmazonRoute53ReadOnlyAccess",
},
{
Name: "AmazonEC2ReportsAccess",
Id: "ANPAIU6NBZVF2PCRW36ZW",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ReportsAccess",
},
{
Name: "AmazonEC2ContainerServiceAutoscaleRole",
Id: "ANPAIUAP3EGGGXXCPDQKK",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole",
},
{
Name: "AWSBatchServiceRole",
Id: "ANPAIUETIXPCKASQJURFE",
Arn: "arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole",
},
{
Name: "AWSElasticBeanstalkWebTier",
Id: "ANPAIUF4325SJYOREKW3A",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier",
},
{
Name: "AmazonSQSReadOnlyAccess",
Id: "ANPAIUGSSQY362XGCM6KW",
Arn: "arn:aws:iam::aws:policy/AmazonSQSReadOnlyAccess",
},
{
Name: "AWSMobileHub_ServiceUseOnly",
Id: "ANPAIUHPQXBDZUWOP3PSK",
Arn: "arn:aws:iam::aws:policy/service-role/AWSMobileHub_ServiceUseOnly",
},
{
Name: "AmazonKinesisFullAccess",
Id: "ANPAIVF32HAMOXCUYRAYE",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisFullAccess",
},
{
Name: "AmazonMachineLearningReadOnlyAccess",
Id: "ANPAIW5VYBCGEX56JCINC",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningReadOnlyAccess",
},
{
Name: "AmazonRekognitionFullAccess",
Id: "ANPAIWDAOK6AIFDVX6TT6",
Arn: "arn:aws:iam::aws:policy/AmazonRekognitionFullAccess",
},
{
Name: "RDSCloudHsmAuthorizationRole",
Id: "ANPAIWKFXRLQG2ROKKXLE",
Arn: "arn:aws:iam::aws:policy/service-role/RDSCloudHsmAuthorizationRole",
},
{
Name: "AmazonMachineLearningFullAccess",
Id: "ANPAIWKW6AGSGYOQ5ERHC",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningFullAccess",
},
{
Name: "AdministratorAccess",
Id: "ANPAIWMBCKSKIEE64ZLYK",
Arn: "arn:aws:iam::aws:policy/AdministratorAccess",
},
{
Name: "AmazonMachineLearningRealTimePredictionOnlyAccess",
Id: "ANPAIWMCNQPRWMWT36GVQ",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningRealTimePredictionOnlyAccess",
},
{
Name: "AWSConfigUserAccess",
Id: "ANPAIWTTSFJ7KKJE3MWGA",
Arn: "arn:aws:iam::aws:policy/AWSConfigUserAccess",
},
{
Name: "AWSIoTConfigAccess",
Id: "ANPAIWWGD4LM4EMXNRL7I",
Arn: "arn:aws:iam::aws:policy/AWSIoTConfigAccess",
},
{
Name: "SecurityAudit",
Id: "ANPAIX2T3QCXHR2OGGCTO",
Arn: "arn:aws:iam::aws:policy/SecurityAudit",
},
{
Name: "AWSCodeStarFullAccess",
Id: "ANPAIXI233TFUGLZOJBEC",
Arn: "arn:aws:iam::aws:policy/AWSCodeStarFullAccess",
},
{
Name: "AWSDataPipeline_FullAccess",
Id: "ANPAIXOFIG7RSBMRPHXJ4",
Arn: "arn:aws:iam::aws:policy/AWSDataPipeline_FullAccess",
},
{
Name: "AmazonDynamoDBReadOnlyAccess",
Id: "ANPAIY2XFNA232XJ6J7X2",
Arn: "arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess",
},
{
Name: "AutoScalingConsoleFullAccess",
Id: "ANPAIYEN6FJGYYWJFFCZW",
Arn: "arn:aws:iam::aws:policy/AutoScalingConsoleFullAccess",
},
{
Name: "AmazonSNSReadOnlyAccess",
Id: "ANPAIZGQCQTFOFPMHSB6W",
Arn: "arn:aws:iam::aws:policy/AmazonSNSReadOnlyAccess",
},
{
Name: "AmazonElasticMapReduceFullAccess",
Id: "ANPAIZP5JFP3AMSGINBB2",
Arn: "arn:aws:iam::aws:policy/AmazonElasticMapReduceFullAccess",
},
{
Name: "AmazonS3ReadOnlyAccess",
Id: "ANPAIZTJ4DXE7G6AGAE6M",
Arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess",
},
{
Name: "AWSElasticBeanstalkFullAccess",
Id: "ANPAIZYX2YLLBW2LJVUFW",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkFullAccess",
},
{
Name: "AmazonWorkSpacesAdmin",
Id: "ANPAJ26AU6ATUQCT5KVJU",
Arn: "arn:aws:iam::aws:policy/AmazonWorkSpacesAdmin",
},
{
Name: "AWSCodeDeployRole",
Id: "ANPAJ2NKMKD73QS5NBFLA",
Arn: "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole",
},
{
Name: "AmazonSESFullAccess",
Id: "ANPAJ2P4NXCHAT7NDPNR4",
Arn: "arn:aws:iam::aws:policy/AmazonSESFullAccess",
},
{
Name: "CloudWatchLogsReadOnlyAccess",
Id: "ANPAJ2YIYDYSNNEHK3VKW",
Arn: "arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess",
},
{
Name: "AmazonKinesisFirehoseReadOnlyAccess",
Id: "ANPAJ36NT645INW4K24W6",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisFirehoseReadOnlyAccess",
},
{
Name: "AWSOpsWorksRegisterCLI",
Id: "ANPAJ3AB5ZBFPCQGTVDU4",
Arn: "arn:aws:iam::aws:policy/AWSOpsWorksRegisterCLI",
},
{
Name: "AmazonDynamoDBFullAccesswithDataPipeline",
Id: "ANPAJ3ORT7KDISSXGHJXA",
Arn: "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccesswithDataPipeline",
},
{
Name: "AmazonEC2RoleforDataPipelineRole",
Id: "ANPAJ3Z5I2WAJE5DN2J36",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforDataPipelineRole",
},
{
Name: "CloudWatchLogsFullAccess",
Id: "ANPAJ3ZGNWK2R5HW5BQFO",
Arn: "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess",
},
{
Name: "AWSElasticBeanstalkMulticontainerDocker",
Id: "ANPAJ45SBYG72SD6SHJEY",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker",
},
{
Name: "AmazonElasticTranscoderFullAccess",
Id: "ANPAJ4D5OJU75P5ZJZVNY",
Arn: "arn:aws:iam::aws:policy/AmazonElasticTranscoderFullAccess",
},
{
Name: "IAMUserChangePassword",
Id: "ANPAJ4L4MM2A7QIEB56MS",
Arn: "arn:aws:iam::aws:policy/IAMUserChangePassword",
},
{
Name: "AmazonAPIGatewayAdministrator",
Id: "ANPAJ4PT6VY5NLKTNUYSI",
Arn: "arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator",
},
{
Name: "ServiceCatalogEndUserAccess",
Id: "ANPAJ56OMCO72RI4J5FSA",
Arn: "arn:aws:iam::aws:policy/ServiceCatalogEndUserAccess",
},
{
Name: "AmazonPollyReadOnlyAccess",
Id: "ANPAJ5FENL3CVPL2FPDLA",
Arn: "arn:aws:iam::aws:policy/AmazonPollyReadOnlyAccess",
},
{
Name: "AmazonMobileAnalyticsWriteOnlyAccess",
Id: "ANPAJ5TAWBBQC2FAL3G6G",
Arn: "arn:aws:iam::aws:policy/AmazonMobileAnalyticsWriteOnlyAccess",
},
{
Name: "AmazonEC2SpotFleetTaggingRole",
Id: "ANPAJ5U6UMLCEYLX5OLC4",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetTaggingRole",
},
{
Name: "DataScientist",
Id: "ANPAJ5YHI2BQW7EQFYDXS",
Arn: "arn:aws:iam::aws:policy/job-function/DataScientist",
},
{
Name: "AWSMarketplaceMeteringFullAccess",
Id: "ANPAJ65YJPG7CC7LDXNA6",
Arn: "arn:aws:iam::aws:policy/AWSMarketplaceMeteringFullAccess",
},
{
Name: "AWSOpsWorksCMServiceRole",
Id: "ANPAJ6I6MPGJE62URSHCO",
Arn: "arn:aws:iam::aws:policy/service-role/AWSOpsWorksCMServiceRole",
},
{
Name: "AWSConnector",
Id: "ANPAJ6YATONJHICG3DJ3U",
Arn: "arn:aws:iam::aws:policy/AWSConnector",
},
{
Name: "AWSBatchFullAccess",
Id: "ANPAJ7K2KIWB3HZVK3CUO",
Arn: "arn:aws:iam::aws:policy/AWSBatchFullAccess",
},
{
Name: "ServiceCatalogAdminReadOnlyAccess",
Id: "ANPAJ7XOUSS75M4LIPKO4",
Arn: "arn:aws:iam::aws:policy/ServiceCatalogAdminReadOnlyAccess",
},
{
Name: "AmazonSSMFullAccess",
Id: "ANPAJA7V6HI4ISQFMDYAG",
Arn: "arn:aws:iam::aws:policy/AmazonSSMFullAccess",
},
{
Name: "AWSCodeCommitReadOnly",
Id: "ANPAJACNSXR7Z2VLJW3D6",
Arn: "arn:aws:iam::aws:policy/AWSCodeCommitReadOnly",
},
{
Name: "AmazonEC2ContainerServiceFullAccess",
Id: "ANPAJALOYVTPDZEMIACSM",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ContainerServiceFullAccess",
},
{
Name: "AmazonCognitoReadOnly",
Id: "ANPAJBFTRZD2GQGJHSVQK",
Arn: "arn:aws:iam::aws:policy/AmazonCognitoReadOnly",
},
{
Name: "AmazonDMSCloudWatchLogsRole",
Id: "ANPAJBG7UXZZXUJD3TDJE",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonDMSCloudWatchLogsRole",
},
{
Name: "AWSApplicationDiscoveryServiceFullAccess",
Id: "ANPAJBNJEA6ZXM2SBOPDU",
Arn: "arn:aws:iam::aws:policy/AWSApplicationDiscoveryServiceFullAccess",
},
{
Name: "AmazonVPCFullAccess",
Id: "ANPAJBWPGNOVKZD3JI2P2",
Arn: "arn:aws:iam::aws:policy/AmazonVPCFullAccess",
},
{
Name: "AWSImportExportFullAccess",
Id: "ANPAJCQCT4JGTLC6722MQ",
Arn: "arn:aws:iam::aws:policy/AWSImportExportFullAccess",
},
{
Name: "AmazonMechanicalTurkFullAccess",
Id: "ANPAJDGCL5BET73H5QIQC",
Arn: "arn:aws:iam::aws:policy/AmazonMechanicalTurkFullAccess",
},
{
Name: "AmazonEC2ContainerRegistryPowerUser",
Id: "ANPAJDNE5PIHROIBGGDDW",
Arn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser",
},
{
Name: "AmazonMachineLearningCreateOnlyAccess",
Id: "ANPAJDRUNIC2RYAMAT3CK",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningCreateOnlyAccess",
},
{
Name: "AWSCloudTrailReadOnlyAccess",
Id: "ANPAJDU7KJADWBSEQ3E7S",
Arn: "arn:aws:iam::aws:policy/AWSCloudTrailReadOnlyAccess",
},
{
Name: "AWSLambdaExecute",
Id: "ANPAJE5FX7FQZSU5XAKGO",
Arn: "arn:aws:iam::aws:policy/AWSLambdaExecute",
},
{
Name: "AWSIoTRuleActions",
Id: "ANPAJEZ6FS7BUZVUHMOKY",
Arn: "arn:aws:iam::aws:policy/service-role/AWSIoTRuleActions",
},
{
Name: "AWSQuickSightDescribeRedshift",
Id: "ANPAJFEM6MLSLTW4ZNBW2",
Arn: "arn:aws:iam::aws:policy/service-role/AWSQuickSightDescribeRedshift",
},
{
Name: "VMImportExportRoleForAWSConnector",
Id: "ANPAJFLQOOJ6F5XNX4LAW",
Arn: "arn:aws:iam::aws:policy/service-role/VMImportExportRoleForAWSConnector",
},
{
Name: "AWSCodePipelineCustomActionAccess",
Id: "ANPAJFW5Z32BTVF76VCYC",
Arn: "arn:aws:iam::aws:policy/AWSCodePipelineCustomActionAccess",
},
{
Name: "AWSOpsWorksInstanceRegistration",
Id: "ANPAJG3LCPVNI4WDZCIMU",
Arn: "arn:aws:iam::aws:policy/AWSOpsWorksInstanceRegistration",
},
{
Name: "AmazonCloudDirectoryFullAccess",
Id: "ANPAJG3XQK77ATFLCF2CK",
Arn: "arn:aws:iam::aws:policy/AmazonCloudDirectoryFullAccess",
},
{
Name: "AWSStorageGatewayFullAccess",
Id: "ANPAJG5SSPAVOGK3SIDGU",
Arn: "arn:aws:iam::aws:policy/AWSStorageGatewayFullAccess",
},
{
Name: "AmazonLexReadOnly",
Id: "ANPAJGBI5LSMAJNDGBNAM",
Arn: "arn:aws:iam::aws:policy/AmazonLexReadOnly",
},
{
Name: "AmazonElasticTranscoderReadOnlyAccess",
Id: "ANPAJGPP7GPMJRRJMEP3Q",
Arn: "arn:aws:iam::aws:policy/AmazonElasticTranscoderReadOnlyAccess",
},
{
Name: "AWSIoTConfigReadOnlyAccess",
Id: "ANPAJHENEMXGX4XMFOIOI",
Arn: "arn:aws:iam::aws:policy/AWSIoTConfigReadOnlyAccess",
},
{
Name: "AmazonWorkMailReadOnlyAccess",
Id: "ANPAJHF7J65E2QFKCWAJM",
Arn: "arn:aws:iam::aws:policy/AmazonWorkMailReadOnlyAccess",
},
{
Name: "AmazonDMSVPCManagementRole",
Id: "ANPAJHKIGMBQI4AEFFSYO",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole",
},
{
Name: "AWSLambdaKinesisExecutionRole",
Id: "ANPAJHOLKJPXV4GBRMJUQ",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole",
},
{
Name: "ResourceGroupsandTagEditorReadOnlyAccess",
Id: "ANPAJHXQTPI5I5JKAIU74",
Arn: "arn:aws:iam::aws:policy/ResourceGroupsandTagEditorReadOnlyAccess",
},
{
Name: "AmazonSSMAutomationRole",
Id: "ANPAJIBQCTBCXD2XRNB6W",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole",
},
{
Name: "ServiceCatalogEndUserFullAccess",
Id: "ANPAJIW7AFFOONVKW75KU",
Arn: "arn:aws:iam::aws:policy/ServiceCatalogEndUserFullAccess",
},
{
Name: "AWSStepFunctionsConsoleFullAccess",
Id: "ANPAJIYC52YWRX6OSMJWK",
Arn: "arn:aws:iam::aws:policy/AWSStepFunctionsConsoleFullAccess",
},
{
Name: "AWSCodeBuildReadOnlyAccess",
Id: "ANPAJIZZWN6557F5HVP2K",
Arn: "arn:aws:iam::aws:policy/AWSCodeBuildReadOnlyAccess",
},
{
Name: "AmazonMachineLearningManageRealTimeEndpointOnlyAccess",
Id: "ANPAJJL3PC3VCSVZP6OCI",
Arn: "arn:aws:iam::aws:policy/AmazonMachineLearningManageRealTimeEndpointOnlyAccess",
},
{
Name: "CloudWatchEventsInvocationAccess",
Id: "ANPAJJXD6JKJLK2WDLZNO",
Arn: "arn:aws:iam::aws:policy/service-role/CloudWatchEventsInvocationAccess",
},
{
Name: "CloudFrontReadOnlyAccess",
Id: "ANPAJJZMNYOTZCNQP36LG",
Arn: "arn:aws:iam::aws:policy/CloudFrontReadOnlyAccess",
},
{
Name: "AmazonSNSRole",
Id: "ANPAJK5GQB7CIK7KHY2GA",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonSNSRole",
},
{
Name: "AmazonMobileAnalyticsFinancialReportAccess",
Id: "ANPAJKJHO2R27TXKCWBU4",
Arn: "arn:aws:iam::aws:policy/AmazonMobileAnalyticsFinancialReportAccess",
},
{
Name: "AWSElasticBeanstalkService",
Id: "ANPAJKQ5SN74ZQ4WASXBM",
Arn: "arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkService",
},
{
Name: "IAMReadOnlyAccess",
Id: "ANPAJKSO7NDY4T57MWDSQ",
Arn: "arn:aws:iam::aws:policy/IAMReadOnlyAccess",
},
{
Name: "AmazonRDSReadOnlyAccess",
Id: "ANPAJKTTTYV2IIHKLZ346",
Arn: "arn:aws:iam::aws:policy/AmazonRDSReadOnlyAccess",
},
{
Name: "AmazonCognitoPowerUser",
Id: "ANPAJKW5H2HNCPGCYGR6Y",
Arn: "arn:aws:iam::aws:policy/AmazonCognitoPowerUser",
},
{
Name: "AmazonElasticFileSystemFullAccess",
Id: "ANPAJKXTMNVQGIDNCKPBC",
Arn: "arn:aws:iam::aws:policy/AmazonElasticFileSystemFullAccess",
},
{
Name: "ServerMigrationConnector",
Id: "ANPAJKZRWXIPK5HSG3QDQ",
Arn: "arn:aws:iam::aws:policy/ServerMigrationConnector",
},
{
Name: "AmazonZocaloFullAccess",
Id: "ANPAJLCDXYRINDMUXEVL6",
Arn: "arn:aws:iam::aws:policy/AmazonZocaloFullAccess",
},
{
Name: "AWSLambdaReadOnlyAccess",
Id: "ANPAJLDG7J3CGUHFN4YN6",
Arn: "arn:aws:iam::aws:policy/AWSLambdaReadOnlyAccess",
},
{
Name: "AWSAccountUsageReportAccess",
Id: "ANPAJLIB4VSBVO47ZSBB6",
Arn: "arn:aws:iam::aws:policy/AWSAccountUsageReportAccess",
},
{
Name: "AWSMarketplaceGetEntitlements",
Id: "ANPAJLPIMQE4WMHDC2K7C",
Arn: "arn:aws:iam::aws:policy/AWSMarketplaceGetEntitlements",
},
{
Name: "AmazonEC2ContainerServiceforEC2Role",
Id: "ANPAJLYJCVHC7TQHCSQDS",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role",
},
{
Name: "AmazonAppStreamFullAccess",
Id: "ANPAJLZZXU2YQVGL4QDNC",
Arn: "arn:aws:iam::aws:policy/AmazonAppStreamFullAccess",
},
{
Name: "AWSIoTDataAccess",
Id: "ANPAJM2KI2UJDR24XPS2K",
Arn: "arn:aws:iam::aws:policy/AWSIoTDataAccess",
},
{
Name: "AmazonESFullAccess",
Id: "ANPAJM6ZTCU24QL5PZCGC",
Arn: "arn:aws:iam::aws:policy/AmazonESFullAccess",
},
{
Name: "ServerMigrationServiceRole",
Id: "ANPAJMBH3M6BO63XFW2D4",
Arn: "arn:aws:iam::aws:policy/service-role/ServerMigrationServiceRole",
},
{
Name: "AWSWAFFullAccess",
Id: "ANPAJMIKIAFXZEGOLRH7C",
Arn: "arn:aws:iam::aws:policy/AWSWAFFullAccess",
},
{
Name: "AmazonKinesisFirehoseFullAccess",
Id: "ANPAJMZQMTZ7FRBFHHAHI",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisFirehoseFullAccess",
},
{
Name: "CloudWatchReadOnlyAccess",
Id: "ANPAJN23PDQP7SZQAE3QE",
Arn: "arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess",
},
{
Name: "AWSLambdaBasicExecutionRole",
Id: "ANPAJNCQGXC42545SKXIK",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
},
{
Name: "ResourceGroupsandTagEditorFullAccess",
Id: "ANPAJNOS54ZFXN4T2Y34A",
Arn: "arn:aws:iam::aws:policy/ResourceGroupsandTagEditorFullAccess",
},
{
Name: "AWSKeyManagementServicePowerUser",
Id: "ANPAJNPP7PPPPMJRV2SA4",
Arn: "arn:aws:iam::aws:policy/AWSKeyManagementServicePowerUser",
},
{
Name: "AWSImportExportReadOnlyAccess",
Id: "ANPAJNTV4OG52ESYZHCNK",
Arn: "arn:aws:iam::aws:policy/AWSImportExportReadOnlyAccess",
},
{
Name: "AmazonElasticTranscoderRole",
Id: "ANPAJNW3WMKVXFJ2KPIQ2",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonElasticTranscoderRole",
},
{
Name: "AmazonEC2ContainerServiceRole",
Id: "ANPAJO53W2XHNACG7V77Q",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole",
},
{
Name: "AWSDeviceFarmFullAccess",
Id: "ANPAJO7KEDP4VYJPNT5UW",
Arn: "arn:aws:iam::aws:policy/AWSDeviceFarmFullAccess",
},
{
Name: "AmazonSSMReadOnlyAccess",
Id: "ANPAJODSKQGGJTHRYZ5FC",
Arn: "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess",
},
{
Name: "AWSStepFunctionsReadOnlyAccess",
Id: "ANPAJONHB2TJQDJPFW5TM",
Arn: "arn:aws:iam::aws:policy/AWSStepFunctionsReadOnlyAccess",
},
{
Name: "AWSMarketplaceRead-only",
Id: "ANPAJOOM6LETKURTJ3XZ2",
Arn: "arn:aws:iam::aws:policy/AWSMarketplaceRead-only",
},
{
Name: "AWSCodePipelineFullAccess",
Id: "ANPAJP5LH77KSAT2KHQGG",
Arn: "arn:aws:iam::aws:policy/AWSCodePipelineFullAccess",
},
{
Name: "AWSGreengrassResourceAccessRolePolicy",
Id: "ANPAJPKEIMB6YMXDEVRTM",
Arn: "arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicy",
},
{
Name: "NetworkAdministrator",
Id: "ANPAJPNMADZFJCVPJVZA2",
Arn: "arn:aws:iam::aws:policy/job-function/NetworkAdministrator",
},
{
Name: "AmazonWorkSpacesApplicationManagerAdminAccess",
Id: "ANPAJPRL4KYETIH7XGTSS",
Arn: "arn:aws:iam::aws:policy/AmazonWorkSpacesApplicationManagerAdminAccess",
},
{
Name: "AmazonDRSVPCManagement",
Id: "ANPAJPXIBTTZMBEFEX6UA",
Arn: "arn:aws:iam::aws:policy/AmazonDRSVPCManagement",
},
{
Name: "AWSXrayFullAccess",
Id: "ANPAJQBYG45NSJMVQDB2K",
Arn: "arn:aws:iam::aws:policy/AWSXrayFullAccess",
},
{
Name: "AWSElasticBeanstalkWorkerTier",
Id: "ANPAJQDLBRSJVKVF4JMSK",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier",
},
{
Name: "AWSDirectConnectFullAccess",
Id: "ANPAJQF2QKZSK74KTIHOW",
Arn: "arn:aws:iam::aws:policy/AWSDirectConnectFullAccess",
},
{
Name: "AWSCodeBuildAdminAccess",
Id: "ANPAJQJGIOIE3CD2TQXDS",
Arn: "arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess",
},
{
Name: "AmazonKinesisAnalyticsFullAccess",
Id: "ANPAJQOSKHTXP43R7P5AC",
Arn: "arn:aws:iam::aws:policy/AmazonKinesisAnalyticsFullAccess",
},
{
Name: "AWSAccountActivityAccess",
Id: "ANPAJQRYCWMFX5J3E333K",
Arn: "arn:aws:iam::aws:policy/AWSAccountActivityAccess",
},
{
Name: "AmazonGlacierFullAccess",
Id: "ANPAJQSTZJWB2AXXAKHVQ",
Arn: "arn:aws:iam::aws:policy/AmazonGlacierFullAccess",
},
{
Name: "AmazonWorkMailFullAccess",
Id: "ANPAJQVKNMT7SVATQ4AUY",
Arn: "arn:aws:iam::aws:policy/AmazonWorkMailFullAccess",
},
{
Name: "AWSMarketplaceManageSubscriptions",
Id: "ANPAJRDW2WIFN7QLUAKBQ",
Arn: "arn:aws:iam::aws:policy/AWSMarketplaceManageSubscriptions",
},
{
Name: "AWSElasticBeanstalkCustomPlatformforEC2Role",
Id: "ANPAJRVFXSS6LEIQGBKDY",
Arn: "arn:aws:iam::aws:policy/AWSElasticBeanstalkCustomPlatformforEC2Role",
},
{
Name: "AWSSupportAccess",
Id: "ANPAJSNKQX2OW67GF4S7E",
Arn: "arn:aws:iam::aws:policy/AWSSupportAccess",
},
{
Name: "AmazonElasticMapReduceforAutoScalingRole",
Id: "ANPAJSVXG6QHPE6VHDZ4Q",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforAutoScalingRole",
},
{
Name: "AWSLambdaInvocation-DynamoDB",
Id: "ANPAJTHQ3EKCQALQDYG5G",
Arn: "arn:aws:iam::aws:policy/AWSLambdaInvocation-DynamoDB",
},
{
Name: "IAMUserSSHKeys",
Id: "ANPAJTSHUA4UXGXU7ANUA",
Arn: "arn:aws:iam::aws:policy/IAMUserSSHKeys",
},
{
Name: "AWSIoTFullAccess",
Id: "ANPAJU2FPGG6PQWN72V2G",
Arn: "arn:aws:iam::aws:policy/AWSIoTFullAccess",
},
{
Name: "AWSQuickSightDescribeRDS",
Id: "ANPAJU5J6OAMCJD3OO76O",
Arn: "arn:aws:iam::aws:policy/service-role/AWSQuickSightDescribeRDS",
},
{
Name: "AWSConfigRulesExecutionRole",
Id: "ANPAJUB3KIKTA4PU4OYAA",
Arn: "arn:aws:iam::aws:policy/service-role/AWSConfigRulesExecutionRole",
},
{
Name: "AmazonESReadOnlyAccess",
Id: "ANPAJUDMRLOQ7FPAR46FQ",
Arn: "arn:aws:iam::aws:policy/AmazonESReadOnlyAccess",
},
{
Name: "AWSCodeDeployDeployerAccess",
Id: "ANPAJUWEPOMGLMVXJAPUI",
Arn: "arn:aws:iam::aws:policy/AWSCodeDeployDeployerAccess",
},
{
Name: "AmazonPollyFullAccess",
Id: "ANPAJUZOYQU6XQYPR7EWS",
Arn: "arn:aws:iam::aws:policy/AmazonPollyFullAccess",
},
{
Name: "AmazonSSMMaintenanceWindowRole",
Id: "ANPAJV3JNYSTZ47VOXYME",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonSSMMaintenanceWindowRole",
},
{
Name: "AmazonRDSEnhancedMonitoringRole",
Id: "ANPAJV7BS425S4PTSSVGK",
Arn: "arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole",
},
{
Name: "AmazonLexFullAccess",
Id: "ANPAJVLXDHKVC23HRTKSI",
Arn: "arn:aws:iam::aws:policy/AmazonLexFullAccess",
},
{
Name: "AWSLambdaVPCAccessExecutionRole",
Id: "ANPAJVTME3YLVNL72YR2K",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole",
},
{
Name: "AmazonLexRunBotsOnly",
Id: "ANPAJVZGB5CM3N6YWJHBE",
Arn: "arn:aws:iam::aws:policy/AmazonLexRunBotsOnly",
},
{
Name: "AmazonSNSFullAccess",
Id: "ANPAJWEKLCXXUNT2SOLSG",
Arn: "arn:aws:iam::aws:policy/AmazonSNSFullAccess",
},
{
Name: "CloudSearchReadOnlyAccess",
Id: "ANPAJWPLX7N7BCC3RZLHW",
Arn: "arn:aws:iam::aws:policy/CloudSearchReadOnlyAccess",
},
{
Name: "AWSGreengrassFullAccess",
Id: "ANPAJWPV6OBK4QONH4J3O",
Arn: "arn:aws:iam::aws:policy/AWSGreengrassFullAccess",
},
{
Name: "AWSCloudFormationReadOnlyAccess",
Id: "ANPAJWVBEE4I2POWLODLW",
Arn: "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess",
},
{
Name: "AmazonRoute53FullAccess",
Id: "ANPAJWVDLG5RPST6PHQ3A",
Arn: "arn:aws:iam::aws:policy/AmazonRoute53FullAccess",
},
{
Name: "AWSLambdaRole",
Id: "ANPAJX4DPCRGTC4NFDUXI",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaRole",
},
{
Name: "AWSLambdaENIManagementAccess",
Id: "ANPAJXAW2Q3KPTURUT2QC",
Arn: "arn:aws:iam::aws:policy/service-role/AWSLambdaENIManagementAccess",
},
{
Name: "AWSOpsWorksCloudWatchLogs",
Id: "ANPAJXFIK7WABAY5CPXM4",
Arn: "arn:aws:iam::aws:policy/AWSOpsWorksCloudWatchLogs",
},
{
Name: "AmazonAppStreamReadOnlyAccess",
Id: "ANPAJXIFDGB4VBX23DX7K",
Arn: "arn:aws:iam::aws:policy/AmazonAppStreamReadOnlyAccess",
},
{
Name: "AWSStepFunctionsFullAccess",
Id: "ANPAJXKA6VP3UFBVHDPPA",
Arn: "arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess",
},
{
Name: "AmazonInspectorReadOnlyAccess",
Id: "ANPAJXQNTHTEJ2JFRN2SE",
Arn: "arn:aws:iam::aws:policy/AmazonInspectorReadOnlyAccess",
},
{
Name: "AWSCertificateManagerFullAccess",
Id: "ANPAJYCHABBP6VQIVBCBQ",
Arn: "arn:aws:iam::aws:policy/AWSCertificateManagerFullAccess",
},
{
Name: "PowerUserAccess",
Id: "ANPAJYRXTHIB4FOVS3ZXS",
Arn: "arn:aws:iam::aws:policy/PowerUserAccess",
},
{
Name: "CloudWatchEventsFullAccess",
Id: "ANPAJZLOYLNHESMYOJAFU",
Arn: "arn:aws:iam::aws:policy/CloudWatchEventsFullAccess",
},
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateQueue struct {
_ string `action:"create" entity:"queue" awsAPI:"sqs" awsCall:"CreateQueue" awsInput:"sqs.CreateQueueInput" awsOutput:"sqs.CreateQueueOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sqs.Client
Name *string `awsName:"QueueName" awsType:"awsstr" templateName:"name"`
Delay *string `awsName:"Attributes[DelaySeconds]" awsType:"awsstringpointermap" templateName:"delay"`
MaxMsgSize *string `awsName:"Attributes[MaximumMessageSize]" awsType:"awsstringpointermap" templateName:"max-msg-size"`
RetentionPeriod *string `awsName:"Attributes[MessageRetentionPeriod]" awsType:"awsstringpointermap" templateName:"retention-period"`
Policy *string `awsName:"Attributes[Policy]" awsType:"awsstringpointermap" templateName:"policy"`
MsgWait *string `awsName:"Attributes[ReceiveMessageWaitTimeSeconds]" awsType:"awsstringpointermap" templateName:"msg-wait"`
RedrivePolicy *string `awsName:"Attributes[RedrivePolicy]" awsType:"awsstringpointermap" templateName:"redrive-policy"`
VisibilityTimeout *string `awsName:"Attributes[VisibilityTimeout]" awsType:"awsstringpointermap" templateName:"visibility-timeout"`
}
func (cmd *CreateQueue) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("delay", "max-msg-size", "msg-wait", "policy", "redrive-policy", "retention-period", "visibility-timeout"),
))
}
func (cmd *CreateQueue) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*sqs.CreateQueueOutput).QueueUrl)
}
type DeleteQueue struct {
_ string `action:"delete" entity:"queue" awsAPI:"sqs" awsCall:"DeleteQueue" awsInput:"sqs.DeleteQueueInput" awsOutput:"sqs.DeleteQueueOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sqs.Client
Url *string `awsName:"QueueUrl" awsType:"awsstr" templateName:"url"`
}
func (cmd *DeleteQueue) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("url")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/match"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
"github.com/wallix/awless/logger"
)
type CreateRecord struct {
_ string `action:"create" entity:"record" awsAPI:"route53"`
logger *logger.Logger
graph cloud.GraphAPI
api *route53.Client
Zone *string `templateName:"zone"`
Name *string `templateName:"name"`
Type *string `templateName:"type"`
Values []*string `templateName:"values"`
Ttl *int64 `templateName:"ttl"`
Comment *string `templateName:"comment"`
}
func (cmd *CreateRecord) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(params.Key("name"), params.Key("ttl"), params.Key("type"), params.OnlyOneOf(params.Key("values"), params.Key("value")), params.Key("zone"),
params.Opt("comment"),
))
builder.AddReducer(valueToValues, "value")
return builder.Done()
}
func (cmd *CreateRecord) ManualRun(renv env.Running) (interface{}, error) { //nolint:staticcheck
start := time.Now()
output, err := changeResourceRecordSets(cmd.api, String("CREATE"), cmd.Zone, cmd.Name, cmd.Type, cmd.Values, cmd.Comment, cmd.Ttl)
cmd.logger.ExtraVerbosef("route53.ChangeResourceRecordSets call took %s", time.Since(start))
return output, err
}
func (cmd *CreateRecord) ExtractResult(i interface{}) string {
return StringValue(i.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo.Id)
}
type UpdateRecord struct {
_ string `action:"update" entity:"record" awsAPI:"route53"`
logger *logger.Logger
graph cloud.GraphAPI
api *route53.Client
Zone *string `templateName:"zone"`
Name *string `templateName:"name"`
Type *string `templateName:"type"`
Values []*string `templateName:"values"`
Ttl *int64 `templateName:"ttl"`
}
func (cmd *UpdateRecord) ParamsSpec() params.Spec {
builder := params.SpecBuilder(params.AllOf(params.Key("name"), params.Key("ttl"), params.Key("type"), params.OnlyOneOf(params.Key("values"), params.Key("value")), params.Key("zone")))
builder.AddReducer(valueToValues, "value")
return builder.Done()
}
func (cmd *UpdateRecord) ManualRun(renv env.Running) (interface{}, error) { //nolint:staticcheck
start := time.Now()
output, err := changeResourceRecordSets(cmd.api, String("UPSERT"), cmd.Zone, cmd.Name, cmd.Type, cmd.Values, nil, cmd.Ttl)
cmd.logger.ExtraVerbosef("route53.ChangeResourceRecordSets call took %s", time.Since(start))
return output, err
}
func (cmd *UpdateRecord) ExtractResult(i interface{}) string {
return StringValue(i.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo.Id)
}
type DeleteRecord struct {
_ string `action:"delete" entity:"record" awsAPI:"route53"`
logger *logger.Logger
graph cloud.GraphAPI
api *route53.Client
Zone *string `templateName:"zone"`
Name *string `templateName:"name"`
Type *string `templateName:"type"`
Values []*string `templateName:"values"`
Ttl *int64 `templateName:"ttl"`
}
func (cmd *DeleteRecord) ParamsSpec() params.Spec {
builder := params.SpecBuilder(
params.OnlyOneOf(
params.AllOf(params.Key("name"), params.Key("ttl"), params.Key("type"), params.OnlyOneOf(params.Key("values"), params.Key("value")), params.Key("zone")),
params.AllOf(params.Key("id")),
),
)
builder.AddReducer(valueToValues, "value")
builder.AddReducer(
func(values map[string]interface{}) (map[string]interface{}, error) {
id, hasId := values["id"].(string)
if hasId {
delete(values, "id")
r, err := cmd.graph.FindOne(cloud.NewQuery(cloud.Record).Match(match.Property(properties.ID, id)))
if err != nil {
return values, fmt.Errorf("can not find record for %s: %s", id, err)
}
if r == nil {
return values, fmt.Errorf("record not found with id '%s' in local model ", id)
}
if name, ok := r.Property(properties.Name); ok {
values["name"] = name
}
if ttl, ok := r.Property(properties.TTL); ok {
values["ttl"] = ttl
}
if t, ok := r.Property(properties.Type); ok {
values["type"] = t
}
if rec, ok := r.Property(properties.Records); ok {
values["values"] = rec
}
parents, err := cmd.graph.ResourceRelations(r, rdf.ParentOf, false)
if err != nil {
return values, fmt.Errorf("cannot get record's zone: %s", err)
}
if len(parents) != 1 || parents[0].Type() != cloud.Zone {
return values, fmt.Errorf("record is not in a zone, got %v ", parents)
}
values["zone"] = parents[0].Id()
}
return values, nil
},
"id",
)
return builder.Done()
}
func (cmd *DeleteRecord) ManualRun(renv env.Running) (interface{}, error) { //nolint:staticcheck
start := time.Now()
output, err := changeResourceRecordSets(cmd.api, String("DELETE"), cmd.Zone, cmd.Name, cmd.Type, cmd.Values, nil, cmd.Ttl)
cmd.logger.ExtraVerbosef("route53.ChangeResourceRecordSets call took %s", time.Since(start))
return output, err
}
func (cmd *DeleteRecord) ExtractResult(i interface{}) string {
return StringValue(i.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo.Id)
}
func changeResourceRecordSets(api *route53.Client, action, zone, name, recordType *string, values []*string, comment *string, ttl *int64) (*route53.ChangeResourceRecordSetsOutput, error) {
input := &route53.ChangeResourceRecordSetsInput{}
var err error
// Required params
err = setFieldWithType(zone, input, "HostedZoneId", awsstr)
if err != nil {
return nil, err
}
change := &route53types.Change{ResourceRecordSet: &route53types.ResourceRecordSet{}}
if err = setFieldWithType(action, change, "Action", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType(name, change, "ResourceRecordSet.Name", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType(recordType, change, "ResourceRecordSet.Type", awsstr); err != nil {
return nil, err
}
if err = setFieldWithType(ttl, change, "ResourceRecordSet.TTL", awsint64); err != nil {
return nil, err
}
for _, value := range values {
resourceRecord := &route53types.ResourceRecord{}
if err = setFieldWithType(value, resourceRecord, "Value", awsstr); err != nil {
return nil, err
}
change.ResourceRecordSet.ResourceRecords = append(change.ResourceRecordSet.ResourceRecords, *resourceRecord)
}
input.ChangeBatch = &route53types.ChangeBatch{Changes: []route53types.Change{*change}}
// Extra params
if comment != nil {
if err = setFieldWithType(comment, input, "ChangeBatch.Comment", awsstr); err != nil {
return nil, err
}
}
return api.ChangeResourceRecordSets(context.Background(), input)
}
func valueToValues(values map[string]interface{}) (map[string]interface{}, error) {
if value, hasValue := values["value"]; hasValue {
return map[string]interface{}{"values": value}, nil
} else {
return nil, nil
}
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"encoding/base64"
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/wallix/awless/logger"
)
type AuthenticateRegistry struct {
_ string `action:"authenticate" entity:"registry" awsAPI:"ecr"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecr.Client
Accounts []*string `templateName:"accounts"`
NoConfirm *bool `templateName:"no-confirm"`
DisableDockerCmd *bool `templateName:"no-docker-login"`
}
func (cmd *AuthenticateRegistry) ParamsSpec() params.Spec {
return params.NewSpec(params.AtLeastOneOf(params.Key("accounts"), params.Key("no-confirm"), params.Key("no-docker-login")))
}
func (cmd *AuthenticateRegistry) ManualRun(renv env.Running) (interface{}, error) {
input := &ecr.GetAuthorizationTokenInput{}
var err error
// Extra params
if len(cmd.Accounts) > 0 {
err = setFieldWithType(cmd.Accounts, input, "RegistryIds", awsstringslice)
if err != nil {
return nil, err
}
}
start := time.Now()
output, err := cmd.api.GetAuthorizationToken(context.Background(), input)
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ecr.GetAuthorizationToken call took %s", time.Since(start))
for _, auth := range output.AuthorizationData {
token := aws.ToString(auth.AuthorizationToken)
decoded, err := base64.StdEncoding.DecodeString(token)
if err != nil {
return nil, err
}
credentials := strings.SplitN(string(decoded), ":", 2)
if len(credentials) != 2 {
return nil, fmt.Errorf("invalid authorization token: expect user:password, got %s", decoded)
}
torun := []string{"docker", "login", "--username", credentials[0], "--password", credentials[1], StringValue(auth.ProxyEndpoint)}
if BoolValue(cmd.DisableDockerCmd) {
cmd.logger.Infof("Docker authentication command:\n%s", strings.Join(torun, " "))
} else {
confirm := !(BoolValue(cmd.NoConfirm))
if confirm {
fmt.Fprintf(os.Stderr, "\nDocker authentication command:\n\n%s\n\nDo you want to run this command:(y/n)? ", strings.Join(torun, " "))
var yesorno string
_, err := fmt.Scanln(&yesorno)
if err != nil {
return nil, err
}
if strings.ToLower(yesorno) != "y" {
return nil, nil
}
}
dockerCmd := exec.Command("docker", torun[1:]...)
out, err := dockerCmd.Output()
if err != nil {
if e, ok := err.(*exec.ExitError); ok {
return nil, fmt.Errorf("error running docker command: %s", e.Stderr)
}
return nil, fmt.Errorf("error running docker command: %s", err)
}
if len(out) > 0 {
cmd.logger.Info(string(out))
}
}
}
return nil, nil
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecr"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateRepository struct {
_ string `action:"create" entity:"repository" awsAPI:"ecr" awsCall:"CreateRepository" awsInput:"ecr.CreateRepositoryInput" awsOutput:"ecr.CreateRepositoryOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecr.Client
Name *string `awsName:"RepositoryName" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateRepository) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateRepository) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ecr.CreateRepositoryOutput).Repository.RepositoryArn)
}
type DeleteRepository struct {
_ string `action:"delete" entity:"repository" awsAPI:"ecr" awsCall:"DeleteRepository" awsInput:"ecr.DeleteRepositoryInput" awsOutput:"ecr.DeleteRepositoryOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ecr.Client
Name *string `awsName:"RepositoryName" awsType:"awsstr" templateName:"name"`
Force *bool `awsName:"Force" awsType:"awsbool" templateName:"force"`
Account *string `awsName:"RegistryId" awsType:"awsstr" templateName:"account"`
}
func (cmd *DeleteRepository) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("account", "force"),
))
}
/*
Copyright 2017 WALLIX
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 awsspec
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/wallix/awless/logger"
)
type CreateRole struct {
_ string `action:"create" entity:"role" awsAPI:"iam"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"RoleName" awsType:"awsstr" templateName:"name" `
PrincipalAccount *string `templateName:"principal-account"`
PrincipalUser *string `templateName:"principal-user"`
PrincipalService *string `templateName:"principal-service"`
Conditions []*string `templateName:"conditions"`
SleepAfter *int64 `templateName:"sleep-after"`
}
func (cmd *CreateRole) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("conditions", "principal-account", "principal-service", "principal-user", "sleep-after"),
))
}
func (cmd *CreateRole) ManualRun(renv env.Running) (interface{}, error) {
princ := new(principal)
if cmd.PrincipalAccount != nil {
princ.AWS = StringValue(cmd.PrincipalAccount)
} else if cmd.PrincipalUser != nil {
princ.AWS = StringValue(cmd.PrincipalUser)
} else if cmd.PrincipalService != nil {
princ.Service = StringValue(cmd.PrincipalService)
}
stat, err := buildStatementFromParams(String("Allow"), nil, []*string{String("sts:AssumeRole")}, cmd.Conditions)
if err != nil {
return nil, err
}
stat.Principal = princ
trust := &policyBody{
Version: "2012-10-17",
Statement: []*policyStatement{stat},
}
b, err := json.MarshalIndent(trust, "", " ")
if err != nil {
return nil, errors.New("cannot marshal role trust policy document")
}
cmd.logger.ExtraVerbosef("role trust policy document json:\n%s\n", string(b))
call := &awsCall{
fnName: "iam.CreateRole",
fn: cmd.api.CreateRole,
logger: cmd.logger,
setters: []setter{
{val: cmd.Name, fieldPath: "RoleName", fieldType: awsstr},
{val: string(b), fieldPath: "AssumeRolePolicyDocument", fieldType: awsstr},
},
}
output, err := call.execute(&iam.CreateRoleInput{})
if err != nil {
return nil, err
}
role := output.(*iam.CreateRoleOutput).Role
createInstProfile := CommandFactory.Build("createinstanceprofile")().(*CreateInstanceprofile)
createInstProfile.Name = cmd.Name
createInstProfile.Run(renv, nil)
attachRole := CommandFactory.Build("attachrole")().(*AttachRole)
attachRole.Name = role.RoleName
attachRole.Instanceprofile = role.RoleName
attachRole.Run(renv, nil)
if v := cmd.SleepAfter; v != nil {
vv := Int64AsIntValue(v)
cmd.logger.Infof("sleeping for %d seconds", vv)
time.Sleep(time.Duration(vv) * time.Second)
}
return output, nil
}
func (cmd *CreateRole) ExtractResult(i interface{}) string {
return StringValue(i.(*iam.CreateRoleOutput).Role.Arn)
}
type DeleteRole struct {
_ string `action:"delete" entity:"role" awsAPI:"iam"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"RoleName" awsType:"awsstr" templateName:"name" `
}
func (cmd *DeleteRole) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *DeleteRole) ManualRun(renv env.Running) (interface{}, error) {
detachRole := CommandFactory.Build("detachrole")().(*DetachRole)
detachRole.Name = cmd.Name
detachRole.Instanceprofile = cmd.Name
detachRole.Run(renv, nil)
deleteInstProfile := CommandFactory.Build("deleteinstanceprofile")().(*DeleteInstanceprofile)
deleteInstProfile.Name = cmd.Name
deleteInstProfile.Run(renv, nil)
input := &iam.DeleteRoleInput{}
if err := setFieldWithType(cmd.Name, input, "RoleName", awsstr); err != nil {
return nil, err
}
start := time.Now()
output, err := cmd.api.DeleteRole(context.Background(), input)
cmd.logger.ExtraVerbosef("iam.DeleteRole call took %s", time.Since(start))
return output, err
}
type AttachRole struct {
_ string `action:"attach" entity:"role" awsAPI:"iam" awsCall:"AddRoleToInstanceProfile" awsInput:"iam.AddRoleToInstanceProfileInput" awsOutput:"iam.AddRoleToInstanceProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Instanceprofile *string `awsName:"InstanceProfileName" awsType:"awsstr" templateName:"instanceprofile" `
Name *string `awsName:"RoleName" awsType:"awsstr" templateName:"name" `
}
func (cmd *AttachRole) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("instanceprofile"), params.Key("name")))
}
type DetachRole struct {
_ string `action:"detach" entity:"role" awsAPI:"iam" awsCall:"RemoveRoleFromInstanceProfile" awsInput:"iam.RemoveRoleFromInstanceProfileInput" awsOutput:"iam.RemoveRoleFromInstanceProfileOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Instanceprofile *string `awsName:"InstanceProfileName" awsType:"awsstr" templateName:"instanceprofile" `
Name *string `awsName:"RoleName" awsType:"awsstr" templateName:"name" `
}
func (cmd *DetachRole) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("instanceprofile"), params.Key("name")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/logger"
)
type CreateRoute struct {
_ string `action:"create" entity:"route" awsAPI:"ec2" awsCall:"CreateRoute" awsInput:"ec2.CreateRouteInput" awsOutput:"ec2.CreateRouteOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Table *string `awsName:"RouteTableId" awsType:"awsstr" templateName:"table"`
CIDR *string `awsName:"DestinationCidrBlock" awsType:"awsstr" templateName:"cidr"`
Gateway *string `awsName:"GatewayId" awsType:"awsstr" templateName:"gateway"`
}
func (cmd *CreateRoute) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cidr"), params.Key("gateway"), params.Key("table")),
params.Validators{"cidr": params.IsCIDR})
}
type DeleteRoute struct {
_ string `action:"delete" entity:"route" awsAPI:"ec2" awsCall:"DeleteRoute" awsInput:"ec2.DeleteRouteInput" awsOutput:"ec2.DeleteRouteOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Table *string `awsName:"RouteTableId" awsType:"awsstr" templateName:"table"`
CIDR *string `awsName:"DestinationCidrBlock" awsType:"awsstr" templateName:"cidr"`
}
func (cmd *DeleteRoute) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cidr"), params.Key("table")),
params.Validators{"cidr": params.IsCIDR})
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateRoutetable struct {
_ string `action:"create" entity:"routetable" awsAPI:"ec2" awsCall:"CreateRouteTable" awsInput:"ec2.CreateRouteTableInput" awsOutput:"ec2.CreateRouteTableOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Vpc *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
}
func (cmd *CreateRoutetable) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("vpc")))
}
func (cmd *CreateRoutetable) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateRouteTableOutput).RouteTable.RouteTableId)
}
type DeleteRoutetable struct {
_ string `action:"delete" entity:"routetable" awsAPI:"ec2" awsCall:"DeleteRouteTable" awsInput:"ec2.DeleteRouteTableInput" awsOutput:"ec2.DeleteRouteTableOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"RouteTableId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteRoutetable) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type AttachRoutetable struct {
_ string `action:"attach" entity:"routetable" awsAPI:"ec2" awsCall:"AssociateRouteTable" awsInput:"ec2.AssociateRouteTableInput" awsOutput:"ec2.AssociateRouteTableOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"RouteTableId" awsType:"awsstr" templateName:"id"`
Subnet *string `awsName:"SubnetId" awsType:"awsstr" templateName:"subnet"`
}
func (cmd *AttachRoutetable) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("subnet")))
}
func (cmd *AttachRoutetable) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.AssociateRouteTableOutput).AssociationId)
}
type DetachRoutetable struct {
_ string `action:"detach" entity:"routetable" awsAPI:"ec2" awsCall:"DisassociateRouteTable" awsInput:"ec2.DisassociateRouteTableInput" awsOutput:"ec2.DisassociateRouteTableOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Association *string `awsName:"AssociationId" awsType:"awsstr" templateName:"association"`
}
func (cmd *DetachRoutetable) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("association")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"mime"
"os"
"path/filepath"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/mitchellh/ioprogress"
"github.com/wallix/awless/logger"
)
type CreateS3object struct {
_ string `action:"create" entity:"s3object" awsAPI:"s3"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Bucket *string `awsName:"Bucket" awsType:"awsstr" templateName:"bucket"`
File *string `awsName:"Body" awsType:"awsstr" templateName:"file"`
Name *string `awsName:"Key" awsType:"awsstr" templateName:"name"`
Acl *string `awsName:"ACL" awsType:"awsstr" templateName:"acl"`
}
func (cmd *CreateS3object) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("bucket"), params.Key("file"), params.Opt("acl", "name")),
params.Validators{"file": params.IsFilepath},
)
}
func (cmd *CreateS3object) ManualRun(env.Running) (interface{}, error) {
input := &s3.PutObjectInput{}
f, err := os.Open(StringValue(cmd.File))
if err != nil {
return nil, err
}
defer f.Close()
progressR, err := ProgressBarFactory(f)
if err != nil {
return nil, err
}
input.Body = progressR
var fileName string
if n := StringValue(cmd.Name); n != "" {
fileName = n
} else {
_, fileName = filepath.Split(f.Name())
}
input.Key = aws.String(fileName)
fileExt := filepath.Ext(f.Name())
if mimeType := mime.TypeByExtension(fileExt); mimeType != "" {
cmd.logger.ExtraVerbosef("setting object content-type to '%s'", mimeType)
input.ContentType = aws.String(mimeType)
}
if err = setFieldWithType(cmd.Bucket, input, "Bucket", awsstr); err != nil {
return nil, err
}
if v := cmd.Acl; v != nil {
if err = setFieldWithType(v, input, "ACL", awsstr); err != nil {
return nil, err
}
}
cmd.logger.Infof("uploading '%s'", fileName)
if _, err = cmd.api.PutObject(context.Background(), input); err != nil {
return nil, err
}
return fileName, nil
}
func (cmd *CreateS3object) ExtractResult(i interface{}) string {
return i.(string)
}
type UpdateS3object struct {
_ string `action:"update" entity:"s3object" awsAPI:"s3" awsCall:"PutObjectAcl" awsInput:"s3.PutObjectAclInput" awsOutput:"s3.PutObjectAclOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Bucket *string `awsName:"Bucket" awsType:"awsstr" templateName:"bucket"`
Name *string `awsName:"Key" awsType:"awsstr" templateName:"name"`
Acl *string `awsName:"ACL" awsType:"awsstr" templateName:"acl"`
Version *string `awsName:"VersionId" awsType:"awsstr" templateName:"version"`
}
func (cmd *UpdateS3object) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("acl"), params.Key("bucket"), params.Key("name"),
params.Opt("version"),
))
}
type DeleteS3object struct {
_ string `action:"delete" entity:"s3object" awsAPI:"s3" awsCall:"DeleteObject" awsInput:"s3.DeleteObjectInput" awsOutput:"s3.DeleteObjectOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *s3.Client
Bucket *string `awsName:"Bucket" awsType:"awsstr" templateName:"bucket"`
Name *string `awsName:"Key" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteS3object) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("bucket"), params.Key("name")))
}
type ProgressReadSeeker struct {
file *os.File
reader *ioprogress.Reader
}
func NewProgressReader(f *os.File) (*ProgressReadSeeker, error) {
finfo, err := f.Stat()
if err != nil {
return nil, err
}
draw := func(progress, total int64) string {
// &s3.PutObjectInput.Body will be read twice
// once in memory and a second time for the HTTP upload
// here we only display for the actual HTTP upload
if progress > total {
return ioprogress.DrawTextFormatBytes(progress/2, total)
}
return ""
}
reader := &ioprogress.Reader{
DrawFunc: ioprogress.DrawTerminalf(os.Stdout, draw),
Reader: f,
Size: finfo.Size(),
}
return &ProgressReadSeeker{file: f, reader: reader}, nil
}
func (pr *ProgressReadSeeker) Read(p []byte) (int, error) {
return pr.reader.Read(p)
}
func (pr *ProgressReadSeeker) Seek(offset int64, whence int) (int64, error) {
return pr.file.Seek(offset, whence)
}
// Allow to control for testing
var ProgressBarFactory func(*os.File) (*ProgressReadSeeker, error)
func init() {
ProgressBarFactory = NewProgressReader
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
"github.com/wallix/awless/logger"
)
type CreateScalinggroup struct {
_ string `action:"create" entity:"scalinggroup" awsAPI:"autoscaling" awsCall:"CreateAutoScalingGroup" awsInput:"autoscaling.CreateAutoScalingGroupInput" awsOutput:"autoscaling.CreateAutoScalingGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Name *string `awsName:"AutoScalingGroupName" awsType:"awsstr" templateName:"name"`
Launchconfiguration *string `awsName:"LaunchConfigurationName" awsType:"awsstr" templateName:"launchconfiguration"`
MaxSize *int64 `awsName:"MaxSize" awsType:"awsint64" templateName:"max-size"`
MinSize *int64 `awsName:"MinSize" awsType:"awsint64" templateName:"min-size"`
Subnets []*string `awsName:"VPCZoneIdentifier" awsType:"awscsvstr" templateName:"subnets"`
Cooldown *int64 `awsName:"DefaultCooldown" awsType:"awsint64" templateName:"cooldown"`
DesiredCapacity *int64 `awsName:"DesiredCapacity" awsType:"awsint64" templateName:"desired-capacity"`
HealthcheckGracePeriod *int64 `awsName:"HealthCheckGracePeriod" awsType:"awsint64" templateName:"healthcheck-grace-period"`
HealthcheckType *string `awsName:"HealthCheckType" awsType:"awsstr" templateName:"healthcheck-type"`
NewInstancesProtected *bool `awsName:"NewInstancesProtectedFromScaleIn" awsType:"awsbool" templateName:"new-instances-protected"`
Targetgroups []*string `awsName:"TargetGroupARNs" awsType:"awsstringslice" templateName:"targetgroups"`
}
func (cmd *CreateScalinggroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("launchconfiguration"), params.Key("max-size"), params.Key("min-size"), params.Key("name"), params.Key("subnets"),
params.Opt("cooldown", "desired-capacity", "healthcheck-grace-period", "healthcheck-type", "new-instances-protected", "targetgroups"),
))
}
func (cmd *CreateScalinggroup) ExtractResult(i interface{}) string {
return StringValue(cmd.Name)
}
type UpdateScalinggroup struct {
_ string `action:"update" entity:"scalinggroup" awsAPI:"autoscaling" awsCall:"UpdateAutoScalingGroup" awsInput:"autoscaling.UpdateAutoScalingGroupInput" awsOutput:"autoscaling.UpdateAutoScalingGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Name *string `awsName:"AutoScalingGroupName" awsType:"awsstr" templateName:"name"`
Cooldown *int64 `awsName:"DefaultCooldown" awsType:"awsint64" templateName:"cooldown"`
DesiredCapacity *int64 `awsName:"DesiredCapacity" awsType:"awsint64" templateName:"desired-capacity"`
HealthcheckGracePeriod *int64 `awsName:"HealthCheckGracePeriod" awsType:"awsint64" templateName:"healthcheck-grace-period"`
HealthcheckType *string `awsName:"HealthCheckType" awsType:"awsstr" templateName:"healthcheck-type"`
Launchconfiguration *string `awsName:"LaunchConfigurationName" awsType:"awsstr" templateName:"launchconfiguration"`
MaxSize *int64 `awsName:"MaxSize" awsType:"awsint64" templateName:"max-size"`
MinSize *int64 `awsName:"MinSize" awsType:"awsint64" templateName:"min-size"`
NewInstancesProtected *bool `awsName:"NewInstancesProtectedFromScaleIn" awsType:"awsbool" templateName:"new-instances-protected"`
Subnets []*string `awsName:"VPCZoneIdentifier" awsType:"awscsvstr" templateName:"subnets"`
}
func (cmd *UpdateScalinggroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("cooldown", "desired-capacity", "healthcheck-grace-period", "healthcheck-type", "launchconfiguration", "max-size", "min-size", "new-instances-protected", "subnets"),
))
}
type DeleteScalinggroup struct {
_ string `action:"delete" entity:"scalinggroup" awsAPI:"autoscaling" awsCall:"DeleteAutoScalingGroup" awsInput:"autoscaling.DeleteAutoScalingGroupInput" awsOutput:"autoscaling.DeleteAutoScalingGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Name *string `awsName:"AutoScalingGroupName" awsType:"awsstr" templateName:"name"`
Force *bool `awsName:"ForceDelete" awsType:"awsbool" templateName:"force"`
}
func (cmd *DeleteScalinggroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("force"),
))
}
type CheckScalinggroup struct {
_ string `action:"check" entity:"scalinggroup" awsAPI:"autoscaling"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Name *string `templateName:"name"`
Count *int64 `templateName:"count"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckScalinggroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("count"), params.Key("name"), params.Key("timeout")))
}
func (sg *CheckScalinggroup) ManualRun(renv env.Running) (interface{}, error) {
input := &autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []string{awssdk.ToString(sg.Name)},
}
sgName := StringValue(sg.Name)
c := &checker{
description: fmt.Sprintf("scalinggroup '%s'", sgName),
timeout: time.Duration(Int64AsIntValue(sg.Timeout)) * time.Second,
frequency: 5 * time.Second,
checkName: "count",
fetchFunc: func() (string, error) {
output, err := sg.api.DescribeAutoScalingGroups(context.Background(), input)
if err != nil {
return "", err
}
for _, group := range output.AutoScalingGroups {
if StringValue(group.AutoScalingGroupName) == sgName {
return fmt.Sprint(len(group.Instances)), nil
}
}
return "", fmt.Errorf("scalinggroup %s not found", sgName)
},
expect: fmt.Sprint(Int64AsIntValue(sg.Count)),
logger: sg.logger,
}
return nil, c.check()
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateScalingpolicy struct {
_ string `action:"create" entity:"scalingpolicy" awsAPI:"autoscaling" awsCall:"PutScalingPolicy" awsInput:"autoscaling.PutScalingPolicyInput" awsOutput:"autoscaling.PutScalingPolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
AdjustmentType *string `awsName:"AdjustmentType" awsType:"awsstr" templateName:"adjustment-type"`
Scalinggroup *string `awsName:"AutoScalingGroupName" awsType:"awsstr" templateName:"scalinggroup"`
Name *string `awsName:"PolicyName" awsType:"awsstr" templateName:"name"`
AdjustmentScaling *int64 `awsName:"ScalingAdjustment" awsType:"awsint64" templateName:"adjustment-scaling"`
Cooldown *int64 `awsName:"Cooldown" awsType:"awsint64" templateName:"cooldown"`
AdjustmentMagnitude *int64 `awsName:"MinAdjustmentMagnitude" awsType:"awsint64" templateName:"adjustment-magnitude"`
}
func (cmd *CreateScalingpolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("adjustment-scaling"), params.Key("adjustment-type"), params.Key("name"), params.Key("scalinggroup"),
params.Opt("adjustment-magnitude", "cooldown"),
))
}
func (cmd *CreateScalingpolicy) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*autoscaling.PutScalingPolicyOutput).PolicyARN)
}
type DeleteScalingpolicy struct {
_ string `action:"delete" entity:"scalingpolicy" awsAPI:"autoscaling" awsCall:"DeletePolicy" awsInput:"autoscaling.DeletePolicyInput" awsOutput:"autoscaling.DeletePolicyOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *autoscaling.Client
Id *string `awsName:"PolicyName" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteScalingpolicy) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateSecuritygroup struct {
_ string `action:"create" entity:"securitygroup" awsAPI:"ec2" awsCall:"CreateSecurityGroup" awsInput:"ec2.CreateSecurityGroupInput" awsOutput:"ec2.CreateSecurityGroupOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Name *string `awsName:"GroupName" awsType:"awsstr" templateName:"name"`
Vpc *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
}
func (cmd *CreateSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("description"), params.Key("name"), params.Key("vpc")))
}
func (cmd *CreateSecuritygroup) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateSecurityGroupOutput).GroupId)
}
type UpdateSecuritygroup struct {
_ string `action:"update" entity:"securitygroup" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
Protocol *string `templateName:"protocol"`
CIDR *string `templateName:"cidr"`
Securitygroup *string `templateName:"securitygroup"`
Inbound *string `templateName:"inbound"`
Outbound *string `templateName:"outbound"`
Portrange *string `templateName:"portrange"`
}
func (cmd *UpdateSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("protocol"), params.OnlyOneOf(params.Key("inbound"), params.Key("outbound")),
params.Opt(params.Suggested("cidr", "portrange"), "securitygroup")),
params.Validators{
"cidr": params.IsCIDR,
"inbound": params.IsInEnumIgnoreCase("authorize", "revoke"),
"outbound": params.IsInEnumIgnoreCase("authorize", "revoke"),
// Fail fast when protocol is TCP/UDP and port range is missing, instead of waiting
// for AWS server validation error:
// InvalidParameterValue: Invalid value 'Must specify both from and to ports with TCP/UDP.' for portRange.
"protocol": func(protocol interface{}, others map[string]interface{}) error {
_, hasPortRange := others["portrange"]
if isTCPorUDP(fmt.Sprint(protocol)) && !hasPortRange {
return errors.New("missing 'portrange' when protocol is TCP/UDP")
}
return nil
},
})
}
func (cmd *UpdateSecuritygroup) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
ipPerms, err := cmd.buildIpPermissions()
if err != nil {
return nil, err
}
var input interface{}
switch StringValue(cmd.Inbound) {
case "authorize":
input = &ec2.AuthorizeSecurityGroupIngressInput{DryRun: Bool(true), IpPermissions: ipPerms}
case "revoke":
input = &ec2.RevokeSecurityGroupIngressInput{DryRun: Bool(true), IpPermissions: ipPerms}
}
switch StringValue(cmd.Outbound) {
case "authorize":
input = &ec2.AuthorizeSecurityGroupEgressInput{DryRun: Bool(true), IpPermissions: ipPerms}
case "revoke":
input = &ec2.RevokeSecurityGroupEgressInput{DryRun: Bool(true), IpPermissions: ipPerms}
}
if input == nil {
return nil, fmt.Errorf("expect either 'inbound' or 'outbound' parameter")
}
err = setFieldWithType(cmd.Id, input, "GroupId", awsstr)
if err != nil {
return nil, err
}
switch ii := input.(type) {
case *ec2.AuthorizeSecurityGroupIngressInput:
_, err = cmd.api.AuthorizeSecurityGroupIngress(context.Background(), ii)
case *ec2.RevokeSecurityGroupIngressInput:
_, err = cmd.api.RevokeSecurityGroupIngress(context.Background(), ii)
case *ec2.AuthorizeSecurityGroupEgressInput:
_, err = cmd.api.AuthorizeSecurityGroupEgress(context.Background(), ii)
case *ec2.RevokeSecurityGroupEgressInput:
_, err = cmd.api.RevokeSecurityGroupEgress(context.Background(), ii)
}
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound):
cmd.logger.Verbose("dry run: update securitygroup ok")
return nil, nil
}
}
return nil, fmt.Errorf("dry run: update securitygroup: %s", err)
}
func (cmd *UpdateSecuritygroup) ManualRun(renv env.Running) (interface{}, error) {
ipPerms, err := cmd.buildIpPermissions()
if err != nil {
return nil, err
}
var input interface{}
if inbound := cmd.Inbound; inbound != nil {
switch StringValue(inbound) {
case "authorize":
input = &ec2.AuthorizeSecurityGroupIngressInput{IpPermissions: ipPerms}
case "revoke":
input = &ec2.RevokeSecurityGroupIngressInput{IpPermissions: ipPerms}
default:
return nil, fmt.Errorf("'inbound' parameter expect 'authorize' or 'revoke', got %s", StringValue(inbound))
}
}
if outbound := cmd.Outbound; outbound != nil {
switch StringValue(outbound) {
case "authorize":
input = &ec2.AuthorizeSecurityGroupEgressInput{IpPermissions: ipPerms}
case "revoke":
input = &ec2.RevokeSecurityGroupEgressInput{IpPermissions: ipPerms}
default:
return nil, fmt.Errorf("'outbound' parameter expect 'authorize' or 'revoke', got %s", StringValue(outbound))
}
}
if input == nil {
return nil, fmt.Errorf("expect either 'inbound' or 'outbound' parameter")
}
// Required params
err = setFieldWithType(cmd.Id, input, "GroupId", awsstr)
if err != nil {
return nil, err
}
var output interface{}
start := time.Now()
switch ii := input.(type) {
case *ec2.AuthorizeSecurityGroupIngressInput:
output, err = cmd.api.AuthorizeSecurityGroupIngress(context.Background(), ii)
cmd.logger.ExtraVerbosef("ec2.AuthorizeSecurityGroupIngress call took %s", time.Since(start))
case *ec2.RevokeSecurityGroupIngressInput:
output, err = cmd.api.RevokeSecurityGroupIngress(context.Background(), ii)
cmd.logger.ExtraVerbosef("ec2.RevokeSecurityGroupIngress call took %s", time.Since(start))
case *ec2.AuthorizeSecurityGroupEgressInput:
output, err = cmd.api.AuthorizeSecurityGroupEgress(context.Background(), ii)
cmd.logger.ExtraVerbosef("ec2.AuthorizeSecurityGroupEgress call took %s", time.Since(start))
case *ec2.RevokeSecurityGroupEgressInput:
output, err = cmd.api.RevokeSecurityGroupEgress(context.Background(), ii)
cmd.logger.ExtraVerbosef("ec2.RevokeSecurityGroupEgress call took %s", time.Since(start))
}
return output, err
}
type DeleteSecuritygroup struct {
_ string `action:"delete" entity:"securitygroup" awsAPI:"ec2" awsCall:"DeleteSecurityGroup" awsInput:"ec2.DeleteSecurityGroupInput" awsOutput:"ec2.DeleteSecurityGroupOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"GroupId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type CheckSecuritygroup struct {
_ string `action:"check" entity:"securitygroup" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("unused"),
})
}
func (cmd *CheckSecuritygroup) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DescribeNetworkInterfacesInput{
Filters: []ec2types.Filter{
{Name: String("group-id"), Values: []string{awssdk.ToString(cmd.Id)}},
},
}
c := &checker{
description: fmt.Sprintf("securitygroup %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeNetworkInterfaces(context.Background(), input)
if err != nil {
return "", err
}
if len(output.NetworkInterfaces) == 0 {
return "unused", nil
}
var niIds []string
for _, ni := range output.NetworkInterfaces {
niIds = append(niIds, StringValue(ni.NetworkInterfaceId))
}
return fmt.Sprintf("used by %s", strings.Join(niIds, ", ")), nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
type AttachSecuritygroup struct {
_ string `action:"attach" entity:"securitygroup" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
Instance *string `templateName:"instance"`
}
func (cmd *AttachSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("instance")))
}
func (cmd *AttachSecuritygroup) ManualRun(renv env.Running) (interface{}, error) {
groups, err := fetchInstanceSecurityGroups(cmd.api, StringValue(cmd.Instance))
if err != nil {
return nil, fmt.Errorf("fetching securitygroups for instance %s: %s", StringValue(cmd.Instance), err)
}
groups = append(groups, StringValue(cmd.Id))
call := &awsCall{
fnName: "ec2.ModifyInstanceAttribute",
fn: cmd.api.ModifyInstanceAttribute,
logger: cmd.logger,
setters: []setter{
{val: cmd.Instance, fieldPath: "InstanceID", fieldType: awsstr},
{val: groups, fieldPath: "Groups", fieldType: awsstringslice},
},
}
return call.execute(&ec2.ModifyInstanceAttributeInput{})
}
type DetachSecuritygroup struct {
_ string `action:"detach" entity:"securitygroup" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
Instance *string `templateName:"instance"`
}
func (cmd *DetachSecuritygroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Key("instance")))
}
func (cmd *DetachSecuritygroup) ManualRun(renv env.Running) (interface{}, error) {
groups, err := fetchInstanceSecurityGroups(cmd.api, StringValue(cmd.Instance))
if err != nil {
return nil, fmt.Errorf("fetching securitygroups for instance %s: %s", StringValue(cmd.Instance), err)
}
cleaned := removeString(groups, StringValue(cmd.Id))
if len(cleaned) == 0 {
cmd.logger.Errorf("AWS instances must have at least one securitygroup")
}
call := &awsCall{
fnName: "ec2.ModifyInstanceAttribute",
fn: cmd.api.ModifyInstanceAttribute,
logger: cmd.logger,
setters: []setter{
{val: cmd.Instance, fieldPath: "InstanceID", fieldType: awsstr},
{val: cleaned, fieldPath: "Groups", fieldType: awsstringslice},
},
}
return call.execute(&ec2.ModifyInstanceAttributeInput{})
}
func (cmd *UpdateSecuritygroup) buildIpPermissions() ([]ec2types.IpPermission, error) {
ipPerm := ec2types.IpPermission{}
if cidr := cmd.CIDR; cidr != nil {
ipPerm.IpRanges = []ec2types.IpRange{{CidrIp: cidr}}
} else if secgroup := cmd.Securitygroup; secgroup != nil {
ipPerm.UserIdGroupPairs = []ec2types.UserIdGroupPair{{GroupId: secgroup}}
} else {
return nil, errors.New("missing either 'cidr' or 'securitygroup' parameter")
}
p := StringValue(cmd.Protocol)
if strings.Contains("any", p) {
ipPerm.FromPort = awssdk.Int32(-1)
ipPerm.ToPort = awssdk.Int32(-1)
ipPerm.IpProtocol = String("-1")
return []ec2types.IpPermission{ipPerm}, nil
}
ipPerm.IpProtocol = String(p)
if pRange := cmd.Portrange; pRange != nil {
ports := *pRange
switch {
case strings.Contains(ports, "any"):
if isTCPorUDP(p) {
ipPerm.FromPort = awssdk.Int32(0)
ipPerm.ToPort = awssdk.Int32(65535)
} else {
ipPerm.FromPort = awssdk.Int32(-1)
ipPerm.ToPort = awssdk.Int32(-1)
}
case strings.Contains(ports, "-"):
from, err := strconv.ParseInt(strings.SplitN(ports, "-", 2)[0], 10, 32)
if err != nil {
return nil, err
}
to, err := strconv.ParseInt(strings.SplitN(ports, "-", 2)[1], 10, 32)
if err != nil {
return nil, err
}
ipPerm.FromPort = awssdk.Int32(int32(from))
ipPerm.ToPort = awssdk.Int32(int32(to))
default:
port, err := strconv.ParseInt(ports, 10, 32)
if err != nil {
return nil, err
}
ipPerm.FromPort = awssdk.Int32(int32(port))
ipPerm.ToPort = awssdk.Int32(int32(port))
}
}
return []ec2types.IpPermission{ipPerm}, nil
}
func isTCPorUDP(p string) bool {
return strings.ToLower(p) == "tcp" || strings.ToLower(p) == "udp"
}
func fetchInstanceSecurityGroups(api *ec2.Client, id string) ([]string, error) {
params := &ec2.DescribeInstanceAttributeInput{
Attribute: ec2types.InstanceAttributeNameGroupSet,
InstanceId: String(id),
}
resp, err := api.DescribeInstanceAttribute(context.Background(), params)
if err != nil {
return nil, err
}
var groups []string
for _, g := range resp.Groups {
groups = append(groups, StringValue(g.GroupId))
}
return groups, nil
}
func removeString(arr []string, s string) (out []string) {
for _, e := range arr {
if e != s {
out = append(out, e)
}
}
return
}
/*
Copyright 2017 WALLIX
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 awsspec
import (
"bytes"
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
"reflect"
"regexp"
"strconv"
"strings"
gotemplate "text/template"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
applicationautoscalingtypes "github.com/aws/aws-sdk-go-v2/service/applicationautoscaling/types"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
"github.com/wallix/awless/logger"
)
const (
awsstr = "awsstr"
awsint = "awsint"
awsint64 = "awsint64"
awsfloat = "awsfloat"
awsbool = "awsbool"
awsboolattribute = "awsboolattribute"
awsstringattribute = "awsstringattribute"
awsint64slice = "awsint64slice"
awsstringslice = "awsstringslice"
awsstringpointermap = "awsstringpointermap"
awsslicestruct = "awsslicestruct"
awsslicestructint64 = "awsslicestructint64"
awsuserdatatobase64 = "awsuserdatatobase64"
awsfiletobyteslice = "awsfiletobyteslice"
awsfiletostring = "awsfiletostring"
awsdimensionslice = "awsdimensionslice"
awsparameterslice = "awsparameterslice"
awsecskeyvalue = "awsecskeyvalue"
awsportmappings = "awsportmappings"
awssubnetmappings = "awssubnetmappings"
awsclassicloadblisteners = "awsclassicloadblisteners"
awsstepadjustments = "awsstepadjustments"
awscsvstr = "awscsvstr"
aws6digitsstring = "aws6digitsstring"
awsbyteslice = "awsbyteslice"
awstagslice = "awstagslice"
awsalarmrollbacktriggers = "awsalarmrollbacktriggers"
)
var (
mapAttributeRegex = regexp.MustCompile(`(.+)\[(.+)\].*`)
sliceStructRegex = regexp.MustCompile(`(.+)\[0\](.*)`)
)
type setter struct {
val interface{}
fieldPath string
fieldType string
}
func (s setter) set(i interface{}) error {
return setFieldWithType(s.val, i, s.fieldPath, s.fieldType)
}
func setFieldWithType(v, i interface{}, fieldPath string, destType string, interfs ...interface{}) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("set field %s for %T object: %s", fieldPath, i, e)
}
}()
if v == nil || i == nil {
return nil
}
switch destType {
case awsstr:
v = castString(v)
case awsint64:
v, err = castInt64(v)
if err != nil {
return
}
case awsint:
v, err = castInt(v)
if err != nil {
return
}
case aws6digitsstring:
v, err = castInt(v)
if err != nil {
return
}
v = fmt.Sprintf("%06d", v)
case awsfloat:
v, err = castFloat(v)
if err != nil {
return
}
case awsbool:
v, err = castBool(v)
if err != nil {
return
}
case awsstringslice:
v = castStringPointerSlice(v)
case awsbyteslice:
case awscsvstr:
v = strings.Join(castStringSlice(v), ",")
case awsdimensionslice:
if dimensions, isDim := v.([]cloudwatchtypes.Dimension); isDim {
v = dimensions
} else {
dimensions = []cloudwatchtypes.Dimension{}
sl := castStringSlice(v)
for _, s := range sl {
splits := strings.SplitN(s, ":", 2)
if len(splits) != 2 {
return fmt.Errorf("invalid dimension '%s', expected 'key:value'", s)
}
dimensions = append(dimensions, cloudwatchtypes.Dimension{Name: aws.String(splits[0]), Value: aws.String(splits[1])})
v = dimensions
}
}
case awsecskeyvalue:
sl := castStringSlice(v)
var keyvalues []ecstypes.KeyValuePair
for _, s := range sl {
splits := strings.SplitN(s, ":", 2)
if len(splits) != 2 {
return fmt.Errorf("invalid keyvalue '%s', expected 'key:value'", s)
}
keyvalues = append(keyvalues, ecstypes.KeyValuePair{Name: aws.String(splits[0]), Value: aws.String(splits[1])})
}
v = keyvalues
case awsparameterslice:
sl := castStringSlice(v)
var parameters []cloudformationtypes.Parameter
for _, s := range sl {
splits := strings.SplitN(s, ":", 2)
if len(splits) != 2 {
return fmt.Errorf("invalid parameter '%s', expected 'key:value'", s)
}
parameters = append(parameters, cloudformationtypes.Parameter{ParameterKey: aws.String(splits[0]), ParameterValue: aws.String(splits[1])})
}
v = parameters
case awssubnetmappings:
sl := castStringSlice(v)
var subnetMappings []elbv2types.SubnetMapping
for i, s := range sl {
splits := strings.Split(s, ":")
if len(splits) != 2 {
return fmt.Errorf("invalid element %d in subnet mapping %v, expect format [subnet-123:eipalloc-321, subnet-234:eipalloc-678, ...]", i+1, splits)
}
subnetMappings = append(subnetMappings, elbv2types.SubnetMapping{SubnetId: aws.String(splits[0]), AllocationId: aws.String(splits[1])})
}
v = subnetMappings
case awsclassicloadblisteners:
var listeners []elbtypes.Listener
for _, s := range castStringSlice(v) {
splits := strings.Split(s, ":")
if len(splits) != 4 {
return fmt.Errorf("missing value in listeners param '%s', expect format like HTTP:80:HTTP:80", splits)
}
loadbPort, err := strconv.ParseInt(splits[1], 10, 64)
if err != nil {
return fmt.Errorf("expecting numerical port value for loadbalancer port in '%s', (expect format like HTTP:80:HTTP:80)", splits)
}
instancePort, err := strconv.ParseInt(splits[3], 10, 64)
if err != nil {
return fmt.Errorf("expecting numerical port value for instance port in '%s', (expect format like HTTP:80:HTTP:80)", splits)
}
listeners = append(listeners, elbtypes.Listener{
Protocol: aws.String(splits[0]),
LoadBalancerPort: int32(loadbPort),
InstanceProtocol: aws.String(splits[2]),
InstancePort: aws.Int32(int32(instancePort)),
})
}
v = listeners
case awsportmappings:
sl := castStringSlice(v)
var portMappings []ecstypes.PortMapping
for _, s := range sl {
portMapping := &ecstypes.PortMapping{}
if strings.Contains(s, "-") {
return fmt.Errorf("invalid port mapping '%s', AWS do not support portrange (from-to)", s)
}
var protocol string
if strings.Contains(s, "/") {
splits := strings.Split(s, "/")
protocol = splits[1]
if protocol != "tcp" && protocol != "udp" {
return fmt.Errorf("invalid port mapping '%s', invalid protocol, expect tcp or udp, got %s", s, protocol)
}
s = strings.TrimRight(s, "/"+protocol)
portMapping.Protocol = ecstypes.TransportProtocol(protocol)
}
splits := strings.Split(s, ":")
switch len(splits) {
case 1:
containerPort, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return fmt.Errorf("invalid port mapping '%s', expect from[:to][/protocol]", s)
}
portMapping.ContainerPort = aws.Int32(int32(containerPort))
case 2:
hostPort, err := strconv.ParseInt(splits[0], 10, 32)
if err != nil {
return fmt.Errorf("invalid port mapping '%s', expect from[:to][/protocol]", s)
}
containerPort, err := strconv.ParseInt(splits[1], 10, 32)
if err != nil {
return fmt.Errorf("invalid port mapping '%s', expect from[:to][/protocol]", s)
}
portMapping.HostPort = aws.Int32(int32(hostPort))
portMapping.ContainerPort = aws.Int32(int32(containerPort))
default:
return fmt.Errorf("invalid port mapping '%s', expect from[:to][/protocol]", s)
}
portMappings = append(portMappings, *portMapping)
}
v = portMappings
case awsstepadjustments:
sl := castStringSlice(v)
var stepAdjustments []applicationautoscalingtypes.StepAdjustment
for _, s := range sl {
splits := strings.Split(s, ":")
if len(splits) != 3 {
return fmt.Errorf("invalid step adjustment '%s', expect from:to:scaling-adjustment", s)
}
stepAdjustment := &applicationautoscalingtypes.StepAdjustment{}
if splits[0] != "" {
lower, err := strconv.ParseFloat(splits[0], 64)
if err != nil {
return fmt.Errorf("invalid from '%s' in step adjustment '%s', expect from:to:scaling-adjustment", splits[0], s)
}
stepAdjustment.MetricIntervalLowerBound = aws.Float64(lower)
}
if splits[1] != "" {
upper, err := strconv.ParseFloat(splits[1], 64)
if err != nil {
return fmt.Errorf("invalid to '%s' in step adjustment '%s', expect from:to:scaling-adjustment", splits[1], s)
}
stepAdjustment.MetricIntervalUpperBound = aws.Float64(upper)
}
adjustment, err := strconv.ParseInt(splits[2], 10, 32)
if err != nil {
return fmt.Errorf("invalid adjustment-adjustment '%s' in step adjustmentstep adjustment '%s', expect from:to:scaling-adjustment", splits[2], s)
}
stepAdjustment.ScalingAdjustment = aws.Int32(int32(adjustment))
stepAdjustments = append(stepAdjustments, *stepAdjustment)
}
v = stepAdjustments
case awsuserdatatobase64:
var tplData interface{}
if len(interfs) > 0 {
tplData = interfs[0]
}
v, err = userDataContentAsBase64(v, tplData)
if err != nil {
return err
}
case awsfiletobyteslice:
v, err = os.ReadFile(castString(v))
if err != nil {
return err
}
case awsfiletostring:
var b []byte
b, err = os.ReadFile(castString(v))
if err != nil {
return err
}
v = string(b)
case awsint64slice:
var awsint int64
awsint, err = castInt64(v)
if err != nil {
return
}
v = []*int64{&awsint}
case awsboolattribute:
var b bool
b, err = castBool(v)
if err != nil {
return
}
v = &ec2types.AttributeBooleanValue{Value: &b}
case awsstringattribute:
str := castString(v)
v = &ec2types.AttributeValue{Value: &str}
case awsstringpointermap:
matches := mapAttributeRegex.FindStringSubmatch(fieldPath)
if len(matches) < 2 {
err = fmt.Errorf("set field awsstringmap: path %s does not start with mymap[key]", fieldPath)
return
}
strcr := reflect.Indirect(reflect.ValueOf(i))
if strcr.Kind() != reflect.Struct {
err = fmt.Errorf("set field awsstringmap: %T is not a struct, but a %s", i, strcr.Kind())
return
}
field := strcr.FieldByName(matches[1])
if field.Kind() != reflect.Map {
err = fmt.Errorf("set field awsstringmap: field %s is not a map, but a %s", matches[0], field.Kind())
return
}
if field.IsNil() {
field.Set(reflect.MakeMap(field.Type()))
}
str := castString(v)
field.SetMapIndex(reflect.ValueOf(matches[2]), reflect.ValueOf(&str))
return nil
case awsslicestruct, awsslicestructint64:
if destType == awsslicestructint64 {
v, err = castInt64(v)
if err != nil {
return
}
}
matches := sliceStructRegex.FindStringSubmatch(fieldPath)
if len(matches) < 2 {
err = fmt.Errorf("set field awsslicestruct: path %s does not start with slice[0]", fieldPath)
return
}
strcr := reflect.Indirect(reflect.ValueOf(i))
if strcr.Kind() != reflect.Struct {
err = fmt.Errorf("set field awsslicestruct: %T is not a struct, but a %s", i, strcr.Kind())
return
}
sliceField := strcr.FieldByName(matches[1])
if sliceField.Kind() != reflect.Slice {
err = fmt.Errorf("set field awsslicestruct: field %s is not a slice, but a %s", matches[0], sliceField.Kind())
return
}
var elemToSet reflect.Value
if sliceField.Len() > 0 {
elemToSet = sliceField.Index(0)
} else {
elemToSet = reflect.New(sliceField.Type().Elem().Elem())
sliceField.Set(reflect.Append(sliceField, elemToSet))
}
if sliceField.Type().Elem().Kind() != reflect.Ptr {
err = fmt.Errorf("set field awsslicestruct: field %s is not a slice of struct pointer, but a %s", matches[0], sliceField.Kind())
return
}
setValueAtPath(elemToSet.Interface(), matches[2], v)
return nil
case awstagslice:
var (
elbTags []elbtypes.Tag
cfTags []cloudformationtypes.Tag
appendFunc func(s1, s2 string)
assignFunc func()
)
switch i.(type) {
case *elb.CreateLoadBalancerInput:
appendFunc = func(s1, s2 string) {
elbTags = append(elbTags, elbtypes.Tag{Key: aws.String(s1), Value: aws.String(s2)})
}
assignFunc = func() { v = elbTags }
case *cloudformation.CreateStackInput, *cloudformation.UpdateStackInput:
appendFunc = func(s1, s2 string) {
cfTags = append(cfTags, cloudformationtypes.Tag{Key: aws.String(s1), Value: aws.String(s2)})
}
assignFunc = func() { v = cfTags }
}
for _, s := range castStringSlice(v) {
splits := strings.SplitN(s, ":", 2)
if len(splits) != 2 {
return fmt.Errorf("invalid tag '%s', expected 'key:value'", s)
}
appendFunc(splits[0], splits[1])
}
assignFunc()
case awsalarmrollbacktriggers:
var triggers []cloudformationtypes.RollbackTrigger
if list := castStringSlice(v); len(list) > 0 {
for _, t := range list {
triggers = append(triggers, cloudformationtypes.RollbackTrigger{
Arn: aws.String(t),
Type: aws.String("AWS::CloudWatch::Alarm"),
})
}
}
v = triggers
}
setValueAtPath(i, fieldPath, v)
return nil
}
func castString(v interface{}) string {
switch vv := v.(type) {
case []string:
return strings.Join(vv, ",")
case *string:
return *vv
default:
return fmt.Sprint(v)
}
}
func castFloat(v interface{}) (float64, error) {
switch vv := v.(type) {
case string:
f, err := strconv.ParseFloat(vv, 64)
if err != nil {
return f, fmt.Errorf("invalid float value '%s'", vv)
}
return f, nil
case float32:
return float64(vv), nil
case float64:
return vv, nil
case *float64:
return aws.ToFloat64(vv), nil
case int:
return float64(vv), nil
case int64:
return float64(vv), nil
default:
return 0, fmt.Errorf("cannot cast %T to float64", v)
}
}
func castInt(v interface{}) (int, error) {
switch vv := v.(type) {
case *string:
i, err := strconv.Atoi(aws.ToString(vv))
if err != nil {
return i, fmt.Errorf("invalid integer value '%s'", aws.ToString(vv))
}
return i, nil
case string:
i, err := strconv.Atoi(vv)
if err != nil {
return i, fmt.Errorf("invalid integer value '%s'", vv)
}
return i, nil
case *int:
return aws.ToInt(vv), nil
case int:
return vv, nil
case int64:
return int(vv), nil
case *int64:
return int(aws.ToInt64(vv)), nil
default:
return 0, fmt.Errorf("cannot cast %T to int", v)
}
}
func castBool(v interface{}) (bool, error) {
switch vv := v.(type) {
case string:
b, err := strconv.ParseBool(vv)
if err != nil {
return b, fmt.Errorf("invalid integer value '%s'", vv)
}
return b, nil
case bool:
return vv, nil
case *bool:
return aws.ToBool(vv), nil
default:
return false, fmt.Errorf("cannot cast %T to bool", v)
}
}
func castInt64(v interface{}) (int64, error) {
switch vv := v.(type) {
case string:
i, err := strconv.Atoi(vv)
if err != nil {
return int64(i), fmt.Errorf("invalid integer value '%s'", vv)
}
return int64(i), nil
case int:
return int64(vv), nil
case *int:
return int64(aws.ToInt(vv)), nil
case int64:
return vv, nil
case *int64:
return aws.ToInt64(vv), nil
default:
return int64(0), fmt.Errorf("cannot cast %T to int64", v)
}
}
func castStringSlice(v interface{}) []string {
switch vv := v.(type) {
case string:
return []string{vv}
case *string:
return []string{aws.ToString(vv)}
case []*string:
return aws.ToStringSlice(vv)
case []string:
return vv
case []interface{}:
var slice []string
for _, i := range vv {
switch ii := i.(type) {
case string:
slice = append(slice, ii)
case *string:
slice = append(slice, *ii)
default:
slice = append(slice, fmt.Sprint(ii))
}
}
return slice
default:
return []string{fmt.Sprint(v)}
}
}
func castStringPointerSlice(v interface{}) []*string {
switch vv := v.(type) {
case string:
return []*string{&vv}
case *string:
return []*string{vv}
case []*string:
return vv
case []string:
return aws.StringSlice(vv)
case []interface{}:
var slice []*string
for _, i := range vv {
switch ii := i.(type) {
case string:
slice = append(slice, &ii)
case *string:
slice = append(slice, ii)
default:
str := fmt.Sprint(ii)
slice = append(slice, &str)
}
}
return slice
default:
str := fmt.Sprint(v)
return []*string{&str}
}
}
func userDataContentAsBase64(v interface{}, tplData interface{}) (string, error) {
userdata := castString(v)
var readErr error
var content []byte
if strings.HasPrefix(strings.TrimSpace(userdata), "#") { // userdata are bash content or yml cloud script content (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html#user-data-shell-scripts)
r := strings.NewReplacer("\\a", "\a", "\\b", "\b", "\\f", "\f", "\\n", "\n", "\\t", "\t", "\\r", "\r", "\\v", "\v")
content = []byte(r.Replace(userdata))
} else if strings.HasPrefix(userdata, "http") {
client := &http.Client{Timeout: 5 * time.Second}
logger.ExtraVerbosef("fetching remote userdata at '%s'", userdata)
resp, err := client.Get(userdata)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode < http.StatusOK || resp.StatusCode > 299 {
return "", fmt.Errorf("'%s' when fetching userdata at '%s'", resp.Status, userdata)
}
content, readErr = io.ReadAll(resp.Body)
} else {
content, readErr = os.ReadFile(userdata)
}
if readErr != nil {
return "", fmt.Errorf("got userdata from '%s' but cannot read content: %s", userdata, readErr)
}
if tpl, err := gotemplate.New("userdata").Parse(string(content)); err != nil {
logger.Warningf("cannot parse userdata as Go template: %s", err)
} else {
var buf bytes.Buffer
if err := tpl.Execute(&buf, tplData); err == nil {
content = buf.Bytes()
}
}
return base64.StdEncoding.EncodeToString(content), nil
}
func structSetter(s interface{}, params map[string]interface{}) error {
if params == nil {
return nil
}
val := reflect.ValueOf(s).Elem()
stru := val.Type()
for i := 0; i < stru.NumField(); i++ {
field := stru.Field(i)
tplName := field.Tag.Get("templateName")
var fieldType string
if v, ok := params[tplName]; ok {
kind := field.Type.Kind()
if kind == reflect.Ptr {
switch field.Type.Elem().Kind() {
case reflect.String:
fieldType = awsstr
case reflect.Int64:
fieldType = awsint64
case reflect.Bool:
fieldType = awsbool
case reflect.Float64:
fieldType = awsfloat
default:
return fmt.Errorf("unknown type %s for parameter %s in struct setter", tplName, field.Type.String())
}
} else if kind == reflect.Slice && field.Type.Elem().Kind() == reflect.Ptr {
switch field.Type.Elem().Elem().Kind() {
case reflect.String:
fieldType = awsstringslice
case reflect.Int64:
fieldType = awsint64slice
default:
return fmt.Errorf("unknown type in slice %s for parameter %s", field.Type.String(), tplName)
}
}
if err := setFieldWithType(v, s, field.Name, fieldType); err != nil {
return fmt.Errorf("%s: %s", tplName, err)
}
}
}
return nil
}
func structInjector(src, dest interface{}, ctx map[string]interface{}) error {
val := reflect.ValueOf(src).Elem()
stru := val.Type()
for i := 0; i < stru.NumField(); i++ {
field := stru.Field(i)
if dstNames, ok := field.Tag.Lookup("awsName"); ok {
splits := strings.Split(dstNames, ",")
for _, destName := range splits {
destName = strings.TrimSpace(destName)
if dstType, tok := field.Tag.Lookup("awsType"); tok {
fieldValue := val.Field(i)
if fieldValue.IsValid() && fieldValue.Interface() != nil && !fieldValue.IsNil() {
if err := setFieldWithType(fieldValue.Interface(), dest, destName, dstType, ctx); err != nil {
fieldName := field.Name
if tplName, ok := field.Tag.Lookup("templateName"); ok {
fieldName = tplName
}
return fmt.Errorf("%s: %s", fieldName, err)
}
}
}
}
}
}
return nil
}
func contains(arr []string, e string) bool {
for _, a := range arr {
if a == e {
return true
}
}
return false
}
// setValueAtPath sets a value at a dot-separated field path in a struct.
// Replaces awsutil.SetValueAtPath from SDK v1.
func setValueAtPath(i interface{}, path string, v interface{}) {
path = strings.TrimPrefix(path, ".")
if path == "" {
return
}
parts := strings.Split(path, ".")
val := reflect.ValueOf(i)
for val.Kind() == reflect.Ptr {
if val.IsNil() {
return
}
val = val.Elem()
}
for idx, part := range parts {
if val.Kind() != reflect.Struct {
return
}
field := val.FieldByName(part)
if !field.IsValid() || !field.CanSet() {
return
}
if idx == len(parts)-1 {
rv := reflect.ValueOf(v)
if field.Kind() == reflect.Ptr {
if rv.Kind() == reflect.Ptr {
field.Set(rv.Convert(field.Type()))
} else {
ptr := reflect.New(field.Type().Elem())
ptr.Elem().Set(rv.Convert(field.Type().Elem()))
field.Set(ptr)
}
} else if rv.Kind() == reflect.Ptr && !rv.IsNil() {
field.Set(rv.Elem().Convert(field.Type()))
} else if rv.Type().AssignableTo(field.Type()) {
field.Set(rv)
} else if rv.Type().ConvertibleTo(field.Type()) {
field.Set(rv.Convert(field.Type()))
} else {
field.Set(rv)
}
} else {
if field.Kind() == reflect.Ptr {
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}
val = field.Elem()
} else {
val = field
}
}
}
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateSnapshot struct {
_ string `action:"create" entity:"snapshot" awsAPI:"ec2" awsCall:"CreateSnapshot" awsInput:"ec2.CreateSnapshotInput" awsOutput:"ec2.Snapshot" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Volume *string `awsName:"VolumeId" awsType:"awsstr" templateName:"volume"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
}
func (cmd *CreateSnapshot) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("volume"),
params.Opt("description"),
))
}
func (cmd *CreateSnapshot) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateSnapshotOutput).SnapshotId)
}
type DeleteSnapshot struct {
_ string `action:"delete" entity:"snapshot" awsAPI:"ec2" awsCall:"DeleteSnapshot" awsInput:"ec2.DeleteSnapshotInput" awsOutput:"ec2.DeleteSnapshotOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"SnapshotId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteSnapshot) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type CopySnapshot struct {
_ string `action:"copy" entity:"snapshot" awsAPI:"ec2" awsCall:"CopySnapshot" awsInput:"ec2.CopySnapshotInput" awsOutput:"ec2.CopySnapshotOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
SourceId *string `awsName:"SourceSnapshotId" awsType:"awsstr" templateName:"source-id"`
SourceRegion *string `awsName:"SourceRegion" awsType:"awsstr" templateName:"source-region"`
Encrypted *bool `awsName:"Encrypted" awsType:"awsbool" templateName:"encrypted"`
Description *string `awsName:"Description" awsType:"awsstr" templateName:"description"`
}
func (cmd *CopySnapshot) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("source-id"), params.Key("source-region"),
params.Opt("description", "encrypted"),
))
}
func (cmd *CopySnapshot) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CopySnapshotOutput).SnapshotId)
}
package awsspec
import (
"errors"
"fmt"
"math/rand"
"reflect"
"strings"
"time"
"github.com/aws/smithy-go"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/fatih/color"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
)
const (
dryRunOperation = "DryRunOperation"
notFound = "NotFound"
)
type BeforeRunner interface {
BeforeRun(env.Running) error
}
type AfterRunner interface {
AfterRun(env.Running, interface{}) error
}
type ResultExtractor interface {
ExtractResult(interface{}) string
}
type InputPostProcessor interface {
PostProcessInput(interface{})
}
type command interface {
ParamsSpec() params.Spec
inject(map[string]interface{}) error
Run(env.Running, map[string]interface{}) (interface{}, error)
}
func implementsBeforeRun(i interface{}) (BeforeRunner, bool) {
v, ok := i.(BeforeRunner)
return v, ok
}
func implementsAfterRun(i interface{}) (AfterRunner, bool) {
v, ok := i.(AfterRunner)
return v, ok
}
func implementsResultExtractor(i interface{}) (ResultExtractor, bool) {
v, ok := i.(ResultExtractor)
return v, ok
}
func implementsInputPostProcessor(i interface{}) (InputPostProcessor, bool) {
v, ok := i.(InputPostProcessor)
return v, ok
}
func fakeDryRunId(entity string) string {
suffix := rand.Intn(1e6)
switch entity {
case cloud.Instance:
return fmt.Sprintf("i-%d", suffix)
case cloud.Subnet:
return fmt.Sprintf("subnet-%d", suffix)
case cloud.Vpc:
return fmt.Sprintf("vpc-%d", suffix)
case cloud.Volume:
return fmt.Sprintf("vol-%d", suffix)
case cloud.SecurityGroup:
return fmt.Sprintf("sg-%d", suffix)
case cloud.InternetGateway:
return fmt.Sprintf("igw-%d", suffix)
case cloud.NatGateway:
return fmt.Sprintf("nat-%d", suffix)
case cloud.RouteTable:
return fmt.Sprintf("rtb-%d", suffix)
default:
return fmt.Sprintf("dryrunid-%d", suffix)
}
}
type awsCall struct {
fnName string
fn interface{}
logger *logger.Logger
setters []setter
}
func (dc *awsCall) execute(input interface{}) (output interface{}, err error) {
defer func() {
if e := recover(); e != nil {
output = nil
err = fmt.Errorf("%s", e)
}
}()
for _, s := range dc.setters {
if err = s.set(input); err != nil {
return nil, err
}
}
fnVal := reflect.ValueOf(dc.fn)
values := []reflect.Value{reflect.ValueOf(input)}
start := time.Now()
results := fnVal.Call(values)
if err, ok := results[1].Interface().(error); ok && err != nil {
return nil, fmt.Errorf("%s", err)
}
dc.logger.ExtraVerbosef("%s call took %s", dc.fnName, time.Since(start))
output = results[0].Interface()
return
}
type checker struct {
description string
timeout time.Duration
frequency time.Duration
fetchFunc func() (string, error)
expect string
logger *logger.Logger
checkName string
}
func (c *checker) check() error {
now := time.Now().UTC()
timer := time.NewTimer(c.timeout)
if c.checkName == "" {
c.checkName = "status"
}
defer timer.Stop()
defer c.logger.Println()
for {
select {
case <-timer.C:
return fmt.Errorf("timeout of %s expired", c.timeout)
default:
}
got, err := c.fetchFunc()
if err != nil {
return fmt.Errorf("check %s: %s", c.description, err)
}
if strings.EqualFold(got, c.expect) {
c.logger.InteractiveInfof("check %s %s '%s' done", c.description, c.checkName, c.expect)
return nil
}
elapsed := time.Since(now)
c.logger.InteractiveInfof("%s %s '%s', expect '%s', timeout in %s (retry in %s)", c.description, c.checkName, got, c.expect, color.New(color.FgGreen).Sprint(c.timeout-elapsed.Round(time.Second)), c.frequency)
time.Sleep(c.frequency)
}
}
type enumValidator struct {
expected []string
}
func NewEnumValidator(expected ...string) *enumValidator {
return &enumValidator{expected: expected}
}
func (v *enumValidator) Validate(in *string) error {
val := strings.ToLower(StringValue(in))
for _, e := range v.expected {
if val == strings.ToLower(e) {
return nil
}
}
var expString string
switch len(v.expected) {
case 0:
return errors.New("empty enumeration")
case 1:
expString = fmt.Sprintf("'%s'", v.expected[0])
case 2:
expString = fmt.Sprintf("'%s' or '%s'", v.expected[0], v.expected[1])
default:
expString = fmt.Sprintf("'%s' or '%s'", strings.Join(v.expected[0:len(v.expected)-1], "', '"), v.expected[len(v.expected)-1])
}
return fmt.Errorf("invalid value '%s' expect %s", StringValue(in), expString)
}
func String(v string) *string {
return &v
}
func StringValue(v *string) string {
if v != nil {
return *v
}
return ""
}
func Int64(v int64) *int64 {
return &v
}
func Int64AsIntValue(v *int64) int {
if v != nil {
return int(*v)
}
return 0
}
func Bool(v bool) *bool {
return &v
}
func BoolValue(v *bool) bool {
if v != nil {
return *v
}
return false
}
func decorateAWSError(err error) error {
if aerr, ok := err.(smithy.APIError); ok {
return fmt.Errorf("%s: %s", aerr.ErrorCode(), aerr.ErrorMessage())
}
return err
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"encoding/json"
"fmt"
"os"
"path"
"sort"
"strings"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
"gopkg.in/yaml.v2"
"github.com/wallix/awless/logger"
)
type CreateStack struct {
_ string `action:"create" entity:"stack" awsAPI:"cloudformation" awsCall:"CreateStack" awsInput:"cloudformation.CreateStackInput" awsOutput:"cloudformation.CreateStackOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudformation.Client
Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"`
TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"`
Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"`
DisableRollback *bool `awsName:"DisableRollback" awsType:"awsbool" templateName:"disable-rollback"`
Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"`
OnFailure *string `awsName:"OnFailure" awsType:"awsstr" templateName:"on-failure"`
Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"`
ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"`
Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"`
PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"`
Timeout *int64 `awsName:"TimeoutInMinutes" awsType:"awsint64" templateName:"timeout"`
Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"`
PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"`
StackFile *string `templateName:"stack-file"`
RollbackTriggers []*string `awsName:"RollbackConfiguration.RollbackTriggers" awsType:"awsalarmrollbacktriggers" templateName:"rollback-triggers"`
RollbackMonitoringMin *int64 `awsName:"RollbackConfiguration.MonitoringTimeInMinutes" awsType:"awsint64" templateName:"rollback-monitoring-min"`
}
func (cmd *CreateStack) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("name"), params.Key("template-file"), params.Opt("capabilities", "disable-rollback", "notifications", "on-failure", "parameters", "policy-file", "resource-types", "role", "stack-file", "tags", "timeout", "rollback-triggers", "rollback-monitoring-min")),
params.Validators{"template-file": params.IsFilepath},
)
}
func (cmd *CreateStack) ExtractResult(i interface{}) string {
return StringValue(i.(*cloudformation.CreateStackOutput).StackId)
}
// Add StackFile support via BeforeRun hook
// https://github.com/wallix/awless/issues/145
// http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html
func (cmd *CreateStack) BeforeRun(renv env.Running) (err error) {
cmd.Parameters, cmd.Tags, cmd.PolicyBody, err = processStackFile(cmd.StackFile, cmd.PolicyFile, cmd.Parameters, cmd.Tags)
return
}
type UpdateStack struct {
_ string `action:"update" entity:"stack" awsAPI:"cloudformation" awsCall:"UpdateStack" awsInput:"cloudformation.UpdateStackInput" awsOutput:"cloudformation.UpdateStackOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudformation.Client
Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"`
Capabilities []*string `awsName:"Capabilities" awsType:"awsstringslice" templateName:"capabilities"`
Notifications []*string `awsName:"NotificationARNs" awsType:"awsstringslice" templateName:"notifications"`
Parameters []*string `awsName:"Parameters" awsType:"awsparameterslice" templateName:"parameters"`
ResourceTypes []*string `awsName:"ResourceTypes" awsType:"awsstringslice" templateName:"resource-types"`
Role *string `awsName:"RoleARN" awsType:"awsstr" templateName:"role"`
PolicyFile *string `awsName:"StackPolicyBody" awsType:"awsfiletostring" templateName:"policy-file"`
PolicyUpdateFile *string `awsName:"StackPolicyDuringUpdateBody" awsType:"awsfiletostring" templateName:"policy-update-file"`
TemplateFile *string `awsName:"TemplateBody" awsType:"awsfiletostring" templateName:"template-file"`
UsePreviousTemplate *bool `awsName:"UsePreviousTemplate" awsType:"awsbool" templateName:"use-previous-template"`
Tags []*string `awsName:"Tags" awsType:"awstagslice" templateName:"tags"`
PolicyBody *string `awsName:"StackPolicyBody" awsType:"awsstr"`
StackFile *string `templateName:"stack-file"`
RollbackTriggers []*string `awsName:"RollbackConfiguration.RollbackTriggers" awsType:"awsalarmrollbacktriggers" templateName:"rollback-triggers"`
RollbackMonitoringMin *int64 `awsName:"RollbackConfiguration.MonitoringTimeInMinutes" awsType:"awsint64" templateName:"rollback-monitoring-min"`
}
func (cmd *UpdateStack) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("capabilities", "notifications", "parameters", "policy-file", "policy-update-file", "resource-types", "role", "stack-file", "tags", "template-file", "use-previous-template", "rollback-triggers", "rollback-monitoring-min"),
))
}
func (cmd *UpdateStack) ExtractResult(i interface{}) string {
return StringValue(i.(*cloudformation.UpdateStackOutput).StackId)
}
// Add StackFile support via BeforeRun hook
// https://github.com/wallix/awless/issues/145
// http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-cfn-artifacts.html
func (cmd *UpdateStack) BeforeRun(renv env.Running) (err error) {
cmd.Parameters, cmd.Tags, cmd.PolicyBody, err = processStackFile(cmd.StackFile, cmd.PolicyFile, cmd.Parameters, cmd.Tags)
return
}
type stackFile struct {
Parameters map[string]string `yaml:"Parameters"`
Tags map[string]string `yaml:"Tags"`
StackPolicy map[string]interface{} `yaml:"StackPolicy"`
}
func processStackFile(stackFilePath, policyFile *string, parameters, tags []*string) (newParams, newTags []*string, policyData *string, err error) {
if stackFilePath == nil {
return parameters, tags, nil, nil
}
data, err := readStackFile(*stackFilePath)
if err != nil {
return nil, nil, nil, err
}
if data == nil {
return parameters, tags, nil, nil
}
newParams = mergeCliAndFileValues(data.Parameters, parameters)
newTags = mergeCliAndFileValues(data.Tags, tags)
if policyFile == nil && data.StackPolicy != nil {
policyBytes, err := json.Marshal(data.StackPolicy)
if err != nil {
return nil, nil, nil, err
}
policyData = String(string(policyBytes))
}
return newParams, newTags, policyData, nil
}
func readStackFile(p string) (sf *stackFile, err error) {
var file []byte
file, err = os.ReadFile(p)
if err != nil {
return nil, err
}
switch path.Ext(p) {
case ".json":
err = json.Unmarshal(file, &sf)
if err != nil {
// Result error message:
// [info] KO update stack
// before run:
// json: unmarshal errors:
// invalid character '}' looking for beginning of object key string
return nil, fmt.Errorf("\njson: unmarshal errors:\n %s", err)
}
case ".yml", ".yaml":
err = yaml.Unmarshal(file, &sf)
if err != nil {
// Result error message:
// [info] KO update stack
// before run:
// yaml: unmarshal errors:
// line 1: cannot unmarshal !!str `lalla` into awsspec.stackFile
return nil, fmt.Errorf("\n%s", err)
}
default:
return nil, fmt.Errorf("Unknown StackFile format %q. Should be \".json\", \".yml\" or \".yaml\"", path.Ext(p))
}
return sf, err
}
// mergeCliAndFileValues is the helper func used to merge tags or parameters
// supplied with CLI and StackFile with higher priority for values passed via CLI
// example:
// via cli passed next parameters:
//
// Test1=a
// Test2=b
//
// via StackFile passed next parameters:
//
// Test2=x
// Test3=y
//
// after merge result will be:
//
// Test1=a
// Test2=b
// Test3=y
func mergeCliAndFileValues(valMap map[string]string, valSlice []*string) (resSlice []*string) {
// if values map are absent in StackFile
// just return slice of CLI values
if valMap == nil {
return valSlice
}
val := make(map[string]string)
// building map of parameters passed from cli
for _, v := range valSlice {
splits := strings.SplitN(*v, ":", 2)
if len(splits) == 2 {
val[splits[0]] = splits[1]
}
}
// adding/overwritting values from cli
// to the files values map
for k, v := range val {
valMap[k] = v
}
mapKeys := make([]string, 0, len(valMap))
for k := range valMap {
mapKeys = append(mapKeys, k)
}
// soring map keys, so we have predictable values order for tests
sort.Strings(mapKeys)
// building final parameters list in the expected
// "awsparameterslice" format
for _, k := range mapKeys {
p := strings.Join([]string{k, valMap[k]}, ":")
resSlice = append(resSlice, &p)
}
return resSlice
}
type DeleteStack struct {
_ string `action:"delete" entity:"stack" awsAPI:"cloudformation" awsCall:"DeleteStack" awsInput:"cloudformation.DeleteStackInput" awsOutput:"cloudformation.DeleteStackOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *cloudformation.Client
Name *string `awsName:"StackName" awsType:"awsstr" templateName:"name"`
RetainResources []*string `awsName:"RetainResources" awsType:"awsstringslice" templateName:"retain-resources"`
}
func (cmd *DeleteStack) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"),
params.Opt("retain-resources"),
))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
)
type CreateSubnet struct {
_ string `action:"create" entity:"subnet" awsAPI:"ec2" awsCall:"CreateSubnet" awsInput:"ec2.CreateSubnetInput" awsOutput:"ec2.CreateSubnetOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
CIDR *string `awsName:"CidrBlock" awsType:"awsstr" templateName:"cidr"`
VPC *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
AvailabilityZone *string `awsName:"AvailabilityZone" awsType:"awsstr" templateName:"availabilityzone"`
Public *bool `awsType:"awsboolattribute" templateName:"public"`
Name *string `templateName:"name"`
}
func (cmd *CreateSubnet) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cidr"), params.Key("vpc"), params.Opt(params.Suggested("name"), "availabilityzone", "public")),
params.Validators{"cidr": params.IsCIDR})
}
func (cmd *CreateSubnet) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateSubnetOutput).Subnet.SubnetId)
}
func (cmd *CreateSubnet) AfterRun(renv env.Running, output interface{}) error {
subnetId := awssdk.String(cmd.ExtractResult(output))
if err := createNameTag(subnetId, cmd.Name, renv); err != nil {
return err
}
if BoolValue(cmd.Public) {
updateSubnet := CommandFactory.Build("updatesubnet")().(*UpdateSubnet)
updateSubnet.Id = subnetId
updateSubnet.Public = Bool(true)
if _, err := updateSubnet.Run(renv, nil); err != nil {
return err
}
}
return nil
}
type UpdateSubnet struct {
_ string `action:"update" entity:"subnet" awsAPI:"ec2" awsCall:"ModifySubnetAttribute" awsInput:"ec2.ModifySubnetAttributeInput" awsOutput:"ec2.ModifySubnetAttributeOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"SubnetId" awsType:"awsstr" templateName:"id"`
Public *bool `awsName:"MapPublicIpOnLaunch" awsType:"awsboolattribute" templateName:"public"`
}
func (cmd *UpdateSubnet) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"), params.Opt("public")))
}
type DeleteSubnet struct {
_ string `action:"delete" entity:"subnet" awsAPI:"ec2" awsCall:"DeleteSubnet" awsInput:"ec2.DeleteSubnetInput" awsOutput:"ec2.DeleteSubnetOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"SubnetId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteSubnet) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateSubscription struct {
_ string `action:"create" entity:"subscription" awsAPI:"sns" awsCall:"Subscribe" awsInput:"sns.SubscribeInput" awsOutput:"sns.SubscribeOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sns.Client
Topic *string `awsName:"TopicArn" awsType:"awsstr" templateName:"topic"`
Endpoint *string `awsName:"Endpoint" awsType:"awsstr" templateName:"endpoint"`
Protocol *string `awsName:"Protocol" awsType:"awsstr" templateName:"protocol"`
}
func (cmd *CreateSubscription) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("endpoint"), params.Key("protocol"), params.Key("topic")))
}
func (cmd *CreateSubscription) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*sns.SubscribeOutput).SubscriptionArn)
}
type DeleteSubscription struct {
_ string `action:"delete" entity:"subscription" awsAPI:"sns" awsCall:"Unsubscribe" awsInput:"sns.UnsubscribeInput" awsOutput:"sns.UnsubscribeOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sns.Client
Id *string `awsName:"SubscriptionArn" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteSubscription) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"fmt"
"strings"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateTag struct {
_ string `action:"create" entity:"tag" awsAPI:"ec2" awsDryRun:"manual"` // awsCall:"CreateTags" awsInput:"ec2.CreateTagsInput" awsOutput:"ec2.CreateTagsOutput"
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Resource *string `awsName:"Resources" awsType:"awsstringslice" templateName:"resource"`
Key *string `templateName:"key"`
Value *string `templateName:"value"`
}
func (cmd *CreateTag) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("key"), params.Key("resource"), params.Key("value")))
}
func (cmd *CreateTag) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("dry run: cannot set params on command struct: %s", err)
}
input := &ec2.CreateTagsInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("dry run: cannot inject in ec2.CreateTagsInput: %s", err)
}
input.Tags = []ec2types.Tag{{Key: cmd.Key, Value: cmd.Value}}
start := time.Now()
_, err := cmd.api.CreateTags(context.Background(), input)
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound):
cmd.logger.ExtraVerbosef("dry run: ec2.CreateTags call took %s", time.Since(start))
cmd.logger.Verbose("dry run: create tag ok")
return fakeDryRunId("tag"), nil
}
}
return nil, fmt.Errorf("dry run: %s", err)
}
func (cmd *CreateTag) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.CreateTagsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.CreateTagsInput: %s", err)
}
input.Tags = []ec2types.Tag{{Key: cmd.Key, Value: cmd.Value}}
start := time.Now()
var err error
for attempt := 0; attempt < 5; attempt++ {
_, err = cmd.api.CreateTags(context.Background(), input)
if err == nil {
break
}
time.Sleep(time.Duration(attempt+1) * time.Second)
}
if err != nil {
return nil, err
}
cmd.logger.ExtraVerbosef("ec2.CreateTags call took %s", time.Since(start))
return nil, nil
}
type DeleteTag struct {
_ string `action:"delete" entity:"tag" awsAPI:"ec2" awsDryRun:"manual"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Resource *string `awsName:"Resources" awsType:"awsstringslice" templateName:"resource"`
Key *string `templateName:"key"`
Value *string `templateName:"value"`
}
func (cmd *DeleteTag) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("key"), params.Key("resource"),
params.Opt("value"),
))
}
func (cmd *DeleteTag) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &ec2.DeleteTagsInput{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteTagsInput: %s", err)
}
input.Tags = []ec2types.Tag{{Key: cmd.Key, Value: cmd.Value}}
start := time.Now()
_, err := cmd.api.DeleteTags(context.Background(), input)
if awsErr, ok := err.(smithy.APIError); ok {
switch code := awsErr.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound):
cmd.logger.ExtraVerbosef("dry run: ec2.DeleteTags call took %s", time.Since(start))
cmd.logger.Verbose("dry run: create tag ok")
return fakeDryRunId("tag"), nil
}
}
return nil, err
}
func (cmd *DeleteTag) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DeleteTagsInput{}
if err := structInjector(cmd, input, renv.Context()); err != nil {
return nil, fmt.Errorf("cannot inject in ec2.DeleteTagsInput: %s", err)
}
input.Tags = []ec2types.Tag{{Key: cmd.Key, Value: cmd.Value}}
start := time.Now()
_, err := cmd.api.DeleteTags(context.Background(), input)
cmd.logger.ExtraVerbosef("ec2.DeleteTags call took %s", time.Since(start))
return nil, err
}
func createNameTag(resource, name *string, renv env.Running) error {
createTag := CommandFactory.Build("createtag")().(*CreateTag)
entries := map[string]interface{}{
"key": "Name",
"value": name,
"resource": resource,
}
if err := params.Validate(createTag.ParamsSpec().Validators(), entries); err != nil {
return err
}
_, err := createTag.Run(renv, entries)
return err
}
/*
Copyright 2017 WALLIX
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 awsspec
import (
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
"context"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
"github.com/wallix/awless/logger"
)
type CreateTargetgroup struct {
_ string `action:"create" entity:"targetgroup" awsAPI:"elbv2" awsCall:"CreateTargetGroup" awsInput:"elbv2.CreateTargetGroupInput" awsOutput:"elbv2.CreateTargetGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
Port *int64 `awsName:"Port" awsType:"awsint64" templateName:"port"`
Protocol *string `awsName:"Protocol" awsType:"awsstr" templateName:"protocol"`
Vpc *string `awsName:"VpcId" awsType:"awsstr" templateName:"vpc"`
Healthcheckinterval *int64 `awsName:"HealthCheckIntervalSeconds" awsType:"awsint64" templateName:"healthcheckinterval"`
Healthcheckpath *string `awsName:"HealthCheckPath" awsType:"awsstr" templateName:"healthcheckpath"`
Healthcheckport *string `awsName:"HealthCheckPort" awsType:"awsstr" templateName:"healthcheckport"`
Healthcheckprotocol *string `awsName:"HealthCheckProtocol" awsType:"awsstr" templateName:"healthcheckprotocol"`
Healthchecktimeout *int64 `awsName:"HealthCheckTimeoutSeconds" awsType:"awsint64" templateName:"healthchecktimeout"`
Healthythreshold *int64 `awsName:"HealthyThresholdCount" awsType:"awsint64" templateName:"healthythreshold"`
Unhealthythreshold *int64 `awsName:"UnhealthyThresholdCount" awsType:"awsint64" templateName:"unhealthythreshold"`
Matcher *string `awsName:"Matcher.HttpCode" awsType:"awsstr" templateName:"matcher"`
}
func (cmd *CreateTargetgroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name"), params.Key("port"), params.Key("protocol"), params.Key("vpc"),
params.Opt("healthcheckinterval", "healthcheckpath", "healthcheckport", "healthcheckprotocol", "healthchecktimeout", "healthythreshold", "matcher", "unhealthythreshold"),
))
}
func (cmd *CreateTargetgroup) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*elbv2.CreateTargetGroupOutput).TargetGroups[0].TargetGroupArn)
}
type UpdateTargetgroup struct {
_ string `action:"update" entity:"targetgroup" awsAPI:"elbv2"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `awsName:"TargetGroupArn" awsType:"awsstr" templateName:"id"`
Deregistrationdelay *string `awsType:"awsstr" templateName:"deregistrationdelay"`
Stickiness *string `awsType:"awsstr" templateName:"stickiness"`
Stickinessduration *string `awsType:"awsstr" templateName:"stickinessduration"`
Healthcheckinterval *int64 `awsName:"HealthCheckIntervalSeconds" awsType:"awsint64" templateName:"healthcheckinterval"`
Healthcheckpath *string `awsName:"HealthCheckPath" awsType:"awsstr" templateName:"healthcheckpath"`
Healthcheckport *string `awsName:"HealthCheckPort" awsType:"awsstr" templateName:"healthcheckport"`
Healthcheckprotocol *string `awsName:"HealthCheckProtocol" awsType:"awsstr" templateName:"healthcheckprotocol"`
Healthchecktimeout *int64 `awsName:"HealthCheckTimeoutSeconds" awsType:"awsint64" templateName:"healthchecktimeout"`
Healthythreshold *int64 `awsName:"HealthyThresholdCount" awsType:"awsint64" templateName:"healthythreshold"`
Unhealthythreshold *int64 `awsName:"UnhealthyThresholdCount" awsType:"awsint64" templateName:"unhealthythreshold"`
Matcher *string `awsName:"Matcher.HttpCode" awsType:"awsstr" templateName:"matcher"`
}
func (cmd *UpdateTargetgroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id"),
params.Opt("deregistrationdelay", "healthcheckinterval", "healthcheckpath", "healthcheckport", "healthcheckprotocol", "healthchecktimeout", "healthythreshold", "matcher", "stickiness", "stickinessduration", "unhealthythreshold"),
))
}
func (tg *UpdateTargetgroup) ManualRun(renv env.Running) (interface{}, error) {
tgArn := StringValue(tg.Id)
attrsInput := &elbv2.ModifyTargetGroupAttributesInput{}
var areTargetAttrsModified bool
if v := tg.Stickiness; v != nil {
attrsInput.Attributes = append(attrsInput.Attributes, elbv2types.TargetGroupAttribute{
Key: String("stickiness.enabled"),
Value: v,
})
areTargetAttrsModified = true
}
if v := tg.Stickinessduration; v != nil {
attrsInput.Attributes = append(attrsInput.Attributes, elbv2types.TargetGroupAttribute{
Key: String("stickiness.lb_cookie.duration_seconds"),
Value: v,
})
areTargetAttrsModified = true
}
if v := tg.Deregistrationdelay; v != nil {
attrsInput.Attributes = append(attrsInput.Attributes, elbv2types.TargetGroupAttribute{
Key: String("deregistration_delay.timeout_seconds"),
Value: v,
})
areTargetAttrsModified = true
}
var err error
if areTargetAttrsModified {
if err = setFieldWithType(tgArn, attrsInput, "TargetGroupArn", awsstr, renv.Context()); err != nil {
return nil, err
}
start := time.Now()
if _, err = tg.api.ModifyTargetGroupAttributes(context.Background(), attrsInput); err != nil {
return nil, err
}
tg.logger.ExtraVerbosef("elbv2.ModifyTargetGroupAttributes call took %s", time.Since(start))
}
input := &elbv2.ModifyTargetGroupInput{}
var isTargetGroupModified bool
if v := tg.Healthcheckinterval; v != nil {
if err = setFieldWithType(v, input, "HealthCheckIntervalSeconds", awsint64, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Healthcheckpath; v != nil {
if err = setFieldWithType(v, input, "HealthCheckPath", awsstr, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Healthcheckport; v != nil {
if err = setFieldWithType(v, input, "HealthCheckPort", awsstr, renv.Context()); err != nil {
return nil, err
}
}
if v := tg.Healthcheckprotocol; v != nil {
if err = setFieldWithType(v, input, "HealthCheckProtocol", awsstr, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Healthchecktimeout; v != nil {
if err = setFieldWithType(v, input, "HealthCheckTimeoutSeconds", awsint64, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Healthythreshold; v != nil {
if err = setFieldWithType(v, input, "HealthyThresholdCount", awsint64, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Unhealthythreshold; v != nil {
if err = setFieldWithType(v, input, "UnhealthyThresholdCount", awsint64, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if v := tg.Matcher; v != nil {
if err = setFieldWithType(v, input, "Matcher.HttpCode", awsstr, renv.Context()); err != nil {
return nil, err
}
isTargetGroupModified = true
}
if isTargetGroupModified {
if err = setFieldWithType(tgArn, input, "TargetGroupArn", awsstr, renv.Context()); err != nil {
return nil, err
}
start := time.Now()
output, err := tg.api.ModifyTargetGroup(context.Background(), input)
tg.logger.ExtraVerbosef("elbv2.ModifyTargetGroup call took %s", time.Since(start))
return output, err
}
return nil, nil
}
type DeleteTargetgroup struct {
_ string `action:"delete" entity:"targetgroup" awsAPI:"elbv2" awsCall:"DeleteTargetGroup" awsInput:"elbv2.DeleteTargetGroupInput" awsOutput:"elbv2.DeleteTargetGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *elbv2.Client
Id *string `awsName:"TargetGroupArn" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteTargetgroup) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateTopic struct {
_ string `action:"create" entity:"topic" awsAPI:"sns" awsCall:"CreateTopic" awsInput:"sns.CreateTopicInput" awsOutput:"sns.CreateTopicOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sns.Client
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateTopic) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateTopic) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*sns.CreateTopicOutput).TopicArn)
}
type DeleteTopic struct {
_ string `action:"delete" entity:"topic" awsAPI:"sns" awsCall:"DeleteTopic" awsInput:"sns.DeleteTopicInput" awsOutput:"sns.DeleteTopicOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *sns.Client
Id *string `awsName:"TopicArn" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteTopic) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/* Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateUser struct {
_ string `action:"create" entity:"user" awsAPI:"iam" awsCall:"CreateUser" awsInput:"iam.CreateUserInput" awsOutput:"iam.CreateUserOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"UserName" awsType:"awsstr" templateName:"name"`
}
func (cmd *CreateUser) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
func (cmd *CreateUser) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*iam.CreateUserOutput).User.UserId)
}
type DeleteUser struct {
_ string `action:"delete" entity:"user" awsAPI:"iam" awsCall:"DeleteUser" awsInput:"iam.DeleteUserInput" awsOutput:"iam.DeleteUserOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Name *string `awsName:"UserName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DeleteUser) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("name")))
}
type AttachUser struct {
_ string `action:"attach" entity:"user" awsAPI:"iam" awsCall:"AddUserToGroup" awsInput:"iam.AddUserToGroupInput" awsOutput:"iam.AddUserToGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Group *string `awsName:"GroupName" awsType:"awsstr" templateName:"group"`
Name *string `awsName:"UserName" awsType:"awsstr" templateName:"name"`
}
func (cmd *AttachUser) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("group"), params.Key("name")))
}
type DetachUser struct {
_ string `action:"detach" entity:"user" awsAPI:"iam" awsCall:"RemoveUserFromGroup" awsInput:"iam.RemoveUserFromGroupInput" awsOutput:"iam.RemoveUserFromGroupOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *iam.Client
Group *string `awsName:"GroupName" awsType:"awsstr" templateName:"group"`
Name *string `awsName:"UserName" awsType:"awsstr" templateName:"name"`
}
func (cmd *DetachUser) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("group"), params.Key("name")))
}
/*
Copyright 2017 WALLIX
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 awsspec
import (
"context"
"fmt"
"time"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/smithy-go"
"github.com/wallix/awless/logger"
)
type CreateVolume struct {
_ string `action:"create" entity:"volume" awsAPI:"ec2" awsCall:"CreateVolume" awsInput:"ec2.CreateVolumeInput" awsOutput:"ec2.Volume" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Availabilityzone *string `awsName:"AvailabilityZone" awsType:"awsstr" templateName:"availabilityzone"`
Size *int64 `awsName:"Size" awsType:"awsint64" templateName:"size"`
}
func (cmd *CreateVolume) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("availabilityzone"), params.Key("size")))
}
func (cmd *CreateVolume) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateVolumeOutput).VolumeId)
}
type CheckVolume struct {
_ string `action:"check" entity:"volume" awsAPI:"ec2"`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `templateName:"id"`
State *string `templateName:"state"`
Timeout *int64 `templateName:"timeout"`
}
func (cmd *CheckVolume) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("id"), params.Key("state"), params.Key("timeout")),
params.Validators{
"state": params.IsInEnumIgnoreCase("available", "in-use", notFoundState),
},
)
}
func (cmd *CheckVolume) ManualRun(renv env.Running) (interface{}, error) {
input := &ec2.DescribeVolumesInput{VolumeIds: []string{awssdk.ToString(cmd.Id)}}
c := &checker{
description: fmt.Sprintf("volume %s", StringValue(cmd.Id)),
timeout: time.Duration(Int64AsIntValue(cmd.Timeout)) * time.Second,
frequency: 5 * time.Second,
fetchFunc: func() (string, error) {
output, err := cmd.api.DescribeVolumes(context.Background(), input)
if err != nil {
if awserr, ok := err.(smithy.APIError); ok {
if awserr.ErrorCode() == "VolumeNotFound" {
return notFoundState, nil
}
} else {
return "", err
}
} else {
for _, vol := range output.Volumes {
if StringValue(vol.VolumeId) == StringValue(cmd.Id) {
return string(vol.State), nil
}
}
}
return notFoundState, nil
},
expect: StringValue(cmd.State),
logger: cmd.logger,
}
return nil, c.check()
}
type DeleteVolume struct {
_ string `action:"delete" entity:"volume" awsAPI:"ec2" awsCall:"DeleteVolume" awsInput:"ec2.DeleteVolumeInput" awsOutput:"ec2.DeleteVolumeOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"VolumeId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteVolume) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
type AttachVolume struct {
_ string `action:"attach" entity:"volume" awsAPI:"ec2" awsCall:"AttachVolume" awsInput:"ec2.AttachVolumeInput" awsOutput:"ec2.VolumeAttachment" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Device *string `awsName:"Device" awsType:"awsstr" templateName:"device"`
Id *string `awsName:"VolumeId" awsType:"awsstr" templateName:"id"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
}
func (cmd *AttachVolume) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("device"), params.Key("id"), params.Key("instance")))
}
func (cmd *AttachVolume) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.AttachVolumeOutput).VolumeId)
}
type DetachVolume struct {
_ string `action:"detach" entity:"volume" awsAPI:"ec2" awsCall:"DetachVolume" awsInput:"ec2.DetachVolumeInput" awsOutput:"ec2.VolumeAttachment" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Device *string `awsName:"Device" awsType:"awsstr" templateName:"device"`
Id *string `awsName:"VolumeId" awsType:"awsstr" templateName:"id"`
Instance *string `awsName:"InstanceId" awsType:"awsstr" templateName:"instance"`
Force *bool `awsName:"Force" awsType:"awsbool" templateName:"force"`
}
func (cmd *DetachVolume) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("device"), params.Key("id"), params.Key("instance"),
params.Opt("force"),
))
}
func (cmd *DetachVolume) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.DetachVolumeOutput).VolumeId)
}
/* Copyright 2017 WALLIX
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 awsspec
import (
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/wallix/awless/logger"
)
type CreateVpc struct {
_ string `action:"create" entity:"vpc" awsAPI:"ec2" awsCall:"CreateVpc" awsInput:"ec2.CreateVpcInput" awsOutput:"ec2.CreateVpcOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
CIDR *string `awsName:"CidrBlock" awsType:"awsstr" templateName:"cidr"`
Name *string `awsName:"Name" templateName:"name"`
}
func (cmd *CreateVpc) ParamsSpec() params.Spec {
return params.NewSpec(
params.AllOf(params.Key("cidr"), params.Opt(params.Suggested("name"))),
params.Validators{"cidr": params.IsCIDR})
}
func (cmd *CreateVpc) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*ec2.CreateVpcOutput).Vpc.VpcId)
}
func (cmd *CreateVpc) AfterRun(renv env.Running, output interface{}) error {
return createNameTag(awssdk.String(cmd.ExtractResult(output)), cmd.Name, renv)
}
type DeleteVpc struct {
_ string `action:"delete" entity:"vpc" awsAPI:"ec2" awsCall:"DeleteVpc" awsInput:"ec2.DeleteVpcInput" awsOutput:"ec2.DeleteVpcOutput" awsDryRun:""`
logger *logger.Logger
graph cloud.GraphAPI
api *ec2.Client
Id *string `awsName:"VpcId" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteVpc) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
/*
Copyright 2017 WALLIX
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 awsspec
import (
awssdk "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/route53"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/params"
)
type CreateZone struct {
_ string `action:"create" entity:"zone" awsAPI:"route53" awsCall:"CreateHostedZone" awsInput:"route53.CreateHostedZoneInput" awsOutput:"route53.CreateHostedZoneOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *route53.Client
Callerreference *string `awsName:"CallerReference" awsType:"awsstr" templateName:"callerreference"`
Name *string `awsName:"Name" awsType:"awsstr" templateName:"name"`
Delegationsetid *string `awsName:"DelegationSetId" awsType:"awsstr" templateName:"delegationsetid"`
Comment *string `awsName:"HostedZoneConfig.Comment" awsType:"awsstr" templateName:"comment"`
Isprivate *bool `awsName:"HostedZoneConfig.PrivateZone" awsType:"awsbool" templateName:"isprivate"`
Vpcid *string `awsName:"VPC.VPCId" awsType:"awsstr" templateName:"vpcid"`
Vpcregion *string `awsName:"VPC.VPCRegion" awsType:"awsstr" templateName:"vpcregion"`
}
func (cmd *CreateZone) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("callerreference"), params.Key("name"),
params.Opt("comment", "delegationsetid", "isprivate", "vpcid", "vpcregion"),
))
}
func (cmd *CreateZone) ExtractResult(i interface{}) string {
return awssdk.ToString(i.(*route53.CreateHostedZoneOutput).HostedZone.Id)
}
type DeleteZone struct {
_ string `action:"delete" entity:"zone" awsAPI:"route53" awsCall:"DeleteHostedZone" awsInput:"route53.DeleteHostedZoneInput" awsOutput:"route53.DeleteHostedZoneOutput"`
logger *logger.Logger
graph cloud.GraphAPI
api *route53.Client
Id *string `awsName:"Id" awsType:"awsstr" templateName:"id"`
}
func (cmd *DeleteZone) ParamsSpec() params.Spec {
return params.NewSpec(params.AllOf(params.Key("id")))
}
package awstailers
import (
"context"
"fmt"
"io"
"sort"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/autoscaling"
autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types"
awsservices "github.com/wallix/awless/aws/services"
)
type scalingActivitiesTailer struct {
follow bool
pollingFrequency time.Duration
lastEventTime time.Time
nbEvents int
}
func NewScalingActivitiesTailer(nbEvents int, follow bool, frequency time.Duration) *scalingActivitiesTailer {
return &scalingActivitiesTailer{nbEvents: nbEvents, follow: follow, pollingFrequency: frequency}
}
func (t *scalingActivitiesTailer) Name() string {
return "scaling-activities"
}
func (t *scalingActivitiesTailer) Tail(w io.Writer) error {
infra, ok := awsservices.InfraService.(*awsservices.Infra)
if !ok {
return fmt.Errorf("invalid cloud service, expected awsservices.Infra, got %T", awsservices.InfraService)
}
if err := t.displayLastEvents(infra, w); err != nil {
return err
}
if t.lastEventTime.IsZero() {
return nil
}
if !t.follow {
return nil
}
if t.pollingFrequency < 5*time.Second {
return fmt.Errorf("invalid polling frequency: %s", t.pollingFrequency)
}
ticker := time.NewTicker(t.pollingFrequency)
defer ticker.Stop()
for range ticker.C {
if err := t.displayNewEvents(infra, w); err != nil {
return err
}
}
return nil
}
func (t *scalingActivitiesTailer) displayLastEvents(infra *awsservices.Infra, w io.Writer) error {
out, err := infra.AutoscalingClient.DescribeScalingActivities(context.Background(), &autoscaling.DescribeScalingActivitiesInput{MaxRecords: aws.Int32(int32(t.nbEvents))})
if err != nil {
return err
}
var events []*event
for i, activity := range out.Activities {
evt := newEventFromScalingActivity(activity)
if i == 0 {
t.lastEventTime = evt.stamp
}
events = append(events, evt)
}
sort.Slice(events, func(i int, j int) bool { return events[i].stamp.Before(events[j].stamp) })
for _, evt := range events {
if err := evt.print(w); err != nil {
return err
}
}
return nil
}
func (t *scalingActivitiesTailer) displayNewEvents(infra *awsservices.Infra, w io.Writer) error {
var eventFound bool
var newEvents []*event
lastEventTime := t.lastEventTime
paginator := autoscaling.NewDescribeScalingActivitiesPaginator(infra.AutoscalingClient, &autoscaling.DescribeScalingActivitiesInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(context.Background())
if err != nil {
return err
}
for _, act := range page.Activities {
evt := newEventFromScalingActivity(act)
if t.lastEventTime.Before(evt.stamp) {
t.lastEventTime = evt.stamp
}
if evt.stamp == lastEventTime || evt.stamp.Before(lastEventTime) {
eventFound = true
break
}
newEvents = append(newEvents, evt)
}
if eventFound {
break
}
}
sort.Slice(newEvents, func(i int, j int) bool { return newEvents[i].stamp.Before(newEvents[j].stamp) })
for _, e := range newEvents {
if err := e.print(w); err != nil {
return err
}
}
return nil
}
type event struct {
id string
element string
stamp time.Time
message string
}
func newEventFromScalingActivity(s autoscalingtypes.Activity) *event {
return &event{
id: aws.ToString(s.ActivityId),
stamp: aws.ToTime(s.StartTime),
message: fmt.Sprintf("%s: %s", string(s.StatusCode), aws.ToString(s.Description)),
element: aws.ToString(s.AutoScalingGroupName),
}
}
func (e *event) print(w io.Writer) error {
_, err := fmt.Fprintf(w, "%s: %s\n\t%s\n", e.stamp, e.element, e.message)
return err
}
package awstailers
import (
"bytes"
"context"
"fmt"
"io"
"strings"
"text/tabwriter"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
"github.com/fatih/color"
awsservices "github.com/wallix/awless/aws/services"
)
const (
StackEventLogicalID = "id"
StackEventTimestamp = "ts"
StackEventStatus = "status"
StackEventStatusReason = "reason"
StackEventType = "type"
// valid stack status codes
StackEventComplete = "COMPLETE"
StackEventFailed = "FAILED"
StackEventInProgress = "IN_PROGRESS"
)
type filters []string
type stackEventTailer struct {
stackName string
follow bool
pollingFrequency time.Duration
lastEventID *string
nbEvents int
filters filters
deploymentStatus deploymentStatus
timeout time.Duration
cancelAfterTimeout bool
}
type stackEvent struct {
cloudformationtypes.StackEvent
}
type stackEvents []stackEvent
func NewCloudformationEventsTailer(stackName string, nbEvents int, enableFollow bool, frequency time.Duration, f filters, timeout time.Duration, cancelAfterTimeout bool) *stackEventTailer {
return &stackEventTailer{
stackName: stackName,
follow: enableFollow,
pollingFrequency: frequency,
nbEvents: nbEvents,
filters: f,
timeout: timeout,
cancelAfterTimeout: cancelAfterTimeout,
}
}
func (t *stackEventTailer) Name() string {
return "stack-events"
}
func (t *stackEventTailer) Tail(w io.Writer) error {
cfn, ok := awsservices.CloudformationService.(*awsservices.Cloudformation)
if !ok {
return fmt.Errorf("invalid cloud service, expected awsservices.Cloudformation, got %T", awsservices.CloudformationService)
}
if t.pollingFrequency < 5*time.Second {
return fmt.Errorf("invalid polling frequency: %s, must be greater than 5s", t.pollingFrequency)
}
tab := tabwriter.NewWriter(w, 8, 8, 8, '\t', 0)
tab.Write(t.filters.header())
if !t.follow {
if err := t.displayLastEvents(cfn, tab); err != nil {
return err
}
tab.Flush()
return nil
}
isDeploying, err := t.isStackBeingDeployed(cfn)
if err != nil {
return err
}
if !isDeploying {
return fmt.Errorf("Stack %s not being deployed at the moment", t.stackName)
}
ticker := time.NewTicker(t.pollingFrequency)
timer := time.NewTimer(t.timeout)
defer ticker.Stop()
defer timer.Stop()
isTimeoutReached := false
for {
select {
case <-timer.C:
isTimeoutReached = true
if t.cancelAfterTimeout {
color.Red("Timeout (%s) reached.", t.timeout.String())
color.Red("Canceling update of stack %q", t.stackName)
err := t.cancelStackUpdate(cfn)
if err != nil {
return fmt.Errorf("Couldn't cancel stack update.\nError: %s\nStack update could be running, please check manually", err)
}
} else {
return fmt.Errorf("Timeout (%s) reached. Exiting...", t.timeout.String())
}
case <-ticker.C:
if err := t.displayRelevantEvents(cfn, tab); err != nil {
return err
}
tab.Flush()
if t.deploymentStatus.isFinished {
if len(t.deploymentStatus.failedEvents) > 0 {
var errBuf bytes.Buffer
var f filters = []string{StackEventLogicalID, StackEventType, StackEventStatus, StackEventStatusReason}
if isTimeoutReached {
errBuf.WriteString("Update was canceled because timeout has been reached and option 'Cancel On Timeout' enabled\n")
} else {
errBuf.WriteString("Update failed\n")
}
errBuf.WriteString("Failed events summary:\n")
// printing error events as a nice table
errTab := tabwriter.NewWriter(&errBuf, 25, 8, 0, '\t', 0)
errTab.Write(f.header())
t.deploymentStatus.failedEvents.printReverse(errTab, f)
errTab.Flush()
return fmt.Errorf("%s", errBuf.String())
}
return nil
}
}
}
}
// get N latest events
func (t *stackEventTailer) getLatestEvents(cfn *awsservices.Cloudformation) (stackEvents, error) {
params := &cloudformation.DescribeStackEventsInput{
StackName: &t.stackName,
}
var stEvents stackEvents
for {
resp, err := cfn.CloudformationClient.DescribeStackEvents(context.Background(), params)
if err != nil {
return nil, err
}
for _, e := range resp.StackEvents {
// if lastEventID == nil, then it's first run, and we just take first N events
if t.lastEventID == nil && len(stEvents) >= t.nbEvents {
return stEvents, nil
}
// if lastEventID found, then take all unseen events
if t.lastEventID != nil && aws.ToString(e.EventId) == *t.lastEventID {
return stEvents, nil
}
stEvents = append(stEvents, stackEvent{e})
}
if resp.NextToken == nil {
return stEvents, nil
}
params.NextToken = resp.NextToken
}
}
func (t *stackEventTailer) displayLastEvents(cfn *awsservices.Cloudformation, w io.Writer) error {
events, err := t.getLatestEvents(cfn)
if err != nil {
return err
}
if len(events) > 0 {
t.lastEventID = events[0].EventId
return events.printReverse(w, t.filters)
}
return nil
}
func (t *stackEventTailer) isStackBeingDeployed(cfn *awsservices.Cloudformation) (bool, error) {
stacks, err := cfn.CloudformationClient.DescribeStacks(context.Background(), &cloudformation.DescribeStacksInput{StackName: &t.stackName})
if err != nil {
return false, err
}
if len(stacks.Stacks) == 0 {
return false, fmt.Errorf("Stack not found")
}
return strings.HasSuffix(string(stacks.Stacks[0].StackStatus), StackEventInProgress), nil
}
type deploymentStatus struct {
isFinished bool
failedEvents stackEvents
}
// get last N events relevant for current deployment in progress
func (t *stackEventTailer) getRelevantEvents(cfn *awsservices.Cloudformation) (stEvents stackEvents, err error) {
params := &cloudformation.DescribeStackEventsInput{
StackName: &t.stackName,
}
for {
var resp *cloudformation.DescribeStackEventsOutput
resp, err = cfn.CloudformationClient.DescribeStackEvents(context.Background(), params)
if err != nil {
return nil, err
}
for _, e := range resp.StackEvents {
event := stackEvent{e}
// if lastEventID == nil then it's first run of this method
// if lastEventID != nil then it's not first run and print only new messages
if t.lastEventID != nil && aws.ToString(e.EventId) == *t.lastEventID {
return stEvents, nil
}
stEvents = append(stEvents, event)
// looking for the message which says that stack update or create started
// making it as a first messages in the deployment events
if event.isDeploymentStart() {
return stEvents, nil
}
// if we found message, that stack create/update/delete completed or failed
// then marking build as complete, but keep tailing
if event.isDeploymentFinished() {
t.deploymentStatus.isFinished = true
}
// if we found fail message then append error to the slice
// but keep tailing
if event.isFailed() {
t.deploymentStatus.failedEvents = append(t.deploymentStatus.failedEvents, event)
}
}
if resp.NextToken == nil {
return stEvents, nil
}
params.NextToken = resp.NextToken
}
}
func (t *stackEventTailer) displayRelevantEvents(cfn *awsservices.Cloudformation, w io.Writer) error {
events, err := t.getRelevantEvents(cfn)
if err != nil {
return err
}
if len(events) > 0 {
t.lastEventID = events[0].EventId
}
return events.printReverse(w, t.filters)
}
func coloredResourceStatus(str string) string {
switch {
case strings.HasSuffix(str, StackEventFailed),
str == string(cloudformationtypes.StackStatusUpdateRollbackInProgress),
str == string(cloudformationtypes.StackStatusRollbackInProgress):
return color.New(color.FgRed).SprintFunc()(str)
case strings.HasSuffix(str, StackEventInProgress):
return color.New(color.FgYellow).SprintFunc()(str)
case strings.HasSuffix(str, StackEventComplete):
return color.New(color.FgGreen).SprintFunc()(str)
default:
return str
}
}
func (e stackEvents) printReverse(w io.Writer, f filters) error {
for i := len(e) - 1; i >= 0; i-- {
w.Write(e[i].filter(f))
}
return nil
}
func (f filters) header() []byte {
var buf bytes.Buffer
for i, filter := range f {
switch filter {
case StackEventLogicalID:
buf.WriteString("Logical ID")
case StackEventTimestamp:
buf.WriteString("Timestamp")
case StackEventStatus:
buf.WriteString("Status")
case StackEventStatusReason:
buf.WriteString("Status Reason")
case StackEventType:
buf.WriteString("Type")
}
if i != len(f)-1 {
buf.WriteRune('\t')
}
}
// with "\n" formatted with bold, tabwriter somehow shift lines
// so we need to add "\n" after string being bolded
return []byte(color.New(color.Bold).Sprintf("%s", buf.String()) + "\n")
}
func (e *stackEvent) filter(filters []string) (out []byte) {
var buf bytes.Buffer
for i, f := range filters {
switch {
case f == StackEventLogicalID && e.LogicalResourceId != nil:
buf.WriteString(*e.LogicalResourceId)
case f == StackEventTimestamp && e.Timestamp != nil:
buf.WriteString(e.Timestamp.Format(time.RFC3339))
case f == StackEventStatus && e.ResourceStatus != "":
buf.WriteString(coloredResourceStatus(string(e.ResourceStatus)))
case f == StackEventStatusReason && e.ResourceStatusReason != nil:
buf.WriteString(*e.ResourceStatusReason)
case f == StackEventType && e.ResourceType != nil:
buf.WriteString(*e.ResourceType)
}
if i != len(filters)-1 {
buf.WriteRune('\t')
}
}
buf.WriteRune('\n')
return buf.Bytes()
}
const cfnStackResourceType = "AWS::CloudFormation::Stack"
func (s *stackEvent) isDeploymentStart() bool {
return (s.ResourceType != nil && *s.ResourceType == cfnStackResourceType) &&
(s.ResourceStatus == cloudformationtypes.ResourceStatusCreateInProgress ||
s.ResourceStatus == cloudformationtypes.ResourceStatusDeleteInProgress ||
s.ResourceStatus == cloudformationtypes.ResourceStatusUpdateInProgress)
}
func (s *stackEvent) isDeploymentFinished() bool {
return (s.ResourceType != nil && *s.ResourceType == cfnStackResourceType) &&
(strings.HasSuffix(string(s.ResourceStatus), StackEventComplete) ||
strings.HasSuffix(string(s.ResourceStatus), StackEventFailed))
}
func (s *stackEvent) isFailed() bool {
return strings.HasSuffix(string(s.ResourceStatus), StackEventFailed) || s.ResourceStatus == cloudformationtypes.ResourceStatusUpdateRollbackInProgress
}
func (s *stackEventTailer) cancelStackUpdate(cfn *awsservices.Cloudformation) error {
inp := &cloudformation.CancelUpdateStackInput{StackName: &s.stackName}
_, err := cfn.CloudformationClient.CancelUpdateStack(context.Background(), inp)
return err
}
/*
Copyright 2017 WALLIX
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 cloud
import (
"context"
"errors"
"fmt"
"strings"
)
var ErrFetchAccessDenied = errors.New("access denied to cloud resource")
// Resources
const (
Region string = "region"
//infra
Vpc string = "vpc"
Subnet string = "subnet"
Image string = "image"
ImportImageTask string = "importimagetask"
SecurityGroup string = "securitygroup"
AvailabilityZone string = "availabilityzone"
Keypair string = "keypair"
Volume string = "volume"
Instance string = "instance"
InstanceProfile string = "instanceprofile"
InternetGateway string = "internetgateway"
NatGateway string = "natgateway"
RouteTable string = "routetable"
ElasticIP string = "elasticip"
Snapshot string = "snapshot"
NetworkInterface string = "networkinterface"
Certificate string = "certificate"
//loadbalancer
ClassicLoadBalancer string = "classicloadbalancer"
LoadBalancer string = "loadbalancer"
TargetGroup string = "targetgroup"
Listener string = "listener"
//database
Database string = "database"
DbSubnetGroup string = "dbsubnetgroup"
//access
User string = "user"
Role string = "role"
Group string = "group"
Policy string = "policy"
AccessKey string = "accesskey"
LoginProfile string = "loginprofile"
MFADevice string = "mfadevice"
//storage
Bucket string = "bucket"
S3Object string = "s3object"
Acl string = "storageacl"
//notification
Subscription string = "subscription"
Topic string = "topic"
//queue
Queue string = "queue"
//dns
Zone string = "zone"
Record string = "record"
//lambda
Function string = "function"
//autoscaling
LaunchConfiguration string = "launchconfiguration"
ScalingGroup string = "scalinggroup"
ScalingPolicy string = "scalingpolicy"
//monitoring
Metric string = "metric"
Alarm string = "alarm"
//cdn
Distribution string = "distribution"
//cloudformation
Stack string = "stack"
//container
Repository string = "repository"
Registry string = "registry"
ContainerCluster string = "containercluster"
ContainerService string = "containerservice"
ContainerTask string = "containertask"
Container string = "container"
ContainerInstance string = "containerinstance"
//application autoscaling
AppScalingTarget string = "appscalingtarget"
AppScalingPolicy string = "appscalingpolicy"
//eks
EKSCluster string = "ekscluster"
EKSNodeGroup string = "eksnodegroup"
//dynamodb
DynamoDBTable string = "dynamodbtable"
//secrets & encryption
Secret string = "secret"
Key string = "key"
//api gateway
ApiGateway string = "apigateway"
ApiGatewayRoute string = "apigatewayroute"
ApiGatewayStage string = "apigatewaystage"
//ssm
SSMParameter string = "ssmparameter"
//efs
FileSystem string = "filesystem"
MountTarget string = "mounttarget"
//cloudtrail
Trail string = "trail"
//cloudwatchlogs
LogGroup string = "loggroup"
)
type Service interface {
Region() string
Profile() string
Name() string
ResourceTypes() []string
IsSyncDisabled() bool
Fetch(context.Context) (GraphAPI, error)
FetchByType(context.Context, string) (GraphAPI, error)
}
type Services []Service
func (srvs Services) Names() (names []string) {
for _, srv := range srvs {
names = append(names, srv.Name())
}
return
}
var ServiceRegistry = make(map[string]Service)
func AllServices() (out []Service) {
for _, srv := range ServiceRegistry {
out = append(out, srv)
}
return
}
func GetServiceForType(t string) (Service, error) {
for _, srv := range ServiceRegistry {
for _, typ := range srv.ResourceTypes() {
if typ == t {
return srv, nil
}
}
}
return nil, fmt.Errorf("cannot find cloud service for resource type %s", t)
}
func PluralizeResource(singular string) string {
if strings.HasSuffix(singular, "cy") || strings.HasSuffix(singular, "ry") {
return strings.TrimSuffix(singular, "y") + "ies"
}
return singular + "s"
}
func SingularizeResource(plural string) string {
if strings.HasSuffix(plural, "ies") {
return strings.TrimSuffix(plural, "ies") + "y"
}
return strings.TrimSuffix(plural, "s")
}
/*
Copyright 2017 WALLIX
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 cloud
import (
"io"
)
type GraphAPI interface {
Find(Query) ([]Resource, error)
FindWithProperties(map[string]interface{}) ([]Resource, error)
FilterGraph(Query) (GraphAPI, error)
FindOne(Query) (Resource, error)
MarshalTo(w io.Writer) error
ResourceRelations(r Resource, relation string, recursive bool) ([]Resource, error)
VisitRelations(Resource, string, bool, func(Resource, int) error) error
ResourceSiblings(Resource) ([]Resource, error)
Merge(GraphAPI) error
}
type Resource interface {
Type() string
Id() string
String() string
Format(string) string
Properties() map[string]interface{}
Property(string) (interface{}, bool)
Meta(string) (interface{}, bool)
Same(Resource) bool
}
type Resources []Resource
func (res Resources) Map(f func(Resource) string) (out []string) {
for _, r := range res {
out = append(out, f(r))
}
return
}
/*
Copyright 2017 WALLIX
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 cloud
import (
"io"
"sync"
)
var _ GraphAPI = new(LazyGraph)
type LazyGraph struct {
LoadingFunc func() GraphAPI
once sync.Once
api GraphAPI
}
func (g *LazyGraph) load() {
g.once.Do(func() {
g.api = g.LoadingFunc()
})
}
func (g *LazyGraph) Find(q Query) ([]Resource, error) {
g.load()
return g.api.Find(q)
}
func (g *LazyGraph) FindWithProperties(props map[string]interface{}) ([]Resource, error) {
g.load()
return g.api.FindWithProperties(props)
}
func (g *LazyGraph) FindOne(q Query) (Resource, error) {
g.load()
return g.api.FindOne(q)
}
func (g *LazyGraph) FilterGraph(q Query) (GraphAPI, error) {
g.load()
return g.api.FilterGraph(q)
}
func (g *LazyGraph) MarshalTo(w io.Writer) error {
g.load()
return g.api.MarshalTo(w)
}
func (g *LazyGraph) ResourceRelations(r Resource, relation string, recursive bool) ([]Resource, error) {
g.load()
return g.api.ResourceRelations(r, relation, recursive)
}
func (g *LazyGraph) VisitRelations(r Resource, relation string, includeResource bool, each func(Resource, int) error) error {
g.load()
return g.api.VisitRelations(r, relation, includeResource, each)
}
func (g *LazyGraph) ResourceSiblings(r Resource) ([]Resource, error) {
g.load()
return g.api.ResourceSiblings(r)
}
func (g *LazyGraph) Merge(aG GraphAPI) error {
g.load()
return g.api.Merge(aG)
}
/*
Copyright 2017 WALLIX
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 match
import (
"fmt"
"reflect"
"regexp"
"strings"
"github.com/wallix/awless/cloud"
)
type and struct {
matchers []cloud.Matcher
}
func (m and) Match(r cloud.Resource) bool {
for _, match := range m.matchers {
if !match.Match(r) {
return false
}
}
return len(m.matchers) > 0
}
func And(matchers ...cloud.Matcher) cloud.Matcher {
return and{matchers: matchers}
}
type or struct {
matchers []cloud.Matcher
}
func (m or) Match(r cloud.Resource) bool {
for _, match := range m.matchers {
if match.Match(r) {
return true
}
}
return false
}
func Or(matchers ...cloud.Matcher) cloud.Matcher {
return or{matchers: matchers}
}
type propertyMatcher struct {
name string
value interface{}
matchOnString bool
ignoreCase bool
contains bool
}
func (m propertyMatcher) Match(r cloud.Resource) bool {
v, found := r.Property(m.name)
if !found {
return false
}
expectVal := m.value
if m.matchOnString {
v = fmt.Sprint(v)
expectVal = fmt.Sprint(m.value)
}
if m.ignoreCase {
if vv, vIsStr := v.(string); vIsStr {
v = strings.ToLower(vv)
}
if expect, expectIsStr := expectVal.(string); expectIsStr {
expectVal = strings.ToLower(expect)
}
}
if m.contains {
vv, vIsStr := v.(string)
expect, expectIsStr := expectVal.(string)
if vIsStr && expectIsStr {
return strings.Contains(vv, expect)
}
}
return reflect.DeepEqual(v, expectVal)
}
func Property(name string, val interface{}) propertyMatcher {
return propertyMatcher{name: name, value: val}
}
func (p propertyMatcher) MatchString() propertyMatcher {
p.matchOnString = true
return p
}
func (p propertyMatcher) IgnoreCase() propertyMatcher {
p.ignoreCase = true
return p
}
func (p propertyMatcher) Contains() propertyMatcher {
p.contains = true
return p
}
type tagMatcher struct {
tagRegexp *regexp.Regexp
}
func (m tagMatcher) Match(r cloud.Resource) bool {
tags, ok := r.Properties()["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
if m.tagRegexp.MatchString(t) {
return true
}
}
return false
}
func Tag(key, val string) tagMatcher {
tagQuoteRegexp := "^" + regexp.QuoteMeta(key+"="+val) + "$"
tagWildcardRegexp := regexp.MustCompile(strings.Replace(tagQuoteRegexp, "\\*", ".*", -1))
return tagMatcher{tagRegexp: tagWildcardRegexp}
}
type tagKeyMatcher struct {
key string
}
func (m tagKeyMatcher) Match(r cloud.Resource) bool {
tags, ok := r.Properties()["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
splits := strings.Split(t, "=")
if len(splits) > 0 {
if splits[0] == m.key {
return true
}
}
}
return false
}
func TagKey(key string) tagKeyMatcher {
return tagKeyMatcher{key: key}
}
type tagValueMatcher struct {
value string
}
func (m tagValueMatcher) Match(r cloud.Resource) bool {
tags, ok := r.Properties()["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
splits := strings.Split(t, "=")
if len(splits) > 1 {
if splits[1] == m.value {
return true
}
}
}
return false
}
func TagValue(value string) tagValueMatcher {
return tagValueMatcher{value: value}
}
/*
Copyright 2017 WALLIX
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 cloud
type Query struct {
ResourceType []string
Matcher Matcher
}
type Matcher interface {
Match(r Resource) bool
}
func NewQuery(resourceType ...string) Query {
return Query{ResourceType: resourceType}
}
func (q Query) Match(m Matcher) Query {
q.Matcher = m
return q
}
/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package rdf
import "github.com/wallix/awless/cloud/properties"
const (
Account = "cloud:account"
ACMCertificate = "cloud:acmCertificate"
Actions = "cloud:actions"
ActionsEnabled = "cloud:actionsEnabled"
ActiveServicesCount = "cloud:activeServicesCount"
AdjustmentType = "cloud:adjustmentType"
Affinity = "cloud:affinity"
AgentConnected = "cloud:agentConnected"
AgentState = "cloud:agentState"
AgentVersion = "cloud:agentVersion"
AlarmActions = "cloud:alarmActions"
AlarmNames = "cloud:alarmNames"
Alias = "cloud:alias"
Aliases = "cloud:aliases"
ApproximateMessageCount = "cloud:approximateMessageCount"
Architecture = "cloud:architecture"
Arn = "cloud:arn"
Association = "cloud:association"
Associations = "cloud:associations"
Attachable = "cloud:attachable"
Attached = "cloud:attached"
AttachedAt = "cloud:attachedAt"
Attachment = "cloud:attachment"
Attributes = "cloud:attributes"
AutoUpgrade = "cloud:autoUpgrade"
AvailabilityZone = "cloud:availabilityZone"
AvailabilityZones = "cloud:availabilityZones"
BackupRetentionPeriod = "cloud:backupRetentionPeriod"
Bucket = "cloud:bucketName"
CallerReference = "cloud:callerReference"
Capabilities = "cloud:capabilities"
Certificate = "cloud:certificate"
CertificateAuthority = "cloud:certificateAuthority"
Certificates = "cloud:certificates"
ChangeSet = "cloud:changeSet"
Charset = "cloud:charset"
CheckHTTPCode = "cloud:checkHTTPCode"
CheckInterval = "cloud:checkInterval"
CheckPath = "cloud:checkPath"
CheckPort = "cloud:checkPort"
CheckProtocol = "cloud:checkProtocol"
CheckTimeout = "cloud:checkTimeout"
CIDR = "net:cidr"
CIDRv6 = "net:cidrv6"
CipherSuite = "cloud:cipherSuite"
Class = "cloud:class"
Cluster = "cloud:cluster"
Comment = "rdfs:comment"
Config = "cloud:config"
ContainerInstance = "cloud:containerInstance"
ContainersImages = "cloud:containersImages"
ContainerTask = "cloud:containerTask"
Continent = "cloud:continent"
Cooldown = "cloud:cooldown"
CopyTagsToSnapshot = "cloud:copyTagsToSnapshot"
Country = "cloud:country"
Created = "cloud:created"
DBSecurityGroups = "cloud:dbSecurityGroups"
DBSubnetGroup = "cloud:dbSubnetGroup"
Default = "cloud:default"
DefaultCooldown = "cloud:defaultCooldown"
Delay = "cloud:delaySeconds"
DeploymentName = "cloud:deploymentName"
Deployments = "cloud:deployments"
Description = "cloud:description"
DesiredCapacity = "cloud:desiredCapacity"
Dimensions = "cloud:dimensions"
DisableRollback = "cloud:disableRollback"
DockerVersion = "cloud:dockerVersion"
Document = "cloud:document"
Enabled = "cloud:enabled"
Encrypted = "cloud:encrypted"
Endpoint = "cloud:endpoint"
Engine = "cloud:engine"
EngineVersion = "cloud:engineVersion"
ExitCode = "cloud:exitCode"
Failover = "cloud:failover"
Fingerprint = "cloud:fingerprint"
GlobalID = "cloud:globalID"
GranteeType = "cloud:granteeType"
Grants = "cloud:grants"
Handler = "cloud:handler"
Hash = "cloud:hash"
HealthCheck = "cloud:healthCheck"
HealthCheckGracePeriod = "cloud:healthCheckGracePeriod"
HealthCheckType = "cloud:healthCheckType"
HealthyThresholdCount = "cloud:healthyThresholdCount"
Host = "cloud:host"
HTTPVersion = "cloud:httpVersion"
Hypervisor = "cloud:hypervisor"
ID = "cloud:id"
Image = "cloud:image"
InboundRules = "net:inboundRules"
InlinePolicies = "cloud:inlinePolicies"
Instance = "cloud:instance"
InstanceOwner = "cloud:instanceOwner"
Instances = "cloud:instances"
InsufficientDataActions = "cloud:insufficientDataActions"
IOPS = "cloud:iops"
IPType = "net:ipType"
IPv6Addresses = "cloud:ipv6Addresses"
IPv6Enabled = "cloud:ipv6Enabled"
Key = "cloud:key"
KeyName = "cloud:keyName"
KeyPair = "cloud:keyPair"
LatestRestorableTime = "cloud:latestRestorableTime"
LaunchConfigurationName = "cloud:launchConfigurationName"
Launched = "cloud:launched"
License = "cloud:license"
Lifecycle = "cloud:lifecycle"
LoadBalancer = "cloud:loadBalancer"
Location = "cloud:location"
MACAddress = "cloud:macAddress"
Main = "cloud:main"
MaxSize = "cloud:maxSize"
Memory = "cloud:memory"
Messages = "cloud:messages"
MetricName = "cloud:metricName"
MinSize = "cloud:minSize"
Modified = "cloud:modified"
MonitoringInterval = "cloud:monitoringInterval"
MonitoringRole = "cloud:monitoringRole"
MultiAZ = "cloud:multiAZ"
Name = "cloud:name"
Namespace = "cloud:namemespace"
NetworkInterfaces = "cloud:networkInterfaces"
NewInstancesProtected = "cloud:newInstancesProtected"
Notifications = "cloud:notifications"
OKActions = "cloud:okActions"
OptionGroups = "cloud:optionGroups"
Origins = "cloud:origins"
OutboundRules = "net:outboundRules"
Outputs = "cloud:outputs"
Owner = "cloud:owner"
ParameterGroups = "cloud:parameterGroups"
Parameters = "cloud:parameters"
PasswordLastUsed = "cloud:passwordLastUsed"
Path = "cloud:path"
PathPrefix = "cloud:pathPrefix"
PendingTasksCount = "cloud:pendingTasksCount"
PlacementGroup = "cloud:placementGroup"
PlatformDetails = "cloud:platformDetails"
Port = "net:port"
Ports = "net:ports"
PortRange = "net:portRange"
PreferredBackupDate = "cloud:preferredBackupDate"
PreferredMaintenanceDate = "cloud:preferredMaintenanceDate"
PriceClass = "cloud:priceClass"
Private = "cloud:private"
PrivateDNS = "cloud:privateDNS"
PrivateIP = "net:privateIP"
Profile = "cloud:profile"
Progress = "cloud:progress"
Protocol = "net:protocol"
Public = "cloud:public"
PublicDNS = "cloud:publicDNS"
PublicIP = "net:publicIP"
RecordCount = "cloud:records"
Records = "cloud:recordCount"
Region = "cloud:region"
RegisteredContainerInstancesCount = "cloud:registeredContainerInstancesCount"
ReplicaOf = "cloud:replicaOf"
Role = "cloud:role"
Roles = "cloud:roles"
RootDevice = "cloud:rootDevice"
RootDeviceType = "cloud:rootDeviceType"
Routes = "net:routes"
RunningTasksCount = "cloud:runningTasksCount"
Runtime = "cloud:runtime"
ScalingAdjustment = "cloud:scalingAdjustment"
ScalingGroupName = "cloud:scalingGroupName"
Scheme = "net:scheme"
SecondaryAvailabilityZone = "cloud:secondaryAvailabilityZone"
SecurityGroups = "cloud:securityGroups"
Set = "cloud:set"
Size = "cloud:size"
Source = "cloud:source"
SpotInstanceRequestId = "cloud:spotInstanceRequestId"
SpotPrice = "cloud:spotPrice"
SSLSupportMethod = "cloud:sslSupportMethod"
State = "cloud:state"
StateMessage = "cloud:stateMessage"
Stopped = "cloud:stopped"
Storage = "cloud:storage"
StorageType = "cloud:storageType"
Subnet = "cloud:subnet"
Subnets = "cloud:subnets"
Tags = "cloud:tags"
TargetGroups = "cloud:targetGroups"
Timeout = "cloud:timezone"
Timezone = "cloud:timeout"
TLSVersionRequired = "cloud:tlsVersionRequired"
Topic = "cloud:topic"
TrafficPolicyInstance = "cloud:trafficPolicyInstance"
TrustPolicy = "cloud:trustPolicy"
TTL = "cloud:ttl"
Type = "cloud:type"
UnhealthyThresholdCount = "cloud:unhealthyThresholdCount"
Updated = "cloud:updated"
URI = "cloud:uri"
UserData = "cloud:userData"
Username = "cloud:username"
Value = "cloud:value"
Version = "cloud:version"
Virtualization = "cloud:virtualization"
Volume = "cloud:volume"
Vpc = "cloud:vpc"
Vpcs = "cloud:vpcs"
WebACL = "cloud:webACL"
Weight = "cloud:weight"
Zone = "cloud:zone"
PlatformVersion = "cloud:platformVersion"
KubernetesVersion = "cloud:kubernetesVersion"
RoleArn = "cloud:roleArn"
ScalingConfig = "cloud:scalingConfig"
TableClass = "cloud:tableClass"
ItemCount = "cloud:itemCount"
SizeBytes = "cloud:sizeBytes"
KeySchema = "cloud:keySchema"
ReadCapacity = "cloud:readCapacity"
WriteCapacity = "cloud:writeCapacity"
RotationEnabled = "cloud:rotationEnabled"
RotationInterval = "cloud:rotationInterval"
LastAccessed = "cloud:lastAccessed"
LastRotated = "cloud:lastRotated"
KeyManager = "cloud:keyManager"
KeyUsage = "cloud:keyUsage"
KeyState = "cloud:keyState"
Origin = "cloud:origin"
ApiProtocol = "cloud:apiProtocol"
RouteKey = "cloud:routeKey"
Target = "cloud:target"
StageName = "cloud:stageName"
DeploymentID = "cloud:deploymentID"
AutoDeploy = "cloud:autoDeploy"
ParameterType = "cloud:parameterType"
DataType = "cloud:dataType"
Tier = "cloud:tier"
PerformanceMode = "cloud:performanceMode"
ThroughputMode = "cloud:throughputMode"
LifecycleState = "cloud:lifecycleState"
IPAddress = "net:ipAddress"
NumberOfMountTargets = "cloud:numberOfMountTargets"
IsMultiRegion = "cloud:isMultiRegion"
IsLogging = "cloud:isLogging"
S3BucketName = "cloud:s3BucketName"
HomeRegion = "cloud:homeRegion"
HasCustomEventSelectors = "cloud:hasCustomEventSelectors"
HasInsightSelectors = "cloud:hasInsightSelectors"
TrailArn = "cloud:trailArn"
RetentionDays = "cloud:retentionDays"
StoredBytes = "cloud:storedBytes"
)
func init() {
Labels = map[string]string{
properties.Account: Account,
properties.ACMCertificate: ACMCertificate,
properties.Actions: Actions,
properties.ActionsEnabled: ActionsEnabled,
properties.ActiveServicesCount: ActiveServicesCount,
properties.AdjustmentType: AdjustmentType,
properties.Affinity: Affinity,
properties.AgentConnected: AgentConnected,
properties.AgentState: AgentState,
properties.AgentVersion: AgentVersion,
properties.AlarmActions: AlarmActions,
properties.AlarmNames: AlarmNames,
properties.Alias: Alias,
properties.Aliases: Aliases,
properties.ApproximateMessageCount: ApproximateMessageCount,
properties.Architecture: Architecture,
properties.Arn: Arn,
properties.Association: Association,
properties.Associations: Associations,
properties.Attachable: Attachable,
properties.Attached: Attached,
properties.AttachedAt: AttachedAt,
properties.Attachment: Attachment,
properties.Attributes: Attributes,
properties.AutoUpgrade: AutoUpgrade,
properties.AvailabilityZone: AvailabilityZone,
properties.AvailabilityZones: AvailabilityZones,
properties.BackupRetentionPeriod: BackupRetentionPeriod,
properties.Bucket: Bucket,
properties.CallerReference: CallerReference,
properties.Capabilities: Capabilities,
properties.Certificate: Certificate,
properties.CertificateAuthority: CertificateAuthority,
properties.Certificates: Certificates,
properties.ChangeSet: ChangeSet,
properties.Charset: Charset,
properties.CheckHTTPCode: CheckHTTPCode,
properties.CheckInterval: CheckInterval,
properties.CheckPath: CheckPath,
properties.CheckPort: CheckPort,
properties.CheckProtocol: CheckProtocol,
properties.CheckTimeout: CheckTimeout,
properties.CIDR: CIDR,
properties.CIDRv6: CIDRv6,
properties.CipherSuite: CipherSuite,
properties.Class: Class,
properties.Cluster: Cluster,
properties.Comment: Comment,
properties.Config: Config,
properties.ContainerInstance: ContainerInstance,
properties.ContainersImages: ContainersImages,
properties.ContainerTask: ContainerTask,
properties.Continent: Continent,
properties.Cooldown: Cooldown,
properties.CopyTagsToSnapshot: CopyTagsToSnapshot,
properties.Country: Country,
properties.Created: Created,
properties.DBSecurityGroups: DBSecurityGroups,
properties.DBSubnetGroup: DBSubnetGroup,
properties.Default: Default,
properties.DefaultCooldown: DefaultCooldown,
properties.Delay: Delay,
properties.DeploymentName: DeploymentName,
properties.Deployments: Deployments,
properties.Description: Description,
properties.DesiredCapacity: DesiredCapacity,
properties.Dimensions: Dimensions,
properties.DisableRollback: DisableRollback,
properties.DockerVersion: DockerVersion,
properties.Document: Document,
properties.Enabled: Enabled,
properties.Encrypted: Encrypted,
properties.Endpoint: Endpoint,
properties.Engine: Engine,
properties.EngineVersion: EngineVersion,
properties.ExitCode: ExitCode,
properties.Failover: Failover,
properties.Fingerprint: Fingerprint,
properties.GlobalID: GlobalID,
properties.GranteeType: GranteeType,
properties.Grants: Grants,
properties.Handler: Handler,
properties.Hash: Hash,
properties.HealthCheck: HealthCheck,
properties.HealthCheckGracePeriod: HealthCheckGracePeriod,
properties.HealthCheckType: HealthCheckType,
properties.HealthyThresholdCount: HealthyThresholdCount,
properties.Host: Host,
properties.HTTPVersion: HTTPVersion,
properties.Hypervisor: Hypervisor,
properties.ID: ID,
properties.Image: Image,
properties.InboundRules: InboundRules,
properties.InlinePolicies: InlinePolicies,
properties.Instance: Instance,
properties.InstanceOwner: InstanceOwner,
properties.Instances: Instances,
properties.InsufficientDataActions: InsufficientDataActions,
properties.IOPS: IOPS,
properties.IPType: IPType,
properties.IPv6Addresses: IPv6Addresses,
properties.IPv6Enabled: IPv6Enabled,
properties.Key: Key,
properties.KeyName: KeyName,
properties.KeyPair: KeyPair,
properties.LatestRestorableTime: LatestRestorableTime,
properties.LaunchConfigurationName: LaunchConfigurationName,
properties.Launched: Launched,
properties.License: License,
properties.Lifecycle: Lifecycle,
properties.LoadBalancer: LoadBalancer,
properties.Location: Location,
properties.MACAddress: MACAddress,
properties.Main: Main,
properties.MaxSize: MaxSize,
properties.Memory: Memory,
properties.Messages: Messages,
properties.MetricName: MetricName,
properties.MinSize: MinSize,
properties.Modified: Modified,
properties.MonitoringInterval: MonitoringInterval,
properties.MonitoringRole: MonitoringRole,
properties.MultiAZ: MultiAZ,
properties.Name: Name,
properties.Namespace: Namespace,
properties.NetworkInterfaces: NetworkInterfaces,
properties.NewInstancesProtected: NewInstancesProtected,
properties.Notifications: Notifications,
properties.OKActions: OKActions,
properties.OptionGroups: OptionGroups,
properties.Origins: Origins,
properties.OutboundRules: OutboundRules,
properties.Outputs: Outputs,
properties.Owner: Owner,
properties.ParameterGroups: ParameterGroups,
properties.Parameters: Parameters,
properties.PasswordLastUsed: PasswordLastUsed,
properties.Path: Path,
properties.PathPrefix: PathPrefix,
properties.PendingTasksCount: PendingTasksCount,
properties.PlacementGroup: PlacementGroup,
properties.PlatformDetails: PlatformDetails,
properties.Port: Port,
properties.Ports: Ports,
properties.PortRange: PortRange,
properties.PreferredBackupDate: PreferredBackupDate,
properties.PreferredMaintenanceDate: PreferredMaintenanceDate,
properties.PriceClass: PriceClass,
properties.Private: Private,
properties.PrivateDNS: PrivateDNS,
properties.PrivateIP: PrivateIP,
properties.Profile: Profile,
properties.Progress: Progress,
properties.Protocol: Protocol,
properties.Public: Public,
properties.PublicDNS: PublicDNS,
properties.PublicIP: PublicIP,
properties.RecordCount: RecordCount,
properties.Records: Records,
properties.Region: Region,
properties.RegisteredContainerInstancesCount: RegisteredContainerInstancesCount,
properties.ReplicaOf: ReplicaOf,
properties.Role: Role,
properties.Roles: Roles,
properties.RootDevice: RootDevice,
properties.RootDeviceType: RootDeviceType,
properties.Routes: Routes,
properties.RunningTasksCount: RunningTasksCount,
properties.Runtime: Runtime,
properties.ScalingAdjustment: ScalingAdjustment,
properties.ScalingGroupName: ScalingGroupName,
properties.Scheme: Scheme,
properties.SecondaryAvailabilityZone: SecondaryAvailabilityZone,
properties.SecurityGroups: SecurityGroups,
properties.Set: Set,
properties.Size: Size,
properties.Source: Source,
properties.SpotInstanceRequestId: SpotInstanceRequestId,
properties.SpotPrice: SpotPrice,
properties.SSLSupportMethod: SSLSupportMethod,
properties.State: State,
properties.StateMessage: StateMessage,
properties.Stopped: Stopped,
properties.Storage: Storage,
properties.StorageType: StorageType,
properties.Subnet: Subnet,
properties.Subnets: Subnets,
properties.Tags: Tags,
properties.TargetGroups: TargetGroups,
properties.Timeout: Timeout,
properties.Timezone: Timezone,
properties.TLSVersionRequired: TLSVersionRequired,
properties.Topic: Topic,
properties.TrafficPolicyInstance: TrafficPolicyInstance,
properties.TrustPolicy: TrustPolicy,
properties.TTL: TTL,
properties.Type: Type,
properties.UnhealthyThresholdCount: UnhealthyThresholdCount,
properties.Updated: Updated,
properties.URI: URI,
properties.UserData: UserData,
properties.Username: Username,
properties.Value: Value,
properties.Version: Version,
properties.Virtualization: Virtualization,
properties.Volume: Volume,
properties.Vpc: Vpc,
properties.Vpcs: Vpcs,
properties.WebACL: WebACL,
properties.Weight: Weight,
properties.Zone: Zone,
properties.PlatformVersion: PlatformVersion,
properties.KubernetesVersion: KubernetesVersion,
properties.RoleArn: RoleArn,
properties.ScalingConfig: ScalingConfig,
properties.TableClass: TableClass,
properties.ItemCount: ItemCount,
properties.SizeBytes: SizeBytes,
properties.KeySchema: KeySchema,
properties.ReadCapacity: ReadCapacity,
properties.WriteCapacity: WriteCapacity,
properties.RotationEnabled: RotationEnabled,
properties.RotationInterval: RotationInterval,
properties.LastAccessed: LastAccessed,
properties.LastRotated: LastRotated,
properties.KeyManager: KeyManager,
properties.KeyUsage: KeyUsage,
properties.KeyState: KeyState,
properties.Origin: Origin,
properties.ApiProtocol: ApiProtocol,
properties.RouteKey: RouteKey,
properties.Target: Target,
properties.StageName: StageName,
properties.DeploymentID: DeploymentID,
properties.AutoDeploy: AutoDeploy,
properties.ParameterType: ParameterType,
properties.DataType: DataType,
properties.Tier: Tier,
properties.PerformanceMode: PerformanceMode,
properties.ThroughputMode: ThroughputMode,
properties.LifecycleState: LifecycleState,
properties.IPAddress: IPAddress,
properties.NumberOfMountTargets: NumberOfMountTargets,
properties.IsMultiRegion: IsMultiRegion,
properties.IsLogging: IsLogging,
properties.S3BucketName: S3BucketName,
properties.HomeRegion: HomeRegion,
properties.HasCustomEventSelectors: HasCustomEventSelectors,
properties.HasInsightSelectors: HasInsightSelectors,
properties.TrailArn: TrailArn,
properties.RetentionDays: RetentionDays,
properties.StoredBytes: StoredBytes,
}
}
var Properties = RDFProperties{
Account: {ID: Account, RdfType: "rdf:Property", RdfsLabel: "Account", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ACMCertificate: {ID: ACMCertificate, RdfType: "rdf:Property", RdfsLabel: "ACMCertificate", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Actions: {ID: Actions, RdfType: "rdf:Property", RdfsLabel: "Actions", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
ActionsEnabled: {ID: ActionsEnabled, RdfType: "rdf:Property", RdfsLabel: "ActionsEnabled", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
ActiveServicesCount: {ID: ActiveServicesCount, RdfType: "rdf:Property", RdfsLabel: "ActiveServicesCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
AdjustmentType: {ID: AdjustmentType, RdfType: "rdf:Property", RdfsLabel: "AdjustmentType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Affinity: {ID: Affinity, RdfType: "rdf:Property", RdfsLabel: "Affinity", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
AgentConnected: {ID: AgentConnected, RdfType: "rdf:Property", RdfsLabel: "AgentConnected", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
AgentState: {ID: AgentState, RdfType: "rdf:Property", RdfsLabel: "AgentState", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
AgentVersion: {ID: AgentVersion, RdfType: "rdf:Property", RdfsLabel: "AgentVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
AlarmActions: {ID: AlarmActions, RdfType: "rdf:Property", RdfsLabel: "AlarmActions", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
AlarmNames: {ID: AlarmNames, RdfType: "rdf:Property", RdfsLabel: "AlarmNames", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Alias: {ID: Alias, RdfType: "rdf:Property", RdfsLabel: "Alias", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Aliases: {ID: Aliases, RdfType: "rdf:Property", RdfsLabel: "Aliases", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
ApproximateMessageCount: {ID: ApproximateMessageCount, RdfType: "rdf:Property", RdfsLabel: "ApproximateMessageCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Architecture: {ID: Architecture, RdfType: "rdf:Property", RdfsLabel: "Architecture", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Arn: {ID: Arn, RdfType: "rdf:Property", RdfsLabel: "Arn", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Association: {ID: Association, RdfType: "rdf:Property", RdfsLabel: "Association", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Associations: {ID: Associations, RdfType: "rdf:Property", RdfsLabel: "Associations", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
Attachable: {ID: Attachable, RdfType: "rdf:Property", RdfsLabel: "Attachable", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
Attached: {ID: Attached, RdfType: "rdf:Property", RdfsLabel: "Attached", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
AttachedAt: {ID: AttachedAt, RdfType: "rdf:Property", RdfsLabel: "AttachedAt", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
Attachment: {ID: Attachment, RdfType: "rdf:Property", RdfsLabel: "Attachment", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Attributes: {ID: Attributes, RdfType: "rdf:Property", RdfsLabel: "Attributes", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
AutoUpgrade: {ID: AutoUpgrade, RdfType: "rdf:Property", RdfsLabel: "AutoUpgrade", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
AvailabilityZone: {ID: AvailabilityZone, RdfType: "rdf:Property", RdfsLabel: "AvailabilityZone", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
AvailabilityZones: {ID: AvailabilityZones, RdfType: "rdf:Property", RdfsLabel: "AvailabilityZones", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
BackupRetentionPeriod: {ID: BackupRetentionPeriod, RdfType: "rdf:Property", RdfsLabel: "BackupRetentionPeriod", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
Bucket: {ID: Bucket, RdfType: "rdf:Property", RdfsLabel: "Bucket", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
CallerReference: {ID: CallerReference, RdfType: "rdf:Property", RdfsLabel: "CallerReference", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Capabilities: {ID: Capabilities, RdfType: "rdf:Property", RdfsLabel: "Capabilities", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Certificate: {ID: Certificate, RdfType: "rdf:Property", RdfsLabel: "Certificate", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CertificateAuthority: {ID: CertificateAuthority, RdfType: "rdf:Property", RdfsLabel: "CertificateAuthority", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Certificates: {ID: Certificates, RdfType: "rdf:Property", RdfsLabel: "Certificates", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
ChangeSet: {ID: ChangeSet, RdfType: "rdf:Property", RdfsLabel: "ChangeSet", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Charset: {ID: Charset, RdfType: "rdf:Property", RdfsLabel: "Charset", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CheckHTTPCode: {ID: CheckHTTPCode, RdfType: "rdf:Property", RdfsLabel: "CheckHTTPCode", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CheckInterval: {ID: CheckInterval, RdfType: "rdf:Property", RdfsLabel: "CheckInterval", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
CheckPath: {ID: CheckPath, RdfType: "rdf:Property", RdfsLabel: "CheckPath", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CheckPort: {ID: CheckPort, RdfType: "rdf:Property", RdfsLabel: "CheckPort", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CheckProtocol: {ID: CheckProtocol, RdfType: "rdf:Property", RdfsLabel: "CheckProtocol", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CheckTimeout: {ID: CheckTimeout, RdfType: "rdf:Property", RdfsLabel: "CheckTimeout", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
CIDR: {ID: CIDR, RdfType: "rdf:Property", RdfsLabel: "CIDR", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CIDRv6: {ID: CIDRv6, RdfType: "rdf:Property", RdfsLabel: "CIDRv6", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
CipherSuite: {ID: CipherSuite, RdfType: "rdf:Property", RdfsLabel: "CipherSuite", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Class: {ID: Class, RdfType: "rdf:Property", RdfsLabel: "Class", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Cluster: {ID: Cluster, RdfType: "rdf:Property", RdfsLabel: "Cluster", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Comment: {ID: Comment, RdfType: "rdf:Property", RdfsLabel: "Comment", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Config: {ID: Config, RdfType: "rdf:Property", RdfsLabel: "Config", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ContainerInstance: {ID: ContainerInstance, RdfType: "rdf:Property", RdfsLabel: "ContainerInstance", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
ContainersImages: {ID: ContainersImages, RdfType: "rdf:Property", RdfsLabel: "ContainersImages", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
ContainerTask: {ID: ContainerTask, RdfType: "rdf:Property", RdfsLabel: "ContainerTask", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Continent: {ID: Continent, RdfType: "rdf:Property", RdfsLabel: "Continent", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Cooldown: {ID: Cooldown, RdfType: "rdf:Property", RdfsLabel: "Cooldown", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
CopyTagsToSnapshot: {ID: CopyTagsToSnapshot, RdfType: "rdf:Property", RdfsLabel: "CopyTagsToSnapshot", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Country: {ID: Country, RdfType: "rdf:Property", RdfsLabel: "Country", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Created: {ID: Created, RdfType: "rdf:Property", RdfsLabel: "Created", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
DBSecurityGroups: {ID: DBSecurityGroups, RdfType: "rdf:Property", RdfsLabel: "DBSecurityGroups", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
DBSubnetGroup: {ID: DBSubnetGroup, RdfType: "rdf:Property", RdfsLabel: "DBSubnetGroup", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Default: {ID: Default, RdfType: "rdf:Property", RdfsLabel: "Default", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
DefaultCooldown: {ID: DefaultCooldown, RdfType: "rdf:Property", RdfsLabel: "DefaultCooldown", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Delay: {ID: Delay, RdfType: "rdf:Property", RdfsLabel: "Delay", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
DeploymentName: {ID: DeploymentName, RdfType: "rdf:Property", RdfsLabel: "DeploymentName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Deployments: {ID: Deployments, RdfType: "rdf:Property", RdfsLabel: "Deployments", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
Description: {ID: Description, RdfType: "rdf:Property", RdfsLabel: "Description", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
DesiredCapacity: {ID: DesiredCapacity, RdfType: "rdf:Property", RdfsLabel: "DesiredCapacity", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Dimensions: {ID: Dimensions, RdfType: "rdf:Property", RdfsLabel: "Dimensions", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
DisableRollback: {ID: DisableRollback, RdfType: "rdf:Property", RdfsLabel: "DisableRollback", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
DockerVersion: {ID: DockerVersion, RdfType: "rdf:Property", RdfsLabel: "DockerVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Document: {ID: Document, RdfType: "rdf:Property", RdfsLabel: "Document", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Enabled: {ID: Enabled, RdfType: "rdf:Property", RdfsLabel: "Enabled", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
Encrypted: {ID: Encrypted, RdfType: "rdf:Property", RdfsLabel: "Encrypted", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
Endpoint: {ID: Endpoint, RdfType: "rdf:Property", RdfsLabel: "Endpoint", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Engine: {ID: Engine, RdfType: "rdf:Property", RdfsLabel: "Engine", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
EngineVersion: {ID: EngineVersion, RdfType: "rdf:Property", RdfsLabel: "EngineVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ExitCode: {ID: ExitCode, RdfType: "rdf:Property", RdfsLabel: "ExitCode", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Failover: {ID: Failover, RdfType: "rdf:Property", RdfsLabel: "Failover", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Fingerprint: {ID: Fingerprint, RdfType: "rdf:Property", RdfsLabel: "Fingerprint", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
GlobalID: {ID: GlobalID, RdfType: "rdf:Property", RdfsLabel: "GlobalID", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
GranteeType: {ID: GranteeType, RdfType: "rdf:Property", RdfsLabel: "GranteeType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Grants: {ID: Grants, RdfType: "rdf:Property", RdfsLabel: "Grants", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:Grant"},
Handler: {ID: Handler, RdfType: "rdf:Property", RdfsLabel: "Handler", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Hash: {ID: Hash, RdfType: "rdf:Property", RdfsLabel: "Hash", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HealthCheck: {ID: HealthCheck, RdfType: "rdf:Property", RdfsLabel: "HealthCheck", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HealthCheckGracePeriod: {ID: HealthCheckGracePeriod, RdfType: "rdf:Property", RdfsLabel: "HealthCheckGracePeriod", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
HealthCheckType: {ID: HealthCheckType, RdfType: "rdf:Property", RdfsLabel: "HealthCheckType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HealthyThresholdCount: {ID: HealthyThresholdCount, RdfType: "rdf:Property", RdfsLabel: "HealthyThresholdCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Host: {ID: Host, RdfType: "rdf:Property", RdfsLabel: "Host", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HTTPVersion: {ID: HTTPVersion, RdfType: "rdf:Property", RdfsLabel: "HTTPVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Hypervisor: {ID: Hypervisor, RdfType: "rdf:Property", RdfsLabel: "Hypervisor", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ID: {ID: ID, RdfType: "rdf:Property", RdfsLabel: "ID", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Image: {ID: Image, RdfType: "rdf:Property", RdfsLabel: "Image", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
InboundRules: {ID: InboundRules, RdfType: "rdf:Property", RdfsLabel: "InboundRules", RdfsDefinedBy: "rdfs:list", RdfsDataType: "net-owl:FirewallRule"},
InlinePolicies: {ID: InlinePolicies, RdfType: "rdf:Property", RdfsLabel: "InlinePolicies", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
Instance: {ID: Instance, RdfType: "rdf:Property", RdfsLabel: "Instance", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
InstanceOwner: {ID: InstanceOwner, RdfType: "rdf:Property", RdfsLabel: "InstanceOwner", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Instances: {ID: Instances, RdfType: "rdf:Property", RdfsLabel: "Instances", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
InsufficientDataActions: {ID: InsufficientDataActions, RdfType: "rdf:Property", RdfsLabel: "InsufficientDataActions", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
IOPS: {ID: IOPS, RdfType: "rdf:Property", RdfsLabel: "IOPS", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
IPType: {ID: IPType, RdfType: "rdf:Property", RdfsLabel: "IPType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
IPv6Addresses: {ID: IPv6Addresses, RdfType: "rdf:Property", RdfsLabel: "IPv6Addresses", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
IPv6Enabled: {ID: IPv6Enabled, RdfType: "rdf:Property", RdfsLabel: "IPv6Enabled", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
Key: {ID: Key, RdfType: "rdf:Property", RdfsLabel: "Key", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
KeyName: {ID: KeyName, RdfType: "rdf:Property", RdfsLabel: "KeyName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
KeyPair: {ID: KeyPair, RdfType: "rdf:Property", RdfsLabel: "KeyPair", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
LatestRestorableTime: {ID: LatestRestorableTime, RdfType: "rdf:Property", RdfsLabel: "LatestRestorableTime", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
LaunchConfigurationName: {ID: LaunchConfigurationName, RdfType: "rdf:Property", RdfsLabel: "LaunchConfigurationName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Launched: {ID: Launched, RdfType: "rdf:Property", RdfsLabel: "Launched", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
License: {ID: License, RdfType: "rdf:Property", RdfsLabel: "License", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Lifecycle: {ID: Lifecycle, RdfType: "rdf:Property", RdfsLabel: "Lifecycle", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
LoadBalancer: {ID: LoadBalancer, RdfType: "rdf:Property", RdfsLabel: "LoadBalancer", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Location: {ID: Location, RdfType: "rdf:Property", RdfsLabel: "Location", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
MACAddress: {ID: MACAddress, RdfType: "rdf:Property", RdfsLabel: "MACAddress", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Main: {ID: Main, RdfType: "rdf:Property", RdfsLabel: "Main", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
MaxSize: {ID: MaxSize, RdfType: "rdf:Property", RdfsLabel: "MaxSize", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Memory: {ID: Memory, RdfType: "rdf:Property", RdfsLabel: "Memory", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Messages: {ID: Messages, RdfType: "rdf:Property", RdfsLabel: "Messages", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
MetricName: {ID: MetricName, RdfType: "rdf:Property", RdfsLabel: "MetricName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
MinSize: {ID: MinSize, RdfType: "rdf:Property", RdfsLabel: "MinSize", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Modified: {ID: Modified, RdfType: "rdf:Property", RdfsLabel: "Modified", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
MonitoringInterval: {ID: MonitoringInterval, RdfType: "rdf:Property", RdfsLabel: "MonitoringInterval", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
MonitoringRole: {ID: MonitoringRole, RdfType: "rdf:Property", RdfsLabel: "MonitoringRole", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
MultiAZ: {ID: MultiAZ, RdfType: "rdf:Property", RdfsLabel: "MultiAZ", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Name: {ID: Name, RdfType: "rdf:Property", RdfsLabel: "Name", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Namespace: {ID: Namespace, RdfType: "rdf:Property", RdfsLabel: "Namespace", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
NetworkInterfaces: {ID: NetworkInterfaces, RdfType: "rdf:Property", RdfsLabel: "NetworkInterfaces", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
NewInstancesProtected: {ID: NewInstancesProtected, RdfType: "rdf:Property", RdfsLabel: "NewInstancesProtected", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
Notifications: {ID: Notifications, RdfType: "rdf:Property", RdfsLabel: "Notifications", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
OKActions: {ID: OKActions, RdfType: "rdf:Property", RdfsLabel: "OKActions", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
OptionGroups: {ID: OptionGroups, RdfType: "rdf:Property", RdfsLabel: "OptionGroups", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Origins: {ID: Origins, RdfType: "rdf:Property", RdfsLabel: "Origins", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:DistributionOrigin"},
OutboundRules: {ID: OutboundRules, RdfType: "rdf:Property", RdfsLabel: "OutboundRules", RdfsDefinedBy: "rdfs:list", RdfsDataType: "net-owl:FirewallRule"},
Outputs: {ID: Outputs, RdfType: "rdf:Property", RdfsLabel: "Outputs", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
Owner: {ID: Owner, RdfType: "rdf:Property", RdfsLabel: "Owner", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ParameterGroups: {ID: ParameterGroups, RdfType: "rdf:Property", RdfsLabel: "ParameterGroups", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Parameters: {ID: Parameters, RdfType: "rdf:Property", RdfsLabel: "Parameters", RdfsDefinedBy: "rdfs:list", RdfsDataType: "cloud-owl:KeyValue"},
PasswordLastUsed: {ID: PasswordLastUsed, RdfType: "rdf:Property", RdfsLabel: "PasswordLastUsed", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
Path: {ID: Path, RdfType: "rdf:Property", RdfsLabel: "Path", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PathPrefix: {ID: PathPrefix, RdfType: "rdf:Property", RdfsLabel: "PathPrefix", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PendingTasksCount: {ID: PendingTasksCount, RdfType: "rdf:Property", RdfsLabel: "PendingTasksCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
PlacementGroup: {ID: PlacementGroup, RdfType: "rdf:Property", RdfsLabel: "PlacementGroup", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PlatformDetails: {ID: PlatformDetails, RdfType: "rdf:Property", RdfsLabel: "PlatformDetails", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Port: {ID: Port, RdfType: "rdf:Property", RdfsLabel: "Port", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Ports: {ID: Ports, RdfType: "rdf:Property", RdfsLabel: "Ports", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
PortRange: {ID: PortRange, RdfType: "rdfs:subPropertyOf", RdfsLabel: "PortRange", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PreferredBackupDate: {ID: PreferredBackupDate, RdfType: "rdf:Property", RdfsLabel: "PreferredBackupDate", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PreferredMaintenanceDate: {ID: PreferredMaintenanceDate, RdfType: "rdf:Property", RdfsLabel: "PreferredMaintenanceDate", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PriceClass: {ID: PriceClass, RdfType: "rdf:Property", RdfsLabel: "PriceClass", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Private: {ID: Private, RdfType: "rdf:Property", RdfsLabel: "Private", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PrivateDNS: {ID: PrivateDNS, RdfType: "rdf:Property", RdfsLabel: "PrivateDNS", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PrivateIP: {ID: PrivateIP, RdfType: "rdf:Property", RdfsLabel: "PrivateIP", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Profile: {ID: Profile, RdfType: "rdf:Property", RdfsLabel: "Profile", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Progress: {ID: Progress, RdfType: "rdf:Property", RdfsLabel: "Progress", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Protocol: {ID: Protocol, RdfType: "rdf:Property", RdfsLabel: "Protocol", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Public: {ID: Public, RdfType: "rdf:Property", RdfsLabel: "Public", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
PublicDNS: {ID: PublicDNS, RdfType: "rdf:Property", RdfsLabel: "PublicDNS", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PublicIP: {ID: PublicIP, RdfType: "rdf:Property", RdfsLabel: "PublicIP", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RecordCount: {ID: RecordCount, RdfType: "rdf:Property", RdfsLabel: "RecordCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Records: {ID: Records, RdfType: "rdf:Property", RdfsLabel: "Records", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Region: {ID: Region, RdfType: "rdf:Property", RdfsLabel: "Region", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RegisteredContainerInstancesCount: {ID: RegisteredContainerInstancesCount, RdfType: "rdf:Property", RdfsLabel: "RegisteredContainerInstancesCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
ReplicaOf: {ID: ReplicaOf, RdfType: "rdf:Property", RdfsLabel: "ReplicaOf", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Role: {ID: Role, RdfType: "rdf:Property", RdfsLabel: "Role", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Roles: {ID: Roles, RdfType: "rdf:Property", RdfsLabel: "Roles", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
RootDevice: {ID: RootDevice, RdfType: "rdf:Property", RdfsLabel: "RootDevice", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RootDeviceType: {ID: RootDeviceType, RdfType: "rdf:Property", RdfsLabel: "RootDeviceType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Routes: {ID: Routes, RdfType: "rdf:Property", RdfsLabel: "Routes", RdfsDefinedBy: "rdfs:list", RdfsDataType: "net-owl:Route"},
RunningTasksCount: {ID: RunningTasksCount, RdfType: "rdf:Property", RdfsLabel: "RunningTasksCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Runtime: {ID: Runtime, RdfType: "rdf:Property", RdfsLabel: "Runtime", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ScalingAdjustment: {ID: ScalingAdjustment, RdfType: "rdf:Property", RdfsLabel: "ScalingAdjustment", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
ScalingGroupName: {ID: ScalingGroupName, RdfType: "rdf:Property", RdfsLabel: "ScalingGroupName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Scheme: {ID: Scheme, RdfType: "rdf:Property", RdfsLabel: "Scheme", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
SecondaryAvailabilityZone: {ID: SecondaryAvailabilityZone, RdfType: "rdf:Property", RdfsLabel: "SecondaryAvailabilityZone", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
SecurityGroups: {ID: SecurityGroups, RdfType: "rdf:Property", RdfsLabel: "SecurityGroups", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
Set: {ID: Set, RdfType: "rdf:Property", RdfsLabel: "Set", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Size: {ID: Size, RdfType: "rdf:Property", RdfsLabel: "Size", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Source: {ID: Source, RdfType: "rdf:Property", RdfsLabel: "Source", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
SpotInstanceRequestId: {ID: SpotInstanceRequestId, RdfType: "rdf:Property", RdfsLabel: "SpotInstanceRequestId", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
SpotPrice: {ID: SpotPrice, RdfType: "rdf:Property", RdfsLabel: "SpotPrice", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
SSLSupportMethod: {ID: SSLSupportMethod, RdfType: "rdf:Property", RdfsLabel: "SSLSupportMethod", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
State: {ID: State, RdfType: "rdf:Property", RdfsLabel: "State", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
StateMessage: {ID: StateMessage, RdfType: "rdf:Property", RdfsLabel: "StateMessage", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Stopped: {ID: Stopped, RdfType: "rdf:Property", RdfsLabel: "Stopped", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
Storage: {ID: Storage, RdfType: "rdf:Property", RdfsLabel: "Storage", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
StorageType: {ID: StorageType, RdfType: "rdf:Property", RdfsLabel: "StorageType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Subnet: {ID: Subnet, RdfType: "rdf:Property", RdfsLabel: "Subnet", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Subnets: {ID: Subnets, RdfType: "rdf:Property", RdfsLabel: "Subnets", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
Tags: {ID: Tags, RdfType: "rdf:Property", RdfsLabel: "Tags", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
TargetGroups: {ID: TargetGroups, RdfType: "rdf:Property", RdfsLabel: "TargetGroups", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
Timeout: {ID: Timeout, RdfType: "rdf:Property", RdfsLabel: "Timeout", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Timezone: {ID: Timezone, RdfType: "rdf:Property", RdfsLabel: "Timezone", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
TLSVersionRequired: {ID: TLSVersionRequired, RdfType: "rdf:Property", RdfsLabel: "TLSVersionRequired", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Topic: {ID: Topic, RdfType: "rdf:Property", RdfsLabel: "Topic", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
TrafficPolicyInstance: {ID: TrafficPolicyInstance, RdfType: "rdf:Property", RdfsLabel: "TrafficPolicyInstance", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
TrustPolicy: {ID: TrustPolicy, RdfType: "rdf:Property", RdfsLabel: "TrustPolicy", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
TTL: {ID: TTL, RdfType: "rdf:Property", RdfsLabel: "TTL", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Type: {ID: Type, RdfType: "rdf:Property", RdfsLabel: "Type", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
UnhealthyThresholdCount: {ID: UnhealthyThresholdCount, RdfType: "rdf:Property", RdfsLabel: "UnhealthyThresholdCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
Updated: {ID: Updated, RdfType: "rdf:Property", RdfsLabel: "Updated", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
URI: {ID: URI, RdfType: "rdf:Property", RdfsLabel: "URI", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
UserData: {ID: UserData, RdfType: "rdf:Property", RdfsLabel: "UserData", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Username: {ID: Username, RdfType: "rdf:Property", RdfsLabel: "Username", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Value: {ID: Value, RdfType: "rdf:Property", RdfsLabel: "Value", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Version: {ID: Version, RdfType: "rdf:Property", RdfsLabel: "Version", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Virtualization: {ID: Virtualization, RdfType: "rdf:Property", RdfsLabel: "Virtualization", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Volume: {ID: Volume, RdfType: "rdf:Property", RdfsLabel: "Volume", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Vpc: {ID: Vpc, RdfType: "rdf:Property", RdfsLabel: "Vpc", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
Vpcs: {ID: Vpcs, RdfType: "rdf:Property", RdfsLabel: "Vpcs", RdfsDefinedBy: "rdfs:list", RdfsDataType: "rdfs:Class"},
WebACL: {ID: WebACL, RdfType: "rdf:Property", RdfsLabel: "WebACL", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Weight: {ID: Weight, RdfType: "rdf:Property", RdfsLabel: "Weight", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Zone: {ID: Zone, RdfType: "rdf:Property", RdfsLabel: "Zone", RdfsDefinedBy: "rdfs:Class", RdfsDataType: "xsd:string"},
PlatformVersion: {ID: PlatformVersion, RdfType: "rdf:Property", RdfsLabel: "PlatformVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
KubernetesVersion: {ID: KubernetesVersion, RdfType: "rdf:Property", RdfsLabel: "KubernetesVersion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RoleArn: {ID: RoleArn, RdfType: "rdf:Property", RdfsLabel: "RoleArn", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ScalingConfig: {ID: ScalingConfig, RdfType: "rdf:Property", RdfsLabel: "ScalingConfig", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
TableClass: {ID: TableClass, RdfType: "rdf:Property", RdfsLabel: "TableClass", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ItemCount: {ID: ItemCount, RdfType: "rdf:Property", RdfsLabel: "ItemCount", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
SizeBytes: {ID: SizeBytes, RdfType: "rdf:Property", RdfsLabel: "SizeBytes", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
KeySchema: {ID: KeySchema, RdfType: "rdf:Property", RdfsLabel: "KeySchema", RdfsDefinedBy: "rdfs:list", RdfsDataType: "xsd:string"},
ReadCapacity: {ID: ReadCapacity, RdfType: "rdf:Property", RdfsLabel: "ReadCapacity", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
WriteCapacity: {ID: WriteCapacity, RdfType: "rdf:Property", RdfsLabel: "WriteCapacity", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
RotationEnabled: {ID: RotationEnabled, RdfType: "rdf:Property", RdfsLabel: "RotationEnabled", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
RotationInterval: {ID: RotationInterval, RdfType: "rdf:Property", RdfsLabel: "RotationInterval", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
LastAccessed: {ID: LastAccessed, RdfType: "rdf:Property", RdfsLabel: "LastAccessed", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
LastRotated: {ID: LastRotated, RdfType: "rdf:Property", RdfsLabel: "LastRotated", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:dateTime"},
KeyManager: {ID: KeyManager, RdfType: "rdf:Property", RdfsLabel: "KeyManager", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
KeyUsage: {ID: KeyUsage, RdfType: "rdf:Property", RdfsLabel: "KeyUsage", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
KeyState: {ID: KeyState, RdfType: "rdf:Property", RdfsLabel: "KeyState", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Origin: {ID: Origin, RdfType: "rdf:Property", RdfsLabel: "Origin", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ApiProtocol: {ID: ApiProtocol, RdfType: "rdf:Property", RdfsLabel: "ApiProtocol", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RouteKey: {ID: RouteKey, RdfType: "rdf:Property", RdfsLabel: "RouteKey", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Target: {ID: Target, RdfType: "rdf:Property", RdfsLabel: "Target", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
StageName: {ID: StageName, RdfType: "rdf:Property", RdfsLabel: "StageName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
DeploymentID: {ID: DeploymentID, RdfType: "rdf:Property", RdfsLabel: "DeploymentID", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
AutoDeploy: {ID: AutoDeploy, RdfType: "rdf:Property", RdfsLabel: "AutoDeploy", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
ParameterType: {ID: ParameterType, RdfType: "rdf:Property", RdfsLabel: "ParameterType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
DataType: {ID: DataType, RdfType: "rdf:Property", RdfsLabel: "DataType", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
Tier: {ID: Tier, RdfType: "rdf:Property", RdfsLabel: "Tier", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
PerformanceMode: {ID: PerformanceMode, RdfType: "rdf:Property", RdfsLabel: "PerformanceMode", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
ThroughputMode: {ID: ThroughputMode, RdfType: "rdf:Property", RdfsLabel: "ThroughputMode", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
LifecycleState: {ID: LifecycleState, RdfType: "rdf:Property", RdfsLabel: "LifecycleState", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
IPAddress: {ID: IPAddress, RdfType: "rdf:Property", RdfsLabel: "IPAddress", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
NumberOfMountTargets: {ID: NumberOfMountTargets, RdfType: "rdf:Property", RdfsLabel: "NumberOfMountTargets", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
IsMultiRegion: {ID: IsMultiRegion, RdfType: "rdf:Property", RdfsLabel: "IsMultiRegion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
IsLogging: {ID: IsLogging, RdfType: "rdf:Property", RdfsLabel: "IsLogging", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
S3BucketName: {ID: S3BucketName, RdfType: "rdf:Property", RdfsLabel: "S3BucketName", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HomeRegion: {ID: HomeRegion, RdfType: "rdf:Property", RdfsLabel: "HomeRegion", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
HasCustomEventSelectors: {ID: HasCustomEventSelectors, RdfType: "rdf:Property", RdfsLabel: "HasCustomEventSelectors", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
HasInsightSelectors: {ID: HasInsightSelectors, RdfType: "rdf:Property", RdfsLabel: "HasInsightSelectors", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:boolean"},
TrailArn: {ID: TrailArn, RdfType: "rdf:Property", RdfsLabel: "TrailArn", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:string"},
RetentionDays: {ID: RetentionDays, RdfType: "rdf:Property", RdfsLabel: "RetentionDays", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
StoredBytes: {ID: StoredBytes, RdfType: "rdf:Property", RdfsLabel: "StoredBytes", RdfsDefinedBy: "rdfs:Literal", RdfsDataType: "xsd:int"},
}
package rdf
import "fmt"
// Namespaces
const (
RdfsNS = "rdfs"
RdfNS = "rdf"
CloudNS = "cloud"
CloudRelNS = "cloud-rel"
CloudOwlNS = "cloud-owl"
XsdNS = "xsd"
NetNS = "net"
NetowlNS = "net-owl"
)
// Existing terms
var (
RdfsLabel = fmt.Sprintf("%s:label", RdfsNS)
RdfsList = fmt.Sprintf("%s:list", RdfsNS)
RdfsDefinedBy = fmt.Sprintf("%s:isDefinedBy", RdfsNS)
RdfsDataType = fmt.Sprintf("%s:Datatype", RdfsNS)
RdfsSeeAlso = fmt.Sprintf("%s:seeAlso", RdfsNS)
RdfsLiteral = fmt.Sprintf("%s:Literal", RdfsNS)
RdfsClass = fmt.Sprintf("%s:Class", RdfsNS)
RdfsSubProperty = fmt.Sprintf("%s:subPropertyOf", RdfsNS)
RdfsComment = fmt.Sprintf("%s:comment", RdfsNS)
RdfType = fmt.Sprintf("%s:type", RdfNS)
RdfProperty = fmt.Sprintf("%s:Property", RdfNS)
XsdString = fmt.Sprintf("%s:string", XsdNS)
XsdBoolean = fmt.Sprintf("%s:boolean", XsdNS)
XsdInt = fmt.Sprintf("%s:int", XsdNS)
XsdDateTime = fmt.Sprintf("%s:dateTime", XsdNS)
)
// Classes
var (
Grant = fmt.Sprintf("%s:Grant", CloudOwlNS)
NetFirewallRule = fmt.Sprintf("%s:FirewallRule", NetowlNS)
NetRoute = fmt.Sprintf("%s:Route", NetowlNS)
CloudGrantee = fmt.Sprintf("%s:Grantee", CloudOwlNS)
KeyValue = fmt.Sprintf("%s:KeyValue", CloudOwlNS)
DistributionOrigin = fmt.Sprintf("%s:DistributionOrigin", CloudOwlNS)
Permission = fmt.Sprintf("%s:permission", CloudNS)
Grantee = fmt.Sprintf("%s:grantee", CloudNS)
NetRouteTargets = fmt.Sprintf("%s:routeTargets", NetNS)
NetDestinationPrefixList = fmt.Sprintf("%s:routeDestinationPrefixList", NetNS)
)
// Relations
var (
ParentOf = fmt.Sprintf("%s:parentOf", CloudRelNS)
ChildrenOfRel = "childrenOf"
ApplyOn = fmt.Sprintf("%s:applyOn", CloudRelNS)
DependingOnRel = "dependingOn"
)
var Labels = make(map[string]string)
type rdfProp struct {
ID, RdfType, RdfsLabel, RdfsDefinedBy, RdfsDataType string
}
type RDFProperties map[string]rdfProp
func (r RDFProperties) Get(prop string) (rdfProp, error) {
p, ok := r[prop]
if !ok {
return rdfProp{}, fmt.Errorf("property '%s' not found", p)
}
return p, nil
}
func (r RDFProperties) IsRDFProperty(id string) bool {
prop, ok := r[id]
if !ok {
return false
}
return prop.RdfType == RdfProperty
}
func (r RDFProperties) IsRDFSubProperty(id string) bool {
prop, ok := r[id]
if !ok {
return false
}
return prop.RdfType == RdfsSubProperty
}
func (r RDFProperties) IsRDFList(prop string) bool {
p, ok := r[prop]
if !ok {
return false
}
return p.RdfsDefinedBy == RdfsList
}
func (r RDFProperties) GetRDFId(label string) (string, error) {
propId, ok := Labels[label]
if !ok {
return "", fmt.Errorf("get property id: label '%s' not found", label)
}
return propId, nil
}
func (r RDFProperties) GetDataType(prop string) (string, error) {
p, ok := r[prop]
if !ok {
return "", fmt.Errorf("property '%s' not found", p)
}
return p.RdfsDataType, nil
}
func (r RDFProperties) GetLabel(prop string) (string, error) {
p, ok := r[prop]
if !ok {
return "", fmt.Errorf("property '%s' not found", p)
}
return p.RdfsLabel, nil
}
func (r RDFProperties) GetDefinedBy(prop string) (string, error) {
p, ok := r[prop]
if !ok {
return "", fmt.Errorf("property '%s' not found", p)
}
return p.RdfsDefinedBy, nil
}
/*
Copyright 2017 WALLIX
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 commands
import (
"bufio"
"bytes"
"os"
"github.com/spf13/cobra"
)
func init() {
autocompleteCmd.AddCommand(bashAutocompleteCmd)
autocompleteCmd.AddCommand(zshAutocompleteCmd)
RootCmd.AddCommand(autocompleteCmd)
}
var autocompleteCmd = &cobra.Command{
Use: "completion",
Short: "Output shell completion code for the given shell (bash or zsh)",
Long: `
Output shell completion code for bash or zsh
This command prints shell code which must be evaluated to provide interactive
completion of awless commands.
Bash
$ source <(awless completion bash)
will load the awless completion code for bash. Note that this depends on the
bash-completion framework. It must be sourced before sourcing the awless
completion, e.g. on macOS:
$ brew install bash-completion
$ source $(brew --prefix)/etc/bash_completion
$ source <(awless completion bash)
(or, if you want to preserve completion within new terminal sessions)
$ echo 'source <(awless completion bash)' >> ~/.bashrc
Zsh
$ source <(awless completion zsh)
(or, if you want to preserve completion within new terminal sessions)
$ echo 'source <(awless completion zsh)' >> ~/.zshrc`,
}
var bashAutocompleteCmd = &cobra.Command{
Use: "bash",
Short: "Output shell completion code for bash",
Long: `
Output shell completion code for bash.
This command prints shell code which must be evaluated to provide interactive
completion of awless commands.
$ source <(awless completion bash)
will load the awless completion code for bash. Note that this depends on the
bash-completion framework. It must be sourced before sourcing the awless
completion, e.g. on macOS:
$ brew install bash-completion
$ source $(brew --prefix)/etc/bash_completion
$ source <(awless completion bash)
(or, if you want to preserve completion within new terminal sessions)
$ echo 'source <(awless completion bash)' >> ~/.bashrc`,
RunE: runCompletionBash,
}
var zshAutocompleteCmd = &cobra.Command{
Use: "zsh",
Short: "List users",
Long: `
Output shell completion code for zsh.
This command prints shell code which must be evaluated to provide interactive
completion of awless commands.
$ source <(awless completion zsh)
(or, if you want to preserve completion within new terminal sessions)
$ echo 'source <(awless completion zsh)' >> ~/.zshrc
zsh completions are only supported in versions of zsh >= 5.2`,
RunE: runCompletionZsh,
}
func runCompletionBash(cmd *cobra.Command, args []string) error {
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
return RootCmd.GenBashCompletion(out)
}
// Copyright 2016 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.
func runCompletionZsh(cmd *cobra.Command, args []string) error {
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
zshInitialization := `
__awless_bash_source() {
alias shopt=':'
alias _expand=_bash_expand
alias _complete=_bash_comp
emulate -L sh
setopt kshglob noshglob braceexpand
source "$@"
}
__awless_type() {
# -t is not supported by zsh
if [ "$1" == "-t" ]; then
shift
# fake Bash 4 to disable "complete -o nospace". Instead
# "compopt +-o nospace" is used in the code to toggle trailing
# spaces. We don't support that, but leave trailing spaces on
# all the time
if [ "$1" = "__awless_compopt" ]; then
echo builtin
return 0
fi
fi
type "$@"
}
__awless_compgen() {
local completions w
completions=( $(compgen "$@") ) || return $?
# filter by given word as prefix
while [[ "$1" = -* && "$1" != -- ]]; do
shift
shift
done
if [[ "$1" == -- ]]; then
shift
fi
for w in "${completions[@]}"; do
if [[ "${w}" = "$1"* ]]; then
echo "${w}"
fi
done
}
__awless_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__awless_declare() {
if [ "$1" == "-F" ]; then
whence -w "$@"
else
builtin declare "$@"
fi
}
__awless_ltrim_colon_completions()
{
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
# Remove colon-word prefix from COMPREPLY items
local colon_word=${1%${1##*:}}
local i=${#COMPREPLY[*]}
while [[ $((--i)) -ge 0 ]]; do
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
done
fi
}
__awless_get_comp_words_by_ref() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
words=("${COMP_WORDS[@]}")
cword=("${COMP_CWORD[@]}")
}
__awless_filedir() {
local RET OLD_IFS w qw
__debug "_filedir $@ cur=$cur"
if [[ "$1" = \~* ]]; then
# somehow does not work. Maybe, zsh does not call this at all
eval echo "$1"
return 0
fi
OLD_IFS="$IFS"
IFS=$'\n'
if [ "$1" = "-d" ]; then
shift
RET=( $(compgen -d) )
else
RET=( $(compgen -f) )
fi
IFS="$OLD_IFS"
IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
for w in ${RET[@]}; do
if [[ ! "${w}" = "${cur}"* ]]; then
continue
fi
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
qw="$(__awless_quote "${w}")"
if [ -d "${w}" ]; then
COMPREPLY+=("${qw}/")
else
COMPREPLY+=("${qw}")
fi
fi
done
}
__awless_quote() {
if [[ $1 == \'* || $1 == \"* ]]; then
# Leave out first character
printf %q "${1:1}"
else
printf %q "$1"
fi
}
autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
LWORD='\<'
RWORD='\>'
fi
__awless_convert_bash_to_zsh() {
sed \
-e 's/declare -F/whence -w/' \
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
-e "s/${LWORD}_filedir${RWORD}/__awless_filedir/g" \
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__awless_get_comp_words_by_ref/g" \
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__awless_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__awless_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__awless_compopt/g" \
-e "s/${LWORD}declare${RWORD}/__awless_declare/g" \
-e "s/\\\$(type${RWORD}/\$(__awless_type/g" \
<<'BASH_COMPLETION_EOF'
`
out.Write([]byte(zshInitialization))
buf := new(bytes.Buffer)
RootCmd.GenBashCompletion(buf)
out.Write(buf.Bytes())
zshTail := `
BASH_COMPLETION_EOF
}
__awless_bash_source <(__awless_convert_bash_to_zsh)
`
out.Write([]byte(zshTail))
return nil
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/wallix/awless/config"
)
var keysOnly bool
func init() {
RootCmd.AddCommand(configCmd)
configCmd.Flags().BoolVar(&keysOnly, "keys", false, "list only config keys")
configCmd.AddCommand(configGetCmd)
configCmd.AddCommand(configSetCmd)
configCmd.AddCommand(configUnsetCmd)
}
var configCmd = &cobra.Command{
Use: "config",
Short: "get, set, unset configuration values",
Example: " awless config # list all your config\n awless config set aws.region eu-west-1\n awless config unset instance.count",
PersistentPreRunE: initAwlessEnvHook,
PersistentPostRunE: notifyOnRegionOrProfilePrecedenceHook,
Run: func(cmd *cobra.Command, args []string) {
if keysOnly {
for k := range config.Config {
fmt.Println(k)
}
for k := range config.Defaults {
fmt.Println(k)
}
} else {
fmt.Println(config.DisplayConfig())
}
},
}
var configGetCmd = &cobra.Command{
Use: "get KEY",
Short: "Get a configuration value",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("not enough parameters")
}
d, ok := config.Get(args[0])
if !ok {
fmt.Println("this parameter has not been set")
} else {
fmt.Printf("%v\n", d)
}
return nil
},
}
var configSetCmd = &cobra.Command{
Use: "set KEY [VALUE]",
Short: "Set or update a configuration value",
PersistentPreRun: applyHooks(initAwlessEnvHook),
PersistentPostRun: applyHooks(includeHookIf(&config.TriggerSyncOnConfigUpdate, initCloudServicesHook)),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("not enough parameters")
}
switch len(args) {
case 0:
return fmt.Errorf("not enough parameters")
case 1:
exitOn(config.InteractiveSet(strings.TrimSpace(args[0])))
default:
exitOn(config.Set(strings.TrimSpace(args[0]), strings.TrimSpace(args[1])))
}
return nil
},
}
var configUnsetCmd = &cobra.Command{
Use: "unset KEY",
Short: "Unset a configuration value",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("not enough parameters")
}
_, ok := config.Get(args[0])
if !ok {
fmt.Println("this parameter has not been set")
} else {
exitOn(config.Unset(args[0]))
}
return nil
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"github.com/fatih/color"
)
func exitOn(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, color.RedString("[error] "), err)
os.Exit(1)
}
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
awsservices "github.com/wallix/awless/aws/services"
"github.com/spf13/cobra"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/console"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/sync"
)
var (
showProperties bool
)
func init() {
RootCmd.AddCommand(historyCmd)
historyCmd.Flags().BoolVar(&showProperties, "properties", false, "Full diff with resources properties")
}
var historyCmd = &cobra.Command{
Use: "history",
Hidden: true,
Short: "(in progress) Show a infra resource history & changes using your locally sync snapshots",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
region := config.GetAWSRegion()
root := graph.InitResource(cloud.Region, region)
var diffs []*sync.Diff
all, err := sync.DefaultSyncer.List()
exitOn(err)
for i := 1; i < len(all); i++ {
from, err := sync.DefaultSyncer.LoadRev(all[i-1].Id)
exitOn(err)
to, err := sync.DefaultSyncer.LoadRev(all[i].Id)
exitOn(err)
d, err := sync.BuildDiff(from, to, root.Id())
exitOn(err)
diffs = append(diffs, d)
}
for _, diff := range diffs {
displayRevisionDiff(diff, awsservices.InfraService.Name(), root, verboseGlobalFlag)
}
return nil
},
}
func displayRevisionDiff(diff *sync.Diff, cloudService string, root *graph.Resource, verbose bool) {
fromRevision := "repository creation"
if diff.From.Id != "" {
fromRevision = diff.From.Id[:7] + " on " + diff.From.Date.Format("Monday January 2, 15:04")
}
var graphdiff *graph.Diff
if cloudService == awsservices.InfraService.Name() {
graphdiff = diff.InfraDiff
}
if showProperties {
if graphdiff.HasDiff() {
fmt.Println("▶", cloudService, "properties, from", fromRevision,
"to", diff.To.Id[:7], "on", diff.To.Date.Format("Monday January 2, 15:04"))
displayer, err := console.BuildOptions(
console.WithFormat("table"),
console.WithRootNode(root),
).SetSource(graphdiff).Build()
exitOn(err)
exitOn(displayer.Print(os.Stdout))
fmt.Println()
} else if verbose {
fmt.Println("▶", cloudService, "properties, from", fromRevision,
"to", diff.To.Id[:7], "on", diff.To.Date.Format("Monday January 2, 15:04"))
fmt.Println("No changes.")
}
} else {
if graphdiff.HasDiff() {
fmt.Println("▶", cloudService, "resources, from", fromRevision,
"to", diff.To.Id[:7], "on", diff.To.Date.Format("Monday January 2, 15:04"))
displayer, err := console.BuildOptions(
console.WithFormat("tree"),
console.WithRootNode(root),
).SetSource(graphdiff).Build()
exitOn(err)
exitOn(displayer.Print(os.Stdout))
fmt.Println()
} else if verbose {
fmt.Println("▶", cloudService, "resources, from", fromRevision,
"to", diff.To.Id[:7], "on", diff.To.Date.Format("Monday January 2, 15:04"))
fmt.Println("No resource changes.")
}
}
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"path/filepath"
"strings"
"context"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/spf13/cobra"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/database"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
func applyHooks(funcs ...func(*cobra.Command, []string) error) func(*cobra.Command, []string) {
return func(cmd *cobra.Command, args []string) {
for _, fn := range funcs {
if err := fn(cmd, args); err != nil {
exitOn(err)
}
}
}
}
func initAwlessEnvHook(cmd *cobra.Command, args []string) error {
if err := config.InitAwlessEnv(); err != nil {
return fmt.Errorf("cannot init awless environment: %s", err)
}
return applyRegionAndProfilePrecedence()
}
var profileOverridenThrough, regionOverridenThrough string
func applyRegionAndProfilePrecedence() error {
if awsProfileGlobalFlag != "" {
if err := config.SetVolatile(config.ProfileConfigKey, awsProfileGlobalFlag); err != nil {
return err
}
profileOverridenThrough = "command flag"
} else if envProfile := os.Getenv("AWS_DEFAULT_PROFILE"); envProfile != "" {
if err := config.SetVolatile(config.ProfileConfigKey, envProfile); err != nil {
return err
}
profileOverridenThrough = "AWS_DEFAULT_PROFILE variable"
} else if envProfile := os.Getenv("AWS_PROFILE"); envProfile != "" {
if err := config.SetVolatile(config.ProfileConfigKey, envProfile); err != nil {
return err
}
profileOverridenThrough = "AWS_PROFILE variable"
}
profile := config.GetAWSProfile()
if region, embedded, err := hasEmbeddedRegionInSharedConfigForProfile(profile); err == nil {
if embedded {
if e := config.SetVolatile(config.RegionConfigKey, region); e != nil {
return e
}
regionOverridenThrough = fmt.Sprintf("profile '%s' (see AWS config files $HOME/.aws/{credentials,config})", profile)
} else {
regionOverridenThrough = ""
}
} else {
return err
}
if awsRegionGlobalFlag != "" {
if err := config.SetVolatile(config.RegionConfigKey, awsRegionGlobalFlag); err != nil {
return err
}
regionOverridenThrough = "command flag"
} else if envRegion := os.Getenv("AWS_DEFAULT_REGION"); envRegion != "" {
if err := config.SetVolatile(config.RegionConfigKey, envRegion); err != nil {
return err
}
regionOverridenThrough = "AWS_DEFAULT_REGION variable"
}
return nil
}
func notifyOnRegionOrProfilePrecedenceHook(*cobra.Command, []string) error {
applyRegionAndProfilePrecedence()
if m := profileOverridenThrough; len(m) > 0 {
logger.Infof("profile precedence: '%s' loaded through %s", config.GetAWSProfile(), m)
}
if m := regionOverridenThrough; len(m) > 0 {
logger.Infof("region precedence: '%s' loaded through %s", config.GetAWSRegion(), m)
}
return nil
}
func initCloudServicesHook(cmd *cobra.Command, args []string) error {
if localGlobalFlag {
return nil
}
profile, region := config.GetAWSProfile(), config.GetAWSRegion()
logger.Verbosef("awless %s - loading AWS session with profile '%s' and region '%s'", config.Version, profile, region)
if err := awsservices.Init(profile, region, config.GetConfigWithPrefix("aws."), logger.DefaultLogger, config.SetProfileCallback, networkMonitorFlag); err != nil {
return err
}
if config.TriggerSyncOnConfigUpdate && !strings.HasPrefix(cmd.Name(), "sync") {
var services []cloud.Service
for _, s := range cloud.ServiceRegistry {
services = append(services, s)
}
if !noSyncGlobalFlag {
logger.Infof("Syncing new region '%s'... (disable with --no-sync global flag)", region)
sync.NewSyncer(logger.DefaultLogger).Sync(services...)
}
}
return nil
}
func includeHookIf(cond *bool, hook func(*cobra.Command, []string) error) func(*cobra.Command, []string) error {
return func(c *cobra.Command, args []string) error {
if *cond {
return hook(c, args)
}
return nil
}
}
func initSyncerHook(cmd *cobra.Command, args []string) error {
if noSyncGlobalFlag {
sync.DefaultSyncer = sync.NoOpSyncer()
} else {
sync.DefaultSyncer = sync.NewSyncer(logger.DefaultLogger)
}
return nil
}
func initLoggerHook(cmd *cobra.Command, args []string) error {
var flag int
if verboseGlobalFlag {
flag = logger.VerboseF
}
if extraVerboseGlobalFlag {
flag = flag | logger.ExtraVerboseF
}
logger.DefaultLogger.SetVerbose(flag)
if silentGlobalFlag {
logger.DefaultLogger = logger.DiscardLogger
}
return nil
}
func onVersionUpgrade(cmd *cobra.Command, args []string) error {
var lastVersion string
if derr := database.Execute(func(db *database.DB) (err error) {
lastVersion, err = db.GetStringValue("current.version")
return
}); derr != nil {
fmt.Printf("cannot verify stored version in db: %s\n", derr)
}
if config.IsSemverUpgrade(lastVersion, config.Version) {
if err := database.Execute(func(db *database.DB) error {
return db.SetStringValue("current.version", config.Version)
}); err != nil {
fmt.Printf("cannot store upgraded version in db: %s\n", err)
}
migrationActionsAndExtraMessages(config.Version)
logger.Infof("You have just upgraded awless from %s to %s", lastVersion, config.Version)
logger.Infof("Check out %s latest features at https://github.com/wallix/awless/blob/master/CHANGELOG.md", config.Version)
}
return nil
}
func verifyNewVersionHook(cmd *cobra.Command, args []string) error {
if localGlobalFlag {
return nil
}
config.VerifyNewVersionAvailable("https://updates.awless.io", os.Stderr)
return nil
}
func networkMonitorHook(cmd *cobra.Command, args []string) error {
if networkMonitorFlag {
awsservices.DefaultNetworkMonitor.DisplayStats(os.Stderr)
}
return nil
}
func firstInstallDoneHook(cmd *cobra.Command, args []string) error {
if config.TriggerSyncOnConfigUpdate {
fmt.Fprintln(os.Stderr, "\nAll done. Enjoy!")
fmt.Fprintln(os.Stderr, "You can review and configure awless with `awless config`")
fmt.Fprintln(os.Stderr)
fmt.Fprintf(os.Stderr, "Now running: `%s`\n", cmd.CommandPath())
}
return nil
}
func migrationActionsAndExtraMessages(current string) {
switch current {
case "v0.1.7":
config.Set("instance.distro", "amazonlinux")
logger.Info("In v0.1.7, the default template config value 'instance.image' has been deprecated in favor of 'instance.distro'")
ami, _ := config.Get("instance.image")
if isNotAwlessFormerDefaultAMI(fmt.Sprint(ami)) {
logger.Warningf("\tYou had a customized value of '%s' for the now deprecated 'instance.image'", fmt.Sprint(ami))
logger.Warning("\tThis value will not be taken into account anymore as default when running templates")
} else {
logger.Info("\tMigrated correctly the deprecated 'instance.image' to 'instance.distro'")
}
config.Unset("instance.image")
logger.Info("\tYou can always check your config values with 'awless config'")
case "v0.1.9":
logger.Info("In v0.1.9, the local data file model has been moved to support multi-account transparently")
oldData := filepath.Join(os.Getenv("__AWLESS_HOME"), "aws", "rdf")
if err := os.RemoveAll(oldData); err == nil {
logger.Info("-> Stale data have been removed. The local model (ex: used for completion) will progressively be synced again through your usage of awless.")
logger.Info("-> You can also manually run `awless sync`")
}
}
}
func hasEmbeddedRegionInSharedConfigForProfile(profile string) (string, bool, error) {
cfg, err := awsconfig.LoadDefaultConfig(context.Background(),
awsconfig.WithSharedConfigProfile(profile),
)
if err != nil {
return "", false, fmt.Errorf("cannot check profile '%s' has embedded region in shared config file: %s", profile, err)
}
region := cfg.Region
return region, len(region) > 0, nil
}
func isNotAwlessFormerDefaultAMI(s string) bool {
amis := []string{"ami-c58c1dd3", "ami-4191b524", "ami-7a85a01a", "ami-4836a428", "ami-0bd66a6f", "ami-d3c0c4b5", "ami-b6daced2", "ami-b968bad6", "ami-fc5ae39f", "ami-762a2315", "ami-923d12f5", "ami-9d15c7f3", "ami-52c7b43d", "ami-2bccae47"}
for _, e := range amis {
if e == s {
return false
}
}
return true
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/inspect"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
var (
inspectorFlag string
)
func init() {
RootCmd.AddCommand(inspectCmd)
inspectCmd.Flags().StringVarP(&inspectorFlag, "inspector", "i", "", "Indicates which inspector to run")
}
var inspectCmd = &cobra.Command{
Use: "inspect",
Short: "Analyze your infrastructure through inspectors",
Long: fmt.Sprintf("Basic proof of concept inspectors to analyze your infrastructure: %s", allInspectors()),
Example: " awless inspect -i bucket_sizer\n awless inspect -i pricer\n awless inspect -i port_scanner",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(c *cobra.Command, args []string) error {
inspector, ok := inspect.InspectorsRegister[inspectorFlag]
if !ok {
return fmt.Errorf("command needs a valid inspector: %s", allInspectors())
}
if !localGlobalFlag {
logger.Info("Running full sync before inspection (disable it with --local flag)\n")
var services []cloud.Service
for _, srv := range cloud.ServiceRegistry {
services = append(services, srv)
}
if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
logger.Verbose(err)
}
}
g, err := sync.LoadLocalGraphs(config.GetAWSProfile(), config.GetAWSRegion())
exitOn(err)
exitOn(inspector.Inspect(g))
inspector.Print(os.Stdout)
return nil
},
}
func allInspectors() string {
var all []string
for name := range inspect.InspectorsRegister {
all = append(all, name)
}
return strings.Join(all, ", ")
}
/*
Copyright 2017 WALLIX
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 commands
import (
"context"
"fmt"
"os"
"sort"
"strings"
"github.com/spf13/cobra"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/console"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
type contextKey string
var (
listingFormat string
listingFiltersFlag []string
listingTagFiltersFlag []string
listingTagKeyFiltersFlag []string
listingTagValueFiltersFlag []string
listingColumnsFlag []string
listOnlyIDs bool
noHeadersFlag bool
sortBy []string
reverseFlag bool
)
func init() {
RootCmd.AddCommand(listCmd)
cobra.EnableCommandSorting = false
for _, srvName := range awsservices.ServiceNames {
listCmd.AddCommand(listAllResourceInServiceCmd(srvName))
}
for _, name := range awsservices.ServiceNames {
resources := append([]string{}, awsservices.ResourceTypesPerServiceName()[name]...)
sort.Strings(resources)
for _, resType := range resources {
listCmd.AddCommand(listSpecificResourceCmd(resType))
}
}
listCmd.PersistentFlags().StringVar(&listingFormat, "format", "table", "Output format: table, csv, tsv, json (default to table)")
listCmd.PersistentFlags().StringSliceVar(&listingFiltersFlag, "filter", []string{}, "Filter resources given key/values fields (case insensitive). Ex: --filter type=t2.micro")
listCmd.PersistentFlags().StringArrayVar(&listingTagFiltersFlag, "tag", []string{}, "Filter EC2 resources given tags (case sensitive!). Ex: --tag Env=Production")
listCmd.PersistentFlags().StringArrayVar(&listingTagKeyFiltersFlag, "tag-key", []string{}, "Filter EC2 resources given a tag key only (case sensitive!). Ex: --tag-key Env")
listCmd.PersistentFlags().StringArrayVar(&listingTagValueFiltersFlag, "tag-value", []string{}, "Filter EC2 resources given a tag value only (case sensitive!). Ex: --tag-value Staging")
listCmd.PersistentFlags().StringSliceVar(&listingColumnsFlag, "columns", []string{}, "Select the properties to display in the columns. Ex: --columns id,name,cidr")
listCmd.PersistentFlags().BoolVar(&listOnlyIDs, "ids", false, "List only ids")
listCmd.PersistentFlags().BoolVar(&noHeadersFlag, "no-headers", false, "Do not display headers")
listCmd.PersistentFlags().BoolVar(&reverseFlag, "reverse", false, "Use in conjunction with --sort to reverse sort")
listCmd.PersistentFlags().StringSliceVar(&sortBy, "sort", []string{"Id"}, "Sort tables by column(s) name(s)")
}
var listCmd = &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Example: ` awless list instances --sort uptime
awless list users --format csv
awless list volumes --filter state=in-use --filter type=gp2
awless list volumes --tag-value Purchased
awless list vpcs --tag-key Dept --tag-key Internal
awless list instances --tag Env=Production,Dept=Marketing
awless list instances --filter state=running,type=t2.micro
awless list s3objects --filter bucket=pdf-bucket
awless list loggroups --sort created --reverse
awless list loggroups --format json
awless list trails
awless list eksclusters
awless list dynamodbtables
awless list secrets
awless list ssmparameters
awless list filesystems
awless list apigateways`,
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
Short: "List resources: sorting, filtering via tag/properties, output formatting, etc...",
}
var listSpecificResourceCmd = func(resType string) *cobra.Command {
return &cobra.Command{
Use: cloud.PluralizeResource(resType),
Short: fmt.Sprintf("[%s] List %s %s", awsservices.ServicePerResourceType[resType], strings.ToUpper(awsservices.APIPerResourceType[resType]), cloud.PluralizeResource(resType)),
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 {
var plural string
if len(args) > 1 {
plural = "s"
}
logger.Errorf("invalid parameter%s '%s'", plural, strings.Join(args, " "))
if strings.Contains(args[0], "=") {
if !promptConfirmDefaultYes("Did you mean `awless list %s --filter %s`? ", cloud.PluralizeResource(resType), strings.Join(args, " ")) {
os.Exit(1)
}
listingFiltersFlag = append(listingFiltersFlag, args...)
} else {
os.Exit(1)
}
}
var g cloud.GraphAPI
if localGlobalFlag {
if srvName, ok := awsservices.ServicePerResourceType[resType]; ok {
g = sync.LoadLocalGraphForService(srvName, config.GetAWSProfile(), config.GetAWSRegion())
} else {
exitOn(fmt.Errorf("cannot find service for resource type %s", resType))
}
} else {
srv, err := cloud.GetServiceForType(resType)
exitOn(err)
fetchContext := context.WithValue(context.Background(), contextKey("force"), true)
g, err = srv.FetchByType(context.WithValue(fetchContext, contextKey("filters"), listingFiltersFlag), resType)
exitOn(err)
}
printResources(g, resType)
},
}
}
var listAllResourceInServiceCmd = func(srvName string) *cobra.Command {
return &cobra.Command{
Use: srvName,
Short: fmt.Sprintf("List all %s resources", srvName),
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
g := sync.LoadLocalGraphForService(srvName, config.GetAWSProfile(), config.GetAWSRegion())
displayer, err := console.BuildOptions(
console.WithFormat(listingFormat),
console.WithMaxWidth(console.GetTerminalWidth()),
console.WithIDsOnly(listOnlyIDs),
).SetSource(g).Build()
exitOn(err)
exitOn(displayer.Print(os.Stdout))
},
}
}
func printResources(g cloud.GraphAPI, resType string) {
displayer, err := console.BuildOptions(
console.WithRdfType(resType),
console.WithColumns(listingColumnsFlag),
console.WithFilters(listingFiltersFlag),
console.WithTagFilters(listingTagFiltersFlag),
console.WithTagKeyFilters(listingTagKeyFiltersFlag),
console.WithTagValueFilters(listingTagValueFiltersFlag),
console.WithMaxWidth(console.GetTerminalWidth()),
console.WithFormat(listingFormat),
console.WithIDsOnly(listOnlyIDs),
console.WithSortBy(sortBy...),
console.WithReverseSort(reverseFlag),
console.WithNoHeaders(noHeadersFlag),
).SetSource(g).Build()
exitOn(err)
exitOn(displayer.Print(os.Stdout))
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wallix/awless/database"
"github.com/wallix/awless/logger"
)
var (
deleteAllLogsFlag bool
deleteFromIdLogsFlag string
limitLogCountFlag int
rawJSONLogFlag, idOnlyLogFlag bool
fullLogFlag, shortLogFlag bool
)
func init() {
RootCmd.AddCommand(logCmd)
logCmd.Flags().BoolVar(&deleteAllLogsFlag, "delete-all", false, "Delete all logs from local db")
logCmd.Flags().StringVar(&deleteFromIdLogsFlag, "delete", "", "Delete a specifc log entry given its id")
logCmd.Flags().IntVarP(&limitLogCountFlag, "number", "n", 0, "Limit log output to the last n logs")
logCmd.Flags().BoolVar(&rawJSONLogFlag, "raw", false, "Display logs as raw json with template context info, usually for debug")
logCmd.Flags().BoolVar(&shortLogFlag, "short", false, "Display one or more template log with less info")
logCmd.Flags().BoolVar(&fullLogFlag, "full", false, "Display template logs with full info")
logCmd.Flags().BoolVar(&idOnlyLogFlag, "id-only", false, "Show only log template IDs (i.e. revert IDs)")
}
var logCmd = &cobra.Command{
Use: "log [REVERTID]",
Short: "Show all awless template actions against your cloud infrastructure",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade),
RunE: func(c *cobra.Command, args []string) error {
var all []*database.LoadedTemplate
printer := getPrinter(args)
if len(args) > 0 {
exitOn(database.Execute(func(db *database.DB) error {
single, err := db.GetLoadedTemplate(args[0])
if err != nil {
return err
}
all = append(all, single)
return nil
}))
print(all, printer)
return nil
}
if deleteAllLogsFlag {
exitOn(database.Execute(func(db *database.DB) error {
return db.DeleteTemplates()
}))
return nil
}
if tid := deleteFromIdLogsFlag; tid != "" {
exitOn(database.Execute(func(db *database.DB) error {
return db.DeleteTemplate(tid)
}))
return nil
}
exitOn(database.Execute(func(db *database.DB) (dberr error) {
all, dberr = db.ListTemplates()
return
}))
print(all, printer)
return nil
},
}
func print(all []*database.LoadedTemplate, printer logPrinter) {
if limitLogCountFlag > 0 && limitLogCountFlag < len(all) {
all = all[len(all)-limitLogCountFlag:]
}
for i, loaded := range all {
if loaded.Err != nil {
logger.Errorf("Template '%s' in error: %s", loaded.Key, loaded.Err)
logger.Verbosef("Template raw content\n%s", loaded.Raw)
fmt.Println()
continue
}
if err := printer.print(loaded.TplExec); err != nil {
logger.Error(err.Error())
}
if i < len(all)-1 {
fmt.Println()
}
}
if shortLogFlag {
fmt.Println()
}
}
func getPrinter(args []string) logPrinter {
var defaultPrinter logPrinter
if len(args) > 0 {
defaultPrinter = &fullLogPrinter{os.Stdout}
} else {
defaultPrinter = &statLogPrinter{os.Stdout}
}
switch {
case rawJSONLogFlag:
return &rawJSONPrinter{os.Stdout}
case idOnlyLogFlag:
return &idOnlyPrinter{os.Stdout}
case shortLogFlag:
return &shortLogPrinter{os.Stdout}
case fullLogFlag:
return &fullLogPrinter{os.Stdout}
default:
return defaultPrinter
}
}
package commands
import (
"encoding/json"
"fmt"
"io"
"time"
"github.com/fatih/color"
"github.com/wallix/awless/console"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template"
)
type logPrinter interface {
print(*template.TemplateExecution) error
}
type fullLogPrinter struct {
w io.Writer
}
func (p *fullLogPrinter) print(t *template.TemplateExecution) error {
writeMultilineLogHeader(t, p.w)
if t.Message != "" {
fmt.Fprintf(p.w, "\t%s\n\n", t.Message)
}
for _, cmd := range t.CommandNodesIterator() {
var status string
if cmd.CmdErr != nil {
status = renderRedFn("KO")
} else {
status = renderGreenFn("OK")
}
var line string
if v, ok := cmd.CmdResult.(string); ok && v != "" {
line = fmt.Sprintf(" %s\t%s\t[%s]", status, cmd.String(), v)
} else {
line = fmt.Sprintf(" %s\t%s", status, cmd.String())
}
fmt.Fprintln(p.w, line)
logger.New("", 0, p.w).MultiLineError(cmd.Err())
}
return nil
}
type statLogPrinter struct {
w io.Writer
}
func (p *statLogPrinter) print(t *template.TemplateExecution) error {
writeLogHeader(t, p.w)
if t.Message != "" {
fmt.Fprintf(p.w, "\n\t%s\n", t.Message)
}
return nil
}
type shortLogPrinter struct {
w io.Writer
}
func (p *shortLogPrinter) print(t *template.TemplateExecution) error {
writeLogHeader(t, p.w)
return nil
}
type rawJSONPrinter struct {
w io.Writer
}
func (p *rawJSONPrinter) print(t *template.TemplateExecution) error {
if err := json.NewEncoder(p.w).Encode(t); err != nil {
return fmt.Errorf("json printer: %s", err)
}
return nil
}
type idOnlyPrinter struct {
w io.Writer
}
func (p *idOnlyPrinter) print(t *template.TemplateExecution) error {
fmt.Fprint(p.w, t.ID)
return nil
}
func writeLogHeader(t *template.TemplateExecution, w io.Writer) {
stats := t.Stats()
fmt.Fprint(w, renderYellowFn(t.ID))
if stats.KOCount == 0 {
color.New(color.FgGreen).Fprint(w, " OK")
} else {
color.New(color.FgRed).Fprint(w, " KO")
}
fmt.Fprintf(w, " (%s ago)", console.HumanizeTime(t.Date()))
if t.Author != "" {
fmt.Fprintf(w, " by %s", renderBlueFn(t.Author))
}
if t.Profile != "" {
fmt.Fprintf(w, " with profile %s", renderBlueFn(t.Profile))
}
if t.Locale != "" {
fmt.Fprintf(w, " in %s", renderBlueFn(t.Locale))
}
if !template.IsRevertible(t.Template) {
fmt.Fprintf(w, " (not revertible)")
}
}
func writeMultilineLogHeader(t *template.TemplateExecution, w io.Writer) {
color.New(color.FgYellow).Fprintf(w, "id %s", t.ID)
if !template.IsRevertible(t.Template) {
fmt.Fprintln(w, " (not revertible)")
} else {
fmt.Fprintln(w)
}
fmt.Fprintf(w, "Date: %s\n", t.Date().Format(time.RFC1123Z))
if t.Author != "" {
fmt.Fprintf(w, "Author: %s\n", t.Author)
}
if t.Profile != "" {
fmt.Fprintf(w, "Profile: %s\n", t.Profile)
}
if t.Locale != "" {
fmt.Fprintf(w, "Region: %s\n", t.Locale)
}
fmt.Fprintln(w)
}
/*
Copyright 2017 WALLIX
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 commands
import (
"errors"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wallix/awless/config"
"github.com/wallix/awless/database"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template"
)
func init() {
RootCmd.AddCommand(revertCmd)
}
var revertCmd = &cobra.Command{
Use: "revert REVERTID",
Short: "Revert a template from a revert ID (see `awless log`). If deployment has changed there is no guarantee that it is still revertible.",
Example: " awless revert 01BA7RV6ES86PZYCM3H28WM6KZ",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(c *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("REVERTID required (see `awless log` to list revert ids)")
}
revertID := args[0]
var loaded *template.TemplateExecution
exitOn(database.Execute(func(db *database.DB) (terr error) {
loaded, terr = db.GetTemplate(revertID)
return
}))
if loc := loaded.Locale; loc != "" && loc != config.GetAWSRegion() {
logger.Errorf("This template was originally run in region %s", loc)
logger.Infof("Revert with `awless revert %s -r %s -p %s`", revertID, loc, loaded.Profile)
os.Exit(1)
}
if prof := loaded.Profile; prof != config.GetAWSProfile() {
logger.Warningf("This template was originally run with profile %s", prof)
}
reverted, err := loaded.Template.Revert()
exitOn(err)
tplExec := &template.TemplateExecution{
Template: reverted,
Locale: config.GetAWSRegion(),
Profile: config.GetAWSProfile(),
Source: reverted.String(),
}
tplExec.SetMessage(fmt.Sprintf("Revert %s: %s", loaded.ID, loaded.Message))
exitOn(NewRunnerRequiredParamsOnly(tplExec.Template, tplExec.Message, tplExec.Path).Run())
return nil
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"github.com/fatih/color"
"github.com/spf13/cobra"
)
var (
verboseGlobalFlag bool
extraVerboseGlobalFlag bool
silentGlobalFlag bool
localGlobalFlag bool
noSyncGlobalFlag bool
forceGlobalFlag bool
versionGlobalFlag bool
awsRegionGlobalFlag string
awsProfileGlobalFlag string
awsColorGlobalFlag string
networkMonitorFlag bool
renderGreenFn = color.New(color.FgGreen).SprintFunc()
renderRedFn = color.New(color.FgRed).SprintFunc()
renderYellowFn = color.New(color.FgYellow).SprintFunc()
renderBlueFn = color.New(color.FgBlue).SprintFunc()
renderCyanBoldFn = color.New(color.FgCyan, color.Bold).SprintFunc()
)
func init() {
RootCmd.PersistentFlags().BoolVarP(&verboseGlobalFlag, "verbose", "v", false, "Turn on verbose mode for all commands")
RootCmd.PersistentFlags().BoolVarP(&extraVerboseGlobalFlag, "extra-verbose", "e", false, "Turn on extra verbose mode (including regular verbose) for all commands")
RootCmd.PersistentFlags().BoolVar(&silentGlobalFlag, "silent", false, "Turn on silent mode for all commands: disable logging, etc...")
RootCmd.PersistentFlags().BoolVarP(&localGlobalFlag, "local", "l", false, "Work offline only using locally synced resources")
RootCmd.PersistentFlags().BoolVarP(&forceGlobalFlag, "force", "f", false, "Force the command and bypass confirmation prompts")
RootCmd.PersistentFlags().BoolVar(&noSyncGlobalFlag, "no-sync", false, "Do not run any sync on command")
RootCmd.PersistentFlags().StringVarP(&awsRegionGlobalFlag, "aws-region", "r", "", "Override AWS region temporarily for the current command")
RootCmd.PersistentFlags().SetAnnotation("aws-region", cobra.BashCompCustom, []string{"__awless_region_list"})
RootCmd.PersistentFlags().StringVarP(&awsProfileGlobalFlag, "aws-profile", "p", "", "Override AWS profile temporarily for the current command")
RootCmd.PersistentFlags().SetAnnotation("aws-profile", cobra.BashCompCustom, []string{"__awless_profile_list"})
RootCmd.PersistentFlags().StringVar(&awsColorGlobalFlag, "color", "auto", "Force enabling/disabling colors in display (auto, never, always)")
RootCmd.PersistentFlags().BoolVar(&networkMonitorFlag, "network-monitor", false, "Debug requests with network monitor")
RootCmd.PersistentFlags().MarkHidden("network-monitor")
RootCmd.Flags().BoolVar(&versionGlobalFlag, "version", false, "Print awless version")
cobra.AddTemplateFunc("IsCmdAnnotatedOneliner", IsCmdAnnotatedOneliner)
cobra.AddTemplateFunc("HasCmdOnelinerChilds", HasCmdOnelinerChilds)
RootCmd.SetUsageTemplate(customRootUsage)
cobra.OnInitialize(func() {
switch awsColorGlobalFlag {
case "never":
color.NoColor = true
case "always":
color.NoColor = false
}
})
}
var RootCmd = &cobra.Command{
Use: "awless COMMAND",
Short: "Manage and explore your cloud",
Long: `awless is a powerful CLI to explore, sync and manage your cloud infrastructure.
Supported services: EC2, IAM, S3, RDS, ELBv2, Auto Scaling, Lambda, SNS, SQS, Route53,
CloudWatch, CloudFront, ECR, ECS, ACM, CloudFormation, EKS, DynamoDB, Secrets Manager,
KMS, API Gateway v2, SSM, EFS, CloudTrail, CloudWatch Logs.
Quick examples:
awless list instances --filter state=running
awless list loggroups --format json
awless show my-resource
awless create instance type=t2.micro distro=debian
awless ssh my-instance`,
BashCompletionFunction: bash_completion_func,
RunE: func(c *cobra.Command, args []string) error {
if versionGlobalFlag {
printVersion(c, args)
return nil
}
return c.Usage()
},
}
const customRootUsage = `USAGE:{{if .Runnable}}
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if gt .Aliases 0}}
ALIASES:
{{.NameAndAliases}}
{{end}}{{if .HasExample}}
EXAMPLES:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
COMMANDS:{{range .Commands}}{{ if not (IsCmdAnnotatedOneliner .Annotations)}}{{if .IsAvailableCommand }}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if HasCmdOnelinerChilds .}}
ONE-LINER TEMPLATE COMMANDS:{{range .Commands}}{{ if IsCmdAnnotatedOneliner .Annotations}}{{if .IsAvailableCommand }}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
FLAGS:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
GLOBAL FLAGS:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
func IsCmdAnnotatedOneliner(annot map[string]string) bool {
if annot == nil {
return false
}
_, ok := annot["one-liner"]
return ok
}
func HasCmdOnelinerChilds(cmd *cobra.Command) bool {
for _, child := range cmd.Commands() {
if IsCmdAnnotatedOneliner(child.Annotations) {
return true
}
}
return false
}
const (
bash_completion_func = `
__awless_get_all_ids()
{
local all_ids_output
if all_ids_output=$(awless list infra --local --ids 2>/dev/null; awless list access --local --ids 2>/dev/null); then
COMPREPLY=( $( compgen -W "${all_ids_output[*]}" -- "$cur" ) )
fi
}
__awless_get_instances_ids()
{
local all_ids_output
if all_ids_output=$(awless list instances --local --ids 2>/dev/null); then
COMPREPLY=( $( compgen -W "${all_ids_output[*]}" -- "$cur" ) )
fi
}
__awless_get_conf_keys()
{
local all_keys_output
if all_keys_output=$(awless config list --keys 2>/dev/null); then
COMPREPLY=( $( compgen -W "${all_keys_output[*]}" -- "$cur" ) )
fi
}
__custom_func() {
case ${last_command} in
awless_ssh )
__awless_get_instances_ids
return
;;
awless_show )
__awless_get_all_ids
return
;;
awless_config_set )
__awless_get_conf_keys
return
;;
awless_config_get )
__awless_get_conf_keys
return
;;
awless_config_unset )
__awless_get_conf_keys
return
;;
awless_switch )
__awless_profile_region_list
return
;;
*)
;;
esac
}
__awless_region_list()
{
cur="${COMP_WORDS[COMP_CWORD]#*=}"
regions="us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 eu-west-1 eu-central-1 eu-west-2 eu-west-3 ap-northeast-1 ap-northeast-2 ap-southeast-1 ap-southeast-2 ap-south-1 sa-east-1"
COMPREPLY=( $(compgen -W "${regions}" -- ${cur}) )
}
__awless_profile_list()
{
cur="${COMP_WORDS[COMP_CWORD]#*=}"
profiles="$((egrep '^\[ *[a-zA-Z0-9_-]+ *\]$' ~/.aws/credentials 2>/dev/null; grep '\[profile' ~/.aws/config 2>/dev/null | sed 's|\[profile ||g') | tr -d '[]' | sort | uniq)"
COMPREPLY=( $(compgen -W "${profiles}" -- ${cur}) )
}
__awless_profile_region_list()
{
cur="${COMP_WORDS[COMP_CWORD]#*=}"
regions="us-east-1 us-east-2 us-west-1 us-west-2 ca-central-1 eu-west-1 eu-central-1 eu-west-2 eu-west-3 ap-northeast-1 ap-northeast-2 ap-southeast-1 ap-southeast-2 ap-south-1 sa-east-1"
profiles="$((egrep '^\[ *[a-zA-Z0-9_-]+ *\]$' ~/.aws/credentials 2>/dev/null; grep '\[profile' ~/.aws/config 2>/dev/null | sed 's|\[profile ||g') | tr -d '[]' | sort | uniq)"
COMPREPLY=( $(compgen -W "${profiles} ${regions}" -- ${cur}) )
}
`
)
/*
Copyright 2017 WALLIX
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
uimitations under the License.
*/
package commands
import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
stdsync "sync"
"text/tabwriter"
"time"
"github.com/chzyer/readline"
"github.com/spf13/cobra"
"github.com/wallix/awless-scheduler/client"
awsdoc "github.com/wallix/awless/aws/doc"
awsservices "github.com/wallix/awless/aws/services"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/match"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/config"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
"github.com/wallix/awless/template"
"github.com/wallix/awless/template/params"
)
var (
scheduleRunInFlag string
scheduleRevertInFlag string
runLogMessage string
listRemoteTemplatesFlag bool
noSuggestedParamsFlag bool
allSuggestedParamsFlag bool
)
func init() {
RootCmd.AddCommand(runCmd)
runCmd.Flags().BoolVar(&listRemoteTemplatesFlag, "list", false, "List templates available at https://github.com/wallix/awless-templates")
runCmd.Flags().StringVar(&scheduleRunInFlag, "run-in", "", "Postpone the execution of this template")
runCmd.Flags().StringVar(&scheduleRevertInFlag, "revert-in", "", "Schedule the revertion of this template")
runCmd.Flags().StringVarP(&runLogMessage, "message", "m", "", "Add a message for this template execution to be persisted in your logs")
var actions []string
for a := range awsspec.DriverSupportedActions {
actions = append(actions, a)
}
sort.Strings(actions)
for _, action := range actions {
entities := awsspec.DriverSupportedActions[action]
sort.Strings(entities)
cmd := createDriverCommands(action, entities)
cmd.PersistentFlags().StringVar(&scheduleRunInFlag, "run-in", "", "Postpone the execution of this command")
cmd.PersistentFlags().StringVar(&scheduleRevertInFlag, "revert-in", "", "Schedule the revertion of this command")
RootCmd.AddCommand(cmd)
}
}
const maxMsgLen = 140
var runCmd = &cobra.Command{
Use: "run PATH",
Short: "Run a template given a filepath or URL",
Example: " awless run ~/templates/my-infra.aws\n awless run https://raw.githubusercontent.com/wallix/awless-templates/master/create_vpc.aws\n awless run repo:create_vpc",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
if listRemoteTemplatesFlag {
exitOn(listRemoteTemplates())
return nil
}
if len(args) < 1 {
return errors.New("missing PATH arg (filepath or url)")
}
if len(runLogMessage) > maxMsgLen {
exitOn(fmt.Errorf("message to be persisted should not exceed %d characters", maxMsgLen))
}
content, fullPath, err := getTemplateText(args[0])
exitOn(err)
logger.Verbosef("Loaded template text:\n\n%s\n", removeComments(content))
templ, err := template.Parse(string(content))
exitOn(err)
extraParams, err := template.ParseParams(strings.Join(args[1:], " "))
exitOn(err)
tplExec := &template.TemplateExecution{
Template: templ,
Path: fullPath,
Message: strings.TrimSpace(runLogMessage),
}
exitOn(NewRunnerRequiredParamsOnly(tplExec.Template, tplExec.Message, tplExec.Path, config.Defaults, extraParams).Run())
return nil
},
}
func missingHolesStdinFunc() func(string, []string, bool) string {
var count int
return func(hole string, paramPaths []string, optional bool) (response string) {
if count < 1 {
fmt.Println("Please specify " + "(" + renderYellowFn("TAB") + " for completion, " + renderYellowFn("','+TAB") + " for list completion, " + renderYellowFn("Enter") + " to skip optionals, " + renderYellowFn("Ctrl+C") + " to quit) :")
}
var docs, enums []string
var typedParam *awsdoc.ParamType
for _, param := range paramPaths {
splits := strings.Split(param, ".")
if len(splits) != 3 {
continue
}
if doc, hasDoc := awsdoc.TemplateParamsDoc(splits[0], splits[1], splits[2]); hasDoc {
docs = append(docs, doc)
}
if enum, hasEnum := awsdoc.EnumDoc[param]; hasEnum {
enums = append(enums, enum...)
}
if tparam, has := awsdoc.ParamTypeDoc[param]; has {
typedParam = tparam
}
}
if len(docs) > 0 {
fmt.Fprintln(os.Stderr, strings.Join(docs, "; ")+":")
}
autocomplete := holeAutoCompletion(allGraphsOnce.mustLoad(), paramPaths)
if typedParam != nil {
autocomplete = typedParamCompletionFunc(allGraphsOnce.mustLoad(), typedParam.ResourceType, typedParam.PropertyName)
}
if len(enums) > 0 {
autocomplete = enumCompletionFunc(enums)
}
var promptSuffix string
if optional {
promptSuffix = " (optional)"
}
var err error
for response, err = askHole(hole, promptSuffix, autocomplete); err != nil; response, err = askHole(hole, promptSuffix, autocomplete) {
if optional {
return ""
}
logger.Error(err)
}
count++
return
}
}
func askHole(hole, promptSuffix string, autocomplete readline.AutoCompleter) (string, error) {
l, err := readline.NewEx(&readline.Config{
Prompt: renderCyanBoldFn(hole+"?") + renderYellowFn(promptSuffix) + " ",
AutoComplete: autocomplete,
InterruptPrompt: "^C",
EOFPrompt: "exit",
})
if err != nil {
exitOn(err)
}
defer l.Close()
for {
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
os.Exit(0)
} else {
continue
}
} else if err == io.EOF {
break
}
line = strings.TrimSpace(line)
if line != "" {
return line, nil
}
return "", errors.New("required value")
}
return "", errors.New("required value")
}
type onceLoader struct {
g cloud.GraphAPI
err error
once stdsync.Once
}
func (l *onceLoader) mustLoad() cloud.GraphAPI {
l.once.Do(func() {
l.g, l.err = sync.LoadLocalGraphs(config.GetAWSProfile(), config.GetAWSRegion())
})
exitOn(l.err)
return l.g
}
var allGraphsOnce = &onceLoader{}
func createDriverCommands(action string, entities []string) *cobra.Command {
actionCmd := &cobra.Command{
Use: fmt.Sprintf("%s ENTITY [param=value ...]", action),
Short: oneLinerShortDesc(action, entities),
Long: fmt.Sprintf("Allow to %s: %v", action, strings.Join(entities, ", ")),
Annotations: map[string]string{"one-liner": "true"},
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("missing ENTITY")
}
invalidEntityErr := fmt.Errorf("invalid entity '%s'", args[0])
_, resources, matchingProperty := resolveResourceFromRefInCurrentRegion(args[0])
if len(resources) != 1 {
return invalidEntityErr
}
templDef, ok := awsspec.AWSLookupDefinitions(fmt.Sprintf("%s%s", action, resources[0].Type()))
if !ok {
return invalidEntityErr
}
templ, err := suggestFixParsingError(templDef, args, matchingProperty, invalidEntityErr)
exitOn(err)
tplExec := &template.TemplateExecution{
Template: templ,
}
exitOn(NewRunner(tplExec.Template, tplExec.Message, tplExec.Path).Run())
return nil
},
}
for _, entity := range entities {
templDef, ok := awsspec.AWSLookupDefinitions(fmt.Sprintf("%s%s", action, entity))
if !ok {
exitOn(errors.New("command unsupported on inline mode"))
}
run := func(def awsspec.Definition) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
text := fmt.Sprintf("%s %s %s", def.Action, def.Entity, strings.Join(args, " "))
templ, err := template.Parse(text)
if err != nil {
_, resources, matchingProperty := resolveResourceFromRefInCurrentRegion(args[0])
if len(resources) != 1 {
exitOn(err)
}
templ, err = suggestFixParsingError(def, args, matchingProperty, err)
exitOn(err)
}
tplExec := &template.TemplateExecution{
Template: templ,
}
exitOn(NewRunner(tplExec.Template, tplExec.Message, tplExec.Path, config.Defaults).Run())
return nil
}
}
var apiStr string
if api, ok := awsspec.APIPerTemplateDefName[templDef.Action+templDef.Entity]; ok {
apiStr = fmt.Sprint(strings.ToUpper(api) + " ")
}
var paramsStr bytes.Buffer
allParams, optParams, _ := params.List(templDef.Params)
tab := tabwriter.NewWriter(¶msStr, 0, 0, 3, '.', 0)
for _, p := range allParams {
fmt.Fprintf(tab, " %s\t", p)
if d, ok := awsdoc.TemplateParamsDocWithEnums(templDef.Action, templDef.Entity, p); ok {
fmt.Fprintf(tab, " %s", d)
}
fmt.Fprintln(tab)
}
for _, p := range optParams {
fmt.Fprintf(tab, " [%s]\t", p)
if d, ok := awsdoc.TemplateParamsDocWithEnums(templDef.Action, templDef.Entity, p); ok {
fmt.Fprintf(tab, " %s", d)
}
fmt.Fprintln(tab)
}
tab.Flush()
var validArgs []string
for _, param := range append(allParams, optParams...) {
validArgs = append(validArgs, param+"=")
}
currentCmd := &cobra.Command{
Use: fmt.Sprintf("%s [param=value ...]", templDef.Entity),
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
Short: awsdoc.AwlessCommandDefinitionsDoc(action, templDef.Entity, fmt.Sprintf("%s a %s%s", strings.Title(action), apiStr, templDef.Entity)),
Long: fmt.Sprintf("PARAMS:\n%s\nPARAMS PATTERNS:\n %s\n\nSEE ALSO:\n%s", paramsStr.String(), templDef.Params, availableActionsForEntity(templDef.Entity)),
Example: awsdoc.AwlessExamplesDoc(action, templDef.Entity),
RunE: run(templDef),
ValidArgs: validArgs,
}
currentCmd.SetUsageTemplate(customCommandUsageTemplate)
currentCmd.SetHelpTemplate(`{{with .Short}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`)
currentCmd.Flags().BoolVar(&noSuggestedParamsFlag, "prompt-only-required", false, "Prompt only required parameters")
currentCmd.Flags().BoolVarP(&allSuggestedParamsFlag, "prompt-all", "a", false, "Prompt all non-provided parameters")
actionCmd.AddCommand(currentCmd)
}
return actionCmd
}
func runSyncFor(tplExec *template.TemplateExecution) {
if !config.GetAutosync() {
return
}
if tplExec.Stats().AllKO() {
return
}
apis := tplExec.Template.UniqueDefinitions(awsspec.APIPerTemplateDefName)
services := awsservices.GetCloudServicesForAPIs(apis...)
if !noSyncGlobalFlag {
go func() { // allow to only display this verbose line only if taking more than 1 second before exiting CLI
time.Sleep(2 * time.Second)
logger.Infof("Resyncing %s ... (disable with --no-sync global flag)", joinSentence(cloud.Services(services).Names()))
}()
}
if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
logger.ExtraVerbosef("%s", err.Error())
}
}
func resolveAliasFunc(paramPath, alias string) string {
splits := strings.Split(paramPath, ".")
if len(splits) != 3 {
logger.Errorf("resolve alias: invalid param path: %s", paramPath)
return ""
}
entity, key := splits[1], splits[2]
var typedParam *awsdoc.ParamType
if tparam, has := awsdoc.ParamTypeDoc[paramPath]; has {
typedParam = tparam
}
gph, err := sync.LoadLocalGraphs(config.GetAWSProfile(), config.GetAWSRegion())
if err != nil {
fmt.Printf("resolve alias '%s': cannot load local graphs for region %s: %s\n", alias, config.GetAWSRegion(), err)
return ""
}
resType := key
if typedParam != nil {
resType = typedParam.ResourceType
} else {
if strings.Contains(key, "id") {
resType = entity
}
}
resources, err := gph.Find(cloud.NewQuery(resType).Match(match.And(match.Property("Name", alias))))
if err != nil {
return ""
}
var matchingResource cloud.Resource
switch len(resources) {
case 1:
matchingResource = resources[0]
default:
resources, err := gph.FindWithProperties(map[string]interface{}{"Name": alias})
if err != nil {
return ""
}
if len(resources) > 0 {
matchingResource = resources[0]
}
}
if matchingResource == nil {
return ""
}
if typedParam != nil {
if prop, ok := matchingResource.Properties()[typedParam.PropertyName].(string); ok {
return prop
}
}
return matchingResource.Id()
}
func availableActionsForEntity(entity string) string {
var out []string
for actionentity := range awsspec.APIPerTemplateDefName {
if strings.HasSuffix(actionentity, entity) {
index := strings.Index(actionentity, entity)
out = append(out, fmt.Sprintf(" %s %s", actionentity[:index], actionentity[index:]))
}
}
if len(out) > 0 {
sort.Strings(out)
return strings.Join(out, "\n")
}
return "none"
}
func oneLinerShortDesc(action string, entities []string) string {
if len(entities) > 5 {
return fmt.Sprintf("%s, \u2026 (see `awless %s -h` for more)", strings.Join(entities[0:5], ", "), action)
} else {
return strings.Join(entities, ", ")
}
}
const (
DEFAULT_REPO_PREFIX = "https://raw.githubusercontent.com/wallix/awless-templates/master"
FILE_EXT = ".aws"
)
type templateMetadata struct {
Title, Name, MinimalVersion string
Tags []string
}
func getTemplateText(path string) (content []byte, expanded string, err error) {
if strings.HasPrefix(path, "repo:") {
path = fmt.Sprintf("%s/%s", DEFAULT_REPO_PREFIX, strings.TrimPrefix(path[5:], "/"))
path = fmt.Sprintf("%s%s", strings.TrimSuffix(path, FILE_EXT), FILE_EXT)
}
expanded = path
if strings.HasPrefix(path, "http") {
logger.ExtraVerbosef("fetching remote template at '%s'", path)
content, err = readHttpContent(path)
} else {
f, ferr := os.Open(path)
if ferr != nil {
return nil, "", ferr
}
defer f.Close()
var perr error
expanded, perr = filepath.Abs(f.Name())
if perr != nil {
expanded = path
}
content, err = io.ReadAll(f)
}
if err != nil {
return content, expanded, err
}
requiredVersion, ok := detectMinimalVersionInTemplate(content)
if ok {
comp, _ := config.CompareSemver(requiredVersion, config.Version)
if comp > 0 {
return content, expanded, fmt.Errorf("This template has metadata indicating to be parsed with at least awless version %s. Your current version is %s", requiredVersion, config.Version)
}
}
return content, expanded, nil
}
var commentRegex = regexp.MustCompile(`^\s*#`)
func removeComments(b []byte) []byte {
scn := bufio.NewScanner(bytes.NewReader(b))
var cleaned bytes.Buffer
for scn.Scan() {
line := scn.Text()
if commentRegex.MatchString(line) {
continue
}
cleaned.WriteString(line)
cleaned.WriteByte('\n')
}
return cleaned.Bytes()
}
var (
minimalVersionRegex = regexp.MustCompile(`^# *MinimalVersion: *(v?\d{1,3}\.\d{1,3}\.\d{1,3}).*$`)
)
func detectMinimalVersionInTemplate(content []byte) (string, bool) {
scn := bufio.NewScanner(bytes.NewReader(content))
for scn.Scan() {
matches := minimalVersionRegex.FindStringSubmatch(scn.Text())
if len(matches) > 1 {
logger.ExtraVerbosef("detected minimal version %s in templates", matches[1])
return matches[1], true
}
}
return "", false
}
func listRemoteTemplates() error {
manifestFile, err := readHttpContent(DEFAULT_REPO_PREFIX + "/manifest.json")
if err != nil {
return err
}
var remoteTemplates []*templateMetadata
if err = json.Unmarshal(manifestFile, &remoteTemplates); err != nil {
return err
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "Title\tTags\tRun it with")
fmt.Fprintln(w, "-----\t----\t-----------")
for _, tpl := range remoteTemplates {
if tpl.MinimalVersion == "" {
tpl.MinimalVersion = config.Version
}
if comp, err := config.CompareSemver(tpl.MinimalVersion, config.Version); comp < 1 && err == nil {
fmt.Fprintf(w, "%s\t%s\tawless run repo:%s -v\n", tpl.Title, strings.Join(tpl.Tags, ","), tpl.Name)
}
}
w.Flush()
return nil
}
func readHttpContent(path string) ([]byte, error) {
resp, err := http.Get(path)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("'%s' when fetching '%s'", resp.Status, path)
}
return io.ReadAll(resp.Body)
}
func isCSV(s string) bool {
if strings.HasPrefix(s, "[") {
if !strings.HasSuffix(s, "]") {
return false
}
s = s[1 : len(s)-1]
}
for _, split := range strings.Split(s, ",") {
if !template.MatchStringParamValue(split) {
return false
}
}
return true
}
func scheduleTemplate(t *template.Template, runIn, revertIn string) error {
schedClient, err := client.New(config.GetSchedulerURL())
if err != nil {
return fmt.Errorf("cannot connect to scheduler: %s", err)
}
logger.Verbosef("sending template to scheduler %s", schedClient.ServiceURL)
if err := schedClient.Post(client.Form{
Region: config.GetAWSRegion(),
RunIn: runIn,
RevertIn: revertIn,
Template: t.String(),
}); err != nil {
return fmt.Errorf("cannot schedule template: %s", err)
}
logger.Info("template scheduled successfully")
return nil
}
func suggestFixParsingError(def awsspec.Definition, args []string, matchingProperty string, defaultErr error) (*template.Template, error) {
if len(def.Params.Required()) != 1 || len(args) != 1 {
return nil, defaultErr
}
propKey := def.Params.Required()[0]
propValue := args[0]
if matchingProperty == properties.Name && !strings.HasPrefix(propValue, "@") && !strings.HasSuffix(propKey, "name") {
propValue = "@" + propValue
}
suggestText := fmt.Sprintf("%s %s %s=%s", def.Action, def.Entity, propKey, propValue)
if !promptConfirmDefaultYes("Did you mean `awless %s` ? ", suggestText) {
return nil, defaultErr
}
templ, err := template.Parse(suggestText)
if err != nil {
return templ, err
}
return templ, nil
}
func isSchedulingMode() bool {
runin := strings.TrimSpace(scheduleRunInFlag)
revertin := strings.TrimSpace(scheduleRevertInFlag)
if runin != "" || revertin != "" {
return true
}
return false
}
func joinSentence(arr []string) string {
sep := ", "
if ln := len(arr); ln > 1 {
return fmt.Sprintf("%s and %s", strings.Join(arr[:ln-1], sep), arr[ln-1])
}
return strings.Join(arr, sep)
}
const customCommandUsageTemplate = `
USAGE:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
ALIASES:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
EXAMPLES:
{{.Example}}{{end}}
{{.Long}}{{if .HasAvailableSubCommands}}
AVAILABLE COMMANDS:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
FLAGS:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
GLOBAL FLAGS:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
func promptConfirmDefaultYes(msg string, a ...interface{}) bool {
var yesorno string
fmt.Fprintf(os.Stderr, "%s [Y/n] ", fmt.Sprintf(msg, a...))
fmt.Scanln(&yesorno)
if y := strings.TrimSpace(strings.ToLower(yesorno)); y == "y" || y == "yes" || y == "" {
return true
}
return false
}
package commands
import (
"fmt"
"os"
"strings"
awsservices "github.com/wallix/awless/aws/services"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/database"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
"github.com/wallix/awless/template"
"github.com/wallix/awless/template/env"
)
func NewRunnerRequiredParamsOnly(tpl *template.Template, msg, tplPath string, fillers ...map[string]interface{}) *template.Runner {
r := NewRunner(tpl, msg, tplPath, fillers...)
r.ParamsSuggested = env.REQUIRED_PARAMS_ONLY
return r
}
func NewRunner(tpl *template.Template, msg, tplPath string, fillers ...map[string]interface{}) *template.Runner {
runner := &template.Runner{}
runner.Template = tpl
runner.Locale = config.GetAWSRegion()
runner.Profile = config.GetAWSProfile()
runner.Log = logger.DefaultLogger
runner.Message = msg
runner.TemplatePath = tplPath
runner.Fillers = fillers
runner.AliasFunc = resolveAliasFunc
runner.MissingHolesFunc = missingHolesStdinFunc()
if allSuggestedParamsFlag {
runner.ParamsSuggested = env.ALL_PARAMS
}
if noSuggestedParamsFlag {
runner.ParamsSuggested = env.REQUIRED_PARAMS_ONLY
}
runner.Validators = []template.Validator{
&template.UniqueNameValidator{LookupGraph: func(key string) (cloud.GraphAPI, bool) {
g := sync.LoadLocalGraphForService(awsservices.ServicePerResourceType[key], config.GetAWSProfile(), config.GetAWSRegion())
return g, true
}},
&template.ParamIsSetValidator{Action: "create", Entity: "instance", Param: "keypair", WarningMessage: "This instance has no access keypair. You might not be able to connect to it. Use `awless create instance keypair=my-keypair ...`"},
}
runner.CmdLookuper = func(tokens ...string) interface{} {
newCommandFunc := awsspec.CommandFactory.Build(strings.Join(tokens, ""))
if newCommandFunc == nil {
return nil
}
return newCommandFunc()
}
runner.BeforeRun = func(tplExec *template.TemplateExecution) (bool, error) {
var yesorno string
if forceGlobalFlag {
yesorno = "y"
} else {
fmt.Printf("%s\n\n", renderGreenFn(tplExec.Template))
if isSchedulingMode() {
fmt.Printf("Confirm scheduling (region: %s)? [y/N] ", config.GetAWSRegion())
} else {
fmt.Printf("Confirm (region: %s)? [y/N] ", config.GetAWSRegion())
}
if _, err := fmt.Scanln(&yesorno); err != nil && err.Error() != "unexpected newline" {
return false, err
}
}
if strings.TrimSpace(strings.ToLower(yesorno)) == "y" {
me, err := awsservices.AccessService.(*awsservices.Access).GetIdentity()
if err != nil {
logger.Warningf("cannot resolve template author identity: %s", err)
} else {
tplExec.Author = me.ResourcePath
logger.ExtraVerbosef("resolved template author: %s", tplExec.Author)
}
if isSchedulingMode() {
return false, scheduleTemplate(tplExec.Template, scheduleRunInFlag, scheduleRevertInFlag)
}
return true, nil
}
os.Exit(1)
return false, nil
}
runner.AfterRun = func(tplExec *template.TemplateExecution) error {
if tplExec.Message == "" {
if tplExec.IsOneLiner() {
tplExec.SetMessage(fmt.Sprintf("Run %s", tplExec.Template))
} else if path := tplExec.Path; path != "" {
stats := tplExec.Stats()
if stats.KOCount > 0 {
tplExec.SetMessage(fmt.Sprintf("Run %d/%d commands from %s", stats.OKCount, stats.CmdCount, path))
} else {
tplExec.SetMessage(fmt.Sprintf("Run %d commands from %s", stats.OKCount, path))
}
}
}
if err := database.Execute(func(db *database.DB) error {
return db.AddTemplate(tplExec)
}); err != nil {
logger.Errorf("Cannot save executed template in awless logs: %s", err)
}
if template.IsRevertible(tplExec.Template) {
fmt.Println()
logger.Infof("Revert this template with `awless revert %s`", tplExec.Template.ID)
}
runSyncFor(tplExec)
return nil
}
return runner
}
/*
Copyright 2017 WALLIX
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 commands
import (
"bytes"
"errors"
"fmt"
"github.com/spf13/cobra"
"github.com/wallix/awless-scheduler/client"
"github.com/wallix/awless-scheduler/model"
"github.com/wallix/awless/config"
)
var (
listSchedulerTasksFlag bool
listSchedulerFailuresFlag bool
)
func init() {
RootCmd.AddCommand(schedulerCmd)
schedulerCmd.Flags().BoolVar(&listSchedulerTasksFlag, "list-tasks", false, "List scheduler tasks")
schedulerCmd.Flags().BoolVar(&listSchedulerFailuresFlag, "list-failures", false, "List scheduler failures")
}
var schedulerCmd = &cobra.Command{
Use: "scheduler",
PersistentPreRun: applyHooks(initAwlessEnvHook, initLoggerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade),
Hidden: true,
Short: "Accessing the scheduler API (when installed). To schedule templates runs/reverts use `awless run --schedule`",
Run: func(cmd *cobra.Command, args []string) {
if config.GetSchedulerURL() == "" {
exitOn(errors.New("no scheduler URL in configuration. Set it with `awless config set scheduler.url`"))
}
cli, err := client.New(config.GetSchedulerURL())
exitOn(err)
if listSchedulerTasksFlag {
tasks, err := cli.ListTasks()
exitOn(err)
printTasks(tasks)
return
}
if listSchedulerFailuresFlag {
tasks, err := cli.ListFailures()
exitOn(err)
printTasks(tasks)
return
}
info := cli.ServiceInfo()
fmt.Printf("Scheduler up!\nAddress: '%s'\nTickerFrequency: %s\nUptime: %s\n", info.ServiceAddr, info.TickerFrequency, info.Uptime)
},
}
func printTasks(tasks []*model.Task) {
for _, t := range tasks {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("Region: %s", t.Region))
buf.WriteString(fmt.Sprintf(", RunAt: %s", t.RunAt))
if !t.RevertAt.IsZero() {
buf.WriteString(fmt.Sprintf(", RevertAt: %s", t.RevertAt))
}
buf.WriteString(fmt.Sprintf("\nContent: %s\n", t.Content))
fmt.Println(buf.String())
}
}
/*
Copyright 2017 WALLIX
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 commands
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/config"
"github.com/wallix/awless/logger"
)
var (
showIdsOnlyFlag, showIdOnlyFlag, showLatestIdOnly bool
)
func init() {
RootCmd.AddCommand(searchCmd)
awsImagesCmd.Flags().BoolVar(&showLatestIdOnly, "latest-id", false, "Returns the id only of the latest AMI matching your query")
awsImagesCmd.Flags().BoolVar(&showIdOnlyFlag, "id-only", false, "(DEPRECATED, use latest-id) Returns only one (the latest) AMI id matching the query")
searchCmd.AddCommand(awsImagesCmd)
}
var searchCmd = &cobra.Command{
Hidden: true,
Use: "search",
Short: "Perform various searches and resolution",
}
var awsImagesCmd = &cobra.Command{
Use: "images",
PersistentPreRun: applyHooks(initAwlessEnvHook, initLoggerHook, initCloudServicesHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(networkMonitorHook),
Short: fmt.Sprintf("Resolve from current region the official community AMIs according to an awless specific bare distro query format, ordering by latest first. Supported owners: %s", strings.Join(awsspec.SupportedAMIOwners, ", ")),
Long: fmt.Sprintf("Resolve from current region the official community AMIs according to an awless specific bare distro query format, ordering by latest first.\n\nQuery string specification is the following column separated format:\n\n\t\t%s\n\nEverything optional expect for the 'owner'. Supported owners: %s", awsspec.ImageQuerySpec, strings.Join(awsspec.SupportedAMIOwners, ", ")),
Example: ` awless search images redhat:rhel:7.2
awless search images debian::jessie
awless search images canonical --latest-id
awless search images coreos
awless search images amazonlinux:amzn2
awless search images amazonlinux:::::instance-store`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
exitOn(fmt.Errorf("expecting image query string. Expecting: %s (with everything optional expect for the owner)", awsspec.ImageQuerySpec))
}
resolver := awsspec.EC2ImageResolver()
query, err := awsspec.ParseImageQuery(args[0])
exitOn(err)
logger.Infof("launching search for image in '%s' region. Query: '%s'", config.GetAWSRegion(), query)
imgs, _, err := resolver.Resolve(query)
exitOn(err)
var ids []string
for _, img := range imgs {
ids = append(ids, img.Id)
}
if showIdsOnlyFlag {
for _, id := range ids {
fmt.Println(id)
}
return
}
if showLatestIdOnly || showIdOnlyFlag {
for i, id := range ids {
fmt.Println(id)
if i == 0 {
break
}
}
return
}
b, err := json.MarshalIndent(imgs, "", " ")
exitOn(err)
fmt.Fprintln(os.Stdout, string(b))
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"bytes"
"errors"
"fmt"
"os"
"sort"
"strings"
"github.com/fatih/color"
"github.com/spf13/cobra"
awsconfig "github.com/wallix/awless/aws/config"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/config"
"github.com/wallix/awless/console"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
var (
listAllSiblingsFlag bool
noAliasFlag bool
showPropertiesValuesOnlyFlag []string
)
func init() {
RootCmd.AddCommand(showCmd)
showCmd.Flags().BoolVar(&listAllSiblingsFlag, "siblings", false, "List all the resource's siblings")
showCmd.Flags().BoolVar(&noAliasFlag, "no-alias", false, "Disable the resolution of ID to alias")
showCmd.Flags().StringSliceVar(&showPropertiesValuesOnlyFlag, "values-for", []string{}, "Output values only for given properties keys")
}
var showCmd = &cobra.Command{
Use: "show REFERENCE",
Short: "Show resources lineage and dependencies given a REFERENCE: name, id, arn, etc...",
Example: ` awless show i-8d43b21b # show an instance via its ref
awless show AIDAJ3Z24GOKHTZO4OIX6 # show a user via its ref
awless show jsmith # show a user via its ref
awless show @jsmith # forcing search by name
awless show /aws/lambda/my-func # show a log group by name
awless show my-cluster # show an EKS cluster
awless show my-table # show a DynamoDB table`,
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("REFERENCE required. See examples.")
}
ref := args[0]
notFound := fmt.Errorf("resource '%s' not found", deprefix(ref))
if _, err := awsconfig.ParseRegion(ref); err == nil && ref != config.GetAWSRegion() {
logger.Errorf("Cannot show region '%s' as you are in region '%s'", ref, config.GetAWSRegion())
logger.Infof("Use `awless show %s -r %s`", ref, ref)
os.Exit(1)
}
var resource cloud.Resource
var gph cloud.GraphAPI
resource, gph = findResourceInLocalGraphs(ref)
if resource == nil && localGlobalFlag {
exitOn(decorateWithSuggestion(notFound, ref))
} else if resource == nil {
runFullSync()
if resource, gph = findResourceInLocalGraphs(ref); resource == nil {
exitOn(decorateWithSuggestion(notFound, ref))
}
}
if !localGlobalFlag && config.GetAutosync() {
var services []cloud.Service
if resource.Type() == cloud.Region {
services = append(services, cloud.AllServices()...)
} else {
srv, err := cloud.GetServiceForType(resource.Type())
exitOn(err)
services = append(services, srv)
}
logger.Verbosef("syncing services for %s type", resource.Type())
if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
logger.Verbose(err)
}
resource, gph = findResourceInLocalGraphs(ref)
}
if resource != nil {
if len(showPropertiesValuesOnlyFlag) > 0 {
showResourceValuesOnlyFor(resource, showPropertiesValuesOnlyFlag)
} else {
showResource(resource, gph)
}
}
return nil
},
}
func showResourceValuesOnlyFor(resource cloud.Resource, propKeys []string) {
var normalized []string
for _, p := range propKeys {
normalized = append(normalized, strings.ToLower(strings.Replace(p, " ", "", -1)))
}
valuesForKeys := map[string]string{}
isIncluded := func(s string) (bool, string) {
for _, n := range normalized {
if n == strings.ToLower(s) {
return true, n
}
}
return false, ""
}
for k, v := range resource.Properties() {
if ok, p := isIncluded(k); ok {
valuesForKeys[p] = fmt.Sprint(v)
}
}
var values []string
for _, n := range normalized {
if v, ok := valuesForKeys[n]; ok {
values = append(values, v)
}
}
if len(values) > 0 {
fmt.Println(strings.Join(values, ","))
} else {
exitOn(fmt.Errorf("no values for %q", propKeys))
}
}
func showResource(resource cloud.Resource, gph cloud.GraphAPI) {
displayer, err := console.BuildOptions(
console.WithColumnDefinitions(console.DefaultsColumnDefinitions[resource.Type()]),
console.WithFormat(listingFormat),
console.WithMaxWidth(console.GetTerminalWidth()),
).SetSource(resource).Build()
exitOn(err)
exitOn(displayer.Print(os.Stdout))
parents, err := gph.ResourceRelations(resource, rdf.ParentOf, true)
exitOn(err)
var parentsW bytes.Buffer
var count int
for i := len(parents) - 1; i >= 0; i-- {
if count == 0 {
fmt.Fprintf(&parentsW, "%s\n", printResourceRef(parents[i]))
} else {
fmt.Fprintf(&parentsW, "%s↳ %s\n", strings.Repeat("\t", count), printResourceRef(parents[i]))
}
count++
}
var childrenW bytes.Buffer
var hasChildren bool
printWithTabs := func(r cloud.Resource, distance int) error {
var tabs bytes.Buffer
tabs.WriteString(strings.Repeat("\t", count))
for i := 0; i < distance; i++ {
tabs.WriteByte('\t')
}
display := printResourceRef(r)
if r.Same(resource) {
display = printResourceRef(resource, renderGreenFn)
} else {
hasChildren = true
}
fmt.Fprintf(&childrenW, "%s↳ %s\n", tabs.String(), display)
return nil
}
err = gph.VisitRelations(resource, rdf.ChildrenOfRel, true, printWithTabs)
exitOn(err)
if len(parents) > 0 || hasChildren {
fmt.Println(renderCyanBoldFn("\nLineage:"))
fmt.Printf("%s", parentsW.String())
fmt.Printf("%s", childrenW.String())
}
appliedOn, err := gph.ResourceRelations(resource, rdf.ApplyOn, false)
exitOn(err)
printResourceList(renderCyanBoldFn("Applied on"), appliedOn)
dependingOn, err := gph.ResourceRelations(resource, rdf.DependingOnRel, false)
exitOn(err)
printResourceList(renderCyanBoldFn("Depending on"), dependingOn)
siblings, err := gph.ResourceSiblings(resource)
exitOn(err)
printResourceList(renderCyanBoldFn("Siblings"), siblings, "display all with flag --siblings")
}
func runFullSync() {
if !config.GetAutosync() {
logger.Info("autosync disabled")
return
}
logger.Infof("cannot find resource in existing data synced locally")
logger.Infof("running sync for region '%s' and profile '%s'", config.GetAWSRegion(), config.GetAWSProfile())
var services []cloud.Service
for _, srv := range cloud.ServiceRegistry {
services = append(services, srv)
}
if _, err := sync.DefaultSyncer.Sync(services...); err != nil {
logger.Verbose(err)
}
}
func findResourceInLocalGraphs(ref string) (cloud.Resource, cloud.GraphAPI) {
g, resources, _ := resolveResourceFromRefInCurrentRegion(ref)
switch len(resources) {
case 0:
return nil, nil
case 1:
return resources[0], g
default:
logger.Infof("%d resources found with name '%s' in region '%s' for profile '%s'. Show a specific resource with:", len(resources), deprefix(ref), config.GetAWSRegion(), config.GetAWSProfile())
for _, res := range resources {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("\t`awless show %s` to show the %s", res.Id(), res.Type()))
if state, ok := res.Properties()[properties.State].(string); ok {
buf.WriteString(fmt.Sprintf(" (state: '%s')", state))
}
logger.Infof("%s", buf.String())
}
os.Exit(0)
}
return nil, nil
}
func resolveResourceFromRefInCurrentRegion(ref string) (cloud.GraphAPI, []cloud.Resource, string) {
g, err := sync.LoadLocalGraphs(config.GetAWSProfile(), config.GetAWSRegion())
exitOn(err)
return resolveResourceFromRef(g, ref)
}
func resolveResourceFromRefInAllLocalRegion(ref string) (cloud.GraphAPI, []cloud.Resource, string) {
g, err := sync.LoadAllLocalGraphs(config.GetAWSProfile())
exitOn(err)
return resolveResourceFromRef(g, ref)
}
func resolveResourceFromRef(g cloud.GraphAPI, ref string) (cloud.GraphAPI, []cloud.Resource, string) {
name := deprefix(ref)
if strings.HasPrefix(ref, "@") {
logger.Verbosef("prefixed with @: forcing research by name '%s'", name)
rs, err := g.FindWithProperties(map[string]interface{}{properties.Name: name})
exitOn(err)
return g, rs, properties.Name
}
rs, err := g.FindWithProperties(map[string]interface{}{properties.ID: name})
exitOn(err)
if len(rs) > 0 {
return g, rs, properties.ID
}
rs, err = g.FindWithProperties(map[string]interface{}{properties.Arn: name})
exitOn(err)
if len(rs) > 0 {
return g, rs, properties.Arn
}
rs, err = g.FindWithProperties(map[string]interface{}{properties.Name: name})
exitOn(err)
return g, rs, properties.Name
}
func deprefix(s string) string {
return strings.TrimPrefix(s, "@")
}
func decorateWithSuggestion(err error, ref string) error {
buf := bytes.NewBufferString(fmt.Sprintf("%s in region '%s' for profile '%s'", err.Error(), config.GetAWSRegion(), config.GetAWSProfile()))
g, resources, _ := resolveResourceFromRefInAllLocalRegion(ref)
for _, res := range resources {
parents, err := g.ResourceRelations(res, rdf.ParentOf, true)
if err != nil {
return err
}
for _, parent := range parents {
if parent.Type() == cloud.Region {
buf.WriteString(fmt.Sprintf("\n\tfound previously synced under region '%s' as %s. Show it with `awless show %s -r %s --local`", parent.Id(), res, res.Id(), parent.Id()))
}
}
}
return errors.New(buf.String())
}
func printResourceList(title string, list []cloud.Resource, shortenListMsg ...string) {
sort.Sort(byTypeAndString{list})
all := cloud.Resources(list).Map(func(r cloud.Resource) string { return printResourceRef(r) })
count := len(all)
max := 3
if count > 0 {
if !listAllSiblingsFlag && len(shortenListMsg) > 0 && count > max {
fmt.Printf("\n%s: %s, ... (%s)\n", title, strings.Join(all[0:max], ", "), shortenListMsg[0])
} else {
fmt.Printf("\n%s: %s\n", title, strings.Join(all, ", "))
}
}
}
func printResourceRef(r cloud.Resource, idRenderFunc ...func(a ...interface{}) string) string {
render := fmt.Sprint
if len(idRenderFunc) > 0 {
render = idRenderFunc[0]
}
if noAliasFlag {
return r.Format(render("%i") + "[" + color.New(color.FgBlue, color.Bold).SprintFunc()("%t") + "]")
}
return r.Format(render("%n") + "[" + color.New(color.FgBlue, color.Bold).SprintFunc()("%t") + "]")
}
type byTypeAndString struct {
res []cloud.Resource
}
func (b byTypeAndString) Len() int { return len(b.res) }
func (b byTypeAndString) Swap(i, j int) {
b.res[i], b.res[j] = b.res[j], b.res[i]
}
func (b byTypeAndString) Less(i, j int) bool {
if b.res[i].Type() != b.res[j].Type() {
return b.res[i].Type() < b.res[j].Type()
}
return b.res[i].String() <= b.res[j].String()
}
/*
Copyright 2017 WALLIX
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 commands
import (
"context"
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/spf13/cobra"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/match"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/config"
"github.com/wallix/awless/console"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/ssh"
)
var keyPathFlag, proxyInstanceThroughFlag string
var sshPortFlag, sshTroughPortFlag int
var printSSHConfigFlag bool
var printSSHCLIFlag bool
var privateIPFlag bool
var disableStrictHostKeyCheckingFlag bool
func init() {
RootCmd.AddCommand(sshCmd)
sshCmd.Flags().StringVarP(&keyPathFlag, "identity", "i", "", "Set path or name toward the identity (key file) to use to connect through SSH")
sshCmd.Flags().IntVar(&sshPortFlag, "port", 22, "Set SSH target port")
sshCmd.Flags().IntVar(&sshTroughPortFlag, "through-port", 22, "Set SSH proxy port")
sshCmd.Flags().StringVar(&proxyInstanceThroughFlag, "through", "", "Name of instance to proxy through to connect to a destination host")
sshCmd.Flags().BoolVar(&printSSHConfigFlag, "print-config", false, "Print SSH configuration for ~/.ssh/config file.")
sshCmd.Flags().BoolVar(&printSSHCLIFlag, "print-cli", false, "Print the CLI one-liner to connect with SSH. (/usr/bin/ssh user@ip -i ...)")
sshCmd.Flags().BoolVar(&privateIPFlag, "private", false, "Use private ip to connect to host")
sshCmd.Flags().BoolVar(&disableStrictHostKeyCheckingFlag, "disable-strict-host-keychecking", false, "Disable the remote host key check from ~/.ssh/known_hosts or ~/.awless/known_hosts file")
}
var defaultAMIUsers = []string{"ec2-user", "ubuntu", "centos", "core", "bitnami", "admin", "root"}
var sshCmd = &cobra.Command{
Use: "ssh [USER@]INSTANCE",
Short: "Launch a SSH session to an instance given an id or alias",
Long: "Launch a SSH session to an instance given an id or alias. All connection details are derived from a given instance name/id.",
Example: ` awless ssh i-8d43b21b # using the instance id
awless ssh redis-prod # using name only (other infos are derived)
awless ssh ec2-user@redis-prod # forcing the user
awless ssh 34.215.29.221 # using the IP
awless ssh root@34.215.29.221 --port 23 # specifying a port
awless ssh redis-prod -i keyname # using AWS keyname (look into ~/.ssh/keyname.pem & ~/.awless/keys/keyname.pem)
awless ssh redis-prod -i ~/path/toward/key # specifying a full key path
awless ssh db-private --through my-bastion # connect to a private inst through a public one
awless ssh db-private --private # connect using the private IP (when you have a VPN, tunnel, etc ...)
awless ssh redis-prod --print-cli # print out the full terminal command to connect to instance
awless ssh redis-prod --print-config # print out the full SSH config (i.e: ~/.ssh/config) to connect to instance
awless ssh private-redis --through my-proxy # connect to private through proxy instance
awless ssh private-redis --through my-proxy --through-port 23 # specifying proxy port
awless ssh 172.31.77.151 --port 2222 --through my-proxy --through-port 23 # specifying target & proxy port`,
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("instance required")
}
var err error
var connectionCtx *instanceConnectionContext
if proxyInstanceThroughFlag != "" {
connectionCtx, err = initInstanceConnectionContext(proxyInstanceThroughFlag, keyPathFlag)
} else {
connectionCtx, err = initInstanceConnectionContext(args[0], keyPathFlag)
}
exitOn(err)
firsHopClient, err := ssh.InitClient(connectionCtx.keypath, config.KeysDir, filepath.Join(os.Getenv("HOME"), ".ssh"))
exitOn(err)
if err != nil && strings.Contains(err.Error(), "cannot find SSH key") && keyPathFlag == "" {
logger.Info("you may want to specify a key filepath with `-i /path/to/key.pem`")
}
exitOn(err)
firsHopClient.SetLogger(logger.DefaultLogger)
firsHopClient.SetStrictHostKeyChecking(!disableStrictHostKeyCheckingFlag)
firsHopClient.InteractiveTerminalFunc = console.InteractiveTerminal
if proxyInstanceThroughFlag != "" {
firsHopClient.Port = sshTroughPortFlag
} else {
firsHopClient.Port = sshPortFlag
}
if privateIPFlag {
if priv := connectionCtx.privip; priv != "" {
firsHopClient.IP = connectionCtx.privip
} else {
exitOn(fmt.Errorf(
"no private IP resolved for instance %s (state '%s')",
connectionCtx.instance.Id(), connectionCtx.state,
))
}
} else {
if pub := connectionCtx.ip; pub != "" {
firsHopClient.IP = connectionCtx.ip
} else if priv := connectionCtx.privip; priv != "" {
firsHopClient.IP = connectionCtx.privip
} else {
exitOn(fmt.Errorf("no public/private IP resolved for instance %s (state '%s')", connectionCtx.instance.Id(), connectionCtx.state))
}
}
if connectionCtx.user != "" {
err = firsHopClient.DialWithUsers(connectionCtx.user)
} else {
err = firsHopClient.DialWithUsers(defaultAMIUsers...)
}
if isConnectionRefusedErr(err) {
logger.Warning("cannot connect to this instance, maybe the system is still booting?")
exitOn(err)
return nil
}
if err != nil {
if e := connectionCtx.checkInstanceAccessible(); e != nil {
logger.Error(e.Error())
}
exitOn(err)
}
targetClient := firsHopClient
if proxyInstanceThroughFlag != "" {
destInstanceCtx, err := initInstanceConnectionContext(args[0], keyPathFlag)
exitOn(err)
if destInstanceCtx.user != "" {
targetClient, err = firsHopClient.NewClientWithProxy(destInstanceCtx.privip, sshPortFlag, destInstanceCtx.user)
} else {
targetClient, err = firsHopClient.NewClientWithProxy(destInstanceCtx.privip, sshPortFlag, defaultAMIUsers...)
}
exitOn(err)
}
if printSSHConfigFlag {
host := connectionCtx.instanceName
if proxyInstanceThroughFlag != "" {
host = args[0]
}
fmt.Println(targetClient.SSHConfigString(host))
return nil
}
if printSSHCLIFlag {
fmt.Println(targetClient.ConnectString())
return nil
}
exitOn(targetClient.Connect())
return nil
},
}
func isConnectionRefusedErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "connection refused")
}
type instanceConnectionContext struct {
ip, privip string
myip net.IP
user, keypath string
state, instanceName string
instance cloud.Resource
resourcesGraph cloud.GraphAPI
}
func initInstanceConnectionContext(userhost, keypath string) (*instanceConnectionContext, error) {
ctx := &instanceConnectionContext{}
if strings.Contains(userhost, "@") {
ctx.user = strings.Split(userhost, "@")[0]
ctx.instanceName = strings.Split(userhost, "@")[1]
} else {
ctx.instanceName = userhost
}
ctx.fetchConnectionInfo()
instanceMatchers := match.Or(match.Property(properties.Name, ctx.instanceName), match.Property(properties.PublicIP, ctx.instanceName), match.Property(properties.PrivateIP, ctx.instanceName))
resources, err := ctx.resourcesGraph.Find(cloud.NewQuery(cloud.Instance).Match(instanceMatchers))
exitOn(err)
switch len(resources) {
case 0:
// No instance with that name, use the id
ctx.instance, err = findResource(ctx.resourcesGraph, ctx.instanceName, cloud.Instance)
exitOn(err)
case 1:
ctx.instance = resources[0]
default:
idStatus := cloud.Resources(resources).Map(func(r cloud.Resource) string {
return fmt.Sprintf("%s (%s)", r.Id(), r.Properties()[properties.State])
})
logger.Infof("Found %d resources with name '%s': %s", len(resources), ctx.instanceName, strings.Join(idStatus, ", "))
var running []cloud.Resource
running, err = ctx.resourcesGraph.Find(cloud.NewQuery(cloud.Instance).Match(match.And(instanceMatchers, match.Property(properties.State, "running"))))
exitOn(err)
switch len(running) {
case 0:
logger.Warning("None of them is running, cannot connect through SSH")
return ctx, errors.New("non running instances")
case 1:
logger.Infof("Found only one instance running: %s. Will connect to this instance.", running[0].Id())
ctx.instance = running[0]
default:
logger.Warning("Connect through the running ones using their id:")
for _, res := range running {
var up string
if uptime, ok := res.Properties()[properties.Launched].(time.Time); ok {
up = fmt.Sprintf("\t\t(uptime: %s)", console.HumanizeTime(uptime))
}
logger.Warningf("\t`awless ssh %s`%s", res.Id(), up)
}
return ctx, errors.New("use instances ids")
}
}
ctx.privip, _ = ctx.instance.Properties()[properties.PrivateIP].(string)
ctx.ip, _ = ctx.instance.Properties()[properties.PublicIP].(string)
ctx.state, _ = ctx.instance.Properties()[properties.State].(string)
if keypath != "" {
ctx.keypath = keypath
} else {
keypair, ok := ctx.instance.Properties()[properties.KeyPair].(string)
if ok {
ctx.keypath = fmt.Sprint(keypair)
}
}
return ctx, nil
}
func (ctx *instanceConnectionContext) fetchConnectionInfo() {
var resourcesGraph, sgroupsGraph cloud.GraphAPI
var myip net.IP
var wg sync.WaitGroup
var errc = make(chan error)
wg.Add(1)
go func() {
var err error
defer wg.Done()
resourcesGraph, err = awsservices.InfraService.FetchByType(context.WithValue(context.Background(), contextKey("force"), true), cloud.Instance)
if err != nil {
errc <- err
}
}()
wg.Add(1)
go func() {
var err error
defer wg.Done()
sgroupsGraph, err = awsservices.InfraService.FetchByType(context.WithValue(context.Background(), contextKey("force"), true), cloud.SecurityGroup)
if err != nil {
errc <- err
}
}()
wg.Add(1)
go func() {
defer wg.Done()
myip = getMyIP()
}()
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
exitOn(err)
}
}
resourcesGraph.Merge(sgroupsGraph)
ctx.resourcesGraph = resourcesGraph
ctx.myip = myip
}
func (ctx *instanceConnectionContext) checkInstanceAccessible() (err error) {
if st := ctx.state; st != "running" {
logger.Warningf("this instance is '%s' (cannot ssh to a non running state)", st)
if st == "stopped" {
logger.Warningf("you can start it with `awless -f start instance id=%s`", ctx.instance.Id())
}
return errors.New("instance not accessible")
}
sgroups, ok := ctx.instance.Properties()[properties.SecurityGroups].([]string)
if ok {
var sshPortOpen, myIPAllowed bool
for _, id := range sgroups {
var sgroup cloud.Resource
sgroup, err = findResource(ctx.resourcesGraph, id, cloud.SecurityGroup)
if err != nil {
logger.Errorf("cannot get securitygroup '%s' for instance '%s': %s", id, ctx.instance.Id(), err)
break
}
rules, ok := sgroup.Properties()[properties.InboundRules].([]*graph.FirewallRule)
if ok {
for _, r := range rules {
if r.PortRange.Contains(22) {
sshPortOpen = true
}
if ctx.myip != nil && r.Contains(ctx.myip.String()) {
myIPAllowed = true
}
}
}
}
if !sshPortOpen {
logger.Warning("port 22 is not open on this instance")
return errors.New("instance not accessible")
}
if !myIPAllowed && ctx.myip != nil {
logger.Warningf("your ip %s is not authorized for this instance. You might want to update the securitygroup with:", ctx.myip)
var group = "mygroup"
if len(sgroups) == 1 {
group = sgroups[0]
}
logger.Warningf("`awless update securitygroup id=%s inbound=authorize protocol=tcp cidr=%s/32 portrange=22`", group, ctx.myip)
return errors.New("instance not accessible")
}
}
return nil
}
func findResource(g cloud.GraphAPI, id, typ string) (cloud.Resource, error) {
found, err := g.FindOne(cloud.NewQuery(typ).Match(match.Property(properties.ID, id)))
if found == nil || err != nil {
return nil, fmt.Errorf("%s '%s' not found", typ, id)
}
return found, nil
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"github.com/spf13/cobra"
awsconfig "github.com/wallix/awless/aws/config"
"github.com/wallix/awless/config"
)
func init() {
RootCmd.AddCommand(switchCmd)
}
var switchCmd = &cobra.Command{
Use: "switch [REGION] [PROFILE]",
Aliases: []string{"sw"},
Short: "Quick way to switch awless config to given profile and/or region",
Example: ` awless switch eu-west-2 # now using region eu-west-2'
awless switch mfa # now using profile mfa (with mfa a valid profile in ~/.aws/{config,credentials})
awless switch default us-west-1 # now using region us-west-1 and the default profile
awless sw eu-west-3 admin # now using profile admin in region eu-west-3`,
PersistentPreRun: applyHooks(initAwlessEnvHook, initLoggerHook),
PersistentPostRun: applyHooks(
includeHookIf(&config.TriggerSyncOnConfigUpdate, initCloudServicesHook),
notifyOnRegionOrProfilePrecedenceHook,
),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 || len(args) > 2 {
fmt.Fprintf(os.Stdout, "currently in region '%s' with profile '%s' (switch -h for help and examples)\n", config.GetAWSRegion(), config.GetAWSProfile())
return
}
for _, arg := range args {
if awsconfig.IsValidRegion(arg) {
exitOn(config.Set(config.RegionConfigKey, arg))
continue
}
if awsconfig.IsValidProfile(arg) {
exitOn(config.Set(config.ProfileConfigKey, arg))
continue
}
exitOn(fmt.Errorf("could not find profile: '%s' in $HOME/.aws/{credentials,config}", arg))
}
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"log"
"os"
"runtime"
"runtime/pprof"
"strings"
"time"
"github.com/spf13/cobra"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/config"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync"
)
var (
servicesToSyncFlags map[string]*bool
profileSyncFlag bool
)
func init() {
RootCmd.AddCommand(syncCmd)
syncCmd.Flags().BoolVar(&profileSyncFlag, "profile-sync", false, "Will dump a cpu and mem profiling file")
servicesToSyncFlags = make(map[string]*bool)
for _, service := range awsservices.ServiceNames {
servicesToSyncFlags[service] = new(bool)
syncCmd.Flags().BoolVar(servicesToSyncFlags[service], service, false, fmt.Sprintf("Sync '%s' service only", service))
}
}
var syncCmd = &cobra.Command{
Use: "sync",
Short: "Manual sync of remote resources to the local store (ex: when autosync is unset)",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, initSyncerHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
RunE: func(cmd *cobra.Command, args []string) error {
var services []cloud.Service
displayAllServices := true
for _, srv := range cloud.ServiceRegistry {
if *servicesToSyncFlags[srv.Name()] {
displayAllServices = false
}
}
for _, srv := range cloud.ServiceRegistry {
if displayAllServices || *servicesToSyncFlags[srv.Name()] {
services = append(services, srv)
}
}
localGraphs := make(map[string]cloud.GraphAPI)
for _, service := range services {
localGraphs[service.Name()] = sync.LoadLocalGraphForService(service.Name(), config.GetAWSProfile(), config.GetAWSRegion())
}
logger.Infof("running sync for region '%s'", config.GetAWSRegion())
var syncErr error
var graphs map[string]cloud.GraphAPI
syncFn := func() {
graphs, syncErr = sync.DefaultSyncer.Sync(services...)
}
start := time.Now()
if profileSyncFlag {
withProfiling(syncFn)
} else {
syncFn()
}
if syncErr != nil {
logger.Verbose(syncErr)
}
for k, g := range graphs {
displaySyncStats(k, g)
}
logger.Infof("sync took %s", time.Since(start))
return nil
},
}
func withProfiling(fn func()) {
logger.Infof("sync profiling on")
mem, err := os.Create("mem-sync.prof")
if err != nil {
log.Fatal("could not create mem profile: ", err)
}
logger.Infof("running garbage collection before profiling")
runtime.GC() // cleaned up memeory before running function
defer mem.Close()
cpu, err := os.Create("cpu-sync.prof")
if err != nil {
log.Fatal("could not create cpu profile: ", err)
}
if err := pprof.StartCPUProfile(cpu); err != nil {
log.Fatal("could not start cpu profile: ", err)
}
fn()
pprof.StopCPUProfile()
if err := pprof.WriteHeapProfile(mem); err != nil {
log.Fatal("could not write memory profile: ", err)
}
logger.Infof("Generated profiling files %s and %s", cpu.Name(), mem.Name())
}
func displaySyncStats(serviceName string, g cloud.GraphAPI) {
var strs []string
for rt, service := range awsservices.ServicePerResourceType {
if service == serviceName {
res, err := g.Find(cloud.NewQuery(rt))
if err != nil {
continue
}
nbRes := len(res)
if nbRes > 1 {
strs = append(strs, fmt.Sprintf("%d %s", nbRes, cloud.PluralizeResource(rt)))
} else {
strs = append(strs, fmt.Sprintf("%d %s", nbRes, rt))
}
}
}
logger.Infof("-> %s: %s", serviceName, strings.Join(strs, ", "))
}
package commands
import (
"fmt"
"sort"
"strings"
"github.com/chzyer/readline"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/template"
)
func enumCompletionFunc(enum []string) readline.AutoCompleter {
if len(enum) == 1 && enum[0] == "" {
return readline.NewPrefixCompleter()
}
var items []readline.PrefixCompleterInterface
for _, e := range enum {
items = append(items, readline.PcItem(e))
}
return readline.NewPrefixCompleter(items...)
}
func typedParamCompletionFunc(g cloud.GraphAPI, resourceType, propName string) readline.AutoCompleter {
var items []readline.PrefixCompleterInterface
resources, _ := g.Find(cloud.NewQuery(resourceType))
for _, res := range resources {
if val, ok := res.Properties()[propName]; ok {
switch vv := val.(type) {
case []string:
for _, s := range vv {
items = append(items, readline.PcItem(s))
}
default:
items = append(items, readline.PcItem(fmt.Sprint(val)))
}
}
}
return readline.NewPrefixCompleter(items...)
}
func holeAutoCompletion(g cloud.GraphAPI, paramPaths []string) readline.AutoCompleter {
type typesProp struct {
types []string
prop string
}
var entities []typesProp
for _, paramPath := range paramPaths {
splits := strings.Split(paramPath, ".")
if len(splits) != 3 {
continue
}
if entityTypes, entityProp := guessEntityTypeFromHoleQuestion(splits[1] + "." + splits[2]); len(entityTypes) > 0 {
entities = append(entities, typesProp{types: entityTypes, prop: entityProp})
}
}
var possibleSuggests []string
for _, entityProp := range entities {
resources, err := g.Find(cloud.NewQuery(entityProp.types...))
exitOn(err)
if len(resources) == 0 {
continue
}
var validPropName string
if entityProp.prop != "" {
for _, r := range resources {
for propName := range r.Properties() {
if keyCorrespondsToProperty(entityProp.prop, propName) {
validPropName = propName
}
}
}
}
if validPropName == "" {
for _, r := range resources {
possibleSuggests = append(possibleSuggests, r.Id())
possibleSuggests = appendWithNameAliases(possibleSuggests, r)
}
continue
}
for _, r := range resources {
if v, ok := r.Property(validPropName); ok {
switch prop := v.(type) {
case string, float64, int, bool:
possibleSuggests = append(possibleSuggests, fmt.Sprint(prop))
if validPropName == "ID" {
possibleSuggests = appendWithNameAliases(possibleSuggests, r)
}
case []string:
possibleSuggests = append(possibleSuggests, prop...)
case []*graph.KeyValue:
for _, kv := range prop {
possibleSuggests = append(possibleSuggests, fmt.Sprintf("%s:%s", kv.KeyName, kv.Value))
}
}
}
}
}
completeFunc := func(s string) (suggest []string) {
s = splitKeepLast(s, ",")
s = strings.TrimLeft(s, "'@\"")
for _, possible := range possibleSuggests {
suggest = appendIfContains(suggest, possible, s)
}
suggest = quotedSortedSet(suggest)
return
}
return &prefixCompleter{callback: completeFunc, splitChar: ","}
}
type prefixCompleter struct {
callback readline.DynamicCompleteFunc
splitChar string
}
func (p *prefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) {
var lines []string
lines, offset = doInternal(p, string(line), pos, line)
for _, l := range lines {
newLine = append(newLine, []rune(l))
}
return
}
func doInternal(p *prefixCompleter, line string, pos int, origLine []rune) (newLine []string, offset int) {
if p.splitChar != "" {
line = splitKeepLast(line, p.splitChar)
}
for _, suggest := range p.callback(line) {
line = strings.TrimLeft(line, "[")
if len(line) >= len(suggest) {
if strings.HasPrefix(line, suggest) {
if len(line) != len(suggest) {
newLine = append(newLine, suggest)
}
offset = len(suggest)
}
} else {
if strings.HasPrefix(suggest, line) {
newLine = append(newLine, suggest[len(line):])
offset = len(line)
}
}
}
return
}
func splitKeepLast(s, sep string) (last string) {
if !strings.Contains(s, sep) {
last = s
return
}
offset := strings.LastIndex(s, sep)
if offset+1 < len(s) {
last = s[offset+1:]
}
return
}
func quotedSortedSet(list []string) (out []string) {
unique := make(map[string]bool)
for _, l := range list {
unique[l] = true
}
for k := range unique {
if !template.MatchStringParamValue(k) {
k = "'" + k + "'"
}
out = append(out, k)
}
sort.Strings(out)
return
}
// Return potential resource types and a prop
// according to given holes questions.
// See corresponding unit test for logic
func guessEntityTypeFromHoleQuestion(hole string) (resolved []string, prop string) {
tokens := strings.Split(strings.TrimSpace(hole), ".")
if len(tokens) == 0 {
return
}
var types []string
for _, t := range tokens {
for _, r := range resourcesTypesWithPlural {
if t == r {
types = append(types, cloud.SingularizeResource(r))
break
}
}
}
if l := len(types); l > 0 {
if len(tokens) == 2 {
prop = tokens[1]
}
if l > 1 {
prop = ""
}
resolved = []string{types[l-1]}
} else if len(tokens) > 1 {
for i := len(tokens) - 1; i >= 0; i-- {
if len(tokens[i]) < 4 {
continue
}
for _, r := range awsservices.ResourceTypes {
if strings.Contains(r, tokens[i]) {
resolved = append(resolved, r)
}
}
if len(resolved) > 0 {
return
}
}
}
return
}
func keyCorrespondsToProperty(holekey, prop string) bool {
holekey = strings.ToLower(holekey)
prop = strings.ToLower(prop)
if holekey == prop {
return true
}
if strings.Replace(holekey, "-", "", -1) == prop {
return true
}
return false
}
func appendIfContains(slice []string, value, subst string) []string {
subst = strings.TrimLeft(subst, "[")
if strings.Contains(value, subst) && value != "" {
return append(slice, value)
}
return slice
}
func appendWithNameAliases(slice []string, res cloud.Resource) []string {
if val, ok := res.Properties()["Name"]; ok {
switch name := val.(type) {
case string:
if name != "" {
slice = append(slice, fmt.Sprintf("@%s", name))
}
}
}
return slice
}
var resourcesTypesWithPlural []string
func init() {
for _, r := range awsservices.ResourceTypes {
resourcesTypesWithPlural = append(resourcesTypesWithPlural, r, cloud.PluralizeResource(r))
}
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
awstailers "github.com/wallix/awless/aws/tailers"
)
var tailFollowFrequencyFlag time.Duration
var tailEnableFollowFlag bool
var tailNumberEventsFlag int
var stackEventsFilters []string
var stackEventsTailTimeout time.Duration
var cancelStackUpdateAfterTimeout bool
func init() {
RootCmd.AddCommand(tailCmd)
tailCmd.PersistentFlags().DurationVar(&tailFollowFrequencyFlag, "frequency", 10*time.Second, "Fetch refresh frequency")
tailCmd.PersistentFlags().BoolVar(&tailEnableFollowFlag, "follow", false, "Periodically refresh and append new data to output")
tailCmd.PersistentFlags().IntVarP(&tailNumberEventsFlag, "number", "n", 10, "Number of events to display")
tailCmd.AddCommand(scalingActivitiesCmd)
stackEventsCmd.PersistentFlags().StringArrayVar(&stackEventsFilters, "filters",
[]string{awstailers.StackEventTimestamp, awstailers.StackEventLogicalID, awstailers.StackEventType, awstailers.StackEventStatus},
fmt.Sprintf("Filter the output columns. Valid filters: %s, %s, %s, %s, %s",
awstailers.StackEventLogicalID,
awstailers.StackEventStatus,
awstailers.StackEventStatusReason,
awstailers.StackEventTimestamp,
awstailers.StackEventType))
stackEventsCmd.PersistentFlags().BoolVar(&cancelStackUpdateAfterTimeout, "cancel-on-timeout", false, "Cancel stack update when timeout is reached, use with 'timeout' flag")
stackEventsCmd.PersistentFlags().DurationVar(&stackEventsTailTimeout, "timeout", 1*time.Hour, "Time to wait for stack update to complete, use with 'follow' flag")
tailCmd.AddCommand(stackEventsCmd)
}
var tailCmd = &cobra.Command{
Use: "tail",
Hidden: true,
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook, initCloudServicesHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, networkMonitorHook),
Short: "Tail cloud events",
}
var scalingActivitiesCmd = &cobra.Command{
Use: "scaling-activities",
Short: "Watch scaling-activities",
Run: func(cmd *cobra.Command, args []string) {
exitOn(awstailers.NewScalingActivitiesTailer(tailNumberEventsFlag, tailEnableFollowFlag, tailFollowFrequencyFlag).Tail(os.Stdout))
},
}
var stackEventsCmd = &cobra.Command{
Use: "stack-events",
Short: "Watch stack-events",
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
exitOn(fmt.Errorf("expecting stack-name string"))
}
exitOn(awstailers.NewCloudformationEventsTailer(args[0], tailNumberEventsFlag, tailEnableFollowFlag, tailFollowFrequencyFlag, stackEventsFilters, stackEventsTailTimeout, cancelStackUpdateAfterTimeout).Tail(os.Stdout))
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wallix/awless/config"
)
func init() {
RootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Show awless version",
Run: printVersion,
}
func printVersion(*cobra.Command, []string) {
fmt.Fprint(os.Stderr, config.AWLESS_ASCII_LOGO)
fmt.Println(config.CurrentBuildInfo)
}
/*
Copyright 2017 WALLIX
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 commands
import (
"strings"
"github.com/spf13/cobra"
"github.com/wallix/awless/config"
"github.com/wallix/awless/web"
)
var (
webPortFlag string
)
func init() {
RootCmd.AddCommand(webCmd)
webCmd.Flags().StringVar(&webPortFlag, "port", ":8080", "Web UI port to listen on")
}
var webCmd = &cobra.Command{
Use: "web",
Hidden: true,
Short: "[Experimental] Browse your locally synced data through the web",
PersistentPreRun: applyHooks(initLoggerHook, initAwlessEnvHook),
Run: func(cmd *cobra.Command, args []string) {
if !strings.HasPrefix(webPortFlag, ":") {
webPortFlag = ":" + webPortFlag
}
server := web.New(webPortFlag, config.GetAWSProfile())
exitOn(server.Start())
},
}
/*
Copyright 2017 WALLIX
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 commands
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
"errors"
"github.com/aws/smithy-go"
"github.com/spf13/cobra"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/logger"
)
var onlyMyIPFlag, onlyMyNameFlag, onlyMyTypeFlag, onlyMyIDFlag, onlyMyAccountFlag, onlyMyResourcePathFlag bool
func init() {
RootCmd.AddCommand(whoamiCmd)
whoamiCmd.Flags().BoolVar(&onlyMyIPFlag, "ip-only", false, "Only returns your IP address as seen by AWS")
whoamiCmd.Flags().BoolVar(&onlyMyNameFlag, "name-only", false, "Only returns the name of the resource (ex: username for a user)")
whoamiCmd.Flags().BoolVar(&onlyMyTypeFlag, "type-only", false, "Only returns the type of the resource (ex: user for a user)")
whoamiCmd.Flags().BoolVar(&onlyMyIDFlag, "id-only", false, "Only returns the ID of the resource")
whoamiCmd.Flags().BoolVar(&onlyMyAccountFlag, "account-only", false, "Only returns the AWS account number")
whoamiCmd.Flags().BoolVar(&onlyMyResourcePathFlag, "resource-only", false, "Only returns the AWS ARN resource path suffix (ex: user/jsmith)")
}
var whoamiCmd = &cobra.Command{
Use: "whoami",
Aliases: []string{"who"},
PersistentPreRun: applyHooks(initAwlessEnvHook, initLoggerHook, initCloudServicesHook, firstInstallDoneHook),
PersistentPostRun: applyHooks(verifyNewVersionHook, onVersionUpgrade, networkMonitorHook),
Short: "Show your account, attached (i.e. managed) and inlined policies",
Run: func(cmd *cobra.Command, args []string) {
if onlyMyIPFlag {
fmt.Println(getMyIP())
return
}
if localGlobalFlag {
logger.Warning("`--local` flag prevent the command from fetching remote information")
return
}
me, err := awsservices.AccessService.(*awsservices.Access).GetIdentity()
exitOn(err)
if me.IsRoot() {
logger.Warning("You are currently root")
logger.Warning("Best practices suggest to create a new user and affecting it roles of access")
logger.Warning("awless official templates might help https://github.com/wallix/awless-templates\n")
}
switch {
case onlyMyAccountFlag:
fmt.Println(me.Account)
return
case onlyMyIDFlag:
fmt.Println(me.UserId)
return
case onlyMyNameFlag:
fmt.Println(me.Resource)
return
case onlyMyTypeFlag:
fmt.Println(me.ResourceType)
return
case onlyMyResourcePathFlag:
fmt.Println(me.ResourcePath)
return
}
if !me.IsUserType() {
fmt.Printf("ResourceType: %s, Resource: %s, Id: %s, Account: %s\n", me.ResourceType, me.Resource, me.UserId, me.Account)
return
}
fmt.Printf("Username: %s, Id: %s, Account: %s\n", me.Resource, me.UserId, me.Account)
policies, err := awsservices.AccessService.(*awsservices.Access).GetUserPolicies(me.Resource)
if err != nil {
var ae smithy.APIError
if errors.As(err, &ae) && ae.ErrorCode() == "AccessDenied" {
logger.Warningf("user '%s' is not authorized to list its policies", me.Resource)
} else {
logger.Error(err)
}
return
}
if attached := policies.Attached; len(attached) > 0 {
fmt.Println("\nAttached policies (i.e. managed):")
for _, name := range attached {
fmt.Printf("\t- %s\n", name)
}
} else {
fmt.Println("\nAttached policies (i.e. managed): none")
}
if inlined := policies.Inlined; len(inlined) > 0 {
fmt.Println("\nInlined policies:")
for _, name := range inlined {
fmt.Printf("\t- %s\n", name)
}
} else {
fmt.Println("\nInlined policies: none")
}
if byGroup := policies.ByGroup; len(byGroup) > 0 {
for g, pol := range byGroup {
fmt.Printf("\nPolicies from group '%s': %s\n", g, strings.Join(pol, ", "))
}
}
},
}
func getMyIP() net.IP {
client := &http.Client{Timeout: 3 * time.Second}
if resp, err := client.Get("http://checkip.amazonaws.com/"); err == nil {
b, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return net.ParseIP(strings.TrimSpace(string(b)))
}
return nil
}
package config
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"text/tabwriter"
awsconfig "github.com/wallix/awless/aws/config"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/database"
)
var (
Config = map[string]interface{}{}
Defaults = map[string]interface{}{}
)
const (
configDatabaseKey = "userconfig"
defaultsDatabaseKey = "defaults"
//Config
autosyncConfigKey = "autosync"
checkUpgradeFrequencyConfigKey = "upgrade.checkfrequency"
schedulerURL = "scheduler.url"
RegionConfigKey = "aws.region"
ProfileConfigKey = "aws.profile"
//Config prefix
awsCloudPrefix = "aws."
)
var configDefinitions = map[string]*Definition{
autosyncConfigKey: {help: "Automatically synchronize your cloud locally", defaultValue: "true", parseParamFn: parseBool},
RegionConfigKey: {help: "AWS region", parseParamFn: awsconfig.ParseRegion, stdinParamProviderFn: awsconfig.StdinRegionSelector, onUpdateFns: []onUpdateFunc{runSyncWithUpdatedRegion}},
ProfileConfigKey: {help: "AWS profile", defaultValue: "default"},
"aws.infra.sync": {help: "Enable/disable sync of infra services (EC2, RDS, etc.) (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.access.sync": {help: "Enable/disable sync of IAM service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.storage.sync": {help: "Enable/disable sync of S3 service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.storage.s3object.sync": {help: "Enable/disable sync of S3/s3object (when empty: true)", defaultValue: "false", parseParamFn: parseBool},
"aws.dns.sync": {help: "Enable/disable sync of DNS service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.dns.record.sync": {help: "Enable/disable sync of DNS/record (when empty: true)", defaultValue: "false", parseParamFn: parseBool},
"aws.notification.sync": {help: "Enable/disable sync of SNS service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.monitoring.sync": {help: "Enable/disable sync of CloudWatch service (when empty: true)", defaultValue: "false", parseParamFn: parseBool},
"aws.lambda.sync": {help: "Enable/disable sync of Lambda service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.messaging.sync": {help: "Enable/disable sync of SQS/SNS service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.cdn.sync": {help: "Enable/disable sync of CloudFront service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
"aws.cloudformation.sync": {help: "Enable/disable sync of CloudFormation service (when empty: true)", defaultValue: "true", parseParamFn: parseBool},
checkUpgradeFrequencyConfigKey: {help: "Upgrade check frequency (hours); a negative value disables check", defaultValue: "8", parseParamFn: parseInt},
schedulerURL: {help: "URL used by awless CLI to interact with pre-installed https://github.com/wallix/awless-scheduler", defaultValue: "http://localhost:8082"},
}
var defaultsDefinitions = map[string]*Definition{
"instance.type": {defaultValue: "t2.micro", help: "AWS EC2 instance type", stdinParamProviderFn: awsconfig.StdinInstanceTypeSelector, parseParamFn: awsconfig.ParseInstanceType},
"instance.distro": {defaultValue: "amazonlinux", help: "Query to fetch latest community bare distro image id (see awless search images -h)", parseParamFn: parseDistroQuery},
"instance.count": {defaultValue: "1", help: "Number of instances to create on AWS EC2", parseParamFn: parseInt},
"instance.timeout": {defaultValue: "180", help: "Time to wait when checking instance states on AWS EC2", parseParamFn: parseInt},
"securitygroup.protocol": {defaultValue: "tcp", help: "The IP protocol to authorize on the security group"},
"volume.device": {defaultValue: "/dev/sdh", help: "Device name to expose to an EC2 instance"},
"elasticip.domain": {defaultValue: "vpc", help: "The domain of elastic IP addresses (standard or vpc)"},
"image.delete-snapshots": {defaultValue: "true", help: "Delete linked snapshots when deleting an image"},
"database.type": {defaultValue: "db.t2.micro", help: "Default RDS database type"},
}
var deprecated = map[string]string{
"sync.auto": autosyncConfigKey,
"region": RegionConfigKey,
}
var TriggerSyncOnConfigUpdate bool
type onUpdateFunc func(interface{})
type Definition struct {
help string
parseParamFn func(string) (interface{}, error)
stdinParamProviderFn func() string
onUpdateFns []onUpdateFunc
defaultValue string
}
func LoadConfig() error {
err := database.Execute(func(db *database.DB) (dberr error) {
Config, dberr = db.GetConfigs(configDatabaseKey)
if dberr != nil {
return fmt.Errorf("config: load config: %s", dberr)
}
Defaults, dberr = db.GetConfigs(defaultsDatabaseKey)
if dberr != nil {
return fmt.Errorf("config: load defaults: %s", dberr)
}
return
})
return err
}
func DisplayConfig() string {
return fmt.Sprintf("%s\n%s", displayConfig(), displayDefaults())
}
func InitConfig(fromEnv map[string]string) error {
for k, v := range configDefinitions {
val := v.defaultValue
if vv, ok := fromEnv[k]; ok {
val = vv
}
if err := Set(k, val); err != nil {
return err
}
}
for k, v := range defaultsDefinitions {
val := v.defaultValue
if vv, ok := fromEnv[k]; ok {
val = vv
}
if err := Set(k, val); err != nil {
return err
}
}
return nil
}
func Set(key, value string) error {
v, def, isConf, err := setVolatile(key, value)
if err != nil {
return err
}
var databaseKey string
if isConf {
databaseKey = configDatabaseKey
} else {
databaseKey = defaultsDatabaseKey
}
if err := database.Execute(func(db *database.DB) error {
return db.SetConfig(databaseKey, key, v)
}); err != nil {
return err
}
if def != nil {
for _, fn := range def.onUpdateFns {
fn(v)
}
}
return nil
}
func SetProfileCallback(value string) error {
return Set(ProfileConfigKey, value)
}
func Unset(key string) error {
var dbKey string
if _, ok := Config[key]; ok {
delete(Config, key)
dbKey = configDatabaseKey
}
if _, ok := Defaults[key]; ok {
delete(Defaults, key)
dbKey = defaultsDatabaseKey
}
if dbKey != "" {
if err := database.Execute(func(db *database.DB) error {
return db.UnsetConfig(dbKey, key)
}); err != nil {
return fmt.Errorf("unset config: %s", err)
}
}
return nil
}
func Get(key string) (interface{}, bool) {
if v, ok := Config[key]; ok {
return v, ok
}
v, ok := Defaults[key]
return v, ok
}
func SetVolatile(key, value string) error {
_, _, _, err := setVolatile(key, value)
return err
}
func InteractiveSet(key string) error {
var val string
if def, ok := configDefinitions[key]; ok && def.stdinParamProviderFn != nil {
val = def.stdinParamProviderFn()
} else if def, ok := defaultsDefinitions[key]; ok && def.stdinParamProviderFn != nil {
val = def.stdinParamProviderFn()
} else {
val = defaultStdinParamProvider()
}
return Set(key, val)
}
func parseBool(i string) (interface{}, error) {
b, err := strconv.ParseBool(i)
if err != nil {
return b, fmt.Errorf("invalid value, expected a boolean, got '%s'", i)
}
return b, nil
}
func parseInt(a string) (interface{}, error) {
i, err := strconv.Atoi(a)
if err != nil {
return i, fmt.Errorf("invalid value, expected an int, got '%s'", a)
}
return i, nil
}
func defaultParser(value string) (interface{}, error) {
if num, err := strconv.Atoi(value); err == nil {
return num, nil
}
if b, err := strconv.ParseBool(value); err == nil {
return b, nil
}
return value, nil
}
func parseDistroQuery(v string) (interface{}, error) {
_, err := awsspec.ParseImageQuery(v)
return v, err
}
func defaultStdinParamProvider() string {
var value string
for value == "" {
fmt.Print("Value ? > ")
fmt.Scan(&value)
}
return value
}
func setVolatile(key, value string) (interface{}, *Definition, bool, error) {
var isConf bool
confDef, confOk := configDefinitions[key]
defDef, defOk := defaultsDefinitions[key]
var def *Definition
switch {
case confOk && defOk:
return nil, def, isConf, fmt.Errorf("%s can not be in both config and defaults", key)
case confOk:
isConf = true
def = confDef
case defOk:
def = defDef
default:
if strings.Contains(key, awsCloudPrefix) {
isConf = true
}
}
var v interface{}
var err error
if def != nil && def.parseParamFn != nil {
if v, err = def.parseParamFn(value); err != nil {
return nil, def, isConf, err
}
} else {
if v, err = defaultParser(value); err != nil {
return nil, def, isConf, err
}
}
if isConf {
Config[key] = v
} else {
Defaults[key] = v
}
return v, def, isConf, nil
}
func displayConfig() string {
var b bytes.Buffer
b.WriteString("# Config parameters\n")
t := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
var keys []string
for k := range Config {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprintf(t, "\t%s:\t%v\t(%[2]T)", k, Config[k])
if def, ok := configDefinitions[k]; ok && def.help != "" {
fmt.Fprintf(t, "\t# %s\n", def.help)
} else {
fmt.Fprintln(t)
}
}
for k := range configDefinitions {
if _, ok := Config[k]; !ok {
fmt.Fprintf(t, "\t%s:\t\t", k)
if def, ok := configDefinitions[k]; ok && def.help != "" {
fmt.Fprintf(t, "\t# %s\n", def.help)
} else {
fmt.Fprintln(t)
}
}
}
t.Flush()
return b.String()
}
func displayDefaults() string {
var b bytes.Buffer
b.WriteString("# Template defaults\n")
b.WriteString(" ## Predefined\n")
t := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
var keys []string
for k := range Defaults {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
if def, ok := defaultsDefinitions[k]; ok {
if def.help != "" {
fmt.Fprintf(t, "\t%s:\t%v\t(%[2]T)\t# %s\n", k, Defaults[k], def.help)
} else {
fmt.Fprintf(t, "\t%s:\t%v\t(%[2]T)\n", k, Defaults[k])
}
}
}
for k := range defaultsDefinitions {
if _, ok := Defaults[k]; !ok {
fmt.Fprintf(t, "\t%s:\t \t(UNSET)", k)
if def, ok := defaultsDefinitions[k]; ok && def.help != "" {
fmt.Fprintf(t, "\t# %s\n", def.help)
} else {
fmt.Fprintln(t)
}
}
}
t.Flush()
count := 0
t = tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0)
for _, k := range keys {
if _, ok := defaultsDefinitions[k]; !ok {
count++
fmt.Fprintf(t, "\t%s:\t%v\t(%[2]T)", k, Defaults[k])
if newKey, ok := deprecated[k]; ok {
fmt.Fprintf(t, "\t# DEPRECATED, update with `awless config set %s` `awless config unset %s`", newKey, k)
}
fmt.Fprintln(t)
}
}
if count > 0 {
b.WriteString("\n ## User defined\n")
t.Flush()
}
return b.String()
}
func runSyncWithUpdatedRegion(i interface{}) {
if !GetAutosync() {
return
}
if !awsconfig.IsValidRegion(fmt.Sprint(i)) {
return
}
TriggerSyncOnConfigUpdate = true
}
package config
import (
"fmt"
"strings"
"time"
)
func GetAWSRegion() string {
if reg, ok := Config[RegionConfigKey]; ok && reg != "" {
return fmt.Sprint(reg)
}
if reg, ok := Defaults["region"]; ok && reg != "" { // Compatibility with old key
return fmt.Sprint(reg)
}
return ""
}
const defaultAWSSessionProfile = "default"
func GetAWSProfile() string {
if profile, ok := Config[ProfileConfigKey]; ok && profile != "" {
return fmt.Sprint(profile)
}
if profile, ok := Defaults[ProfileConfigKey]; ok && profile != "" { // Compatibility with old key
return fmt.Sprint(profile)
}
return defaultAWSSessionProfile
}
func GetAutosync() bool {
if autoSync, ok := Config[autosyncConfigKey].(bool); ok {
return autoSync
}
if autoSync, ok := Defaults["sync.auto"].(bool); ok { //Compatibility with old key
return autoSync
}
return true
}
func GetSchedulerURL() string {
if u, ok := Config[schedulerURL].(string); ok {
return u
}
return ""
}
func GetConfigWithPrefix(prefix string) map[string]interface{} {
conf := make(map[string]interface{})
for k, v := range Config {
if strings.HasPrefix(k, prefix) {
conf[k] = v
}
}
return conf
}
func getCheckUpgradeFrequency() time.Duration {
if frequency, ok := Config[checkUpgradeFrequencyConfigKey].(int); ok {
return time.Duration(frequency) * time.Hour
}
return 8 * time.Hour
}
/*
Copyright 2017 WALLIX
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 config
import (
"fmt"
"os"
"path/filepath"
"strconv"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/database"
)
var (
AwlessHome = filepath.Join(os.Getenv("HOME"), ".awless")
DBPath = filepath.Join(AwlessHome, database.Filename)
Dir = filepath.Join(AwlessHome, "aws")
KeysDir = filepath.Join(AwlessHome, "keys")
AwlessFirstInstall bool
)
func init() {
os.Setenv("__AWLESS_HOME", AwlessHome)
os.Setenv("__AWLESS_CACHE", filepath.Join(AwlessHome, "cache"))
os.Setenv("__AWLESS_KEYS_DIR", KeysDir)
}
func InitAwlessEnv() error {
_, err := os.Stat(DBPath)
AwlessFirstInstall = os.IsNotExist(err)
os.Setenv("__AWLESS_FIRST_INSTALL", strconv.FormatBool(AwlessFirstInstall))
os.MkdirAll(KeysDir, 0700)
if AwlessFirstInstall {
fmt.Fprint(os.Stderr, AWLESS_ASCII_LOGO)
fmt.Fprintln(os.Stderr, "Welcome! Resolving environment data...")
fmt.Fprintln(os.Stderr)
if err = InitConfig(resolveRequiredConfigFromEnv()); err != nil {
return err
}
err = database.Execute(func(db *database.DB) error {
return db.SetStringValue("current.version", Version)
})
if err != nil {
fmt.Fprintf(os.Stderr, "cannot store current version in db: %s\n", err)
}
}
if err = LoadConfig(); err != nil {
return err
}
return nil
}
func resolveRequiredConfigFromEnv() map[string]string {
region := awsservices.ResolveRegionFromEnv()
resolved := make(map[string]string)
if region != "" {
resolved[RegionConfigKey] = region
}
return resolved
}
/*
Copyright 2017 WALLIX
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 config
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"runtime"
"strconv"
"strings"
"time"
"github.com/wallix/awless/database"
)
const (
lastUpgradeCheckDbKey = "upgrade.lastcheck"
)
func VerifyNewVersionAvailable(url string, messaging io.Writer) error {
return database.Execute(func(db *database.DB) error {
last, err := db.GetTimeValue(lastUpgradeCheckDbKey)
if err != nil {
return err
}
upgradeFreq := getCheckUpgradeFrequency()
if upgradeFreq < 0 {
return nil
}
if time.Since(last) > upgradeFreq {
notifyIfUpgrade(url, messaging)
}
return db.SetTimeValue(lastUpgradeCheckDbKey, time.Now())
})
}
func notifyIfUpgrade(url string, messaging io.Writer) error {
client := &http.Client{Timeout: 1500 * time.Millisecond}
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header.Set("User-Agent", "awless-client-"+Version)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
latest := struct {
Version, URL string
}{}
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&latest); err == nil {
if IsSemverUpgrade(Version, latest.Version) {
var install string
switch BuildFor {
case "brew":
install = "Run `brew upgrade awless`"
case "zip", "targz":
ext := "tar.gz"
if runtime.GOOS == "windows" {
ext = "zip"
}
install = fmt.Sprintf("Run `wget -O awless-%s.%s https://github.com/wallix/awless/releases/download/%s/awless-%s-%s.%s`", latest.Version, ext, latest.Version, runtime.GOOS, runtime.GOARCH, ext)
default:
install = "Run `go get -u github.com/wallix/awless`"
}
fmt.Fprintf(messaging, "New version %s available. Checkout the latest features at https://github.com/wallix/awless/blob/master/CHANGELOG.md\n%s\n", latest.Version, install)
}
}
return nil
}
const semverLen = 3
type semver [semverLen]int
var SemverInvalidFormatErr = errors.New("semver invalid format")
func IsSemverUpgrade(current, latest string) bool {
i, err := CompareSemver(current, latest)
if err != nil {
return false
}
return i < 0
}
func CompareSemver(current, latest string) (int, error) {
current = strings.TrimPrefix(current, "v")
latest = strings.TrimPrefix(latest, "v")
dot := func(r rune) bool {
return r == '.'
}
cFields := strings.FieldsFunc(current, dot)
lFields := strings.FieldsFunc(latest, dot)
if len(cFields) != semverLen || len(lFields) != semverLen {
return 0, SemverInvalidFormatErr
}
currents := new(semver)
for i, f := range cFields {
num, err := strconv.Atoi(f)
if err != nil {
return 0, SemverInvalidFormatErr
}
currents[i] = num
}
latests := new(semver)
for i, f := range lFields {
num, err := strconv.Atoi(f)
if err != nil {
return 0, SemverInvalidFormatErr
}
latests[i] = num
}
for i := 0; i < semverLen; i++ {
if latests[i] > currents[i] {
return -1, nil
} else if latests[i] == currents[i] {
continue
} else {
return 1, nil
}
}
return 0, nil
}
/*
Copyright 2017 WALLIX
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 config
import (
"bytes"
"fmt"
)
const AWLESS_ASCII_LOGO = `
█████╗ ██╗ ██╗ ██╗ ██████ ██████╗ ██████╗
██╔══██╗ ██║ ██║ ██║ ██╔══╝ ██╔═══╝ ██╔═══╝
███████║ ██║ █╗ ██║ ██║ ████╗ ██████ ██████
██╔══██║ ██║███╗██║ ██║ ██╔═╝ ██╗ ██╗
██║ ██║ ╚███╔███╔╝ ██████╗ ██████╗ ██████║ ██████║
╚═╝ ╚═╝ ╚══╝╚══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
`
var (
Version = "v1.0.0"
BuildFor string
buildSha, buildDate, buildArch, buildOS string
)
type BuildInfo struct {
Version, Sha, Date, Arch, OS, For string
}
func (b BuildInfo) String() string {
var buff bytes.Buffer
buff.WriteString(fmt.Sprintf("version=%s", b.Version))
if b.Sha != "" {
buff.WriteString(fmt.Sprintf(", commit=%s", b.Sha))
}
if b.Date != "" {
buff.WriteString(fmt.Sprintf(", build-date=%s", b.Date))
}
if b.Arch != "" {
buff.WriteString(fmt.Sprintf(", build-arch=%s", b.Arch))
}
if b.OS != "" {
buff.WriteString(fmt.Sprintf(", build-os=%s", b.OS))
}
if b.For != "" {
buff.WriteString(fmt.Sprintf(", build-for=%s", b.For))
}
return buff.String()
}
var CurrentBuildInfo = BuildInfo{
Version: Version,
For: BuildFor,
Sha: buildSha,
Date: buildDate,
Arch: buildArch,
OS: buildOS,
}
package console
import (
"bytes"
"strings"
)
const wrapingChars = " \n.,?;:/+=*-_°)(\"!@#"
type autoWraper struct {
maxWidth int
wrappingChar string
}
func (aw autoWraper) Wrap(input string) string {
wrappingChar := aw.wrappingChar
if wrappingChar == "" {
wrappingChar = " "
}
splits := strings.Split(input, wrappingChar)
var output bytes.Buffer
for i, split := range splits {
if len(split) <= aw.maxWidth {
output.WriteString(split)
} else {
var toWrite bytes.Buffer
var buff bytes.Buffer
for _, c := range split {
if toWrite.Len() != 0 {
if buff.Len()+toWrite.Len() >= aw.maxWidth {
output.WriteString(toWrite.String())
toWrite.Reset()
output.WriteString(wrappingChar)
}
}
if buff.Len() >= aw.maxWidth {
output.WriteString(buff.String())
buff.Reset()
output.WriteString(wrappingChar)
}
buff.WriteRune(c)
if strings.ContainsRune(wrapingChars, c) {
toWrite.WriteString(buff.String())
buff.Reset()
}
}
output.WriteString(toWrite.String())
output.WriteString(buff.String())
}
if i < len(splits)-1 {
output.WriteString(wrappingChar)
}
}
return output.String()
}
/*
Copyright 2017 WALLIX
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 console
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"reflect"
"sort"
"strings"
"time"
"github.com/fatih/color"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/pkg/twwarp"
"github.com/olekukonko/tablewriter/pkg/twwidth"
"github.com/olekukonko/tablewriter/tw"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/match"
"github.com/wallix/awless/graph"
)
var (
tableColWidth = 30
autowrapMaxSize = 35
)
type Displayer interface {
Print(io.Writer) error
}
type sorter interface {
sort(table)
columns() []int
symbol() string
}
type Builder struct {
filters []string
tagFilters []string
tagKeyFilters []string
tagValueFilters []string
columnDefinitions []ColumnDefinition
format string
rdfType string
sort []int
reverseSort bool
maxwidth int
dataSource interface{}
root cloud.Resource
noHeaders bool
}
func (b *Builder) SetSource(i interface{}) *Builder {
b.dataSource = i
return b
}
func (b *Builder) buildQuery() (cloud.Query, error) {
var matchers []cloud.Matcher
for _, f := range b.filters {
splits := strings.SplitN(f, "=", 2)
if len(splits) == 2 {
name, val := strings.TrimSpace(strings.Title(splits[0])), strings.TrimSpace(splits[1])
key := ColumnDefinitions(b.columnDefinitions).resolveKey(name)
if key != "" {
matchers = append(matchers, match.Property(key, val).IgnoreCase().MatchString())
} else {
var allowed []string
for _, h := range b.columnDefinitions {
allowed = append(allowed, h.propKey())
}
return cloud.Query{}, fmt.Errorf("Invalid filter key '%s'. Expecting any of: %s. (Note: filter keys/values are case insensitive)", name, strings.Join(allowed, ", "))
}
}
}
for _, f := range b.tagFilters {
splits := strings.SplitN(f, "=", 2)
if len(splits) == 2 {
key, val := strings.TrimSpace(splits[0]), strings.TrimSpace(splits[1])
matchers = append(matchers, match.Tag(key, val))
}
}
for _, k := range b.tagKeyFilters {
matchers = append(matchers, match.TagKey(k))
}
for _, v := range b.tagValueFilters {
matchers = append(matchers, match.TagValue(v))
}
q := cloud.NewQuery(b.rdfType)
if len(matchers) > 0 {
q = cloud.NewQuery(b.rdfType).Match(match.And(matchers...))
}
return q, nil
}
func (b *Builder) Build() (Displayer, error) {
base := fromGraphDisplayer{sorter: &defaultSorter{sortBy: b.sort, descending: b.reverseSort}, rdfType: b.rdfType, columnDefinitions: b.columnDefinitions, maxwidth: b.maxwidth, noHeaders: b.noHeaders}
switch b.dataSource.(type) {
case cloud.GraphAPI:
if b.rdfType == "" {
gph := b.dataSource.(cloud.GraphAPI)
switch b.format {
case "table":
dis := &multiResourcesTableDisplayer{base}
dis.setGraph(gph)
return dis, nil
case "json":
dis := &multiResourcesJSONDisplayer{base}
dis.setGraph(gph)
return dis, nil
case "porcelain":
dis := &porcelainDisplayer{base}
dis.setGraph(gph)
return dis, nil
default:
fmt.Fprintf(os.Stderr, "unknown format '%s', display as 'table'\n", b.format)
dis := &multiResourcesTableDisplayer{base}
dis.setGraph(gph)
return dis, nil
}
}
filteredGraph := b.dataSource.(cloud.GraphAPI)
q, err := b.buildQuery()
if err != nil {
return nil, err
}
if filteredGraph, err = filteredGraph.FilterGraph(q); err != nil {
return nil, err
}
switch b.format {
case "csv":
dis := &csvDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
case "tsv":
dis := &tsvDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
case "json":
dis := &jsonDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
case "porcelain":
dis := &porcelainDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
case "table":
dis := &tableDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
default:
fmt.Fprintf(os.Stderr, "unknown format '%s', display as 'table'\n", b.format)
dis := &tableDisplayer{base}
dis.setGraph(filteredGraph)
return dis, nil
}
case cloud.Resource:
dis := &tableResourceDisplayer{columnDefinitions: b.columnDefinitions, maxwidth: b.maxwidth}
dis.SetResource(b.dataSource.(cloud.Resource))
return dis, nil
case *graph.Diff:
base := fromDiffDisplayer{root: b.root}
switch b.format {
case "tree":
dis := &diffTreeDisplayer{&base}
dis.SetDiff(b.dataSource.(*graph.Diff))
return dis, nil
case "table":
dis := &diffTableDisplayer{&base}
dis.SetDiff(b.dataSource.(*graph.Diff))
return dis, nil
default:
fmt.Fprintf(os.Stderr, "unknown format '%s', display as 'tree'\n", b.format)
dis := &diffTreeDisplayer{&base}
dis.SetDiff(b.dataSource.(*graph.Diff))
return dis, nil
}
}
return nil, nil
}
type optsFn func(b *Builder) *Builder
func BuildOptions(opts ...optsFn) *Builder {
b := &Builder{}
b.sort = []int{0}
b.format = "table"
for _, fn := range opts {
fn(b)
}
if len(b.columnDefinitions) == 0 {
b.columnDefinitions = DefaultsColumnDefinitions[b.rdfType]
}
return b
}
func WithFormat(format string) optsFn {
return func(b *Builder) *Builder {
b.format = format
return b
}
}
func WithColumns(properties []string) optsFn {
return func(b *Builder) *Builder {
if len(properties) == 0 {
properties = ColumnsInListing[b.rdfType]
}
var columns []ColumnDefinition
for _, p := range properties {
var found bool
for _, definition := range DefaultsColumnDefinitions[b.rdfType] {
if strings.EqualFold(p, definition.propKey()) || strings.EqualFold(p, definition.title()) {
found = true
columns = append(columns, definition)
continue
}
}
if !found {
columns = append(columns, StringColumnDefinition{Prop: strings.Title(p)})
}
}
b.columnDefinitions = columns
return b
}
}
func WithColumnDefinitions(definitions []ColumnDefinition) optsFn {
return func(b *Builder) *Builder {
b.columnDefinitions = definitions
return b
}
}
func WithFilters(fs []string) optsFn {
return func(b *Builder) *Builder {
b.filters = fs
return b
}
}
func WithTagFilters(fs []string) optsFn {
return func(b *Builder) *Builder {
b.tagFilters = fs
return b
}
}
func WithTagKeyFilters(fs []string) optsFn {
return func(b *Builder) *Builder {
b.tagKeyFilters = fs
return b
}
}
func WithTagValueFilters(fs []string) optsFn {
return func(b *Builder) *Builder {
b.tagValueFilters = fs
return b
}
}
func WithIDsOnly(only bool) optsFn {
return func(b *Builder) *Builder {
if only {
b.columnDefinitions = []ColumnDefinition{
&StringColumnDefinition{Prop: "ID"},
&StringColumnDefinition{Prop: "Name"},
}
b.format = "porcelain"
}
return b
}
}
func WithSortBy(sortingBy ...string) optsFn {
return func(b *Builder) *Builder {
indexes, err := resolveSortIndexes(b.columnDefinitions, sortingBy...)
if err != nil {
fmt.Fprint(os.Stderr, err, "\n")
}
b.sort = indexes
return b
}
}
func WithReverseSort(r bool) optsFn {
return func(b *Builder) *Builder {
b.reverseSort = r
return b
}
}
func WithMaxWidth(maxwidth int) optsFn {
return func(b *Builder) *Builder {
b.maxwidth = maxwidth
return b
}
}
func WithRdfType(rdfType string) optsFn {
return func(b *Builder) *Builder {
b.rdfType = rdfType
return b
}
}
func WithRootNode(root cloud.Resource) optsFn {
return func(b *Builder) *Builder {
b.root = root
return b
}
}
func WithNoHeaders(nh bool) optsFn {
return func(b *Builder) *Builder {
b.noHeaders = nh
return b
}
}
type table [][]interface{}
type fromGraphDisplayer struct {
sorter
g cloud.GraphAPI
rdfType string
columnDefinitions []ColumnDefinition
maxwidth int
noHeaders bool
}
func (d *fromGraphDisplayer) setGraph(g cloud.GraphAPI) {
d.g = g
}
type csvDisplayer struct {
fromGraphDisplayer
}
func (d *csvDisplayer) Print(w io.Writer) error {
resources, err := d.g.Find(cloud.NewQuery(d.rdfType))
if err != nil {
return err
}
if len(d.columnDefinitions) == 0 {
return nil
}
values := make(table, len(resources))
for i, res := range resources {
if v := values[i]; v == nil {
values[i] = make([]interface{}, len(d.columnDefinitions))
}
for j, h := range d.columnDefinitions {
values[i][j] = res.Properties()[h.propKey()]
}
}
d.sorter.sort(values)
var buff bytes.Buffer
var head []string
for _, h := range d.columnDefinitions {
head = append(head, h.title())
}
if !d.noHeaders {
buff.WriteString(strings.Join(head, ",") + "\n")
}
for i := range values {
var props []string
for j, h := range d.columnDefinitions {
val := h.format(values[i][j])
if strings.ContainsAny(val, ",\n\"") {
val = strings.Replace(val, "\"", "\"\"", -1) // Replace " in val by "" (cf https://tools.ietf.org/html/rfc4180)
val = "\"" + val + "\""
}
props = append(props, val)
}
buff.WriteString(strings.Join(props, ",") + "\n")
}
_, err = w.Write(buff.Bytes())
return err
}
type tsvDisplayer struct {
fromGraphDisplayer
}
func (d *tsvDisplayer) Print(w io.Writer) error {
color.NoColor = true // as default tabwriter does not play nice with the color library
resources, err := d.g.Find(cloud.NewQuery(d.rdfType))
if err != nil {
return err
}
if len(d.columnDefinitions) == 0 {
return nil
}
values := make(table, len(resources))
for i, res := range resources {
if v := values[i]; v == nil {
values[i] = make([]interface{}, len(d.columnDefinitions))
}
for j, h := range d.columnDefinitions {
values[i][j] = res.Properties()[h.propKey()]
}
}
d.sorter.sort(values)
var head []string
for _, h := range d.columnDefinitions {
head = append(head, h.title())
}
if !d.noHeaders {
fmt.Fprintln(w, strings.Join(head, "\t"))
}
for i := range values {
var props []string
for j, h := range d.columnDefinitions {
props = append(props, h.format(values[i][j]))
}
fmt.Fprintln(w, strings.Join(props, "\t"))
}
return nil
}
type jsonDisplayer struct {
fromGraphDisplayer
}
func (d *jsonDisplayer) Print(w io.Writer) error {
resources, err := d.g.Find(cloud.NewQuery(d.rdfType))
if err != nil {
return err
}
sort.Slice(resources, func(i, j int) bool { return resources[i].Id() < resources[j].Id() })
var props []map[string]interface{}
for _, res := range resources {
props = append(props, res.Properties())
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(props)
}
type tableDisplayer struct {
fromGraphDisplayer
}
func (d *tableDisplayer) Print(w io.Writer) error {
resources, err := d.g.Find(cloud.NewQuery(d.rdfType))
if err != nil {
return err
}
if len(resources) == 0 {
w.Write([]byte("No results found.\n"))
return nil
}
if len(d.columnDefinitions) == 0 {
w.Write([]byte("No columns to display.\n"))
return nil
}
values := make(table, len(resources))
for i, res := range resources {
if v := values[i]; v == nil {
values[i] = make([]interface{}, len(d.columnDefinitions))
}
for j, h := range d.columnDefinitions {
values[i][j] = res.Properties()[h.propKey()]
}
}
d.sorter.sort(values)
markColumnAsc := -1
if len(d.sorter.columns()) > 0 {
markColumnAsc = d.sorter.columns()[0]
}
columnsToDisplay := d.columnDefinitions
maxWidthNoWraping := 1
if d.maxwidth != 0 {
columnsToDisplay = []ColumnDefinition{}
currentWidth := 1 // first border
for j, h := range d.columnDefinitions {
var symbol string
if markColumnAsc == j {
symbol = d.sorter.symbol()
}
colW := colWidth(j, values, h, symbol) + 3 // +3 (tables margin + border)
if currentWidth+colW > d.maxwidth {
break
}
currentWidth += colW
maxWidthNoWraping += colWidthNoWraping(j, values, h, symbol) + 3
columnsToDisplay = append(columnsToDisplay, h)
}
}
table := tablewriter.NewWriter(w)
table.Options(
tablewriter.WithRendition(tw.Rendition{
Borders: tw.Border{Left: tw.On, Top: tw.Off, Right: tw.On, Bottom: tw.Off},
Symbols: tw.NewSymbols(tw.StyleMarkdown),
}),
tablewriter.WithRowAlignment(tw.AlignLeft),
tablewriter.WithRowMaxWidth(tableColWidth),
tablewriter.WithHeaderAutoFormat(tw.Off),
)
if !d.noHeaders {
var displayHeaders []string
for i, h := range columnsToDisplay {
var symbol string
if markColumnAsc == i {
symbol = d.sorter.symbol()
}
displayHeaders = append(displayHeaders, h.title(symbol))
}
table.Header(displayHeaders)
}
var enableWraping bool
if d.maxwidth > 0 && d.maxwidth <= maxWidthNoWraping {
enableWraping = true
}
wraper := autoWraper{maxWidth: autowrapMaxSize, wrappingChar: " "}
for i := range values {
var props []string
for j, h := range columnsToDisplay {
val := h.format(values[i][j])
if enableWraping {
props = append(props, wraper.Wrap(val))
} else {
props = append(props, val)
}
}
table.Append(props)
}
table.Render()
if len(columnsToDisplay) < len(d.columnDefinitions) {
var hiddenColumns []string
for i := len(columnsToDisplay); i < len(d.columnDefinitions); i++ {
hiddenColumns = append(hiddenColumns, "'"+d.columnDefinitions[i].title()+"'")
}
if len(hiddenColumns) == 1 {
fmt.Fprint(w, color.New(color.FgRed).SprintfFunc()("Column truncated to fit terminal: %s\n", hiddenColumns[0]))
} else {
fmt.Fprint(w, color.New(color.FgRed).SprintfFunc()("Columns truncated to fit terminal: %s\n", strings.Join(hiddenColumns, ", ")))
}
}
return nil
}
type porcelainDisplayer struct {
fromGraphDisplayer
}
func (d *porcelainDisplayer) Print(w io.Writer) error {
var types []string
if d.rdfType == "" {
for t := range DefaultsColumnDefinitions {
types = append(types, t)
}
} else {
types = append(types, d.rdfType)
}
var values table
for _, t := range types {
resources, err := d.g.Find(cloud.NewQuery(t))
if err != nil {
return err
}
for _, res := range resources {
var row = make([]interface{}, len(d.columnDefinitions))
for j, h := range d.columnDefinitions {
row[j] = res.Properties()[h.propKey()]
}
values = append(values, row)
}
}
d.sorter.sort(values)
var lines []string
for i := range values {
for j := range d.columnDefinitions {
v := values[i][j]
if v != nil {
val := fmt.Sprint(v)
if val != "" {
lines = append(lines, val)
}
}
}
}
_, err := w.Write([]byte(strings.Join(lines, "\n")))
return err
}
type multiResourcesTableDisplayer struct {
fromGraphDisplayer
}
func (d *multiResourcesTableDisplayer) Print(w io.Writer) error {
var values table
for t, propDefs := range DefaultsColumnDefinitions {
resources, err := d.g.Find(cloud.NewQuery(t))
if err != nil {
return err
}
for _, res := range resources {
for prop, val := range res.Properties() {
var header ColumnDefinition
for _, h := range propDefs {
if h.propKey() == prop {
header = h
}
}
if header == nil {
header = &StringColumnDefinition{Prop: prop}
}
var row [4]interface{}
row[0] = t
row[1] = nameOrID(res)
row[2] = header.title()
row[3] = header.format(val)
values = append(values, row[:])
}
}
}
ds := defaultSorter{sortBy: []int{0, 1, 2, 3}}
ds.sort(values)
table := tablewriter.NewWriter(w)
table.Options(
tablewriter.WithRowMergeMode(tw.MergeVertical),
tablewriter.WithRowAlignment(tw.AlignLeft),
tablewriter.WithRowMaxWidth(tableColWidth),
tablewriter.WithHeaderMaxWidth(tableColWidth),
tablewriter.WithRendition(tw.Rendition{
Borders: tw.Border{Left: tw.On, Top: tw.Off, Right: tw.On, Bottom: tw.Off},
Symbols: tw.NewSymbols(tw.StyleMarkdown),
}),
tablewriter.WithHeaderAutoFormat(tw.Off),
)
table.Header([]string{"Type" + ds.symbol(), "Name/Id", "Property", "Value"})
wraper := autoWraper{maxWidth: autowrapMaxSize, wrappingChar: " "}
for i := range values {
row := make([]string, len(values[i]))
for j := range values[i] {
row[j] = wraper.Wrap(fmt.Sprint(values[i][j]))
}
table.Append(row)
}
table.Render()
return nil
}
type multiResourcesJSONDisplayer struct {
fromGraphDisplayer
}
func (d *multiResourcesJSONDisplayer) Print(w io.Writer) error {
var resources []cloud.Resource
var err error
all := make(map[string]interface{})
for t := range DefaultsColumnDefinitions {
resources, err = d.g.Find(cloud.NewQuery(t))
if err != nil {
return err
}
var props []map[string]interface{}
for _, res := range resources {
props = append(props, res.Properties())
}
if len(resources) > 0 {
all[cloud.PluralizeResource(t)] = props
}
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(all)
}
type fromDiffDisplayer struct {
root cloud.Resource
diff *graph.Diff
}
func (d *fromDiffDisplayer) SetDiff(diff *graph.Diff) {
d.diff = diff
}
type diffTableDisplayer struct {
*fromDiffDisplayer
}
func (d *diffTableDisplayer) Print(w io.Writer) error {
var values table
fromCommons := make(map[string]cloud.Resource)
toCommons := make(map[string]cloud.Resource)
each := func(res *graph.Resource, distance int) error {
diff, _ := res.Meta("diff")
switch diff {
case "extra":
values = append(values, []interface{}{
res.Type(), color.New(color.FgRed).SprintFunc()("- " + nameOrID(res)), "", "",
})
default:
fromCommons[res.Id()] = res
}
return nil
}
err := d.diff.FromGraph().Accept(&graph.ChildrenVisitor{From: d.root.(*graph.Resource), Each: each})
if err != nil {
return err
}
each = func(res *graph.Resource, distance int) error {
meta, _ := res.Meta("diff")
switch meta {
case "extra":
values = append(values, []interface{}{
res.Type(), color.New(color.FgGreen).SprintFunc()("+ " + nameOrID(res)), "", "",
})
default:
toCommons[res.Id()] = res
}
return nil
}
err = d.diff.ToGraph().Accept(&graph.ChildrenVisitor{From: d.root.(*graph.Resource), Each: each})
if err != nil {
return err
}
for _, common := range fromCommons {
resType := common.Type()
naming := nameOrID(common)
if rem, ok := toCommons[common.Id()]; ok {
added := graph.Subtract(rem.Properties(), common.Properties())
for k, v := range added {
values = append(values, []interface{}{
resType, naming, k, color.New(color.FgGreen).SprintFunc()("+ " + fmt.Sprint(v)),
})
}
deleted := graph.Subtract(common.Properties(), rem.Properties())
for k, v := range deleted {
values = append(values, []interface{}{
resType, naming, k, color.New(color.FgRed).SprintFunc()("- " + fmt.Sprint(v)),
})
}
}
}
ds := defaultSorter{sortBy: []int{0, 1, 2, 3}}
ds.sort(values)
table := tablewriter.NewWriter(w)
table.Options(
tablewriter.WithRowMergeMode(tw.MergeVertical),
tablewriter.WithRowAlignment(tw.AlignLeft),
tablewriter.WithRendition(tw.Rendition{
Borders: tw.Border{Left: tw.On, Top: tw.Off, Right: tw.On, Bottom: tw.Off},
Symbols: tw.NewSymbols(tw.StyleMarkdown),
}),
tablewriter.WithHeaderAutoFormat(tw.Off),
)
table.Header([]string{"Type" + ds.symbol(), "Name/Id", "Property", "Value"})
for i := range values {
row := make([]string, len(values[i]))
for j := range values[i] {
row[j] = fmt.Sprint(values[i][j])
}
table.Append(row)
}
table.Render()
return nil
}
type diffTreeDisplayer struct {
*fromDiffDisplayer
}
func (d *diffTreeDisplayer) Print(w io.Writer) error {
g := graph.NewGraph()
each := func(res *graph.Resource, distance int) error {
meta, _ := res.Meta("diff")
switch meta {
case "extra", "missing":
var parents []*graph.Resource
err := d.diff.MergedGraph().Accept(&graph.ParentsVisitor{From: res, Each: graph.VisitorCollectFunc(&parents)})
if err != nil {
return err
}
if err := g.AddResource(res); err != nil {
return err
}
previous := res
for _, parent := range parents {
if err := g.AddResource(parent); err != nil {
return err
}
if err := g.AddParentRelation(parent, previous); err != nil {
return err
}
previous = parent
}
}
return nil
}
err := d.diff.MergedGraph().Accept(&graph.ChildrenVisitor{From: d.root.(*graph.Resource), Each: each, IncludeFrom: true})
if err != nil {
return err
}
each = func(res *graph.Resource, distance int) error {
tabs := strings.Repeat("\t", distance)
diffMeta, _ := res.Meta("diff")
switch diffMeta {
case "extra":
color.Set(color.FgGreen)
fmt.Fprintf(w, "+%s%s, %s\n", tabs, res.Type(), res.Id())
color.Unset()
case "missing":
color.Set(color.FgRed)
fmt.Fprintf(w, "-%s%s, %s\n", tabs, res.Type(), res.Id())
color.Unset()
default:
fmt.Fprintf(w, "%s%s, %s\n", tabs, res.Type(), res.Id())
}
return nil
}
err = g.Accept(&graph.ChildrenVisitor{From: d.root.(*graph.Resource), Each: each, IncludeFrom: true})
if err != nil {
return err
}
return nil
}
type defaultSorter struct {
sortBy []int
descending bool
}
func (d *defaultSorter) sort(lines table) {
var compare func(i, j int) bool
if d.descending {
compare = func(j, i int) bool {
for _, col := range d.sortBy {
if reflect.DeepEqual(lines[i][col], lines[j][col]) {
continue
}
return valueLowerOrEqual(lines[i][col], lines[j][col])
}
return false
}
} else {
compare = func(i, j int) bool {
for _, col := range d.sortBy {
if reflect.DeepEqual(lines[i][col], lines[j][col]) {
continue
}
return valueLowerOrEqual(lines[i][col], lines[j][col])
}
return false
}
}
sort.Slice(lines, compare)
}
func (d *defaultSorter) columns() []int {
return d.sortBy
}
func (d *defaultSorter) symbol() string {
if d.descending {
return " ▼"
}
return " ▲"
}
func valueLowerOrEqual(a, b interface{}) bool {
if a == nil && b == nil {
return true
}
if aTyp, bTyp := reflect.TypeOf(a), reflect.TypeOf(b); aTyp != nil &&
aTyp.Comparable() && bTyp != nil && bTyp.Comparable() && a == b {
return true
}
if a == nil {
return true
}
if b == nil {
return false
}
if reflect.TypeOf(a) != reflect.TypeOf(b) {
panic(fmt.Sprintf("can not compare values of type %T and %T", a, b))
}
switch a.(type) {
case int:
aa := a.(int)
bb := b.(int)
return aa <= bb
case float64:
aa := a.(float64)
bb := b.(float64)
return aa <= bb
case string:
aa := a.(string)
bb := b.(string)
return aa <= bb
case time.Time:
aa := a.(time.Time)
bb := b.(time.Time)
return aa.After(bb)
case bool:
aa := a.(bool)
bb := b.(bool)
return !aa || bb
case []string, []int:
return fmt.Sprint(a) <= fmt.Sprint(b)
default:
panic(fmt.Sprintf("can not compare values of type %T", a))
}
}
func resolveSortIndexes(headers []ColumnDefinition, sortingBy ...string) ([]int, error) {
sortBy := []string{"id"}
if len(sortingBy) > 0 {
sortBy = sortingBy
}
normalized := make(map[string]int)
for i, h := range headers {
normalized[strings.ToLower(h.title())] = i
}
var ids []int
for _, t := range sortBy {
id, ok := normalized[strings.ToLower(t)]
if !ok && strings.ToLower(t) != "id" {
return ids, fmt.Errorf("Invalid column name '%s'", t)
}
ids = append(ids, id)
}
return ids, nil
}
func colWidth(j int, t table, h ColumnDefinition, sortSymbol string) int {
max := twwidth.Width(h.title(sortSymbol))
wraper := autoWraper{maxWidth: autowrapMaxSize, wrappingChar: " "}
for i := range t {
val := wraper.Wrap(h.format(t[i][j]))
valLen := twwidth.Width(val)
if valLen > tableColWidth {
if tableColWidth > max {
max = tableColWidth
}
}
lines, _ := twwarp.WrapString(val, tableColWidth)
for _, line := range lines {
width := twwidth.Width(line)
if width > max {
max = width
}
}
}
return max
}
func colWidthNoWraping(j int, t table, h ColumnDefinition, sortSymbol string) int {
max := twwidth.Width(h.title(sortSymbol))
for i := range t {
val := h.format(t[i][j])
valLen := twwidth.Width(val)
if valLen > tableColWidth {
if tableColWidth > max {
max = tableColWidth
}
}
lines, _ := twwarp.WrapString(val, tableColWidth)
for _, line := range lines {
width := twwidth.Width(line)
if width > max {
max = width
}
}
}
return max
}
func nameOrID(res cloud.Resource) string {
if name, ok := res.Property("Name"); ok && name != "" {
return fmt.Sprint(name)
}
if id, ok := res.Property("Id"); ok && id != "" {
return fmt.Sprint(id)
}
return res.Id()
}
/*
Copyright 2017 WALLIX
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 console
import (
"bytes"
"fmt"
"reflect"
"strings"
"time"
"github.com/fatih/color"
"github.com/wallix/awless/graph"
)
type TimeFormat int
const (
Humanize TimeFormat = iota
Basic
Short
)
type ColumnDefinition interface {
propKey() string
title(...string) string
format(i interface{}) string
}
type ColumnDefinitions []ColumnDefinition
func (d ColumnDefinitions) resolveKey(name string) string {
low := strings.ToLower(name)
for _, def := range d {
switch low {
case strings.ToLower(def.propKey()), strings.ToLower(def.title()):
return def.propKey()
}
}
return ""
}
type StringColumnDefinition struct {
Prop, Friendly string
}
func (h StringColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
return fmt.Sprint(i)
}
func (h StringColumnDefinition) propKey() string { return h.Prop }
func (h StringColumnDefinition) title(suffix ...string) string {
t := h.Friendly
if t == "" {
t = h.Prop
}
if len(suffix) > 0 {
t += suffix[0]
}
return t
}
type ColoredValueColumnDefinition struct {
StringColumnDefinition
ColoredValues map[string]color.Attribute
}
func (h ColoredValueColumnDefinition) format(i interface{}) string {
str := h.StringColumnDefinition.format(i)
col, ok := h.ColoredValues[str]
if ok {
return color.New(col).SprintFunc()(str)
}
return str
}
type ARNLastValueColumnDefinition struct {
StringColumnDefinition
Separator string
}
func (h ARNLastValueColumnDefinition) format(i interface{}) string {
str := h.StringColumnDefinition.format(i)
splits := strings.Split(str, h.Separator)
if len(splits) > 1 {
return splits[len(splits)-1]
}
return str
}
func ToShortArn(s string) string {
index := strings.LastIndex(s, ":")
if index > 0 {
return s[index+1:]
}
return s
}
type SliceColumnDefinition struct {
ForEach func(string) string
StringColumnDefinition
}
func (h SliceColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
value := reflect.ValueOf(i)
if value.Kind() != reflect.Slice {
return fmt.Sprintf("invalid slice: %T", i)
}
var buf bytes.Buffer
for i := 0; i < value.Len(); i++ {
s := fmt.Sprint(value.Index(i).Interface())
if h.ForEach != nil {
s = h.ForEach(s)
}
buf.WriteString(s)
if i < value.Len()-1 {
buf.WriteRune(' ')
}
}
return buf.String()
}
type KeyValuesColumnDefinition struct {
StringColumnDefinition
}
func (h KeyValuesColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
ii, ok := i.([]*graph.KeyValue)
if !ok {
return fmt.Sprintf("invalid keyvalue, got %T", i)
}
var b bytes.Buffer
for i, kv := range ii {
b.WriteString(fmt.Sprintf("%s:%s", color.CyanString(kv.KeyName), kv.Value))
if i < len(ii)-1 {
b.WriteString(" ")
}
}
return b.String()
}
type TimeColumnDefinition struct {
StringColumnDefinition
Format TimeFormat
}
func (h TimeColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
ii, ok := i.(time.Time)
if !ok {
return "invalid time"
}
switch h.Format {
case Humanize:
return HumanizeTime(ii)
case Short:
return ii.Format("1/2/06 15:04")
default:
return ii.Format("Mon, Jan 2, 2006 15:04")
}
}
type StorageColumnDefinition struct {
StringColumnDefinition
Unit storageUnit
}
func (h StorageColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
val := reflect.ValueOf(i)
if val.Kind() == reflect.Uint || val.Kind() == reflect.Uint64 {
return HumanizeStorage(val.Uint(), h.Unit)
}
if val.Kind() == reflect.Int || val.Kind() == reflect.Int64 {
return HumanizeStorage(uint64(val.Int()), h.Unit)
}
return "invalid size"
}
type FirewallRulesColumnDefinition struct {
StringColumnDefinition
}
func (h FirewallRulesColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
ii, ok := i.([]*graph.FirewallRule)
if !ok {
return "invalid rules"
}
var w bytes.Buffer
for _, r := range ii {
w.WriteString("[")
var netStrings []string
for _, net := range r.IPRanges {
netStrings = append(netStrings, net.String())
}
netStrings = append(netStrings, r.Sources...)
w.WriteString(strings.Join(netStrings, ";"))
w.WriteString("](")
switch {
case r.Protocol == "any":
w.WriteString(r.Protocol)
case r.PortRange.Any:
w.WriteString(fmt.Sprintf("%s:any", r.Protocol))
case r.PortRange.FromPort == r.PortRange.ToPort:
w.WriteString(fmt.Sprintf("%s:%d", r.Protocol, r.PortRange.FromPort))
default:
w.WriteString(fmt.Sprintf("%s:%d-%d", r.Protocol, r.PortRange.FromPort, r.PortRange.ToPort))
}
w.WriteString(") ")
}
return w.String()
}
type RoutesColumnDefinition struct {
StringColumnDefinition
}
func (h RoutesColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
ii, ok := i.([]*graph.Route)
if !ok {
return "invalid routes"
}
var w bytes.Buffer
for _, r := range ii {
if r.Destination != nil {
w.WriteString(r.Destination.String())
}
if r.DestinationIPv6 != nil && r.Destination != nil {
w.WriteString("+")
}
if r.DestinationIPv6 != nil {
w.WriteString(r.DestinationIPv6.String())
}
w.WriteString("->")
if len(r.Targets) > 1 {
w.WriteString("[")
}
for _, t := range r.Targets {
switch t.Type {
case graph.EgressOnlyInternetGatewayTarget:
w.WriteString("inbound-internget-gw")
case graph.GatewayTarget:
w.WriteString("gw")
case graph.InstanceTarget:
w.WriteString("inst")
case graph.NatTarget:
w.WriteString("nat")
case graph.NetworkInterfaceTarget:
w.WriteString("ni")
case graph.VpcPeeringConnectionTarget:
w.WriteString("vpc")
default:
w.WriteString("unknown")
}
w.WriteString(":")
w.WriteString(t.Ref)
w.WriteString(" ")
}
if len(r.Targets) > 1 {
w.WriteString("] ")
}
}
return w.String()
}
type GrantsColumnDefinition struct {
StringColumnDefinition
}
func (h GrantsColumnDefinition) format(i interface{}) string {
if i == nil {
return ""
}
ii, ok := i.([]*graph.Grant)
if !ok {
return "invalid grants"
}
var w bytes.Buffer
for _, g := range ii {
w.WriteString(g.Permission)
w.WriteString("[")
switch g.Grantee.GranteeType {
case "CanonicalUser":
w.WriteString("user:")
if g.Grantee.GranteeDisplayName != "" {
w.WriteString(g.Grantee.GranteeDisplayName)
} else {
w.WriteString(g.Grantee.GranteeID)
}
case "Group":
w.WriteString("group:")
w.WriteString(g.Grantee.GranteeID)
default:
w.WriteString(g.Grantee.GranteeType)
w.WriteString(":")
w.WriteString(g.Grantee.GranteeID)
}
w.WriteString("]")
w.WriteString(" ")
}
return w.String()
}
/*
Copyright 2017 WALLIX
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 console
import (
"fmt"
"time"
)
var globalNow = time.Now().UTC()
func HumanizeTime(t time.Time) string {
d := globalNow.Sub(t)
switch {
case d.Seconds() <= time.Second.Seconds():
return "now"
case d.Seconds() <= 2*60*time.Second.Seconds():
return fmt.Sprintf("%d secs", int(d.Seconds()))
case d.Seconds() <= 2*60*time.Minute.Seconds():
return fmt.Sprintf("%d mins", int(d.Minutes()))
case d.Seconds() <= 2*24*time.Hour.Seconds():
return fmt.Sprintf("%d hours", int(d.Hours()))
case d.Seconds() <= 2*7*24*time.Hour.Seconds():
return fmt.Sprintf("%d days", int(d.Hours()/24))
case d.Seconds() <= 2*30*24*time.Hour.Seconds():
return fmt.Sprintf("%d weeks", int(d.Hours()/(24*7)))
case d.Seconds() <= 2*365*24*time.Hour.Seconds():
return fmt.Sprintf("%d months", int(d.Hours()/(24*30)))
default:
return fmt.Sprintf("%d years", int(d.Hours()/(24*365)))
}
}
type storageUnit uint
const (
b storageUnit = iota
kb
mb
gb
)
func HumanizeStorage(nb uint64, unit storageUnit) string {
var nbBytes uint64
switch unit {
case b:
nbBytes = nb
case kb:
nbBytes = nb * 1024
case mb:
nbBytes = nb * 1024 * 1024
case gb:
nbBytes = nb * 1024 * 1024 * 1024
default:
return "invalid storage unit"
}
switch {
case nbBytes < 1024:
return fmt.Sprintf("%dB", nbBytes)
case nbBytes < 1024*1024:
return fmt.Sprintf("%sK", divideValue(nbBytes, 1024))
case nbBytes < 1024*1024*1024:
return fmt.Sprintf("%sM", divideValue(nbBytes, 1024*1024))
default:
return fmt.Sprintf("%sG", divideValue(nbBytes, 1024*1024*1024))
}
}
func divideValue(from, by uint64) string {
res := from / by
if from%by != 0 {
return fmt.Sprintf("~%d", res)
}
return fmt.Sprint(res)
}
/*
Copyright 2017 WALLIX
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 console
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"syscall"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
var askPasswordFunc func() ([]byte, error) = func() ([]byte, error) {
fmt.Fprint(os.Stderr, "This SSH key will be encrypted. Please enter new password:")
for {
pass, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert // needed for Windows where syscall.Stdin is uintptr
if err != nil {
return pass, err
}
fmt.Fprint(os.Stderr, "\nConfirm password:")
pass2, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert // needed for Windows where syscall.Stdin is uintptr
if err != nil {
return pass, err
}
if !bytes.Equal(pass, pass2) {
fmt.Fprint(os.Stderr, "\nPasswords are different. Please enter new password:")
continue
}
fmt.Fprintln(os.Stderr)
return pass, nil
}
}
var GenerateSSHKeyPair = func(size int, encryptKey bool) ([]byte, []byte, error) {
key, err := rsa.GenerateKey(rand.Reader, size)
if err != nil {
return nil, nil, err
}
var pemBlock *pem.Block
var passwd []byte
if encryptKey {
passwd, err = askPasswordFunc()
if err != nil {
return nil, nil, err
}
if len(passwd) == 0 {
fmt.Fprintln(os.Stderr, "Empty password given, the keypair will not be encrypted.")
}
}
if len(passwd) != 0 {
pemBlock, err = x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(key), passwd, x509.PEMCipherAES256)
if err != nil {
return nil, nil, err
}
} else {
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
}
privPem := pem.EncodeToMemory(pemBlock)
sshPub, err := ssh.NewPublicKey(&key.PublicKey)
if err != nil {
return nil, nil, err
}
return ssh.MarshalAuthorizedKey(sshPub), privPem, nil
}
/*
Copyright 2017 WALLIX
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 console
import (
"fmt"
"io"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/tw"
"github.com/wallix/awless/cloud"
)
type tableResourceDisplayer struct {
maxwidth int
r cloud.Resource
columnDefinitions []ColumnDefinition
}
func (d *tableResourceDisplayer) Print(w io.Writer) error {
values := make(table, len(d.r.Properties()))
i := 0
propertyNameMaxWith := 13
for prop, val := range d.r.Properties() {
var header ColumnDefinition
for _, h := range d.columnDefinitions {
if h.propKey() == prop {
header = h
}
}
if header == nil {
header = &StringColumnDefinition{Prop: prop}
}
if v := values[i]; v == nil {
values[i] = make([]interface{}, 2)
}
values[i][0] = header.title()
if l := len(header.title()); l > propertyNameMaxWith {
propertyNameMaxWith = l
}
values[i][1] = header.format(val)
i++
}
ds := defaultSorter{sortBy: []int{0}}
ds.sort(values)
valueColumnMaxwidth := d.maxwidth - (propertyNameMaxWith + 7) // ( = border + 2 * margin + border + 2 * margin + border)
if valueColumnMaxwidth <= 0 {
valueColumnMaxwidth = 50
}
table := tablewriter.NewWriter(w)
table.Options(
tablewriter.WithRendition(tw.Rendition{
Borders: tw.Border{Left: tw.On, Top: tw.Off, Right: tw.On, Bottom: tw.Off},
Symbols: tw.NewSymbols(tw.StyleMarkdown),
}),
tablewriter.WithRowMaxWidth(valueColumnMaxwidth),
tablewriter.WithHeaderMaxWidth(valueColumnMaxwidth),
tablewriter.WithRowAlignment(tw.AlignLeft),
tablewriter.WithHeaderAutoFormat(tw.Off),
)
table.Header([]string{"Property" + ds.symbol(), "Value"})
wraper := autoWraper{maxWidth: valueColumnMaxwidth, wrappingChar: " "}
for i := range values {
if val := fmt.Sprint(values[i][1]); val != "" {
table.Append([]string{fmt.Sprint(values[i][0]), wraper.Wrap(val)})
}
}
table.Render()
return nil
}
func (d *tableResourceDisplayer) SetResource(r cloud.Resource) {
d.r = r
}
/*
Copyright 2017 WALLIX
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 console
import (
"fmt"
"io"
"os"
"os/signal"
"golang.org/x/crypto/ssh"
"golang.org/x/term"
)
func GetTerminalWidth() int {
w, _, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil {
return 0
}
return w
}
func GetTerminalHeight() int {
_, h, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil {
return 0
}
return h
}
func InteractiveTerminal(client *ssh.Client) error {
defer client.Close()
session, err := client.NewSession()
if err != nil {
return err
}
defer session.Close()
stdin, err := session.StdinPipe()
if err != nil {
return err
}
go io.Copy(stdin, os.Stdin)
stdout, err := session.StdoutPipe()
if err != nil {
return err
}
go io.Copy(os.Stdout, stdout)
stderr, err := session.StderrPipe()
if err != nil {
return err
}
go io.Copy(os.Stderr, stderr)
// Set up terminal modes
modes := ssh.TerminalModes{
ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Request pseudo terminal
width := GetTerminalWidth()
if width == 0 {
width = 100
}
height := GetTerminalHeight()
if height == 0 {
height = 100
}
if err := session.RequestPty("xterm", height, width, modes); err != nil {
return err
}
// Start remote shell
if err := session.Shell(); err != nil {
return err
}
signalc := make(chan os.Signal, 1)
defer func() {
signal.Reset()
close(signalc)
}()
go propagateSignals(signalc, session, stdin)
signal.Notify(signalc, os.Interrupt)
return session.Wait()
}
func propagateSignals(signalc chan os.Signal, session *ssh.Session, stdin io.WriteCloser) {
for s := range signalc {
switch s {
case os.Interrupt:
fmt.Fprint(stdin, "\x03")
}
}
}
/*
Copyright 2017 WALLIX
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 database
import (
"bytes"
"encoding/gob"
)
type configs map[string]interface{}
func (db *DB) GetConfigs(key string) (configs, error) {
d := make(configs)
b, err := db.GetBytes(key)
if err != nil {
return d, err
}
if len(b) == 0 {
return d, nil
}
return d, gob.NewDecoder(bytes.NewReader(b)).Decode(&d)
}
func (db *DB) SetConfig(configsKey, k string, v interface{}) error {
d, err := db.GetConfigs(configsKey)
if err != nil {
return err
}
d[k] = v
return db.saveConfigs(configsKey, d)
}
func (db *DB) UnsetConfig(configsKey, k string) error {
d, err := db.GetConfigs(configsKey)
if err != nil {
return err
}
delete(d, k)
return db.saveConfigs(configsKey, d)
}
func (db *DB) GetConfig(configsKey, k string) (interface{}, bool) {
d, err := db.GetConfigs(configsKey)
if err != nil {
return nil, false
}
i, ok := d[k]
return i, ok
}
func (db *DB) GetConfigString(configsKey, k string) (string, bool) {
v, ok := db.GetConfig(configsKey, k)
if !ok {
return "", ok
}
str, ok := v.(string)
return str, ok
}
func (db *DB) saveConfigs(configsKey string, d configs) error {
var buff bytes.Buffer
enc := gob.NewEncoder(&buff)
if err := enc.Encode(d); err != nil {
return err
}
return db.SetBytes(configsKey, buff.Bytes())
}
/*
Copyright 2017 WALLIX
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 database
import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"time"
"github.com/boltdb/bolt"
)
const (
Filename = "awless.db"
awlessBucket = "awless"
)
type DB struct {
bolt *bolt.DB
}
func Execute(fn func(*DB) error) error {
db, err := current()
if err != nil {
return err
}
defer db.Close()
return fn(db)
}
func current() (*DB, error) {
awlessHome := os.Getenv("__AWLESS_HOME")
if awlessHome == "" {
return nil, errors.New("database: awless home is not set")
}
path := filepath.Join(awlessHome, Filename)
db, err := open(path)
if err != nil {
return nil, err
}
if db == nil {
return nil, fmt.Errorf("db is nil while no error in opening at '%s'", path)
}
return db, nil
}
func open(path string) (*DB, error) {
boltdb, err := bolt.Open(path, 0600, &bolt.Options{Timeout: 2 * time.Second})
if err != nil {
return nil, fmt.Errorf("opening db at %s: %s (any awless existing process running?)", path, err)
}
return &DB{bolt: boltdb}, nil
}
// DeleteBucket deletes a bucket if it exists
func (db *DB) DeleteBucket(name string) error {
return db.deleteBucket(name)
}
// GetBytes gets a []byte value from database
func (db *DB) GetBytes(key string) ([]byte, error) {
return db.getValue(key)
}
// GetStringValue gets a string value from database
func (db *DB) GetStringValue(key string) (string, error) {
str, err := db.getValue(key)
if err != nil {
return "", err
}
return string(str), nil
}
// GetTimeValue gets a time value from database
func (db *DB) GetTimeValue(key string) (time.Time, error) {
var t time.Time
bin, err := db.getValue(key)
if err != nil {
return t, err
}
if len(bin) == 0 {
return t, nil
}
err = t.UnmarshalBinary(bin)
return t, err
}
// GetIntValue gets a int value from database
func (db *DB) GetIntValue(key string) (int, error) {
str, err := db.GetStringValue(key)
if err != nil {
return 0, err
}
if str == "" {
return 0, nil
}
return strconv.Atoi(str)
}
// SetBytes sets a []byte value in database
func (db *DB) SetBytes(key string, value []byte) error {
return db.setValue(key, value)
}
// SetStringValue sets a string value in database
func (db *DB) SetStringValue(key, value string) error {
return db.setValue(key, []byte(value))
}
// SetTimeValue sets a time value in database
func (db *DB) SetTimeValue(key string, t time.Time) error {
bin, err := t.MarshalBinary()
if err != nil {
return err
}
return db.setValue(key, bin)
}
// SetIntValue sets a int value in database
func (db *DB) SetIntValue(key string, value int) error {
return db.SetStringValue(key, strconv.Itoa(value))
}
// Close the database
func (db *DB) Close() {
if db.bolt != nil {
db.bolt.Close()
}
}
func (db *DB) deleteBucket(name string) error {
return db.bolt.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(name))
if b == nil {
return nil
}
e := tx.DeleteBucket([]byte(name))
return e
})
}
func (db *DB) getValue(key string) ([]byte, error) {
var value []byte
err := db.bolt.View(func(tx *bolt.Tx) error {
if b := tx.Bucket([]byte(awlessBucket)); b != nil {
value = b.Get([]byte(key))
}
return nil
})
if err != nil {
return value, err
}
return value, nil
}
func (db *DB) setValue(key string, value []byte) error {
return db.bolt.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(awlessBucket))
if err != nil {
return err
}
return b.Put([]byte(key), value)
})
}
/*
Copyright 2017 WALLIX
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 database
import (
"errors"
"fmt"
"github.com/wallix/awless/template"
"github.com/boltdb/bolt"
)
const TEMPLATES_BUCKET = "templates"
func (db *DB) AddTemplate(tplExec *template.TemplateExecution) error {
return db.bolt.Update(func(tx *bolt.Tx) error {
if tplExec.ID == "" {
return errors.New("cannot persist template with empty ID")
}
bucket, err := tx.CreateBucketIfNotExists([]byte(TEMPLATES_BUCKET))
if err != nil {
return fmt.Errorf("create bucket %s: %s", TEMPLATES_BUCKET, err)
}
b, err := tplExec.MarshalJSON()
if err != nil {
return err
}
return bucket.Put([]byte(tplExec.ID), b)
})
}
func (db *DB) GetTemplate(id string) (*template.TemplateExecution, error) {
tplExec := &template.TemplateExecution{}
err := db.bolt.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(TEMPLATES_BUCKET))
if b == nil {
return errors.New("no templates stored yet")
}
if content := b.Get([]byte(id)); content != nil {
return tplExec.UnmarshalJSON(content)
} else {
return fmt.Errorf("no content for id '%s'", id)
}
})
return tplExec, err
}
func (db *DB) DeleteTemplates() error {
return db.bolt.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(TEMPLATES_BUCKET))
if b == nil {
return nil
}
return tx.DeleteBucket([]byte(TEMPLATES_BUCKET))
})
}
func (db *DB) DeleteTemplate(id string) error {
return db.bolt.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(TEMPLATES_BUCKET))
if b == nil {
return errors.New("no templates stored yet")
}
return b.Delete([]byte(id))
})
}
type LoadedTemplate struct {
Err error
TplExec *template.TemplateExecution
Key, Raw string
}
func (db *DB) GetLoadedTemplate(id string) (*LoadedTemplate, error) {
loadedTpl := &LoadedTemplate{}
err := db.bolt.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(TEMPLATES_BUCKET))
if b == nil {
return errors.New("no templates stored yet")
}
if content := b.Get([]byte(id)); content != nil {
tplExec := &template.TemplateExecution{}
terr := tplExec.UnmarshalJSON(content)
loadedTpl.TplExec = tplExec
loadedTpl.Err = terr
loadedTpl.Key = id
loadedTpl.Raw = string(content)
return nil
}
return fmt.Errorf("no content for id '%s'", id)
})
return loadedTpl, err
}
func (db *DB) ListTemplates() ([]*LoadedTemplate, error) {
var results []*LoadedTemplate
err := db.bolt.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(TEMPLATES_BUCKET))
if b == nil {
return nil
}
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
tplExec := &template.TemplateExecution{}
terr := tplExec.UnmarshalJSON(v)
lt := &LoadedTemplate{TplExec: tplExec, Err: terr, Key: string(k), Raw: string(v)}
results = append(results, lt)
}
return nil
})
return results, err
}
package fetch
import "strings"
// Not goroutine safe as for now
type Error []error
func WrapError(errs ...error) *Error {
fe := &Error{}
for _, e := range errs {
switch ee := e.(type) {
case *Error:
for _, eee := range *ee {
fe.Add(eee)
}
default:
fe.Add(e)
}
}
return fe
}
func (fe *Error) Add(err error) {
if err != nil {
*fe = append(*fe, err)
}
}
func (fe *Error) Any() bool {
return len(*fe) > 0
}
func (fe *Error) Error() string {
var all []string
for _, e := range *fe {
all = append(all, e.Error())
}
return strings.Join(all, "\n")
}
package fetch
import (
"context"
"fmt"
"sync"
"github.com/wallix/awless/graph"
)
type Fetcher interface {
Cache
Fetch(context.Context) (*graph.Graph, error)
FetchByType(context.Context, string) (*graph.Graph, error)
}
type Cache interface {
Store(key string, val interface{})
Get(key string, funcs ...func() (interface{}, error)) (interface{}, error)
Reset()
}
type FetchResult struct {
ResourceType string
Err error
Resources []*graph.Resource
Objects interface{}
}
type Func func(context.Context, Cache) ([]*graph.Resource, interface{}, error)
type Funcs map[string]Func
type fetcher struct {
*cache
fetchFuncs map[string]Func
resourceTypes []string
}
func NewFetcher(funcs Funcs) *fetcher {
ftr := &fetcher{
fetchFuncs: make(Funcs),
cache: newCache(),
}
for resType, f := range funcs {
ftr.resourceTypes = append(ftr.resourceTypes, resType)
ftr.fetchFuncs[resType] = f
}
return ftr
}
func (f *fetcher) Fetch(ctx context.Context) (*graph.Graph, error) {
results := make(chan FetchResult, len(f.resourceTypes))
var wg sync.WaitGroup
for _, resType := range f.resourceTypes {
wg.Add(1)
go func(t string, co context.Context) {
f.fetchResource(co, t, results)
wg.Done()
}(resType, ctx)
}
go func() {
wg.Wait()
close(results)
}()
gph := graph.NewGraph()
ferr := new(Error)
for res := range results {
if err := res.Err; err != nil {
ferr.Add(err)
}
gph.AddResource(res.Resources...)
}
if ferr.Any() {
return gph, ferr
}
return gph, nil
}
// ContextKey is a typed key for context values to avoid SA1029.
type ContextKey string
// FetchModeKey is the context key used to indicate fetch-by-type mode.
const FetchModeKey ContextKey = "fetchmode"
func IsFetchingByType(c context.Context) (string, bool) {
v, ok := c.Value(FetchModeKey).(string)
return v, len(v) != 0 && ok
}
func (f *fetcher) FetchByType(ctx context.Context, resourceType string) (*graph.Graph, error) {
results := make(chan FetchResult)
defer close(results)
go f.fetchResource(
context.WithValue(ctx, FetchModeKey, resourceType),
resourceType,
results)
gph := graph.NewGraph()
res := <-results
if err := res.Err; err != nil {
return gph, err
}
for _, r := range res.Resources {
gph.AddResource(r)
}
return gph, nil
}
func (f *fetcher) fetchResource(ctx context.Context, resourceType string, results chan<- FetchResult) {
var err error
var objects interface{}
resources := make([]*graph.Resource, 0)
fn, ok := f.fetchFuncs[resourceType]
if ok {
resources, objects, err = fn(ctx, f.cache)
} else {
err = fmt.Errorf("no fetch func defined for resource type '%s'", resourceType)
}
f.cache.Store(fmt.Sprintf("%s_objects", resourceType), objects)
results <- FetchResult{
ResourceType: resourceType,
Err: err,
Resources: resources,
Objects: objects,
}
}
type cache struct {
mu sync.RWMutex
cached map[string]*keyCache
}
func newCache() *cache {
return &cache{
cached: make(map[string]*keyCache),
}
}
type keyCache struct {
once sync.Once
err error
result interface{}
}
func (c *cache) Get(key string, funcs ...func() (interface{}, error)) (interface{}, error) {
c.mu.Lock()
cache, ok := c.cached[key]
if !ok {
cache = &keyCache{}
c.cached[key] = cache
}
c.mu.Unlock()
if len(funcs) > 0 {
cache.once.Do(func() {
cache.result, cache.err = funcs[0]()
})
}
return cache.result, cache.err
}
func (c *cache) Store(key string, val interface{}) {
c.mu.Lock()
c.cached[key] = &keyCache{result: val}
c.mu.Unlock()
}
func (c *cache) Reset() {
c.mu.Lock()
c.cached = make(map[string]*keyCache)
c.mu.Unlock()
}
/*
Copyright 2017 WALLIX
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 aws
import (
"strings"
"github.com/wallix/awless/cloud"
)
func ApiToInterface(api string) string {
switch api {
case "autoscaling":
return "AutoScalingAPI"
case "cloudwatch":
return "CloudWatchAPI"
case "cloudfront":
return "CloudFrontAPI"
case "applicationautoscaling":
return "ApplicationAutoScalingAPI"
case "cloudformation":
return "CloudFormationAPI"
case "apigatewayv2":
return "ApiGatewayV2API"
case "secretsmanager":
return "SecretsManagerAPI"
case "cloudtrail":
return "CloudTrailAPI"
case "cloudwatchlogs":
return "CloudWatchLogsAPI"
case "route53", "lambda":
return strings.Title(api) + "API"
default:
return strings.ToUpper(api) + "API"
}
}
// SdkModulePath maps the short API name to the AWS SDK v2 Go module path.
// Most services use the same name, but some differ (e.g., elb → elasticloadbalancing).
func SdkModulePath(api string) string {
switch api {
case "elb":
return "elasticloadbalancing"
case "elbv2":
return "elasticloadbalancingv2"
default:
return api
}
}
type fetchersDef struct {
Name string
Global bool
Api []string
Fetchers []fetcher
}
// AutoFetcherAPIs returns the set of API names used by non-manual fetchers.
func (d fetchersDef) AutoFetcherAPIs() []string {
seen := make(map[string]bool)
var apis []string
for _, f := range d.Fetchers {
if !f.ManualFetcher && !seen[f.Api] {
seen[f.Api] = true
apis = append(apis, f.Api)
}
}
return apis
}
type fetcher struct {
ResourceType string
AWSType string
ApiMethod, Input string
Output, OutputsContainers, OutputsExtractor string
ManualFetcher bool
Multipage bool
NextPageMarker string
Api string
}
var FetchersDefs = []fetchersDef{
{
Name: "infra",
Api: []string{"ec2", "elbv2", "elb", "rds", "autoscaling", "ecr", "ecs", "applicationautoscaling", "acm"},
Fetchers: []fetcher{
{Api: "ec2", ResourceType: cloud.Instance, AWSType: "ec2types.Instance", ApiMethod: "DescribeInstances", Input: "ec2.DescribeInstancesInput{}", Output: "ec2.DescribeInstancesOutput", OutputsExtractor: "Instances", OutputsContainers: "Reservations", Multipage: true, NextPageMarker: "NextToken"},
{Api: "ec2", ResourceType: cloud.Subnet, AWSType: "ec2types.Subnet", ApiMethod: "DescribeSubnets", Input: "ec2.DescribeSubnetsInput{}", Output: "ec2.DescribeSubnetsOutput", OutputsExtractor: "Subnets"},
{Api: "ec2", ResourceType: cloud.Vpc, AWSType: "ec2types.Vpc", ApiMethod: "DescribeVpcs", Input: "ec2.DescribeVpcsInput{}", Output: "ec2.DescribeVpcsOutput", OutputsExtractor: "Vpcs"},
{Api: "ec2", ResourceType: cloud.Keypair, AWSType: "ec2types.KeyPairInfo", ApiMethod: "DescribeKeyPairs", Input: "ec2.DescribeKeyPairsInput{}", Output: "ec2.DescribeKeyPairsOutput", OutputsExtractor: "KeyPairs"},
{Api: "ec2", ResourceType: cloud.SecurityGroup, AWSType: "ec2types.SecurityGroup", ApiMethod: "DescribeSecurityGroups", Input: "ec2.DescribeSecurityGroupsInput{}", Output: "ec2.DescribeSecurityGroupsOutput", OutputsExtractor: "SecurityGroups"},
{Api: "ec2", ResourceType: cloud.Volume, AWSType: "ec2types.Volume", ApiMethod: "DescribeVolumes", Input: "ec2.DescribeVolumesInput{}", Output: "ec2.DescribeVolumesOutput", OutputsExtractor: "Volumes", Multipage: true, NextPageMarker: "NextToken"},
{Api: "ec2", ResourceType: cloud.InternetGateway, AWSType: "ec2types.InternetGateway", ApiMethod: "DescribeInternetGateways", Input: "ec2.DescribeInternetGatewaysInput{}", Output: "ec2.DescribeInternetGatewaysOutput", OutputsExtractor: "InternetGateways"},
{Api: "ec2", ResourceType: cloud.NatGateway, AWSType: "ec2types.NatGateway", ApiMethod: "DescribeNatGateways", Input: "ec2.DescribeNatGatewaysInput{}", Output: "ec2.DescribeNatGatewaysOutput", OutputsExtractor: "NatGateways"},
{Api: "ec2", ResourceType: cloud.RouteTable, AWSType: "ec2types.RouteTable", ApiMethod: "DescribeRouteTables", Input: "ec2.DescribeRouteTablesInput{}", Output: "ec2.DescribeRouteTablesOutput", OutputsExtractor: "RouteTables"},
{Api: "ec2", ResourceType: cloud.AvailabilityZone, AWSType: "ec2types.AvailabilityZone", ApiMethod: "DescribeAvailabilityZones", Input: "ec2.DescribeAvailabilityZonesInput{}", Output: "ec2.DescribeAvailabilityZonesOutput", OutputsExtractor: "AvailabilityZones"},
{Api: "ec2", ResourceType: cloud.Image, AWSType: "ec2types.Image", ApiMethod: "DescribeImages", Input: "ec2.DescribeImagesInput{Owners: []string{\"self\"}}", Output: "ec2.DescribeImagesOutput", OutputsExtractor: "Images"},
{Api: "ec2", ResourceType: cloud.ImportImageTask, AWSType: "ec2types.ImportImageTask", ApiMethod: "DescribeImportImageTasks", Input: "ec2.DescribeImportImageTasksInput{}", Output: "ec2.DescribeImportImageTasksOutput", OutputsExtractor: "ImportImageTasks"},
{Api: "ec2", ResourceType: cloud.ElasticIP, AWSType: "ec2types.Address", ApiMethod: "DescribeAddresses", Input: "ec2.DescribeAddressesInput{}", Output: "ec2.DescribeAddressesOutput", OutputsExtractor: "Addresses"},
{Api: "ec2", ResourceType: cloud.Snapshot, AWSType: "ec2types.Snapshot", ApiMethod: "DescribeSnapshots", Input: "ec2.DescribeSnapshotsInput{OwnerIds: []string{\"self\"}}", Output: "ec2.DescribeSnapshotsOutput", OutputsExtractor: "Snapshots", Multipage: true, NextPageMarker: "NextToken"},
{Api: "ec2", ResourceType: cloud.NetworkInterface, AWSType: "ec2types.NetworkInterface", ApiMethod: "DescribeNetworkInterfaces", Input: "ec2.DescribeNetworkInterfacesInput{}", Output: "ec2.DescribeNetworkInterfacesOutput", OutputsExtractor: "NetworkInterfaces"},
{Api: "elb", ResourceType: cloud.ClassicLoadBalancer, AWSType: "elbtypes.LoadBalancerDescription", ApiMethod: "DescribeLoadBalancers", Input: "elb.DescribeLoadBalancersInput{}", Output: "elb.DescribeLoadBalancersOutput", OutputsExtractor: "LoadBalancerDescriptions", Multipage: true, NextPageMarker: "NextMarker"},
{Api: "elbv2", ResourceType: cloud.LoadBalancer, AWSType: "elbv2types.LoadBalancer", ApiMethod: "DescribeLoadBalancers", Input: "elbv2.DescribeLoadBalancersInput{}", Output: "elbv2.DescribeLoadBalancersOutput", OutputsExtractor: "LoadBalancers", Multipage: true, NextPageMarker: "NextMarker"},
{Api: "elbv2", ResourceType: cloud.TargetGroup, AWSType: "elbv2types.TargetGroup", ApiMethod: "DescribeTargetGroups", Input: "elbv2.DescribeTargetGroupsInput{}", Output: "elbv2.DescribeTargetGroupsOutput", OutputsExtractor: "TargetGroups"},
{Api: "elbv2", ResourceType: cloud.Listener, AWSType: "elbv2types.Listener", ManualFetcher: true},
{Api: "rds", ResourceType: cloud.Database, AWSType: "rdstypes.DBInstance", ApiMethod: "DescribeDBInstances", Input: "rds.DescribeDBInstancesInput{}", Output: "rds.DescribeDBInstancesOutput", OutputsExtractor: "DBInstances", Multipage: true, NextPageMarker: "Marker"},
{Api: "rds", ResourceType: cloud.DbSubnetGroup, AWSType: "rdstypes.DBSubnetGroup", ApiMethod: "DescribeDBSubnetGroups", Input: "rds.DescribeDBSubnetGroupsInput{}", Output: "rds.DescribeDBSubnetGroupsOutput", OutputsExtractor: "DBSubnetGroups", Multipage: true, NextPageMarker: "Marker"},
{Api: "autoscaling", ResourceType: cloud.LaunchConfiguration, AWSType: "autoscalingtypes.LaunchConfiguration", ApiMethod: "DescribeLaunchConfigurations", Input: "autoscaling.DescribeLaunchConfigurationsInput{}", Output: "autoscaling.DescribeLaunchConfigurationsOutput", OutputsExtractor: "LaunchConfigurations", Multipage: true, NextPageMarker: "NextToken"},
{Api: "autoscaling", ResourceType: cloud.ScalingGroup, AWSType: "autoscalingtypes.AutoScalingGroup", ApiMethod: "DescribeAutoScalingGroups", Input: "autoscaling.DescribeAutoScalingGroupsInput{}", Output: "autoscaling.DescribeAutoScalingGroupsOutput", OutputsExtractor: "AutoScalingGroups", Multipage: true, NextPageMarker: "NextToken"},
{Api: "autoscaling", ResourceType: cloud.ScalingPolicy, AWSType: "autoscalingtypes.ScalingPolicy", ApiMethod: "DescribePolicies", Input: "autoscaling.DescribePoliciesInput{}", Output: "autoscaling.DescribePoliciesOutput", OutputsExtractor: "ScalingPolicies", Multipage: true, NextPageMarker: "NextToken"},
{Api: "ecr", ResourceType: cloud.Repository, AWSType: "ecrtypes.Repository", ApiMethod: "DescribeRepositories", Input: "ecr.DescribeRepositoriesInput{}", Output: "ecr.DescribeRepositoriesOutput", OutputsExtractor: "Repositories", Multipage: true, NextPageMarker: "NextToken"},
{Api: "ecs", ResourceType: cloud.ContainerCluster, AWSType: "ecstypes.Cluster", ManualFetcher: true},
{Api: "ecs", ResourceType: cloud.ContainerTask, AWSType: "ecstypes.TaskDefinition", ManualFetcher: true},
{Api: "ecs", ResourceType: cloud.Container, AWSType: "ecstypes.Container", ManualFetcher: true},
{Api: "ecs", ResourceType: cloud.ContainerInstance, AWSType: "ecstypes.ContainerInstance", ManualFetcher: true},
{Api: "acm", ResourceType: cloud.Certificate, AWSType: "acmtypes.CertificateSummary", ApiMethod: "ListCertificates", Input: "acm.ListCertificatesInput{}", Output: "acm.ListCertificatesOutput", OutputsExtractor: "CertificateSummaryList", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Name: "access",
Global: true,
Api: []string{"iam", "sts"},
Fetchers: []fetcher{
{Api: "iam", ResourceType: cloud.User, AWSType: "iamtypes.UserDetail", ManualFetcher: true},
{Api: "iam", ResourceType: cloud.Group, AWSType: "iamtypes.GroupDetail", ManualFetcher: true},
{Api: "iam", ResourceType: cloud.Role, AWSType: "iamtypes.RoleDetail", ManualFetcher: true},
{Api: "iam", ResourceType: cloud.Policy, AWSType: "iamtypes.Policy", ManualFetcher: true},
{Api: "iam", ResourceType: cloud.AccessKey, AWSType: "iamtypes.AccessKeyMetadata", ManualFetcher: true},
{Api: "iam", ResourceType: cloud.InstanceProfile, AWSType: "iamtypes.InstanceProfile", ApiMethod: "ListInstanceProfiles", Input: "iam.ListInstanceProfilesInput{}", Output: "iam.ListInstanceProfilesOutput", OutputsExtractor: "InstanceProfiles", Multipage: true, NextPageMarker: "Marker"},
{Api: "iam", ResourceType: cloud.MFADevice, AWSType: "iamtypes.VirtualMFADevice", ApiMethod: "ListVirtualMFADevices", Input: "iam.ListVirtualMFADevicesInput{}", Output: "iam.ListVirtualMFADevicesOutput", OutputsExtractor: "VirtualMFADevices", Multipage: true, NextPageMarker: "Marker"},
},
},
{
Name: "storage",
Api: []string{"s3"},
Fetchers: []fetcher{
{Api: "s3", ResourceType: cloud.Bucket, AWSType: "s3types.Bucket", ManualFetcher: true},
{Api: "s3", ResourceType: cloud.S3Object, AWSType: "s3types.Object", ManualFetcher: true},
},
},
{
Name: "messaging",
Api: []string{"sns", "sqs"},
Fetchers: []fetcher{
{Api: "sns", ResourceType: cloud.Subscription, AWSType: "snstypes.Subscription", ApiMethod: "ListSubscriptions", Input: "sns.ListSubscriptionsInput{}", Output: "sns.ListSubscriptionsOutput", OutputsExtractor: "Subscriptions", Multipage: true, NextPageMarker: "NextToken"},
{Api: "sns", ResourceType: cloud.Topic, AWSType: "snstypes.Topic", ApiMethod: "ListTopics", Input: "sns.ListTopicsInput{}", Output: "sns.ListTopicsOutput", OutputsExtractor: "Topics", Multipage: true, NextPageMarker: "NextToken"},
{Api: "sqs", ResourceType: cloud.Queue, AWSType: "string", ManualFetcher: true},
},
},
{
Name: "dns",
Global: true,
Api: []string{"route53"},
Fetchers: []fetcher{
{Api: "route53", ResourceType: cloud.Zone, AWSType: "route53types.HostedZone", ApiMethod: "ListHostedZones", Input: "route53.ListHostedZonesInput{}", Output: "route53.ListHostedZonesOutput", OutputsExtractor: "HostedZones", Multipage: true, NextPageMarker: "NextMarker"},
{Api: "route53", ResourceType: cloud.Record, AWSType: "route53types.ResourceRecordSet", ManualFetcher: true},
},
},
{
Name: "lambda",
Api: []string{"lambda"},
Fetchers: []fetcher{
{Api: "lambda", ResourceType: cloud.Function, AWSType: "lambdatypes.FunctionConfiguration", ApiMethod: "ListFunctions", Input: "lambda.ListFunctionsInput{}", Output: "lambda.ListFunctionsOutput", OutputsExtractor: "Functions", Multipage: true, NextPageMarker: "NextMarker"},
},
},
{
Name: "monitoring",
Api: []string{"cloudwatch"},
Fetchers: []fetcher{
{Api: "cloudwatch", ResourceType: cloud.Metric, AWSType: "cloudwatchtypes.Metric", ApiMethod: "ListMetrics", Input: "cloudwatch.ListMetricsInput{}", Output: "cloudwatch.ListMetricsOutput", OutputsExtractor: "Metrics", Multipage: true, NextPageMarker: "NextToken"},
{Api: "cloudwatch", ResourceType: cloud.Alarm, AWSType: "cloudwatchtypes.MetricAlarm", ApiMethod: "DescribeAlarms", Input: "cloudwatch.DescribeAlarmsInput{}", Output: "cloudwatch.DescribeAlarmsOutput", OutputsExtractor: "MetricAlarms", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Name: "cdn",
Global: true,
Api: []string{"cloudfront"},
Fetchers: []fetcher{
{Api: "cloudfront", ResourceType: cloud.Distribution, AWSType: "cloudfronttypes.DistributionSummary", ApiMethod: "ListDistributions", Input: "cloudfront.ListDistributionsInput{}", Output: "cloudfront.ListDistributionsOutput", OutputsExtractor: "DistributionList.Items", Multipage: true, NextPageMarker: "DistributionList.NextMarker"},
},
},
{
Name: "cloudformation", //deployment ?
Api: []string{"cloudformation"},
Fetchers: []fetcher{
{Api: "cloudformation", ResourceType: cloud.Stack, AWSType: "cloudformationtypes.Stack", ApiMethod: "DescribeStacks", Input: "cloudformation.DescribeStacksInput{}", Output: "cloudformation.DescribeStacksOutput", OutputsExtractor: "Stacks", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Name: "eks",
Api: []string{"eks"},
Fetchers: []fetcher{
{Api: "eks", ResourceType: cloud.EKSCluster, AWSType: "ekstypes.Cluster", ManualFetcher: true},
{Api: "eks", ResourceType: cloud.EKSNodeGroup, AWSType: "ekstypes.Nodegroup", ManualFetcher: true},
},
},
{
Name: "dynamodb",
Api: []string{"dynamodb"},
Fetchers: []fetcher{
{Api: "dynamodb", ResourceType: cloud.DynamoDBTable, AWSType: "dynamodbtypes.TableDescription", ManualFetcher: true},
},
},
{
Name: "secretsmanager",
Api: []string{"secretsmanager", "kms"},
Fetchers: []fetcher{
{Api: "secretsmanager", ResourceType: cloud.Secret, AWSType: "secretsmanagertypes.SecretListEntry", ApiMethod: "ListSecrets", Input: "secretsmanager.ListSecretsInput{}", Output: "secretsmanager.ListSecretsOutput", OutputsExtractor: "SecretList", Multipage: true, NextPageMarker: "NextToken"},
{Api: "kms", ResourceType: cloud.Key, AWSType: "kmstypes.KeyMetadata", ManualFetcher: true},
},
},
{
Name: "apigateway",
Api: []string{"apigatewayv2"},
Fetchers: []fetcher{
{Api: "apigatewayv2", ResourceType: cloud.ApiGateway, AWSType: "apigatewayv2types.Api", ManualFetcher: true},
{Api: "apigatewayv2", ResourceType: cloud.ApiGatewayRoute, AWSType: "apigatewayv2types.Route", ManualFetcher: true},
{Api: "apigatewayv2", ResourceType: cloud.ApiGatewayStage, AWSType: "apigatewayv2types.Stage", ManualFetcher: true},
},
},
{
Name: "ssm",
Api: []string{"ssm"},
Fetchers: []fetcher{
{Api: "ssm", ResourceType: cloud.SSMParameter, AWSType: "ssmtypes.ParameterMetadata", ApiMethod: "DescribeParameters", Input: "ssm.DescribeParametersInput{}", Output: "ssm.DescribeParametersOutput", OutputsExtractor: "Parameters", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Name: "efs",
Api: []string{"efs"},
Fetchers: []fetcher{
{Api: "efs", ResourceType: cloud.FileSystem, AWSType: "efstypes.FileSystemDescription", ApiMethod: "DescribeFileSystems", Input: "efs.DescribeFileSystemsInput{}", Output: "efs.DescribeFileSystemsOutput", OutputsExtractor: "FileSystems", Multipage: true, NextPageMarker: "NextMarker"},
{Api: "efs", ResourceType: cloud.MountTarget, AWSType: "efstypes.MountTargetDescription", ManualFetcher: true},
},
},
{
Name: "cloudtrail",
Api: []string{"cloudtrail"},
Fetchers: []fetcher{
{Api: "cloudtrail", ResourceType: cloud.Trail, AWSType: "cloudtrailtypes.Trail", ApiMethod: "DescribeTrails", Input: "cloudtrail.DescribeTrailsInput{}", Output: "cloudtrail.DescribeTrailsOutput", OutputsExtractor: "TrailList"},
},
},
{
Name: "cloudwatchlogs",
Api: []string{"cloudwatchlogs"},
Fetchers: []fetcher{
{Api: "cloudwatchlogs", ResourceType: cloud.LogGroup, AWSType: "cloudwatchlogstypes.LogGroup", ApiMethod: "DescribeLogGroups", Input: "cloudwatchlogs.DescribeLogGroupsInput{}", Output: "cloudwatchlogs.DescribeLogGroupsOutput", OutputsExtractor: "LogGroups", Multipage: true, NextPageMarker: "NextToken"},
},
},
}
package main
import (
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"text/template"
)
func generateAcceptanceMocks() {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, SPEC_DIR, func(os.FileInfo) bool { return true }, 0)
if err != nil {
panic(err)
}
finder := &findStructs{}
for _, pkg := range pkgs {
for _, f := range pkg.Files {
ast.Walk(finder, f)
}
}
usedApis := make(map[string]bool)
for _, cmd := range finder.result {
if cmd.API == "" {
continue
}
usedApis[cmd.API] = true
}
apiList := make([]string, 0, len(usedApis))
for api := range usedApis {
apiList = append(apiList, api)
}
templ, err := template.New("mocks").Funcs(
template.FuncMap{
"Join": strings.Join,
"Title": strings.Title,
},
).Parse(atMocksTemplate)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, apiList, AWSAT_DIR, "gen_mocks.go")
}
func generateAcceptanceFactory() {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, SPEC_DIR, func(os.FileInfo) bool { return true }, 0)
if err != nil {
panic(err)
}
finder := &findStructs{}
for _, pkg := range pkgs {
for _, f := range pkg.Files {
ast.Walk(finder, f)
}
}
templ, err := template.New("acceptanceFactory").Funcs(
template.FuncMap{
"Title": strings.Title,
},
).Parse(atMocksCmdBuilders)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, finder.result, AWSAT_DIR, "gen_factory.go")
}
const atMocksCmdBuilders = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsat
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/wallix/awless/cloud"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/logger"
)
type AcceptanceFactory struct {
Mock interface{}
Logger *logger.Logger
Graph cloud.GraphAPI
}
func NewAcceptanceFactory(mock interface{}, g cloud.GraphAPI, l ...*logger.Logger) *AcceptanceFactory {
lg := logger.DiscardLogger
if len(l) > 0 {
lg = l[0]
}
return &AcceptanceFactory{Mock: mock, Graph:g, Logger: lg}
}
func (f *AcceptanceFactory) Build(key string) func() interface{} {
switch key {
{{- range $cmdName, $cmd := . }}
case "{{ $cmd.Action }}{{ $cmd.Entity }}":
return func() interface{} {
cmd := awsspec.New{{ $cmdName }}(aws.Config{}, f.Graph, f.Logger)
// TODO: SDK v2 mocking needs rework - SetApi expects *service.Client
_ = cmd
return cmd
}
{{- end}}
}
return nil
}
`
const atMocksTemplate = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsat
// TODO: Acceptance mocks need reworking for AWS SDK v2.
// SDK v2 does not have iface packages, so mocks must be manually defined
// or use a different mocking strategy (e.g., httptest or interface wrappers).
{{ range $, $api := . }}
type {{ $api }}Mock struct {
basicMock
}
{{- end }}
`
/*
Copyright 2017 WALLIX
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 main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"strings"
"text/template"
"sort"
"github.com/wallix/awless/gen/aws"
)
func loadCommandStructs() map[string]cmdData {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, SPEC_DIR, func(os.FileInfo) bool { return true }, 0)
if err != nil {
panic(err)
}
finder := &findStructs{}
for _, pkg := range pkgs {
for _, f := range pkg.Files {
ast.Walk(finder, f)
}
}
return finder.result
}
func uniqueAPIs(cmds map[string]cmdData) []string {
seen := make(map[string]bool)
var apis []string
for _, cmd := range cmds {
if !seen[cmd.API] {
seen[cmd.API] = true
apis = append(apis, cmd.API)
}
}
sort.Strings(apis)
return apis
}
func generateCommands() {
cmdsData := loadCommandStructs()
templ, err := template.New("cmdRuns").Funcs(
template.FuncMap{
"SdkModulePath": aws.SdkModulePath,
"UniqueAPIs": uniqueAPIs,
},
).Parse(cmdRuns)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, cmdsData, SPEC_DIR, "gen_runs.go")
templ, err = template.New("cmdInits").Parse(cmdInits)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, cmdsData, SPEC_DIR, "gen_inits.go")
templ, err = template.New("templates_definitions").Funcs(
template.FuncMap{
"BuildSupportedActions": BuildSupportedActions,
},
).Parse(cmdsDefinitions)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, cmdsData, SPEC_DIR, "gen_cmds_defs.go")
}
type cmdData struct {
Action, Entity, API, Call, Input, Output string
Params []templateParam
RequiredParamsKey []string
ExtrasParamsKey []string
HasRequiredParams bool
HasDryRun bool
GenDryRun bool
}
type templateParam struct {
Name string
AwsField string
IsRequired bool
}
type findStructs struct {
result map[string]cmdData
}
func (v *findStructs) Visit(node ast.Node) (w ast.Visitor) {
if v.result == nil {
v.result = make(map[string]cmdData)
}
if typ, ok := node.(*ast.TypeSpec); ok {
if s, isStruct := typ.Type.(*ast.StructType); isStruct {
var cmd *cmdData
var params []templateParam
for _, f := range s.Fields.List {
if tag := f.Tag; tag != nil && strings.Contains(tag.Value, "awsAPI") {
extractedCmd := extractCmdData(tag.Value)
cmd = &extractedCmd
continue
}
if tag := f.Tag; tag != nil && strings.Contains(tag.Value, "templateName") {
params = append(params, extractParam(tag.Value))
}
}
if cmd != nil {
if len(params) > 0 {
sort.Slice(params, func(i, j int) bool {
return params[i].Name < params[j].Name
})
cmd.Params = params
}
for _, p := range cmd.Params {
if p.IsRequired {
cmd.HasRequiredParams = true
cmd.RequiredParamsKey = append(cmd.RequiredParamsKey, p.Name)
} else {
cmd.ExtrasParamsKey = append(cmd.ExtrasParamsKey, p.Name)
}
}
v.result[typ.Name.Name] = *cmd
}
}
}
return v
}
func extractCmdData(s string) (t cmdData) {
tags := extractTags(s)
if v, ok := tags["action"]; ok {
t.Action = v
}
if v, ok := tags["entity"]; ok {
t.Entity = v
}
if v, ok := tags["awsAPI"]; ok {
t.API = v
}
if v, ok := tags["awsCall"]; ok {
t.Call = v
}
if v, ok := tags["awsInput"]; ok {
t.Input = v
}
if v, ok := tags["awsOutput"]; ok {
t.Output = v
}
if v, ok := tags["awsDryRun"]; ok {
t.HasDryRun = true
t.GenDryRun = true
if strings.ToLower(v) == "manual" {
t.GenDryRun = false
}
}
return
}
func extractParam(s string) (p templateParam) {
tags := extractTags(s)
if v, ok := tags["templateName"]; ok {
p.Name = v
}
if _, ok := tags["required"]; ok {
p.IsRequired = true
}
if v, ok := tags["awsName"]; ok {
p.AwsField = v
}
return
}
func BuildSupportedActions(cmds map[string]cmdData) map[string][]string {
supportedActions := make(map[string][]string)
for _, cmd := range cmds {
supportedActions[cmd.Action] = append(supportedActions[cmd.Action], cmd.Entity)
}
for _, entities := range supportedActions {
sort.Slice(entities, func(i, j int) bool {
return entities[i] < entities[j]
})
}
return supportedActions
}
func extractTags(s string) map[string]string {
splits := strings.Split(s[1:len(s)-1], " ")
tags := make(map[string]string)
for _, e := range splits {
el := strings.Split(e, ":")
if len(el) > 1 {
if len(el[1]) < 2 || el[1][0] != '"' || el[1][len(el[1])-1] != '"' {
panic(fmt.Sprintf("malformed tag: '%s':'%s'", el[0], el[1]))
}
tags[el[0]] = el[1][1 : len(el[1])-1]
}
}
return tags
}
const cmdRuns = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsspec
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
{{- range $, $api := UniqueAPIs . }}
{{ $api }} "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $api }}"
{{- end }}
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
)
{{ range $cmdName, $tag := . }}
func New{{ $cmdName }}(cfg aws.Config, g cloud.GraphAPI, l ...*logger.Logger) *{{ $cmdName }}{
cmd := new({{ $cmdName }})
if len(l) > 0 {
cmd.logger = l[0]
} else {
cmd.logger = logger.DiscardLogger
}
if cfg.Region != "" {
cmd.api = {{ $tag.API }}.NewFromConfig(cfg)
}
cmd.graph = g
return cmd
}
func (cmd *{{ $cmdName }}) SetApi(api *{{$tag.API}}.Client) {
cmd.api = api
}
func (cmd *{{ $cmdName }}) Run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if renv.IsDryRun() {
return cmd.dryRun(renv, params)
}
return cmd.run(renv, params)
}
func (cmd *{{ $cmdName }}) run(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
if v, ok := implementsBeforeRun(cmd); ok {
if brErr := v.BeforeRun(renv); brErr != nil {
return nil, fmt.Errorf("before run: %s", brErr)
}
}
{{ if $tag.Call }}
input := &{{ $tag.Input }}{}
if err := structInjector(cmd, input, renv.Context()) ; err != nil {
return nil, fmt.Errorf("cannot inject in {{ $tag.Input }}: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
output, err := cmd.api.{{ $tag.Call }}(context.Background(), input)
renv.Log().ExtraVerbosef("{{ $tag.API }}.{{ $tag.Call }} call took %s", time.Since(start))
if err != nil {
return nil, decorateAWSError(err)
}
{{- else }}
output, err := cmd.ManualRun(renv)
if err != nil {
return nil, decorateAWSError(err)
}
{{- end }}
var extracted interface{}
if v, ok := implementsResultExtractor(cmd); ok {
if output != nil {
extracted = v.ExtractResult(output)
} else {
renv.Log().Warning("{{ $tag.Action }} {{ $tag.Entity }}: AWS command returned nil output")
}
}
if extracted != nil {
renv.Log().Verbosef("{{ $tag.Action }} {{ $tag.Entity }} '%s' done", extracted)
} else {
renv.Log().Verbose("{{ $tag.Action }} {{ $tag.Entity }} done")
}
if v, ok := implementsAfterRun(cmd); ok {
if brErr := v.AfterRun(renv, output); brErr != nil {
return nil, fmt.Errorf("after run: %s", brErr)
}
}
return extracted, nil
}
{{ if $tag.HasDryRun }}
{{ if $tag.GenDryRun }}
func (cmd *{{ $cmdName }}) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
if err := cmd.inject(params); err != nil {
return nil, fmt.Errorf("cannot set params on command struct: %s", err)
}
input := &{{ $tag.Input }}{}
input.DryRun = aws.Bool(true)
if err := structInjector(cmd, input, renv.Context()) ; err != nil {
return nil, fmt.Errorf("cannot inject in {{ $tag.Input }}: %s", err)
}
if v, ok := implementsInputPostProcessor(cmd); ok {
v.PostProcessInput(input)
}
start := time.Now()
_, err := cmd.api.{{ $tag.Call }}(context.Background(), input);
var ae smithy.APIError
if errors.As(err, &ae) {
switch code := ae.ErrorCode(); {
case code == dryRunOperation, strings.HasSuffix(code, notFound), strings.Contains(ae.ErrorMessage(), "Invalid IAM Instance Profile name"):
renv.Log().ExtraVerbosef("dry run: {{ $tag.API }}.{{ $tag.Call }} call took %s", time.Since(start))
renv.Log().Verbose("dry run: {{ $tag.Action }} {{ $tag.Entity }} ok")
return fakeDryRunId("{{ $tag.Entity }}"), nil
}
}
return nil, err
}
{{- end }}
{{- else }}
func (cmd *{{ $cmdName }}) dryRun(renv env.Running, params map[string]interface{}) (interface{}, error) {
return fakeDryRunId("{{ $tag.Entity }}"), nil
}
{{- end }}
func (cmd *{{ $cmdName }}) inject(params map[string]interface{}) error {
return structSetter(cmd, params)
}
{{ end }}
`
const cmdInits = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsspec
import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/logger"
)
type Factory interface {
Build(key string) func() interface{}
}
var CommandFactory Factory
var MockAWSSessionFactory = &AWSFactory{
Log: logger.DiscardLogger,
Cfg: aws.Config{},
}
type AWSFactory struct {
Log *logger.Logger
Cfg aws.Config
Graph cloud.GraphAPI
}
func (f *AWSFactory) Build(key string) func() interface{} {
switch key {
{{- range $cmdName, $tag := . }}
case "{{ $tag.Action }}{{ $tag.Entity }}":
return func() interface{} { return New{{ $cmdName }}(f.Cfg, f.Graph, f.Log) }
{{- end}}
}
return nil
}
var (
{{- range $cmdName, $tag := . }}
_ command = &{{ $cmdName }}{}
{{- end }}
)
`
const cmdsDefinitions = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package awsspec
var APIPerTemplateDefName = map[string]string {
{{- range $, $cmd := . }}
"{{ $cmd.Action }}{{ $cmd.Entity }}": "{{ $cmd.API }}",
{{- end }}
}
var AWSTemplatesDefinitions = map[string]Definition{
{{- range $cmdName, $cmd := . }}
"{{ $cmd.Action }}{{ $cmd.Entity }}": Definition{
Action: "{{ $cmd.Action }}",
Entity: "{{ $cmd.Entity }}",
Api: "{{ $cmd.API }}",
Params: new({{ $cmdName }}).ParamsSpec().Rule(),
},
{{- end }}
}
var DriverSupportedActions = map[string][]string{
{{- range $action, $entities := BuildSupportedActions . }}
"{{ $action }}" : []string{ {{- range $entity := $entities }}"{{ $entity }}", {{- end}} },
{{- end }}
}
`
/*
Copyright 2017 WALLIX
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 main
import (
"strings"
"text/template"
"github.com/wallix/awless/gen/aws"
)
func generateFetcherFuncs() {
templ, err := template.New("funcs").Funcs(template.FuncMap{
"Title": strings.Title,
"ToUpper": strings.ToUpper,
"Join": strings.Join,
"SdkModulePath": aws.SdkModulePath,
}).Parse(fetchersTempl)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, aws.FetchersDefs, FETCHERS_DIR, "gen_fetchers.go")
}
const fetchersTempl = `// Auto generated implementation for the AWS cloud service
/*
Copyright 2017 WALLIX
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 awsfetch
// DO NOT EDIT - This file was automatically generated with go generate
import (
"context"
{{- range $index, $service := . }}
{{- range $, $api := $service.AutoFetcherAPIs }}
{{ $api }} "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $api }}"
{{ $api }}types "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $api }}/types"
{{- end }}
{{- end }}
"github.com/wallix/awless/fetch"
"github.com/wallix/awless/graph"
awsconv "github.com/wallix/awless/aws/conv"
)
{{- range $index, $service := . }}
func Build{{ Title $service.Name }}FetchFuncs(conf *Config) fetch.Funcs {
funcs := make(map[string]fetch.Func)
addManual{{ Title $service.Name }}FetchFuncs(conf, funcs)
{{- range $index, $fetcher := $service.Fetchers }}
{{- if not $fetcher.ManualFetcher }}
funcs["{{ $fetcher.ResourceType }}"] = func(ctx context.Context, cache fetch.Cache) ([]*graph.Resource, interface{}, error) {
var resources []*graph.Resource
var objects []{{ $fetcher.AWSType }}
if !conf.getBoolDefaultTrue("aws.{{ $service.Name }}.{{ $fetcher.ResourceType }}.sync") && !getBoolFromContext(ctx, "force") {
conf.Log.Verbose("sync: *disabled* for resource {{ $service.Name }}[{{ $fetcher.ResourceType }}]")
return resources, objects, nil
}
{{- if $fetcher.Multipage }}
paginator := {{ $fetcher.Api }}.New{{ $fetcher.ApiMethod }}Paginator(conf.APIs.{{ Title $fetcher.Api}}, &{{ $fetcher.Input }})
for paginator.HasMorePages() {
out, err := paginator.NextPage(ctx)
if err != nil {
return resources, objects, err
}
{{- if ne $fetcher.OutputsContainers "" }}
for _, all := range out.{{ $fetcher.OutputsContainers }} {
{{- end }}
for _, output := range {{ if ne $fetcher.OutputsContainers "" }}all{{ else }}out{{ end }}.{{ $fetcher.OutputsExtractor }} {
objects = append(objects, output)
var res *graph.Resource
res, err = awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
{{- if ne $fetcher.OutputsContainers "" }}
}
{{- end }}
}
return resources, objects, nil
{{- else }}
out, err := conf.APIs.{{ Title $fetcher.Api}}.{{ $fetcher.ApiMethod }}(ctx, &{{ $fetcher.Input }})
if err != nil {
return resources, objects, err
}
for _, output := range out.{{ $fetcher.OutputsExtractor }} {
objects = append(objects, output)
res, err := awsconv.NewResource(output)
if err != nil {
return resources, objects, err
}
resources = append(resources, res)
}
return resources, objects, nil{{ end }}
}
{{- end }}
{{- end }}
return funcs
}
{{- end }}`
//go:generate go run $GOFILE properties.go mocks.go fetchers.go services.go commands.go acceptance_mocks.go
//go:generate gofmt -s -w ../../../aws
//go:generate goimports -w ../../../aws
//go:generate gofmt -s -w ../../../aws/services
//go:generate goimports -w ../../../aws/services
//go:generate gofmt -s -w ../../../aws/fetch
//go:generate goimports -w ../../../aws/fetch
//go:generate gofmt -s -w ../../../cloud/properties
//go:generate goimports -w ../../../cloud/properties
//go:generate gofmt -s -w ../../../cloud/rdf
//go:generate goimports -w ../../../cloud/rdf
//go:generate gofmt -s -w ../../../aws/spec
//go:generate goimports -w ../../../aws/spec
//go:generate gofmt -s -w ../../../acceptance/aws
//go:generate goimports -w ../../../acceptance/aws
package main
import (
"bytes"
"flag"
"log"
"os"
"path/filepath"
"text/template"
)
var (
ROOT_DIR = filepath.Join("..", "..", "..")
FETCHERS_DIR = filepath.Join(ROOT_DIR, "aws", "fetch")
SERVICES_DIR = filepath.Join(ROOT_DIR, "aws", "services")
SPEC_DIR = filepath.Join(ROOT_DIR, "aws", "spec")
AWSAT_DIR = filepath.Join(ROOT_DIR, "acceptance", "aws")
CLOUD_PROPERTIES_DIR = filepath.Join(ROOT_DIR, "cloud", "properties")
CLOUD_RDF_DIR = filepath.Join(ROOT_DIR, "cloud", "rdf")
)
func main() {
log.SetFlags(0)
log.SetPrefix("[+] ")
flag.Parse()
// fetchers
generateFetcherFuncs()
generateServicesFuncs()
// mocks
generateTestMocks()
// commands
generateCommands()
generateAcceptanceMocks()
generateAcceptanceFactory()
// properties
generateProperties()
generateRDFProperties()
}
func writeTemplateToFile(templ *template.Template, data interface{}, dir, filename string) {
var buff bytes.Buffer
if err := templ.Execute(&buff, data); err != nil {
log.Fatal(err)
}
path := filepath.Join(dir, filename)
if err := os.WriteFile(path, buff.Bytes(), 0666); err != nil {
log.Fatal(err)
}
log.Printf("generated %s", relativePathToRoot(path))
}
func relativePathToRoot(path string) string {
rel, _ := filepath.Rel(ROOT_DIR, path)
return rel
}
/*
Copyright 2017 WALLIX
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 main
import (
"strings"
"text/template"
"github.com/wallix/awless/gen/aws"
)
func generateTestMocks() {
// Build a set of APIs that need types imports based on AWSType references
apisNeedingTypes := make(map[string]bool)
for _, mock := range aws.Mocks() {
for _, f := range mock.Funcs {
if f.AWSType != "" && strings.Contains(f.AWSType, "types.") {
apisNeedingTypes[mock.Api] = true
}
}
}
templ, err := template.New("mocks").Funcs(template.FuncMap{
"Title": strings.Title,
"ToUpper": strings.ToUpper,
"Join": strings.Join,
"SdkModulePath": aws.SdkModulePath,
"NeedsTypesImport": func(api string) bool { return apisNeedingTypes[api] },
}).Parse(mocksTempl)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, aws.Mocks(), SERVICES_DIR, "gen_mocks_test.go")
}
const mocksTempl = `// Auto generated implementation for the AWS cloud service
/*
Copyright 2017 WALLIX
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 awsservices
// DO NOT EDIT - This file was automatically generated with go generate
import (
"context"
{{- range $index, $mock := . }}
{{ $mock.Api }} "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $mock.Api }}"
{{- if NeedsTypesImport $mock.Api }}
{{ $mock.Api }}types "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $mock.Api }}/types"
{{- end }}
{{- end }}
"github.com/wallix/awless/cloud"
)
{{ range $, $mock := . }}
type {{ $mock.Name }} struct {
{{- range $, $func := $mock.Funcs }}
{{- if eq $func.MockFieldType "mapslice" }}
{{ $func.MockField}} map[string][]{{ $func.AWSType }}
{{- else if eq $func.MockFieldType "map" }}
{{ $func.MockField}} map[string]{{ $func.AWSType }}
{{- else }}
{{ $func.MockField}} []{{ $func.AWSType }}
{{- end }}
{{- end }}
}
func (m * {{ $mock.Name }}) Name() string {
return ""
}
func (m * {{ $mock.Name }}) Region() string {
return ""
}
func (m * {{ $mock.Name }}) Profile() string {
return ""
}
func (m * {{ $mock.Name }}) Provider() string {
return ""
}
func (m * {{ $mock.Name }}) ProviderAPI() string {
return ""
}
func (m * {{ $mock.Name }}) ResourceTypes() []string {
return []string{}
}
func (m * {{ $mock.Name }}) Fetch(context.Context) (cloud.GraphAPI, error) {
return nil, nil
}
func (m * {{ $mock.Name }}) IsSyncDisabled() bool {
return false
}
func (m * {{ $mock.Name }}) FetchByType(context.Context, string) (cloud.GraphAPI, error) {
return nil, nil
}
{{ range $, $func := $mock.Funcs }}
{{- if not $func.Manual }}
{{- if eq $func.FuncType "list" }}
func (m * {{ $mock.Name }}) {{ $func.ApiMethod }}(ctx context.Context, input *{{ $func.Input }}, optFns ...func(*{{ $mock.Api }}.Options)) (*{{ $func.Output}}, error) {
return &{{ $func.Output}}{ {{ $func.OutputsExtractor }}: m.{{ $func.MockField}} }, nil
}
{{- end }}
{{- end }}
{{ end }}
{{- end }}
`
/*
Copyright 2017 WALLIX
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 main
import (
"text/template"
"github.com/wallix/awless/gen/aws"
)
func generateProperties() {
templ, err := template.New("properties").Parse(propertiesTempl)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, aws.PropertiesDefinitions, CLOUD_PROPERTIES_DIR, "gen_properties.go")
}
func generateRDFProperties() {
templ, err := template.New("rdfProperties").Parse(rdfPropertiesTempl)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, aws.PropertiesDefinitions, CLOUD_RDF_DIR, "gen_rdf.go")
}
const propertiesTempl = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package properties
const (
{{- range $, $prop := . }}
{{ $prop.AwlessLabel }} = "{{ $prop.AwlessLabel }}"
{{- end }}
)
`
const rdfPropertiesTempl = `/* Copyright 2017 WALLIX
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.
*/
// DO NOT EDIT
// This file was automatically generated with go generate
package rdf
import "github.com/wallix/awless/cloud/properties"
const (
{{- range $, $prop := . }}
{{ $prop.AwlessLabel }} = "{{ $prop.RDFLabel }}"
{{- end }}
)
func init() {
Labels = map[string]string{
{{- range $, $prop := . }}
properties.{{ $prop.AwlessLabel }}: {{ $prop.AwlessLabel }},
{{- end }}
}
}
var Properties = RDFProperties{
{{- range $, $prop := . }}
{{ $prop.AwlessLabel }}: {ID: {{ $prop.AwlessLabel }}, RdfType: "{{ $prop.RDFType }}", RdfsLabel: "{{ $prop.AwlessLabel }}", RdfsDefinedBy: "{{ $prop.RdfsDefinedBy }}", RdfsDataType: "{{ $prop.RdfsDataType }}"},
{{- end }}
}
`
/*
Copyright 2017 WALLIX
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 main
import (
"strings"
"text/template"
"github.com/wallix/awless/gen/aws"
)
func generateServicesFuncs() {
// Build a set of APIs that need types imports based on AWSType references
apisNeedingTypes := make(map[string]bool)
for _, def := range aws.FetchersDefs {
for _, f := range def.Fetchers {
if idx := strings.Index(f.AWSType, "types."); idx > 0 {
apisNeedingTypes[f.AWSType[:idx]] = true
}
}
}
templ, err := template.New("funcs").Funcs(template.FuncMap{
"Title": strings.Title,
"ToUpper": strings.ToUpper,
"Join": strings.Join,
"SdkModulePath": aws.SdkModulePath,
"NeedsTypesImport": func(api string) bool { return apisNeedingTypes[api] },
}).Parse(servicesTempl)
if err != nil {
panic(err)
}
writeTemplateToFile(templ, aws.FetchersDefs, SERVICES_DIR, "gen_services.go")
}
const servicesTempl = `// Auto generated implementation for the AWS cloud service
/*
Copyright 2017 WALLIX
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 awsservices
// DO NOT EDIT - This file was automatically generated with go generate
import (
"context"
"errors"
"sync"
"github.com/aws/aws-sdk-go-v2/aws"
{{- range $index, $service := . }}
{{- range $, $api := $service.Api }}
{{ $api }} "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $api }}"
{{- if NeedsTypesImport $api }}
{{ $api }}types "github.com/aws/aws-sdk-go-v2/service/{{ SdkModulePath $api }}/types"
{{- end }}
{{- end }}
{{- end }}
"github.com/aws/smithy-go"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/fetch"
awsfetch "github.com/wallix/awless/aws/fetch"
tstore "github.com/wallix/triplestore"
)
const accessDenied = "Access Denied"
var ServiceNames = []string{
{{- range $index, $service := . }}
"{{ $service.Name }}",
{{- end }}
}
var ResourceTypes = []string {
{{- range $index, $service := . }}
{{- range $idx, $fetcher := $service.Fetchers }}
"{{ $fetcher.ResourceType }}",
{{- end }}
{{- end }}
}
var ServicePerAPI = map[string]string {
{{- range $index, $service := . }}
{{- range $, $api := $service.Api }}
"{{ $api }}": "{{ $service.Name }}",
{{- end }}
{{- end }}
}
var ServicePerResourceType = map[string]string {
{{- range $index, $service := . }}
{{- range $idx, $fetcher := $service.Fetchers }}
"{{ $fetcher.ResourceType }}": "{{ $service.Name }}",
{{- end }}
{{- end }}
}
var APIPerResourceType = map[string]string {
{{- range $index, $service := . }}
{{- range $idx, $fetcher := $service.Fetchers }}
"{{ $fetcher.ResourceType }}": "{{ $fetcher.Api }}",
{{- end }}
{{- end }}
}
{{ range $index, $service := . }}
type {{ Title $service.Name }} struct {
fetcher fetch.Fetcher
region, profile string
config map[string]interface{}
log *logger.Logger
{{- range $, $api := $service.Api }}
{{ Title $api }}Client *{{ $api }}.Client
{{- end }}
}
func New{{ Title $service.Name }}(cfg aws.Config, profile string, extraConf map[string]interface{}, log *logger.Logger) cloud.Service {
{{- if $service.Global }}
region := "global"
{{- else}}
region := cfg.Region
{{- end}}
{{- range $, $api := $service.Api }}
{{ $api }}Client := {{ $api }}.NewFromConfig(cfg)
{{- end }}
fetchConfig := awsfetch.NewConfig(
{{- range $, $api := $service.Api }}
{{ $api }}Client,
{{- end }}
)
fetchConfig.Extra = extraConf
fetchConfig.Log = log
return &{{ Title $service.Name }}{
{{- range $, $api := $service.Api }}
{{ Title $api }}Client: {{ $api }}Client,
{{- end }}
fetcher: fetch.NewFetcher(awsfetch.Build{{ Title $service.Name }}FetchFuncs(fetchConfig)),
config: extraConf,
region: region,
profile: profile,
log: log,
}
}
func (s *{{ Title $service.Name }}) Name() string {
return "{{ $service.Name }}"
}
func (s *{{ Title $service.Name }}) Region() string {
return s.region
}
func (s *{{ Title $service.Name }}) Profile() string {
return s.profile
}
func (s *{{ Title $service.Name }}) ResourceTypes() []string {
return []string{
{{- range $index, $fetcher := $service.Fetchers }}
"{{ $fetcher.ResourceType }}",
{{- end }}
}
}
func (s *{{ Title $service.Name }}) Fetch(ctx context.Context) (cloud.GraphAPI, error) {
if s.IsSyncDisabled() {
return graph.NewGraph(), nil
}
allErrors := new(fetch.Error)
gph, err := s.fetcher.Fetch(context.WithValue(ctx, "region", s.region))
defer s.fetcher.Reset()
for _, e := range *fetch.WrapError(err) {
switch ee := e.(type) {
case nil:
continue
default:
var ae smithy.APIError
if errors.As(ee, &ae) && ae.ErrorMessage() == accessDenied {
allErrors.Add(cloud.ErrFetchAccessDenied)
} else {
allErrors.Add(ee)
}
}
}
if err := gph.AddResource(graph.InitResource(cloud.Region, s.region)); err != nil {
return gph, err
}
snap := gph.AsRDFGraphSnaphot()
errc := make(chan error)
var wg sync.WaitGroup
{{- range $index, $fetcher := $service.Fetchers }}
if getBool(s.config, "aws.{{ $service.Name }}.{{ $fetcher.ResourceType }}.sync", true) {
list, err := s.fetcher.Get("{{ $fetcher.ResourceType }}_objects")
if err != nil {
return gph, err
}
if _, ok := list.([]{{ $fetcher.AWSType }}); !ok {
return gph, errors.New("cannot cast to '[]{{ $fetcher.AWSType }}' type from fetch context")
}
for _, r := range list.([]{{ $fetcher.AWSType }}) {
for _, fn := range addParentsFns["{{ $fetcher.ResourceType }}"] {
wg.Add(1)
go func(f addParentFn, snap tstore.RDFGraph, region string, res *{{ $fetcher.AWSType }}) {
defer wg.Done()
err := f(gph, snap, region, res)
if err != nil {
errc <- err
return
}
}(fn, snap, s.region, &r)
}
}
}
{{- end }}
go func() {
wg.Wait()
close(errc)
}()
for err := range errc {
if err != nil {
allErrors.Add(err)
}
}
if allErrors.Any() {
return gph, allErrors
}
return gph, nil
}
func (s *{{ Title $service.Name }}) FetchByType(ctx context.Context, t string) (cloud.GraphAPI, error) {
defer s.fetcher.Reset()
return s.fetcher.FetchByType(context.WithValue(ctx, "region", s.region), t)
}
func (s *{{ Title $service.Name }}) IsSyncDisabled() bool {
return !getBool(s.config, "aws.{{ $service.Name }}.sync", true)
}
{{ end }}`
/*
Copyright 2017 WALLIX
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 aws
import "strings"
type mockDef struct {
Api, Name string
Funcs []*mockFuncDef
}
type mockFuncDef struct {
FuncType, AWSType, ApiMethod, Input, Output, OutputsExtractor, OutputsContainers string
Manual bool
Multipage bool
NextPageMarker string
MockField, MockFieldType string
}
var mocksDefs = []*mockDef{
{
Api: "ec2",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "ec2types.Instance", ApiMethod: "DescribeInstances", Input: "ec2.DescribeInstancesInput", Output: "ec2.DescribeInstancesOutput", OutputsExtractor: "Instances", OutputsContainers: "Reservations", Multipage: true, NextPageMarker: "NextToken", Manual: true},
{FuncType: "list", AWSType: "ec2types.Subnet", ApiMethod: "DescribeSubnets", Input: "ec2.DescribeSubnetsInput", Output: "ec2.DescribeSubnetsOutput", OutputsExtractor: "Subnets"},
{FuncType: "list", AWSType: "ec2types.Vpc", ApiMethod: "DescribeVpcs", Input: "ec2.DescribeVpcsInput", Output: "ec2.DescribeVpcsOutput", OutputsExtractor: "Vpcs"},
{FuncType: "list", AWSType: "ec2types.KeyPairInfo", ApiMethod: "DescribeKeyPairs", Input: "ec2.DescribeKeyPairsInput", Output: "ec2.DescribeKeyPairsOutput", OutputsExtractor: "KeyPairs"},
{FuncType: "list", AWSType: "ec2types.SecurityGroup", ApiMethod: "DescribeSecurityGroups", Input: "ec2.DescribeSecurityGroupsInput", Output: "ec2.DescribeSecurityGroupsOutput", OutputsExtractor: "SecurityGroups"},
{FuncType: "list", AWSType: "ec2types.Volume", ApiMethod: "DescribeVolumes", Input: "ec2.DescribeVolumesInput", Output: "ec2.DescribeVolumesOutput", OutputsExtractor: "Volumes", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "ec2types.InternetGateway", ApiMethod: "DescribeInternetGateways", Input: "ec2.DescribeInternetGatewaysInput", Output: "ec2.DescribeInternetGatewaysOutput", OutputsExtractor: "InternetGateways"},
{FuncType: "list", AWSType: "ec2types.NatGateway", ApiMethod: "DescribeNatGateways", Input: "ec2.DescribeNatGatewaysInput", Output: "ec2.DescribeNatGatewaysOutput", OutputsExtractor: "NatGateways"},
{FuncType: "list", AWSType: "ec2types.RouteTable", ApiMethod: "DescribeRouteTables", Input: "ec2.DescribeRouteTablesInput", Output: "ec2.DescribeRouteTablesOutput", OutputsExtractor: "RouteTables"},
{FuncType: "list", AWSType: "ec2types.AvailabilityZone", ApiMethod: "DescribeAvailabilityZones", Input: "ec2.DescribeAvailabilityZonesInput", Output: "ec2.DescribeAvailabilityZonesOutput", OutputsExtractor: "AvailabilityZones"},
{FuncType: "list", AWSType: "ec2types.Image", ApiMethod: "DescribeImages", Input: "ec2.DescribeImagesInput", Output: "ec2.DescribeImagesOutput", OutputsExtractor: "Images"},
{FuncType: "list", AWSType: "ec2types.ImportImageTask", ApiMethod: "DescribeImportImageTasks", Input: "ec2.DescribeImportImageTasksInput", Output: "ec2.DescribeImportImageTasksOutput", OutputsExtractor: "ImportImageTasks"},
{FuncType: "list", AWSType: "ec2types.Address", ApiMethod: "DescribeAddresses", Input: "ec2.DescribeAddressesInput", Output: "ec2.DescribeAddressesOutput", OutputsExtractor: "Addresses"},
{FuncType: "list", AWSType: "ec2types.Snapshot", ApiMethod: "DescribeSnapshots", Input: "ec2.DescribeSnapshotsInput", Output: "ec2.DescribeSnapshotsOutput", OutputsExtractor: "Snapshots", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "ec2types.NetworkInterface", ApiMethod: "DescribeNetworkInterfaces", Input: "ec2.DescribeNetworkInterfacesInput", Output: "ec2.DescribeNetworkInterfacesOutput", OutputsExtractor: "NetworkInterfaces"},
},
},
{
Api: "elbv2",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "elbv2types.LoadBalancer", ApiMethod: "DescribeLoadBalancers", Input: "elbv2.DescribeLoadBalancersInput", Output: "elbv2.DescribeLoadBalancersOutput", OutputsExtractor: "LoadBalancers", Multipage: true, NextPageMarker: "NextMarker"},
{FuncType: "list", AWSType: "elbv2types.TargetGroup", ApiMethod: "DescribeTargetGroups", Input: "elbv2.DescribeTargetGroupsInput", Output: "elbv2.DescribeTargetGroupsOutput", OutputsExtractor: "TargetGroups"},
{FuncType: "list", AWSType: "elbv2types.Listener", Manual: true},
{FuncType: "list", AWSType: "elbv2types.TargetHealthDescription", Manual: true, MockFieldType: "mapslice"},
},
},
{
Api: "elb",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "elbtypes.LoadBalancerDescription", ApiMethod: "DescribeLoadBalancers", Input: "elb.DescribeLoadBalancersInput", Output: "elb.DescribeLoadBalancersOutput", OutputsExtractor: "LoadBalancerDescriptions", Multipage: true, NextPageMarker: "NextMarker"},
},
},
{
Api: "rds",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "rdstypes.DBInstance", ApiMethod: "DescribeDBInstances", Input: "rds.DescribeDBInstancesInput", Output: "rds.DescribeDBInstancesOutput", OutputsExtractor: "DBInstances", Multipage: true, NextPageMarker: "Marker"},
{FuncType: "list", AWSType: "rdstypes.DBSubnetGroup", ApiMethod: "DescribeDBSubnetGroups", Input: "rds.DescribeDBSubnetGroupsInput", Output: "rds.DescribeDBSubnetGroupsOutput", OutputsExtractor: "DBSubnetGroups", Multipage: true, NextPageMarker: "Marker"},
},
},
{
Api: "autoscaling",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "autoscalingtypes.LaunchConfiguration", ApiMethod: "DescribeLaunchConfigurations", Input: "autoscaling.DescribeLaunchConfigurationsInput", Output: "autoscaling.DescribeLaunchConfigurationsOutput", OutputsExtractor: "LaunchConfigurations", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "autoscalingtypes.AutoScalingGroup", ApiMethod: "DescribeAutoScalingGroups", Input: "autoscaling.DescribeAutoScalingGroupsInput", Output: "autoscaling.DescribeAutoScalingGroupsOutput", OutputsExtractor: "AutoScalingGroups", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "autoscalingtypes.ScalingPolicy", ApiMethod: "DescribePolicies", Input: "autoscaling.DescribePoliciesInput", Output: "autoscaling.DescribePoliciesOutput", OutputsExtractor: "ScalingPolicies", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "acm",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "acmtypes.CertificateSummary", ApiMethod: "ListCertificates", Input: "acm.ListCertificatesInput", Output: "acm.ListCertificatesOutput", OutputsExtractor: "CertificateSummaryList", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "iam",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "iamtypes.UserDetail", Manual: true},
{FuncType: "list", AWSType: "iamtypes.GroupDetail", ApiMethod: "GetAccountAuthorizationDetails", Input: "iam.GetAccountAuthorizationDetailsInput", Output: "iam.GetAccountAuthorizationDetailsOutput", OutputsExtractor: "GroupDetailList", Multipage: true, NextPageMarker: "Marker", Manual: true},
{FuncType: "list", AWSType: "iamtypes.RoleDetail", ApiMethod: "GetAccountAuthorizationDetails", Input: "iam.GetAccountAuthorizationDetailsInput", Output: "iam.GetAccountAuthorizationDetailsOutput", OutputsExtractor: "RoleDetailList", Multipage: true, NextPageMarker: "Marker", Manual: true},
{FuncType: "list", AWSType: "iamtypes.Policy", ApiMethod: "ListPolicies", Input: "iam.ListPoliciesInput", Output: "iam.ListPoliciesOutput", OutputsExtractor: "Policies", Multipage: true, NextPageMarker: "Marker", Manual: true},
{FuncType: "list", AWSType: "iamtypes.AccessKeyMetadata", ApiMethod: "ListAccessKeys", Input: "iam.ListAccessKeysInput", Output: "iam.ListAccessKeysOutput", OutputsExtractor: "AccessKeyMetadata", Multipage: true, NextPageMarker: "Marker"},
{FuncType: "list", AWSType: "iamtypes.InstanceProfile", ApiMethod: "ListInstanceProfiles", Input: "iam.ListInstanceProfilesInput", Output: "iam.ListInstanceProfilesOutput", OutputsExtractor: "InstanceProfiles", Multipage: true, NextPageMarker: "Marker"},
{FuncType: "list", AWSType: "iamtypes.ManagedPolicyDetail", Manual: true},
{FuncType: "list", AWSType: "iamtypes.User", Manual: true},
{FuncType: "list", AWSType: "iamtypes.VirtualMFADevice", ApiMethod: "ListVirtualMFADevices", Input: "iam.ListVirtualMFADevicesInput", Output: "iam.ListVirtualMFADevicesOutput", OutputsExtractor: "VirtualMFADevices", Multipage: true, NextPageMarker: "Marker"},
},
},
{
Api: "s3",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "s3types.Bucket", Manual: true, MockFieldType: "mapslice"},
{FuncType: "list", AWSType: "s3types.Object", Manual: true, MockFieldType: "mapslice"},
{FuncType: "list", AWSType: "s3types.Grant", Manual: true, MockFieldType: "mapslice"},
},
},
{
Api: "sns",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "snstypes.Subscription", ApiMethod: "ListSubscriptions", Input: "sns.ListSubscriptionsInput", Output: "sns.ListSubscriptionsOutput", OutputsExtractor: "Subscriptions", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "snstypes.Topic", ApiMethod: "ListTopics", Input: "sns.ListTopicsInput", Output: "sns.ListTopicsOutput", OutputsExtractor: "Topics", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "sqs",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "string", ApiMethod: "ListQueues", Input: "sqs.ListQueuesInput", Output: "sqs.ListQueuesOutput", OutputsExtractor: "QueueUrls"},
{FuncType: "list", AWSType: "map[string]string", Manual: true, MockFieldType: "map"},
},
},
{
Api: "route53",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "route53types.HostedZone", ApiMethod: "ListHostedZones", Input: "route53.ListHostedZonesInput", Output: "route53.ListHostedZonesOutput", OutputsExtractor: "HostedZones", Multipage: true, NextPageMarker: "NextMarker"},
{FuncType: "list", AWSType: "route53types.ResourceRecordSet", Manual: true, MockFieldType: "mapslice"},
},
},
{
Api: "lambda",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "lambdatypes.FunctionConfiguration", ApiMethod: "ListFunctions", Input: "lambda.ListFunctionsInput", Output: "lambda.ListFunctionsOutput", OutputsExtractor: "Functions", Multipage: true, NextPageMarker: "NextMarker"},
},
},
{
Api: "cloudwatch",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "cloudwatchtypes.Metric", ApiMethod: "ListMetrics", Input: "cloudwatch.ListMetricsInput", Output: "cloudwatch.ListMetricsOutput", OutputsExtractor: "Metrics", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "cloudwatchtypes.MetricAlarm", ApiMethod: "DescribeAlarms", Input: "cloudwatch.DescribeAlarmsInput", Output: "cloudwatch.DescribeAlarmsOutput", OutputsExtractor: "MetricAlarms", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "cloudfront",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "cloudfronttypes.DistributionSummary", Manual: true},
},
},
{
Api: "cloudformation",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "cloudformationtypes.Stack", ApiMethod: "DescribeStacks", Input: "cloudformation.DescribeStacksInput", Output: "cloudformation.DescribeStacksOutput", OutputsExtractor: "Stacks", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "ecr",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "ecrtypes.Repository", ApiMethod: "DescribeRepositories", Input: "ecr.DescribeRepositoriesInput", Output: "ecr.DescribeRepositoriesOutput", OutputsExtractor: "Repositories", Multipage: true, NextPageMarker: "NextToken"},
},
},
{
Api: "ecs",
Funcs: []*mockFuncDef{
{FuncType: "list", AWSType: "ecstypes.Cluster", Manual: true},
{FuncType: "list", MockField: "clusterNames", AWSType: "string", ApiMethod: "ListClusters", Input: "ecs.ListClustersInput", Output: "ecs.ListClustersOutput", OutputsExtractor: "ClusterArns", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", AWSType: "ecstypes.TaskDefinition", Manual: true},
{FuncType: "list", MockField: "taskdefinitionNames", AWSType: "string", ApiMethod: "ListTaskDefinitions", Input: "ecs.ListTaskDefinitionsInput", Output: "ecs.ListTaskDefinitionsOutput", OutputsExtractor: "TaskDefinitionArns", Multipage: true, NextPageMarker: "NextToken"},
{FuncType: "list", MockFieldType: "mapslice", AWSType: "ecstypes.Task", Manual: true},
{FuncType: "list", MockFieldType: "mapslice", MockField: "tasksNames", AWSType: "string", Manual: true},
{FuncType: "list", MockFieldType: "mapslice", MockField: "containerinstancesNames", AWSType: "string", Manual: true},
{FuncType: "list", MockFieldType: "mapslice", AWSType: "ecstypes.ContainerInstance", Manual: true},
},
},
}
func Mocks() []*mockDef {
for _, def := range mocksDefs {
def.Name = "mock" + strings.Title(def.Api)
for _, f := range def.Funcs {
if f.MockField == "" {
f.MockField = nameFromAwsType(f.AWSType)
}
}
}
return mocksDefs
}
func nameFromAwsType(awstype string) string {
if awstype == "map[string]string" {
return "attributes"
}
splits := strings.Split(awstype, ".")
return strings.ToLower(splits[len(splits)-1]) + "s"
}
/*
Copyright 2017 WALLIX
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 graph
import (
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud/rdf"
)
var (
DefaultDiffer = &hierarchicDiffer{rdf.ParentOf}
MetaPredicate = "meta"
)
const (
extraLit = "extra"
missingLit = "missing"
)
type Differ interface {
Run(string, *Graph, *Graph) (*Diff, error)
}
type Diff struct {
fromGraph *Graph
toGraph *Graph
mergedGraph *Graph
hasDiffs bool
}
func NewDiff(fromG, toG *Graph) *Diff {
return &Diff{fromGraph: fromG, toGraph: toG}
}
func (d *Diff) FromGraph() *Graph {
return d.fromGraph
}
func (d *Diff) ToGraph() *Graph {
return d.toGraph
}
func (d *Diff) MergedGraph() *Graph {
d.mergedGraph = NewGraph()
d.mergedGraph.store.Add(d.toGraph.store.Snapshot().Triples()...)
fromTriples := d.fromGraph.store.Snapshot().Triples()
for _, fromT := range fromTriples {
if MetaPredicate == fromT.Predicate() {
d.mergedGraph.store.Add(tstore.SubjPred(fromT.Subject(), MetaPredicate).StringLiteral(missingLit))
} else {
d.mergedGraph.store.Add(fromT)
}
}
return d.mergedGraph
}
func (d *Diff) HasDiff() bool {
return d.hasDiffs
}
type hierarchicDiffer struct {
predicate string
}
func (d *hierarchicDiffer) Run(root string, from *Graph, to *Graph) (*Diff, error) {
diff := &Diff{fromGraph: from, toGraph: to}
fromSnap := from.store.Snapshot()
toSnap := to.store.Snapshot()
maxCount := max(uint32(fromSnap.Count()), uint32(toSnap.Count()))
processing := make(chan string, maxCount)
if maxCount < 1 {
return diff, nil
}
processing <- root
for len(processing) > 0 {
current := <-processing
extras, missings, commons, err := compareChildTriplesOf(d.predicate, current, fromSnap, toSnap)
if err != nil {
return diff, err
}
for _, extra := range extras {
res, ok := extra.Object().Resource()
if ok {
diff.hasDiffs = true
diff.toGraph.store.Add(tstore.SubjPred(res, MetaPredicate).StringLiteral(extraLit))
processing <- res
}
}
for _, missing := range missings {
res, ok := missing.Object().Resource()
if ok {
diff.hasDiffs = true
diff.fromGraph.store.Add(tstore.SubjPred(res, MetaPredicate).StringLiteral(extraLit))
processing <- res
}
}
for _, nextNodeToProcess := range commons {
res, ok := nextNodeToProcess.Object().Resource()
if ok {
processing <- res
}
}
}
return diff, nil
}
func compareChildTriplesOf(onPredicate, root string, fromGraph tstore.RDFGraph, toGraph tstore.RDFGraph) ([]tstore.Triple, []tstore.Triple, []tstore.Triple, error) {
var extras, missings, commons []tstore.Triple
fromTriples := fromGraph.WithSubjPred(root, onPredicate)
toTriples := toGraph.WithSubjPred(root, onPredicate)
extras = append(extras, subtractTriples(toTriples, fromTriples)...)
missings = append(missings, subtractTriples(fromTriples, toTriples)...)
commons = append(commons, intersectTriples(fromTriples, toTriples)...)
return extras, missings, commons, nil
}
func intersectTriples(a, b []tstore.Triple) []tstore.Triple {
var inter []tstore.Triple
for i := 0; i < len(a); i++ {
for j := 0; j < len(b); j++ {
if a[i].Equal(b[j]) {
inter = append(inter, a[i])
}
}
}
return inter
}
func subtractTriples(a, b []tstore.Triple) []tstore.Triple {
var sub []tstore.Triple
for i := 0; i < len(a); i++ {
var found bool
for j := 0; j < len(b); j++ {
if a[i].Equal(b[j]) {
found = true
}
}
if !found {
sub = append(sub, a[i])
}
}
return sub
}
func max(a, b uint32) uint32 {
if a < b {
return b
}
return a
}
package graph
import (
"fmt"
"strings"
)
type FilterFn func(*Resource) bool
func (g *Graph) Filter(entity string, filters ...FilterFn) (*Graph, error) {
return g.filter(applyAnd, entity, filters...)
}
func (g *Graph) OrFilter(entity string, filters ...FilterFn) (*Graph, error) {
return g.filter(applyOr, entity, filters...)
}
func (g *Graph) filter(apply func(filters ...FilterFn) FilterFn, entity string, filters ...FilterFn) (*Graph, error) {
filtered := NewGraph()
all, err := g.GetAllResources(entity)
if err != nil {
return filtered, err
}
for _, r := range all {
if apply(filters...)(r) {
filtered.AddResource(r)
}
}
return filtered, nil
}
func BuildPropertyFilterFunc(key, val string) FilterFn {
return func(r *Resource) bool {
return strings.Contains(strings.ToLower(fmt.Sprint(r.properties[key])), strings.ToLower(val))
}
}
func BuildTagFilterFunc(key, val string) FilterFn {
return func(r *Resource) bool {
tags, ok := r.properties["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
if fmt.Sprintf("%s=%s", key, val) == t {
return true
}
}
return false
}
}
func BuildTagKeyFilterFunc(key string) FilterFn {
return func(r *Resource) bool {
tags, ok := r.properties["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
splits := strings.Split(t, "=")
if len(splits) > 0 {
if splits[0] == key {
return true
}
}
}
return false
}
}
func BuildTagValueFilterFunc(value string) FilterFn {
return func(r *Resource) bool {
tags, ok := r.properties["Tags"].([]string)
if !ok {
return false
}
for _, t := range tags {
splits := strings.Split(t, "=")
if len(splits) > 1 {
if splits[1] == value {
return true
}
}
}
return false
}
}
func applyAnd(filters ...FilterFn) FilterFn {
return func(r *Resource) bool {
include := true
for _, f := range filters {
include = include && f(r)
}
return include
}
}
func applyOr(filters ...FilterFn) FilterFn {
return func(r *Resource) bool {
if len(filters) == 0 {
return true
}
for _, f := range filters {
if f(r) {
return true
}
}
return false
}
}
/*
Copyright 2017 WALLIX
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 graph
import (
"bytes"
"fmt"
"io"
"os"
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/rdf"
)
type Graph struct {
store tstore.Source
}
func NewGraph() *Graph {
return &Graph{tstore.NewSource()}
}
func NewGraphFromFiles(files ...string) (cloud.GraphAPI, error) {
g := NewGraph()
var readers []io.Reader
for _, f := range files {
if reader, err := os.Open(f); err != nil {
return g, err
} else {
readers = append(readers, reader)
}
}
err := g.UnmarshalFromReaders(readers...)
return g, err
}
func (g *Graph) AsRDFGraphSnaphot() tstore.RDFGraph {
return g.store.Snapshot()
}
func (g *Graph) AddResource(resources ...*Resource) error {
for _, res := range resources {
triples, err := res.marshalFullRDF()
if err != nil {
return err
}
for relType, attachedRes := range res.relations {
switch relType {
case rdf.ChildrenOfRel:
for _, attached := range attachedRes {
if err := g.AddParentRelation(attached, res); err != nil {
return err
}
}
case rdf.DependingOnRel:
for _, attached := range attachedRes {
if err := g.AddAppliesOnRelation(attached, res); err != nil {
return err
}
}
}
}
g.store.Add(triples...)
}
return nil
}
func (g *Graph) AddGraph(other *Graph) {
g.store.Add(other.store.CopyTriples()...)
}
func (g *Graph) AddParentRelation(parent, child *Resource) error {
return g.addRelation(parent, child, rdf.ParentOf)
}
func (g *Graph) AddAppliesOnRelation(parent, child *Resource) error {
return g.addRelation(parent, child, rdf.ApplyOn)
}
func (g *Graph) GetResource(t string, id string) (*Resource, error) {
resource := InitResource(t, id)
snap := g.store.Snapshot()
if err := resource.unmarshalFullRdf(snap); err != nil {
return resource, err
}
if err := resource.unmarshalMeta(snap); err != nil {
return resource, err
}
return resource, nil
}
func (g *Graph) FindResource(id string) (*Resource, error) {
byId := &ById{id}
resources, err := byId.Resolve(g.store.Snapshot())
if err != nil {
return nil, err
}
if len(resources) == 1 {
return resources[0], nil
} else if len(resources) > 1 {
return nil, fmt.Errorf("multiple resources with id '%s' found", id)
}
return nil, nil
}
func (g *Graph) FindResourcesByProperty(key string, value interface{}) ([]*Resource, error) {
byProperty := ByProperty{key, value}
return byProperty.Resolve(g.store.Snapshot())
}
func (g *Graph) FindAncestor(res *Resource, resourceType string) *Resource {
var found *Resource
find := func(res *Resource, depth int) error {
if res.Type() == resourceType {
found = res
return nil
}
return nil
}
g.Accept(&ParentsVisitor{From: res, Each: find})
return found
}
func (g *Graph) GetAllResources(typs ...string) ([]*Resource, error) {
byTypes := &ByTypes{typs}
return byTypes.Resolve(g.store.Snapshot())
}
func (g *Graph) ResolveResources(resolvers ...Resolver) ([]*Resource, error) {
var resources []*Resource
snap := g.store.Snapshot()
for _, resolv := range resolvers {
rs, err := resolv.Resolve(snap)
if err != nil {
return resources, err
}
resources = append(resources, rs...)
}
return resources, nil
}
func (g *Graph) FilterGraph(q cloud.Query) (cloud.GraphAPI, error) {
if len(q.ResourceType) != 1 {
return nil, fmt.Errorf("invalid query: must have exactly one resource type, got %d", len(q.ResourceType))
}
resourceType := q.ResourceType[0]
return g.Filter(resourceType, func(r *Resource) bool {
if q.Matcher == nil {
return true
}
return q.Matcher.Match(r)
})
}
func (g *Graph) Find(q cloud.Query) ([]cloud.Resource, error) {
var resources []*Resource
var err error
switch len(q.ResourceType) {
case 0:
return nil, fmt.Errorf("invalid query: need at least one resource type")
case 1:
var filtered cloud.GraphAPI
filtered, err = g.FilterGraph(q)
if err != nil {
return nil, err
}
resources, err = filtered.(*Graph).GetAllResources(q.ResourceType[0])
if err != nil {
return nil, err
}
default:
if q.Matcher != nil {
return nil, fmt.Errorf("invalid query: can not filter with multiple resource types")
}
resources, err = g.GetAllResources(q.ResourceType...)
if err != nil {
return nil, err
}
}
var res []cloud.Resource
for _, r := range resources {
res = append(res, r)
}
return res, nil
}
func (g *Graph) FindWithProperties(props map[string]interface{}) ([]cloud.Resource, error) {
var resolvers []Resolver
for k, v := range props {
resolvers = append(resolvers, &ByProperty{Key: k, Value: v})
}
resources, err := g.ResolveResources(&And{Resolvers: resolvers})
if err != nil {
return nil, err
}
var res []cloud.Resource
for _, r := range resources {
res = append(res, r)
}
return res, nil
}
func (g *Graph) FindOne(q cloud.Query) (cloud.Resource, error) {
filtered, err := g.FilterGraph(q)
if err != nil {
return nil, err
}
resources, err := filtered.(*Graph).GetAllResources(q.ResourceType[0])
if err != nil {
return nil, err
}
switch len(resources) {
case 0:
return nil, fmt.Errorf("resource not found")
case 1:
return resources[0], nil
default:
return nil, fmt.Errorf("multiple resources found")
}
}
func (g *Graph) Merge(mg cloud.GraphAPI) error {
toMerge, ok := mg.(*Graph)
if !ok {
return fmt.Errorf("can not merge graphs, graph to merge is not a *graph.Graph, but a %T", mg)
}
g.AddGraph(toMerge)
return nil
}
func (g *Graph) ResourceRelations(from cloud.Resource, relation string, recursive bool) (collect []cloud.Resource, err error) {
collectFunc := func(r *Resource, depth int) error {
if depth == 1 || recursive {
collect = append(collect, r)
}
return nil
}
switch relation {
case rdf.ChildrenOfRel:
err = g.Accept(&ChildrenVisitor{From: from.(*Resource), IncludeFrom: false, Relation: rdf.ParentOf, Each: collectFunc})
case rdf.DependingOnRel:
err = g.Accept(&ParentsVisitor{From: from.(*Resource), IncludeFrom: false, Relation: rdf.ApplyOn, Each: collectFunc})
case rdf.ApplyOn:
err = g.Accept(&ChildrenVisitor{From: from.(*Resource), IncludeFrom: false, Relation: rdf.ApplyOn, Each: collectFunc})
default:
err = g.Accept(&ParentsVisitor{From: from.(*Resource), IncludeFrom: false, Relation: relation, Each: collectFunc})
}
return
}
func (g *Graph) VisitRelations(from cloud.Resource, relation string, includeFrom bool, each func(cloud.Resource, int) error) (err error) {
eachFunc := func(r *Resource, depth int) error {
return each(r, depth)
}
switch relation {
case rdf.ChildrenOfRel:
err = g.Accept(&ChildrenVisitor{From: from.(*Resource), IncludeFrom: includeFrom, Relation: rdf.ParentOf, Each: eachFunc})
case rdf.DependingOnRel:
err = g.Accept(&ParentsVisitor{From: from.(*Resource), IncludeFrom: includeFrom, Relation: rdf.ApplyOn, Each: eachFunc})
case rdf.ApplyOn:
err = g.Accept(&ChildrenVisitor{From: from.(*Resource), IncludeFrom: includeFrom, Relation: rdf.ApplyOn, Each: eachFunc})
default:
err = g.Accept(&ParentsVisitor{From: from.(*Resource), IncludeFrom: includeFrom, Relation: relation, Each: eachFunc})
}
return
}
func (g *Graph) ResourceSiblings(res cloud.Resource) (collect []cloud.Resource, err error) {
err = g.Accept(&SiblingsVisitor{From: res.(*Resource), IncludeFrom: false, Each: func(r *Resource, depth int) error {
collect = append(collect, r)
return nil
}})
return collect, err
}
func ResolveResourcesWithProp(snap tstore.RDFGraph, resType, propName, propVal string) ([]*Resource, error) {
resolv := ByTypeAndProperty{
Type: resType,
Key: propName,
Value: propVal,
}
return resolv.Resolve(snap)
}
func (g *Graph) ListResourcesDependingOn(start *Resource) ([]*Resource, error) {
var resources []*Resource
snap := g.store.Snapshot()
for _, tri := range snap.WithPredObj(rdf.ApplyOn, tstore.Resource(start.Id())) {
id := tri.Subject()
rT, err := resolveResourceType(snap, id)
if err != nil {
if err == errTypeNotFound {
resources = append(resources, NotFoundResource(id))
continue
}
return resources, err
}
res, err := g.GetResource(rT, id)
if err != nil {
return resources, err
}
resources = append(resources, res)
}
return resources, nil
}
func (g *Graph) ListResourcesAppliedOn(start *Resource) ([]*Resource, error) {
var resources []*Resource
snap := g.store.Snapshot()
for _, tri := range snap.WithSubjPred(start.Id(), rdf.ApplyOn) {
id, ok := tri.Object().Resource()
if !ok {
return resources, fmt.Errorf("triple %s %s: object is not a resource identifier", start.Id(), rdf.ApplyOn)
}
rT, err := resolveResourceType(snap, id)
if err != nil {
if err == errTypeNotFound {
resources = append(resources, NotFoundResource(id))
continue
}
return resources, err
}
res, err := g.GetResource(rT, id)
if err != nil {
return resources, err
}
resources = append(resources, res)
}
return resources, nil
}
func (g *Graph) Accept(v Visitor) error {
return v.Visit(g)
}
func (g *Graph) Unmarshal(data []byte) error {
ts, err := tstore.NewAutoDecoder(bytes.NewReader(data)).Decode()
if err != nil {
return err
}
g.store.Add(ts...)
return nil
}
func (g *Graph) UnmarshalFromReaders(readers ...io.Reader) error {
dec := tstore.NewDatasetDecoder(tstore.NewLenientNTDecoder, readers...)
ts, err := dec.Decode()
if err != nil {
return err
}
g.store.Add(ts...)
return nil
}
func (g *Graph) MustMarshal() string {
var buff bytes.Buffer
if err := tstore.NewLenientNTEncoder(&buff).Encode(g.store.CopyTriples()...); err != nil {
panic(err)
}
return buff.String()
}
func (g *Graph) MarshalTo(w io.Writer) error {
return tstore.NewLenientNTEncoder(w).Encode(g.store.CopyTriples()...)
}
func (g *Graph) addRelation(one, other *Resource, pred string) error {
g.store.Add(tstore.SubjPred(one.Id(), pred).Resource(other.Id()))
return nil
}
package graph
import (
"fmt"
"math/rand"
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud/rdf"
)
func getPropertyValue(gph tstore.RDFGraph, propObj tstore.Object, prop string) (interface{}, error) {
rdfProp, err := rdf.Properties.Get(prop)
if err != nil {
return "", err
}
definedBy := rdfProp.RdfsDefinedBy
dataType := rdfProp.RdfsDataType
switch {
case definedBy == rdf.RdfsLiteral, (definedBy == rdf.RdfsList) && (dataType == rdf.XsdString):
return tstore.ParseLiteral(propObj)
case definedBy == rdf.RdfsList && dataType == rdf.NetFirewallRule:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': object not resource identifier", prop)
}
rule := &FirewallRule{}
err := rule.unmarshalFromTriples(gph, id)
if err != nil {
return nil, err
}
return rule, nil
case definedBy == rdf.RdfsClass, dataType == rdf.RdfsClass:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': '%+v' object not resource identifier", prop, propObj)
}
return id, nil
case definedBy == rdf.RdfsList && dataType == rdf.NetRoute:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': object not resource identifier", prop)
}
route := &Route{}
err := route.unmarshalFromTriples(gph, id)
if err != nil {
return nil, err
}
return route, nil
case definedBy == rdf.RdfsList && dataType == rdf.Grant:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': object not resource identifier", prop)
}
grant := &Grant{}
err := grant.unmarshalFromTriples(gph, id)
if err != nil {
return nil, err
}
return grant, nil
case definedBy == rdf.RdfsList && dataType == rdf.KeyValue:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': object not resource identifier", prop)
}
kv := &KeyValue{}
err := kv.unmarshalFromTriples(gph, id)
if err != nil {
return nil, err
}
return kv, nil
case definedBy == rdf.RdfsList && dataType == rdf.DistributionOrigin:
id, ok := propObj.Resource()
if !ok {
return nil, fmt.Errorf("get property '%s': object not resource identifier", prop)
}
o := &DistributionOrigin{}
err := o.unmarshalFromTriples(gph, id)
if err != nil {
return nil, err
}
return o, nil
default:
return "", fmt.Errorf("get property value: %s is neither literal nor class, nor list", definedBy)
}
}
func extractUniqueLiteralTextFromTriples(triples []tstore.Triple) (string, error) {
if ln := len(triples); ln != 1 {
return "", fmt.Errorf("expected unique, got %d: %s", ln, triples)
}
return tstore.ParseString(triples[0].Object())
}
func randomRdfId() string {
return fmt.Sprintf("%x", rand.Uint32())
}
package graph
import (
"fmt"
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
)
type Resolver interface {
Resolve(snap tstore.RDFGraph) ([]*Resource, error)
}
type ById struct {
Id string
}
func (r *ById) Resolve(snap tstore.RDFGraph) ([]*Resource, error) {
resolver := &ByProperty{Key: properties.ID, Value: r.Id}
return resolver.Resolve(snap)
}
type ByTypeAndProperty struct {
Type string
Key string
Value interface{}
}
func (r *ByTypeAndProperty) Resolve(snap tstore.RDFGraph) ([]*Resource, error) {
var resources []*Resource
if r.Value == nil {
return resources, nil
}
rdfpropLabel, ok := rdf.Labels[r.Key]
if !ok {
return resources, fmt.Errorf("resolve by property: undefined property label '%s'", r.Key)
}
rdfProp, err := rdf.Properties.Get(rdfpropLabel)
if err != nil {
return resources, fmt.Errorf("resolve by property: %s", err)
}
obj, err := marshalToRdfObject(r.Value, rdfProp.RdfsDefinedBy, rdfProp.RdfsDataType)
if err != nil {
return resources, fmt.Errorf("resolve by property: unmarshaling property '%s': %s", r.Key, err)
}
for _, t := range snap.WithPredObj(rdfpropLabel, obj) {
rt, err := resolveResourceType(snap, t.Subject())
if err != nil {
return resources, err
}
if rt == r.Type {
r := InitResource(rt, t.Subject())
if err := r.unmarshalFullRdf(snap); err != nil {
return resources, err
}
resources = append(resources, r)
}
}
return resources, nil
}
type ByProperty struct {
Key string
Value interface{}
}
func (r *ByProperty) Resolve(snap tstore.RDFGraph) ([]*Resource, error) {
var resources []*Resource
if r.Value == nil {
return resources, nil
}
rdfpropLabel, ok := rdf.Labels[r.Key]
if !ok {
return resources, fmt.Errorf("resolve by property: undefined property label '%s'", r.Key)
}
rdfProp, err := rdf.Properties.Get(rdfpropLabel)
if err != nil {
return resources, fmt.Errorf("resolve by property: %s", err)
}
obj, err := marshalToRdfObject(r.Value, rdfProp.RdfsDefinedBy, rdfProp.RdfsDataType)
if err != nil {
return resources, fmt.Errorf("resolve by property: unmarshaling property '%s': %s", r.Key, err)
}
for _, t := range snap.WithPredObj(rdfpropLabel, obj) {
rt, err := resolveResourceType(snap, t.Subject())
if err != nil {
return resources, err
}
r := InitResource(rt, t.Subject())
if err := r.unmarshalFullRdf(snap); err != nil {
return resources, err
}
resources = append(resources, r)
}
return resources, nil
}
type And struct {
Resolvers []Resolver
}
func (r *And) Resolve(snap tstore.RDFGraph) (result []*Resource, err error) {
if len(r.Resolvers) == 0 {
return
}
result, err = r.Resolvers[0].Resolve(snap)
if err != nil {
return
}
gg := NewGraph()
if err = gg.AddResource(result...); err != nil {
return
}
for _, resolv := range r.Resolvers {
result, err = resolv.Resolve(gg.store.Snapshot())
if err != nil {
return
}
gg = NewGraph()
if err = gg.AddResource(result...); err != nil {
return
}
}
return
}
type Or struct {
Resolvers []Resolver
}
func (r *Or) Resolve(snap tstore.RDFGraph) (result []*Resource, err error) {
for _, resolv := range r.Resolvers {
result, err = resolv.Resolve(snap)
if err != nil {
return
}
if len(result) > 0 {
return
}
}
return
}
type ByType struct {
Typ string
}
func (r *ByType) Resolve(snap tstore.RDFGraph) ([]*Resource, error) {
var resources []*Resource
typ := namespacedResourceType(r.Typ)
for _, t := range snap.WithPredObj(rdf.RdfType, tstore.Resource(typ)) {
r := InitResource(r.Typ, t.Subject())
err := r.unmarshalFullRdf(snap)
if err != nil {
return resources, err
}
resources = append(resources, r)
}
return resources, nil
}
type ByTypes struct {
Typs []string
}
func (r *ByTypes) Resolve(snap tstore.RDFGraph) ([]*Resource, error) {
var res []*Resource
for _, t := range r.Typs {
bt := &ByType{t}
all, err := bt.Resolve(snap)
if err != nil {
return res, err
}
res = append(res, all...)
}
return res, nil
}
/*
Copyright 2017 WALLIX
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 graph
import (
"errors"
"fmt"
"reflect"
"regexp"
"strings"
"unicode"
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
)
type Resource struct {
kind, id string
properties map[string]interface{}
relations map[string][]*Resource
meta map[string]interface{}
}
const notFoundResourceType = "notfound"
func NotFoundResource(id string) *Resource {
return &Resource{
id: id,
kind: notFoundResourceType,
properties: make(map[string]interface{}),
meta: make(map[string]interface{}),
relations: make(map[string][]*Resource),
}
}
func InitResource(kind, id string) *Resource {
return &Resource{
id: id,
kind: kind,
properties: map[string]interface{}{properties.ID: id},
meta: make(map[string]interface{}),
relations: make(map[string][]*Resource),
}
}
func (res *Resource) String() string {
if res == nil {
res = &Resource{}
}
return res.Format("%n[%t]")
}
var (
layoutRegex = regexp.MustCompile(`%(\[(\w+)\])?(\w)`)
)
func (res *Resource) Format(layout string) (out string) {
out = layout
if matches := layoutRegex.FindAllStringSubmatch(layout, -1); matches != nil {
for _, match := range matches {
var val string
verb := match[len(match)-1]
switch verb {
case "i":
val = "<none>"
if id := res.Id(); id != "" {
val = id
}
case "t":
switch {
case res.Type() == notFoundResourceType:
val = "<not-found>"
case res.Type() != "":
val = res.Type()
default:
val = "<none>"
}
case "n":
val = res.Id()
if name, ok := res.properties[properties.Name].(string); ok && name != "" {
val = "@" + name
}
case "p":
if v, ok := res.properties[match[2]]; ok {
val = fmt.Sprint(v)
}
default:
return fmt.Sprintf("invalid verb '%s' in layout '%s'", verb, layout)
}
out = strings.Replace(out, match[0], val, 1)
}
}
return
}
func (res *Resource) Type() string {
return res.kind
}
func (res *Resource) Id() string {
return res.id
}
func (res *Resource) Properties() map[string]interface{} {
return res.properties
}
func (res *Resource) Property(k string) (interface{}, bool) {
v, ok := res.properties[k]
return v, ok
}
func (res *Resource) Meta(k string) (interface{}, bool) {
v, ok := res.meta[k]
return v, ok
}
func (res *Resource) SetProperty(k string, v interface{}) {
res.properties[k] = v
}
func (res *Resource) AddRelation(typ string, rel *Resource) {
res.relations[typ] = append(res.relations[typ], rel)
}
// Compare only the id and type of the resources (no properties nor meta)
func (res *Resource) Same(other cloud.Resource) bool {
if res == nil && other == nil {
return true
}
if res == nil || other == nil {
return false
}
return res.Id() == other.Id() && res.Type() == other.Type()
}
func (res *Resource) marshalFullRDF() ([]tstore.Triple, error) {
var triples []tstore.Triple
cloudType := namespacedResourceType(res.Type())
triples = append(triples, tstore.SubjPred(res.id, rdf.RdfType).Resource(cloudType))
for key, value := range res.meta {
if key == "diff" {
triples = append(triples, tstore.SubjPred(res.id, MetaPredicate).StringLiteral(fmt.Sprint(value)))
}
}
for key, value := range res.properties {
if value == nil {
continue
}
propId, err := rdf.Properties.GetRDFId(key)
if err != nil {
return triples, fmt.Errorf("resource %s: marshaling property: %s", res, err)
}
propType, err := rdf.Properties.GetDefinedBy(propId)
if err != nil {
return triples, fmt.Errorf("resource %s: marshaling property: %s", res, err)
}
dataType, err := rdf.Properties.GetDataType(propId)
if err != nil {
return triples, fmt.Errorf("resource %s: marshaling property: %s", res, err)
}
switch propType {
case rdf.RdfsLiteral, rdf.RdfsClass:
obj, err := marshalToRdfObject(value, propType, dataType)
if err != nil {
return triples, fmt.Errorf("resource %s: marshaling property '%s': %s", res, key, err)
}
triples = append(triples, tstore.SubjPred(res.Id(), propId).Object(obj))
case rdf.RdfsList:
switch dataType {
case rdf.XsdString:
list, ok := value.([]string)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a string slice, got a %T", res, key, value)
}
for _, l := range list {
triples = append(triples, tstore.SubjPred(res.id, propId).StringLiteral(l))
}
case rdf.RdfsClass:
list, ok := value.([]string)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a string slice, got a %T", res, key, value)
}
for _, l := range list {
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(l))
}
case rdf.NetFirewallRule:
list, ok := value.([]*FirewallRule)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a firewall rule slice, got a %T", res, key, value)
}
for _, r := range list {
ruleId := randomRdfId()
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(ruleId))
triples = append(triples, r.marshalToTriples(ruleId)...)
}
case rdf.NetRoute:
list, ok := value.([]*Route)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a route slice, got a %T", res, key, value)
}
for _, r := range list {
routeId := randomRdfId()
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(routeId))
triples = append(triples, r.marshalToTriples(routeId)...)
}
case rdf.Grant:
list, ok := value.([]*Grant)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a grant slice, got a %T", res, key, value)
}
for _, g := range list {
grantId := randomRdfId()
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(grantId))
triples = append(triples, g.marshalToTriples(grantId)...)
}
case rdf.KeyValue:
list, ok := value.([]*KeyValue)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a keyvalue slice, got a %T", res, key, value)
}
for _, kv := range list {
keyValId := randomRdfId()
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(keyValId))
triples = append(triples, kv.marshalToTriples(keyValId)...)
}
case rdf.DistributionOrigin:
list, ok := value.([]*DistributionOrigin)
if !ok {
return triples, fmt.Errorf("resource %s: marshaling property '%s': expected a distribution origin slice, got a %T", res, key, value)
}
for _, o := range list {
keyValId := randomRdfId()
triples = append(triples, tstore.SubjPred(res.id, propId).Resource(keyValId))
triples = append(triples, o.marshalToTriples(keyValId)...)
}
case rdf.Grant:
default:
return triples, fmt.Errorf("resource %s: marshaling property '%s': unexpected rdfs:DataType: %s", res, key, dataType)
}
default:
return triples, fmt.Errorf("resource %s: marshaling property '%s': unexpected rdfs:isDefinedBy: %s", res, key, propType)
}
}
return triples, nil
}
func marshalToRdfObject(i interface{}, definedBy, dataType string) (tstore.Object, error) {
switch definedBy {
case rdf.RdfsLiteral:
return tstore.ObjectLiteral(i)
case rdf.RdfsClass:
return tstore.Resource(fmt.Sprint(i)), nil
default:
return nil, fmt.Errorf("unexpected rdfs:isDefinedBy: %s", definedBy)
}
}
func (res *Resource) unmarshalFullRdf(gph tstore.RDFGraph) error {
cloudType := namespacedResourceType(res.Type())
if !gph.Contains(tstore.SubjPred(res.Id(), rdf.RdfType).Resource(cloudType)) {
return fmt.Errorf("triple <%s><%s><%s> not found in graph", res.Id(), rdf.RdfType, cloudType)
}
for _, t := range gph.WithSubject(res.Id()) {
pred := t.Predicate()
if !rdf.Properties.IsRDFProperty(pred) || rdf.Properties.IsRDFSubProperty(pred) {
continue
}
propKey, err := rdf.Properties.GetLabel(pred)
if err != nil {
return fmt.Errorf("unmarshaling property: label: %s", err)
}
propVal, err := getPropertyValue(gph, t.Object(), pred)
if err != nil {
return fmt.Errorf("unmarshaling property '%s' of resource '%s': %s", propKey, res.Id(), err)
}
if rdf.Properties.IsRDFList(pred) {
dataType, err := rdf.Properties.GetDataType(pred)
if err != nil {
return fmt.Errorf("unmarshaling property: datatype: %s", err)
}
switch dataType {
case rdf.RdfsClass, rdf.XsdString:
list, ok := res.properties[propKey].([]string)
if !ok {
list = []string{}
}
list = append(list, propVal.(string))
res.properties[propKey] = list
case rdf.NetFirewallRule:
list, ok := res.properties[propKey].([]*FirewallRule)
if !ok {
list = []*FirewallRule{}
}
list = append(list, propVal.(*FirewallRule))
res.properties[propKey] = list
case rdf.NetRoute:
list, ok := res.properties[propKey].([]*Route)
if !ok {
list = []*Route{}
}
list = append(list, propVal.(*Route))
res.properties[propKey] = list
case rdf.Grant:
list, ok := res.properties[propKey].([]*Grant)
if !ok {
list = []*Grant{}
}
list = append(list, propVal.(*Grant))
res.properties[propKey] = list
case rdf.KeyValue:
list, ok := res.properties[propKey].([]*KeyValue)
if !ok {
list = []*KeyValue{}
}
list = append(list, propVal.(*KeyValue))
res.properties[propKey] = list
case rdf.DistributionOrigin:
list, ok := res.properties[propKey].([]*DistributionOrigin)
if !ok {
list = []*DistributionOrigin{}
}
list = append(list, propVal.(*DistributionOrigin))
res.properties[propKey] = list
default:
return fmt.Errorf("unmarshaling property: unexpected datatype %s", dataType)
}
} else {
res.properties[propKey] = propVal
}
}
return nil
}
func (r *Resource) unmarshalMeta(gph tstore.RDFGraph) error {
for _, t := range gph.WithSubjPred(r.Id(), MetaPredicate) {
text, err := tstore.ParseString(t.Object())
if err != nil {
return err
}
r.meta["diff"] = text
}
return nil
}
func namespacedResourceType(typ string) string {
return fmt.Sprintf("%s:%s", rdf.CloudOwlNS, strings.Title(typ))
}
type Resources []*Resource
func (res Resources) Map(f func(*Resource) string) (out []string) {
for _, r := range res {
out = append(out, f(r))
}
return
}
func Subtract(one, other map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for propK, propV := range one {
var found bool
if otherV, ok := other[propK]; ok {
if reflect.DeepEqual(propV, otherV) {
found = true
}
}
if !found {
result[propK] = propV
}
}
return result
}
var errTypeNotFound = errors.New("resource type not found")
func resolveResourceType(g tstore.RDFGraph, id string) (string, error) {
typeTs := g.WithSubjPred(id, rdf.RdfType)
switch len(typeTs) {
case 0:
return "", errTypeNotFound
case 1:
return unmarshalResourceType(typeTs[0].Object())
default:
return "", fmt.Errorf("cannot resolve unique type for resource '%s', got: %v", id, typeTs)
}
}
func lowerFirstLetter(s string) string {
a := []rune(s)
a[0] = unicode.ToLower(a[0])
return string(a)
}
func unmarshalResourceType(obj tstore.Object) (string, error) {
node, ok := obj.Resource()
if !ok {
return "", fmt.Errorf("object is not a resource identifier, %v", obj)
}
return lowerFirstLetter(trimNS(node)), nil
}
func trimNS(s string) string {
spl := strings.Split(s, ":")
if len(spl) == 0 {
return s
}
return spl[len(spl)-1]
}
package resourcetest
import (
"fmt"
"strings"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/graph"
)
type rBuilder struct {
id, typ string
props map[string]interface{}
}
func new(typ, id string) *rBuilder {
r := &rBuilder{id: id, typ: typ, props: make(map[string]interface{})}
return r.Prop(properties.ID, id)
}
func Region(id string) *rBuilder {
return new("region", id)
}
func Instance(id string) *rBuilder {
return new("instance", id)
}
func Subnet(id string) *rBuilder {
return new("subnet", id)
}
func VPC(id string) *rBuilder {
return new("vpc", id)
}
func SecurityGroup(id string) *rBuilder {
return new("securitygroup", id)
}
func KeyPair(id string) *rBuilder {
return new("keypair", id)
}
func InternetGw(id string) *rBuilder {
return new("internetgateway", id)
}
func NatGw(id string) *rBuilder {
return new("natgateway", id)
}
func RouteTable(id string) *rBuilder {
return new("routetable", id)
}
func LoadBalancer(id string) *rBuilder {
return new("loadbalancer", id)
}
func ClassicLoadBalancer(id string) *rBuilder {
return new("classicloadbalancer", id)
}
func AvailabilityZone(id string) *rBuilder {
return new("availabilityzone", id)
}
func TargetGroup(id string) *rBuilder {
return new("targetgroup", id)
}
func Policy(id string) *rBuilder {
return new("policy", id)
}
func Group(id string) *rBuilder {
return new("group", id)
}
func Role(id string) *rBuilder {
return new("role", id)
}
func User(id string) *rBuilder {
return new("user", id)
}
func MfaDevice(id string) *rBuilder {
return new("mfadevice", id)
}
func Listener(id string) *rBuilder {
return new("listener", id)
}
func Bucket(id string) *rBuilder {
return new("bucket", id)
}
func Zone(id string) *rBuilder {
return new("zone", id)
}
func Record(id string) *rBuilder {
return new("record", id)
}
func ScalingGroup(id string) *rBuilder {
return new("scalinggroup", id)
}
func LaunchConfig(id string) *rBuilder {
return new("launchconfiguration", id)
}
func Subscription(id string) *rBuilder {
return new("subscription", id)
}
func Topic(id string) *rBuilder {
return new("topic", id)
}
func Queue(id string) *rBuilder {
return new("queue", id)
}
func Function(id string) *rBuilder {
return new("function", id)
}
func Alarm(id string) *rBuilder {
return new("alarm", id)
}
func Metric(id string) *rBuilder {
return new("metric", id)
}
func Image(id string) *rBuilder {
return new("image", id)
}
func Distribution(id string) *rBuilder {
return new("distribution", id)
}
func Stack(id string) *rBuilder {
return new("stack", id)
}
func Repository(id string) *rBuilder {
return new("repository", id)
}
func ContainerCluster(id string) *rBuilder {
return new("containercluster", id)
}
func ContainerTask(id string) *rBuilder {
return new("containertask", id)
}
func Container(id string) *rBuilder {
return new("container", id)
}
func ContainerInstance(id string) *rBuilder {
return new("containerinstance", id)
}
func NetworkInterface(id string) *rBuilder {
return new("networkinterface", id)
}
func Certificate(id string) *rBuilder {
return new("certificate", id)
}
func AccessKey(id string) *rBuilder {
return new("accesskey", id)
}
func (b *rBuilder) Prop(key string, value interface{}) *rBuilder {
b.props[key] = value
return b
}
func (b *rBuilder) Build() *graph.Resource {
res := graph.InitResource(b.typ, b.id)
for k, v := range b.props {
res.Properties()[k] = v
}
return res
}
func AddParents(g *graph.Graph, relations ...string) {
for _, rel := range relations {
splits := strings.Split(rel, "->")
if len(splits) != 2 {
panic(fmt.Sprintf("invalid relation '%s'", rel))
}
r1 := graph.InitResource("", strings.TrimSpace(splits[0]))
r2 := graph.InitResource("", strings.TrimSpace(splits[1]))
err := g.AddParentRelation(r1, r2)
if err != nil {
panic(err)
}
}
}
/*
Copyright 2017 WALLIX
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 graph
import (
"encoding/json"
"fmt"
"net"
"sort"
"strconv"
"strings"
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud/rdf"
)
type FirewallRules []*FirewallRule
func (rules FirewallRules) Sort() {
for _, r := range rules {
sort.Slice(r.IPRanges, func(i int, j int) bool {
return r.IPRanges[i].String() < r.IPRanges[j].String()
})
}
sort.Slice(rules, func(i int, j int) bool {
return rules[i].String() < rules[j].String()
})
}
type FirewallRule struct {
PortRange PortRange `predicate:"net:portRange"`
Protocol string `predicate:"net:protocol"`
IPRanges []*net.IPNet `predicate:"net:cidr"` // IPv4 or IPv6 range
Sources []string `predicate:"cloud:source"`
}
func (r *FirewallRule) Contains(ip string) bool {
addr := net.ParseIP(ip)
for _, n := range r.IPRanges {
if n.Contains(addr) {
return true
}
}
return false
}
func (r *FirewallRule) String() string {
return fmt.Sprintf("PortRange:%+v; Protocol:%s; IPRanges:%+v; Sources:%+v", r.PortRange, r.Protocol, r.IPRanges, r.Sources)
}
func (r *FirewallRule) marshalToTriples(id string) []tstore.Triple {
var triples []tstore.Triple
triples = append(triples, tstore.SubjPred(id, rdf.RdfType).Resource(rdf.NetFirewallRule))
triples = append(triples, tstore.TriplesFromStruct(id, r)...)
return triples
}
func (r *FirewallRule) unmarshalFromTriples(g tstore.RDFGraph, id string) error {
portRangeTs := g.WithSubjPred(id, rdf.PortRange)
ports, err := extractUniqueLiteralTextFromTriples(portRangeTs)
if err != nil {
return fmt.Errorf("unmarshal firewall rule: port range: %s", err)
}
pr, err := ParsePortRange(ports)
if err != nil {
return fmt.Errorf("unmarshal firewall rule: %s", err)
}
r.PortRange = pr
protocolTs := g.WithSubjPred(id, rdf.Protocol)
protocol, err := extractUniqueLiteralTextFromTriples(protocolTs)
if err != nil {
return fmt.Errorf("unmarshal firewall rule: protocol: %s", err)
}
r.Protocol = protocol
cidrTs := g.WithSubjPred(id, rdf.CIDR)
for _, cidrT := range cidrTs {
cidrTxt, err := tstore.ParseString(cidrT.Object())
if err != nil {
return fmt.Errorf("unmarshal firewall rule: cidr: %s", err)
}
_, cidr, err := net.ParseCIDR(cidrTxt)
if err != nil {
return fmt.Errorf("unmarshal firewall rule: cidr: %s", err)
}
r.IPRanges = append(r.IPRanges, cidr)
}
sourceTs := g.WithSubjPred(id, rdf.Source)
for _, sourceT := range sourceTs {
source, err := tstore.ParseString(sourceT.Object())
if err != nil {
return fmt.Errorf("unmarshal firewall rule: source: %s", err)
}
r.Sources = append(r.Sources, source)
}
return nil
}
type PortRange struct {
FromPort, ToPort int64
Any bool
}
func (p PortRange) Contains(port int64) bool {
if p.Any {
return true
}
from, to := p.FromPort, p.ToPort
if from == port || to == port || (from < port && to > port) {
return true
}
return false
}
func (p PortRange) String() string {
switch {
case p.Any:
return ":"
case p.FromPort == int64(-1):
return fmt.Sprintf("%d:%[1]d", p.ToPort)
case p.ToPort == int64(-1):
return fmt.Sprintf("%d:%[1]d", p.FromPort)
default:
return fmt.Sprintf("%d:%d", p.FromPort, p.ToPort)
}
}
func ParsePortRange(s string) (PortRange, error) {
splits := strings.Split(s, ":")
switch {
case s == ":":
return PortRange{Any: true}, nil
case len(splits) == 2:
from, err := strconv.Atoi(splits[0])
if err != nil {
return PortRange{}, err
}
to, err := strconv.Atoi(splits[1])
if err != nil {
return PortRange{}, err
}
return PortRange{FromPort: int64(from), ToPort: int64(to)}, nil
default:
return PortRange{}, fmt.Errorf("unexpected portrange: '%s'", s)
}
}
type routeTargetType int
const (
EgressOnlyInternetGatewayTarget routeTargetType = iota
GatewayTarget
InstanceTarget
NatTarget
NetworkInterfaceTarget
VpcPeeringConnectionTarget
)
type RouteTarget struct {
Type routeTargetType
Ref string
Owner string
}
func (t *RouteTarget) String() string {
return fmt.Sprintf("%d|%s|%s", t.Type, t.Ref, t.Owner)
}
func ParseRouteTarget(s string) (*RouteTarget, error) {
splits := strings.Split(s, "|")
if len(splits) != 3 {
return &RouteTarget{}, fmt.Errorf("unexpected route target: '%s'", s)
}
typ, err := strconv.Atoi(splits[0])
if err != nil {
return &RouteTarget{}, err
}
return &RouteTarget{Type: routeTargetType(typ), Ref: splits[1], Owner: splits[2]}, nil
}
type Routes []*Route
func (routes Routes) Sort() {
for _, r := range routes {
sort.Slice(r.Targets, func(i int, j int) bool {
return r.Targets[i].String() < r.Targets[j].String()
})
}
sort.Slice(routes, func(i int, j int) bool {
return routes[i].String() < routes[j].String()
})
}
type Route struct {
Destination *net.IPNet `predicate:"net:cidr"`
DestinationIPv6 *net.IPNet `predicate:"net:cidrv6"`
DestinationPrefixListId string `predicate:"net:routeDestinationPrefixList"`
Targets []*RouteTarget `predicate:"net:routeTargets"`
}
func (r *Route) String() string {
return fmt.Sprintf("Destination:%+v; DestinationIPv6:%+v; DestinationPrefixListId:%s; Targets:%+v", r.Destination, r.DestinationIPv6, r.DestinationPrefixListId, r.Targets)
}
func (r *Route) marshalToTriples(id string) []tstore.Triple {
var triples []tstore.Triple
triples = append(triples, tstore.SubjPred(id, rdf.RdfType).Resource(rdf.NetRoute))
triples = append(triples, tstore.TriplesFromStruct(id, r)...)
return triples
}
func (r *Route) unmarshalFromTriples(g tstore.RDFGraph, id string) error {
routeDestTs := g.WithSubjPred(id, rdf.CIDR)
if len(routeDestTs) > 0 {
dest, err := extractUniqueLiteralTextFromTriples(routeDestTs)
if err != nil {
return fmt.Errorf("unmarshal route: destination: %s", err)
}
_, r.Destination, err = net.ParseCIDR(dest)
if err != nil {
return fmt.Errorf("unmarshal route: destination: %s", err)
}
}
routeDestv6Ts := g.WithSubjPred(id, rdf.CIDRv6)
if len(routeDestv6Ts) > 0 {
destv6, err := extractUniqueLiteralTextFromTriples(routeDestv6Ts)
if err != nil {
return fmt.Errorf("unmarshal route: destinationV6: %s", err)
}
_, r.DestinationIPv6, err = net.ParseCIDR(destv6)
if err != nil {
return fmt.Errorf("unmarshal route: destinationV6: %s", err)
}
}
destPrefixTs := g.WithSubjPred(id, rdf.NetDestinationPrefixList)
if len(destPrefixTs) > 0 {
var err error
r.DestinationPrefixListId, err = extractUniqueLiteralTextFromTriples(destPrefixTs)
if err != nil {
return fmt.Errorf("unmarshal route: destination prefix: %s", err)
}
}
targetTs := g.WithSubjPred(id, rdf.NetRouteTargets)
for _, targetT := range targetTs {
litText, err := tstore.ParseString(targetT.Object())
if err != nil {
return err
}
target, err := ParseRouteTarget(litText)
if err != nil {
return fmt.Errorf("unmarshal route target: %s", err)
}
r.Targets = append(r.Targets, target)
}
return nil
}
type Grants []*Grant
func (grants Grants) Sort() {
sort.Slice(grants, func(i int, j int) bool {
return grants[i].String() < grants[j].String()
})
}
type Grant struct {
Permission string `predicate:"cloud:permission"`
Grantee Grantee `predicate:"cloud:grantee" bnode:""`
}
type Grantee struct {
GranteeID string `predicate:"cloud:id"`
GranteeDisplayName string `predicate:"cloud:name"`
GranteeType string `predicate:"cloud:granteeType"`
}
func (g *Grant) String() string {
return fmt.Sprintf("Permission:%s; GranteeID:%s; GranteeDisplayName:%s; GranteeType:%s", g.Permission, g.Grantee.GranteeID, g.Grantee.GranteeDisplayName, g.Grantee.GranteeType)
}
func (g *Grant) marshalToTriples(id string) []tstore.Triple {
var triples []tstore.Triple
triples = append(triples, tstore.SubjPred(id, rdf.RdfType).Resource(rdf.Grant))
triples = append(triples, tstore.TriplesFromStruct(id, g)...)
return triples
}
func (g *Grant) unmarshalFromTriples(gph tstore.RDFGraph, id string) error {
permissionTs := gph.WithSubjPred(id, rdf.Permission)
var err error
g.Permission, err = extractUniqueLiteralTextFromTriples(permissionTs)
if err != nil {
return fmt.Errorf("unmarshal grant: permission: %s", err)
}
granteeTs := gph.WithSubjPred(id, rdf.Grantee)
if len(granteeTs) != 1 {
return fmt.Errorf("unmarshal grant: expect 1 grantee got: %d", len(granteeTs))
}
granteeNode, ok := granteeTs[0].Object().Bnode()
if !ok {
return fmt.Errorf("unmarshal grant: grantee does not contain a resource identifier")
}
granteeIdTs := gph.WithSubjPred(granteeNode, rdf.ID)
if len(granteeIdTs) > 0 {
g.Grantee.GranteeID, err = extractUniqueLiteralTextFromTriples(granteeIdTs)
if err != nil {
return fmt.Errorf("unmarshal grant: grantee id: %s", err)
}
}
granteeNameTs := gph.WithSubjPred(granteeNode, rdf.Name)
if len(granteeNameTs) > 0 {
g.Grantee.GranteeDisplayName, err = extractUniqueLiteralTextFromTriples(granteeNameTs)
if err != nil {
return fmt.Errorf("unmarshal grant: grantee name: %s", err)
}
}
granteeTypeTs := gph.WithSubjPred(granteeNode, rdf.GranteeType)
if len(granteeTypeTs) > 0 {
g.Grantee.GranteeType, err = extractUniqueLiteralTextFromTriples(granteeTypeTs)
if err != nil {
return fmt.Errorf("unmarshal grant: grantee type: %s", err)
}
}
return nil
}
type KeyValue struct {
KeyName string `predicate:"cloud:keyName"`
Value string `predicate:"cloud:value"`
}
func (kv *KeyValue) String() string {
return fmt.Sprintf("[Key:%s,Value:%s]", kv.KeyName, kv.Value)
}
func (kv *KeyValue) marshalToTriples(id string) []tstore.Triple {
var triples []tstore.Triple
triples = append(triples, tstore.SubjPred(id, rdf.RdfType).Resource(rdf.KeyValue))
triples = append(triples, tstore.TriplesFromStruct(id, kv)...)
return triples
}
func (kv *KeyValue) unmarshalFromTriples(gph tstore.RDFGraph, id string) error {
var err error
kv.KeyName, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.KeyName)
if err != nil {
return fmt.Errorf("unmarshal keyvalue: key name: %s", err)
}
kv.Value, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.Value)
if err != nil {
return fmt.Errorf("unmarshal keyvalue: val name: %s", err)
}
return nil
}
type DistributionOrigin struct {
ID string `predicate:"cloud:id"`
PublicDNS string `predicate:"cloud:publicDNS"`
PathPrefix string `predicate:"cloud:pathPrefix"`
OriginType string `predicate:"cloud:type"`
Config string `predicate:"cloud:config"`
}
func (o *DistributionOrigin) String() string {
var elems []string
elems = append(elems, "ID:"+o.ID)
if o.PublicDNS != "" {
elems = append(elems, "PublicDNS:"+o.PublicDNS)
}
if o.PathPrefix != "" {
elems = append(elems, "PathPrefix:"+o.PathPrefix)
}
if o.OriginType != "" {
elems = append(elems, "Type:"+o.OriginType)
}
if o.Config != "" {
elems = append(elems, "Config:"+o.Config)
}
return fmt.Sprintf("[%s]", strings.Join(elems, ","))
}
func (o *DistributionOrigin) marshalToTriples(id string) []tstore.Triple {
var triples []tstore.Triple
triples = append(triples, tstore.SubjPred(id, rdf.RdfType).Resource(rdf.DistributionOrigin))
triples = append(triples, tstore.TriplesFromStruct(id, o)...)
return triples
}
func (o *DistributionOrigin) unmarshalFromTriples(gph tstore.RDFGraph, id string) error {
var err error
o.ID, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.ID)
if err != nil {
return fmt.Errorf("unmarshal DistributionOrigin: extract id: %s", err)
}
o.PublicDNS, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.PublicDNS)
if err != nil {
return fmt.Errorf("unmarshal DistributionOrigin: extract PublicDNS: %s", err)
}
o.PathPrefix, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.PathPrefix)
if err != nil {
return fmt.Errorf("unmarshal DistributionOrigin: extract PathPrefix: %s", err)
}
o.OriginType, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.Type)
if err != nil {
return fmt.Errorf("unmarshal DistributionOrigin: extract Type: %s", err)
}
o.Config, err = extractUniqueLiteralTextFromGraph(gph, id, rdf.Config)
if err != nil {
return fmt.Errorf("unmarshal DistributionOrigin: extract Config: %s", err)
}
return nil
}
type Policy struct {
Version string `json:",omitempty"`
ID string `json:"Id,omitempty"`
Statements compositeStatement `json:"Statement,omitempty"`
}
type PolicyStatement struct {
ID string `json:"Sid,omitempty"`
Principal *StatementPrincipal `json:",omitempty"`
NotPrincipal *StatementPrincipal `json:",omitempty"`
Effect string `json:",omitempty"`
Actions compositeString `json:"Action,omitempty"`
NotActions compositeString `json:"NotAction,omitempty"`
Resources compositeString `json:"Resource,omitempty"`
NotResources compositeString `json:"NotResource,omitempty"`
Condition interface{} `json:",omitempty"`
}
type StatementPrincipal struct {
AWS compositeString `json:",omitempty"`
Service compositeString `json:",omitempty"`
Federated compositeString `json:",omitempty"`
}
// To support AWS JSON for Policy in which Principal, Action,... can be either string or slice of string
type compositeString []string
func (c *compositeString) UnmarshalJSON(data []byte) (err error) {
var str string
if err = json.Unmarshal(data, &str); err == nil {
*c = []string{str}
return
}
var slice []string
if err = json.Unmarshal(data, &slice); err != nil {
return
}
*c = slice
return
}
// To support AWS JSON for Policy in which Statement can be either Statement or slice of Statement
type compositeStatement []*PolicyStatement
func (c *compositeStatement) UnmarshalJSON(data []byte) (err error) {
var statement *PolicyStatement
if err = json.Unmarshal(data, &statement); err == nil {
*c = []*PolicyStatement{statement}
return
}
var slice []*PolicyStatement
if err = json.Unmarshal(data, &slice); err != nil {
return
}
*c = slice
return
}
// To support AWS JSON for Policy in which a principal can be either a JSON object, either "*"
func (c *StatementPrincipal) UnmarshalJSON(data []byte) (err error) {
var wildCardString string
if err = json.Unmarshal(data, &wildCardString); err == nil {
if wildCardString == "*" {
c.AWS = []string{"*"} // according to doc, "Principal":"*" is equivalent to "Principal":{"AWS":"*"}
return
} else {
return fmt.Errorf("unmarshaling policy: a principal string can only contain '*', but got %s", wildCardString)
}
}
type aliasPrincipal struct {
AWS compositeString `json:",omitempty"`
Service compositeString `json:",omitempty"`
Federated compositeString `json:",omitempty"`
}
var principal *aliasPrincipal
if err = json.Unmarshal(data, &principal); err != nil {
return
}
*c = StatementPrincipal(*principal)
return
}
func extractUniqueLiteralTextFromGraph(gph tstore.RDFGraph, subj, pred string) (string, error) {
ts := gph.WithSubjPred(subj, pred)
if len(ts) != 1 {
return "", fmt.Errorf("%s,%s: expect 1 triple got: %d", subj, pred, len(ts))
}
return extractUniqueLiteralTextFromTriples(ts)
}
/*
Copyright 2017 WALLIX
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 graph
import (
tstore "github.com/wallix/triplestore"
"github.com/wallix/awless/cloud/rdf"
)
type Visitor interface {
Visit(*Graph) error
}
type visitEachFunc func(res *Resource, depth int) error
func VisitorCollectFunc(collect *[]*Resource) visitEachFunc {
return func(res *Resource, depth int) error {
*collect = append(*collect, res)
return nil
}
}
type ParentsVisitor struct {
From *Resource
Each visitEachFunc
IncludeFrom bool
Relation string
}
func (v *ParentsVisitor) Visit(g *Graph) error {
startNode, foreach, err := prepareRDFVisit(g, v.From, v.Each, v.IncludeFrom)
if err != nil {
return err
}
if v.Relation == "" {
v.Relation = rdf.ParentOf
}
return tstore.NewTree(g.store.Snapshot(), v.Relation).TraverseAncestors(startNode, foreach)
}
type ChildrenVisitor struct {
From *Resource
Each visitEachFunc
IncludeFrom bool
Relation string
}
func (v *ChildrenVisitor) Visit(g *Graph) error {
startNode, foreach, err := prepareRDFVisit(g, v.From, v.Each, v.IncludeFrom)
if err != nil {
return err
}
if v.Relation == "" {
v.Relation = rdf.ParentOf
}
return tstore.NewTree(g.store.Snapshot(), v.Relation).TraverseDFS(startNode, foreach)
}
type SiblingsVisitor struct {
From *Resource
Each visitEachFunc
IncludeFrom bool
}
func (v *SiblingsVisitor) Visit(g *Graph) error {
startNode, foreach, err := prepareRDFVisit(g, v.From, v.Each, v.IncludeFrom)
if err != nil {
return err
}
return tstore.NewTree(g.store.Snapshot(), rdf.ParentOf).TraverseSiblings(startNode, resolveResourceType, foreach)
}
func prepareRDFVisit(g *Graph, root *Resource, each visitEachFunc, includeRoot bool) (string, func(g tstore.RDFGraph, n string, i int) error, error) {
rootNode := root.Id()
foreach := func(rdfG tstore.RDFGraph, n string, i int) error {
rT, err := resolveResourceType(rdfG, n)
if err != nil {
return err
}
res, err := g.GetResource(rT, n)
if err != nil {
return err
}
if includeRoot || !root.Same(res) {
if err := each(res, i); err != nil {
return err
}
}
return nil
}
return rootNode, foreach, nil
}
/*
Copyright 2017 WALLIX
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 inspect
import (
"io"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/inspect/inspectors"
)
var InspectorsRegister map[string]Inspector
func init() {
all := []Inspector{
&inspectors.Pricer{}, &inspectors.BucketSizer{},
&inspectors.PortScanner{}, &inspectors.OpenBuckets{},
}
InspectorsRegister = make(map[string]Inspector)
for _, i := range all {
InspectorsRegister[i.Name()] = i
}
}
type Inspector interface {
Name() string
Inspect(cloud.GraphAPI) error
Print(io.Writer)
}
/*
Copyright 2017 WALLIX
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 inspectors
import (
"fmt"
"io"
"text/tabwriter"
"github.com/wallix/awless/cloud"
)
type BucketSizer struct {
total int
buckets map[string]*bucket
}
type bucket struct {
objects, size int
}
func (*BucketSizer) Name() string {
return "bucket_sizer"
}
func (i *BucketSizer) Inspect(g cloud.GraphAPI) error {
i.buckets = make(map[string]*bucket)
objects, err := g.Find(cloud.NewQuery(cloud.S3Object))
if err != nil {
return err
}
for _, obj := range objects {
size := obj.Properties()["Size"].(int)
i.total = i.total + size
name := obj.Properties()["Bucket"].(string)
b := i.buckets[name]
if b == nil {
b = new(bucket)
i.buckets[name] = b
}
b.size = b.size + size
b.objects = b.objects + 1
}
return nil
}
func (i *BucketSizer) Print(w io.Writer) {
tabw := tabwriter.NewWriter(w, 0, 8, 0, '\t', 0)
fmt.Fprintln(tabw, "Bucket\tObject count\tS3 total storage\t")
fmt.Fprintln(tabw, "--------\t----------\t-----------------\t")
for name, bucket := range i.buckets {
fmt.Fprintf(tabw, "%s\t%d\t%0.6f Gb\t\n", name, bucket.objects, float64(bucket.size)/1e9)
}
fmt.Fprintf(tabw, "%s\t%s\t%0.6f Gb\t\n", "", "", float64(i.total)/1e9)
tabw.Flush()
}
/*
Copyright 2017 WALLIX
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 inspectors
import (
"fmt"
"io"
"strings"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
)
type OpenBuckets struct {
openToAny []string
openToAnyAuth []string
}
func (*OpenBuckets) Name() string {
return "open_buckets"
}
func (a *OpenBuckets) Inspect(g cloud.GraphAPI) error {
buckets, err := g.Find(cloud.NewQuery(cloud.Bucket))
if err != nil {
return err
}
openToAuthUsers := make(map[string]bool)
openToUsers := make(map[string]bool)
for _, buck := range buckets {
grants, ok := buck.Properties()["Grants"].([]*graph.Grant)
if ok {
for _, g := range grants {
if strings.Contains(g.Grantee.GranteeID, "AllUsers") {
openToUsers[fmt.Sprint(buck.Properties()["ID"])] = true
}
if strings.Contains(g.Grantee.GranteeID, "AuthenticatedUsers") {
openToAuthUsers[fmt.Sprint(buck.Properties()["ID"])] = true
}
}
}
}
for k := range openToUsers {
a.openToAny = append(a.openToAny, k)
}
for k := range openToAuthUsers {
a.openToAnyAuth = append(a.openToAnyAuth, k)
}
return nil
}
func (a *OpenBuckets) Print(w io.Writer) {
if len(a.openToAny) > 0 {
fmt.Fprintf(w, "Buckets open to anybody: %s\n", strings.Join(a.openToAny, ", "))
}
if len(a.openToAnyAuth) > 0 {
fmt.Fprintf(w, "Buckets open to anyone with an AWS account: %s\n", strings.Join(a.openToAnyAuth, ", "))
}
if len(a.openToAnyAuth) == 0 && len(a.openToAny) == 0 {
fmt.Fprintln(w, "none found")
}
}
/*
Copyright 2017 WALLIX
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 inspectors
import (
"fmt"
"io"
"net"
"strings"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/graph"
)
type PortScanner struct {
inbounds map[string][]*graph.FirewallRule
applyingOn map[string][]string
}
func (p *PortScanner) Name() string {
return "port_scanner"
}
func (p *PortScanner) Inspect(g cloud.GraphAPI) error {
sgroups, err := g.Find(cloud.NewQuery(cloud.SecurityGroup))
if err != nil {
return err
}
p.inbounds = make(map[string][]*graph.FirewallRule)
p.applyingOn = make(map[string][]string)
for _, sg := range sgroups {
rules := sg.Properties()["InboundRules"]
switch typedRules := rules.(type) {
case []*graph.FirewallRule:
p.inbounds[sg.Id()] = typedRules
res, err := g.ResourceRelations(sg, rdf.ApplyOn, false)
if err != nil {
return err
}
for _, r := range res {
p.applyingOn[sg.Id()] = append(p.applyingOn[sg.Id()], r.String())
}
}
}
return nil
}
var allLocalIPs = net.ParseIP("0.0.0.0")
func (p *PortScanner) Print(w io.Writer) {
for sg, inbounds := range p.inbounds {
var targets string
if len(p.applyingOn[sg]) == 0 {
targets = "nothing"
} else {
targets = strings.Join(p.applyingOn[sg], ", ")
}
fmt.Fprintf(w, "Securitygroup %s applying on %s: \n", sg, targets)
var allPermissive bool
for _, inbound := range inbounds {
if portRange, prot := inbound.PortRange, inbound.Protocol; portRange.Any && prot == "any" {
var allIps bool
for _, n := range inbound.IPRanges {
if n.IP.Equal(allLocalIPs) {
allIps = true
}
}
if allIps {
fmt.Fprintf(w, "\tall ports via any protocol for all IPs\n")
} else {
fmt.Fprintf(w, "\tall ports via any protocol for IPs: %s\n", inbound.IPRanges)
}
allPermissive = true
}
}
if !allPermissive {
for _, inbound := range inbounds {
if portRange, prot := inbound.PortRange, inbound.Protocol; prot != "any" {
if from, to := portRange.FromPort, portRange.ToPort; from == to {
fmt.Fprintf(w, "\tport %d via %s\n", from, prot)
} else {
fmt.Fprintf(w, "\tports %d-%d via %s\n", from, to, prot)
}
}
}
}
}
}
/*
Copyright 2017 WALLIX
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 inspectors
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"sync"
"text/tabwriter"
"github.com/wallix/awless/cloud"
)
var pricesURL = "http://ec2-price.com"
type Pricer struct {
total float64
count map[string]int
}
func (p *Pricer) Name() string {
return "pricer"
}
func (p *Pricer) Inspect(g cloud.GraphAPI) error {
region, err := getRegion(g)
if err != nil {
return err
}
instances, err := g.Find(cloud.NewQuery(cloud.Instance))
if err != nil {
return err
}
p.count = make(map[string]int)
pricePerType := make(map[string]float64)
for _, inst := range instances {
typ := inst.Properties()["Type"].(string)
pricePerType[typ] = 0.0
p.count[typ] = p.count[typ] + 1
}
fmt.Printf("Fetching prices at %s for region %s\n\n", pricesURL, region)
type result struct {
typ string
price float64
}
var wg sync.WaitGroup
resultC := make(chan result)
for ty := range pricePerType {
wg.Add(1)
go func(t string) {
defer wg.Done()
price, err := fetchPrice(t, region)
if err != nil {
fmt.Printf("fetching price for '%s': %s\n", t, err)
return
}
resultC <- result{typ: t, price: price}
}(ty)
}
go func() {
wg.Wait()
close(resultC)
}()
for r := range resultC {
pricePerType[r.typ] = r.price
}
for typ, count := range p.count {
p.total = p.total + (float64(count) * pricePerType[typ])
}
return nil
}
func (p *Pricer) Print(w io.Writer) {
tabw := tabwriter.NewWriter(w, 0, 8, 0, '\t', 0)
fmt.Fprintln(tabw, "Instance\tCount\tEstimated total/day (no EBS)\t")
fmt.Fprintln(tabw, "--------\t-----\t----------------------------\t")
for instType, count := range p.count {
fmt.Fprintf(tabw, "%s\t%d\t%s\t\n", instType, count, "")
}
fmt.Fprintf(tabw, "%s\t%s\t$%.2f\t\n", "", "", p.total*24)
tabw.Flush()
}
func fetchPrice(instType, region string) (float64, error) {
resp, err := http.PostForm(
pricesURL,
url.Values{"instance_type": {instType}, "location": {region}},
)
if err != nil {
return 0.0, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0.0, err
}
price, err := strconv.ParseFloat(string(body), 64)
if err != nil {
return 0.0, err
}
return price, nil
}
func getRegion(g cloud.GraphAPI) (string, error) {
all, err := g.Find(cloud.NewQuery("region"))
if err != nil {
return "", err
}
if len(all) < 1 {
return "", errors.New("cannot resolve region from graph")
}
return all[0].Id(), nil
}
/*
Copyright 2017 WALLIX
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 logger
import (
"fmt"
"io"
"log"
"os"
"strings"
"sync/atomic"
"github.com/fatih/color"
)
var DefaultLogger *Logger = New("", 0)
var DiscardLogger *Logger = New("", 0, io.Discard)
const (
VerboseF = 1 << iota
ExtraVerboseF
)
type Logger struct {
verbose uint32 // atomic
out *log.Logger
w io.Writer
}
var (
infoPrefix = color.GreenString("[info] ")
errorPrefix = color.RedString("[error] ")
warningPrefix = color.YellowString("[warning]")
verbosePrefix = color.CyanString("[verbose]")
extraVerbosePrefix = color.MagentaString("[extra] ")
)
func New(prefix string, flag int, w ...io.Writer) *Logger {
var out io.Writer = os.Stderr
if len(w) > 0 {
out = w[0]
}
return &Logger{out: log.New(out, prefix, flag), w: out}
}
func (l *Logger) Verbosef(format string, v ...interface{}) {
if l.verbosity() > 0 {
l.out.Println(prepend(verbosePrefix, fmt.Sprintf(format, v...))...)
}
}
func (l *Logger) Verbose(v ...interface{}) {
if l.verbosity() > 0 {
l.out.Println(prepend(verbosePrefix, v...)...)
}
}
func (l *Logger) ExtraVerbosef(format string, v ...interface{}) {
if l.verbosity() > 1 {
l.out.Println(prepend(extraVerbosePrefix, fmt.Sprintf(format, v...))...)
}
}
func (l *Logger) ExtraVerbose(v ...interface{}) {
if l.verbosity() > 1 {
l.out.Println(prepend(extraVerbosePrefix, v...)...)
}
}
func (l *Logger) Info(v ...interface{}) {
l.out.Println(prepend(infoPrefix, v...)...)
}
func (l *Logger) Infof(format string, v ...interface{}) {
l.out.Println(prepend(infoPrefix, fmt.Sprintf(format, v...))...)
}
func (l *Logger) InteractiveInfof(format string, v ...interface{}) {
fmt.Fprint(l.w, prepend("\r\033[K"+infoPrefix, " ", fmt.Sprintf(format, v...))...)
}
func (l *Logger) Error(v ...interface{}) {
l.out.Println(prepend(errorPrefix, v...)...)
}
func (l *Logger) Errorf(format string, v ...interface{}) {
l.out.Println(prepend(errorPrefix, fmt.Sprintf(format, v...))...)
}
func (l *Logger) MultiLineError(err error) {
if err != nil {
for _, msg := range formatMultiLineErrMsg(err.Error()) {
l.out.Println(color.New(color.FgRed).Sprint(msg))
}
}
}
func (l *Logger) Warning(v ...interface{}) {
l.out.Println(prepend(warningPrefix, v...)...)
}
func (l *Logger) Warningf(format string, v ...interface{}) {
l.out.Println(prepend(warningPrefix, fmt.Sprintf(format, v...))...)
}
func (l *Logger) Println() {
l.out.Println()
}
func (l *Logger) SetVerbose(level int) {
atomic.StoreUint32(&l.verbose, uint32(level))
}
func (l *Logger) verbosity() uint32 {
return atomic.LoadUint32(&l.verbose)
}
func Verbosef(format string, v ...interface{}) {
DefaultLogger.Verbosef(format, v...)
}
func Verbose(v ...interface{}) {
DefaultLogger.Verbose(v...)
}
func ExtraVerbosef(format string, v ...interface{}) {
DefaultLogger.ExtraVerbosef(format, v...)
}
func ExtraVerbose(v ...interface{}) {
DefaultLogger.ExtraVerbose(v...)
}
func Info(v ...interface{}) {
DefaultLogger.Info(v...)
}
func Infof(format string, v ...interface{}) {
DefaultLogger.Infof(format, v...)
}
func Error(v ...interface{}) {
DefaultLogger.Error(v...)
}
func Errorf(format string, v ...interface{}) {
DefaultLogger.Errorf(format, v...)
}
func Warning(v ...interface{}) {
DefaultLogger.Warning(v...)
}
func Warningf(format string, v ...interface{}) {
DefaultLogger.Warningf(format, v...)
}
func MultiLineError(err error) {
DefaultLogger.MultiLineError(err)
}
func prepend(s interface{}, v ...interface{}) []interface{} {
return append([]interface{}{s}, v...)
}
func formatMultiLineErrMsg(msg string) []string {
notabs := strings.Replace(msg, "\t", "", -1)
var indented []string
for _, line := range strings.Split(notabs, "\n") {
indented = append(indented, fmt.Sprintf(" %s", line))
}
return indented
}
/*
Copyright 2017 WALLIX
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 main
import "github.com/wallix/awless/commands"
func main() {
commands.RootCmd.Execute()
}
package ssh
import (
"fmt"
"net"
"os"
"strings"
"syscall"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/term"
)
func agentAuth() (ssh.AuthMethod, error) {
sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
if err != nil {
return nil, err
}
return ssh.PublicKeysCallback(agent.NewClient(sock).Signers), nil
}
func privateKeyAuth(priv privateKey) (ssh.AuthMethod, error) {
signer, err := ssh.ParsePrivateKey(priv.body)
if err != nil {
if strings.Contains(err.Error(), "cannot decode encrypted private keys") {
return encryptedPrivKeyAuth(priv)
}
return nil, err
}
return ssh.PublicKeys(signer), nil
}
func encryptedPrivKeyAuth(priv privateKey) (ssh.AuthMethod, error) {
fmt.Fprintf(os.Stderr, "This SSH key is encrypted. Please enter passphrase for key '%s':", priv.path)
passphrase, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert // needed for Windows where syscall.Stdin is uintptr
if err != nil {
return nil, err
}
fmt.Fprintln(os.Stderr)
signer, err := DecryptSSHKey(priv.body, passphrase)
if err != nil {
return nil, err
}
return ssh.PublicKeys(signer), nil
}
package ssh
import (
"bytes"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"text/template"
"time"
"github.com/wallix/awless/logger"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
)
type Client struct {
*gossh.Client
Config *gossh.ClientConfig
IP, User, Keypath string
Port int
Proxy *Client
HostKeyCallback gossh.HostKeyCallback
StrictHostKeyChecking bool
InteractiveTerminalFunc func(*gossh.Client) error
logger *logger.Logger
}
func InitClient(keyname string, keyFolders ...string) (*Client, error) {
var auths []gossh.AuthMethod
privkey, ok := findPrivateKeyFromName(keyname, keyFolders...)
if ok {
if a, err := privateKeyAuth(privkey); err == nil {
auths = append(auths, a)
}
}
if a, err := agentAuth(); err == nil {
auths = append(auths, a)
}
if len(auths) == 0 {
return nil, fmt.Errorf("No key provided and no SSH_AUTH_SOCK env variable set, unable to resolve auth")
}
return &Client{
Config: &gossh.ClientConfig{
Auth: auths,
Timeout: 2 * time.Second,
HostKeyCallback: checkHostKey,
},
Keypath: privkey.path,
logger: logger.DiscardLogger,
InteractiveTerminalFunc: func(*gossh.Client) error { return nil },
StrictHostKeyChecking: true,
}, nil
}
func (c *Client) SetLogger(l *logger.Logger) {
c.logger = l
}
func (c *Client) SetStrictHostKeyChecking(hostKeyChecking bool) {
c.StrictHostKeyChecking = hostKeyChecking
}
func (c *Client) DialWithUsers(usernames ...string) error {
var err error
var client *gossh.Client
hostport := fmt.Sprintf("%s:%d", c.IP, c.Port)
for _, user := range usernames {
newConfig := *c.Config
newConfig.User = user
if !c.StrictHostKeyChecking {
newConfig.HostKeyCallback = gossh.InsecureIgnoreHostKey()
}
client, err = gossh.Dial("tcp", hostport, &newConfig)
if err != nil {
continue
} else {
c.logger.ExtraVerbosef("dialed %s successfully with user %s", hostport, user)
c.User = user
c.Client = client
return nil
}
}
return fmt.Errorf("unable to authenticate to %s for users %q. Last error: %s", hostport, usernames, err)
}
func (c *Client) NewClientWithProxy(destinationHost string, destinationPort int, usernames ...string) (*Client, error) {
hostport := fmt.Sprintf("%s:%d", destinationHost, destinationPort)
for _, user := range usernames {
netConn, err := c.Dial("tcp", hostport)
if err != nil {
return nil, fmt.Errorf("cannot dial from %s:%d to %s:%d - %s", c.IP, c.Port, destinationHost, destinationPort, err)
}
c.logger.ExtraVerbosef("successful tcp connection from %s:%d to %s:%d", c.IP, c.Port, destinationHost, destinationPort)
newConfig := *c.Config
newConfig.User = user
if !c.StrictHostKeyChecking {
newConfig.HostKeyCallback = gossh.InsecureIgnoreHostKey()
}
conn, chans, reqs, err := gossh.NewClientConn(netConn, hostport, &newConfig)
if err != nil {
netConn.Close()
c.logger.ExtraVerbosef("cannot proxy with user %s (err: %s)", user, err)
continue
}
c.logger.ExtraVerbosef("proxied successfully with user %s", user)
return &Client{
Client: gossh.NewClient(conn, chans, reqs),
Proxy: c,
IP: destinationHost,
User: user,
Keypath: c.Keypath,
Port: destinationPort,
InteractiveTerminalFunc: func(*gossh.Client) error { return nil },
StrictHostKeyChecking: c.StrictHostKeyChecking,
logger: logger.DiscardLogger,
}, nil
}
return nil, fmt.Errorf("cannot proxy from %s:%d to %s:%d with users %q", c.IP, c.Port, destinationHost, destinationPort, usernames)
}
func (c *Client) CloseAll() error {
if c != nil {
if c.Client != nil {
return c.Client.Close()
}
if c.Proxy != nil {
return c.Proxy.Close()
}
}
return nil
}
func (c *Client) Connect() (err error) {
args, installed := c.localExec()
if installed {
c.logger.Infof("Login as '%s' on '%s'; client '%s'", c.User, c.IP, args[0])
c.logger.ExtraVerbosef("running locally %s", args)
if err := c.CloseAll(); err != nil {
c.logger.Warning("could not close properly SSH awless client before delegating")
}
if c.Proxy != nil {
return workaroundExeCVEThroughScript(args)
}
return syscall.Exec(args[0], args, os.Environ())
}
c.logger.Infof("No SSH. Fallback on builtin client. Login as '%s' on '%s'", c.User, c.IP)
return c.InteractiveTerminalFunc(c.Client)
}
func (c *Client) SSHConfigString(hostname string) string {
var buf bytes.Buffer
extraOpts := map[string]string{}
if len(c.Keypath) > 0 {
extraOpts["IdentityFile"] = c.Keypath
}
if !c.StrictHostKeyChecking {
extraOpts["StrictHostKeychecking"] = "no"
}
if c.Port != 22 {
extraOpts["Port"] = strconv.Itoa(c.Port)
}
if c.Proxy != nil {
var keyArg string
if k := c.Proxy.Keypath; len(k) > 0 {
keyArg = fmt.Sprintf("-i %s", k)
}
extraOpts["ProxyCommand"] = fmt.Sprintf("ssh %s %s@%s -p %d -W %%h:%%p", keyArg, c.Proxy.User, c.Proxy.IP, c.Proxy.Port)
}
params := struct {
IP, User, Name string
Extra map[string]string
}{c.IP, c.User, hostname, extraOpts}
template.Must(template.New("ssh_config").Parse(`
Host {{ .Name }}
Hostname {{ .IP }}
User {{ .User }}
{{- range $key, $value := .Extra }}
{{ $key }} {{ $value -}}
{{ end -}}
`)).Execute(&buf, params)
return buf.String()
}
func (c *Client) ConnectString() string {
args, _ := c.localExec()
return strings.Join(args, " ")
}
func (c *Client) localExec() ([]string, bool) {
exists := true
bin, err := exec.LookPath("ssh")
if err != nil {
exists = false
bin = "ssh"
}
args := []string{bin}
if len(c.Keypath) > 0 {
args = append(args, "-i", c.Keypath)
}
if c.Port != 22 {
args = append(args, "-p", strconv.Itoa(c.Port))
}
if !c.StrictHostKeyChecking {
args = append(args, "-o", "StrictHostKeychecking=no")
}
args = append(args, fmt.Sprintf("%s@%s", c.User, c.IP))
if c.Proxy != nil {
var keyArg string
if k := c.Proxy.Keypath; len(k) > 0 {
keyArg = fmt.Sprintf("-i %s", k)
}
args = append(args, "-o", fmt.Sprintf("ProxyCommand='ssh %s %s@%s -p %d -W %%h:%%p'", keyArg, c.Proxy.User, c.Proxy.IP, c.Proxy.Port))
}
return args, exists
}
func DecryptSSHKey(key []byte, password []byte) (gossh.Signer, error) {
block, _ := pem.Decode(key)
pem, err := x509.DecryptPEMBlock(block, password)
if err != nil {
return nil, err
}
sshkey, err := x509.ParsePKCS1PrivateKey(pem)
if err != nil {
return nil, err
}
return gossh.NewSignerFromKey(sshkey)
}
type privateKey struct {
path string
body []byte
}
func findPrivateKeyFromName(keyname string, keyFolders ...string) (privateKey, bool) {
var priv privateKey
if len(keyname) == 0 {
return priv, false
}
keyPaths := []string{
keyname,
}
if !strings.HasPrefix(keyname, ".pem") {
keyPaths = append(keyPaths, fmt.Sprintf("%s.pem", keyname))
}
for _, folder := range keyFolders {
if filepath.IsAbs(keyname) {
break
}
if _, err := os.Stat(folder); err != nil {
continue
}
keyPaths = append(keyPaths, filepath.Join(folder, keyname))
if !strings.HasPrefix(keyname, ".pem") {
keyPaths = append(keyPaths, filepath.Join(folder, fmt.Sprintf("%s.pem", keyname)))
}
}
for _, path := range keyPaths {
b, err := os.ReadFile(path)
if err == nil {
priv.path = path
priv.body = b
return priv, true
}
if !os.IsNotExist(err) {
return priv, false
}
}
return priv, false
}
func checkHostKey(hostname string, remote net.Addr, key gossh.PublicKey) error {
var knownHostsFiles []string
var fileToAddKnownKey string
opensshFile := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
if _, err := os.Stat(opensshFile); err == nil {
knownHostsFiles = append(knownHostsFiles, opensshFile)
fileToAddKnownKey = opensshFile
}
awlessFile := filepath.Join(os.Getenv("__AWLESS_HOME"), "known_hosts")
if _, err := os.Stat(awlessFile); err == nil {
knownHostsFiles = append(knownHostsFiles, awlessFile)
}
if fileToAddKnownKey == "" {
fileToAddKnownKey = awlessFile
}
checkKnownHostFunc, err := knownhosts.New(knownHostsFiles...)
if err != nil {
return err
}
knownhostsErr := checkKnownHostFunc(hostname, remote, key)
keyError, ok := knownhostsErr.(*knownhosts.KeyError)
if !ok {
return knownhostsErr
}
if len(keyError.Want) == 0 {
if trustKeyFunc(hostname, remote, key, fileToAddKnownKey) {
f, err := os.OpenFile(fileToAddKnownKey, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(knownhosts.Line([]string{hostname}, key) + "\n")
return err
} else {
return errors.New("Host public key verification failed.")
}
}
var knownKeyInfos string
var knownKeyFiles []string
for _, knownKey := range keyError.Want {
knownKeyInfos += fmt.Sprintf("\n-> %s (%s key in %s:%d)", gossh.FingerprintSHA256(knownKey.Key), knownKey.Key.Type(), knownKey.Filename, knownKey.Line)
knownKeyFiles = append(knownKeyFiles, fmt.Sprintf("'%s:%d'", knownKey.Filename, knownKey.Line))
}
return fmt.Errorf(`
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
AWLESS DETECTED THAT THE REMOTE HOST PUBLIC KEY HAS CHANGED
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Host key for '%s' has changed and you did not disable strict host key checking.
Someone may be trying to intercept your connection (man-in-the-middle attack). Otherwise, the host key may have been changed.
The fingerprint for the %s key sent by the remote host is %s.
You persisted:%s
To get rid of this message, update %s`, hostname, key.Type(), gossh.FingerprintSHA256(key), knownKeyInfos, strings.Join(knownKeyFiles, ","))
}
var trustKeyFunc func(hostname string, remote net.Addr, key gossh.PublicKey, keyFileName string) bool = func(hostname string, remote net.Addr, key gossh.PublicKey, keyFileName string) bool {
fmt.Printf("awless could not validate the authenticity of '%s' (unknown host)\n", hostname)
fmt.Printf("%s public key fingerprint is %s.\n", key.Type(), gossh.FingerprintSHA256(key))
fmt.Printf("Do you want to continue connecting and persist this key to '%s' (yes/no)? ", keyFileName)
var yesorno string
_, err := fmt.Scanln(&yesorno)
if err != nil {
return false
}
return strings.ToLower(yesorno) == "yes"
}
const tmpProxyCommandScriptFilename = "awless-ssh-proxycommand"
// This hack is used to circumvent a bug i cannot yet figure out
// Bug: when executing syscall.Exec(args[0], args, os.Environ()) and args contains
// the proxy command (typically args := []string{"/usr/bin/ssh", "ec2-user@172.31.78.138", "-o", "StrictHostKeychecking=no", "-o", "ProxyCommand='ssh ec2-user@52.26.181.76 -W [%h]:%p'"}
// we get an error like (in Go, Python):
//
// /bin/bash: 1: exec: ssh ec2-user@52.26.181.76 -W [172.31.78.138]:22: not found
// ssh_exchange_identification: Connection closed by remote host
//
// Since execve(2) can take as the first argument a filename, the workaround is to use
// a temporary script to execute this command.
//
// Note that the file cannot be removed since we syscall for another process. So the first time
// it is created and after that only truncated (reuse the same file)
func workaroundExeCVEThroughScript(args []string) error {
fpath := filepath.Join(os.TempDir(), tmpProxyCommandScriptFilename)
logger.ExtraVerbosef("using script %s", fpath)
tmpExec, err := os.OpenFile(fpath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0700)
if err != nil {
return err
}
script := fmt.Sprintf("#! /bin/bash\n%s", strings.Join(args, " "))
if _, err := tmpExec.Write([]byte(script)); err != nil {
return err
}
if err := tmpExec.Close(); err != nil {
return err
}
return syscall.Exec(tmpExec.Name(), []string{}, os.Environ())
}
/*
Copyright 2017 WALLIX
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 sync
import (
"github.com/wallix/awless/graph"
"github.com/wallix/awless/sync/repo"
)
// Diff represents the deleted/inserted RDF triples of a revision
type Diff struct {
From *repo.Rev
To *repo.Rev
InfraDiff *graph.Diff
AccessDiff *graph.Diff
}
func BuildDiff(from, to *repo.Rev, root string) (*Diff, error) {
infraDiff, err := graph.DefaultDiffer.Run(root, from.Infra, to.Infra)
if err != nil {
return nil, err
}
accessDiff, err := graph.DefaultDiffer.Run(root, from.Access, to.Access)
if err != nil {
return nil, err
}
res := &Diff{
From: from,
To: to,
InfraDiff: infraDiff,
AccessDiff: accessDiff,
}
return res, nil
}
/*
Copyright 2017 WALLIX
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 repo
import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/wallix/awless/graph"
git "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)
type Rev struct {
Id string
Date time.Time
Infra *graph.Graph
Access *graph.Graph
}
func (r *Rev) DateString() string {
return r.Date.Format("Mon Jan 2 15:04:05")
}
type Repo interface {
Commit(files ...string) error
List() ([]*Rev, error)
LoadRev(version string) (*Rev, error)
BaseDir() string
}
type NullRepo struct{}
func (NullRepo) Commit(files ...string) error { return nil }
func (NullRepo) List() ([]*Rev, error) { return nil, nil }
func (NullRepo) LoadRev(version string) (*Rev, error) { return nil, nil }
func (NullRepo) BaseDir() string { return "" }
type gitRepo struct {
repo *git.Repository
basedir string
}
func BaseDir() string {
return filepath.Join(os.Getenv("__AWLESS_HOME"), "aws", "rdf")
}
func New() (Repo, error) {
dir := BaseDir()
os.MkdirAll(dir, 0700)
return newGitRepo(dir)
}
func newGitRepo(path string) (Repo, error) {
if _, err := os.Stat(filepath.Join(path, ".git")); os.IsNotExist(err) {
if _, err := git.PlainInit(path, false); err != nil {
return nil, err
}
}
repo, err := git.PlainOpen(path)
return &gitRepo{repo: repo, basedir: path}, err
}
func (r *gitRepo) BaseDir() string {
return r.basedir
}
func (r *gitRepo) List() ([]*Rev, error) {
var all []*Rev
iter, err := r.repo.CommitObjects()
if err != nil {
return all, err
}
defer iter.Close()
for {
commit, err := iter.Next()
if err != nil {
if err == io.EOF {
break
}
panic(fmt.Sprintf("error listing repo revisions: %s", err))
}
all = append(all, &Rev{Id: commit.Hash.String(), Date: commit.Committer.When})
}
sort.Slice(all, func(i, j int) bool { return all[i].Date.Before(all[j].Date) })
return all, nil
}
func reduceToLastRevOfEachDay(revs []*Rev) []*Rev {
perDay := make(map[string][]*Rev)
for _, rev := range revs {
day := rev.Date.Format("2006-01-02")
perDay[day] = append(perDay[day], rev)
}
reduce := []*Rev{}
for _, v := range perDay {
sort.Slice(v, func(i, j int) bool { return v[i].Date.After(v[j].Date) })
reduce = append(reduce, v[0])
}
return reduce
}
func (r *gitRepo) LoadRev(version string) (*Rev, error) {
rev := &Rev{Id: version}
commit, err := r.repo.CommitObject(plumbing.NewHash(version))
if err != nil {
return nil, err
}
rev.Date = commit.Committer.When
rev.Infra = graph.NewGraph()
rev.Access = graph.NewGraph()
if err := unmarshalIntoGraph(rev.Infra, commit, "infra.triples"); err != nil {
return rev, err
}
if err := unmarshalIntoGraph(rev.Access, commit, "access.triples"); err != nil {
return rev, err
}
return rev, nil
}
func unmarshalIntoGraph(g *graph.Graph, commit *object.Commit, filename string) error {
f, err := commit.File(filename)
if err != nil && err != object.ErrFileNotFound {
return err
} else if err == nil {
contents, err := f.Contents()
if err != nil {
return err
}
g.Unmarshal([]byte(contents))
}
return nil
}
func (r *gitRepo) Commit(relativePaths ...string) error {
wt, err := r.repo.Worktree()
if err != nil {
return err
}
for _, f := range relativePaths {
if _, err := wt.Add(f); err != nil {
return err
}
}
msg := fmt.Sprintf("syncing %s", strings.Join(relativePaths, ", "))
committer := &object.Signature{Name: "awlessCLI", When: time.Now(), Email: "git@awless.io"}
_, err = wt.Commit(msg, &git.CommitOptions{Author: committer})
return err
}
/*
Copyright 2017 WALLIX
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 sync
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
gosync "sync"
"time"
"runtime"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/sync/repo"
)
const fileExt = ".nt"
var DefaultSyncer Syncer
type Syncer interface {
repo.Repo
Sync(...cloud.Service) (map[string]cloud.GraphAPI, error)
}
type noopsyncer struct {
repo.NullRepo
}
func NoOpSyncer() Syncer { return new(noopsyncer) }
func (s *noopsyncer) Sync(services ...cloud.Service) (map[string]cloud.GraphAPI, error) {
return map[string]cloud.GraphAPI{}, nil
}
type syncer struct {
repo.Repo
logger *logger.Logger
}
func NewSyncer(l ...*logger.Logger) Syncer {
repo, err := repo.New()
if err != nil {
panic(err)
}
s := &syncer{Repo: repo}
if len(l) > 0 {
s.logger = l[0]
} else {
s.logger = logger.DiscardLogger
}
return s
}
func (s *syncer) Sync(services ...cloud.Service) (map[string]cloud.GraphAPI, error) {
var workers gosync.WaitGroup
type result struct {
service cloud.Service
gph cloud.GraphAPI
start time.Time
err error
}
resultc := make(chan *result, len(services))
for _, service := range services {
if service.IsSyncDisabled() {
s.logger.Verbosef("sync: *disabled* for service %s", service.Name())
continue
}
workers.Add(1)
go func(srv cloud.Service) {
defer workers.Done()
start := time.Now()
g, err := srv.Fetch(context.Background())
resultc <- &result{service: srv, gph: g, start: start, err: err}
}(service)
}
go func() {
workers.Wait()
close(resultc)
}()
var allErrors []error
graphs := make(map[string]cloud.GraphAPI)
servicesByName := make(map[string]cloud.Service)
for res := range resultc {
if res.err != nil {
allErrors = append(allErrors, fmt.Errorf("syncing %s: %s", res.service.Name(), res.err))
} else {
s.logger.ExtraVerbosef("sync: fetched %s service took %s", res.service.Name(), time.Since(res.start))
}
if serv := res.service; serv != nil {
servicesByName[serv.Name()] = serv
if res.gph != nil {
graphs[serv.Name()] = res.gph
}
}
}
var filepaths []string
for name, g := range graphs {
serviceRegion := servicesByName[name].Region()
serviceProfile := servicesByName[name].Profile()
serviceDir := filepath.Join(s.BaseDir(), serviceProfile, serviceRegion)
os.MkdirAll(serviceDir, 0700)
fullpath := filepath.Join(serviceDir, fmt.Sprintf("%s%s", name, fileExt))
f, err := os.OpenFile(fullpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
allErrors = append(allErrors, fmt.Errorf("opening %s: %s", fullpath, err))
continue
}
closeFile := func() {
if err := f.Close(); err != nil {
allErrors = append(allErrors, fmt.Errorf("closing file %s: %s", fullpath, err))
}
}
if err := g.MarshalTo(f); err != nil {
allErrors = append(allErrors, fmt.Errorf("marshal to %s: %s", fullpath, err))
closeFile()
continue
}
relPath, err := filepath.Rel(s.BaseDir(), fullpath)
if err != nil {
allErrors = append(allErrors, err)
closeFile()
continue
}
filepaths = append(filepaths, relPath)
closeFile()
}
if runtime.GOOS != "windows" { // https://github.com/wallix/awless/issues/119
if err := s.Commit(filepaths...); err != nil {
allErrors = append(allErrors, fmt.Errorf("committing %s: %s", strings.Join(filepaths, ", "), err))
}
}
return graphs, concatErrors(allErrors)
}
func concatErrors(errs []error) error {
if len(errs) == 0 {
return nil
}
lines := []string{"syncing errors:"}
for _, err := range errs {
lines = append(lines, fmt.Sprintf("\t\t%s", err))
}
return errors.New(strings.Join(lines, "\n"))
}
func LoadLocalGraphForService(serviceName, profile, region string) cloud.GraphAPI {
regionDir := region
if serviceName == "access" || serviceName == "dns" || serviceName == "cdn" {
regionDir = "global"
}
path := filepath.Join(repo.BaseDir(), profile, regionDir, fmt.Sprintf("%s%s", serviceName, fileExt))
g, err := graph.NewGraphFromFiles(path)
if err != nil {
return graph.NewGraph()
}
return g
}
func LoadLocalGraphs(profile, region string) (cloud.GraphAPI, error) {
var files []string
globalFiles, _ := filepath.Glob(filepath.Join(repo.BaseDir(), profile, "global", fmt.Sprintf("*%s", fileExt)))
regionFiles, _ := filepath.Glob(filepath.Join(repo.BaseDir(), profile, region, fmt.Sprintf("*%s", fileExt)))
files = append(files, globalFiles...)
files = append(files, regionFiles...)
return graph.NewGraphFromFiles(files...)
}
func LoadAllLocalGraphs(profile string) (cloud.GraphAPI, error) {
path := filepath.Join(repo.BaseDir(), profile, "*", fmt.Sprintf("*%s", fileExt))
files, _ := filepath.Glob(path)
return graph.NewGraphFromFiles(files...)
}
package template
import (
"errors"
"fmt"
"sort"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/internal/ast"
"github.com/wallix/awless/template/params"
)
type Mode []compileFunc
var (
TestCompileMode = []compileFunc{
injectCommandsInNodesPass,
failOnDeclarationWithNoResultPass,
processAndValidateParamsPass,
checkInvalidReferenceDeclarationsPass,
resolveHolesPass,
resolveMissingHolesPass,
removeOptionalHolesPass,
resolveAliasPass,
inlineVariableValuePass,
resolveParamsAndExtractRefsPass,
}
PreRevertCompileMode = []compileFunc{
resolveParamsAndExtractRefsPass,
}
NewRunnerCompileMode = []compileFunc{
injectCommandsInNodesPass,
failOnDeclarationWithNoResultPass,
processAndValidateParamsPass,
checkInvalidReferenceDeclarationsPass,
resolveHolesPass,
resolveMissingHolesPass,
removeOptionalHolesPass,
resolveAliasPass,
inlineVariableValuePass,
failOnUnresolvedHolesPass,
failOnUnresolvedAliasPass,
resolveParamsAndExtractRefsPass,
convertParamsPass,
validateCommandsPass,
}
)
func Compile(tpl *Template, cenv env.Compiling, mode ...Mode) (*Template, env.Compiling, error) {
var pass *multiPass
if len(mode) > 0 {
pass = newMultiPass(mode[0]...)
} else {
pass = newMultiPass(NewRunnerCompileMode...)
}
return pass.compile(tpl, cenv)
}
type compileFunc func(*Template, env.Compiling) (*Template, env.Compiling, error)
// Leeloo Dallas
type multiPass struct {
passes []compileFunc
}
func newMultiPass(passes ...compileFunc) *multiPass {
return &multiPass{passes: passes}
}
func (p *multiPass) compile(tpl *Template, cenv env.Compiling) (newTpl *Template, newEnv env.Compiling, err error) {
newTpl, newEnv = tpl, cenv
for _, pass := range p.passes {
newTpl, newEnv, err = pass(newTpl, newEnv)
if err != nil {
return
}
}
return
}
func injectCommandsInNodesPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
if cenv.LookupCommandFunc() == nil {
return tpl, cenv, fmt.Errorf("command lookuper is undefined")
}
for _, node := range tpl.CommandNodesIterator() {
key := fmt.Sprintf("%s%s", node.Action, node.Entity)
cmd, ok := cenv.LookupCommandFunc()(key).(ast.Command)
if !ok {
return tpl, cenv, fmt.Errorf("%s: casting: %v is not a command", key, cmd)
}
if cmd == nil {
return tpl, cenv, fmt.Errorf("command for '%s' is nil", key)
}
node.Command = cmd
}
return tpl, cenv, nil
}
func failOnDeclarationWithNoResultPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
failOnDeclarationWithNoResult := func(node *ast.DeclarationNode) error {
cmdNode, ok := node.Expr.(*ast.CommandNode)
if !ok {
return nil
}
type ER interface {
ExtractResult(interface{}) string
}
if _, ok := cmdNode.Command.(ER); !ok {
return cmdErr(cmdNode, "command does not return a result, cannot assign to a variable")
}
return nil
}
for _, dcl := range tpl.declarationNodesIterator() {
if err := failOnDeclarationWithNoResult(dcl); err != nil {
return tpl, cenv, err
}
}
return tpl, cenv, nil
}
func processAndValidateParamsPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
normalizeMissingRequiredParamsAsHoleAndValidate := func(node *ast.CommandNode) error {
rule := node.ParamsSpec().Rule()
missingRequired := rule.Missing(node.Keys())
for _, e := range missingRequired {
normalized := fmt.Sprintf("%s.%s", node.Entity, e)
node.ParamNodes[e] = ast.NewHoleNode(normalized)
}
if err := params.Run(rule, node.Keys()); err != nil {
return cmdErr(node, err)
}
_, optionals, suggested := params.List(rule)
switch cenv.ParamsMode() {
case env.REQUIRED_PARAMS_ONLY:
return nil
case env.REQUIRED_AND_SUGGESTED_PARAMS:
suggested = excludeFromSlice(suggested, node.Keys())
case env.ALL_PARAMS:
suggested = excludeFromSlice(optionals, node.Keys())
}
for _, e := range suggested {
key := fmt.Sprintf("%s.%s", node.Entity, e)
node.ParamNodes[e] = ast.NewOptionalHoleNode(key)
}
return nil
}
err := tpl.visitCommandNodesE(normalizeMissingRequiredParamsAsHoleAndValidate)
return tpl, cenv, err
}
func resolveParamsAndExtractRefsPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
for _, node := range tpl.CommandNodesIterator() {
for k, param := range node.ParamNodes {
switch paramNode := param.(type) {
case ast.InterfaceNode:
node.ParamNodes[k] = paramNode.Value()
case ast.RefNode:
node.Refs[k] = paramNode
delete(node.ParamNodes, k)
case ast.ListNode:
var hasRef bool
var arr []interface{}
for _, elem := range paramNode.Elems() {
switch e := elem.(type) {
case ast.InterfaceNode:
arr = append(arr, e.Value())
case ast.RefNode:
hasRef = true
arr = append(arr, e)
case ast.ConcatenationNode:
arr = append(arr, e.Concat())
case ast.HoleNode, ast.AliasNode, ast.ListNode:
return tpl, cenv, fmt.Errorf("%s: unresolved value in list of type %T", k, e)
default:
arr = append(arr, e)
}
}
if hasRef {
node.Refs[k] = ast.NewListNode(arr)
delete(node.ParamNodes, k)
} else {
node.ParamNodes[k] = arr
}
case ast.ConcatenationNode:
node.ParamNodes[k] = paramNode.Concat()
case ast.HoleNode, ast.AliasNode:
return tpl, cenv, fmt.Errorf("%s: unresolved value of type %T", k, paramNode)
}
}
}
return tpl, cenv, nil
}
func convertParamsPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
convert := func(node *ast.CommandNode) error {
for _, reducer := range node.ParamsSpec().Reducers() {
params := make(map[string]interface{})
for k, v := range node.ParamNodes {
params[k] = v
}
for k, v := range node.Refs {
params[k] = v
}
out, err := reducer.Reduce(params)
if err != nil {
return cmdErr(node, err)
}
for _, k := range reducer.Keys() {
delete(node.ParamNodes, k)
delete(node.Refs, k)
}
for k, v := range out {
switch v.(type) {
case ast.ListNode, ast.RefNode, ast.ConcatenationNode:
node.Refs[k] = v
default:
node.ParamNodes[k] = v
}
}
}
return nil
}
err := tpl.visitCommandNodesE(convert)
return tpl, cenv, err
}
func validateCommandsPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
collectValidationErrs := func(node *ast.CommandNode) error {
if err := params.Validate(node.ParamsSpec().Validators(), node.ParamNodes); err != nil {
return cmdErr(node, err)
}
return nil
}
err := tpl.visitCommandNodesE(collectValidationErrs)
return tpl, cenv, err
}
func checkInvalidReferenceDeclarationsPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
return tpl, cenv, ast.VerifyRefs(tpl.AST)
}
func inlineVariableValuePass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
newTpl := &Template{ID: tpl.ID, AST: tpl.AST.Clone()}
newTpl.Statements = []*ast.Statement{}
for i, st := range tpl.Statements {
decl, isDecl := st.Node.(*ast.DeclarationNode)
if isDecl {
if right, isRightExpr := decl.Expr.(*ast.RightExpressionNode); isRightExpr {
if res := right.Result(); res != nil {
cenv.Push(env.RESOLVED_VARS, map[string]interface{}{decl.Ident: res})
}
ast.ProcessRefs(
&ast.AST{Statements: tpl.Statements[i+1:]},
map[string]interface{}{decl.Ident: right.Node()},
)
continue
}
}
newTpl.Statements = append(newTpl.Statements, st)
}
return newTpl, cenv, nil
}
func resolveHolesPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
processed := ast.ProcessHoles(tpl.AST, cenv.Get(env.FILLERS))
cenv.Push(env.PROCESSED_FILLERS, processed)
return tpl, cenv, nil
}
func resolveMissingHolesPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
uniqueHoles := ast.CollectUniqueHoles(tpl.AST)
var sortedHoles []ast.HoleNode
for hole := range uniqueHoles {
sortedHoles = append(sortedHoles, hole)
}
sort.Slice(sortedHoles, func(i, j int) bool {
a := sortedHoles[i]
b := sortedHoles[j]
if a.IsOptional() == b.IsOptional() {
return a.Hole() < b.Hole()
}
return !a.IsOptional()
})
for _, hole := range sortedHoles {
k := hole.Hole()
if cenv.MissingHolesFunc() != nil {
actual := cenv.MissingHolesFunc()(k, uniqueHoles[hole], hole.IsOptional())
if actual == "" && hole.IsOptional() {
continue
}
params, err := ParseParams(fmt.Sprintf("%s=%s", k, actual))
if err != nil {
if params, err = ParseParams(fmt.Sprintf("%s=%s", k, ast.Quote(actual))); err != nil {
return tpl, cenv, err
}
}
cenv.Push(env.FILLERS, map[string]interface{}{k: params[k]})
}
}
processed := ast.ProcessHoles(tpl.AST, cenv.Get(env.FILLERS))
cenv.Push(env.PROCESSED_FILLERS, processed)
return tpl, cenv, nil
}
func removeOptionalHolesPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
ast.RemoveOptionalHoles(tpl.AST)
return tpl, cenv, nil
}
func resolveAliasPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
var emptyResolv []string
resolvAliasFunc := func(action, entity string, key string) func(string) (string, bool) {
return func(alias string) (string, bool) {
if cenv.AliasFunc() == nil {
return "", false
}
normalized := fmt.Sprintf("%s.%s.%s", action, entity, key)
actual := cenv.AliasFunc()(normalized, alias)
if actual == "" {
emptyResolv = append(emptyResolv, alias)
return "", false
} else {
cenv.Log().ExtraVerbosef("alias: resolved '%s' to '%s' for key %s", alias, actual, key)
return actual, true
}
}
}
ast.ProcessAliases(tpl.AST, resolvAliasFunc)
switch len(emptyResolv) {
case 0:
break
case 1:
return tpl, cenv, fmt.Errorf("cannot resolve alias \"%s\". Not found in locally synced data.", emptyResolv[0])
default:
return tpl, cenv, fmt.Errorf("cannot resolve aliases: %q. Not found in locally synced data.", emptyResolv)
}
return tpl, cenv, nil
}
func failOnUnresolvedHolesPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
uniqueUnresolved := make(map[string]struct{})
for _, hole := range ast.CollectHoles(tpl.AST) {
uniqueUnresolved[hole.String()] = struct{}{}
}
var unresolved []string
for k := range uniqueUnresolved {
unresolved = append(unresolved, k)
}
if len(unresolved) > 0 {
sort.Strings(unresolved)
return tpl, cenv, fmt.Errorf("template contains unresolved holes: %v", unresolved)
}
return tpl, cenv, nil
}
func failOnUnresolvedAliasPass(tpl *Template, cenv env.Compiling) (*Template, env.Compiling, error) {
uniqueUnresolved := make(map[string]struct{})
for _, alias := range ast.CollectAliases(tpl.AST) {
uniqueUnresolved[alias.String()] = struct{}{}
}
var unresolved []string
for k := range uniqueUnresolved {
unresolved = append(unresolved, k)
}
if len(unresolved) > 0 {
sort.Strings(unresolved)
return tpl, cenv, fmt.Errorf("template contains unresolved alias: %v", unresolved)
}
return tpl, cenv, nil
}
func cmdErr(cmd *ast.CommandNode, i interface{}, a ...interface{}) error {
var prefix string
if cmd != nil {
prefix = fmt.Sprintf("%s %s: ", cmd.Action, cmd.Entity)
}
var msg string
switch ii := i.(type) {
case nil:
return nil
case string:
msg = ii
case error:
msg = ii.Error()
}
if len(a) == 0 {
return errors.New(prefix + msg)
}
return fmt.Errorf("%s"+msg, append([]interface{}{prefix}, a...)...)
}
func contains(arr []string, s string) bool {
for _, v := range arr {
if v == s {
return true
}
}
return false
}
func excludeFromSlice(in []string, exclude []string) (out []string) {
for _, v := range in {
if !contains(exclude, v) {
out = append(out, v)
}
}
return out
}
package template
import (
"sync"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
)
var (
_ env.Running = (*runEnv)(nil)
_ env.Compiling = (*compileEnv)(nil)
)
type runEnv struct {
log *logger.Logger
dryRun bool
ctx map[string]interface{}
}
func NewRunEnv(cenv env.Compiling, context ...map[string]interface{}) env.Running {
renv := new(runEnv)
renv.log = cenv.Log()
renv.ctx = make(map[string]interface{})
for _, m := range context {
for k, v := range m {
renv.ctx[k] = v
}
}
renv.ctx["AWLESS"] = cenv.Get(env.RESOLVED_VARS)
renv.ctx["Variables"] = cenv.Get(env.RESOLVED_VARS) // retro-compatibility with > v0.1.9
renv.ctx["References"] = cenv.Get(env.RESOLVED_VARS) // retro-compatibility with v0.1.2
return renv
}
func (e *runEnv) IsDryRun() bool {
return e.dryRun
}
func (e *runEnv) SetDryRun(b bool) {
e.dryRun = b
}
func (e *runEnv) Context() (out map[string]interface{}) {
out = make(map[string]interface{})
for k, v := range e.ctx {
out[k] = v
}
return
}
func (e *runEnv) Log() *logger.Logger {
return e.log
}
type compileEnv struct {
*dataMap
lookupCommandFunc func(...string) interface{}
aliasFunc func(paramPath, alias string) string
missingHolesFunc func(string, []string, bool) string
log *logger.Logger
paramsSuggested int
}
func (e *compileEnv) LookupCommandFunc() func(...string) interface{} {
return e.lookupCommandFunc
}
func (e *compileEnv) AliasFunc() func(paramPath, alias string) string {
return e.aliasFunc
}
func (e *compileEnv) MissingHolesFunc() func(string, []string, bool) string {
return e.missingHolesFunc
}
func (e *compileEnv) ParamsMode() int {
return e.paramsSuggested
}
func (e *compileEnv) Log() *logger.Logger {
return e.log
}
type noopCompileEnv struct{}
func (*noopCompileEnv) LookupCommandFunc() func(...string) interface{} { return nil }
func (*noopCompileEnv) AliasFunc() func(paramPath, alias string) string { return nil }
func (*noopCompileEnv) MissingHolesFunc() func(string, []string, bool) string { return nil }
func (*noopCompileEnv) ParamsMode() int { return -1 }
func (*noopCompileEnv) Log() *logger.Logger { return logger.DiscardLogger }
func (*noopCompileEnv) Push(int, ...map[string]interface{}) {}
func (*noopCompileEnv) Get(int) map[string]interface{} { return make(map[string]interface{}) }
func NewEnv() *envBuilder {
b := &envBuilder{new(compileEnv)}
b.E.lookupCommandFunc = func(...string) interface{} { return nil }
b.E.log = logger.DiscardLogger
b.E.dataMap = new(dataMap)
return b
}
type dataMap struct {
mu sync.Mutex
M map[int]map[string]interface{}
}
func (d *dataMap) Push(typ int, data ...map[string]interface{}) {
d.mu.Lock()
defer d.mu.Unlock()
if d.M == nil {
d.M = make(map[int]map[string]interface{})
}
if d.M[typ] == nil {
d.M[typ] = make(map[string]interface{})
}
for _, m := range data {
for k, v := range m {
d.M[typ][k] = v
}
}
}
func (d *dataMap) Get(typ int) (out map[string]interface{}) {
d.mu.Lock()
defer d.mu.Unlock()
out = make(map[string]interface{})
if d.M[typ] == nil {
return
}
for k, v := range d.M[typ] {
out[k] = v
}
return
}
type envBuilder struct {
E *compileEnv
}
func (b *envBuilder) WithAliasFunc(fn func(paramPath, alias string) string) *envBuilder {
b.E.aliasFunc = fn
return b
}
func (b *envBuilder) WithMissingHolesFunc(fn func(string, []string, bool) string) *envBuilder {
b.E.missingHolesFunc = fn
return b
}
func (b *envBuilder) WithLookupCommandFunc(fn func(...string) interface{}) *envBuilder {
b.E.lookupCommandFunc = fn
return b
}
func (b *envBuilder) WithLog(l *logger.Logger) *envBuilder {
b.E.log = l
return b
}
func (b *envBuilder) WithParamsMode(paramsSuggested int) *envBuilder {
b.E.paramsSuggested = paramsSuggested
return b
}
func (b *envBuilder) Build() env.Compiling {
return b.E
}
package parameters
import (
"fmt"
"strings"
awsspec "github.com/wallix/awless/aws/spec"
"github.com/wallix/awless/template"
"github.com/wallix/awless/template/env"
)
func Fuzz(data []byte) int {
var ok bool
for _, def := range awsspec.AWSTemplatesDefinitions {
fillers := make(map[string]interface{})
for _, param := range def.Params.Required() {
fillers[param] = "default"
}
cenv := template.NewEnv().WithAliasFunc(func(p, v string) string { return "" }).
WithLookupCommandFunc(func(tokens ...string) interface{} {
return awsspec.MockAWSSessionFactory.Build(strings.Join(tokens, ""))()
}).Build()
cenv.Push(env.FILLERS, fillers)
for _, param := range def.Params.Required() {
inTpl, err := template.Parse(fmt.Sprintf("%s %s %s=%s", def.Action, def.Entity, param, string(data)))
if err != nil {
continue
}
_, _, err = template.Compile(inTpl, cenv, template.TestCompileMode)
if err != nil {
continue
}
ok = true
}
}
if ok {
return 1
}
return 0
}
package parsing
import (
"fmt"
"github.com/wallix/awless/template"
)
func Fuzz(data []byte) int {
if _, err := template.Parse(fmt.Sprintf("none none %s", data)); err != nil {
return 0
}
return 1
}
package ast
type Action string
const (
UnknownAction Action = "unknown"
NoneAction Action = "none"
Create Action = "create"
Delete Action = "delete"
Update Action = "update"
Check Action = "check"
Start Action = "start"
Restart Action = "restart"
Stop Action = "stop"
Attach Action = "attach"
Detach Action = "detach"
Copy Action = "copy"
Import Action = "import"
Authenticate Action = "authenticate"
)
var actions = map[Action]struct{}{
NoneAction: {},
Create: {},
Delete: {},
Update: {},
Check: {},
Start: {},
Restart: {},
Stop: {},
Attach: {},
Detach: {},
Copy: {},
Import: {},
Authenticate: {},
}
func IsInvalidAction(s string) bool {
_, ok := actions[Action(s)]
return !ok
}
/*
Copyright 2017 WALLIX
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 ast
import (
"bytes"
"fmt"
"sort"
"strings"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/params"
)
type Node interface {
clone() Node
String() string
}
type AST struct {
Statements []*Statement
// state to build the AST
stmtBuilder *statementBuilder
}
type Statement struct {
Node
}
type DeclarationNode struct {
Ident string
Expr ExpressionNode
}
type ExpressionNode interface {
Node
Result() interface{}
Err() error
}
type Command interface {
ParamsSpec() params.Spec
Run(env.Running, map[string]interface{}) (interface{}, error)
}
func (c *CommandNode) Result() interface{} { return c.CmdResult }
func (c *CommandNode) Err() error { return c.CmdErr }
func (c *CommandNode) Keys() (keys []string) {
for k := range c.ParamNodes {
keys = append(keys, k)
}
for k := range c.Refs {
keys = append(keys, k)
}
return
}
func (c *CommandNode) String() string {
var all []string
for k, v := range c.ParamNodes {
switch vv := v.(type) {
case string:
all = append(all, fmt.Sprintf("%s=%v", k, quoteStringIfNeeded(vv)))
case []interface{}:
var a []string
for _, e := range vv {
switch ee := e.(type) {
case string:
a = append(a, quoteStringIfNeeded(ee))
default:
a = append(a, fmt.Sprint(ee))
}
}
all = append(all, fmt.Sprintf("%s=[%s]", k, strings.Join(a, ",")))
default:
all = append(all, fmt.Sprintf("%s=%v", k, v))
}
}
for k, v := range c.Refs {
all = append(all, fmt.Sprintf("%s=%v", k, v))
}
sort.Strings(all)
var buff bytes.Buffer
fmt.Fprintf(&buff, "%s %s", c.Action, c.Entity)
if len(all) > 0 {
fmt.Fprintf(&buff, " %s", strings.Join(all, " "))
}
return buff.String()
}
func (c *CommandNode) clone() Node {
cmd := &CommandNode{
Command: c.Command,
Action: c.Action, Entity: c.Entity,
ParamNodes: make(map[string]interface{}),
Refs: make(map[string]interface{}),
}
for k, v := range c.ParamNodes {
cmd.ParamNodes[k] = v
}
for k, v := range c.Refs {
cmd.Refs[k] = v
}
return cmd
}
func (c *CommandNode) ProcessRefs(refs map[string]interface{}) {
for paramKey, param := range c.Refs {
if ref, ok := param.(RefNode); ok {
for k, v := range refs {
if k == ref.key {
c.ParamNodes[paramKey] = v
}
}
}
if list, ok := param.(ListNode); ok {
var new []interface{}
for _, e := range list.arr {
newElem := e
if ref, isRef := e.(RefNode); isRef {
for k, v := range refs {
if k == ref.key {
newElem = v
}
}
}
new = append(new, newElem)
}
c.ParamNodes[paramKey] = new
}
}
}
func (c *CommandNode) ToDriverParams() map[string]interface{} {
params := make(map[string]interface{})
for k, v := range c.ParamNodes {
switch node := v.(type) {
case InterfaceNode:
params[k] = node.i
case RefNode, HoleNode, AliasNode:
default:
params[k] = node
}
}
return params
}
func (c *CommandNode) ToFillerParams() map[string]interface{} {
params := make(map[string]interface{})
fn := func(k string, v interface{}) interface{} {
switch vv := v.(type) {
case InterfaceNode:
return vv.i
case AliasNode:
return v
}
return nil
}
for k, v := range c.ParamNodes {
i := fn(k, v)
if i != nil {
params[k] = i
continue
}
switch vv := v.(type) {
case ListNode:
var arr []interface{}
for _, a := range vv.arr {
arr = append(arr, fn(k, a))
}
params[k] = NewListNode(arr)
}
}
return params
}
func (s *Statement) Clone() *Statement {
newStat := &Statement{}
newStat.Node = s.Node.clone()
return newStat
}
func (a *AST) String() string {
var all []string
for _, stat := range a.Statements {
all = append(all, stat.String())
}
return strings.Join(all, "\n")
}
func (n *DeclarationNode) clone() Node {
decl := &DeclarationNode{
Ident: n.Ident,
}
if n.Expr != nil {
decl.Expr = n.Expr.clone().(ExpressionNode)
}
return decl
}
func (n *DeclarationNode) String() string {
return fmt.Sprintf("%s = %s", n.Ident, n.Expr)
}
func (a *AST) Clone() *AST {
clone := &AST{}
for _, stat := range a.Statements {
clone.Statements = append(clone.Statements, stat.Clone())
}
return clone
}
func (a *AST) clone() Node {
return a.Clone()
}
package ast
import (
"fmt"
"math"
"sort"
"strconv"
)
const endSymbol rune = 1114112
/* The rule types inferred from the grammar are below. */
type pegRule uint8
const (
ruleUnknown pegRule = iota
ruleScript
ruleStatement
ruleAction
ruleEntity
ruleDeclaration
ruleValueExpr
ruleCmdExpr
ruleParams
ruleParam
ruleIdentifier
ruleCompositeValue
ruleListValue
ruleListWithoutSquareBrackets
ruleNoRefValue
ruleValue
ruleCustomTypedValue
ruleUnquotedParamValue
ruleUnquotedParam
ruleConcatenationValue
ruleQuotedStringValue
ruleQuotedString
ruleDoubleQuotedValue
ruleSingleQuotedValue
ruleIntRangeValue
ruleRefValue
ruleAliasValue
ruleHoleValue
ruleHole
ruleHolesStringValue
ruleHoleWithSuffixValue
ruleComment
ruleSingleQuote
ruleDoubleQuote
ruleWhiteSpacing
ruleMustWhiteSpacing
ruleEqual
ruleBlankLine
ruleWhitespace
ruleEndOfLine
ruleEndOfFile
ruleAction0
ruleAction1
rulePegText
ruleAction2
ruleAction3
ruleAction4
ruleAction5
ruleAction6
ruleAction7
ruleAction8
ruleAction9
ruleAction10
ruleAction11
ruleAction12
ruleAction13
ruleAction14
ruleAction15
ruleAction16
ruleAction17
ruleAction18
ruleAction19
ruleAction20
ruleAction21
ruleAction22
ruleAction23
ruleAction24
)
var rul3s = [...]string{
"Unknown",
"Script",
"Statement",
"Action",
"Entity",
"Declaration",
"ValueExpr",
"CmdExpr",
"Params",
"Param",
"Identifier",
"CompositeValue",
"ListValue",
"ListWithoutSquareBrackets",
"NoRefValue",
"Value",
"CustomTypedValue",
"UnquotedParamValue",
"UnquotedParam",
"ConcatenationValue",
"QuotedStringValue",
"QuotedString",
"DoubleQuotedValue",
"SingleQuotedValue",
"IntRangeValue",
"RefValue",
"AliasValue",
"HoleValue",
"Hole",
"HolesStringValue",
"HoleWithSuffixValue",
"Comment",
"SingleQuote",
"DoubleQuote",
"WhiteSpacing",
"MustWhiteSpacing",
"Equal",
"BlankLine",
"Whitespace",
"EndOfLine",
"EndOfFile",
"Action0",
"Action1",
"PegText",
"Action2",
"Action3",
"Action4",
"Action5",
"Action6",
"Action7",
"Action8",
"Action9",
"Action10",
"Action11",
"Action12",
"Action13",
"Action14",
"Action15",
"Action16",
"Action17",
"Action18",
"Action19",
"Action20",
"Action21",
"Action22",
"Action23",
"Action24",
}
type token32 struct {
pegRule
begin, end uint32
}
func (t *token32) String() string {
return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end)
}
type node32 struct {
token32
up, next *node32
}
func (node *node32) print(pretty bool, buffer string) {
var print func(node *node32, depth int)
print = func(node *node32, depth int) {
for node != nil {
for c := 0; c < depth; c++ {
fmt.Printf(" ")
}
rule := rul3s[node.pegRule]
quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end])))
if !pretty {
fmt.Printf("%v %v\n", rule, quote)
} else {
fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote)
}
if node.up != nil {
print(node.up, depth+1)
}
node = node.next
}
}
print(node, 0)
}
func (node *node32) Print(buffer string) {
node.print(false, buffer)
}
func (node *node32) PrettyPrint(buffer string) {
node.print(true, buffer)
}
type tokens32 struct {
tree []token32
}
func (t *tokens32) Trim(length uint32) {
t.tree = t.tree[:length]
}
func (t *tokens32) Print() {
for _, token := range t.tree {
fmt.Println(token.String())
}
}
func (t *tokens32) AST() *node32 {
type element struct {
node *node32
down *element
}
tokens := t.Tokens()
var stack *element
for _, token := range tokens {
if token.begin == token.end {
continue
}
node := &node32{token32: token}
for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end {
stack.node.next = node.up
node.up = stack.node
stack = stack.down
}
stack = &element{node: node, down: stack}
}
if stack != nil {
return stack.node
}
return nil
}
func (t *tokens32) PrintSyntaxTree(buffer string) {
t.AST().Print(buffer)
}
func (t *tokens32) PrettyPrintSyntaxTree(buffer string) {
t.AST().PrettyPrint(buffer)
}
func (t *tokens32) Add(rule pegRule, begin, end, index uint32) {
if tree := t.tree; int(index) >= len(tree) {
expanded := make([]token32, 2*len(tree))
copy(expanded, tree)
t.tree = expanded
}
t.tree[index] = token32{
pegRule: rule,
begin: begin,
end: end,
}
}
func (t *tokens32) Tokens() []token32 {
return t.tree
}
type Peg struct {
*AST
Buffer string
buffer []rune
rules [67]func() bool
parse func(rule ...int) error
reset func()
Pretty bool
tokens32
}
func (p *Peg) Parse(rule ...int) error {
return p.parse(rule...)
}
func (p *Peg) Reset() {
p.reset()
}
type textPosition struct {
line, symbol int
}
type textPositionMap map[int]textPosition
func translatePositions(buffer []rune, positions []int) textPositionMap {
length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0
sort.Ints(positions)
search:
for i, c := range buffer {
if c == '\n' {
line, symbol = line+1, 0
} else {
symbol++
}
if i == positions[j] {
translations[positions[j]] = textPosition{line, symbol}
for j++; j < length; j++ {
if i != positions[j] {
continue search
}
}
break search
}
}
return translations
}
type parseError struct {
p *Peg
max token32
}
func (e *parseError) Error() string {
tokens, error := []token32{e.max}, "\n"
positions, p := make([]int, 2*len(tokens)), 0
for _, token := range tokens {
positions[p], p = int(token.begin), p+1
positions[p], p = int(token.end), p+1
}
translations := translatePositions(e.p.buffer, positions)
format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n"
if e.p.Pretty {
format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n"
}
for _, token := range tokens {
begin, end := int(token.begin), int(token.end)
error += fmt.Sprintf(format,
rul3s[token.pegRule],
translations[begin].line, translations[begin].symbol,
translations[end].line, translations[end].symbol,
strconv.Quote(string(e.p.buffer[begin:end])))
}
return error
}
func (p *Peg) PrintSyntaxTree() {
if p.Pretty {
p.tokens32.PrettyPrintSyntaxTree(p.Buffer)
} else {
p.tokens32.PrintSyntaxTree(p.Buffer)
}
}
func (p *Peg) Execute() {
buffer, _buffer, text, begin, end := p.Buffer, p.buffer, "", 0, 0
for _, token := range p.Tokens() {
switch token.pegRule {
case rulePegText:
begin, end = int(token.begin), int(token.end)
text = string(_buffer[begin:end])
case ruleAction0:
p.NewStatement()
case ruleAction1:
p.StatementDone()
case ruleAction2:
p.addDeclarationIdentifier(text)
case ruleAction3:
p.addValue()
case ruleAction4:
p.addAction(text)
case ruleAction5:
p.addEntity(text)
case ruleAction6:
p.addParamKey(text)
case ruleAction7:
p.addFirstValueInList()
case ruleAction8:
p.lastValueInList()
case ruleAction9:
p.addFirstValueInList()
case ruleAction10:
p.lastValueInList()
case ruleAction11:
p.addAliasParam(text)
case ruleAction12:
p.addParamRefValue(text)
case ruleAction13:
p.addParamValue(text)
case ruleAction14:
p.addParamValue(text)
case ruleAction15:
p.addFirstValueInConcatenation()
case ruleAction16:
p.lastValueInConcatenation()
case ruleAction17:
p.addFirstValueInConcatenation()
case ruleAction18:
p.lastValueInConcatenation()
case ruleAction19:
p.addStringValue(text)
case ruleAction20:
p.addParamHoleValue(text)
case ruleAction21:
p.addFirstValueInConcatenation()
case ruleAction22:
p.lastValueInConcatenation()
case ruleAction23:
p.addFirstValueInConcatenation()
case ruleAction24:
p.lastValueInConcatenation()
}
}
_, _, _, _, _ = buffer, _buffer, text, begin, end
}
func (p *Peg) Init() {
var (
max token32
position, tokenIndex uint32
buffer []rune
)
p.reset = func() {
max = token32{}
position, tokenIndex = 0, 0
p.buffer = []rune(p.Buffer)
if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol {
p.buffer = append(p.buffer, endSymbol)
}
buffer = p.buffer
}
p.reset()
_rules := p.rules
tree := tokens32{tree: make([]token32, math.MaxInt16)}
p.parse = func(rule ...int) error {
r := 1
if len(rule) > 0 {
r = rule[0]
}
matches := p.rules[r]()
p.tokens32 = tree
if matches {
p.Trim(tokenIndex)
return nil
}
return &parseError{p, max}
}
add := func(rule pegRule, begin uint32) {
tree.Add(rule, begin, position, tokenIndex)
tokenIndex++
if begin != position && position > max.end {
max = token32{rule, begin, position}
}
}
matchDot := func() bool {
if buffer[position] != endSymbol {
position++
return true
}
return false
}
/*matchChar := func(c byte) bool {
if buffer[position] == c {
position++
return true
}
return false
}*/
/*matchRange := func(lower byte, upper byte) bool {
if c := buffer[position]; c >= lower && c <= upper {
position++
return true
}
return false
}*/
_rules = [...]func() bool{
nil,
/* 0 Script <- <((BlankLine* Statement BlankLine*)+ WhiteSpacing EndOfFile)> */
func() bool {
position0, tokenIndex0 := position, tokenIndex
{
position1 := position
l4:
{
position5, tokenIndex5 := position, tokenIndex
if !_rules[ruleBlankLine]() {
goto l5
}
goto l4
l5:
position, tokenIndex = position5, tokenIndex5
}
{
position6 := position
{
add(ruleAction0, position)
}
if !_rules[ruleWhiteSpacing]() {
goto l0
}
{
position8, tokenIndex8 := position, tokenIndex
if !_rules[ruleCmdExpr]() {
goto l9
}
goto l8
l9:
position, tokenIndex = position8, tokenIndex8
{
position11 := position
{
position12 := position
if !_rules[ruleIdentifier]() {
goto l10
}
add(rulePegText, position12)
}
{
add(ruleAction2, position)
}
if !_rules[ruleEqual]() {
goto l10
}
{
position14, tokenIndex14 := position, tokenIndex
if !_rules[ruleCmdExpr]() {
goto l15
}
goto l14
l15:
position, tokenIndex = position14, tokenIndex14
{
position16 := position
{
add(ruleAction3, position)
}
if !_rules[ruleCompositeValue]() {
goto l10
}
add(ruleValueExpr, position16)
}
}
l14:
add(ruleDeclaration, position11)
}
goto l8
l10:
position, tokenIndex = position8, tokenIndex8
{
position18 := position
{
position19, tokenIndex19 := position, tokenIndex
if buffer[position] != rune('#') {
goto l20
}
position++
l21:
{
position22, tokenIndex22 := position, tokenIndex
{
position23, tokenIndex23 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l23
}
goto l22
l23:
position, tokenIndex = position23, tokenIndex23
}
if !matchDot() {
goto l22
}
goto l21
l22:
position, tokenIndex = position22, tokenIndex22
}
goto l19
l20:
position, tokenIndex = position19, tokenIndex19
if buffer[position] != rune('/') {
goto l0
}
position++
if buffer[position] != rune('/') {
goto l0
}
position++
l24:
{
position25, tokenIndex25 := position, tokenIndex
{
position26, tokenIndex26 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l26
}
goto l25
l26:
position, tokenIndex = position26, tokenIndex26
}
if !matchDot() {
goto l25
}
goto l24
l25:
position, tokenIndex = position25, tokenIndex25
}
}
l19:
add(ruleComment, position18)
}
}
l8:
if !_rules[ruleWhiteSpacing]() {
goto l0
}
l27:
{
position28, tokenIndex28 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l28
}
goto l27
l28:
position, tokenIndex = position28, tokenIndex28
}
{
add(ruleAction1, position)
}
add(ruleStatement, position6)
}
l30:
{
position31, tokenIndex31 := position, tokenIndex
if !_rules[ruleBlankLine]() {
goto l31
}
goto l30
l31:
position, tokenIndex = position31, tokenIndex31
}
l2:
{
position3, tokenIndex3 := position, tokenIndex
l32:
{
position33, tokenIndex33 := position, tokenIndex
if !_rules[ruleBlankLine]() {
goto l33
}
goto l32
l33:
position, tokenIndex = position33, tokenIndex33
}
{
position34 := position
{
add(ruleAction0, position)
}
if !_rules[ruleWhiteSpacing]() {
goto l3
}
{
position36, tokenIndex36 := position, tokenIndex
if !_rules[ruleCmdExpr]() {
goto l37
}
goto l36
l37:
position, tokenIndex = position36, tokenIndex36
{
position39 := position
{
position40 := position
if !_rules[ruleIdentifier]() {
goto l38
}
add(rulePegText, position40)
}
{
add(ruleAction2, position)
}
if !_rules[ruleEqual]() {
goto l38
}
{
position42, tokenIndex42 := position, tokenIndex
if !_rules[ruleCmdExpr]() {
goto l43
}
goto l42
l43:
position, tokenIndex = position42, tokenIndex42
{
position44 := position
{
add(ruleAction3, position)
}
if !_rules[ruleCompositeValue]() {
goto l38
}
add(ruleValueExpr, position44)
}
}
l42:
add(ruleDeclaration, position39)
}
goto l36
l38:
position, tokenIndex = position36, tokenIndex36
{
position46 := position
{
position47, tokenIndex47 := position, tokenIndex
if buffer[position] != rune('#') {
goto l48
}
position++
l49:
{
position50, tokenIndex50 := position, tokenIndex
{
position51, tokenIndex51 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l51
}
goto l50
l51:
position, tokenIndex = position51, tokenIndex51
}
if !matchDot() {
goto l50
}
goto l49
l50:
position, tokenIndex = position50, tokenIndex50
}
goto l47
l48:
position, tokenIndex = position47, tokenIndex47
if buffer[position] != rune('/') {
goto l3
}
position++
if buffer[position] != rune('/') {
goto l3
}
position++
l52:
{
position53, tokenIndex53 := position, tokenIndex
{
position54, tokenIndex54 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l54
}
goto l53
l54:
position, tokenIndex = position54, tokenIndex54
}
if !matchDot() {
goto l53
}
goto l52
l53:
position, tokenIndex = position53, tokenIndex53
}
}
l47:
add(ruleComment, position46)
}
}
l36:
if !_rules[ruleWhiteSpacing]() {
goto l3
}
l55:
{
position56, tokenIndex56 := position, tokenIndex
if !_rules[ruleEndOfLine]() {
goto l56
}
goto l55
l56:
position, tokenIndex = position56, tokenIndex56
}
{
add(ruleAction1, position)
}
add(ruleStatement, position34)
}
l58:
{
position59, tokenIndex59 := position, tokenIndex
if !_rules[ruleBlankLine]() {
goto l59
}
goto l58
l59:
position, tokenIndex = position59, tokenIndex59
}
goto l2
l3:
position, tokenIndex = position3, tokenIndex3
}
if !_rules[ruleWhiteSpacing]() {
goto l0
}
{
position60 := position
{
position61, tokenIndex61 := position, tokenIndex
if !matchDot() {
goto l61
}
goto l0
l61:
position, tokenIndex = position61, tokenIndex61
}
add(ruleEndOfFile, position60)
}
add(ruleScript, position1)
}
return true
l0:
position, tokenIndex = position0, tokenIndex0
return false
},
/* 1 Statement <- <(Action0 WhiteSpacing (CmdExpr / Declaration / Comment) WhiteSpacing EndOfLine* Action1)> */
nil,
/* 2 Action <- <[a-z]+> */
nil,
/* 3 Entity <- <([a-z] / [0-9])+> */
nil,
/* 4 Declaration <- <(<Identifier> Action2 Equal (CmdExpr / ValueExpr))> */
nil,
/* 5 ValueExpr <- <(Action3 CompositeValue)> */
nil,
/* 6 CmdExpr <- <(<Action> Action4 MustWhiteSpacing <Entity> Action5 (MustWhiteSpacing Params)?)> */
func() bool {
position67, tokenIndex67 := position, tokenIndex
{
position68 := position
{
position69 := position
{
position70 := position
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l67
}
position++
l71:
{
position72, tokenIndex72 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l72
}
position++
goto l71
l72:
position, tokenIndex = position72, tokenIndex72
}
add(ruleAction, position70)
}
add(rulePegText, position69)
}
{
add(ruleAction4, position)
}
if !_rules[ruleMustWhiteSpacing]() {
goto l67
}
{
position74 := position
{
position75 := position
{
position78, tokenIndex78 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l79
}
position++
goto l78
l79:
position, tokenIndex = position78, tokenIndex78
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l67
}
position++
}
l78:
l76:
{
position77, tokenIndex77 := position, tokenIndex
{
position80, tokenIndex80 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l81
}
position++
goto l80
l81:
position, tokenIndex = position80, tokenIndex80
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l77
}
position++
}
l80:
goto l76
l77:
position, tokenIndex = position77, tokenIndex77
}
add(ruleEntity, position75)
}
add(rulePegText, position74)
}
{
add(ruleAction5, position)
}
{
position83, tokenIndex83 := position, tokenIndex
if !_rules[ruleMustWhiteSpacing]() {
goto l83
}
{
position85 := position
{
position88 := position
{
position89 := position
if !_rules[ruleIdentifier]() {
goto l83
}
add(rulePegText, position89)
}
{
add(ruleAction6, position)
}
if !_rules[ruleEqual]() {
goto l83
}
if !_rules[ruleCompositeValue]() {
goto l83
}
if !_rules[ruleWhiteSpacing]() {
goto l83
}
add(ruleParam, position88)
}
l86:
{
position87, tokenIndex87 := position, tokenIndex
{
position91 := position
{
position92 := position
if !_rules[ruleIdentifier]() {
goto l87
}
add(rulePegText, position92)
}
{
add(ruleAction6, position)
}
if !_rules[ruleEqual]() {
goto l87
}
if !_rules[ruleCompositeValue]() {
goto l87
}
if !_rules[ruleWhiteSpacing]() {
goto l87
}
add(ruleParam, position91)
}
goto l86
l87:
position, tokenIndex = position87, tokenIndex87
}
add(ruleParams, position85)
}
goto l84
l83:
position, tokenIndex = position83, tokenIndex83
}
l84:
add(ruleCmdExpr, position68)
}
return true
l67:
position, tokenIndex = position67, tokenIndex67
return false
},
/* 7 Params <- <Param+> */
nil,
/* 8 Param <- <(<Identifier> Action6 Equal CompositeValue WhiteSpacing)> */
nil,
/* 9 Identifier <- <((&('.') '.') | (&('_') '_') | (&('-') '-') | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') [A-Z]) | (&('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z') [a-z]))+> */
func() bool {
position96, tokenIndex96 := position, tokenIndex
{
position97 := position
{
switch buffer[position] {
case '.':
if buffer[position] != rune('.') {
goto l96
}
position++
break
case '_':
if buffer[position] != rune('_') {
goto l96
}
position++
break
case '-':
if buffer[position] != rune('-') {
goto l96
}
position++
break
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l96
}
position++
break
case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z':
if c := buffer[position]; c < rune('A') || c > rune('Z') {
goto l96
}
position++
break
default:
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l96
}
position++
break
}
}
l98:
{
position99, tokenIndex99 := position, tokenIndex
{
switch buffer[position] {
case '.':
if buffer[position] != rune('.') {
goto l99
}
position++
break
case '_':
if buffer[position] != rune('_') {
goto l99
}
position++
break
case '-':
if buffer[position] != rune('-') {
goto l99
}
position++
break
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l99
}
position++
break
case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z':
if c := buffer[position]; c < rune('A') || c > rune('Z') {
goto l99
}
position++
break
default:
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l99
}
position++
break
}
}
goto l98
l99:
position, tokenIndex = position99, tokenIndex99
}
add(ruleIdentifier, position97)
}
return true
l96:
position, tokenIndex = position96, tokenIndex96
return false
},
/* 10 CompositeValue <- <(ListValue / ListWithoutSquareBrackets / Value)> */
func() bool {
position102, tokenIndex102 := position, tokenIndex
{
position103 := position
{
position104, tokenIndex104 := position, tokenIndex
{
position106 := position
{
add(ruleAction7, position)
}
if buffer[position] != rune('[') {
goto l105
}
position++
{
position108, tokenIndex108 := position, tokenIndex
if !_rules[ruleWhiteSpacing]() {
goto l108
}
if !_rules[ruleValue]() {
goto l108
}
if !_rules[ruleWhiteSpacing]() {
goto l108
}
goto l109
l108:
position, tokenIndex = position108, tokenIndex108
}
l109:
l110:
{
position111, tokenIndex111 := position, tokenIndex
if buffer[position] != rune(',') {
goto l111
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l111
}
if !_rules[ruleValue]() {
goto l111
}
if !_rules[ruleWhiteSpacing]() {
goto l111
}
goto l110
l111:
position, tokenIndex = position111, tokenIndex111
}
if buffer[position] != rune(']') {
goto l105
}
position++
{
add(ruleAction8, position)
}
add(ruleListValue, position106)
}
goto l104
l105:
position, tokenIndex = position104, tokenIndex104
{
position114 := position
{
add(ruleAction9, position)
}
if !_rules[ruleWhiteSpacing]() {
goto l113
}
if !_rules[ruleValue]() {
goto l113
}
if !_rules[ruleWhiteSpacing]() {
goto l113
}
if buffer[position] != rune(',') {
goto l113
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l113
}
if !_rules[ruleValue]() {
goto l113
}
if !_rules[ruleWhiteSpacing]() {
goto l113
}
l116:
{
position117, tokenIndex117 := position, tokenIndex
if buffer[position] != rune(',') {
goto l117
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l117
}
if !_rules[ruleValue]() {
goto l117
}
if !_rules[ruleWhiteSpacing]() {
goto l117
}
goto l116
l117:
position, tokenIndex = position117, tokenIndex117
}
{
add(ruleAction10, position)
}
add(ruleListWithoutSquareBrackets, position114)
}
goto l104
l113:
position, tokenIndex = position104, tokenIndex104
if !_rules[ruleValue]() {
goto l102
}
}
l104:
add(ruleCompositeValue, position103)
}
return true
l102:
position, tokenIndex = position102, tokenIndex102
return false
},
/* 11 ListValue <- <(Action7 '[' (WhiteSpacing Value WhiteSpacing)? (',' WhiteSpacing Value WhiteSpacing)* ']' Action8)> */
nil,
/* 12 ListWithoutSquareBrackets <- <(Action9 (WhiteSpacing Value WhiteSpacing) (',' WhiteSpacing Value WhiteSpacing)+ Action10)> */
nil,
/* 13 NoRefValue <- <(ConcatenationValue / HoleWithSuffixValue / HoleValue / HolesStringValue / (AliasValue Action11) / (DoubleQuote CustomTypedValue DoubleQuote) / (SingleQuote CustomTypedValue SingleQuote) / CustomTypedValue / QuotedStringValue / UnquotedParamValue)> */
nil,
/* 14 Value <- <((RefValue Action12) / NoRefValue)> */
func() bool {
position122, tokenIndex122 := position, tokenIndex
{
position123 := position
{
position124, tokenIndex124 := position, tokenIndex
{
position126 := position
if buffer[position] != rune('$') {
goto l125
}
position++
{
position127 := position
if !_rules[ruleIdentifier]() {
goto l125
}
add(rulePegText, position127)
}
add(ruleRefValue, position126)
}
{
add(ruleAction12, position)
}
goto l124
l125:
position, tokenIndex = position124, tokenIndex124
{
position129 := position
{
position130, tokenIndex130 := position, tokenIndex
{
position132 := position
{
position133, tokenIndex133 := position, tokenIndex
{
add(ruleAction15, position)
}
if !_rules[ruleHoleValue]() {
goto l134
}
if !_rules[ruleWhiteSpacing]() {
goto l134
}
if buffer[position] != rune('+') {
goto l134
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l134
}
{
position138, tokenIndex138 := position, tokenIndex
if !_rules[ruleQuotedStringValue]() {
goto l139
}
goto l138
l139:
position, tokenIndex = position138, tokenIndex138
if !_rules[ruleHoleValue]() {
goto l134
}
}
l138:
l136:
{
position137, tokenIndex137 := position, tokenIndex
if !_rules[ruleWhiteSpacing]() {
goto l137
}
if buffer[position] != rune('+') {
goto l137
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l137
}
{
position140, tokenIndex140 := position, tokenIndex
if !_rules[ruleQuotedStringValue]() {
goto l141
}
goto l140
l141:
position, tokenIndex = position140, tokenIndex140
if !_rules[ruleHoleValue]() {
goto l137
}
}
l140:
goto l136
l137:
position, tokenIndex = position137, tokenIndex137
}
{
add(ruleAction16, position)
}
goto l133
l134:
position, tokenIndex = position133, tokenIndex133
{
add(ruleAction17, position)
}
if !_rules[ruleQuotedStringValue]() {
goto l131
}
if !_rules[ruleWhiteSpacing]() {
goto l131
}
if buffer[position] != rune('+') {
goto l131
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l131
}
{
position146, tokenIndex146 := position, tokenIndex
if !_rules[ruleQuotedStringValue]() {
goto l147
}
goto l146
l147:
position, tokenIndex = position146, tokenIndex146
if !_rules[ruleHoleValue]() {
goto l131
}
}
l146:
l144:
{
position145, tokenIndex145 := position, tokenIndex
if !_rules[ruleWhiteSpacing]() {
goto l145
}
if buffer[position] != rune('+') {
goto l145
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l145
}
{
position148, tokenIndex148 := position, tokenIndex
if !_rules[ruleQuotedStringValue]() {
goto l149
}
goto l148
l149:
position, tokenIndex = position148, tokenIndex148
if !_rules[ruleHoleValue]() {
goto l145
}
}
l148:
goto l144
l145:
position, tokenIndex = position145, tokenIndex145
}
{
add(ruleAction18, position)
}
}
l133:
add(ruleConcatenationValue, position132)
}
goto l130
l131:
position, tokenIndex = position130, tokenIndex130
{
position152 := position
{
add(ruleAction23, position)
}
{
position154 := position
if !_rules[ruleHoleValue]() {
goto l151
}
if !_rules[ruleUnquotedParamValue]() {
goto l151
}
l155:
{
position156, tokenIndex156 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l156
}
goto l155
l156:
position, tokenIndex = position156, tokenIndex156
}
l157:
{
position158, tokenIndex158 := position, tokenIndex
{
position159, tokenIndex159 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l159
}
goto l160
l159:
position, tokenIndex = position159, tokenIndex159
}
l160:
if !_rules[ruleHoleValue]() {
goto l158
}
{
position161, tokenIndex161 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l161
}
goto l162
l161:
position, tokenIndex = position161, tokenIndex161
}
l162:
goto l157
l158:
position, tokenIndex = position158, tokenIndex158
}
add(rulePegText, position154)
}
{
add(ruleAction24, position)
}
add(ruleHoleWithSuffixValue, position152)
}
goto l130
l151:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleHoleValue]() {
goto l164
}
goto l130
l164:
position, tokenIndex = position130, tokenIndex130
{
position166 := position
{
add(ruleAction21, position)
}
{
position168 := position
{
position171, tokenIndex171 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l171
}
goto l172
l171:
position, tokenIndex = position171, tokenIndex171
}
l172:
if !_rules[ruleHoleValue]() {
goto l165
}
{
position173, tokenIndex173 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l173
}
goto l174
l173:
position, tokenIndex = position173, tokenIndex173
}
l174:
l169:
{
position170, tokenIndex170 := position, tokenIndex
{
position175, tokenIndex175 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l175
}
goto l176
l175:
position, tokenIndex = position175, tokenIndex175
}
l176:
if !_rules[ruleHoleValue]() {
goto l170
}
{
position177, tokenIndex177 := position, tokenIndex
if !_rules[ruleUnquotedParamValue]() {
goto l177
}
goto l178
l177:
position, tokenIndex = position177, tokenIndex177
}
l178:
goto l169
l170:
position, tokenIndex = position170, tokenIndex170
}
add(rulePegText, position168)
}
{
add(ruleAction22, position)
}
add(ruleHolesStringValue, position166)
}
goto l130
l165:
position, tokenIndex = position130, tokenIndex130
{
position181 := position
{
position182, tokenIndex182 := position, tokenIndex
if buffer[position] != rune('@') {
goto l183
}
position++
{
position184 := position
if !_rules[ruleUnquotedParam]() {
goto l183
}
add(rulePegText, position184)
}
goto l182
l183:
position, tokenIndex = position182, tokenIndex182
if buffer[position] != rune('@') {
goto l185
}
position++
if !_rules[ruleDoubleQuotedValue]() {
goto l185
}
goto l182
l185:
position, tokenIndex = position182, tokenIndex182
if buffer[position] != rune('@') {
goto l180
}
position++
if !_rules[ruleSingleQuotedValue]() {
goto l180
}
}
l182:
add(ruleAliasValue, position181)
}
{
add(ruleAction11, position)
}
goto l130
l180:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleDoubleQuote]() {
goto l187
}
if !_rules[ruleCustomTypedValue]() {
goto l187
}
if !_rules[ruleDoubleQuote]() {
goto l187
}
goto l130
l187:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleSingleQuote]() {
goto l188
}
if !_rules[ruleCustomTypedValue]() {
goto l188
}
if !_rules[ruleSingleQuote]() {
goto l188
}
goto l130
l188:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleCustomTypedValue]() {
goto l189
}
goto l130
l189:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleQuotedStringValue]() {
goto l190
}
goto l130
l190:
position, tokenIndex = position130, tokenIndex130
if !_rules[ruleUnquotedParamValue]() {
goto l122
}
}
l130:
add(ruleNoRefValue, position129)
}
}
l124:
add(ruleValue, position123)
}
return true
l122:
position, tokenIndex = position122, tokenIndex122
return false
},
/* 15 CustomTypedValue <- <(<IntRangeValue> Action13)> */
func() bool {
position191, tokenIndex191 := position, tokenIndex
{
position192 := position
{
position193 := position
{
position194 := position
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l191
}
position++
l195:
{
position196, tokenIndex196 := position, tokenIndex
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l196
}
position++
goto l195
l196:
position, tokenIndex = position196, tokenIndex196
}
if buffer[position] != rune('-') {
goto l191
}
position++
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l191
}
position++
l197:
{
position198, tokenIndex198 := position, tokenIndex
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l198
}
position++
goto l197
l198:
position, tokenIndex = position198, tokenIndex198
}
add(ruleIntRangeValue, position194)
}
add(rulePegText, position193)
}
{
add(ruleAction13, position)
}
add(ruleCustomTypedValue, position192)
}
return true
l191:
position, tokenIndex = position191, tokenIndex191
return false
},
/* 16 UnquotedParamValue <- <(<UnquotedParam> Action14)> */
func() bool {
position200, tokenIndex200 := position, tokenIndex
{
position201 := position
{
position202 := position
if !_rules[ruleUnquotedParam]() {
goto l200
}
add(rulePegText, position202)
}
{
add(ruleAction14, position)
}
add(ruleUnquotedParamValue, position201)
}
return true
l200:
position, tokenIndex = position200, tokenIndex200
return false
},
/* 17 UnquotedParam <- <((&('*') '*') | (&('>') '>') | (&('<') '<') | (&('@') '@') | (&('~') '~') | (&(';') ';') | (&('+') '+') | (&('/') '/') | (&(':') ':') | (&('_') '_') | (&('.') '.') | (&('-') '-') | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') [A-Z]) | (&('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z') [a-z]))+> */
func() bool {
position204, tokenIndex204 := position, tokenIndex
{
position205 := position
{
switch buffer[position] {
case '*':
if buffer[position] != rune('*') {
goto l204
}
position++
break
case '>':
if buffer[position] != rune('>') {
goto l204
}
position++
break
case '<':
if buffer[position] != rune('<') {
goto l204
}
position++
break
case '@':
if buffer[position] != rune('@') {
goto l204
}
position++
break
case '~':
if buffer[position] != rune('~') {
goto l204
}
position++
break
case ';':
if buffer[position] != rune(';') {
goto l204
}
position++
break
case '+':
if buffer[position] != rune('+') {
goto l204
}
position++
break
case '/':
if buffer[position] != rune('/') {
goto l204
}
position++
break
case ':':
if buffer[position] != rune(':') {
goto l204
}
position++
break
case '_':
if buffer[position] != rune('_') {
goto l204
}
position++
break
case '.':
if buffer[position] != rune('.') {
goto l204
}
position++
break
case '-':
if buffer[position] != rune('-') {
goto l204
}
position++
break
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l204
}
position++
break
case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z':
if c := buffer[position]; c < rune('A') || c > rune('Z') {
goto l204
}
position++
break
default:
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l204
}
position++
break
}
}
l206:
{
position207, tokenIndex207 := position, tokenIndex
{
switch buffer[position] {
case '*':
if buffer[position] != rune('*') {
goto l207
}
position++
break
case '>':
if buffer[position] != rune('>') {
goto l207
}
position++
break
case '<':
if buffer[position] != rune('<') {
goto l207
}
position++
break
case '@':
if buffer[position] != rune('@') {
goto l207
}
position++
break
case '~':
if buffer[position] != rune('~') {
goto l207
}
position++
break
case ';':
if buffer[position] != rune(';') {
goto l207
}
position++
break
case '+':
if buffer[position] != rune('+') {
goto l207
}
position++
break
case '/':
if buffer[position] != rune('/') {
goto l207
}
position++
break
case ':':
if buffer[position] != rune(':') {
goto l207
}
position++
break
case '_':
if buffer[position] != rune('_') {
goto l207
}
position++
break
case '.':
if buffer[position] != rune('.') {
goto l207
}
position++
break
case '-':
if buffer[position] != rune('-') {
goto l207
}
position++
break
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l207
}
position++
break
case 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z':
if c := buffer[position]; c < rune('A') || c > rune('Z') {
goto l207
}
position++
break
default:
if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l207
}
position++
break
}
}
goto l206
l207:
position, tokenIndex = position207, tokenIndex207
}
add(ruleUnquotedParam, position205)
}
return true
l204:
position, tokenIndex = position204, tokenIndex204
return false
},
/* 18 ConcatenationValue <- <((Action15 HoleValue (WhiteSpacing '+' WhiteSpacing (QuotedStringValue / HoleValue))+ Action16) / (Action17 QuotedStringValue (WhiteSpacing '+' WhiteSpacing (QuotedStringValue / HoleValue))+ Action18))> */
nil,
/* 19 QuotedStringValue <- <(QuotedString Action19)> */
func() bool {
position211, tokenIndex211 := position, tokenIndex
{
position212 := position
{
position213 := position
{
position214, tokenIndex214 := position, tokenIndex
if !_rules[ruleDoubleQuotedValue]() {
goto l215
}
goto l214
l215:
position, tokenIndex = position214, tokenIndex214
if !_rules[ruleSingleQuotedValue]() {
goto l211
}
}
l214:
add(ruleQuotedString, position213)
}
{
add(ruleAction19, position)
}
add(ruleQuotedStringValue, position212)
}
return true
l211:
position, tokenIndex = position211, tokenIndex211
return false
},
/* 20 QuotedString <- <(DoubleQuotedValue / SingleQuotedValue)> */
nil,
/* 21 DoubleQuotedValue <- <(DoubleQuote <(!'"' .)*> DoubleQuote)> */
func() bool {
position218, tokenIndex218 := position, tokenIndex
{
position219 := position
if !_rules[ruleDoubleQuote]() {
goto l218
}
{
position220 := position
l221:
{
position222, tokenIndex222 := position, tokenIndex
{
position223, tokenIndex223 := position, tokenIndex
if buffer[position] != rune('"') {
goto l223
}
position++
goto l222
l223:
position, tokenIndex = position223, tokenIndex223
}
if !matchDot() {
goto l222
}
goto l221
l222:
position, tokenIndex = position222, tokenIndex222
}
add(rulePegText, position220)
}
if !_rules[ruleDoubleQuote]() {
goto l218
}
add(ruleDoubleQuotedValue, position219)
}
return true
l218:
position, tokenIndex = position218, tokenIndex218
return false
},
/* 22 SingleQuotedValue <- <(SingleQuote <(!'\'' .)*> SingleQuote)> */
func() bool {
position224, tokenIndex224 := position, tokenIndex
{
position225 := position
if !_rules[ruleSingleQuote]() {
goto l224
}
{
position226 := position
l227:
{
position228, tokenIndex228 := position, tokenIndex
{
position229, tokenIndex229 := position, tokenIndex
if buffer[position] != rune('\'') {
goto l229
}
position++
goto l228
l229:
position, tokenIndex = position229, tokenIndex229
}
if !matchDot() {
goto l228
}
goto l227
l228:
position, tokenIndex = position228, tokenIndex228
}
add(rulePegText, position226)
}
if !_rules[ruleSingleQuote]() {
goto l224
}
add(ruleSingleQuotedValue, position225)
}
return true
l224:
position, tokenIndex = position224, tokenIndex224
return false
},
/* 23 IntRangeValue <- <([0-9]+ '-' [0-9]+)> */
nil,
/* 24 RefValue <- <('$' <Identifier>)> */
nil,
/* 25 AliasValue <- <(('@' <UnquotedParam>) / ('@' DoubleQuotedValue) / ('@' SingleQuotedValue))> */
nil,
/* 26 HoleValue <- <(Hole Action20)> */
func() bool {
position233, tokenIndex233 := position, tokenIndex
{
position234 := position
{
position235 := position
if buffer[position] != rune('{') {
goto l233
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l233
}
{
position236 := position
if !_rules[ruleIdentifier]() {
goto l233
}
add(rulePegText, position236)
}
if !_rules[ruleWhiteSpacing]() {
goto l233
}
if buffer[position] != rune('}') {
goto l233
}
position++
add(ruleHole, position235)
}
{
add(ruleAction20, position)
}
add(ruleHoleValue, position234)
}
return true
l233:
position, tokenIndex = position233, tokenIndex233
return false
},
/* 27 Hole <- <('{' WhiteSpacing <Identifier> WhiteSpacing '}')> */
nil,
/* 28 HolesStringValue <- <(Action21 <(UnquotedParamValue? HoleValue UnquotedParamValue?)+> Action22)> */
nil,
/* 29 HoleWithSuffixValue <- <(Action23 <(HoleValue UnquotedParamValue+ (UnquotedParamValue? HoleValue UnquotedParamValue?)*)> Action24)> */
nil,
/* 30 Comment <- <(('#' (!EndOfLine .)*) / ('/' '/' (!EndOfLine .)*))> */
nil,
/* 31 SingleQuote <- <'\''> */
func() bool {
position242, tokenIndex242 := position, tokenIndex
{
position243 := position
if buffer[position] != rune('\'') {
goto l242
}
position++
add(ruleSingleQuote, position243)
}
return true
l242:
position, tokenIndex = position242, tokenIndex242
return false
},
/* 32 DoubleQuote <- <'"'> */
func() bool {
position244, tokenIndex244 := position, tokenIndex
{
position245 := position
if buffer[position] != rune('"') {
goto l244
}
position++
add(ruleDoubleQuote, position245)
}
return true
l244:
position, tokenIndex = position244, tokenIndex244
return false
},
/* 33 WhiteSpacing <- <Whitespace*> */
func() bool {
{
position247 := position
l248:
{
position249, tokenIndex249 := position, tokenIndex
if !_rules[ruleWhitespace]() {
goto l249
}
goto l248
l249:
position, tokenIndex = position249, tokenIndex249
}
add(ruleWhiteSpacing, position247)
}
return true
},
/* 34 MustWhiteSpacing <- <Whitespace+> */
func() bool {
position250, tokenIndex250 := position, tokenIndex
{
position251 := position
if !_rules[ruleWhitespace]() {
goto l250
}
l252:
{
position253, tokenIndex253 := position, tokenIndex
if !_rules[ruleWhitespace]() {
goto l253
}
goto l252
l253:
position, tokenIndex = position253, tokenIndex253
}
add(ruleMustWhiteSpacing, position251)
}
return true
l250:
position, tokenIndex = position250, tokenIndex250
return false
},
/* 35 Equal <- <(WhiteSpacing '=' WhiteSpacing)> */
func() bool {
position254, tokenIndex254 := position, tokenIndex
{
position255 := position
if !_rules[ruleWhiteSpacing]() {
goto l254
}
if buffer[position] != rune('=') {
goto l254
}
position++
if !_rules[ruleWhiteSpacing]() {
goto l254
}
add(ruleEqual, position255)
}
return true
l254:
position, tokenIndex = position254, tokenIndex254
return false
},
/* 36 BlankLine <- <(WhiteSpacing EndOfLine)> */
func() bool {
position256, tokenIndex256 := position, tokenIndex
{
position257 := position
if !_rules[ruleWhiteSpacing]() {
goto l256
}
if !_rules[ruleEndOfLine]() {
goto l256
}
add(ruleBlankLine, position257)
}
return true
l256:
position, tokenIndex = position256, tokenIndex256
return false
},
/* 37 Whitespace <- <(' ' / '\t')> */
func() bool {
position258, tokenIndex258 := position, tokenIndex
{
position259 := position
{
position260, tokenIndex260 := position, tokenIndex
if buffer[position] != rune(' ') {
goto l261
}
position++
goto l260
l261:
position, tokenIndex = position260, tokenIndex260
if buffer[position] != rune('\t') {
goto l258
}
position++
}
l260:
add(ruleWhitespace, position259)
}
return true
l258:
position, tokenIndex = position258, tokenIndex258
return false
},
/* 38 EndOfLine <- <(('\r' '\n') / '\n' / '\r')> */
func() bool {
position262, tokenIndex262 := position, tokenIndex
{
position263 := position
{
position264, tokenIndex264 := position, tokenIndex
if buffer[position] != rune('\r') {
goto l265
}
position++
if buffer[position] != rune('\n') {
goto l265
}
position++
goto l264
l265:
position, tokenIndex = position264, tokenIndex264
if buffer[position] != rune('\n') {
goto l266
}
position++
goto l264
l266:
position, tokenIndex = position264, tokenIndex264
if buffer[position] != rune('\r') {
goto l262
}
position++
}
l264:
add(ruleEndOfLine, position263)
}
return true
l262:
position, tokenIndex = position262, tokenIndex262
return false
},
/* 39 EndOfFile <- <!.> */
nil,
/* 41 Action0 <- <{ p.NewStatement() }> */
nil,
/* 42 Action1 <- <{ p.StatementDone() }> */
nil,
nil,
/* 44 Action2 <- <{ p.addDeclarationIdentifier(text) }> */
nil,
/* 45 Action3 <- <{ p.addValue() }> */
nil,
/* 46 Action4 <- <{ p.addAction(text) }> */
nil,
/* 47 Action5 <- <{ p.addEntity(text) }> */
nil,
/* 48 Action6 <- <{ p.addParamKey(text) }> */
nil,
/* 49 Action7 <- <{ p.addFirstValueInList() }> */
nil,
/* 50 Action8 <- <{ p.lastValueInList() }> */
nil,
/* 51 Action9 <- <{ p.addFirstValueInList() }> */
nil,
/* 52 Action10 <- <{ p.lastValueInList() }> */
nil,
/* 53 Action11 <- <{ p.addAliasParam(text) }> */
nil,
/* 54 Action12 <- <{ p.addParamRefValue(text) }> */
nil,
/* 55 Action13 <- <{ p.addParamValue(text) }> */
nil,
/* 56 Action14 <- <{ p.addParamValue(text) }> */
nil,
/* 57 Action15 <- <{ p.addFirstValueInConcatenation() }> */
nil,
/* 58 Action16 <- <{ p.lastValueInConcatenation() }> */
nil,
/* 59 Action17 <- <{ p.addFirstValueInConcatenation() }> */
nil,
/* 60 Action18 <- <{ p.lastValueInConcatenation() }> */
nil,
/* 61 Action19 <- <{ p.addStringValue(text) }> */
nil,
/* 62 Action20 <- <{ p.addParamHoleValue(text) }> */
nil,
/* 63 Action21 <- <{ p.addFirstValueInConcatenation() }> */
nil,
/* 64 Action22 <- <{ p.lastValueInConcatenation() }> */
nil,
/* 65 Action23 <- <{ p.addFirstValueInConcatenation() }> */
nil,
/* 66 Action24 <- <{ p.lastValueInConcatenation() }> */
nil,
}
p.rules = _rules
}
package ast
import (
"fmt"
"strconv"
)
type statementBuilder struct {
action string
entity string
declarationIdentifier string
isValue bool
newparams map[string]interface{}
currentKey string
currentNode interface{}
listBuilder *listValueBuilder
concatenationBuilder *concatenationValueBuilder
}
func (b *statementBuilder) build() *Statement {
if b.action == "" && b.entity == "" && b.declarationIdentifier == "" && !b.isValue {
return nil
}
var expr ExpressionNode
if b.isValue {
expr = &RightExpressionNode{i: b.currentNode}
} else {
if b.newparams == nil {
b.newparams = make(map[string]interface{})
}
expr = &CommandNode{
Action: b.action,
Entity: b.entity,
ParamNodes: b.newparams,
Refs: make(map[string]interface{}),
}
}
if b.declarationIdentifier != "" {
decl := &DeclarationNode{Ident: b.declarationIdentifier, Expr: expr}
return &Statement{Node: decl}
}
return &Statement{Node: expr}
}
func (b *statementBuilder) addParamKey(key string) *statementBuilder {
b.currentKey = key
return b
}
func (b *statementBuilder) addParamValue(node interface{}) *statementBuilder {
if b.newparams == nil {
b.newparams = make(map[string]interface{})
}
b.currentNode = node
if b.concatenationBuilder != nil {
b.concatenationBuilder.add(node)
b.currentNode = nil
} else if b.listBuilder != nil {
b.listBuilder.add(node)
b.currentNode = nil
} else {
if b.currentKey != "" {
b.newparams[b.currentKey] = node
b.currentKey = ""
b.currentNode = nil
}
}
return b
}
func (b *statementBuilder) newList() *statementBuilder {
b.listBuilder = &listValueBuilder{}
return b
}
func (b *statementBuilder) buildList() *statementBuilder {
if b.listBuilder != nil {
node := b.listBuilder.build()
b.listBuilder = nil
b.addParamValue(node)
}
return b
}
func (a *AST) addAction(text string) {
if IsInvalidAction(text) {
panic(fmt.Errorf("unknown action '%s'", text))
}
a.stmtBuilder.action = text
}
func (a *AST) addEntity(text string) {
if IsInvalidEntity(text) {
panic(fmt.Errorf("unknown entity '%s'", text))
}
a.stmtBuilder.entity = text
}
func (a *AST) addValue() {
a.stmtBuilder.isValue = true
}
func (a *AST) addDeclarationIdentifier(text string) {
a.stmtBuilder.declarationIdentifier = text
}
func (a *AST) NewStatement() {
a.stmtBuilder = &statementBuilder{}
}
func (a *AST) StatementDone() {
if stmt := a.stmtBuilder.build(); stmt != nil {
a.Statements = append(a.Statements, stmt)
}
a.stmtBuilder = nil
}
func (a *AST) addParamKey(text string) {
a.stmtBuilder.addParamKey(text)
}
func (a *AST) addParamValue(text string) {
var val interface{}
i, err := strconv.Atoi(text)
if err == nil {
if len(text) > 1 && text[0] == '0' {
// We want an integer beginning with '0' to keep its initial '0' (so as string)
val = text
} else {
val = i
}
} else {
f, err := strconv.ParseFloat(text, 64)
if err == nil {
val = f
} else {
val = text
}
}
a.stmtBuilder.addParamValue(InterfaceNode{i: val})
}
func (a *AST) addFirstValueInList() {
a.stmtBuilder.newList()
}
func (a *AST) lastValueInList() {
a.stmtBuilder.buildList()
}
func (a *AST) addFirstValueInConcatenation() {
a.stmtBuilder.concatenationBuilder = &concatenationValueBuilder{}
}
func (a *AST) lastValueInConcatenation() {
if a.stmtBuilder.concatenationBuilder != nil {
node := a.stmtBuilder.concatenationBuilder.build()
a.stmtBuilder.concatenationBuilder = nil
a.stmtBuilder.addParamValue(node)
}
}
func (a *AST) addStringValue(text string) {
a.stmtBuilder.addParamValue(InterfaceNode{i: text})
}
func (a *AST) addParamRefValue(text string) {
a.stmtBuilder.addParamValue(RefNode{key: text})
}
func (a *AST) addParamHoleValue(text string) {
a.stmtBuilder.addParamValue(HoleNode{key: text})
}
func (a *AST) addAliasParam(text string) {
a.stmtBuilder.addParamValue(AliasNode{key: text})
}
type listValueBuilder struct {
elements []interface{}
}
func (c *listValueBuilder) add(node interface{}) *listValueBuilder {
c.elements = append(c.elements, node)
return c
}
func (c *listValueBuilder) build() ListNode {
node := ListNode{arr: c.elements}
return node
}
type concatenationValueBuilder struct {
elements []interface{}
}
func (c *concatenationValueBuilder) add(node interface{}) *concatenationValueBuilder {
c.elements = append(c.elements, node)
return c
}
func (c *concatenationValueBuilder) build() ConcatenationNode {
node := ConcatenationNode{arr: c.elements}
return node
}
package ast
type Entity string
var entities = map[Entity]struct{}{
"none": {},
"accesskey": {},
"alarm": {},
"appscalingtarget": {},
"appscalingpolicy": {},
"scalinggroup": {},
"bucket": {},
"certificate": {},
"classicloadbalancer": {},
"container": {},
"containercluster": {},
"containerservice": {},
"containertask": {},
"database": {},
"distribution": {},
"dbsubnetgroup": {},
"elasticip": {},
"function": {},
"group": {},
"instance": {},
"image": {},
"internetgateway": {},
"mfadevice": {},
"natgateway": {},
"networkinterface": {},
"instanceprofile": {},
"keypair": {},
"launchconfiguration": {},
"listener": {},
"loadbalancer": {},
"loginprofile": {},
"policy": {},
"queue": {},
"record": {},
"registry": {},
"repository": {},
"role": {},
"route": {},
"routetable": {},
"s3object": {},
"scalingpolicy": {},
"securitygroup": {},
"snapshot": {},
"stack": {},
"subnet": {},
"subscription": {},
"tag": {},
"targetgroup": {},
"topic": {},
"user": {},
"volume": {},
"vpc": {},
"zone": {},
}
func IsInvalidEntity(s string) bool {
_, ok := entities[Entity(s)]
return !ok
}
package ast
import (
"errors"
"fmt"
"strings"
)
var (
_ ExpressionNode = (*RightExpressionNode)(nil)
_ Node = (*HoleNode)(nil)
_ Node = (*AliasNode)(nil)
_ Node = (*RefNode)(nil)
_ Node = (*ConcatenationNode)(nil)
_ Node = (*ListNode)(nil)
_ Node = (*InterfaceNode)(nil)
)
type RightExpressionNode struct {
i interface{}
}
func (n *RightExpressionNode) Node() interface{} {
return n.i
}
func (n *RightExpressionNode) Result() interface{} {
switch v := n.i.(type) {
case InterfaceNode:
return v.i
case RefNode, AliasNode, HoleNode:
return nil
case ListNode:
var arr []interface{}
for _, e := range v.arr {
switch ev := e.(type) {
case InterfaceNode:
arr = append(arr, ev.i)
case RefNode, AliasNode, HoleNode:
return nil
default:
arr = append(arr, ev)
}
}
return arr
case ConcatenationNode:
return v.Concat()
default:
return n.i
}
}
func (n *RightExpressionNode) Err() error {
switch n.i.(type) {
case InterfaceNode:
return nil
default:
return errors.New("right expr node is not an interface node")
}
}
func (n *RightExpressionNode) String() string {
return fmt.Sprint(n.i)
}
func (n *RightExpressionNode) clone() Node {
return &RightExpressionNode{
i: n.i,
}
}
type CommandNode struct {
Command
CmdResult interface{}
CmdErr error
Action, Entity string
ParamNodes map[string]interface{}
Refs map[string]interface{}
}
type RefNode struct {
key string
}
func NewRefNode(s string) RefNode {
return RefNode{key: s}
}
func (n RefNode) Ref() string {
return n.key
}
func (n RefNode) clone() Node {
return n
}
func (n RefNode) String() string {
return "$" + n.key
}
type AliasNode struct {
key string
}
func NewAliasNode(s string) AliasNode {
return AliasNode{key: s}
}
func (n AliasNode) clone() Node {
return n
}
func (n AliasNode) Alias() string {
return n.key
}
func (n AliasNode) String() string {
return "@" + n.key
}
type HoleNode struct {
key string
optional bool
}
func NewHoleNode(s string) HoleNode {
return HoleNode{key: s}
}
func NewOptionalHoleNode(s string) HoleNode {
return HoleNode{key: s, optional: true}
}
func (n HoleNode) IsOptional() bool {
return n.optional
}
func (n HoleNode) Hole() string {
return n.key
}
func (n HoleNode) String() string {
return "{" + n.key + "}"
}
func (n HoleNode) clone() Node {
return n
}
type ListNode struct {
arr []interface{}
}
func NewListNode(arr []interface{}) ListNode {
return ListNode{arr: arr}
}
func (n ListNode) String() string {
var a []string
for _, e := range n.arr {
a = append(a, fmt.Sprint(e))
}
return "[" + strings.Join(a, ",") + "]"
}
func (n ListNode) Elems() []interface{} {
return n.arr
}
func (n ListNode) clone() Node {
return n
}
type ConcatenationNode struct {
arr []interface{}
}
func NewConcatenationNode(arr []interface{}) ConcatenationNode {
return ConcatenationNode{arr: arr}
}
func (n ConcatenationNode) Concat() string {
var arr []string
for _, e := range n.arr {
switch ee := e.(type) {
case InterfaceNode:
arr = append(arr, fmt.Sprint(ee.i))
default:
arr = append(arr, fmt.Sprint(ee))
}
}
return strings.Join(arr, "")
}
func (n ConcatenationNode) String() string {
var hasUnresolvedHole bool
var elems []string
for _, val := range n.arr {
if _, has := val.(HoleNode); has {
hasUnresolvedHole = true
break
}
}
for _, val := range n.arr {
switch node := val.(type) {
case InterfaceNode:
if str, isStr := node.i.(string); isStr {
if hasUnresolvedHole {
elems = append(elems, Quote(str))
} else {
elems = append(elems, str)
}
} else {
panic(fmt.Sprintf("concatenation node expects only strings and holes: got %T", node.i))
}
default:
elems = append(elems, fmt.Sprint(val))
}
}
if hasUnresolvedHole {
return strings.Join(elems, "+")
} else {
return quoteStringIfNeeded(strings.Join(elems, ""))
}
}
func (n ConcatenationNode) clone() Node {
return n
}
type InterfaceNode struct {
i interface{}
}
func (n InterfaceNode) Value() interface{} {
return n.i
}
func (n InterfaceNode) String() string {
switch v := n.i.(type) {
case []string:
return "[" + strings.Join(v, ",") + "]"
case string:
return quoteStringIfNeeded(v)
default:
return fmt.Sprint(v)
}
}
func (n InterfaceNode) clone() Node {
return n
}
package ast
import (
"regexp"
"strconv"
"strings"
)
var SimpleStringValue = regexp.MustCompile("^[a-zA-Z0-9-._:/+;~@<>*]+$") // in sync with [a-zA-Z0-9-._:/+;~@<>]+ in PEG (with ^ and $ around)
func quoteStringIfNeeded(input string) string {
if _, err := strconv.Atoi(input); err == nil {
return "'" + input + "'"
}
if _, err := strconv.ParseFloat(input, 64); err == nil {
return "'" + input + "'"
}
if SimpleStringValue.MatchString(input) {
return input
} else {
return Quote(input)
}
}
func Quote(str string) string {
if strings.ContainsRune(str, '\'') {
return "\"" + str + "\""
} else {
return "'" + str + "'"
}
}
func isQuoted(str string) bool {
if len(str) < 2 {
return false
}
if str[0] == '\'' && str[len(str)-1] == '\'' {
return true
}
if str[0] == '"' && str[len(str)-1] == '"' {
return true
}
return false
}
package ast
import (
"errors"
"fmt"
"strings"
)
func VerifyRefs(tree Node) error {
var errs []string
addErr := func(err string) {
errs = append(errs, err)
}
v := newVisitor()
v.onRefs = func(parent interface{}, node RefNode) {
if !contains(v.declaredVariables, node.key) {
addErr(fmt.Sprintf("using reference '$%s' but '%[1]s' is undefined in template", node.key))
}
}
v.visit(tree)
for i, declared := range v.declaredVariables {
if contains(v.declaredVariables[:i], declared) {
addErr(fmt.Sprintf("using reference '$%s' but '%[1]s' has already been assigned in template", declared))
}
}
if len(errs) > 0 {
return errors.New(strings.Join(errs, "; "))
}
return nil
}
func ProcessRefs(tree Node, fillers map[string]interface{}) {
v := newVisitor()
v.onRefs = func(parent interface{}, node RefNode) {
var done bool
var val interface{}
for k, v := range fillers {
if k == node.key {
done = true
val = v
}
}
if done {
switch p := parent.(type) {
case ListNode:
p.arr[v.listIndex] = val
case *CommandNode:
p.ParamNodes[v.key] = val
case *RightExpressionNode:
p.i = val
}
}
}
v.visit(tree)
}
func RemoveOptionalHoles(tree Node) {
v := newVisitor()
v.onHoles = func(parent interface{}, node HoleNode) {
if node.IsOptional() {
switch p := parent.(type) {
case ListNode:
p.arr = append(p.arr[:v.listIndex], p.arr[v.listIndex+1:]...)
case *CommandNode:
delete(p.ParamNodes, v.key)
case *RightExpressionNode:
p.i = nil
}
}
}
v.visit(tree)
}
func CollectUniqueHoles(tree Node) map[HoleNode][]string {
uniqueHoles := make(map[HoleNode][]string)
v := newVisitor()
v.onHoles = func(parent interface{}, node HoleNode) {
if _, ok := uniqueHoles[node]; !ok {
uniqueHoles[node] = []string{}
}
if v.action != "" && v.entity != "" && v.key != "" {
paramPath := fmt.Sprintf("%s.%s.%s", v.action, v.entity, v.key)
if !contains(uniqueHoles[node], paramPath) {
uniqueHoles[node] = append(uniqueHoles[node], paramPath)
}
}
}
v.visit(tree)
return uniqueHoles
}
func CollectHoles(tree Node) (holes []HoleNode) {
v := newVisitor()
v.onHoles = func(parent interface{}, node HoleNode) {
holes = append(holes, node)
}
v.visit(tree)
return
}
func ProcessHoles(tree Node, fillers map[string]interface{}) map[string]interface{} {
processed := make(map[string]interface{})
v := newVisitor()
v.onHoles = func(parent interface{}, node HoleNode) {
var done bool
var val interface{}
for k, v := range fillers {
if k == node.key {
done = true
val = v
switch vv := v.(type) {
case AliasNode, RefNode, HoleNode, ConcatenationNode:
processed[k] = fmt.Sprint(v)
case ListNode:
var arr []interface{}
for _, a := range vv.arr {
switch e := a.(type) {
case AliasNode, RefNode, HoleNode:
arr = append(arr, fmt.Sprint(e))
default:
arr = append(arr, e)
}
}
processed[k] = arr
default:
processed[k] = v
}
}
}
if done {
switch p := parent.(type) {
case ConcatenationNode:
p.arr[v.concatItemIndex] = val
case ListNode:
p.arr[v.listIndex] = val
case *CommandNode:
p.ParamNodes[v.key] = val
case *RightExpressionNode:
p.i = val
}
}
}
v.visit(tree)
return processed
}
func CollectAliases(tree Node) (aliases []AliasNode) {
v := newVisitor()
v.onAliases = func(parent interface{}, node AliasNode) {
aliases = append(aliases, node)
}
v.visit(tree)
return
}
func ProcessAliases(tree Node, aliasFunc func(action, entity string, key string) func(string) (string, bool)) {
v := newVisitor()
v.onAliases = func(parent interface{}, node AliasNode) {
if resolv, hasResolv := aliasFunc(v.action, v.entity, v.key)(node.key); hasResolv {
switch p := parent.(type) {
case ListNode:
p.arr[v.listIndex] = resolv
case ConcatenationNode:
p.arr[v.concatItemIndex] = resolv
case *CommandNode:
p.ParamNodes[v.key] = resolv
case *RightExpressionNode:
p.i = resolv
}
}
}
v.visit(tree)
}
type visitor struct {
onRefs func(parent interface{}, n RefNode)
onAliases func(parent interface{}, n AliasNode)
onHoles func(parent interface{}, n HoleNode)
parent Node
declaredVariables []string
action, entity, key string
listIndex, concatItemIndex int
}
func newVisitor() *visitor {
return &visitor{
onRefs: func(interface{}, RefNode) {},
onAliases: func(interface{}, AliasNode) {},
onHoles: func(interface{}, HoleNode) {},
}
}
func (v *visitor) visit(tree Node) {
switch t := tree.(type) {
case InterfaceNode:
return
case HoleNode:
v.onHoles(v.parent, t)
return
case AliasNode:
v.onAliases(v.parent, t)
return
case RefNode:
v.onRefs(v.parent, t)
return
}
switch t := tree.(type) {
case *AST:
v.parent = tree
for _, st := range t.Statements {
v.visit(st)
}
case *Statement:
v.parent = tree
v.visit(t.Node)
case *CommandNode:
v.action, v.entity = t.Action, t.Entity
for key, param := range t.ParamNodes {
if n, ok := param.(Node); ok {
v.parent = tree
v.key = key
v.visit(n)
}
}
case *DeclarationNode:
v.key = t.Ident
v.parent = tree
v.visit(t.Expr)
v.declaredVariables = append(v.declaredVariables, t.Ident)
case *RightExpressionNode:
if n, ok := t.i.(Node); ok {
v.parent = tree
v.visit(n)
}
case ListNode:
for i, el := range t.arr {
if n, ok := el.(Node); ok {
v.listIndex = i
v.parent = tree
v.visit(n)
}
}
case ConcatenationNode:
for i, el := range t.arr {
if n, ok := el.(Node); ok {
v.concatItemIndex = i
v.parent = tree
v.visit(n)
}
}
default:
panic(fmt.Sprintf("unsupported AST type %T", t))
}
}
func contains(arr []string, s string) bool {
for _, v := range arr {
if v == s {
return true
}
}
return false
}
package template
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/oklog/ulid"
"github.com/wallix/awless/template/internal/ast"
)
// Allow template executions serialization with context for JSON storage
// without altering the template.Template model
type TemplateExecution struct {
*Template
Author, Source, Locale string
Profile, Path, Message string
Fillers map[string]interface{}
}
// Date extract the date from the ulid template identifier
func (t *TemplateExecution) Date() time.Time {
parsed, err := ulid.Parse(t.ID)
if err != nil {
panic(err)
}
return time.Unix(int64(parsed.Time())/int64(1000), time.Nanosecond.Nanoseconds())
}
func (t *TemplateExecution) IsOneLiner() bool {
var count int
for range t.CommandNodesIterator() {
count++
}
return count == 1
}
const maxMsgLen = 140
// SetMessage set the value of Message, truncating it if exceeds max len
func (t *TemplateExecution) SetMessage(s string) {
out := strings.TrimSpace(s)
if len(out) > maxMsgLen {
out = out[:maxMsgLen-3] + "..."
}
t.Message = out
}
func (t *TemplateExecution) MarshalJSON() ([]byte, error) {
out := &toJSON{}
out.ID = t.ID
out.Author = t.Author
out.Source = t.Source
out.Locale = t.Locale
out.Profile = t.Profile
out.Message = t.Message
out.Path = t.Path
out.Fillers = t.Fillers
if out.Fillers == nil {
out.Fillers = make(map[string]interface{}, 0) // friendlier for json, avoiding "fillers": null,
}
out.Commands = []command{}
for _, cmd := range t.CommandNodesIterator() {
newCmd := command{}
newCmd.Line = cmd.String()
if cmd.CmdErr != nil {
newCmd.Errors = append(newCmd.Errors, cmd.CmdErr.Error())
}
if cmd.CmdResult != nil {
if s, ok := cmd.CmdResult.(string); ok {
newCmd.Results = append(newCmd.Results, s)
}
}
out.Commands = append(out.Commands, newCmd)
}
return json.MarshalIndent(out, "", " ")
}
func (t *TemplateExecution) UnmarshalJSON(b []byte) error {
if t == nil {
t = new(TemplateExecution)
}
if t.Template == nil {
t.Template = new(Template)
}
var v toJSON
if err := json.Unmarshal(b, &v); err != nil {
return err
}
t.Source = v.Source
t.Locale = v.Locale
t.Profile = v.Profile
t.Message = v.Message
t.Path = v.Path
t.Author = v.Author
t.Fillers = v.Fillers
tpl := &Template{ID: v.ID, AST: &ast.AST{
Statements: make([]*ast.Statement, 0),
}}
for _, c := range v.Commands {
node, err := parseStatement(c.Line)
if err != nil {
return err
}
switch n := node.(type) {
case *ast.CommandNode:
if len(c.Results) > 0 {
n.CmdResult = c.Results[0]
}
if len(c.Errors) > 0 {
n.CmdErr = errors.New(c.Errors[0])
}
tpl.Statements = append(tpl.Statements, &ast.Statement{Node: n})
}
}
*(t.Template) = *tpl
return nil
}
type TemplateExecutionStats struct {
KOCount, OKCount, CmdCount int
ActionEntityCount map[string]int
Oneliner string
}
func (te *TemplateExecutionStats) AllKO() bool {
return te.KOCount == te.CmdCount
}
func (t *TemplateExecution) Stats() *TemplateExecutionStats {
stats := &TemplateExecutionStats{ActionEntityCount: make(map[string]int)}
var actionentity string
for _, cmd := range t.CommandNodesIterator() {
actionentity = fmt.Sprintf("%s %s", cmd.Action, cmd.Entity)
if cmd.Err() != nil {
stats.KOCount++
} else {
stats.OKCount++
}
stats.CmdCount++
stats.ActionEntityCount[actionentity]++
}
if stats.CmdCount == 1 {
stats.Oneliner = actionentity
}
return stats
}
type toJSON struct {
ID string `json:"id"`
Author string `json:"author,omitempty"`
Source string `json:"source"`
Locale string `json:"locale"`
Profile string `json:"profile,omitempty"`
Message string `json:"message,omitempty"`
Path string `json:"path,omitempty"`
Fillers map[string]interface{} `json:"fillers"`
Commands []command `json:"commands"`
}
type command struct {
Line string `json:"line"`
Errors []string `json:"errors,omitempty"`
Results []string `json:"results,omitempty"`
}
package params
type Reducer interface {
Keys() []string
Reduce(map[string]interface{}) (map[string]interface{}, error)
}
type reduceFunc func(map[string]interface{}) (map[string]interface{}, error)
func newReducer(fn reduceFunc, keys ...string) Reducer {
return &reducer{reduce: fn, keys: keys}
}
type reducer struct {
keys []string
reduce reduceFunc
}
func (r *reducer) Keys() []string {
return r.keys
}
func (r *reducer) Reduce(all map[string]interface{}) (map[string]interface{}, error) {
in := make(map[string]interface{})
for _, k := range r.keys {
if v, ok := all[k]; ok {
in[k] = v
}
}
return r.reduce(in)
}
package params
import (
"errors"
"fmt"
"sort"
"strings"
)
type Rule interface {
Visit(func(Rule))
Run(input []string) error
Required() []string
Missing(input []string) []string
String() string
}
func List(r Rule) (required []string, optionals []string, suggested []string) {
return collect(r)
}
func Run(r Rule, input []string) error {
if err := unexpectedParam(r, input); err != nil {
return err
}
return r.Run(input)
}
func unexpectedParam(r Rule, input []string) (err error) {
var unex []string
params, opts, _ := collect(r)
all := append(params, opts...)
for _, s := range input {
if !contains(all, s) {
unex = append(unex, s)
}
}
if len(unex) > 0 {
err = fmt.Errorf("unexpected param(s): %s", strings.Join(unex, ", "))
}
return
}
func collect(r Rule) (out []string, opts []string, suggs []string) {
r.Visit(func(r Rule) {
switch v := r.(type) {
case key:
out = append(out, v.String())
case opt:
opts = append(opts, v.keys()...)
for _, p := range v.suggested {
suggs = append(suggs, string(p))
}
}
})
sort.Strings(out)
sort.Strings(opts)
sort.Strings(suggs)
return
}
type allOf struct {
defaultRule
}
func AllOf(rules ...Rule) Rule {
return allOf{build(rules)}
}
func (n allOf) Run(input []string) (err error) {
for _, r := range n.rules {
err = r.Run(input)
if err != optErr && err != nil {
return fmt.Errorf("%s: expecting %s", err, n.String())
}
}
return nil
}
func (n allOf) Missing(input []string) (miss []string) {
for _, r := range n.rules {
miss = append(miss, r.Missing(input)...)
}
return
}
func (n allOf) Required() (all []string) {
for _, r := range n.rules {
all = append(all, r.Required()...)
}
return
}
func (n allOf) String() string {
return n.rules.join(" + ")
}
type onlyOneOf struct {
defaultRule
}
func OnlyOneOf(rules ...Rule) Rule {
return onlyOneOf{build(rules)}
}
func (n onlyOneOf) Run(input []string) error {
if len(n.rules) == 0 {
return nil
}
var pass int
for _, r := range n.rules {
if err := r.Run(input); err == nil {
pass++
}
}
if pass != 1 {
return fmt.Errorf("only %s", n.rules)
}
return nil
}
func (n onlyOneOf) Missing(input []string) (miss []string) {
if err := n.Run(input); err != nil && len(n.rules) > 0 {
miss = append(miss, n.rules[0].Missing(input)...)
}
return
}
func (n onlyOneOf) Required() (all []string) {
if len(n.rules) > 0 {
all = append(all, n.rules[0].Required()...)
}
return
}
func (n onlyOneOf) String() string {
return "(" + n.rules.join(" | ") + ")"
}
type atLeastOneOf struct {
defaultRule
}
func AtLeastOneOf(rules ...Rule) Rule {
return atLeastOneOf{build(rules)}
}
func (n atLeastOneOf) Run(input []string) error {
if len(n.rules) == 0 {
return nil
}
var pass int
for _, r := range n.rules {
if err := r.Run(input); err == nil || err == optErr {
pass++
}
}
if pass < 1 {
return fmt.Errorf("at least one of %v", n.rules)
}
return nil
}
func (n atLeastOneOf) Missing(input []string) (miss []string) {
if err := n.Run(input); err != nil && len(n.rules) > 0 {
miss = append(miss, n.rules[0].Missing(input)...)
}
return
}
func (n atLeastOneOf) Required() (all []string) {
if len(n.rules) > 0 {
all = append(all, n.rules[0].Required()...)
}
return
}
func (n atLeastOneOf) String() string {
return "(" + n.rules.join(" / ") + ")"
}
type opt struct {
optionals []string
suggested []suggested
}
func Opt(i ...interface{}) Rule {
o := opt{}
for _, v := range i {
switch vv := v.(type) {
case string:
o.optionals = append(o.optionals, vv)
case []suggested:
o.suggested = append(o.suggested, vv...)
default:
panic("invalid type for optional param")
}
}
return o
}
func Suggested(s ...string) (sugs []suggested) {
for _, v := range s {
sugs = append(sugs, suggested(v))
}
return
}
var optErr = errors.New("opt err")
func (n opt) Visit(fn func(r Rule)) {
fn(n)
}
func (n opt) Run(input []string) error {
return optErr
}
func (n opt) Missing(input []string) (miss []string) {
return
}
func (n opt) Required() []string {
return []string{}
}
func (n opt) String() string {
return "[" + strings.Join(n.keys(), " ") + "]"
}
func (n opt) keys() (keys []string) {
keys = append(keys, n.optionals...)
for _, k := range n.suggested {
keys = append(keys, string(k))
}
return
}
func Key(k string) Rule {
return key(k)
}
type key string
type suggested key
func (n key) Visit(fn func(r Rule)) {
fn(n)
}
func (n key) Run(input []string) error {
if !contains(input, string(n)) {
return errors.New(string(n))
}
return nil
}
func (n key) Missing(input []string) (miss []string) {
if !contains(input, string(n)) {
miss = append(miss, string(n))
}
return
}
func (n key) Required() []string {
return []string{string(n)}
}
func (n key) String() string {
return string(n)
}
type none struct{}
func None() Rule {
return none{}
}
func (n none) Visit(func(Rule)) {}
func (n none) Run(input []string) error { return nil }
func (n none) Required() []string { return []string{} }
func (n none) Missing([]string) []string { return []string{} }
func (n none) String() string { return "none" }
func build(rules []Rule) (d defaultRule) {
for _, n := range rules {
d.rules = append(d.rules, n)
}
return
}
type defaultRule struct {
rules rules
}
type rules []Rule
func (rs rules) join(sep string) string {
var arr []string
for _, r := range rs {
arr = append(arr, fmt.Sprint(r))
}
return strings.Join(arr, sep)
}
func (r defaultRule) Visit(fn func(r Rule)) {
for _, n := range r.rules {
n.Visit(fn)
}
}
func contains(arr []string, s string) bool {
for _, a := range arr {
if s == a {
return true
}
}
return false
}
package params
type Spec interface {
Rule() Rule
Reducers() []Reducer
Validators() Validators
}
func NewSpec(r Rule, vs ...Validators) Spec {
return newSpec(r, vs...)
}
func SpecBuilder(r Rule, vs ...Validators) *specBuilder {
b := &specBuilder{newSpec(r, vs...)}
b.s.reds = make([]Reducer, 0)
return b
}
type specBuilder struct {
s *spec
}
func (b *specBuilder) AddReducer(r reduceFunc, keys ...string) *specBuilder {
b.s.reds = append(b.s.reds, newReducer(r, keys...))
return b
}
func (b *specBuilder) Done() Spec {
return b.s
}
func newSpec(r Rule, vs ...Validators) *spec {
if len(vs) > 0 {
return &spec{r: r, v: vs[0]}
}
return &spec{r: r}
}
type spec struct {
r Rule
reds []Reducer
v Validators
}
func (s *spec) Rule() Rule {
if s.r == nil {
return None()
}
return s.r
}
func (s *spec) Validators() Validators {
if s.v == nil {
return Validators(make(map[string]validatorFunc, 0))
}
return s.v
}
func (s *spec) Reducers() []Reducer {
return s.reds
}
package params
import (
"bytes"
"errors"
"fmt"
"net"
"os"
"strings"
)
func Validate(all Validators, paramValues map[string]interface{}) error {
msg := bytes.NewBufferString("param validation:")
var hasErr bool
for key, vFn := range all {
if val, ok := paramValues[key]; ok {
if err := vFn(val, paramValues); err != nil {
hasErr = true
msg.WriteString(fmt.Sprintf("\n\t\t- param '%s': %s", key, err))
}
}
}
if hasErr {
return errors.New(msg.String())
}
return nil
}
type validatorFunc func(val interface{}, others map[string]interface{}) error
type Validators map[string]validatorFunc
func IsInEnumIgnoreCase(items ...string) validatorFunc {
included := func(arr []string, s string) bool {
for _, a := range arr {
if strings.EqualFold(s, a) {
return true
}
}
return false
}
return func(i interface{}, others map[string]interface{}) error {
s, err := toString(i)
if err != nil {
return err
}
if !included(items, s) {
return fmt.Errorf("expected any of %s but got '%s'", items, s)
}
return nil
}
}
func MaxLengthOf(l int) validatorFunc {
return func(i interface{}, others map[string]interface{}) error {
s, err := toString(i)
if err != nil {
return err
}
if actual := len(s); actual > l {
return fmt.Errorf("expected max length of %d but got %d", l, actual)
}
return nil
}
}
func MinLengthOf(l int) validatorFunc {
return func(i interface{}, others map[string]interface{}) error {
s, err := toString(i)
if err != nil {
return err
}
if actual := len(s); actual < l {
return fmt.Errorf("expected min length of %d but got %d", l, actual)
}
return nil
}
}
func IsFilepath(i interface{}, others map[string]interface{}) error {
filepath, err := toString(i)
if err != nil {
return err
}
stat, err := os.Stat(filepath)
if os.IsNotExist(err) {
return fmt.Errorf("cannot find file '%s'", filepath)
}
if err != nil {
return err
}
if stat.IsDir() {
return fmt.Errorf("'%s' is a directory", filepath)
}
return nil
}
func IsCIDR(i interface{}, others map[string]interface{}) error {
s, err := toString(i)
if err != nil {
return err
}
_, _, err = net.ParseCIDR(s)
return err
}
func IsIP(i interface{}, others map[string]interface{}) (err error) {
s, err := toString(i)
if err != nil {
return
}
if ip := net.ParseIP(s); ip == nil {
err = fmt.Errorf("expected valid IP address but got '%s'", s)
}
return
}
func toString(i interface{}) (string, error) {
s, ok := i.(string)
if !ok {
return s, fmt.Errorf("expected a string but got %T", i)
}
return s, nil
}
/*
Copyright 2017 WALLIX
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 template
import (
"bytes"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/wallix/awless/template/internal/ast"
)
func Parse(text string) (tmpl *Template, err error) {
defer func() { // as peg lib does not allow errors in Execute, we use panic to build the AST
if rerr := recover(); rerr != nil {
switch rerr := rerr.(type) {
case error:
err = fmt.Errorf("template parsing: %s", rerr)
default:
panic(rerr)
}
}
}()
if clean := strings.TrimSpace(text); clean == "" {
return nil, errors.New("empty template")
}
tmpl = &Template{}
p := &ast.Peg{AST: &ast.AST{}, Buffer: text}
p.Init()
if err = p.Parse(); err != nil {
err = newParseError(text, err.Error())
return
}
p.Execute()
tmpl.AST = p.AST
return
}
func MustParse(text string) *Template {
t, err := Parse(text)
if err != nil {
panic(err)
}
return t
}
func ParseParams(text string) (map[string]interface{}, error) {
node, err := parseParamsAsCommandNode(text)
if err != nil {
return nil, err
}
return node.ToFillerParams(), nil
}
func parseParamsAsCommandNode(text string) (*ast.CommandNode, error) {
full := fmt.Sprintf("none none %s", text)
n, err := parseStatement(full)
if err != nil {
return nil, fmt.Errorf("parse params: %s", err)
}
switch n := n.(type) {
case *ast.CommandNode:
return n, nil
default:
return nil, fmt.Errorf("parse params: expected a command node")
}
}
func parseStatement(text string) (ast.Node, error) {
templ, err := Parse(text)
if err != nil {
return nil, err
}
return templ.Statements[0].Node, nil
}
type parseError struct {
origMsg string
lines []string
line, start, end int
}
func newParseError(templText, pegErrMsg string) (perr *parseError) {
perr = buildParseError(pegErrMsg)
perr.origMsg = pegErrMsg
perr.lines = strings.Split(templText, "\n")
return
}
func (pe *parseError) Error() string {
if pe.invalidIndexes() {
return pe.origMsg
}
var buff bytes.Buffer
buff.WriteString(fmt.Sprintf("error parsing template at line %d (char %d):\n", pe.line, pe.start))
for i, l := range pe.lines {
buff.WriteByte('\t')
if pe.line == i+1 {
buff.WriteString("-> ")
buff.WriteString(l[0:pe.start])
} else {
buff.WriteString(" ")
buff.WriteString(l)
}
if i < len(pe.lines)-1 {
buff.WriteByte('\n')
}
}
return buff.String()
}
func (pe *parseError) invalidIndexes() bool {
if pe.line == 0 {
return true
}
if pe.line > len(pe.lines) {
return true
}
if pe.start > len(pe.lines[pe.line-1]) {
return true
}
return false
}
// ex: parse error near Equal (line 1 symbol 21 - line 1 symbol 22)
var indexesRegex = regexp.MustCompile(`line\s+(\d{1,})\s+symbol\s+(\d{1,})\s+-\s+line \d{1,}\s+symbol\s+(\d{1,3})`)
func buildParseError(s string) (perr *parseError) {
perr = &parseError{}
matches := indexesRegex.FindStringSubmatch(s)
if len(matches) != 4 {
return
}
toInt := func(s string) (i int) {
i, _ = strconv.Atoi(s)
return
}
perr.line, perr.start, perr.end = toInt(matches[1]), toInt(matches[2]), toInt(matches[3])
return
}
package template
import (
"fmt"
"strconv"
"strings"
"github.com/wallix/awless/template/internal/ast"
)
func (temp *Template) Revert() (*Template, error) {
tpl, _, err := Compile(temp, new(noopCompileEnv), PreRevertCompileMode)
if err != nil {
return temp, err
}
var lines []string
cmdsReverseIterator := tpl.CommandNodesReverseIterator()
for i, cmd := range cmdsReverseIterator {
notLastCommand := (i != len(cmdsReverseIterator)-1)
if isRevertible(cmd) {
var revertAction string
var params []string
switch cmd.Action {
case "create", "copy":
revertAction = "delete"
case "start":
revertAction = "stop"
case "stop":
revertAction = "start"
case "detach":
revertAction = "attach"
case "attach":
revertAction = "detach"
case "delete":
revertAction = "create"
case "update":
revertAction = "update"
}
switch cmd.Action {
case "attach":
switch cmd.Entity {
case "routetable", "elasticip":
params = append(params, fmt.Sprintf("association=%s", quoteParamIfNeeded(cmd.CmdResult)))
case "instance":
for k, v := range cmd.ParamNodes {
if k == "port" {
continue
}
params = append(params, fmt.Sprintf("%s=%s", k, printItem(v)))
}
case "containertask":
params = append(params, fmt.Sprintf("name=%s", printItem(cmd.ParamNodes["name"])))
params = append(params, fmt.Sprintf("container-name=%s", printItem(cmd.ParamNodes["container-name"])))
case "networkinterface":
params = append(params, fmt.Sprintf("attachment=%s", quoteParamIfNeeded(cmd.CmdResult)))
case "mfadevice":
params = append(params, fmt.Sprintf("id=%s", printItem(cmd.ParamNodes["id"])))
params = append(params, fmt.Sprintf("user=%s", printItem(cmd.ParamNodes["user"])))
default:
for k, v := range cmd.ParamNodes {
params = append(params, fmt.Sprintf("%s=%v", k, v))
}
}
case "start", "stop", "detach":
switch {
case cmd.Entity == "routetable":
params = append(params, fmt.Sprintf("association=%s", quoteParamIfNeeded(cmd.CmdResult)))
case cmd.Entity == "volume" && cmd.Action == "detach":
for k, v := range cmd.ParamNodes {
if k == "force" {
continue
}
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
case cmd.Entity == "containertask":
params = append(params, fmt.Sprintf("cluster=%s", printItem(cmd.ParamNodes["cluster"])))
params = append(params, fmt.Sprintf("type=%s", printItem(cmd.ParamNodes["type"])))
switch fmt.Sprint(printItem(cmd.ParamNodes["type"])) {
case "service":
params = append(params, fmt.Sprintf("deployment-name=%s", printItem(cmd.ParamNodes["deployment-name"])))
case "task":
params = append(params, fmt.Sprintf("run-arn=%s", quoteParamIfNeeded(cmd.CmdResult)))
default:
return nil, fmt.Errorf("start containertask with type '%v' can not be reverted", printItem(cmd.ParamNodes["deployment-name"]))
}
default:
for k, v := range cmd.ParamNodes {
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
}
case "create":
switch cmd.Entity {
case "tag":
for k, v := range cmd.ParamNodes {
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
case "record":
for k, v := range cmd.ParamNodes {
if k == "comment" {
continue
}
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
case "route":
for k, v := range cmd.ParamNodes {
if k == "gateway" {
continue
}
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
case "database":
params = append(params, fmt.Sprintf("id=%s", quoteParamIfNeeded(cmd.CmdResult)))
params = append(params, "skip-snapshot=true")
case "certificate":
params = append(params, fmt.Sprintf("arn=%s", quoteParamIfNeeded(cmd.CmdResult)))
case "policy":
params = append(params, fmt.Sprintf("arn=%s", quoteParamIfNeeded(cmd.CmdResult)))
params = append(params, "all-versions=true")
case "queue":
params = append(params, fmt.Sprintf("url=%s", quoteParamIfNeeded(cmd.CmdResult)))
case "s3object":
params = append(params, fmt.Sprintf("name=%s", quoteParamIfNeeded(cmd.CmdResult)))
params = append(params, fmt.Sprintf("bucket=%s", printItem(cmd.ParamNodes["bucket"])))
case "role", "group", "user", "stack", "instanceprofile", "repository", "classicloadbalancer":
params = append(params, fmt.Sprintf("name=%s", printItem(cmd.ParamNodes["name"])))
case "accesskey":
params = append(params, fmt.Sprintf("id=%s", quoteParamIfNeeded(cmd.CmdResult)))
params = append(params, fmt.Sprintf("user=%s", printItem(cmd.ParamNodes["user"])))
case "appscalingtarget":
params = append(params, fmt.Sprintf("dimension=%s", printItem(cmd.ParamNodes["dimension"])))
params = append(params, fmt.Sprintf("resource=%s", printItem(cmd.ParamNodes["resource"])))
params = append(params, fmt.Sprintf("service-namespace=%s", printItem(cmd.ParamNodes["service-namespace"])))
case "appscalingpolicy":
params = append(params, fmt.Sprintf("dimension=%s", printItem(cmd.ParamNodes["dimension"])))
params = append(params, fmt.Sprintf("name=%s", printItem(cmd.ParamNodes["name"])))
params = append(params, fmt.Sprintf("resource=%s", printItem(cmd.ParamNodes["resource"])))
params = append(params, fmt.Sprintf("service-namespace=%s", printItem(cmd.ParamNodes["service-namespace"])))
case "loginprofile":
params = append(params, fmt.Sprintf("username=%s", printItem(cmd.ParamNodes["username"])))
case "bucket", "launchconfiguration", "scalinggroup", "alarm", "dbsubnetgroup", "keypair":
params = append(params, fmt.Sprintf("name=%s", quoteParamIfNeeded(cmd.CmdResult)))
if cmd.Entity == "scalinggroup" {
params = append(params, "force=true")
}
default:
params = append(params, fmt.Sprintf("id=%s", quoteParamIfNeeded(cmd.CmdResult)))
}
case "delete":
switch cmd.Entity {
case "record":
for k, v := range cmd.ParamNodes {
params = append(params, fmt.Sprintf("%s=%v", k, quoteParamIfNeeded(v)))
}
case "instanceprofile":
params = append(params, fmt.Sprintf("name=%s", printItem(cmd.ParamNodes["name"])))
}
case "copy":
switch cmd.Entity {
case "image":
params = append(params, fmt.Sprintf("id=%s", quoteParamIfNeeded(cmd.CmdResult)))
params = append(params, "delete-snapshots=true")
default:
params = append(params, fmt.Sprintf("id=%s", quoteParamIfNeeded(cmd.CmdResult)))
}
case "update":
switch cmd.Entity {
case "securitygroup":
for k, v := range cmd.ParamNodes {
if k == "inbound" || k == "outbound" {
if fmt.Sprint(v) == "authorize" {
params = append(params, fmt.Sprintf("%s=revoke", k))
} else if fmt.Sprint(v) == "revoke" {
params = append(params, fmt.Sprintf("%s=authorize", k))
}
continue
}
params = append(params, fmt.Sprintf("%s=%v", k, printItem(v)))
}
}
}
// Prechecks
if cmd.Action == "create" && cmd.Entity == "securitygroup" {
lines = append(lines, fmt.Sprintf("check securitygroup id=%s state=unused timeout=300", quoteParamIfNeeded(cmd.CmdResult)))
}
if cmd.Action == "create" && cmd.Entity == "scalinggroup" {
lines = append(lines, fmt.Sprintf("update scalinggroup name=%s max-size=0 min-size=0", quoteParamIfNeeded(cmd.CmdResult)))
lines = append(lines, fmt.Sprintf("check scalinggroup count=0 name=%s timeout=600", quoteParamIfNeeded(cmd.CmdResult)))
}
if cmd.Action == "start" && cmd.Entity == "instance" {
switch vv := cmd.ParamNodes["ids"].(type) {
case string:
lines = append(lines, fmt.Sprintf("check instance id=%s state=running timeout=180", printItem(vv)))
case []interface{}:
for _, s := range vv {
lines = append(lines, fmt.Sprintf("check instance id=%v state=running timeout=180", printItem(s)))
}
default:
return nil, fmt.Errorf("revert start instance: unexpected type of ids: %T", vv)
}
}
if cmd.Action == "stop" && cmd.Entity == "instance" {
switch vv := cmd.ParamNodes["ids"].(type) {
case string:
lines = append(lines, fmt.Sprintf("check instance id=%s state=stopped timeout=180", printItem(vv)))
case []interface{}:
for _, s := range vv {
lines = append(lines, fmt.Sprintf("check instance id=%v state=stopped timeout=180", printItem(s)))
}
default:
return nil, fmt.Errorf("revert stop instance: unexpected type of ids: %T", vv)
}
}
if cmd.Action == "start" && cmd.Entity == "containertask" && fmt.Sprint(printItem(cmd.ParamNodes["type"])) == "service" {
lines = append(lines, fmt.Sprintf("update containertask cluster=%s deployment-name=%s desired-count=0", printItem(cmd.ParamNodes["cluster"]), printItem(cmd.ParamNodes["deployment-name"])))
}
lines = append(lines, fmt.Sprintf("%s %s %s", revertAction, cmd.Entity, strings.Join(params, " ")))
// Postchecks
if notLastCommand {
if cmd.Action == "create" && cmd.Entity == "instance" {
lines = append(lines, fmt.Sprintf("check instance id=%s state=terminated timeout=180", quoteParamIfNeeded(cmd.CmdResult)))
}
if cmd.Action == "create" && cmd.Entity == "database" {
lines = append(lines, fmt.Sprintf("check database id=%s state=not-found timeout=900", quoteParamIfNeeded(cmd.CmdResult)))
}
if cmd.Action == "create" && cmd.Entity == "loadbalancer" {
lines = append(lines, fmt.Sprintf("check loadbalancer id=%s state=not-found timeout=180", quoteParamIfNeeded(cmd.CmdResult)))
}
if cmd.Action == "attach" && cmd.Entity == "volume" {
lines = append(lines, fmt.Sprintf("check volume id=%s state=available timeout=180", printItem(cmd.ParamNodes["id"])))
}
if cmd.Action == "create" && cmd.Entity == "natgateway" {
lines = append(lines, fmt.Sprintf("check natgateway id=%s state=deleted timeout=180", quoteParamIfNeeded(cmd.CmdResult)))
}
}
}
}
text := strings.Join(lines, "\n")
reverted, err := Parse(text)
if err != nil {
return nil, fmt.Errorf("revert: \n%s\n%s", text, err)
}
return reverted, nil
}
func IsRevertible(t *Template) bool {
revertible := false
t.visitCommandNodes(func(cmd *ast.CommandNode) {
if isRevertible(cmd) {
revertible = true
}
})
return revertible
}
func isRevertible(cmd *ast.CommandNode) bool {
if cmd.CmdErr != nil {
return false
}
if cmd.Action == "check" {
return false
}
if cmd.Action == "detach" && cmd.Entity == "routetable" {
return false
}
if cmd.Entity == "record" && (cmd.Action == "create" || cmd.Action == "delete") {
return true
}
if cmd.Entity == "instanceprofile" && (cmd.Action == "create" || cmd.Action == "delete") {
return true
}
if cmd.Entity == "alarm" && (cmd.Action == "start" || cmd.Action == "stop") {
return true
}
if cmd.Entity == "database" && (cmd.Action == "start" || cmd.Action == "stop") {
return true
}
if cmd.Entity == "containertask" && cmd.Action == "start" {
t, ok := cmd.ToDriverParams()["type"].(string)
return ok && (t == "service" || t == "task")
}
if cmd.Entity == "container" && cmd.Action == "create" {
return true
}
if cmd.Entity == "appscalingtarget" && cmd.Action == "create" {
return true
}
if cmd.Entity == "securitygroup" && cmd.Action == "update" {
return true
}
if cmd.Entity == "appscalingpolicy" && cmd.Action == "create" {
return true
}
if v, ok := cmd.CmdResult.(string); ok && v != "" {
if cmd.Action == "create" || cmd.Action == "start" || cmd.Action == "stop" || cmd.Action == "copy" {
return true
}
}
return cmd.Action == "attach" || cmd.Action == "detach" || cmd.Action == "check" ||
(cmd.Action == "create" && cmd.Entity == "tag") || (cmd.Action == "create" && cmd.Entity == "route")
}
func printItem(i interface{}) string {
switch ii := i.(type) {
case string:
if _, err := strconv.Atoi(ii); err == nil {
return "'" + ii + "'"
}
if _, err := strconv.ParseFloat(ii, 64); err == nil {
return "'" + ii + "'"
}
return quoteParamIfNeeded(i)
case []interface{}:
var out []string
for _, e := range ii {
out = append(out, printItem(e))
}
return "[" + strings.Join(out, ",") + "]"
default:
return fmt.Sprint(ii)
}
}
func quoteParamIfNeeded(param interface{}) string {
input := fmt.Sprint(param)
if ast.SimpleStringValue.MatchString(input) {
return input
} else {
if strings.ContainsRune(input, '\'') {
return "\"" + input + "\""
} else {
return "'" + input + "'"
}
}
}
package template
import (
"errors"
"fmt"
"os"
"github.com/wallix/awless/logger"
"github.com/wallix/awless/template/env"
)
type Runner struct {
Template *Template
Locale, Profile, Message, TemplatePath string
Log *logger.Logger
Fillers []map[string]interface{}
AliasFunc func(paramPath, alias string) string
MissingHolesFunc func(string, []string, bool) string
CmdLookuper func(tokens ...string) interface{}
Validators []Validator
ParamsSuggested int
BeforeRun func(*TemplateExecution) (bool, error)
AfterRun func(*TemplateExecution) error
}
func (ru *Runner) Run() error {
tplExec := &TemplateExecution{
Template: ru.Template,
Path: ru.TemplatePath,
Locale: ru.Locale,
Profile: ru.Profile,
Source: ru.Template.String(),
}
tplExec.SetMessage(ru.Message)
cenv := NewEnv().WithAliasFunc(ru.AliasFunc).WithMissingHolesFunc(ru.MissingHolesFunc).
WithLookupCommandFunc(ru.CmdLookuper).WithLog(ru.Log).WithParamsMode(ru.ParamsSuggested).Build()
cenv.Push(env.FILLERS, ru.Fillers...)
var err error
tplExec.Template, cenv, err = Compile(tplExec.Template, cenv, NewRunnerCompileMode)
if err != nil {
return err
}
tplExec.Fillers = cenv.Get(env.PROCESSED_FILLERS)
errs := tplExec.Template.Validate(ru.Validators...)
if len(errs) > 0 {
for _, err := range errs {
logger.Warning(err)
}
fmt.Fprintln(os.Stderr)
}
if tplExec.IsOneLiner() {
logger.Verbose("Dry running template ...")
} else {
logger.Info("Dry running template ...")
}
renv := NewRunEnv(cenv)
if _, err = tplExec.Template.DryRun(renv); err != nil {
switch t := err.(type) {
case *Errors:
errs, _ := t.Errors()
for _, e := range errs {
logger.Errorf("%s", e.Error())
}
default:
logger.Error(err)
}
return errors.New("Dry run failed")
}
ok, err := ru.BeforeRun(tplExec)
if err != nil {
return err
}
if ok {
tplExec.Template, err = tplExec.Template.Run(renv)
if err != nil {
logger.Errorf("Running template error: %s", err)
}
if err := ru.AfterRun(tplExec); err != nil {
return err
}
}
if tplExec.Stats().KOCount > 0 {
os.Exit(1)
}
return nil
}
/*
Copyright 2017 WALLIX
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 template
import (
"crypto/rand"
"fmt"
"strings"
"time"
"github.com/fatih/color"
"github.com/oklog/ulid"
"github.com/wallix/awless/template/env"
"github.com/wallix/awless/template/internal/ast"
)
type Template struct {
ID string
*ast.AST
}
func (s *Template) DryRun(renv env.Running) (tpl *Template, err error) {
renv.SetDryRun(true)
defer renv.SetDryRun(false)
tpl, err = s.Run(renv)
if err != nil {
return
}
errs := &Errors{}
for _, cmd := range tpl.CommandNodesIterator() {
if cmderr := cmd.Err(); cmderr != nil {
errs.add(cmderr)
}
}
if _, any := errs.Errors(); any {
err = errs
}
return
}
func (s *Template) Run(renv env.Running) (*Template, error) {
vars := map[string]interface{}{}
current := &Template{AST: &ast.AST{}}
current.ID = ulid.MustNew(ulid.Timestamp(time.Now()), rand.Reader).String()
for _, sts := range s.Statements {
clone := sts.Clone()
current.Statements = append(current.Statements, clone)
switch n := clone.Node.(type) {
case *ast.CommandNode:
n.ProcessRefs(vars)
if stop := processCmdNode(renv, n); stop {
return current, nil
}
case *ast.DeclarationNode:
ident := n.Ident
expr := n.Expr
switch n := expr.(type) {
case *ast.CommandNode:
n.ProcessRefs(vars)
if stop := processCmdNode(renv, n); stop {
return current, nil
}
vars[ident] = n.Result()
default:
return current, fmt.Errorf("unknown type of node: %T", expr)
}
default:
return current, fmt.Errorf("unknown type of node: %T", clone.Node)
}
}
return current, nil
}
func processCmdNode(renv env.Running, n *ast.CommandNode) bool {
if renv.IsDryRun() {
n.CmdResult, n.CmdErr = n.Command.Run(renv, n.ToDriverParams())
n.CmdErr = prefixError(n.CmdErr, fmt.Sprintf("dry run: %s %s", n.Action, n.Entity))
} else {
n.CmdResult, n.CmdErr = n.Run(renv, n.ToDriverParams())
var res, status string
if n.CmdResult != nil {
res = " (" + color.New(color.FgCyan).Sprint(n.CmdResult) + ") "
}
if n.CmdErr != nil {
status = color.New(color.FgRed).Sprint("KO")
} else {
status = color.New(color.FgGreen).Sprint("OK")
}
renv.Log().Infof("%s %s %s%s", status, n.Action, n.Entity, res)
if n.CmdErr != nil {
renv.Log().MultiLineError(n.CmdErr)
}
}
return n.CmdErr != nil
}
func prefixError(err error, prefix string) error {
if err == nil {
return err
}
return fmt.Errorf("%s: %s", prefix, err.Error())
}
func (s *Template) Validate(rules ...Validator) (all []error) {
for _, rule := range rules {
errs := rule.Execute(s)
all = append(all, errs...)
}
return
}
func (t *Template) HasErrors() bool {
for _, cmd := range t.CommandNodesIterator() {
if cmd.CmdErr != nil {
return true
}
}
return false
}
func (t *Template) UniqueDefinitions(apis map[string]string) (res []string) {
unique := make(map[string]struct{})
for _, cmd := range t.CommandNodesIterator() {
key := fmt.Sprintf("%s%s", cmd.Action, cmd.Entity)
if api, found := apis[key]; found {
unique[api] = struct{}{}
}
}
for api := range unique {
res = append(res, api)
}
return
}
func (s *Template) visitCommandNodes(fn func(n *ast.CommandNode)) {
for _, cmd := range s.CommandNodesIterator() {
fn(cmd)
}
}
func (s *Template) visitCommandNodesE(fn func(n *ast.CommandNode) error) error {
for _, cmd := range s.CommandNodesIterator() {
if err := fn(cmd); err != nil {
return err
}
}
return nil
}
func (s *Template) CommandNodesIterator() (nodes []*ast.CommandNode) {
for _, sts := range s.Statements {
switch nn := sts.Node.(type) {
case *ast.CommandNode:
nodes = append(nodes, nn)
case *ast.DeclarationNode:
expr := nn.Expr
switch cmd := expr.(type) {
case *ast.CommandNode:
nodes = append(nodes, cmd)
}
}
}
return
}
func (s *Template) CommandNodesReverseIterator() (nodes []*ast.CommandNode) {
for i := len(s.Statements) - 1; i >= 0; i-- {
sts := s.Statements[i]
switch nn := sts.Node.(type) {
case *ast.CommandNode:
nodes = append(nodes, nn)
case *ast.DeclarationNode:
expr := nn.Expr
switch cmd := expr.(type) {
case *ast.CommandNode:
nodes = append(nodes, cmd)
}
}
}
return
}
func (s *Template) declarationNodesIterator() (nodes []*ast.DeclarationNode) {
for _, sts := range s.Statements {
switch n := sts.Node.(type) {
case *ast.DeclarationNode:
nodes = append(nodes, n)
}
}
return
}
func (s *Template) expressionNodesIterator() (nodes []ast.ExpressionNode) {
for _, st := range s.Statements {
if expr := extractExpressionNode(st); expr != nil {
nodes = append(nodes, expr)
}
}
return
}
func extractExpressionNode(st *ast.Statement) ast.ExpressionNode {
switch n := st.Node.(type) {
case *ast.DeclarationNode:
return n.Expr
case ast.ExpressionNode:
return n
}
return nil
}
type Errors struct {
errs []error
}
func (d *Errors) Errors() ([]error, bool) {
return d.errs, len(d.errs) > 0
}
func (d *Errors) add(err error) {
d.errs = append(d.errs, err)
}
func (d *Errors) Error() string {
var all []string
for _, err := range d.errs {
all = append(all, err.Error())
}
return strings.Join(all, "\n")
}
func MatchStringParamValue(s string) bool {
return ast.SimpleStringValue.MatchString(s)
}
package template
import (
"bytes"
"errors"
"fmt"
"github.com/wallix/awless/cloud"
)
type Validator interface {
Execute(t *Template) []error
}
type LookupGraphFunc func(key string) (cloud.GraphAPI, bool)
type UniqueNameValidator struct {
LookupGraph LookupGraphFunc
}
func (v *UniqueNameValidator) Execute(t *Template) (errs []error) {
for _, cmd := range t.CommandNodesIterator() {
if cmd.Action == "create" {
name := cmd.ParamNodes["name"]
g, ok := v.LookupGraph(cmd.Entity)
if !ok {
continue
}
resources, err := g.FindWithProperties(map[string]interface{}{"Name": name})
if err != nil {
errs = append(errs, err)
}
if len(resources) > 0 {
for _, r := range resources {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("'%s' name already used for %s %s", name, r.Type(), r.Id()))
if state, ok := r.Properties()["State"].(string); ok {
buf.WriteString(fmt.Sprintf(" (state: '%s')", state))
}
errs = append(errs, errors.New(buf.String()))
}
}
}
}
return
}
type ParamIsSetValidator struct {
Entity, Action, Param, WarningMessage string
}
func (v *ParamIsSetValidator) Execute(t *Template) (errs []error) {
for _, cmd := range t.CommandNodesIterator() {
if cmd.Action == v.Action && cmd.Entity == v.Entity {
_, hasParam := cmd.ParamNodes[v.Param]
if !hasParam {
errs = append(errs, fmt.Errorf("%s", v.WarningMessage))
}
}
}
return
}
package web
import (
"bytes"
"fmt"
"html/template"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/gorilla/mux"
tstore "github.com/wallix/triplestore"
awsservices "github.com/wallix/awless/aws/services"
"github.com/wallix/awless/cloud"
"github.com/wallix/awless/cloud/properties"
"github.com/wallix/awless/cloud/rdf"
"github.com/wallix/awless/graph"
"github.com/wallix/awless/sync"
"github.com/wallix/awless/sync/repo"
)
type server struct {
port string
awsProfile string
gph cloud.GraphAPI
}
func New(port, profile string) *server {
return &server{port: port, awsProfile: profile}
}
func (s *server) Start() error {
log.Printf("Retrieving all local synced regions for the '%s' profile\n", s.awsProfile)
log.Println("(use awless web -p otherprofile for browsing through another profile)")
g, err := sync.LoadAllLocalGraphs(s.awsProfile)
if err != nil {
return fmt.Errorf("cannot load local graphs: %s", err)
}
s.gph = g
log.Printf("Starting browsing at http://localhost%s\n", s.port)
return http.ListenAndServe(s.port, s.routes())
}
func (s *server) routes() http.Handler {
r := mux.NewRouter()
r.HandleFunc("/resources/{id}", s.showResourceHandler)
r.HandleFunc("/resources", s.listResourcesHandler)
r.HandleFunc("/rdf", s.rdfHandler)
r.HandleFunc("/graph", s.graphHandler)
r.HandleFunc("/", s.homeHandler)
return r
}
func (s *server) homeHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.New("home").Parse(homeTpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if err := t.Execute(w, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *server) rdfHandler(w http.ResponseWriter, r *http.Request) {
tris, err := loadLocalTriples(s.awsProfile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var encErr error
if r.FormValue("namespaced") == "true" {
encErr = tstore.NewLenientNTEncoderWithContext(w, tstore.RDFContext).Encode(tris...)
} else {
encErr = tstore.NewLenientNTEncoder(w).Encode(tris...)
}
if encErr != nil {
http.Error(w, encErr.Error(), http.StatusInternalServerError)
return
}
}
func (s *server) graphHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.New("graph").Parse(graphVizTpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
tris, err := loadLocalTriples(s.awsProfile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var data bytes.Buffer
if err := tstore.NewDotGraphEncoder(&data, "cloud-rel:parentOf").Encode(tris...); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := t.Execute(w, data.String()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *server) showResourceHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.New("show").Parse(showResourceTpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
resId := mux.Vars(r)["id"]
res, err := s.gph.FindWithProperties(map[string]interface{}{properties.ID: resId})
if err != nil && len(res) != 1 {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
resource := newResource(res[0])
deps, _ := s.gph.ResourceRelations(res[0], rdf.DependingOnRel, false)
resource.AddDependsOn(deps...)
applies, _ := s.gph.ResourceRelations(res[0], rdf.ApplyOn, false)
resource.AddAppliesOn(applies...)
parents, _ := s.gph.ResourceRelations(res[0], rdf.ParentOf, true)
resource.AddDependsOn(parents...)
children, _ := s.gph.ResourceRelations(res[0], rdf.ChildrenOfRel, true)
resource.AddDependsOn(children...)
if err := t.Execute(w, resource); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *server) listResourcesHandler(w http.ResponseWriter, r *http.Request) {
t, err := template.New("resources").Parse(resourcesTpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
resourcesByTypes := make(map[string][]*Resource)
for _, typ := range append(awsservices.ResourceTypes, "region") {
gRes, err := s.gph.Find(cloud.NewQuery(typ))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
for _, r := range gRes {
resourcesByTypes[typ] = append(resourcesByTypes[typ], newResource(r))
}
}
if err := t.Execute(w, resourcesByTypes); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
type Resource struct {
Id, Type string
Properties map[string]interface{}
Parents []*Resource
Children []*Resource
DependsOn []*Resource
AppliesOn []*Resource
}
func (r *Resource) AddDependsOn(gr ...cloud.Resource) {
for _, res := range gr {
r.DependsOn = append(r.DependsOn, newResource(res))
}
}
func (r *Resource) AddAppliesOn(gr ...cloud.Resource) {
for _, res := range gr {
r.AppliesOn = append(r.AppliesOn, newResource(res))
}
}
func (r *Resource) AddParents(gr ...cloud.Resource) {
for _, res := range gr {
r.Parents = append(r.Parents, newResource(res))
}
}
func (r *Resource) AddChildren(gr ...*graph.Resource) {
for _, res := range gr {
r.Children = append(r.Children, newResource(res))
}
}
func newResource(r cloud.Resource) *Resource {
return &Resource{Id: r.Id(), Type: r.Type(), Properties: r.Properties()}
}
func loadLocalTriples(profile string) ([]tstore.Triple, error) {
path := filepath.Join(repo.BaseDir(), profile, "*", fmt.Sprintf("*%s", ".nt"))
files, _ := filepath.Glob(path)
var readers []io.Reader
for _, f := range files {
reader, err := os.Open(f)
if err != nil {
return nil, fmt.Errorf("loading '%s': %s", f, err)
}
readers = append(readers, reader)
}
dec := tstore.NewDatasetDecoder(tstore.NewAutoDecoder, readers...)
return dec.Decode()
}
const homeTpl = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<ul>
<li><a href="/resources">View resources and their relations</a></li>
<li><a href="/rdf">View RDF</a></li>
<li><a href="/rdf?namespaced=true">View namespaced RDF</a></li>
<li><a href="/graph">View DOT graph (experimental)</a></li>
</ul>
</body>
</html>`
const showResourceTpl = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h2>{{.Type}}: {{.Id}}</h2>
<ul>
{{range $name, $val := .Properties}}
<li><b>{{$name}}:</b> {{$val}}</li>
{{end}}
</ul>
{{if (len .Parents) gt 0}}
<h4>Parents:</h4>
<ul>
{{range .Parents}}
<li>{{.Type}} <a href="/resources/{{ urlquery .Id}}">{{.Id}}</a></li>
{{end}}
</ul>
{{end}}
{{if (len .Children) gt 0}}
<h4>Children:</h4>
<ul>
{{range .Children}}
<li>{{.Type}} <a href="/resources/{{ urlquery .Id}}">{{.Id}}</a></li>
{{end}}
</ul>
{{end}}
{{if (len .DependsOn) gt 0}}
<h4>Depends on:</h4>
<ul>
{{range .DependsOn}}
<li>{{.Type}} <a href="/resources/{{ urlquery .Id}}">{{.Id}}</a></li>
{{end}}
</ul>
{{end}}
{{if (len .AppliesOn) gt 0}}
<h4>Applies on:</h4>
<ul>
{{range .AppliesOn}}
<li>{{.Type}} <a href="/resources/{{ urlquery .Id}}">{{.Id}}</a></li>
{{end}}
</ul>
{{end}}
</body>
</html>`
const resourcesTpl = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
{{range $type, $resource := .}}
<h2>{{$type}} ({{len .}})</h2>
<ul>
{{range $resource}}
{{ $name := index .Properties "Name" }}
<li>
{{if $name}} {{if (ne (print $name) "")}}<b>Name:</b> {{$name}}, {{end}}{{end}}
<b>Id: </b><a href="/resources/{{ urlquery .Id}}">{{.Id}}</a>
</li>
{{end}}
</ul>
{{end}}
</body>
</html>`
const graphVizTpl = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="https://github.com/mdaines/viz.js/releases/download/v1.7.1/viz-lite.js"></script>
<script>
document.body.innerHTML += Viz("{{ . }}");
</script>
</body>
</html>`