1/* Copyright 2017 WALLIX 2 3Licensed under the Apache License, Version 2.0 (the "License"); 4you may not use this file except in compliance with the License. 5You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9Unless required by applicable law or agreed to in writing, software 10distributed under the License is distributed on an "AS IS" BASIS, 11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12See the License for the specific language governing permissions and 13limitations under the License. 14*/ 15 16package awsspec 17 18import ( 19 "errors" 20 "fmt" 21 22 "github.com/wallix/awless/cloud" 23 "github.com/wallix/awless/template/env" 24 "github.com/wallix/awless/template/params" 25 26 "github.com/aws/aws-sdk-go/aws" 27 "github.com/aws/aws-sdk-go/service/cloudwatch" 28 "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" 29 "github.com/wallix/awless/logger" 30) 31 32type CreateAlarm struct { 33 _ string `action:"create" entity:"alarm" awsAPI:"cloudwatch" awsCall:"PutMetricAlarm" awsInput:"cloudwatch.PutMetricAlarmInput" awsOutput:"cloudwatch.PutMetricAlarmOutput"` 34 logger *logger.Logger 35 graph cloud.GraphAPI 36 api cloudwatchiface.CloudWatchAPI 37 Name *string `awsName:"AlarmName" awsType:"awsstr" templateName:"name"` 38 Operator *string `awsName:"ComparisonOperator" awsType:"awsstr" templateName:"operator"` 39 Metric *string `awsName:"MetricName" awsType:"awsstr" templateName:"metric"` 40 Namespace *string `awsName:"Namespace" awsType:"awsstr" templateName:"namespace"` 41 EvaluationPeriods *int64 `awsName:"EvaluationPeriods" awsType:"awsint64" templateName:"evaluation-periods"` 42 Period *int64 `awsName:"Period" awsType:"awsint64" templateName:"period"` 43 StatisticFunction *string `awsName:"Statistic" awsType:"awsstr" templateName:"statistic-function"` 44 Threshold *float64 `awsName:"Threshold" awsType:"awsfloat" templateName:"threshold"` 45 Enabled *bool `awsName:"ActionsEnabled" awsType:"awsbool" templateName:"enabled"` 46 AlarmActions []*string `awsName:"AlarmActions" awsType:"awsstringslice" templateName:"alarm-actions"` 47 InsufficientdataActions []*string `awsName:"InsufficientDataActions" awsType:"awsstringslice" templateName:"insufficientdata-actions"` 48 OkActions []*string `awsName:"OKActions" awsType:"awsstringslice" templateName:"ok-actions"` 49 Description *string `awsName:"AlarmDescription" awsType:"awsstr" templateName:"description"` 50 Dimensions []*string `awsName:"Dimensions" awsType:"awsdimensionslice" templateName:"dimensions"` 51 Unit *string `awsName:"Unit" awsType:"awsstr" templateName:"unit"` 52} 53 54func (cmd *CreateAlarm) ParamsSpec() params.Spec { 55 return params.NewSpec( 56 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"), 57 params.Opt("alarm-actions", "description", "dimensions", "enabled", "insufficientdata-actions", "ok-actions", "unit"), 58 ), 59 params.Validators{ 60 "operator": params.IsInEnumIgnoreCase("GreaterThanThreshold", "LessThanThreshold", "LessThanOrEqualToThreshold", "GreaterThanOrEqualToThreshold"), 61 }) 62} 63 64func (cmd *CreateAlarm) ExtractResult(i interface{}) string { 65 return StringValue(cmd.Name) 66} 67 68type DeleteAlarm struct { 69 _ string `action:"delete" entity:"alarm" awsAPI:"cloudwatch" awsCall:"DeleteAlarms" awsInput:"cloudwatch.DeleteAlarmsInput" awsOutput:"cloudwatch.DeleteAlarmsOutput"` 70 logger *logger.Logger 71 graph cloud.GraphAPI 72 api cloudwatchiface.CloudWatchAPI 73 Name []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"name"` 74} 75 76func (cmd *DeleteAlarm) ParamsSpec() params.Spec { 77 return params.NewSpec(params.AllOf(params.Key("name"))) 78} 79 80type StartAlarm struct { 81 _ string `action:"start" entity:"alarm" awsAPI:"cloudwatch" awsCall:"EnableAlarmActions" awsInput:"cloudwatch.EnableAlarmActionsInput" awsOutput:"cloudwatch.EnableAlarmActionsOutput"` 82 logger *logger.Logger 83 graph cloud.GraphAPI 84 api cloudwatchiface.CloudWatchAPI 85 Names []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"names"` 86} 87 88func (cmd *StartAlarm) ParamsSpec() params.Spec { 89 return params.NewSpec(params.AllOf(params.Key("names"))) 90} 91 92type StopAlarm struct { 93 _ string `action:"stop" entity:"alarm" awsAPI:"cloudwatch" awsCall:"DisableAlarmActions" awsInput:"cloudwatch.DisableAlarmActionsInput" awsOutput:"cloudwatch.DisableAlarmActionsOutput"` 94 logger *logger.Logger 95 graph cloud.GraphAPI 96 api cloudwatchiface.CloudWatchAPI 97 Names []*string `awsName:"AlarmNames" awsType:"awsstringslice" templateName:"names"` 98} 99 100func (cmd *StopAlarm) ParamsSpec() params.Spec { 101 return params.NewSpec(params.AllOf(params.Key("names"))) 102} 103 104type AttachAlarm struct { 105 _ string `action:"attach" entity:"alarm" awsAPI:"cloudwatch"` 106 logger *logger.Logger 107 graph cloud.GraphAPI 108 api cloudwatchiface.CloudWatchAPI 109 Name *string `templateName:"name"` 110 ActionArn *string `templateName:"action-arn"` 111} 112 113func (cmd *AttachAlarm) ParamsSpec() params.Spec { 114 return params.NewSpec(params.AllOf(params.Key("action-arn"), params.Key("name"))) 115} 116 117func (cmd *AttachAlarm) ManualRun(renv env.Running) (interface{}, error) { 118 alarm, err := getAlarm(cmd.api, cmd.Name) 119 if err != nil { 120 return nil, err 121 } 122 alarm.AlarmActions = append(alarm.AlarmActions, cmd.ActionArn) 123 124 return cmd.api.PutMetricAlarm(&cloudwatch.PutMetricAlarmInput{ 125 ActionsEnabled: alarm.ActionsEnabled, 126 AlarmActions: alarm.AlarmActions, 127 AlarmDescription: alarm.AlarmDescription, 128 AlarmName: alarm.AlarmName, 129 ComparisonOperator: alarm.ComparisonOperator, 130 Dimensions: alarm.Dimensions, 131 EvaluateLowSampleCountPercentile: alarm.EvaluateLowSampleCountPercentile, 132 EvaluationPeriods: alarm.EvaluationPeriods, 133 ExtendedStatistic: alarm.ExtendedStatistic, 134 InsufficientDataActions: alarm.InsufficientDataActions, 135 MetricName: alarm.MetricName, 136 Namespace: alarm.Namespace, 137 OKActions: alarm.OKActions, 138 Period: alarm.Period, 139 Statistic: alarm.Statistic, 140 Threshold: alarm.Threshold, 141 TreatMissingData: alarm.TreatMissingData, 142 Unit: alarm.Unit, 143 }) 144} 145 146type DetachAlarm struct { 147 _ string `action:"detach" entity:"alarm" awsAPI:"cloudwatch"` 148 logger *logger.Logger 149 graph cloud.GraphAPI 150 api cloudwatchiface.CloudWatchAPI 151 Name *string `templateName:"name"` 152 ActionArn *string `templateName:"action-arn"` 153} 154 155func (cmd *DetachAlarm) ParamsSpec() params.Spec { 156 return params.NewSpec(params.AllOf(params.Key("action-arn"), params.Key("name"))) 157} 158 159func (cmd *DetachAlarm) ManualRun(renv env.Running) (interface{}, error) { 160 alarm, err := getAlarm(cmd.api, cmd.Name) 161 if err != nil { 162 return nil, err 163 } 164 actionArn := aws.StringValue(cmd.ActionArn) 165 var found bool 166 var updatedActions []*string 167 for _, action := range alarm.AlarmActions { 168 if aws.StringValue(action) == actionArn { 169 found = true 170 } else { 171 updatedActions = append(updatedActions, action) 172 } 173 } 174 if !found { 175 return nil, fmt.Errorf("detach alarm: action '%s' is not attached to alarm actions of alarm %s", actionArn, aws.StringValue(alarm.AlarmName)) 176 } 177 178 return cmd.api.PutMetricAlarm(&cloudwatch.PutMetricAlarmInput{ 179 ActionsEnabled: alarm.ActionsEnabled, 180 AlarmActions: updatedActions, 181 AlarmDescription: alarm.AlarmDescription, 182 AlarmName: alarm.AlarmName, 183 ComparisonOperator: alarm.ComparisonOperator, 184 Dimensions: alarm.Dimensions, 185 EvaluateLowSampleCountPercentile: alarm.EvaluateLowSampleCountPercentile, 186 EvaluationPeriods: alarm.EvaluationPeriods, 187 ExtendedStatistic: alarm.ExtendedStatistic, 188 InsufficientDataActions: alarm.InsufficientDataActions, 189 MetricName: alarm.MetricName, 190 Namespace: alarm.Namespace, 191 OKActions: alarm.OKActions, 192 Period: alarm.Period, 193 Statistic: alarm.Statistic, 194 Threshold: alarm.Threshold, 195 TreatMissingData: alarm.TreatMissingData, 196 Unit: alarm.Unit, 197 }) 198} 199 200func getAlarm(api cloudwatchiface.CloudWatchAPI, name *string) (*cloudwatch.MetricAlarm, error) { 201 if name == nil { 202 return nil, errors.New("missing required params 'name'") 203 } 204 out, err := api.DescribeAlarms(&cloudwatch.DescribeAlarmsInput{AlarmNames: []*string{name}}) 205 if err != nil { 206 return nil, err 207 } 208 if l := len(out.MetricAlarms); l == 0 { 209 return nil, fmt.Errorf("alarm '%s' not found", StringValue(name)) 210 } else if l > 1 { 211 return nil, fmt.Errorf("%d alarms found with name '%s'", l, StringValue(name)) 212 } 213 return out.MetricAlarms[0], nil 214} 215