2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								package  cloudwatch  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  (  
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"encoding/json" 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"errors" 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-09 19:43:22 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"fmt" 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"math" 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"regexp" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"sort" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"strconv" 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"strings" 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"time" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-03-15 00:44:04 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/google/uuid" 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-23 23:32:12 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/grafana/grafana-plugin-sdk-go/backend" 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/grafana/grafana/pkg/services/featuremgmt" 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								)  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-03-15 00:44:04 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								var  validMetricDataID  =  regexp . MustCompile ( ` ^[a-z][a-zA-Z0-9_]*$ ` )  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								type  QueryJson  struct  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Datasource         map [ string ] string       ` json:"datasource,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Dimensions         map [ string ] interface { }  ` json:"dimensions,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Expression         string                  ` json:"expression,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Id                 string                  ` json:"id,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Label              * string                 ` json:"label,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									MatchExact         * bool                   ` json:"matchExact,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									MaxDataPoints      int                     ` json:"maxDataPoints,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									MetricEditorMode   * int                    ` json:"metricEditorMode,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									MetricName         string                  ` json:"metricName,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									MetricQueryType    metricQueryType         ` json:"metricQueryType,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Namespace          string                  ` json:"namespace,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Period             string                  ` json:"period,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									RefId              string                  ` json:"refId,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Region             string                  ` json:"region,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									SqlExpression      string                  ` json:"sqlExpression,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Statistic          * string                 ` json:"statistic,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Statistics         [ ] * string               ` json:"statistics,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									TimezoneUTCOffset  string                  ` json:"timezoneUTCOffset,omitempty" ` 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-27 20:45:59 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									QueryType          string                  ` json:"type,omitempty" ` 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									Hide               * bool                   ` json:"hide,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									Alias              * string                 ` json:"alias,omitempty" ` 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// parseQueries parses the json queries and returns a map of cloudWatchQueries by region. The cloudWatchQuery has a 1 to 1 mapping to a query editor row
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  ( e  * cloudWatchExecutor )  parseQueries ( queries  [ ] backend . DataQuery ,  startTime  time . Time ,  endTime  time . Time )  ( map [ string ] [ ] * cloudWatchQuery ,  error )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									requestQueries  :=  make ( map [ string ] [ ] * cloudWatchQuery ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-08 15:27:03 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									migratedQueries ,  err  :=  migrateLegacyQuery ( queries ,  e . features . IsEnabled ( featuremgmt . FlagCloudWatchDynamicLabels ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  query  :=  range  migratedQueries  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										var  model  QueryJson 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  json . Unmarshal ( query . JSON ,  & model ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-23 23:32:12 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  & queryError { err :  err ,  RefID :  query . RefID } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										queryType  :=  model . QueryType 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-27 20:45:59 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  queryType  !=  timeSeriesQuery  &&  queryType  !=  ""  { 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
											continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  model . MatchExact  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											trueBooleanValue  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											model . MatchExact  =  & trueBooleanValue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-08 14:02:49 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										refID  :=  query . RefID 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-23 23:32:12 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										query ,  err  :=  parseRequestQuery ( model ,  refID ,  startTime ,  endTime ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 18:25:58 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  & queryError { err :  err ,  RefID :  refID } 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 18:25:58 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										if  _ ,  exist  :=  requestQueries [ query . Region ] ;  ! exist  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											requestQueries [ query . Region ]  =  [ ] * cloudWatchQuery { } 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										requestQueries [ query . Region ]  =  append ( requestQueries [ query . Region ] ,  query ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  requestQueries ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// migrateLegacyQuery is also done in the frontend, so this should only ever be needed for alerting queries
  
						 
					
						
							
								
									
										
										
										
											2022-05-08 15:27:03 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  migrateLegacyQuery ( queries  [ ] backend . DataQuery ,  dynamicLabelsEnabled  bool )  ( [ ] * backend . DataQuery ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									migratedQueries  :=  [ ] * backend . DataQuery { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  q  :=  range  queries  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										query  :=  q 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										var  queryJson  * QueryJson 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  json . Unmarshal ( query . JSON ,  & queryJson ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  err  :=  migrateStatisticsToStatistic ( queryJson ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  queryJson . Label  ==  nil  &&  dynamicLabelsEnabled  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											migrateAliasToDynamicLabel ( queryJson ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										query . JSON ,  err  =  json . Marshal ( queryJson ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  err 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										migratedQueries  =  append ( migratedQueries ,  & query ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  migratedQueries ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// migrateStatisticsToStatistic migrates queries that has a `statistics` field to use the `statistic` field instead.
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// In case the query used more than one stat, the first stat in the slice will be used in the statistic field
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Read more here https://github.com/grafana/grafana/issues/30629
  
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  migrateStatisticsToStatistic ( queryJson  * QueryJson )  error  {  
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// If there's not a statistic property in the json, we know it's the legacy format and then it has to be migrated
 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  queryJson . Statistic  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  queryJson . Statistics  ==  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  fmt . Errorf ( "query must have either statistic or statistics field" ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										queryJson . Statistic  =  queryJson . Statistics [ 0 ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										queryJson . Statistics  =  nil 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								var  aliasPatterns  =  map [ string ] string {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"metric" :     ` $ { PROP('MetricName')} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"namespace" :  ` $ { PROP('Namespace')} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"period" :     ` $ { PROP('Period')} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"region" :     ` $ { PROP('Region')} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"stat" :       ` $ { PROP('Stat')} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"label" :      ` $ { LABEL} ` , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								var  legacyAliasRegexp  =  regexp . MustCompile ( ` {{ \ s * ( . + ? ) \ s * }} ` )  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  migrateAliasToDynamicLabel ( queryJson  * QueryJson )  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									fullAliasField  :=  "" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  queryJson . Alias  !=  nil  &&  * queryJson . Alias  !=  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										matches  :=  legacyAliasRegexp . FindAllStringSubmatch ( * queryJson . Alias ,  - 1 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										fullAliasField  =  * queryJson . Alias 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										for  _ ,  groups  :=  range  matches  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											fullMatch  :=  groups [ 0 ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											subgroup  :=  groups [ 1 ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  dynamicLabel ,  ok  :=  aliasPatterns [ subgroup ] ;  ok  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												fullAliasField  =  strings . ReplaceAll ( fullAliasField ,  fullMatch ,  dynamicLabel ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												fullAliasField  =  strings . ReplaceAll ( fullAliasField ,  fullMatch ,  fmt . Sprintf ( ` $ { PROP('Dim.%s')} ` ,  subgroup ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									queryJson . Label  =  & fullAliasField 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-03 20:41:51 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  parseRequestQuery ( model  QueryJson ,  refId  string ,  startTime  time . Time ,  endTime  time . Time )  ( * cloudWatchQuery ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2020-10-06 19:45:58 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									plog . Debug ( "Parsing request query" ,  "query" ,  model ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									cloudWatchQuery  :=  cloudWatchQuery { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Alias :              "" , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Label :              "" , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										MatchExact :         true , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Statistic :          "" , 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-27 20:45:59 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										ReturnData :         true , 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										UsedExpression :     "" , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										RefId :              refId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Id :                 model . Id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Region :             model . Region , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Namespace :          model . Namespace , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										MetricName :         model . MetricName , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										MetricQueryType :    model . MetricQueryType , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										SqlExpression :      model . SqlExpression , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										TimezoneUTCOffset :  model . TimezoneUTCOffset , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Expression :         model . Expression , 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									reNumber  :=  regexp . MustCompile ( ` ^\d+$ ` ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									dimensions ,  err  :=  parseDimensions ( model . Dimensions ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-09 19:43:22 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  fmt . Errorf ( "failed to parse dimensions: %v" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									cloudWatchQuery . Dimensions  =  dimensions 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									p  :=  model . Period 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									var  period  int 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  strings . ToLower ( p )  ==  "auto"  ||  p  ==  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										deltaInSeconds  :=  endTime . Sub ( startTime ) . Seconds ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-01 15:44:47 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										periods  :=  getRetainedPeriods ( time . Since ( startTime ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-22 20:43:36 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										datapoints  :=  int ( math . Ceil ( deltaInSeconds  /  2000 ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										period  =  periods [ len ( periods ) - 1 ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										for  _ ,  value  :=  range  periods  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  datapoints  <=  value  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												period  =  value 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 18:25:58 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  reNumber . Match ( [ ] byte ( p ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											period ,  err  =  strconv . Atoi ( p ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-09 19:43:22 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												return  nil ,  fmt . Errorf ( "failed to parse period as integer: %v" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											d ,  err  :=  time . ParseDuration ( p ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-09 19:43:22 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
												return  nil ,  fmt . Errorf ( "failed to parse period as duration: %v" ,  err ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-01-17 20:22:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											period  =  int ( d . Seconds ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									cloudWatchQuery . Period  =  period 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  model . Id  ==  ""  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// Why not just use refId if id is not specified in the frontend? When specifying an id in the editor,
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// and alphabetical must be used. The id must be unique, so if an id like for example a, b or c would be used,
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// it would likely collide with some ref id. That's why the `query` prefix is used.
 
							 
						 
					
						
							
								
									
										
										
										
											2022-03-15 00:44:04 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										suffix  :=  refId 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  ! validMetricDataID . MatchString ( suffix )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											uuid  :=  uuid . NewString ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											suffix  =  strings . Replace ( uuid ,  "-" ,  "" ,  - 1 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										cloudWatchQuery . Id  =  fmt . Sprintf ( "query%s" ,  suffix ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  model . Hide  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										cloudWatchQuery . ReturnData  =  ! * model . Hide 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-08 22:06:43 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  model . QueryType  ==  ""  { 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										// If no type is provided we assume we are called by alerting service, which requires to return data!
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// Note, this is sort of a hack, but the official Grafana interfaces do not carry the information
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// who (which service) called the TsdbQueryEndpoint.Query(...) function.
 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										cloudWatchQuery . ReturnData  =  true 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  model . MetricEditorMode  ==  nil  &&  len ( model . Expression )  >  0  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-11-30 17:53:31 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										// this should only ever happen if this is an alerting query that has not yet been migrated in the frontend
 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										cloudWatchQuery . MetricEditorMode  =  MetricEditorModeRaw 
							 
						 
					
						
							
								
									
										
										
										
											2021-11-30 17:53:31 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										if  model . MetricEditorMode  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											cloudWatchQuery . MetricEditorMode  =  metricEditorMode ( * model . MetricEditorMode ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											cloudWatchQuery . MetricEditorMode  =  metricEditorMode ( 0 ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
									
										
										
										
											2021-11-30 17:53:31 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  model . Statistic  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										cloudWatchQuery . Statistic  =  * model . Statistic 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  model . MatchExact  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										cloudWatchQuery . MatchExact  =  * model . MatchExact 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  model . Alias  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										cloudWatchQuery . Alias  =  * model . Alias 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  model . Label  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										cloudWatchQuery . Label  =  * model . Label 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  & cloudWatchQuery ,  nil 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-09-01 15:44:47 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  getRetainedPeriods ( timeSince  time . Duration )  [ ] int  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// See https://aws.amazon.com/about-aws/whats-new/2016/11/cloudwatch-extends-metrics-retention-and-new-user-interface/
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  timeSince  >  time . Duration ( 455 ) * 24 * time . Hour  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  [ ] int { 21600 ,  86400 } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  if  timeSince  >  time . Duration ( 63 ) * 24 * time . Hour  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  [ ] int { 3600 ,  21600 ,  86400 } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  if  timeSince  >  time . Duration ( 15 ) * 24 * time . Hour  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  [ ] int { 300 ,  900 ,  3600 ,  21600 ,  86400 } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  [ ] int { 60 ,  300 ,  900 ,  3600 ,  21600 ,  86400 } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  parseDimensions ( dimensions  map [ string ] interface { } )  ( map [ string ] [ ] string ,  error )  {  
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									parsedDimensions  :=  make ( map [ string ] [ ] string ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-07-09 03:39:53 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									for  k ,  v  :=  range  dimensions  { 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										// This is for backwards compatibility. Before 6.5 dimensions values were stored as strings and not arrays
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  value ,  ok  :=  v . ( string ) ;  ok  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											parsedDimensions [ k ]  =  [ ] string { value } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										}  else  if  values ,  ok  :=  v . ( [ ] interface { } ) ;  ok  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											for  _ ,  value  :=  range  values  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												parsedDimensions [ k ]  =  append ( parsedDimensions [ k ] ,  value . ( string ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										}  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-09 19:43:22 +08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											return  nil ,  errors . New ( "unknown type as dimension value" ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-11-14 17:59:41 +08:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									sortedDimensions  :=  sortDimensions ( parsedDimensions ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  sortedDimensions ,  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  sortDimensions ( dimensions  map [ string ] [ ] string )  map [ string ] [ ] string  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									sortedDimensions  :=  make ( map [ string ] [ ] string ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  keys  [ ] string 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  k  :=  range  dimensions  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										keys  =  append ( keys ,  k ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									sort . Strings ( keys ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  k  :=  range  keys  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										sortedDimensions [ k ]  =  dimensions [ k ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  sortedDimensions 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}