github.com/in4it/ecs-deploy@v0.0.42-0.20240508120354-ed77ff16df25/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	//"github.com/RobotsAndPencils/go-saml"
     5  	jwt "github.com/appleboy/gin-jwt/v2"
     6  	"github.com/gin-contrib/location"
     7  	"github.com/gin-gonic/gin"
     8  	_ "github.com/in4it/ecs-deploy/docs"
     9  	"github.com/in4it/ecs-deploy/ipfilter"
    10  	"github.com/in4it/ecs-deploy/ngserve"
    11  	"github.com/in4it/ecs-deploy/provider/ecs"
    12  	"github.com/in4it/ecs-deploy/service"
    13  	"github.com/in4it/ecs-deploy/session"
    14  	"github.com/in4it/ecs-deploy/util"
    15  	"github.com/juju/loggo"
    16  	sns "github.com/robbiet480/go.sns"
    17  	swaggerfiles "github.com/swaggo/files"     // swagger embed files
    18  	ginSwagger "github.com/swaggo/gin-swagger" // gin-swagger middleware
    19  
    20  	"encoding/json"
    21  	"errors"
    22  	"net/http"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  )
    27  
    28  // logging
    29  var apiLogger = loggo.GetLogger("api")
    30  
    31  // version
    32  var apiVersion = "1.2"
    33  
    34  // API struct
    35  type API struct {
    36  	authMiddleware *jwt.GinJWTMiddleware
    37  	//sp             saml.ServiceProviderSettings
    38  	samlHelper   *SAML
    39  	asController AutoscalingController
    40  }
    41  
    42  type User struct {
    43  	UserID string
    44  }
    45  type login struct {
    46  	Username string `form:"username" json:"username" binding:"required"`
    47  	Password string `form:"password" json:"password" binding:"required"`
    48  }
    49  
    50  func (a *API) Launch() error {
    51  	if util.GetEnv("SAML_ENABLED", "") == "yes" {
    52  		err := a.initSAML()
    53  		if err != nil {
    54  			return err
    55  		}
    56  	}
    57  
    58  	a.asController = AutoscalingController{}
    59  
    60  	a.createAuthMiddleware()
    61  	a.createRoutes()
    62  
    63  	return nil
    64  }
    65  
    66  func (a *API) initSAML() error {
    67  	// initialize samlHelper
    68  	var err error
    69  	a.samlHelper, err = newSAML(util.GetEnv("SAML_METADATA_URL", ""), []byte(util.GetEnv("SAML_CERTIFICATE", "")), []byte(util.GetEnv("SAML_PRIVATE_KEY", "")))
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func (a *API) createRoutes() {
    78  	// create
    79  	r := gin.Default()
    80  
    81  	// location
    82  	r.Use(location.Default())
    83  
    84  	// cookie sessions
    85  	r.Use(session.SessionHandler("ecs-deploy", util.GetEnv("JWT_SECRET", "unsecure secret key 8a045eb")))
    86  
    87  	// prefix
    88  	prefix := util.GetEnv("URL_PREFIX", "")
    89  	apiPrefix := prefix + util.GetEnv("URL_PREFIX_API", "/api/v1")
    90  
    91  	// ip whitelisting
    92  	r.Use(ipfilter.IPWhiteList(util.GetEnv("ECS_WHITELIST", "0.0.0.0/0")))
    93  
    94  	// webapp
    95  	r.Use(ngserve.ServeWithDefault(prefix+"/webapp", ngserve.LocalFile("./webapp/dist", false), "./webapp/dist/index.html"))
    96  
    97  	auth := r.Group(apiPrefix)
    98  	auth.Use(a.authMiddleware.MiddlewareFunc())
    99  	{
   100  		// frontend redirect
   101  		r.GET(prefix, a.redirectFrontendHandler)
   102  		r.GET(prefix+"/", a.redirectFrontendHandler)
   103  
   104  		// health check
   105  		r.GET(prefix+"/health", a.healthHandler)
   106  
   107  		// saml init
   108  		if util.GetEnv("SAML_ENABLED", "") == "yes" {
   109  			r.POST(prefix+"/saml/acs", a.samlHelper.samlInitHandler)
   110  			r.GET(prefix+"/saml/acs", a.samlHelper.samlInitHandler)
   111  		}
   112  		r.GET(prefix+"/saml/enabled", a.samlHelper.samlEnabledHandler)
   113  
   114  		// swagger
   115  		r.GET(prefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
   116  
   117  		// webhook
   118  		r.POST(prefix+"/webhook", a.webhookHandler)
   119  
   120  		// login handlers
   121  		r.POST(prefix+"/login", a.authMiddleware.LoginHandler)
   122  
   123  		// health with auth
   124  		auth.GET("/health", a.healthHandler)
   125  
   126  		// refresh token
   127  		auth.GET("/refresh_token", a.authMiddleware.RefreshHandler)
   128  
   129  		// ECR
   130  		auth.POST("/ecr/create/:repository", a.ecrCreateHandler)
   131  
   132  		// Deploy
   133  		auth.POST("/deploy/:service", a.deployServiceHandler)
   134  		auth.POST("/deploy", a.deployServicesHandler)
   135  
   136  		// Redeploy existing version
   137  		auth.POST("/deploy/:service/:time", a.redeployServiceHandler)
   138  
   139  		// Export
   140  		auth.GET("/export/terraform", a.exportTerraformHandler)
   141  		auth.GET("/export/terraform/:service/targetgrouparn", a.exportTerraformTargetGroupArnHandler)
   142  		auth.GET("/export/terraform/:service/listenerrulearn", a.exportTerraformListenerRuleArnsHandler)
   143  		auth.GET("/export/terraform/:service/listenerrulearn/:rule", a.exportTerraformListenerRuleArnHandler)
   144  
   145  		// deploy list
   146  		auth.GET("/deploy/list", a.listDeploysHandler)
   147  		auth.GET("/deploy/list/:service", a.listDeploysForServiceHandler)
   148  		auth.GET("/deploy/status/:service/:time", a.getDeploymentStatusHandler)
   149  		auth.GET("/deploy/get/:service/:time", a.getDeploymentHandler)
   150  		// service list
   151  		auth.GET("/service/list", a.listServicesHandler)
   152  		// service list
   153  		auth.GET("/service/describe", a.describeServicesHandler)
   154  		// get service information
   155  		auth.GET("/service/describe/:service", a.describeServiceHandler)
   156  		// get version information
   157  		auth.GET("/service/describe/:service/versions", a.describeServiceVersionsHandler)
   158  		// scale service
   159  		auth.POST("/service/scale/:service/:count", a.scaleServiceHandler)
   160  		// run task
   161  		auth.POST("/service/runtask/:service", a.runTaskHandler)
   162  		// get taskdefinition
   163  		auth.GET("/service/describe/:service/taskdefinition", a.describeServiceTaskdefinitionHandler)
   164  		// get all tasks
   165  		auth.GET("/service/describe/:service/tasks", a.describeTasksHandler)
   166  
   167  		// parameter store
   168  		auth.GET("/service/parameter/:service/list", a.listServiceParametersHandler)
   169  		auth.POST("/service/parameter/:service/put", a.putServiceParameterHandler)
   170  		auth.POST("/service/parameter/:service/delete/:parameter", a.deleteServiceParameterHandler)
   171  
   172  		// cloudwatch logs
   173  		auth.GET("/service/log/:service/get/:taskarn/:container/:start/:end", a.getServiceLogsHandler)
   174  
   175  		// service autoscaling
   176  		auth.POST("/service/autoscaling/:service/put", a.putServiceAutoscalingHandler)
   177  		auth.GET("/service/autoscaling/:service/get", a.getServiceAutoscalingHandler)
   178  		auth.POST("/service/autoscaling/:service/delete/:policyname", a.deleteServiceAutoscalingPolicyHandler)
   179  		auth.POST("/service/autoscaling/:service/delete", a.deleteServiceAutoscalingHandler)
   180  	}
   181  
   182  	// run API
   183  	r.Run()
   184  }
   185  
   186  // @summary login to receive jwt token
   187  // @description login with user and password to receive jwt token
   188  // @id login
   189  // @accept  json
   190  // @produce  json
   191  // @router /login [post]
   192  func (a *API) createAuthMiddleware() {
   193  	var (
   194  		identityKey = "id"
   195  		err         error
   196  	)
   197  	a.authMiddleware, err = jwt.New(&jwt.GinJWTMiddleware{
   198  		Realm:            "ecs-deploy",
   199  		Key:              []byte(util.GetEnv("JWT_SECRET", "unsecure secret key 8a045eb")),
   200  		SigningAlgorithm: "HS256",
   201  		Timeout:          time.Hour,
   202  		MaxRefresh:       time.Hour,
   203  		IdentityKey:      identityKey,
   204  		PayloadFunc: func(data interface{}) jwt.MapClaims {
   205  			if v, ok := data.(*User); ok {
   206  				return jwt.MapClaims{
   207  					identityKey: v.UserID,
   208  				}
   209  			}
   210  			return jwt.MapClaims{}
   211  		},
   212  		IdentityHandler: func(c *gin.Context) interface{} {
   213  			claims := jwt.ExtractClaims(c)
   214  			return &User{
   215  				UserID: claims["id"].(string),
   216  			}
   217  		},
   218  		Authenticator: func(c *gin.Context) (interface{}, error) {
   219  			var loginVals login
   220  			if err := c.ShouldBind(&loginVals); err != nil {
   221  				return "", jwt.ErrMissingLoginValues
   222  			}
   223  			userID := loginVals.Username
   224  			password := loginVals.Password
   225  
   226  			if (userID == "deploy" && password == util.GetEnv("DEPLOY_PASSWORD", "deploy")) || (userID == "developer" && password == util.GetEnv("DEVELOPER_PASSWORD", "developer")) {
   227  				return &User{UserID: userID}, nil
   228  			}
   229  
   230  			return nil, jwt.ErrFailedAuthentication
   231  		},
   232  		Authorizator: func(data interface{}, c *gin.Context) bool {
   233  			if v, ok := data.(*User); ok && v.UserID != "" {
   234  				return true
   235  			}
   236  
   237  			return false
   238  		},
   239  		Unauthorized: func(c *gin.Context, code int, message string) {
   240  			c.JSON(code, gin.H{
   241  				"code":    code,
   242  				"message": message,
   243  			})
   244  		},
   245  		// TokenLookup is a string in the form of "<source>:<name>" that is used
   246  		// to extract token from the request.
   247  		// Optional. Default value "header:Authorization".
   248  		// Possible values:
   249  		// - "header:<name>"
   250  		// - "query:<name>"
   251  		// - "cookie:<name>"
   252  		TokenLookup: "header:Authorization",
   253  		// TokenLookup: "query:token",
   254  		// TokenLookup: "cookie:token",
   255  
   256  		// TokenHeadName is a string in the header. Default value is "Bearer"
   257  		TokenHeadName: "Bearer",
   258  
   259  		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
   260  		TimeFunc: time.Now,
   261  	})
   262  	if err != nil {
   263  		panic(err)
   264  	}
   265  }
   266  
   267  // @summary Create ECR repository
   268  // @description Creates AWS ECR (Docker) repository using repository name as parameter
   269  // @id ecr-create-repository
   270  // @accept  json
   271  // @produce  json
   272  // @param   repository     path    string     true        "repository"
   273  // @router /api/v1/ecr/create/{repository} [post]
   274  func (a *API) ecrCreateHandler(c *gin.Context) {
   275  	controller := Controller{}
   276  	res, err := controller.createRepository(c.Param("repository"))
   277  	if err == nil {
   278  		c.JSON(200, gin.H{
   279  			"message": res,
   280  		})
   281  	} else {
   282  		c.JSON(200, gin.H{
   283  			"error": err.Error(),
   284  		})
   285  	}
   286  }
   287  
   288  // @summary Healthcheck
   289  // @description Healthcheck for loadbalancer
   290  // @id healthcheck
   291  // @accept  json
   292  // @produce  json
   293  // @router /api/v1/healthcheck [get]
   294  func (a *API) healthHandler(c *gin.Context) {
   295  	c.JSON(200, gin.H{
   296  		"message": "OK",
   297  	})
   298  }
   299  
   300  // @summary Deploy service to ECS
   301  // @description Deploy a service to ECS
   302  // @id ecs-deploy-service
   303  // @accept  json
   304  // @produce  json
   305  // @param   service         path    string     true        "service name"
   306  // @router /api/v1/deploy/{service} [post]
   307  func (a *API) deployServiceHandler(c *gin.Context) {
   308  	var json service.Deploy
   309  	controller := Controller{}
   310  	service.SetDeployDefaults(&json)
   311  	if err := c.ShouldBindJSON(&json); err == nil {
   312  		if err = a.deployServiceValidator(c.Param("service"), json); err == nil {
   313  			res, err := controller.Deploy(c.Param("service"), json)
   314  			if err == nil {
   315  				c.JSON(200, gin.H{
   316  					"message": res,
   317  				})
   318  			} else {
   319  				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   320  			}
   321  		} else {
   322  			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   323  		}
   324  	} else {
   325  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   326  	}
   327  }
   328  
   329  // @summary Deploy services to ECS
   330  // @description Deploy services to ECS
   331  // @id ecs-deploy-service
   332  // @accept  json
   333  // @produce  json
   334  // @router /api/v1/deploy [post]
   335  func (a *API) deployServicesHandler(c *gin.Context) {
   336  	var json service.DeployServices
   337  	var errors map[string]string
   338  	var results []*service.DeployResult
   339  	var err error
   340  	var res *service.DeployResult
   341  	var failures int
   342  	errors = make(map[string]string)
   343  	controller := Controller{}
   344  	if err = c.ShouldBindJSON(&json); err == nil {
   345  		for i, v := range json.Services {
   346  			if err = a.deployServiceValidator(v.ServiceName, json.Services[i]); err == nil {
   347  				res, err = controller.Deploy(v.ServiceName, json.Services[i])
   348  				if err == nil {
   349  					results = append(results, res)
   350  				}
   351  			}
   352  			if err != nil {
   353  				failures += 1
   354  				errors[v.ServiceName] = err.Error()
   355  			}
   356  		}
   357  		c.JSON(200, gin.H{
   358  			"messages": results,
   359  			"failures": failures,
   360  			"errors":   errors,
   361  		})
   362  	} else {
   363  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   364  	}
   365  }
   366  
   367  // @summary Redeploy existing service to ECS
   368  // @description Redeploy existing service to ECS
   369  // @id ecs-redeploy-service
   370  // @accept  json
   371  // @produce  json
   372  // @param   service         path    string     true        "service name"
   373  // @param   time            path    time       true        "timestamp"
   374  // @router /api/v1/deploy/{service} [post]
   375  func (a *API) redeployServiceHandler(c *gin.Context) {
   376  	controller := Controller{}
   377  	res, err := controller.redeploy(c.Param("service"), c.Param("time"))
   378  	if err == nil {
   379  		c.JSON(200, gin.H{
   380  			"message": res,
   381  		})
   382  	} else {
   383  		c.JSON(200, gin.H{
   384  			"error": err.Error(),
   385  		})
   386  	}
   387  }
   388  
   389  func (a *API) deployServiceValidator(serviceName string, d service.Deploy) error {
   390  	if len(serviceName) < 3 {
   391  		return errors.New("service name needs to be at least 3 characters")
   392  	}
   393  	if strings.ToLower(d.ServiceProtocol) != "none" && d.ServicePort == 0 {
   394  		return errors.New("ServicePort needs to be set if ServiceProtocol is not set to none.")
   395  	}
   396  	t := false
   397  	for _, container := range d.Containers {
   398  		if container.ContainerName == serviceName {
   399  			t = true
   400  		}
   401  	}
   402  	if !t {
   403  		return errors.New("At least one container needs to have the same name as the service (" + serviceName + ")")
   404  	}
   405  
   406  	return nil
   407  }
   408  
   409  // @summary Export current services to terraform
   410  // @description Export service data stored in dynamodb into terraform tf files
   411  // @id export-terraform
   412  // @produce  json
   413  // @router /api/v1/export/terraform [get]
   414  func (a *API) exportTerraformHandler(c *gin.Context) {
   415  	e := Export{}
   416  	exp, err := e.terraform()
   417  	if err == nil {
   418  		if exp == nil {
   419  			c.JSON(200, gin.H{
   420  				"export": "",
   421  			})
   422  		} else {
   423  			c.JSON(200, gin.H{
   424  				"export": *exp,
   425  			})
   426  		}
   427  	} else {
   428  		c.JSON(200, gin.H{
   429  			"error": err.Error(),
   430  		})
   431  	}
   432  }
   433  
   434  // @summary Export targetgroup arn
   435  // @description Export target group arn stored in dynamodb into terraform tf files
   436  // @id export-terraform-targetgroup-arn
   437  // @produce  json
   438  // @router /api/v1/export/terraform/:service/targetgrouparn [get]
   439  func (a *API) exportTerraformTargetGroupArnHandler(c *gin.Context) {
   440  	e := Export{}
   441  	targetGroupArn, err := e.getTargetGroupArn(c.Param("service"))
   442  	if err == nil && targetGroupArn != nil {
   443  		c.JSON(200, gin.H{
   444  			"targetGroupArn": targetGroupArn,
   445  		})
   446  	} else {
   447  		c.JSON(200, gin.H{
   448  			"error": err.Error(),
   449  		})
   450  	}
   451  }
   452  
   453  // @summary Export listener rule arn
   454  // @description Export listener rule arn
   455  // @id export-terraform-listener-rule-arn
   456  // @produce  json
   457  // @router /api/v1/export/terraform/:service/listenerrulearn/:rule [get]
   458  func (a *API) exportTerraformListenerRuleArnHandler(c *gin.Context) {
   459  	e := Export{}
   460  	listenerRuleArn, err := e.getListenerRuleArn(c.Param("service"), c.Param("rule"))
   461  	if err == nil && listenerRuleArn != nil {
   462  		c.JSON(200, gin.H{
   463  			"listenerRuleArn": listenerRuleArn,
   464  		})
   465  	} else {
   466  		c.JSON(200, gin.H{
   467  			"error": err.Error(),
   468  		})
   469  	}
   470  }
   471  
   472  // @summary Export listener rule arns
   473  // @description Export listener rule arns
   474  // @id export-terraform-listener-rule-arns
   475  // @produce  json
   476  // @router /api/v1/export/terraform/:service/listenerrulearn [get]
   477  func (a *API) exportTerraformListenerRuleArnsHandler(c *gin.Context) {
   478  	e := Export{}
   479  	listenerRuleArns, err := e.getListenerRuleArns(c.Param("service"))
   480  	if err == nil && listenerRuleArns != nil {
   481  		c.JSON(200, gin.H{
   482  			"listenerRuleKeys": listenerRuleArns.RuleKeys,
   483  			"listenerRules":    listenerRuleArns.Rules,
   484  		})
   485  	} else {
   486  		c.JSON(200, gin.H{
   487  			"error": err.Error(),
   488  		})
   489  	}
   490  }
   491  func (a *API) listDeploysHandler(c *gin.Context) {
   492  	controller := Controller{}
   493  	deploys, err := controller.getDeploys()
   494  	if err == nil {
   495  		c.JSON(200, gin.H{
   496  			"deployments": deploys,
   497  		})
   498  	} else {
   499  		c.JSON(200, gin.H{
   500  			"error": err.Error(),
   501  		})
   502  	}
   503  }
   504  func (a *API) listDeploysForServiceHandler(c *gin.Context) {
   505  	controller := Controller{}
   506  	deploys, err := controller.getDeploysForService(c.Param("service"))
   507  	if err == nil {
   508  		c.JSON(200, gin.H{
   509  			"deployments": deploys,
   510  		})
   511  	} else {
   512  		c.JSON(200, gin.H{
   513  			"error": err.Error(),
   514  		})
   515  	}
   516  }
   517  func (a *API) listServicesHandler(c *gin.Context) {
   518  	controller := Controller{}
   519  	services, err := controller.getServices()
   520  	if err == nil {
   521  		c.JSON(200, gin.H{
   522  			"services": services,
   523  		})
   524  	} else {
   525  		c.JSON(200, gin.H{
   526  			"error": err.Error(),
   527  		})
   528  	}
   529  }
   530  
   531  func (a *API) describeServicesHandler(c *gin.Context) {
   532  	controller := Controller{}
   533  	services, err := controller.describeServices()
   534  	if err == nil {
   535  		c.JSON(200, gin.H{
   536  			"services": services,
   537  		})
   538  	} else {
   539  		c.JSON(200, gin.H{
   540  			"error": err.Error(),
   541  		})
   542  	}
   543  }
   544  func (a *API) describeServiceHandler(c *gin.Context) {
   545  	controller := Controller{}
   546  	service, err := controller.describeService(c.Param("service"))
   547  	if err == nil {
   548  		c.JSON(200, gin.H{
   549  			"service": service,
   550  		})
   551  	} else {
   552  		c.JSON(200, gin.H{
   553  			"error": err.Error(),
   554  		})
   555  	}
   556  }
   557  func (a *API) describeServiceVersionsHandler(c *gin.Context) {
   558  	controller := Controller{}
   559  	versions, err := controller.describeServiceVersions(c.Param("service"))
   560  	if err == nil {
   561  		c.JSON(200, gin.H{
   562  			"versions": versions,
   563  		})
   564  	} else {
   565  		c.JSON(200, gin.H{
   566  			"error": err.Error(),
   567  		})
   568  	}
   569  }
   570  func (a *API) getDeploymentStatusHandler(c *gin.Context) {
   571  	controller := Controller{}
   572  	service, err := controller.getDeploymentStatus(c.Param("service"), c.Param("time"))
   573  	if err == nil {
   574  		c.JSON(200, gin.H{
   575  			"service": service,
   576  		})
   577  	} else {
   578  		c.JSON(200, gin.H{
   579  			"error": err.Error(),
   580  		})
   581  	}
   582  }
   583  func (a *API) getDeploymentHandler(c *gin.Context) {
   584  	controller := Controller{}
   585  	deployment, err := controller.getDeployment(c.Param("service"), c.Param("time"))
   586  	if err == nil {
   587  		c.JSON(200, gin.H{
   588  			"deployment": deployment,
   589  		})
   590  	} else {
   591  		c.JSON(200, gin.H{
   592  			"error": err.Error(),
   593  		})
   594  	}
   595  }
   596  
   597  func (a *API) redirectFrontendHandler(c *gin.Context) {
   598  	c.Redirect(http.StatusMovedPermanently, util.GetEnv("URL_PREFIX", "")+"/webapp/")
   599  }
   600  
   601  func (a *API) listServiceParametersHandler(c *gin.Context) {
   602  	var creds string
   603  	claims := jwt.ExtractClaims(c)
   604  	controller := Controller{}
   605  	session, sessionExists := session.RetrieveSession(c)
   606  	if sessionExists {
   607  		if c, ok := session.Get("paramstore_creds").(string); ok {
   608  			creds = c
   609  		}
   610  	}
   611  	parameters, creds, err := controller.getServiceParameters(c.Param("service"), claims["id"].(string), creds)
   612  	session.Set("paramstore_creds", creds)
   613  	session.Save()
   614  	if err == nil {
   615  		c.JSON(200, gin.H{
   616  			"parameters": parameters,
   617  		})
   618  	} else {
   619  		c.JSON(200, gin.H{
   620  			"error": err.Error(),
   621  		})
   622  	}
   623  }
   624  func (a *API) putServiceParameterHandler(c *gin.Context) {
   625  	var json service.DeployServiceParameter
   626  	var creds string
   627  	claims := jwt.ExtractClaims(c)
   628  	controller := Controller{}
   629  	session, sessionExists := session.RetrieveSession(c)
   630  	if sessionExists {
   631  		if c, ok := session.Get("paramstore_creds").(string); ok {
   632  			creds = c
   633  		}
   634  	}
   635  	if err := c.ShouldBindJSON(&json); err == nil {
   636  		res, creds, err := controller.putServiceParameter(c.Param("service"), claims["id"].(string), creds, json)
   637  		session.Set("paramstore_creds", creds)
   638  		session.Save()
   639  		if err == nil {
   640  			c.JSON(200, gin.H{
   641  				"parameters": res,
   642  			})
   643  		} else {
   644  			c.JSON(200, gin.H{
   645  				"error": err.Error(),
   646  			})
   647  		}
   648  	} else {
   649  		c.JSON(200, gin.H{
   650  			"error": "Invalid input",
   651  		})
   652  	}
   653  }
   654  func (a *API) deleteServiceParameterHandler(c *gin.Context) {
   655  	var creds string
   656  	claims := jwt.ExtractClaims(c)
   657  	controller := Controller{}
   658  	session, sessionExists := session.RetrieveSession(c)
   659  	if sessionExists {
   660  		if c, ok := session.Get("paramstore_creds").(string); ok {
   661  			creds = c
   662  		}
   663  	}
   664  	creds, err := controller.deleteServiceParameter(c.Param("service"), claims["id"].(string), creds, c.Param("parameter"))
   665  	session.Set("paramstore_creds", creds)
   666  	session.Save()
   667  	if err == nil {
   668  		c.JSON(200, gin.H{
   669  			"message": "OK",
   670  		})
   671  	} else {
   672  		c.JSON(200, gin.H{
   673  			"error": err.Error(),
   674  		})
   675  	}
   676  }
   677  func (a *API) scaleServiceHandler(c *gin.Context) {
   678  	controller := Controller{}
   679  	desiredCount, err := strconv.ParseInt(c.Param("count"), 10, 64)
   680  	if err != nil {
   681  		c.JSON(200, gin.H{
   682  			"error": err.Error(),
   683  		})
   684  		return
   685  	}
   686  	err = controller.scaleService(c.Param("service"), desiredCount)
   687  	if err == nil {
   688  		c.JSON(200, gin.H{
   689  			"message": "OK",
   690  		})
   691  	} else {
   692  		c.JSON(200, gin.H{
   693  			"error": err.Error(),
   694  		})
   695  	}
   696  }
   697  
   698  func (a *API) webhookHandler(c *gin.Context) {
   699  	var err error
   700  
   701  	snsMessageType := c.GetHeader("x-amz-sns-message-type")
   702  	apiLogger.Tracef("Checking message type: %v", snsMessageType)
   703  	var snsPayload sns.Payload
   704  	if err = c.ShouldBindJSON(&snsPayload); err == nil {
   705  		err = snsPayload.VerifyPayload()
   706  		if err == nil {
   707  			apiLogger.Tracef("Verified Payload.")
   708  			if snsMessageType == "SubscriptionConfirmation" {
   709  				apiLogger.Debugf("Subscribing...")
   710  				_, err = snsPayload.Subscribe()
   711  			} else if snsMessageType == "Notification" {
   712  				apiLogger.Debugf("Incoming Notification")
   713  				var genericMessage ecs.SNSPayloadGeneric
   714  				if err = json.Unmarshal([]byte(snsPayload.Message), &genericMessage); err == nil {
   715  					apiLogger.Tracef("Message detail type: %v", genericMessage.DetailType)
   716  					if genericMessage.DetailType == "ECS Container Instance State Change" {
   717  						var ecsMessage ecs.SNSPayloadEcs
   718  						if err = json.Unmarshal([]byte(snsPayload.Message), &ecsMessage); err == nil {
   719  							apiLogger.Tracef("ECS Message: %v", snsPayload.Message)
   720  							err = a.asController.processEcsMessage(ecsMessage)
   721  						}
   722  					} else if genericMessage.DetailType == "EC2 Instance-terminate Lifecycle Action" {
   723  						var lifecycleMessage ecs.SNSPayloadLifecycle
   724  						if err = json.Unmarshal([]byte(snsPayload.Message), &lifecycleMessage); err == nil {
   725  							apiLogger.Debugf("Lifecycle Message: %v", snsPayload.Message)
   726  							err = a.asController.processLifecycleMessage(lifecycleMessage)
   727  						}
   728  					}
   729  				}
   730  			} else {
   731  				err = errors.New("MessageType not recognized")
   732  			}
   733  		}
   734  	}
   735  	if err == nil {
   736  		c.JSON(200, gin.H{
   737  			"message": "OK",
   738  		})
   739  	} else {
   740  		apiLogger.Errorf("Error: %v", err.Error())
   741  		c.JSON(200, gin.H{
   742  			"error": err.Error(),
   743  		})
   744  	}
   745  }
   746  func (a *API) runTaskHandler(c *gin.Context) {
   747  	var json service.RunTask
   748  	controller := Controller{}
   749  	if err := c.ShouldBindJSON(&json); err == nil {
   750  		claims := jwt.ExtractClaims(c)
   751  		json.StartedBy = strings.Replace(claims["id"].(string), "@", "-", -1)
   752  		taskArn, err := controller.runTask(c.Param("service"), json)
   753  		if err == nil {
   754  			c.JSON(200, gin.H{
   755  				"taskArn": taskArn,
   756  			})
   757  		} else {
   758  			c.JSON(200, gin.H{
   759  				"error": err.Error(),
   760  			})
   761  		}
   762  	} else {
   763  		c.JSON(200, gin.H{
   764  			"error": err.Error(),
   765  		})
   766  	}
   767  }
   768  func (a *API) describeServiceTaskdefinitionHandler(c *gin.Context) {
   769  	controller := Controller{}
   770  	taskDefinition, err := controller.describeTaskDefinition(c.Param("service"))
   771  	if err == nil {
   772  		c.JSON(200, gin.H{
   773  			"taskDefinition": taskDefinition,
   774  		})
   775  	} else {
   776  		c.JSON(200, gin.H{
   777  			"error": err.Error(),
   778  		})
   779  	}
   780  }
   781  func (a *API) describeTasksHandler(c *gin.Context) {
   782  	controller := Controller{}
   783  	taskArns, err := controller.listTasks(c.Param("service"))
   784  	if err == nil {
   785  		c.JSON(200, gin.H{
   786  			"tasks": taskArns,
   787  		})
   788  	} else {
   789  		c.JSON(200, gin.H{
   790  			"error": err.Error(),
   791  		})
   792  	}
   793  }
   794  func (a *API) getServiceLogsHandler(c *gin.Context) {
   795  	controller := Controller{}
   796  	layout := "2006-01-02T15:04:05.9Z"
   797  	start, err := time.Parse(layout, c.Param("start"))
   798  	if err != nil {
   799  		c.JSON(200, gin.H{
   800  			"error": "Can't parse start date: " + err.Error(),
   801  		})
   802  		return
   803  	}
   804  	end, err := time.Parse(layout, c.Param("end"))
   805  	if err != nil {
   806  		c.JSON(200, gin.H{
   807  			"error": "Can't parse end date",
   808  		})
   809  		return
   810  	}
   811  	logs, err := controller.getServiceLogs(c.Param("service"), c.Param("taskarn"), c.Param("container"), start, end)
   812  	if err == nil {
   813  		c.JSON(200, gin.H{
   814  			"logs": logs,
   815  		})
   816  	} else {
   817  		c.JSON(200, gin.H{
   818  			"error": err.Error(),
   819  		})
   820  	}
   821  }
   822  func (a *API) putServiceAutoscalingHandler(c *gin.Context) {
   823  	var json service.Autoscaling
   824  	controller := Controller{}
   825  	if err := c.ShouldBindJSON(&json); err == nil {
   826  		result, err := controller.putServiceAutoscaling(c.Param("service"), json)
   827  		if err == nil {
   828  			c.JSON(200, gin.H{
   829  				"autoscaling": result,
   830  			})
   831  		} else {
   832  			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   833  		}
   834  	} else {
   835  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   836  	}
   837  }
   838  func (a *API) getServiceAutoscalingHandler(c *gin.Context) {
   839  	controller := Controller{}
   840  	result, err := controller.getServiceAutoscaling(c.Param("service"))
   841  	if err == nil {
   842  		c.JSON(200, gin.H{
   843  			"autoscaling": result,
   844  		})
   845  	} else {
   846  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   847  	}
   848  }
   849  func (a *API) deleteServiceAutoscalingPolicyHandler(c *gin.Context) {
   850  	controller := Controller{}
   851  	err := controller.deleteServiceAutoscalingPolicy(c.Param("service"), c.Param("policyname"))
   852  	if err == nil {
   853  		c.JSON(200, gin.H{
   854  			"autoscaling": "deleted",
   855  		})
   856  	} else {
   857  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   858  	}
   859  }
   860  
   861  func (a *API) deleteServiceAutoscalingHandler(c *gin.Context) {
   862  	controller := Controller{}
   863  	err := controller.deleteServiceAutoscaling(c.Param("service"))
   864  	if err == nil {
   865  		c.JSON(200, gin.H{
   866  			"autoscaling": "deleted",
   867  		})
   868  	} else {
   869  		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   870  	}
   871  }