├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── ContributorAgreement.txt ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── client ├── .eslintrc.js ├── .jscsrc ├── STYLEGUIDE.md ├── app │ ├── app.module.js │ ├── common │ │ ├── MainController.js │ │ ├── common-modal │ │ │ ├── CommonModalController.js │ │ │ └── common-modal.html │ │ ├── common.module.js │ │ ├── directives │ │ │ ├── cronEditor.html │ │ │ ├── cronEditor.js │ │ │ ├── defaultIfEmptyDirective.js │ │ │ ├── healthStatus.js │ │ │ ├── jsonDiffPatchDirective.js │ │ │ ├── loadingOverlay.js │ │ │ ├── multipleEmails.js │ │ │ ├── scalingCronEditor.html │ │ │ ├── scalingCronEditor.js │ │ │ ├── scalingScheduleEditor.html │ │ │ ├── scalingScheduleEditor.js │ │ │ ├── scheduleEditor.html │ │ │ ├── scheduleEditor.js │ │ │ ├── scheduleViewer.html │ │ │ ├── scheduleViewer.js │ │ │ ├── spinner.js │ │ │ ├── uniqueAmongDirective.js │ │ │ └── validJsonDirective.js │ │ ├── launch-config │ │ │ ├── asg-size.html │ │ │ └── launch-config.html │ │ ├── loginForm.html │ │ ├── loginForm.js │ │ ├── models │ │ │ ├── AutoScalingGroup.js │ │ │ ├── Deployment.js │ │ │ ├── DeploymentMap.js │ │ │ ├── Environment.js │ │ │ ├── Image.js │ │ │ └── UpstreamConfig.js │ │ ├── services │ │ │ ├── accountMappingService.js │ │ │ ├── asgService.js │ │ │ ├── awsService.js │ │ │ ├── cacheService.js │ │ │ ├── cachedResourcesService.js │ │ │ ├── comparisons.js │ │ │ ├── enumsService.js │ │ │ ├── environmentDeployService.js │ │ │ ├── environmentStorageService.js │ │ │ ├── instancesService.js │ │ │ ├── linkHeaderService.js │ │ │ ├── loadBalancerService.js │ │ │ ├── loading.js │ │ │ ├── localResourceFactoryService.js │ │ │ ├── localStorageService.js │ │ │ ├── modalService.js │ │ │ ├── releaseNotesService.js │ │ │ ├── remoteResourceFactoryService.js │ │ │ ├── resourcesService.js │ │ │ ├── rolesService.js │ │ │ ├── schemaValidatorService.js │ │ │ ├── serviceDiscovery.js │ │ │ ├── storageServiceFactory.js │ │ │ ├── taggable.js │ │ │ ├── targetStateService.js │ │ │ ├── teamStorageService.js │ │ │ └── upstreamService.js │ │ ├── utilities.js │ │ └── utils │ │ │ ├── QuerySync.js │ │ │ └── QuerySync.spec.js │ ├── compare │ │ ├── CompareController.js │ │ ├── compare-objects.html │ │ ├── compare-services.html │ │ ├── compare.html │ │ ├── compare.module.js │ │ ├── diff-viewer │ │ │ ├── DiffViewerController.js │ │ │ └── diff-viewer.html │ │ ├── directives │ │ │ ├── serviceCell.html │ │ │ └── serviceCell.js │ │ └── services │ │ │ ├── ResourceComparison.js │ │ │ ├── ResourceComparison.spec.js │ │ │ ├── comparableResources.js │ │ │ ├── serviceComparison.js │ │ │ └── upstreamService.js │ ├── configuration │ │ ├── accounts │ │ │ ├── AccountController.js │ │ │ ├── AccountsController.js │ │ │ ├── account.html │ │ │ └── accounts.html │ │ ├── audit │ │ │ ├── AuditCompareModalController.js │ │ │ ├── AuditController.js │ │ │ ├── audit-compare-modal.html │ │ │ └── audit.html │ │ ├── clusters │ │ │ ├── ClusterController.js │ │ │ ├── ClustersController.js │ │ │ ├── cluster.html │ │ │ └── clusters.html │ │ ├── configuration.module.js │ │ ├── deployment-maps │ │ │ ├── DeploymentMapController.js │ │ │ ├── DeploymentMapCreateController.js │ │ │ ├── DeploymentMapTargetController.js │ │ │ ├── DeploymentMapsController.js │ │ │ ├── copyServerRole.html │ │ │ ├── copyServerRole.js │ │ │ ├── deployment-map.html │ │ │ ├── deployment-maps-create-modal.html │ │ │ ├── deployment-maps-target-modal.html │ │ │ └── deployment-maps.html │ │ ├── em-services │ │ │ ├── ServiceController.js │ │ │ ├── ServicesController.js │ │ │ ├── portService.js │ │ │ ├── service.html │ │ │ └── services.html │ │ ├── environment-types │ │ │ ├── EnvironmentTypeController.js │ │ │ ├── EnvironmentTypesController.js │ │ │ ├── environment-type.html │ │ │ └── environment-types.html │ │ ├── export │ │ │ ├── ExportController.js │ │ │ └── export.html │ │ ├── import │ │ │ ├── ImportController.js │ │ │ └── import.html │ │ ├── lbupstreams │ │ │ ├── lbupstream.html │ │ │ ├── lbupstreams.html │ │ │ ├── upstreamViewModel.js │ │ │ └── upstreamViewModel.spec.js │ │ ├── load-balancers │ │ │ ├── LBCloneController.js │ │ │ ├── LBController.js │ │ │ ├── LBUpstreamController.js │ │ │ ├── LBUpstreamsController.js │ │ │ ├── LBsController.js │ │ │ ├── lb-clone-modal.html │ │ │ ├── loadbalancer.html │ │ │ └── loadbalancers.html │ │ ├── notification-settings │ │ │ ├── notificationSettingsEntry.html │ │ │ ├── notificationSettingsEntry.js │ │ │ ├── notificationSettingsList.html │ │ │ └── notificationSettingsList.js │ │ ├── permissions │ │ │ ├── PermissionController.js │ │ │ ├── PermissionsController.js │ │ │ ├── permission.html │ │ │ └── permissions.html │ │ ├── pick-ami │ │ │ ├── PickAmiController.js │ │ │ └── pickami-modal.html │ │ ├── popovers │ │ │ ├── multiple-values.html │ │ │ ├── notification-settings-paging.html │ │ │ └── notification-settings.html │ │ └── services │ │ │ ├── arrayItemHashDetectorService.js │ │ │ ├── deploymentMapConverter.js │ │ │ ├── lbBulkOperationService.js │ │ │ ├── permissionsValidationService.js │ │ │ └── permissionsValidationService.spec.js │ ├── environments │ │ ├── dialogs │ │ │ ├── ASGDetailsModalController.js │ │ │ ├── CreateEnvironmentController.js │ │ │ ├── DeployModalController.js │ │ │ ├── LaunchConfigConfirmationController.js │ │ │ ├── asg │ │ │ │ ├── asgDistribution.js │ │ │ │ ├── asgInstances.html │ │ │ │ ├── asgInstances.js │ │ │ │ ├── asgServices.html │ │ │ │ ├── asgServices.js │ │ │ │ ├── asgSingleInstance.html │ │ │ │ ├── asgSingleInstance.js │ │ │ │ ├── asgSingleService.html │ │ │ │ ├── asgSingleService.js │ │ │ │ ├── launchConfigConfirmation.html │ │ │ │ ├── popovers │ │ │ │ │ ├── help-disable-service.html │ │ │ │ │ ├── help-service-ignored.html │ │ │ │ │ ├── help-service-missing.html │ │ │ │ │ └── help-service-unexpected.html │ │ │ │ └── runtime-asg.html │ │ │ ├── deployment-dry-run-result.html │ │ │ ├── env-asg-details-modal.html │ │ │ ├── env-create-environment-modal.html │ │ │ └── env-deploy-modal.html │ │ ├── directives │ │ │ ├── currentDesiredTitle.js │ │ │ ├── healthChecks.js │ │ │ ├── lbStatusView.html │ │ │ ├── lbStatusView.js │ │ │ ├── serviceDiffWithTargetState.html │ │ │ └── serviceDiffWithTargetState.js │ │ ├── environments.module.js │ │ ├── popovers │ │ │ └── alert-settings.html │ │ ├── schedule │ │ │ ├── ManageEnvironmentScheduleController.js │ │ │ └── env-manage-schedule.html │ │ ├── servers │ │ │ ├── ManageEnvironmentServersController.js │ │ │ ├── env-manage-servers.html │ │ │ └── serversView.js │ │ ├── settings │ │ │ ├── ManageEnvironmentSettingsController.js │ │ │ └── env-manage-settings.html │ │ └── summary │ │ │ ├── EnvironmentsSummaryController.js │ │ │ └── env-summary.html │ ├── operations │ │ ├── ToggleServiceModalController.js │ │ ├── amis │ │ │ ├── OpsAMIsController.js │ │ │ └── ops-amis.html │ │ ├── deployments │ │ │ ├── DeploymentDetailsModalController.js │ │ │ ├── OpsDeploymentsController.js │ │ │ ├── ops-deployment-details-modal.html │ │ │ ├── ops-deployments.html │ │ │ ├── opsDeploymentsInstances.html │ │ │ ├── opsDeploymentsInstances.js │ │ │ ├── opsDeploymentsList.html │ │ │ └── opsDeploymentsList.js │ │ ├── maintenance │ │ │ ├── MainteinanceAddServerModalController.js │ │ │ ├── OpsMaintenanceController.js │ │ │ ├── ops-maintenance-addserver-modal.html │ │ │ └── ops-maintenance.html │ │ ├── operations.module.js │ │ ├── ops-toggle-service-modal.html │ │ ├── popovers │ │ │ └── instance-deployment-status.html │ │ ├── service │ │ │ ├── OpsServiceController.js │ │ │ ├── OpsServiceInstallcheckModalController.js │ │ │ ├── ops-service-installcheck-modal.html │ │ │ └── ops-service.html │ │ └── upstream │ │ │ ├── ASGSelectionModalController.js │ │ │ ├── OpsUpstreamController.js │ │ │ ├── UpstreamDetailsModalController.js │ │ │ ├── directives │ │ │ ├── lbServersStatesCell.html │ │ │ └── lbServersStatesCell.js │ │ │ ├── ops-upstream-details-modal.html │ │ │ ├── ops-upstream.html │ │ │ └── select-asg-modal.html │ └── settings │ │ ├── UserSettingsController.js │ │ ├── settings.module.js │ │ └── user-settings-modal.html ├── assets │ ├── css │ │ ├── angular-ui-grid.min.css │ │ ├── angular-ui-tree.min.css │ │ ├── bootstrap-3.3.5 │ │ │ ├── css │ │ │ │ ├── bootstrap-theme.css │ │ │ │ ├── bootstrap-theme.css.map │ │ │ │ ├── bootstrap-theme.min.css │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.css.map │ │ │ │ └── bootstrap.min.css │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.min.js │ │ │ │ └── npm.js │ │ ├── jsondiffpatch.css │ │ ├── loading-bar.min.css │ │ ├── select.min.css │ │ ├── tipped.css │ │ └── tourist.css │ ├── images │ │ ├── activity.gif │ │ ├── clippy.svg │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo.jpg │ │ └── trainline.svg │ └── lib │ │ ├── ace.js │ │ ├── ajv.min.js │ │ ├── angular-file-saver.bundle.min.js │ │ ├── angular-moment.min.js │ │ ├── backbone-min.js │ │ ├── ext-searchbox.js │ │ ├── glob-intersection.js │ │ ├── jquery.min.js │ │ ├── linq.min.js │ │ ├── loading-bar.min.js │ │ ├── mode-json.js │ │ ├── select.min.js │ │ ├── smart-table.min.js │ │ ├── spin.min.js │ │ ├── thenBy.min.js │ │ ├── tipped.js │ │ ├── tourist.min.js │ │ ├── ui-ace.js │ │ ├── ui-ace.min.js │ │ ├── ui-bootstrap-tpls-2.1.3.min.js │ │ ├── ui-grid.min.js │ │ └── worker-json.js ├── bower.json ├── docs │ ├── css │ │ ├── print.css │ │ ├── reset.css │ │ ├── screen.css │ │ ├── style.css │ │ └── typography.css │ ├── fonts │ │ ├── DroidSans-Bold.ttf │ │ └── DroidSans.ttf │ ├── images │ │ ├── collapse.gif │ │ ├── expand.gif │ │ ├── explorer_icons.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo_small.png │ │ ├── pet_store_api.png │ │ ├── throbber.gif │ │ └── wordnik_api.png │ ├── lang │ │ ├── en.js │ │ ├── es.js │ │ ├── fr.js │ │ ├── it.js │ │ ├── ja.js │ │ ├── pl.js │ │ ├── pt.js │ │ ├── ru.js │ │ ├── tr.js │ │ ├── translator.js │ │ └── zh-cn.js │ ├── lib │ │ ├── backbone-min.js │ │ ├── handlebars-2.0.0.js │ │ ├── highlight.7.3.pack.js │ │ ├── jquery-1.8.0.min.js │ │ ├── jquery.ba-bbq.min.js │ │ ├── jquery.slideto.min.js │ │ ├── jquery.wiggle.min.js │ │ ├── jsoneditor.min.js │ │ ├── marked.js │ │ ├── swagger-oauth.js │ │ ├── underscore-min.js │ │ └── underscore-min.map │ └── swagger-ui.js ├── gulp │ ├── build.js │ ├── conf.js │ ├── e2e-tests.js │ ├── eslint.js │ ├── inject.js │ ├── scripts.js │ ├── server.js │ ├── styles.js │ ├── unit-tests.js │ └── watch.js ├── gulpfile.js ├── index.html ├── karma.conf.js ├── npm-shrinkwrap.json ├── package.json ├── protractor.conf.js ├── schema │ ├── EnvironmentType.schema.json │ └── LBSettings.schema.json ├── styles │ ├── app-generated.css │ └── app.scss ├── test │ └── lib │ │ ├── angular-mocks.js │ │ ├── ngAnimateMock.js │ │ ├── ngMock.js │ │ └── ngMockE2E.js └── yarn.lock ├── contracts ├── em-server.instances.json └── em-server.json ├── contributors ├── DavidHunt.txt ├── DuncanHall.txt ├── FabioGariglio.txt ├── FilipSobczak.txt ├── JakeCross.txt └── MichalChaniewski.txt ├── em2 ├── README.md ├── config │ └── accounts │ │ ├── index.js │ │ ├── package.json │ │ ├── readme.md │ │ ├── serverless.yml │ │ └── src │ │ ├── lambdas.js │ │ └── services │ │ └── dynamo.js ├── deploy │ ├── account │ │ ├── aws_default_network_acl.tf │ │ ├── aws_default_route_table.tf │ │ ├── aws_default_security_group.tf │ │ ├── aws_internet_gateway.tf │ │ ├── aws_main_route_table_association.tf │ │ ├── aws_nat_gateway.tf │ │ ├── aws_route_table.tf │ │ ├── aws_route_table_association.tf │ │ ├── aws_subnet.tf │ │ ├── aws_vpc.tf │ │ ├── em_provider.tf │ │ ├── outputs.tf │ │ ├── remote_state.tf │ │ └── variables.tf │ ├── ami │ │ └── ubuntu_1604 │ │ │ ├── init_ubuntu_1604.sh │ │ │ └── userdata_ubuntu_1604.sh │ ├── consul │ │ ├── README.md │ │ ├── consul.tf │ │ ├── outputs.tf │ │ ├── scripts │ │ │ ├── debian_consul.service │ │ │ ├── debian_upstart.conf │ │ │ ├── install.sh │ │ │ ├── iptables.sh │ │ │ ├── rhel_consul.service │ │ │ ├── rhel_upstart.conf │ │ │ └── service.sh │ │ └── variables.tf │ ├── em-child │ │ ├── README.md │ │ ├── aws_iam_role.tf │ │ ├── aws_iam_role_policy.tf │ │ ├── aws_security_groups.tf │ │ ├── consul.tf │ │ ├── em-provider.tf │ │ ├── outputs.tf │ │ ├── scripts │ │ │ ├── debian_consul.service │ │ │ ├── debian_upstart.conf │ │ │ ├── install.sh │ │ │ ├── iptables.sh │ │ │ ├── rhel_consul.service │ │ │ ├── rhel_upstart.conf │ │ │ └── service.sh │ │ └── variables.tf │ ├── em-master │ │ ├── aws_ami.tf │ │ ├── aws_app.tf │ │ ├── aws_cloudwatch_event_rules.tf │ │ ├── aws_cloudwatch_metric_alarm.tf │ │ ├── aws_dynamodb_table.tf │ │ ├── aws_elasticache_cluster.tf │ │ ├── aws_iam_instance_profile.tf │ │ ├── aws_iam_role.tf │ │ ├── aws_iam_role_policy.tf │ │ ├── aws_lambda_function.tf │ │ ├── aws_security_group.tf │ │ ├── aws_sns_topic.tf │ │ ├── em-provider.tf │ │ ├── em-terraform-remote-state.tf │ │ ├── em-variables.tf │ │ ├── environment-manager.env │ │ ├── init.sh │ │ ├── lambda │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc.js │ │ │ ├── .jscsrc │ │ │ ├── InfraEnvironmentManagerAudit │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── InfraEnvironmentManagerAuditBackup │ │ │ │ ├── AwsAccount.js │ │ │ │ ├── DynamoTable.js │ │ │ │ ├── DynamoTables.js │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── InfraEnvironmentManagerBackup │ │ │ │ ├── AwsAccount.js │ │ │ │ ├── DynamoTable.js │ │ │ │ ├── DynamoTables.js │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── InfraEnvironmentManagerScheduler │ │ │ │ ├── build.js │ │ │ │ ├── environment.js │ │ │ │ ├── index.js │ │ │ │ ├── local │ │ │ │ │ ├── config.sample.json │ │ │ │ │ └── index.js │ │ │ │ ├── npm-shrinkwrap.json │ │ │ │ ├── package.json │ │ │ │ ├── presentation │ │ │ │ │ ├── reporting.js │ │ │ │ │ └── reporting.spec.js │ │ │ │ ├── readme.md │ │ │ │ ├── scheduler.js │ │ │ │ └── services │ │ │ │ │ ├── aws.js │ │ │ │ │ ├── em.js │ │ │ │ │ ├── rateLimiter.js │ │ │ │ │ └── rateLimiter.spec.js │ │ │ ├── package.json │ │ │ └── scheduler │ │ │ │ ├── build.js │ │ │ │ ├── environment.js │ │ │ │ ├── index.js │ │ │ │ ├── local │ │ │ │ ├── config.sample.json │ │ │ │ └── index.js │ │ │ │ ├── npm-shrinkwrap.json │ │ │ │ ├── package.json │ │ │ │ ├── presentation │ │ │ │ ├── reporting.js │ │ │ │ └── reporting.spec.js │ │ │ │ ├── readme.md │ │ │ │ ├── scheduler.js │ │ │ │ └── services │ │ │ │ ├── aws.js │ │ │ │ ├── em.js │ │ │ │ ├── rateLimiter.js │ │ │ │ └── rateLimiter.spec.js │ │ └── scripts │ │ │ ├── app │ │ │ ├── em-asg-setup.sh │ │ │ ├── em-install.sh │ │ │ └── user-data.tpl │ │ │ ├── environment-manager-env-variables.sh │ │ │ └── install_consul.sh │ ├── scripts │ │ ├── cda_install.sh │ │ ├── consul_client_install.sh │ │ ├── consul_server_install.sh │ │ └── name_instance.sh │ └── test │ │ └── test.tf └── gateway │ └── session │ ├── index.js │ ├── package.json │ ├── serverless.yml │ └── src │ ├── lambdas.js │ └── services │ └── session-service.js ├── gulpfile.js ├── package-lock.json ├── package.json ├── server ├── .eslintignore ├── .eslintrc.js ├── .jscsrc ├── Enums.js ├── api │ ├── api-utils │ │ ├── dateUtil.js │ │ ├── ifNotFound.js │ │ ├── logicalTableName.js │ │ ├── notImplemented.js │ │ ├── requestMetadata.js │ │ └── requestParam.js │ ├── controllers │ │ ├── asgs │ │ │ └── asgController.js │ │ ├── audit │ │ │ └── auditController.js │ │ ├── config │ │ │ ├── accounts │ │ │ │ └── accountsController.js │ │ │ ├── clusters │ │ │ │ └── clusterController.js │ │ │ ├── deployment-maps │ │ │ │ └── deploymentMapController.js │ │ │ ├── environment-types │ │ │ │ └── environmentTypeController.js │ │ │ ├── environments │ │ │ │ └── environmentsConfigController.js │ │ │ ├── export │ │ │ │ └── exportController.js │ │ │ ├── import │ │ │ │ └── importController.js │ │ │ ├── lb-settings │ │ │ │ └── lbSettingsController.js │ │ │ ├── notification-settings │ │ │ │ └── notificationSettingsController.js │ │ │ ├── permissions │ │ │ │ └── permissionsController.js │ │ │ ├── services │ │ │ │ └── servicesConfigController.js │ │ │ └── upstreams │ │ │ │ └── upstreamsConfigController.js │ │ ├── deployments │ │ │ └── deploymentsController.js │ │ ├── diagnostics │ │ │ └── diagnosticsController.js │ │ ├── environments │ │ │ └── environmentsController.js │ │ ├── images │ │ │ └── imagesController.js │ │ ├── instances │ │ │ └── instancesController.js │ │ ├── load-balancer │ │ │ └── loadBalancerController.js │ │ ├── package-upload-url │ │ │ ├── dynamicResponseCreator.js │ │ │ └── packageUploadUrlController.js │ │ ├── services │ │ │ └── servicesController.js │ │ ├── target-state │ │ │ └── targetStateController.js │ │ ├── token │ │ │ └── tokenController.js │ │ ├── upstreams │ │ │ └── upstreamsController.js │ │ └── user │ │ │ └── userController.js │ ├── em-internal │ │ └── controllers │ │ │ └── initial-data.js │ ├── error-handler │ │ └── defaultErrorHandler.js │ ├── swagger.yaml │ └── v1.js ├── appspec.yml ├── commands │ ├── asg │ │ ├── EnterAutoScalingGroupInstancesToStandby.js │ │ ├── ExitAutoScalingGroupInstancesFromStandby.js │ │ ├── SetAutoScalingGroupSchedule.js │ │ ├── SetAutoScalingGroupSize.js │ │ └── UpdateAutoScalingGroup.js │ ├── aws │ │ ├── GetAccountByEnvironment.js │ │ └── SetInstanceMaintenanceMode.js │ ├── deployments │ │ ├── CreateAutoScalingGroup.js │ │ ├── CreateLaunchConfiguration.js │ │ ├── DeployService.js │ │ ├── DeploymentCommandHandlerLogger.js │ │ ├── DeploymentContract.schema.js │ │ ├── GetInfrastructureRequirements.js │ │ ├── PreparePackage.js │ │ ├── PreparePackageCommand.schema.js │ │ ├── ProvideInfrastructure.js │ │ ├── PushDeployment.js │ │ ├── S3PathContract.schema.js │ │ └── packageMover.js │ ├── launch-config │ │ ├── SetLaunchConfiguration.js │ │ └── launchConfigUpdater.js │ ├── services │ │ ├── DeleteTargetState.js │ │ ├── ToggleTargetStatus.js │ │ └── UpdateTargetState.js │ ├── slices │ │ ├── ToggleSlicesByService.js │ │ └── ToggleSlicesByUpstream.js │ ├── utils │ │ ├── metadata.js │ │ ├── operationResult.js │ │ └── toggleSlices.js │ └── validators │ │ ├── awsAccountValidator.js │ │ └── lbUpstreamValidator.js ├── config │ ├── index.js │ └── version.js ├── configuration.sample.json ├── deployment │ ├── code-deploy │ │ ├── application-start.sh │ │ ├── application-stop.sh │ │ ├── before-install.sh │ │ ├── on-after-install.sh │ │ └── validate-service.sh │ └── systemd │ │ ├── environment-manager-debug.service │ │ └── environment-manager.service ├── healthchecks │ └── sensu │ │ ├── healthchecks.py │ │ ├── healthchecks.yml │ │ ├── ping.py │ │ └── redis.py ├── index.js ├── models │ ├── AutoScalingGroup.js │ ├── Deployment.js │ ├── DeploymentMap.js │ ├── Environment.js │ ├── EnvironmentType.js │ ├── Image.js │ ├── Instance.js │ ├── OpsEnvironment.js │ ├── SecurityGroup.js │ ├── Service.js │ └── TaggableMixin.js ├── modules │ ├── DeploymentLogger.js │ ├── DeploymentLogsStreamer.js │ ├── MainServer.js │ ├── PackagePathProvider.js │ ├── S3GetObjectRequest.js │ ├── active-directory-adapter │ │ ├── activeDirectoryAdapter.mock.js │ │ ├── activeDirectoryAdapter.prod.js │ │ ├── activeDirectoryAdapterConfiguration.js │ │ └── index.js │ ├── amazon-client │ │ ├── childAccountClient.js │ │ ├── masterAccountClient.js │ │ ├── myIdentity.js │ │ ├── pages.js │ │ └── s3Url.js │ ├── auditLogReader.js │ ├── authentication.js │ ├── authentications │ │ ├── cookieAuthentication.js │ │ ├── cookieAuthenticationConfiguration.js │ │ ├── tokenAuthentication.js │ │ └── tokenAuthenticationConfiguration.js │ ├── authorization.js │ ├── authorizer.js │ ├── authorizers │ │ ├── allow-authenticated.js │ │ ├── asgs.js │ │ ├── deploy-authorizer.js │ │ ├── deployments.js │ │ ├── environmentProtection.js │ │ ├── environments-schedule.js │ │ ├── environments.js │ │ ├── index.js │ │ ├── instances.js │ │ ├── load-balancer-settings.js │ │ ├── none.js │ │ ├── package-upload-url.js │ │ ├── services.js │ │ ├── simple.js │ │ ├── toggle-service-status.js │ │ ├── toggle-services.js │ │ ├── toggle-upstreams.js │ │ └── upstreams.js │ ├── autoScalingGroupSizePredictor.js │ ├── awsAccounts.js │ ├── awsDynamo │ │ └── dynamodbExpression.js │ ├── awsResourceNameProvider.js │ ├── base64.js │ ├── cacheManager.js │ ├── cacheRouter.js │ ├── checkAppPrerequisites.js │ ├── clientFactories │ │ ├── IAMRoleClient.js │ │ ├── SNSTopicClient.js │ │ ├── autoScalingGroupClientFactory.js │ │ ├── ec2InstanceClientFactory.js │ │ ├── iamRoleClientFactory.js │ │ └── snsTopicClientFactory.js │ ├── clusterNode.js │ ├── configuration │ │ ├── ConfigurationProvider.js │ │ ├── LocalConfigurationProvider.js │ │ └── S3ConfigurationProvider.js │ ├── configurationCache.js │ ├── consul-client │ │ ├── clientConfig.mock.js │ │ ├── clientConfig.prod.js │ │ └── index.js │ ├── consul-node │ │ ├── consul-node-sorting-service.js │ │ └── consul-node.js │ ├── consulDataStructures.js │ ├── consulSecretCache.js │ ├── cronService.js │ ├── data-access │ │ ├── accounts.js │ │ ├── asgips.js │ │ ├── cacheManagerEncryptedRedis.js │ │ ├── cachedSingleAccountDynamoTable.js │ │ ├── clusters.js │ │ ├── configEnvironmentTypes.js │ │ ├── configEnvironments.js │ │ ├── deploymentMaps.js │ │ ├── deployments.js │ │ ├── describeDynamoTable.js │ │ ├── dynamoAudit.js │ │ ├── dynamoImport.js │ │ ├── dynamoItemFilter.js │ │ ├── dynamoSoftDelete.js │ │ ├── dynamoTable.js │ │ ├── dynamoTableCache.js │ │ ├── dynamoTableDescription.js │ │ ├── dynamoVersion.js │ │ ├── encryptedRedisStore.js │ │ ├── lbSettingsAdapter.js │ │ ├── lbUpstreamAdapter.js │ │ ├── loadBalancerSettings.js │ │ ├── loadBalancerUpstreams.js │ │ ├── notificationSettings.js │ │ ├── opsEnvironment.js │ │ ├── permissions.js │ │ ├── services.js │ │ └── singleAccountDynamoTable.js │ ├── deployment │ │ ├── DeploymentContract.js │ │ ├── S3PathContract.js │ │ ├── deploymentDefinition.js │ │ ├── deploymentRepository.js │ │ ├── deploymentValidators.js │ │ ├── serverRoleDefinition.js │ │ ├── serviceDefinition.js │ │ ├── serviceDeploymentDefinition.js │ │ ├── serviceInstallationDefinition.js │ │ └── validators │ │ │ ├── blueGreenDeploymentValidator.js │ │ │ └── uniqueServiceVersionDeploymentValidator.js │ ├── ec2-monitor │ │ └── ec2-monitor-client.js │ ├── emCrypto.js │ ├── environment-state │ │ ├── deleteTargetState.js │ │ ├── getASGReady.js │ │ ├── getASGState.js │ │ ├── getAWSInstances.js │ │ ├── getAWSInstancesByName.js │ │ ├── getInstanceState.js │ │ ├── getOverallServiceHealth.js │ │ ├── getServiceHealth.js │ │ ├── getServiceInstallationCheck.js │ │ ├── getServicesState.js │ │ ├── healthReporter.js │ │ ├── serverRoleFilters.js │ │ └── serviceStateUtils.js │ ├── environmentDatabase.js │ ├── errors │ │ ├── ActiveDirectoryError.class.js │ │ ├── AutoScalingGroupAlreadyExistsError.class.js │ │ ├── AutoScalingGroupNotFoundError.class.js │ │ ├── AwsError.class.js │ │ ├── BadRequestError.class.js │ │ ├── BaseError.class.js │ │ ├── ConfigurationError.class.js │ │ ├── DeploymentValidationError.class.js │ │ ├── DynamoItemNotFoundError.class.js │ │ ├── HttpRequestError.class.js │ │ ├── ImageNotFoundError.class.js │ │ ├── InconsistentSlicesStatusError.class.js │ │ ├── InstanceNotFoundError.class.js │ │ ├── InstanceProfileNotFoundError.class.js │ │ ├── InvalidContractError.class.js │ │ ├── InvalidCredentialsError.class.js │ │ ├── InvalidItemSchemaError.class.js │ │ ├── InvalidOperationError.class.js │ │ ├── KeyPairNotFoundError.class.js │ │ ├── LaunchConfigurationAlreadyExistsError.class.js │ │ ├── PackagePreparationError.class.js │ │ ├── ResourceLockedError.js │ │ ├── ResourceNotFoundError.class.js │ │ ├── RoleNotFoundError.class.js │ │ └── TopicNotFoundError.class.js │ ├── express-middleware │ │ ├── deprecateMiddleware.js │ │ ├── loggingMiddleware.js │ │ ├── swaggerAuthorizerMiddleware.js │ │ └── swaggerNewRelicMiddleware.js │ ├── factories │ │ └── keypairFactory.js │ ├── functional.js │ ├── health-checks │ │ ├── index.js │ │ ├── library │ │ │ ├── ping.js │ │ │ └── redis.js │ │ └── resultCodes.js │ ├── http-server-factory │ │ ├── HttpServerFactory.js │ │ ├── HttpsServerFactory.js │ │ └── index.js │ ├── httpHealthChecks.js │ ├── logger.js │ ├── machineImage │ │ └── imageSummary.js │ ├── memoize.js │ ├── merge.js │ ├── miniStack.js │ ├── monitoring │ │ ├── DeploymentMonitor.js │ │ ├── DeploymentMonitorScheduler.js │ │ └── activeDeploymentsStatusProvider.js │ ├── new-relic │ │ └── check.js │ ├── promiseUtil.js │ ├── provisioning │ │ ├── Image.class.js │ │ ├── autoScaling │ │ │ ├── subnetsProvider.js │ │ │ ├── tagsProvider.js │ │ │ └── topicNotificationMappingProvider.js │ │ ├── autoScalingTemplatesProvider.js │ │ ├── infrastructureConfigurationProvider.js │ │ ├── launchConfiguration │ │ │ ├── UserDataBuilder.js │ │ │ ├── iamInstanceProfileNameProvider.js │ │ │ ├── imageProvider.js │ │ │ ├── instanceDevicesProvider.js │ │ │ ├── keyNameProvider.js │ │ │ ├── securityGroupsProvider.js │ │ │ ├── userData │ │ │ │ ├── linux-user-data.txt │ │ │ │ └── windows-user-data.txt │ │ │ └── userDataProvider.js │ │ ├── launchConfigurationTemplatesProvider.js │ │ └── namingConventionProvider.js │ ├── queryHandlersUtil │ │ ├── applyFuncToAccounts.js │ │ ├── deployments-helper.js │ │ ├── getASG.js │ │ ├── getSlices.js │ │ ├── scanCrossAccount.js │ │ └── scanCrossAccountFn.js │ ├── remoteCacheFlush.js │ ├── renderer.js │ ├── resourceFactories │ │ ├── AsgLifeCycleHooksResource.js │ │ ├── AsgResource.js │ │ ├── AsgResourceBase.js │ │ ├── AsgScheduledActionsResource.js │ │ ├── InstanceResourceBase.js │ │ ├── SecurityGroupResource.js │ │ ├── asgLifeCycleHooksResourceFactory.js │ │ ├── asgResourceFactory.js │ │ ├── asgScheduledActionsResourceFactory.js │ │ ├── ec2ImageResourceFactory.js │ │ ├── ec2InstanceResourceFactory.js │ │ ├── iamInstanceProfileResourceFactory.js │ │ ├── keyPairResourceFactory.js │ │ ├── launchConfigurationResourceFactory.js │ │ ├── nginxUpstreamsResourceFactory.js │ │ └── securityGroupResourceFactory.js │ ├── s3PackageLocator.js │ ├── scheduling │ │ ├── index.js │ │ └── parseSchedule.js │ ├── schema │ │ ├── AccountName.json │ │ ├── AccountNumber.json │ │ ├── ConsulConnectCommon.json │ │ ├── ConsulKey.json │ │ ├── EnvironmentName.json │ │ ├── GetTargetStateQuery.json │ │ ├── UpdateTargetStateCommand.json │ │ └── schema.js │ ├── sender.js │ ├── serverFactoryConfiguration.js │ ├── service-discovery │ │ ├── consul │ │ │ ├── consulCatalog.js │ │ │ └── index.js │ │ └── index.js │ ├── service-targets │ │ ├── consul │ │ │ ├── ConsulManager.js │ │ │ ├── consulMacroManager.js │ │ │ ├── index.js │ │ │ └── keyValueStore.js │ │ └── index.js │ ├── serviceName.js │ ├── simple-http.js │ ├── sns │ │ └── EnvironmentManagerEvents │ │ │ ├── createEvent.js │ │ │ ├── createTopic.js │ │ │ ├── getTargetArn.js │ │ │ ├── index.js │ │ │ └── publishEvent.js │ ├── sslComponentsRepository │ │ ├── index.js │ │ ├── sslComponentsRepository.mock.js │ │ ├── sslComponentsRepository.prod.config.js │ │ └── sslComponentsRepository.prod.js │ ├── systemUser.js │ ├── toggleServiceStatus.js │ ├── user-service │ │ ├── index.js │ │ ├── userService.mock.js │ │ └── userService.prod.js │ ├── user.js │ ├── userRolesProvider.js │ ├── userSessionStore.js │ ├── utilities.js │ ├── validate │ │ ├── index.js │ │ └── rule │ │ │ ├── environmentExists.js │ │ │ └── serviceExists.js │ └── weblink.js ├── package-lock.json ├── package.json ├── queryHandlers │ ├── GetASGState.js │ ├── GetAutoScalingGroup.js │ ├── GetAutoScalingGroupLifeCycleHooks.js │ ├── GetAutoScalingGroupScheduleStatus.js │ ├── GetAutoScalingGroupScheduledActions.js │ ├── GetAutoScalingGroupSize.js │ ├── GetEnvironmentScheduleStatus.js │ ├── GetInstanceProfile.js │ ├── GetKeyPair.js │ ├── GetLaunchConfiguration.js │ ├── GetRole.js │ ├── GetServicePortConfig.js │ ├── GetTopic.js │ ├── ScanAutoScalingGroups.js │ ├── ScanCrossAccountAutoScalingGroups.js │ ├── ScanCrossAccountImages.js │ ├── ScanCrossAccountInstances.js │ ├── ScanImages.js │ ├── ScanInstances.js │ ├── ScanInstancesScheduleStatus.js │ ├── ScanLaunchConfigurations.js │ ├── ScanNginxUpstreams.js │ ├── ScanSecurityGroups.js │ ├── ScanServersStatus.js │ ├── deployments │ │ └── GetNodeDeploymentLog.js │ ├── services │ │ ├── GetAllNodes.js │ │ ├── GetNode.js │ │ ├── GetServerRoles.js │ │ └── GetTargetState.js │ └── slices │ │ ├── GetSlicesByService.js │ │ └── GetSlicesByUpstream.js ├── routes │ ├── deploymentNodeLogs.js │ └── home.js ├── scripts │ ├── actionDefinitions.js │ ├── cleanup.js │ ├── command.js │ └── preinstall.js ├── start ├── test │ ├── .eslintrc.js │ ├── bootstrap.js │ ├── commandHandlers │ │ ├── GetAccountByEnvironmentTest.js │ │ ├── deployment │ │ │ ├── CreateAutoScalingGroupTest.js │ │ │ ├── CreateLaunchConfigurationCommandHandler.class.spec.js │ │ │ ├── DeployServiceTest.js │ │ │ └── GetInfrastructureRequirementsTest.js │ │ ├── enterAutoScalingGroupInstancesToStandbyTest.js │ │ ├── exitAutoScalingGroupInstancesFromStandbyTest.js │ │ ├── launchConfigUpdater.spec.js │ │ ├── services │ │ │ └── ToggleTargetStatusTest.js │ │ ├── setAutoScalingGroupScheduleCommandHandlerTest.js │ │ └── validators │ │ │ ├── awsAccountValidatorTest.js │ │ │ └── lbUpstreamDynamoResourceValidatorTest.js │ ├── commands │ │ ├── asg │ │ │ └── SetAutoScalingGroupScheduleTest.js │ │ └── deployments │ │ │ └── packageMoverTests.js │ ├── models │ │ ├── AutoScalingGroupTest.js │ │ └── InstanceTest.js │ ├── modules │ │ ├── DeploymentLoggerTest.js │ │ ├── DeploymentLogsStreamerTest.js │ │ ├── DeploymentMonitorTest.js │ │ ├── PackagePathProviderTest.js │ │ ├── amazon-client │ │ │ ├── pagesTest.js │ │ │ └── s3UrlTest.js │ │ ├── authorizer.spec.js │ │ ├── authorizers │ │ │ ├── environmentProtectedTest.js │ │ │ └── instances.spec.js │ │ ├── autoScalingGroupSizePredictorTest.js │ │ ├── awsDynamo │ │ │ └── dynamodbExpressionTest.js │ │ ├── base64Test.js │ │ ├── cacheTest.js │ │ ├── clientFactories │ │ │ └── ConsulManagerTest.js │ │ ├── consul-node │ │ │ ├── consul-node-sorting-service.spec.js │ │ │ └── data.js │ │ ├── consulDataStructuresTests.js │ │ ├── data-access │ │ │ ├── cacheManagerEncryptedRedisTest.js │ │ │ ├── deploymentsTest.js │ │ │ ├── describeDynamoTableTest.js │ │ │ ├── dynamoAuditTest.js │ │ │ ├── dynamoItemFilterTest.js │ │ │ ├── dynamoTableCacheTest.js │ │ │ ├── dynamoTableDescriptionTest.js │ │ │ ├── dynamoTableTest.js │ │ │ └── dynamoVersionTest.js │ │ ├── deployment │ │ │ ├── deploymentDefinitionTest.js │ │ │ ├── serverRoleDefinitionTest.js │ │ │ ├── serviceDefinitionTest.js │ │ │ ├── serviceInstallationDefinitionTest.js │ │ │ └── validators │ │ │ │ ├── BlueGreenDeploymentValidatorTest.js │ │ │ │ └── UniqueServiceVersionDeploymentValidatorTest.js │ │ ├── em-services │ │ │ └── servicesConfigController.spec.js │ │ ├── emCryptoTest.js │ │ ├── environment-state │ │ │ ├── deleteTargetStateTest.js │ │ │ ├── getASGStateTest.js │ │ │ ├── getInstanceStateTest.js │ │ │ ├── healthReporterTests.js │ │ │ ├── serverInstallCheckTests.js │ │ │ ├── serverRoleFiltersTest.js │ │ │ └── serviceStateUtilsTest.js │ │ ├── express-middleware │ │ │ └── swaggerAuthorizerMiddlewareTest.js │ │ ├── instances.spec.js │ │ ├── logger.spec.js │ │ ├── machineImage │ │ │ └── imageSummaryTest.js │ │ ├── memoizeTest.js │ │ ├── mergeTest.js │ │ ├── miniStackTest.js │ │ ├── promiseUtil.spec.js │ │ ├── provisioning │ │ │ ├── AutoScalingTemplatesProvider.spec.js │ │ │ ├── LaunchConfigurationTemplatesProvider.spec.js │ │ │ ├── NamingConventionProvider.spec.js │ │ │ ├── autoScaling │ │ │ │ ├── SubnetsProvider.spec.js │ │ │ │ ├── TagsProvider.spec.js │ │ │ │ └── TopicNotificationMappingProvider.spec.js │ │ │ ├── infrastructureConfigurationProvider.spec.js │ │ │ └── launchConfiguration │ │ │ │ ├── IamInstanceProfileNameProviderTest.js │ │ │ │ ├── ImageProviderTest.js │ │ │ │ ├── InstanceDevicesProviderTest.js │ │ │ │ ├── KeyNameProviderTest.js │ │ │ │ ├── SecurityGroupsProviderTest.js │ │ │ │ ├── UserDataBuilderTest.js │ │ │ │ └── UserDataProviderTest.js │ │ ├── queryHandlersUtil │ │ │ ├── deployments-helperTest.js │ │ │ └── scanCrossAccountFnTest.js │ │ ├── remoteCacheFlushTest.js │ │ ├── resourceFactories │ │ │ ├── AsgResourceBaseTest.js │ │ │ └── ec2InstanceResourceFactoryTest.js │ │ ├── s3PackageLocatorTest.js │ │ ├── scheduling-expectedActions.spec.js │ │ ├── scheduling-expectedStates.spec.js │ │ ├── schema │ │ │ └── schemaTest.js │ │ ├── service-reporter │ │ │ └── service-reporterTest.js │ │ ├── service-updater │ │ │ └── service-updaterTest.js │ │ ├── sns │ │ │ └── EnvironmentManagerEvents │ │ │ │ ├── createEvent.spec.js │ │ │ │ ├── createTopic.spec.js │ │ │ │ ├── getTargetArn.spec.js │ │ │ │ └── publishEvent.spec.js │ │ ├── toggleServiceStatus.spec.js │ │ ├── userRolesProviderTest.js │ │ ├── utilitiesTest.js │ │ ├── validate │ │ │ └── validateTest.js │ │ └── weblinkTest.js │ ├── queryHandlers │ │ ├── GetServicePortTest.js │ │ ├── ScanCrossAccountImagesTest.js │ │ └── ScanServersStatusTest.js │ ├── schema-validation │ │ ├── environment-type.sample.json │ │ └── environment-types.spec.js │ ├── serviceNameTests.js │ ├── test-api │ │ ├── controllers │ │ │ ├── asgs │ │ │ │ └── asgControllerTests.js │ │ │ ├── config │ │ │ │ └── deploymentMapController.spec.js │ │ │ ├── instances │ │ │ │ └── instancesController.spec.js │ │ │ └── services │ │ │ │ └── servicesController.spec.js │ │ ├── dynamicResponseCreator.spec.js │ │ └── userControllerTest.js │ ├── test-profile.json │ └── utils │ │ ├── fakeLogger.js │ │ └── sinonHelper.js └── yarn.lock ├── setup ├── cloudformation │ ├── .eslintrc.yaml │ ├── .gitignore │ ├── EnvironmentManager.template.yaml │ ├── EnvironmentManagerChildResources.template.json │ ├── EnvironmentManagerCommonResources.template.js │ ├── EnvironmentManagerEventAuditListeners │ │ └── EnvironmentManagerEventsAuditListeners.template.yaml │ ├── EnvironmentManagerEventPublication │ │ └── EnvironmentManagerEvents.template.yaml │ ├── EnvironmentManagerMasterResources.template.js │ ├── EnvironmentManagerRedis.template.yaml │ ├── EnvironmentManagerRedisNetwork.template.yaml │ ├── LambdaFailureAlerts │ │ └── EnvironmentManagerLambdaFailureAlerts.template.yaml │ ├── README.md │ ├── config.yaml │ ├── lambda │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .jscsrc │ │ ├── InfraAsgLambdaScale │ │ │ ├── README.md │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── InfraEnvironmentManagerAudit │ │ │ ├── index.js │ │ │ ├── index.spec.js │ │ │ ├── package-lock.json │ │ │ └── package.json │ │ ├── InfraEnvironmentManagerAuditBackup │ │ │ ├── AwsAccount.js │ │ │ ├── DynamoTable.js │ │ │ ├── DynamoTables.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── InfraEnvironmentManagerBackup │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── InfraEnvironmentManagerConfigurationChangeAudit │ │ │ └── index.js │ │ ├── InfraEnvironmentManagerOperationsChangeAudit │ │ │ └── index.js │ │ ├── InfraEnvironmentManagerScheduler │ │ │ ├── build.js │ │ │ ├── environment.js │ │ │ ├── index.js │ │ │ ├── local │ │ │ │ ├── config.sample.json │ │ │ │ └── index.js │ │ │ ├── npm-shrinkwrap.json │ │ │ ├── package.json │ │ │ ├── presentation │ │ │ │ ├── reporting.js │ │ │ │ └── reporting.spec.js │ │ │ ├── readme.md │ │ │ ├── scheduler.js │ │ │ ├── scheduler.tf │ │ │ └── services │ │ │ │ ├── aws.js │ │ │ │ ├── em.js │ │ │ │ ├── rateLimiter.js │ │ │ │ └── rateLimiter.spec.js │ │ └── package.json │ ├── package.json │ ├── template.js │ └── tools │ │ ├── common.js │ │ ├── deploy.js │ │ └── index.js ├── consul-acl │ ├── consul-deployment-agent.json │ ├── environment-manager.json │ └── upstreamr.json ├── data-migration │ ├── replicate-dynamodb-table │ │ ├── .eslintrc.yaml │ │ ├── index.js │ │ ├── index.spec.js │ │ ├── package.json │ │ └── yarn.lock │ └── touch-dynamodb-table │ │ ├── .eslintrc.yaml │ │ ├── index.js │ │ ├── index.spec.js │ │ ├── package.json │ │ └── yarn.lock ├── iam │ ├── commonInstanceProfile.policy.json │ ├── roleInfraEnvironmentManager.policy.json │ └── roleInfraEnvironmentManagerChild.policy.json ├── linux-sample-package.zip └── terraform │ └── upstreamCleaner │ ├── aws_iam_role.tf │ ├── aws_lambda_function.tf │ ├── lambda │ ├── .eslintrc.js │ ├── build.sh │ ├── index.js │ ├── package-lock.json │ └── package.json │ └── variables.tf ├── test ├── package.json ├── pages │ ├── config-page.js │ ├── config │ │ └── deploymentmap-page.js │ ├── home-page.js │ └── login-page.js ├── specs │ └── config │ │ └── deploymentmap-test.spec.js ├── test │ └── test-ui │ │ └── screenshots │ │ └── ERROR_chrome_2018-05-15T12-41-26.078Z.png └── wdio.conf.js └── version.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto-detect text files, use LF in repository and working copy 2 | * text=auto eol=lf 3 | 4 | # Binary files. Do not attempt EOL conversion 5 | *.png -text 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | 5 | cache: 6 | directories: 7 | - "${HOME}/.bower" 8 | - "${HOME}/.cache/bower" 9 | 10 | install: 11 | - npm install 12 | - (cd server && exec npm install) 13 | - (cd client && exec npm install) 14 | 15 | before_script: 16 | - npm install -g gulp-cli 17 | 18 | script: 19 | - (cd server && exec npm test) 20 | - (cd client && exec npm test) 21 | 22 | after_success: 23 | - gulp 24 | 25 | addons: 26 | artifacts: 27 | paths: 28 | - $(git ls-files -o test-results | tr "\n" ":") 29 | 30 | deploy: 31 | provider: s3 32 | access_key_id: $ARTIFACTS_KEY 33 | secret_access_key: $ARTIFACTS_SECRET 34 | bucket: $ARTIFACTS_BUCKET 35 | region: $ARTIFACTS_REGION 36 | local_dir: dist 37 | upload-dir: $DIST_DIR 38 | skip_cleanup: true 39 | on: 40 | repo: trainline/environment-manager 41 | all_branches: true -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015-2016 Trainline.com Ltd 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Environment Manager 2 | Copyright Trainline Limited, 2016 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | ## This project is no longer maintained 4 | 5 | [![Build Status](https://travis-ci.org/trainline/environment-manager.svg?branch=master)](https://travis-ci.org/trainline/environment-manager) 6 | 7 | ### Welcome 8 | Welcome to Environment Manager, an open source platform for automating and managing continuous delivery in AWS. 9 | 10 | ### Requirements 11 | To run Environment Manager, you will need: 12 | - AWS 13 | - NodeJS 6.8.0 14 | - npm 15 | 16 | ### Installation 17 | Please refer to the website for overview and setup instructions: 18 | 19 | 20 | -------------------------------------------------------------------------------- /client/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "airbnb", 3 | "validateIndentation": 2 4 | } -------------------------------------------------------------------------------- /client/STYLEGUIDE.md: -------------------------------------------------------------------------------- 1 | 2 | ### All components 3 | - always one component per file 4 | 5 | - direct mapping of component name and filename 6 | ie. ServicesController sits in ServicesController.js 7 | 8 | 9 | ### Naming 10 | 11 | ServicesController.js 12 | 13 | ### Directories 14 | 15 | Multiple words in directories names should be separated by hyphens, ie.: common-modal 16 | 17 | 18 | ### Formatting 19 | 20 | Airbnb 21 | https://github.com/airbnb/javascript -------------------------------------------------------------------------------- /client/app/common/common.module.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common', [ 6 | 'ngRoute', 7 | 'ui.bootstrap' 8 | ]); 9 | -------------------------------------------------------------------------------- /client/app/common/directives/cronEditor.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | 7 |
8 | : 11 | 14 |
-------------------------------------------------------------------------------- /client/app/common/directives/defaultIfEmptyDirective.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').directive('defaultIfEmpty', function () { 6 | return { 7 | restrict: 'A', 8 | require: 'ngModel', 9 | link: function (scope, elm, attrs, ctrl) { 10 | var defaultValue = attrs.defaultIfEmpty || null; 11 | 12 | ctrl.$parsers.unshift(function (viewValue) { 13 | var result = viewValue !== '' ? viewValue : defaultValue; 14 | return result; 15 | }); 16 | } 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /client/app/common/directives/loadingOverlay.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | angular.module('EnvironmentManager.common') 8 | .component('loadingOverlay', { 9 | restrict: 'E', 10 | bindings: { 11 | }, 12 | template: '
', 13 | controllerAs: 'vm', 14 | controller: function (loading) { 15 | var vm = this; 16 | vm.visible = false; 17 | loading.registerLoadingOverlay(vm); 18 | } 19 | }); 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /client/app/common/directives/scheduleViewer.html: -------------------------------------------------------------------------------- 1 |
2 | {{vm.simpleOption}} 3 | 4 |
5 |
    6 |
  • 7 | {{ cron.cron }} 8 |
  • 9 |
10 |
in {{ vm.timezone.code }} time
11 |
12 | 13 |
14 |
Time Zone:
{{ vm.timezone.readable }}
15 | Rules: 16 |
    17 |
  • 18 | {{ cron.cron }} 19 |
  • 20 |
21 |
Next action:
{{ vm.next }}
22 |
23 | 24 |
-------------------------------------------------------------------------------- /client/app/common/services/cacheService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | angular 5 | .module('EnvironmentManager.common') 6 | .factory('cacheservice', cacheservice); 7 | 8 | cacheservice.$inject = ['$http']; 9 | 10 | function cacheservice($http) { 11 | var url = '/flushcache/'; 12 | 13 | return { 14 | flush: flush 15 | }; 16 | 17 | /** 18 | * Send 'resetcache' message to target all services in {{environment}} 19 | * @param {String} environment 20 | * @param {Object: {host: String, port: Number, environment: String }} hosts 21 | */ 22 | function flush(environment, hosts) { 23 | return $http.post(url + environment, { 24 | hosts: hosts 25 | }); 26 | } 27 | } 28 | }()); 29 | -------------------------------------------------------------------------------- /client/app/common/services/comparisons.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').factory('comparisons', function () { 6 | return { 7 | semver: function (a, b) { 8 | var pa = a.split('.'); 9 | var pb = b.split('.'); 10 | for (var i = 0; i < 3; i++) { 11 | var na = Number(pa[i]); 12 | var nb = Number(pb[i]); 13 | if (na > nb) return 1; 14 | if (nb > na) return -1; 15 | if (!isNaN(na) && isNaN(nb)) return 1; 16 | if (isNaN(na) && !isNaN(nb)) return -1; 17 | } 18 | return 0; 19 | } 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/common/services/enumsService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').factory('enums', 6 | function () { 7 | var MILLISECONDS = { 8 | PerHour: 3600000, 9 | PerDay: 86400000 10 | }; 11 | 12 | return { 13 | MILLISECONDS: MILLISECONDS 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /client/app/common/services/environmentDeployService.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | angular.module('EnvironmentManager.common').factory('environmentDeploy', 8 | function () { 9 | var deployHandler; 10 | 11 | function registerDeployHandler(handler) { 12 | deployHandler = handler; 13 | } 14 | 15 | function destroyHandler() { 16 | deployHandler = null; 17 | } 18 | 19 | function callDeployHandler() { 20 | deployHandler(); 21 | } 22 | 23 | return { 24 | registerDeployHandler: registerDeployHandler, 25 | callDeployHandler: callDeployHandler, 26 | destroyHandler: destroyHandler 27 | } 28 | } 29 | ); 30 | 31 | -------------------------------------------------------------------------------- /client/app/common/services/environmentStorageService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | (function () { 6 | angular 7 | .module('EnvironmentManager.common') 8 | .factory('environmentstorageservice', environmentstorageservice); 9 | 10 | environmentstorageservice.$inject = ['storageservicefactory']; 11 | 12 | function environmentstorageservice(storageservicefactory) { 13 | return storageservicefactory.create('em-selections-environment'); 14 | } 15 | }()); 16 | -------------------------------------------------------------------------------- /client/app/common/services/instancesService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').factory('instancesService', 6 | function ($http, $rootScope) { 7 | return { 8 | setMaintenanceMode: function (accountName, instanceId, enable) { 9 | var url = ['api', 'v1', 'instances', instanceId, 'maintenance'].join('/'); 10 | return $http.put(url, { enable: enable }).then(function (response) { 11 | return response.data; 12 | }, function (response) { 13 | $rootScope.$broadcast('error', response); 14 | }); 15 | } 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /client/app/common/services/loading.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | angular.module('EnvironmentManager.common').factory('loading', 8 | function () { 9 | var loadingOverlay; 10 | 11 | return { 12 | registerLoadingOverlay: function (overlay) { 13 | loadingOverlay = overlay; 14 | }, 15 | lockPage: function (locked) { 16 | loadingOverlay.visible = locked; 17 | } 18 | } 19 | 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /client/app/common/services/localResourceFactoryService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').factory('localResourceFactory', 6 | function ($q) { 7 | function LocalResource(source) { 8 | var dataSource = source; 9 | 10 | this.all = function () { 11 | return $q.when(dataSource); 12 | }; 13 | } 14 | 15 | return function (source) { 16 | return new LocalResource(source); 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /client/app/common/services/localStorageService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | (function () { 6 | angular 7 | .module('EnvironmentManager.common') 8 | .factory('localstorageservice', localstorageservice); 9 | 10 | localstorageservice.$inject = []; 11 | 12 | function localstorageservice() { 13 | var localStorage = window.localStorage; 14 | 15 | return { 16 | get: get, 17 | set: set, 18 | exists: exists 19 | }; 20 | 21 | function get(key) { 22 | return localStorage.getItem(key); 23 | } 24 | 25 | function set(key, value) { 26 | return localStorage.setItem(key, value); 27 | } 28 | 29 | function exists(key) { 30 | if (get(key) === null || get(key) === '') return false; 31 | return true; 32 | } 33 | } 34 | }()); 35 | -------------------------------------------------------------------------------- /client/app/common/services/rolesService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common') 6 | .factory('roles', function ($http) { 7 | return { 8 | get: function (accountName, environmentName) { 9 | var url = '/api/v1/target-state/' + environmentName; 10 | return $http.get(url).then(function (response) { 11 | return response.data; 12 | }); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /client/app/common/services/serviceDiscovery.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common') 6 | .factory('serviceDiscovery', function ($rootScope, $q, $http) { 7 | function defaultFailure(error) { 8 | $rootScope.$broadcast('error', error); 9 | } 10 | 11 | return { 12 | getASGState: function (environmentName, asgName) { 13 | var path = ['api', 'v1', 'environments', environmentName, 'servers', asgName]; 14 | return $http.get(path.join('/')).then(function (response) { 15 | return response.data; 16 | }, defaultFailure); 17 | } 18 | }; 19 | }); 20 | -------------------------------------------------------------------------------- /client/app/common/services/taggable.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common') 6 | .factory('taggable', function () { 7 | return function (cls) { 8 | cls.prototype.getTag = function (key) { 9 | var tag = _.find(this.Tags, { Key: key }); 10 | if (tag === undefined) { 11 | throw new Error('Can\'t find tag'); 12 | } 13 | return tag.Value; 14 | }; 15 | 16 | cls.prototype.setTag = function (key, value) { 17 | var tag = this.getTag(key); 18 | if (tag === undefined) { 19 | tag = { 20 | Key: key, 21 | Value: value 22 | }; 23 | this.Tags.push(tag); 24 | } else { 25 | tag.Value = value; 26 | } 27 | }; 28 | }; 29 | }); 30 | -------------------------------------------------------------------------------- /client/app/common/services/targetStateService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.common').factory('targetStateService', function ($rootScope, $q, $http) { 6 | return { 7 | toggleServiceStatus: function (environment, service, slice, enable, serverRole, environmentType) { 8 | var url = '/api/v1/target-state/' + environment + '/' + service + '/toggle-status'; 9 | 10 | var data = { 11 | Enable: enable, 12 | Slice: slice, 13 | ServerRole: serverRole, 14 | EnvironmentType: environmentType 15 | }; 16 | 17 | return $http.put(url, data) 18 | .then(function (result) { 19 | return result.data; 20 | }, $rootScope.$broadcast.bind($rootScope, 'error')); 21 | } 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /client/app/common/services/teamStorageService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | (function () { 6 | angular 7 | .module('EnvironmentManager.common') 8 | .factory('teamstorageservice', teamstorageservice); 9 | 10 | teamstorageservice.$inject = ['storageservicefactory']; 11 | 12 | function teamstorageservice(storageservicefactory) { 13 | return storageservicefactory.create('em-selections-team'); 14 | } 15 | }()); 16 | -------------------------------------------------------------------------------- /client/app/common/utilities.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | Date.prototype.getDaysBetweenDates = function (date2) { 8 | var diff = this.getTime() - new Date(date2).getTime(); 9 | return Math.round(diff / (1000 * 60 * 60 * 24)); 10 | }; 11 | 12 | Array.prototype.merge = function (targets, comparer, builder) { 13 | return this.map(function (source) { 14 | var target = targets.filter(function (x) { return comparer(source, x); })[0]; 15 | return builder(source, target); 16 | }); 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /client/app/compare/compare.module.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.compare', [ 6 | 'ngRoute', 7 | 'ui.bootstrap', 8 | 'ui.select', 9 | 'EnvironmentManager.common' 10 | ]); 11 | 12 | angular.module('EnvironmentManager.compare').config(function ($routeProvider) { 13 | $routeProvider 14 | .when('/compare', { 15 | templateUrl: '/app/compare/compare.html', 16 | controller: 'CompareController as vm', 17 | menusection: '', 18 | reloadOnSearch: false 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /client/app/compare/diff-viewer/diff-viewer.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /client/app/compare/services/upstreamService.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | angular.module('EnvironmentManager.compare').factory('upstreamService', 8 | function ($http) { 9 | 10 | var baseUrl = '/api/v1/' 11 | 12 | function get() { 13 | return $http.get(baseUrl + 'config/upstreams/'); 14 | } 15 | 16 | function getSlice(name, environment) { 17 | return $http.get(baseUrl + 'upstreams/' + name + '/slices/', { params: { environment: environment } }); 18 | } 19 | 20 | return { 21 | get: get, 22 | getSlice: getSlice 23 | } 24 | } 25 | ); 26 | 27 | -------------------------------------------------------------------------------- /client/app/configuration/audit/AuditCompareModalController.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | var app = angular.module('EnvironmentManager.configuration').controller('AuditCompareModalController', 8 | function ($scope, $uibModalInstance, audit, arrayItemHashDetector) { 9 | $scope.Audit = audit; 10 | $scope.DiffOptions = arrayItemHashDetector; 11 | 12 | $scope.ok = function () { 13 | $uibModalInstance.dismiss('cancel'); 14 | }; 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /client/app/configuration/audit/audit-compare-modal.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /client/app/configuration/popovers/multiple-values.html: -------------------------------------------------------------------------------- 1 |
2 | To input multiple values, delimit them with ',' 3 |
-------------------------------------------------------------------------------- /client/app/configuration/popovers/notification-settings-paging.html: -------------------------------------------------------------------------------- 1 |
2 | Enter Pager Duty API ID. 3 |
-------------------------------------------------------------------------------- /client/app/configuration/popovers/notification-settings.html: -------------------------------------------------------------------------------- 1 |
2 | Notification settings which will be used for notification alerts of environment / service owned by this team. 3 |
-------------------------------------------------------------------------------- /client/app/environments/dialogs/asg/asgSingleInstance.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.environments').component('asgSingleInstance', { 6 | templateUrl: '/app/environments/dialogs/asg/asgSingleInstance.html', 7 | bindings: { 8 | resolve: '<', 9 | close: '&', 10 | dismiss: '&' 11 | }, 12 | controllerAs: 'vm', 13 | controller: function () { 14 | var vm = this; 15 | vm.dataLoading = false; 16 | vm.instance = vm.resolve.instance; 17 | vm.showLogLink = function (service) { 18 | return !_.includes(['Ignored'], service.DiffWithTargetState); 19 | }; 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/environments/dialogs/asg/popovers/help-disable-service.html: -------------------------------------------------------------------------------- 1 |
2 | Disabling future installation of a service will prevent any newly created infrastructure from installing it (eg. if you scale up this ASG, new instances will not install a disabled service). 3 |
4 | -------------------------------------------------------------------------------- /client/app/environments/dialogs/asg/popovers/help-service-ignored.html: -------------------------------------------------------------------------------- 1 |
2 | This service is in the target state of this server role, but marked as ignored, so it's not installed. There are no logs for this service installation, because installation wasn't attempted. 3 |
4 | -------------------------------------------------------------------------------- /client/app/environments/dialogs/asg/popovers/help-service-missing.html: -------------------------------------------------------------------------------- 1 |
2 | This service is in the target state of this server role, but not present on this instance. See deployment logs to find out, why it failed. 3 |
4 | -------------------------------------------------------------------------------- /client/app/environments/dialogs/asg/popovers/help-service-unexpected.html: -------------------------------------------------------------------------------- 1 |
2 | This service is present on the instance, but it's not in the target state of this server role. 3 |
4 | -------------------------------------------------------------------------------- /client/app/environments/dialogs/deployment-dry-run-result.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /client/app/environments/directives/healthChecks.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | angular.module('EnvironmentManager.environments') 8 | .directive('healthChecks', function ($parse) { 9 | return { 10 | restrict: 'E', 11 | scope: false, 12 | template: ' {{ check.Name }}
' 13 | + '-
', 14 | replace: true, 15 | link: function (scope, elm, attrs) { 16 | scope.healthChecks = $parse(attrs.list)(scope); 17 | } 18 | }; 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /client/app/environments/directives/serviceDiffWithTargetState.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.environments').component('serviceDiffWithTargetState', { 6 | templateUrl: '/app/environments/directives/serviceDiffWithTargetState.html', 7 | bindings: { 8 | state: '<' 9 | }, 10 | controllerAs: 'vm', 11 | controller: function () { 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /client/app/environments/popovers/alert-settings.html: -------------------------------------------------------------------------------- 1 |
2 | Who should get alerts when not explicitly specified in the health check definition. 3 |
-------------------------------------------------------------------------------- /client/app/operations/popovers/instance-deployment-status.html: -------------------------------------------------------------------------------- 1 |
2 | Success - all services on instance deployed successfuly
3 | Failed - at least one service deployment failed
4 | In Progress - at least one of these conditions is true:
5 | - at least one service deployment is in progress
6 | - the deployment hasn't started yet, and less than 60 minutes have passed 7 |
-------------------------------------------------------------------------------- /client/app/operations/upstream/ASGSelectionModalController.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.operations').controller('ASGSelectionModalController', 6 | function ($scope, $uibModal, $uibModalInstance, $q, parameters) { 7 | var vm = this; 8 | vm.asgs = _.uniq(parameters.asgs); 9 | 10 | vm.close = function () { 11 | $uibModalInstance.dismiss(); 12 | }; 13 | 14 | vm.selectASG = function (asg) { 15 | $uibModalInstance.close(asg); 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /client/app/operations/upstream/select-asg-modal.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /client/app/settings/settings.module.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | angular.module('EnvironmentManager.settings', []); 6 | -------------------------------------------------------------------------------- /client/app/settings/user-settings-modal.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/css/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /client/assets/css/bootstrap-3.3.5/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition') 3 | require('../../js/alert') 4 | require('../../js/button') 5 | require('../../js/carousel') 6 | require('../../js/collapse') 7 | require('../../js/dropdown') 8 | require('../../js/modal') 9 | require('../../js/tooltip') 10 | require('../../js/popover') 11 | require('../../js/scrollspy') 12 | require('../../js/tab') 13 | require('../../js/affix') -------------------------------------------------------------------------------- /client/assets/images/activity.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/images/activity.gif -------------------------------------------------------------------------------- /client/assets/images/clippy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /client/assets/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/images/favicon-16x16.png -------------------------------------------------------------------------------- /client/assets/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/images/favicon-32x32.png -------------------------------------------------------------------------------- /client/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/images/favicon.ico -------------------------------------------------------------------------------- /client/assets/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/assets/images/logo.jpg -------------------------------------------------------------------------------- /client/assets/lib/thenBy.min.js: -------------------------------------------------------------------------------- 1 | /*** Copyright 2013 Teun Duynstee Licensed under the Apache License, Version 2.0 ***/ 2 | firstBy = function () { function n(n, t) { if ("function" != typeof n) { var r = n; n = function (n) { return n[r]?n[r]:"" } } if (1 === n.length) { var u = n; n = function (n, t) { return u(n) < u(t)?-1:u(n) > u(t)?1:0 } } return -1 === t?function (t, r) { return -n(t, r) }:n } function t(t, u) { return t = n(t, u), t.thenBy = r, t } function r(r, u) { var f = this; return r = n(r, u), t(function (n, t) { return f(n, t) || r(n, t) }) } return t }(); -------------------------------------------------------------------------------- /client/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "environments-manager", 3 | "version": "3.0.0", 4 | "dependencies": { 5 | "moment": "~2.10.6", 6 | "angular": "~1.6.1", 7 | "jsondiffpatch": "^0.1.43", 8 | "lodash": "^4.13.1", 9 | "prettycron": "^0.10.0", 10 | "angular-ui-tree": "^2.16.0", 11 | "spin": "^1.1.6", 12 | "angular-ui-grid": "ui-grid#^3.2.1", 13 | "select": "^1.0.6", 14 | "later": "^1.2.0", 15 | "angular-route": "^1.6.1", 16 | "ngclipboard": "^1.1.1", 17 | "moment-timezone": "^0.5.11", 18 | "angular-loading-bar": "^0.9.0", 19 | "angular-smart-table": "^2.1.8", 20 | "Tourist.js": "tourist#^0.1.2", 21 | "backbone": "^1.3.3", 22 | "jquery": "^3.2.1", 23 | "highcharts-ng": "^1.1.0", 24 | "highcharts": "^6.0.1" 25 | }, 26 | "devDependencies": { 27 | "angular-mocks": "~1.6.1" 28 | }, 29 | "resolutions": { 30 | "angular": "~1.6.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/docs/css/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf') format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf') format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /client/docs/fonts/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/fonts/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /client/docs/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /client/docs/images/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/collapse.gif -------------------------------------------------------------------------------- /client/docs/images/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/expand.gif -------------------------------------------------------------------------------- /client/docs/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/explorer_icons.png -------------------------------------------------------------------------------- /client/docs/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/favicon-32x32.png -------------------------------------------------------------------------------- /client/docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/favicon.ico -------------------------------------------------------------------------------- /client/docs/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/logo_small.png -------------------------------------------------------------------------------- /client/docs/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/pet_store_api.png -------------------------------------------------------------------------------- /client/docs/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/throbber.gif -------------------------------------------------------------------------------- /client/docs/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/client/docs/images/wordnik_api.png -------------------------------------------------------------------------------- /client/docs/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /client/docs/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /client/gulp/eslint.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var eslint = require('gulp-eslint'); 3 | 4 | gulp.task('lint', function () { 5 | return gulp.src(['./app/**/*.js', '!node_modules/**']) 6 | .pipe(eslint()) 7 | .pipe(eslint.format()) 8 | .pipe(eslint.failAfterError()); 9 | }); 10 | 11 | 12 | gulp.task('lint-fix', function () { 13 | return gulp.src(['./app/**/*.js', '!node_modules/**']) 14 | .pipe(eslint({ 15 | fix: true 16 | })) 17 | .pipe(eslint.format()) 18 | .pipe(eslint.failAfterError()) 19 | .pipe(gulp.dest('./app')); 20 | }); 21 | -------------------------------------------------------------------------------- /client/gulp/scripts.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 5 | 'use strict'; 6 | 7 | var gulp = require('gulp'); 8 | 9 | var browserSync = require('browser-sync'); 10 | 11 | var $ = require('gulp-load-plugins')(); 12 | 13 | 14 | gulp.task('scripts-reload', function () { 15 | return buildScripts() 16 | .pipe(browserSync.stream()); 17 | }); 18 | 19 | gulp.task('scripts', function () { 20 | return buildScripts(); 21 | }); 22 | 23 | function buildScripts() { 24 | return gulp.src('app/**/*.js') 25 | .pipe($.eslint()) 26 | .pipe($.eslint.format()) 27 | .pipe($.size()); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /client/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict'; 3 | 4 | let fs = require('fs'); 5 | let gulp = require('gulp'); 6 | let zip = require('gulp-zip'); 7 | 8 | /** 9 | * This will load all js files in the gulp directory 10 | * in order to load all gulp tasks 11 | */ 12 | fs.readdirSync('./gulp').filter(function(file) { 13 | return (/\.js$/i).test(file); 14 | }).map(function(file) { 15 | require('./gulp/' + file); 16 | }); 17 | 18 | /** 19 | * Default task clean temporaries directories and launch the 20 | * main optimization build task 21 | */ 22 | gulp.task('default', ['clean'], function () { 23 | gulp.start('serve'); 24 | }); 25 | -------------------------------------------------------------------------------- /client/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict'; 3 | 4 | // An example configuration file. 5 | exports.config = { 6 | // The address of a running selenium server. 7 | //seleniumAddress: 'http://localhost:4444/wd/hub', 8 | //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json 9 | 10 | // Capabilities to be passed to the webdriver instance. 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | 15 | baseUrl: 'http://localhost:3001', 16 | 17 | // Spec patterns are relative to the current working directory when 18 | // protractor is called. 19 | specs: ['./test/e2e/**/*.js'], 20 | 21 | // Options to be passed to Jasmine-node. 22 | jasmineNodeOpts: { 23 | showColors: true, 24 | defaultTimeoutInterval: 30000 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /client/test/lib/ngAnimateMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngAnimateMock'; 3 | -------------------------------------------------------------------------------- /client/test/lib/ngMock.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMock'; 3 | -------------------------------------------------------------------------------- /client/test/lib/ngMockE2E.js: -------------------------------------------------------------------------------- 1 | require('./angular-mocks'); 2 | module.exports = 'ngMockE2E'; 3 | -------------------------------------------------------------------------------- /em2/README.md: -------------------------------------------------------------------------------- 1 | # Environment Manager (Serverless) 2 | 3 | ## Deployments.. 4 | 5 | - Install serverless 6 | 7 | ``` npm install -g serverless ``` 8 | 9 | - Set AWS Credentials in Environment Variables 10 | - Open prompt at service level. E.g.. 11 | 12 | ``` cd config/accounts ``` 13 | 14 | - Install dependencies 15 | 16 | ``` npm install ``` 17 | 18 | - Deploy 19 | 20 | ``` serverless deploy --region eu-west-1 --stage uat --bucket my-uat-packages-bucket ``` -------------------------------------------------------------------------------- /em2/config/accounts/index.js: -------------------------------------------------------------------------------- 1 | let rl = require('roast-lambda'); 2 | let _ = require('lodash'); 3 | 4 | let lambdas = require('./src/lambdas'); 5 | let dynamo = require('./src/services/dynamo'); 6 | 7 | function inject(fn) { 8 | return (args) => { 9 | args.dynamo = dynamo.createClient(args.AWS, args.context.awsRegion, args.context.env.STAGE); 10 | return fn(args); 11 | }; 12 | } 13 | 14 | _.forOwn(lambdas, (lambda, name) => { 15 | lambda.handler = inject(lambda.handler); 16 | exports[name] = rl.init(lambda); 17 | }); -------------------------------------------------------------------------------- /em2/config/accounts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "em-get-accounts", 3 | "version": "0.0.1", 4 | "description": "Gets all AWS Accounts", 5 | "main": "index.js", 6 | "scripts": { 7 | "deploy": "serverless deploy --region eu-west-1" 8 | }, 9 | "author": "Trainline", 10 | "dependencies": { 11 | "lodash": "^4.17.4", 12 | "roast-lambda": "0.x.x" 13 | }, 14 | "files": [ 15 | "index.js", 16 | "package.json", 17 | "src/**", 18 | "node_modules/**" 19 | ], 20 | "devDependencies": { 21 | "co": "^4.6.0", 22 | "request": "^2.81.0", 23 | "request-promise": "^4.2.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /em2/config/accounts/readme.md: -------------------------------------------------------------------------------- 1 | # Config / Accounts Lambda 2 | 3 | Gets and sets accounts -------------------------------------------------------------------------------- /em2/deploy/account/aws_default_network_acl.tf: -------------------------------------------------------------------------------- 1 | resource "aws_default_network_acl" "default" { 2 | default_network_acl_id = "${aws_vpc.main.default_network_acl_id}" 3 | 4 | ingress { 5 | protocol = -1 6 | rule_no = 100 7 | action = "allow" 8 | cidr_block = "0.0.0.0/0" 9 | from_port = 0 10 | to_port = 0 11 | } 12 | 13 | egress { 14 | protocol = -1 15 | rule_no = 100 16 | action = "allow" 17 | cidr_block = "0.0.0.0/0" 18 | from_port = 0 19 | to_port = 0 20 | } 21 | 22 | tags { 23 | Name = "NACL" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_default_route_table.tf: -------------------------------------------------------------------------------- 1 | resource "aws_default_route_table" "private" { 2 | default_route_table_id = "${aws_vpc.main.default_route_table_id}" 3 | 4 | route { 5 | cidr_block = "0.0.0.0/0" 6 | nat_gateway_id = "${aws_nat_gateway.gw.id}" 7 | } 8 | 9 | tags { 10 | Name = "Private Subnets" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_default_security_group.tf: -------------------------------------------------------------------------------- 1 | resource "aws_default_security_group" "default" { 2 | vpc_id = "${aws_vpc.main.id}" 3 | 4 | ingress { 5 | from_port = 0 6 | to_port = 0 7 | protocol = "-1" 8 | cidr_blocks = ["172.27.224.0/20", "172.31.0.0/16"] 9 | } 10 | 11 | egress { 12 | from_port = 0 13 | to_port = 0 14 | protocol = "-1" 15 | cidr_blocks = ["0.0.0.0/0"] 16 | } 17 | 18 | tags { 19 | Name = "sg-default" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_internet_gateway.tf: -------------------------------------------------------------------------------- 1 | resource "aws_internet_gateway" "gw" { 2 | vpc_id = "${aws_vpc.main.id}" 3 | 4 | tags { 5 | Name = "internet-gateway" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_main_route_table_association.tf: -------------------------------------------------------------------------------- 1 | resource "aws_main_route_table_association" "a" { 2 | vpc_id = "${aws_vpc.main.id}" 3 | route_table_id = "${aws_default_route_table.private.id}" 4 | } 5 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_nat_gateway.tf: -------------------------------------------------------------------------------- 1 | resource "aws_nat_gateway" "gw" { 2 | allocation_id = "${aws_eip.nat.id}" 3 | subnet_id = "${aws_subnet.public_a.id}" 4 | depends_on = ["aws_internet_gateway.gw"] 5 | } 6 | 7 | resource "aws_eip" "nat" { 8 | vpc = true 9 | } 10 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_route_table.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route_table" "public" { 2 | vpc_id = "${aws_vpc.main.id}" 3 | 4 | route { 5 | cidr_block = "0.0.0.0/0" 6 | gateway_id = "${aws_internet_gateway.gw.id}" 7 | } 8 | 9 | tags { 10 | Name = "Public Subnets" 11 | } 12 | } 13 | 14 | resource "aws_route_table" "elb" { 15 | vpc_id = "${aws_vpc.main.id}" 16 | 17 | route { 18 | cidr_block = "0.0.0.0/0" 19 | gateway_id = "${aws_internet_gateway.gw.id}" 20 | } 21 | 22 | tags { 23 | Name = "ELB Subnets" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /em2/deploy/account/aws_vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "main" { 2 | cidr_block = "${var.vpc_cidr}" 3 | 4 | tags { 5 | Name = "${var.vpc_name}" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /em2/deploy/account/em_provider.tf: -------------------------------------------------------------------------------- 1 | # AWS 2 | provider "aws" {} 3 | 4 | data "aws_region" "current" { 5 | current = true 6 | } 7 | 8 | # Use this data source to get the access to the effective 9 | # Account ID, User ID, and ARN in which Terraform is authorized. 10 | data "aws_caller_identity" "current" {} 11 | -------------------------------------------------------------------------------- /em2/deploy/account/outputs.tf: -------------------------------------------------------------------------------- 1 | output "Subnet ELB A" { 2 | value = "{${aws_subnet.elb_a.id}}" 3 | } 4 | 5 | output "Subnet ELB B" { 6 | value = "{${aws_subnet.elb_b.id}}" 7 | } 8 | 9 | output "Subnet ELB C" { 10 | value = "{${aws_subnet.elb_c.id}}" 11 | } 12 | 13 | output "Subnet Public A" { 14 | value = "{${aws_subnet.public_a.id}}" 15 | } 16 | 17 | output "Subnet Public B" { 18 | value = "{${aws_subnet.public_b.id}}" 19 | } 20 | 21 | output "Subnet Public C" { 22 | value = "{${aws_subnet.public_c.id}}" 23 | } 24 | 25 | output "Subnet Private A" { 26 | value = "{${aws_subnet.private_a.id}}" 27 | } 28 | 29 | output "Subnet Private B" { 30 | value = "{${aws_subnet.private_b.id}}" 31 | } 32 | 33 | output "Subnet Private C" { 34 | value = "{${aws_subnet.private_c.id}}" 35 | } 36 | -------------------------------------------------------------------------------- /em2/deploy/account/remote_state.tf: -------------------------------------------------------------------------------- 1 | # Write the remote state 2 | terraform { 3 | backend "s3" { 4 | bucket = "terraform-em-remote-state" 5 | key = "account" 6 | region = "eu-west-1" 7 | } 8 | } 9 | 10 | # Use the remote state 11 | data "terraform_remote_state" "remote_state" { 12 | backend = "s3" 13 | 14 | config { 15 | bucket = "terraform-em-remote-state" 16 | key = "account" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /em2/deploy/ami/ubuntu_1604/userdata_ubuntu_1604.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #init 3 | apt-get update 4 | apt-get install awscli -y 5 | 6 | #fetch init script from s3 7 | aws s3api get-object --bucket em-testing-init --key init_ubuntu_1604.sh ./init_ubuntu_1604.sh 8 | 9 | #execute init script 10 | chmod 700 ./init_ubuntu_1604.sh 11 | ./init_ubuntu_1604.sh 12 | -------------------------------------------------------------------------------- /em2/deploy/consul/outputs.tf: -------------------------------------------------------------------------------- 1 | output "server_address" { 2 | value = "${aws_instance.server.0.public_dns}" 3 | } 4 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/debian_consul.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=consul agent 3 | Wants=basic.target 4 | After=basic.target network.target 5 | 6 | [Service] 7 | EnvironmentFile=-/etc/default/consul 8 | Restart=on-failure 9 | ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir=/etc/consul.d 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | KillMode=process 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/debian_upstart.conf: -------------------------------------------------------------------------------- 1 | description "Consul agent" 2 | 3 | start on started networking 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | # This is to avoid Upstart re-spawning the process upon `consul leave` 8 | normal exit 0 INT 9 | 10 | script 11 | if [ -f "/etc/service/consul" ]; then 12 | . /etc/service/consul 13 | fi 14 | 15 | # Get the local IP 16 | BIND=`ifconfig eth0 | grep "inet addr" | awk '{ print substr($2,6) }'` 17 | 18 | exec /usr/local/bin/consul agent \ 19 | -config-dir="/etc/consul.d" \ 20 | -bind=$BIND \ 21 | ${CONSUL_FLAGS} \ 22 | >>/var/log/consul.log 2>&1 23 | end script 24 | 25 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/iptables.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8300 -j ACCEPT 5 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8301 -j ACCEPT 6 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8302 -j ACCEPT 7 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8400 -j ACCEPT 8 | 9 | if [ -d /etc/sysconfig ]; then 10 | sudo iptables-save | sudo tee /etc/sysconfig/iptables 11 | else 12 | sudo iptables-save | sudo tee /etc/iptables.rules 13 | fi 14 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/rhel_consul.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=consul agent 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | EnvironmentFile=-/etc/sysconfig/consul 8 | Restart=on-failure 9 | ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir=/etc/consul.d 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | KillMode=process 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/rhel_upstart.conf: -------------------------------------------------------------------------------- 1 | description "Consul agent" 2 | 3 | start on started network 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | # This is to avoid Upstart re-spawning the process upon `consul leave` 8 | normal exit 0 INT 9 | 10 | script 11 | if [ -f "/etc/service/consul" ]; then 12 | . /etc/service/consul 13 | fi 14 | 15 | # Make sure to use all our CPUs, because Consul can block a scheduler thread 16 | export GOMAXPROCS=`nproc` 17 | 18 | # Get the public IP 19 | BIND=`ifconfig eth0 | grep "inet addr" | awk '{ print substr($2,6) }'` 20 | 21 | exec /usr/local/bin/consul agent \ 22 | -config-dir="/etc/consul.d" \ 23 | -bind=$BIND \ 24 | ${CONSUL_FLAGS} \ 25 | >>/var/log/consul.log 2>&1 26 | end script 27 | -------------------------------------------------------------------------------- /em2/deploy/consul/scripts/service.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | echo "Starting Consul..." 5 | if [ -x "$(command -v systemctl)" ]; then 6 | echo "using systemctl" 7 | sudo systemctl enable consul.service 8 | sudo systemctl start consul 9 | else 10 | echo "using upstart" 11 | sudo start consul 12 | fi 13 | -------------------------------------------------------------------------------- /em2/deploy/em-child/em-provider.tf: -------------------------------------------------------------------------------- 1 | # AWS 2 | provider "aws" {} 3 | 4 | data "aws_region" "current" { 5 | current = true 6 | } 7 | 8 | # Use this data source to get the access to the effective 9 | # Account ID, User ID, and ARN in which Terraform is authorized. 10 | data "aws_caller_identity" "current" {} 11 | -------------------------------------------------------------------------------- /em2/deploy/em-child/outputs.tf: -------------------------------------------------------------------------------- 1 | # output "server_address" { 2 | # value = "${aws_instance.server.0.public_dns}" 3 | # } 4 | 5 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/debian_consul.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=consul agent 3 | Wants=basic.target 4 | After=basic.target network.target 5 | 6 | [Service] 7 | EnvironmentFile=-/etc/default/consul 8 | Restart=on-failure 9 | ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir=/etc/consul.d 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | KillMode=process 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/debian_upstart.conf: -------------------------------------------------------------------------------- 1 | description "Consul agent" 2 | 3 | start on started networking 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | # This is to avoid Upstart re-spawning the process upon `consul leave` 8 | normal exit 0 INT 9 | 10 | script 11 | if [ -f "/etc/service/consul" ]; then 12 | . /etc/service/consul 13 | fi 14 | 15 | # Get the local IP 16 | BIND=`ifconfig eth0 | grep "inet addr" | awk '{ print substr($2,6) }'` 17 | 18 | exec /usr/local/bin/consul agent \ 19 | -config-dir="/etc/consul.d" \ 20 | -bind=$BIND \ 21 | ${CONSUL_FLAGS} \ 22 | >>/var/log/consul.log 2>&1 23 | end script 24 | 25 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/iptables.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8300 -j ACCEPT 5 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8301 -j ACCEPT 6 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8302 -j ACCEPT 7 | sudo iptables -I INPUT -s 0/0 -p tcp --dport 8400 -j ACCEPT 8 | 9 | if [ -d /etc/sysconfig ]; then 10 | sudo iptables-save | sudo tee /etc/sysconfig/iptables 11 | else 12 | sudo iptables-save | sudo tee /etc/iptables.rules 13 | fi 14 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/rhel_consul.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=consul agent 3 | Requires=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | EnvironmentFile=-/etc/sysconfig/consul 8 | Restart=on-failure 9 | ExecStart=/usr/local/bin/consul agent $CONSUL_FLAGS -config-dir=/etc/consul.d 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | KillMode=process 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/rhel_upstart.conf: -------------------------------------------------------------------------------- 1 | description "Consul agent" 2 | 3 | start on started network 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | # This is to avoid Upstart re-spawning the process upon `consul leave` 8 | normal exit 0 INT 9 | 10 | script 11 | if [ -f "/etc/service/consul" ]; then 12 | . /etc/service/consul 13 | fi 14 | 15 | # Make sure to use all our CPUs, because Consul can block a scheduler thread 16 | export GOMAXPROCS=`nproc` 17 | 18 | # Get the public IP 19 | BIND=`ifconfig eth0 | grep "inet addr" | awk '{ print substr($2,6) }'` 20 | 21 | exec /usr/local/bin/consul agent \ 22 | -config-dir="/etc/consul.d" \ 23 | -bind=$BIND \ 24 | ${CONSUL_FLAGS} \ 25 | >>/var/log/consul.log 2>&1 26 | end script 27 | -------------------------------------------------------------------------------- /em2/deploy/em-child/scripts/service.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | echo "Starting Consul..." 5 | if [ -x "$(command -v systemctl)" ]; then 6 | echo "using systemctl" 7 | sudo systemctl enable consul.service 8 | sudo systemctl start consul 9 | else 10 | echo "using upstart" 11 | sudo start consul 12 | fi 13 | -------------------------------------------------------------------------------- /em2/deploy/em-master/aws_ami.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "ubuntu" { 2 | most_recent = true 3 | 4 | filter { 5 | name = "image-id" 6 | values = ["ami-a8d2d7ce"] 7 | } 8 | 9 | filter { 10 | name = "virtualization-type" 11 | values = ["hvm"] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /em2/deploy/em-master/aws_cloudwatch_event_rules.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_event_rule" "every_five_minutes" { 2 | name = "every-5-minutes" 3 | description = "5 minute repeating cloudwatch event" 4 | schedule_expression = "rate(5 minutes)" 5 | } 6 | 7 | resource "aws_cloudwatch_event_target" "scheduler_cloudwatch_scheduled_trigger" { 8 | rule = "${aws_cloudwatch_event_rule.every_five_minutes.name}" 9 | target_id = "em-scheduler-trigger" 10 | arn = "${aws_lambda_function.scheduler.arn}" 11 | } 12 | -------------------------------------------------------------------------------- /em2/deploy/em-master/aws_elasticache_cluster.tf: -------------------------------------------------------------------------------- 1 | resource "aws_elasticache_cluster" "cache-cluster" { 2 | cluster_id = "${var.stack}" 3 | node_type = "cache.t2.micro" 4 | 5 | subnet_group_name = "${var.redis_subnet_group}" 6 | engine = "redis" 7 | num_cache_nodes = 1 8 | port = 11211 9 | security_group_ids = ["${var.default_sg}"] 10 | } 11 | -------------------------------------------------------------------------------- /em2/deploy/em-master/aws_iam_instance_profile.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_instance_profile" "app" { 2 | name = "${var.stack}-app" 3 | role = "${aws_iam_role.app.name}" 4 | } 5 | -------------------------------------------------------------------------------- /em2/deploy/em-master/aws_sns_topic.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sns_topic" "alert_sns_topic" { 2 | name = "${var.stack}-alert-sns-topic" 3 | } 4 | -------------------------------------------------------------------------------- /em2/deploy/em-master/em-provider.tf: -------------------------------------------------------------------------------- 1 | # AWS 2 | provider "aws" {} 3 | 4 | data "aws_region" "current" { 5 | current = true 6 | } 7 | 8 | # Use this data source to get the access to the effective 9 | # Account ID, User ID, and ARN in which Terraform is authorized. 10 | data "aws_caller_identity" "current" {} 11 | -------------------------------------------------------------------------------- /em2/deploy/em-master/em-terraform-remote-state.tf: -------------------------------------------------------------------------------- 1 | # Write the remote state 2 | terraform { 3 | backend "s3" { 4 | bucket = "newco-remote-state-master" 5 | key = "demo" 6 | region = "eu-west-1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /em2/deploy/em-master/em-variables.tf: -------------------------------------------------------------------------------- 1 | # App 2 | 3 | variable "stack" {} 4 | 5 | variable "ec2_key_pair" {} 6 | 7 | # Buckets 8 | 9 | variable "bucket" {} 10 | 11 | variable "secure_bucket" {} 12 | 13 | variable "init_script_bucket" {} 14 | 15 | # Scheduler 16 | 17 | variable scheduler_package {} 18 | 19 | # Redis 20 | 21 | variable "redis_subnet_group" {} 22 | 23 | # Security Groups 24 | 25 | variable "default_sg" {} 26 | 27 | variable "app_sg_cidr_inbound" { 28 | type = "list" 29 | } 30 | 31 | variable "app_sg_cidr_outbound" { 32 | type = "list" 33 | } 34 | 35 | # Account 36 | 37 | variable "vpc_id" {} 38 | 39 | variable "subnet_ids" { 40 | type = "map" 41 | } 42 | -------------------------------------------------------------------------------- /em2/deploy/em-master/environment-manager.env: -------------------------------------------------------------------------------- 1 | EM_AWS_REGION=eu-west-1 2 | EM_AWS_S3_BUCKET=em-daveandjake-config 3 | EM_AWS_S3_KEY=config.json 4 | AWS_REGION=eu-west-1 5 | EM_PACKAGES_BUCKET=em-daveandjake-packages 6 | EM_PACKAGES_KEY_PREFIX=packages 7 | IS_PRODUCTION=true 8 | EM_REDIS_ADDRESS=em-cache-cluster.qcyyv8.0001.euw1.cache.amazonaws.com 9 | EM_REDIS_PORT=11211 10 | EM_REDIS_CRYPTO_KEY="abcdefg" 11 | -------------------------------------------------------------------------------- /em2/deploy/em-master/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | < fs.mkdir(outDir)); 15 | yield zipFiles(__dirname, config.files, destination); 16 | 17 | console.log(destination); 18 | }); 19 | 20 | function zipFiles(path, files, destination) { 21 | let archive = archiver(destination); 22 | archive.pipe(fs.createWriteStream(destination)); 23 | files.forEach(file => archive.glob(file, { cwd: path })); 24 | return archive.finalize(); 25 | } -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/InfraEnvironmentManagerScheduler/local/config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "em": { 3 | "host": "https://environment-manager-domain-name", 4 | "credentials": { 5 | "username": "username", 6 | "password": "password" 7 | } 8 | }, 9 | "limitToEnvironment": "xxx", 10 | "whatIf": true, 11 | "listSkippedInstances": true, 12 | "ignoreASGInstances": false, 13 | "errorOnFailure": true 14 | } -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/InfraEnvironmentManagerScheduler/local/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict' 3 | 4 | let config = require('./config.json'); 5 | let emFactory = require('../services/em'); 6 | let schedulerFactory = require('../scheduler'); 7 | 8 | let awsConfig = { region: 'eu-west-1' }; 9 | let em = emFactory.create(config.em); 10 | 11 | let context = { awsAccountId: 123456789, awsRegion: 'eu-west-1', env: { CHILD_ACCOUNT_ROLE_NAME: 'enterRoleName' } }; 12 | let scheduler = schedulerFactory.create(config, em, require('aws-sdk'), context); 13 | 14 | let write = result => console.log(JSON.stringify(result, null, 2)); 15 | 16 | scheduler.doScheduling() 17 | .then(write).catch(write); -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --recursive ./test --EM_PROFILE test/test-profile.json", 8 | "test-watch": "mocha --watch --recursive ./test --EM_PROFILE test/test-profile.json" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "aws-sdk": "^2.26.0", 14 | "eslint": "^3.17.1", 15 | "eslint-config-airbnb-base": "^11.1.1", 16 | "eslint-plugin-import": "^2.2.0", 17 | "mocha": "^3.2.0", 18 | "rewire": "^2.5.2", 19 | "sinon": "^1.17.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/scheduler/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let archiver = require('archiver-promise'); 4 | let fs = require('fs-extra'); 5 | let co = require('co'); 6 | 7 | let config = require('./package.json'); 8 | 9 | co(function* () { 10 | let { name, version } = config; 11 | let outDir = 'out'; 12 | let destination = `${outDir}/${name}-${version}.zip`; 13 | 14 | yield fs.remove(outDir).then(() => fs.mkdir(outDir)); 15 | yield zipFiles(__dirname, config.files, destination); 16 | 17 | console.log(destination); 18 | }); 19 | 20 | function zipFiles(path, files, destination) { 21 | let archive = archiver(destination); 22 | archive.pipe(fs.createWriteStream(destination)); 23 | files.forEach(file => archive.glob(file, { cwd: path })); 24 | return archive.finalize(); 25 | } -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/scheduler/local/config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "em": { 3 | "host": "https://environment-manager-domain-name", 4 | "credentials": { 5 | "username": "username", 6 | "password": "password" 7 | } 8 | }, 9 | "limitToEnvironment": "xxx", 10 | "whatIf": true, 11 | "listSkippedInstances": true, 12 | "ignoreASGInstances": false, 13 | "errorOnFailure": true 14 | } -------------------------------------------------------------------------------- /em2/deploy/em-master/lambda/scheduler/local/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict' 3 | 4 | let config = require('./config.json'); 5 | let emFactory = require('../services/em'); 6 | let schedulerFactory = require('../scheduler'); 7 | 8 | let awsConfig = { region: 'eu-west-1' }; 9 | let em = emFactory.create(config.em); 10 | 11 | let context = { awsAccountId: 123456789, awsRegion: 'eu-west-1', env: { CHILD_ACCOUNT_ROLE_NAME: 'enterRoleName' } }; 12 | let scheduler = schedulerFactory.create(config, em, require('aws-sdk'), context); 13 | 14 | let write = result => console.log(JSON.stringify(result, null, 2)); 15 | 16 | scheduler.doScheduling() 17 | .then(write).catch(write); -------------------------------------------------------------------------------- /em2/deploy/scripts/consul_server_install.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/em2/deploy/scripts/consul_server_install.sh -------------------------------------------------------------------------------- /em2/gateway/session/index.js: -------------------------------------------------------------------------------- 1 | let rl = require('roast-lambda'); 2 | let _ = require('lodash'); 3 | 4 | let lambdas = require('./src/lambdas'); 5 | 6 | function inject(fn) { 7 | return (args) => { 8 | return fn(args); 9 | }; 10 | } 11 | 12 | _.forOwn(lambdas, (lambda, name) => { 13 | lambda.handler = inject(lambda.handler); 14 | exports[name] = rl.init(lambda); 15 | }); -------------------------------------------------------------------------------- /em2/gateway/session/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth-lambda", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "roast-lambda": "^0.1.6" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /em2/gateway/session/src/lambdas.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let sessionService = require('./services/session-service'); 4 | 5 | exports.authenticate = { 6 | handler: ({ event, context }) => { 7 | let res = sessionService.authenticate({ user }) 8 | .then(jsonResponse); 9 | } 10 | } 11 | 12 | exports.getSession = { 13 | handler: ({ event, context }) => { 14 | return tokenService.createToken(user) 15 | .then(jsonResponse); 16 | } 17 | } 18 | 19 | exports.createToken = { 20 | handler: ({ event, context }) => { 21 | return tokenService.createSession() 22 | .then(jsonResponse); 23 | } 24 | } 25 | 26 | exports.storeToken = { 27 | handler: ({ event, context }) => { 28 | return tokenService.storeSession() 29 | .then(jsonResponse); 30 | } 31 | } 32 | 33 | function jsonResponse(data) { 34 | return { 35 | statusCode: 200, 36 | body: data ? JSON.stringify(data) : undefined 37 | }; 38 | } -------------------------------------------------------------------------------- /server/.eslintignore: -------------------------------------------------------------------------------- 1 | /acceptance-tests/**/*.* 2 | /build/**/*.* 3 | /deployment/**/*.* 4 | /node_modules/**/*.* 5 | /scripts/**/*.* 6 | /src/test/**/*.* 7 | -------------------------------------------------------------------------------- /server/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "airbnb", 3 | "validateIndentation": 2 4 | } -------------------------------------------------------------------------------- /server/api/api-utils/dateUtil.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | function beginningOfToday() { 6 | let today = new Date(); 7 | today.setHours(0, 0, 0, 0); 8 | return toDynamoFormat(today); 9 | } 10 | 11 | function toDynamoFormat(date) { 12 | return date.toISOString(); 13 | } 14 | 15 | module.exports = { beginningOfToday, toDynamoFormat }; 16 | -------------------------------------------------------------------------------- /server/api/api-utils/ifNotFound.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let { hasValue, when } = require('../../modules/functional'); 4 | 5 | let ifNotFound = when.bind(null, hasValue, data => res => res.json(data)); 6 | 7 | let notFoundMessage = resourceType => 8 | () => res => res.status(404).json({ error: `The ${resourceType} was not found` }); 9 | 10 | module.exports = { 11 | ifNotFound, 12 | notFoundMessage 13 | }; 14 | -------------------------------------------------------------------------------- /server/api/api-utils/logicalTableName.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const logicalTableNames = { 4 | accounts: 'InfraConfigAccounts', 5 | clusters: 'InfraConfigClusters', 6 | deploymentmaps: 'ConfigDeploymentMaps', 7 | environments: 'ConfigEnvironments', 8 | environmenttypes: 'ConfigEnvironments', 9 | lbsettings: 'InfraConfigLBSettings', 10 | lbupstream: 'InfraConfigLBUpstream', 11 | permissions: 'InfraConfigPermissions', 12 | services: 'InfraConfigServices' 13 | }; 14 | 15 | function logicalTableName(entityTypeName) { 16 | return logicalTableNames[entityTypeName]; 17 | } 18 | 19 | module.exports = logicalTableName; 20 | -------------------------------------------------------------------------------- /server/api/api-utils/notImplemented.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | function notImplemented(res, reason) { 6 | res.status(501); 7 | throw new Error(`Sorry, this action is not yet implemented: ${reason}`); 8 | } 9 | 10 | module.exports = notImplemented; 11 | -------------------------------------------------------------------------------- /server/api/api-utils/requestMetadata.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let getMetadataForDynamoAudit = req => ({ 6 | TransactionID: req.id, 7 | User: req.user.getName() 8 | }); 9 | 10 | module.exports = { 11 | getMetadataForDynamoAudit 12 | }; 13 | -------------------------------------------------------------------------------- /server/api/api-utils/requestParam.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let fp = require('lodash/fp'); 6 | 7 | module.exports = (name, req) => fp.get(['swagger', 'params', name, 'value'])(req); 8 | -------------------------------------------------------------------------------- /server/api/controllers/diagnostics/diagnosticsController.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../../config'); 6 | const clusterNode = require('../../../modules/clusterNode'); 7 | 8 | function getHealthcheck(req, res) { 9 | res.json({ 10 | OK: true, 11 | Version: config.get('APP_VERSION'), 12 | Leader: clusterNode.isLeader() 13 | }); 14 | } 15 | 16 | module.exports = { 17 | getHealthcheck 18 | }; 19 | -------------------------------------------------------------------------------- /server/api/controllers/load-balancer/loadBalancerController.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let sender = require('../../../modules/sender'); 6 | let ScanNginxUpstreams = require('../../../queryHandlers/ScanNginxUpstreams'); 7 | 8 | /** 9 | * GET /load-balancer/{name} 10 | */ 11 | function getLoadBalancer(req, res, next) { 12 | const fqdn = req.swagger.params.id.value; 13 | 14 | let query = { 15 | name: 'ScanNginxUpstreams', 16 | instanceDomainName: fqdn 17 | }; 18 | 19 | return sender.sendQuery(ScanNginxUpstreams, { query }) 20 | .then(data => res.json(data)) 21 | .catch(next); 22 | } 23 | 24 | module.exports = { 25 | getLoadBalancer 26 | }; 27 | -------------------------------------------------------------------------------- /server/api/controllers/package-upload-url/dynamicResponseCreator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function create(status, value) { 4 | return response => response.format({ 5 | 'text/plain': () => response.status(status).send(value), 6 | 'application/json': () => response.status(status).json({ url: value }) 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /server/appspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.0 2 | os: linux 3 | files: 4 | - source: / 5 | destination: /opt/environment-manager 6 | hooks: 7 | ApplicationStop: 8 | - location: deployment/code-deploy/application-stop.sh 9 | runas: root 10 | BeforeInstall: 11 | - location: deployment/code-deploy/before-install.sh 12 | runas: root 13 | AfterInstall: 14 | - location: deployment/code-deploy/on-after-install.sh 15 | runas: root 16 | ApplicationStart: 17 | - location: deployment/code-deploy/application-start.sh 18 | runas: root 19 | ValidateService: 20 | - location: deployment/code-deploy/validate-service.sh 21 | runas: root 22 | -------------------------------------------------------------------------------- /server/commands/aws/SetInstanceMaintenanceMode.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let serviceTargets = require('../../modules/service-targets'); 6 | 7 | module.exports = function SetTargetMaintenanceState(command) { 8 | let accountName = command.accountName; 9 | let environment = command.instance.getTag('Environment'); 10 | let host = command.instance.PrivateIpAddress; 11 | let enable = command.enable; 12 | 13 | return serviceTargets.setInstanceMaintenanceMode(accountName, host, environment, enable); 14 | }; 15 | -------------------------------------------------------------------------------- /server/commands/deployments/S3PathContract.schema.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | id: 'http://thetrainline.com/environment-manager/S3PathContract-schema#', 5 | $schema: 'http://json-schema.org/draft-04/schema#', 6 | title: 'S3PathContract', 7 | description: 'It represents the path of an object in S3', 8 | type: 'object', 9 | properties: { 10 | bucket: { 11 | description: 'Name of the S3 bucket', 12 | type: 'string' 13 | }, 14 | key: { 15 | description: 'Name of the S3 key', 16 | type: 'string' 17 | } 18 | }, 19 | required: [ 20 | 'bucket', 21 | 'key' 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /server/commands/services/DeleteTargetState.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | let serviceTargets = require('../../modules/service-targets'); 7 | let schema = require('../../modules/schema/schema'); 8 | 9 | module.exports = function DeleteTargetState(command) { 10 | return co(function* () { 11 | yield schema('DeleteTargetStateCommand').then(x => x.assert(command)); 12 | 13 | let key = command.key; 14 | return yield serviceTargets.removeTargetState(command.environment, { key }); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /server/commands/services/UpdateTargetState.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let serviceTargets = require('../../modules/service-targets'); 6 | let schema = require('../../modules/schema/schema'); 7 | let DeploymentLogsStreamer = require('../../modules/DeploymentLogsStreamer'); 8 | let deploymentLogsStreamer = new DeploymentLogsStreamer(); 9 | 10 | module.exports = function UpdateTargetState(command) { 11 | let { deploymentId, key, options, value } = command; 12 | return schema('UpdateTargetStateCommand') 13 | .then(x => x.assert(command)) 14 | .then(() => deploymentLogsStreamer.log(deploymentId, `Updating Consul key ${command.key}`)) 15 | .then(() => serviceTargets.setTargetState(command.environment, { key, value, options })); 16 | }; 17 | -------------------------------------------------------------------------------- /server/config/version.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const VERSION_INFO = 'version.txt'; 6 | 7 | let fs = require('fs'); 8 | let packageInfo = require('../package.json'); 9 | 10 | function getVersion() { 11 | if (fs.existsSync(VERSION_INFO)) { 12 | return fs.readFileSync(VERSION_INFO, 'utf-8').trim(); 13 | } else { 14 | return `${packageInfo.version}-DEV`; 15 | } 16 | } 17 | 18 | module.exports = { 19 | getVersion 20 | }; 21 | -------------------------------------------------------------------------------- /server/deployment/code-deploy/application-start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | systemctl enable environment-manager 4 | systemctl start environment-manager 5 | -------------------------------------------------------------------------------- /server/deployment/code-deploy/application-stop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if systemctl is-active environment-manager | grep "^active$"; then 4 | systemctl stop environment-manager 5 | fi 6 | -------------------------------------------------------------------------------- /server/deployment/code-deploy/before-install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | -------------------------------------------------------------------------------- /server/deployment/code-deploy/on-after-install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | create_systemd_unit() { 4 | echo "Creating systemd unit ${1}" >&2 5 | local SERVICE_DEFINITION=$1 6 | local SRC_FILE=/opt/environment-manager/deployment/systemd/${SERVICE_DEFINITION} 7 | local TARGET_FILE=/lib/systemd/system/${SERVICE_DEFINITION} 8 | 9 | cp -f ${SRC_FILE} ${TARGET_FILE} 10 | chmod 644 ${TARGET_FILE} 11 | chown root.root ${TARGET_FILE} 12 | } 13 | 14 | create_systemd_unit "environment-manager.service" 15 | create_systemd_unit "environment-manager-debug.service" 16 | 17 | echo "Allowing execution of environment-manager start script" >&2 18 | chmod a+x /opt/environment-manager/start -------------------------------------------------------------------------------- /server/deployment/code-deploy/validate-service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set +e 4 | 5 | max_retries=10 6 | retries=0 7 | http_port=40500 8 | 9 | echo "Verifying the installation" 10 | 11 | url="https://127.0.0.1:$http_port/diagnostics/healthchecks/ping" 12 | echo "Service URL=$url" 13 | 14 | while [[ $retries -lt $max_retries ]] 15 | do 16 | if curl -k -s $url --insecure | grep "Success"; then 17 | echo "Got Success!" 18 | exit 0 19 | fi 20 | sleep 1 21 | ((retries++)) 22 | done 23 | 24 | echo "max retries ($max_retries) reached, installation check failed" 25 | exit 1 26 | -------------------------------------------------------------------------------- /server/deployment/systemd/environment-manager-debug.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Conflicts=environment-manager.service 3 | 4 | [Service] 5 | EnvironmentFile=/etc/environment-manager.env 6 | WorkingDirectory=/opt/environment-manager/ 7 | ExecStart=/usr/bin/npm run start-debug 8 | 9 | Restart=always 10 | SyslogIdentifier=environment-manager-debug 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /server/deployment/systemd/environment-manager.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Conflicts=environment-manager-debug.service 3 | 4 | [Service] 5 | EnvironmentFile=/etc/environment-manager.env 6 | WorkingDirectory=/opt/environment-manager/ 7 | ExecStart=/opt/environment-manager/start 8 | 9 | Restart=always 10 | StandardOutput=syslog 11 | StandardError=syslog 12 | SyslogIdentifier=environment-manager 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /server/healthchecks/sensu/healthchecks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import requests 3 | from requests import ConnectionError 4 | 5 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 6 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 7 | 8 | def success(): 9 | print('Healthcheck passed') 10 | sys.exit(0) 11 | 12 | def fail(reason): 13 | print(reason) 14 | sys.exit(2) 15 | 16 | def run_check(name): 17 | url = 'https://localhost:40500/diagnostics/healthchecks/' + name 18 | 19 | try: 20 | response = requests.get(url, verify=False) 21 | except ConnectionError: 22 | fail('Could not connect to ' + url) 23 | except Exception as ex: 24 | fail('An error ocurred: ' + ex.message) 25 | 26 | if response.status_code == 200: 27 | success() 28 | else: 29 | fail(response.json()['reason']) -------------------------------------------------------------------------------- /server/healthchecks/sensu/healthchecks.yml: -------------------------------------------------------------------------------- 1 | sensu_healthchecks: 2 | sensu_check1: 3 | name: enivornment-manager-ping 4 | local_script: ping.py 5 | interval: 10 6 | alert_after: 6 7 | tip: Checks that environment manager is reachable 8 | runbook: Check that the service is running properly 9 | sensu_check2: 10 | name: enivornment-manager-redis 11 | local_script: redis.py 12 | interval: 10 13 | alert_after: 6 14 | tip: Checks that environment manager can connect to the redis cluster it uses for user session storage 15 | runbook: Check the redis cluster is up and running -------------------------------------------------------------------------------- /server/healthchecks/sensu/ping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import healthchecks 3 | healthchecks.run_check('ping') -------------------------------------------------------------------------------- /server/healthchecks/sensu/redis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import healthchecks 3 | healthchecks.run_check('redis') -------------------------------------------------------------------------------- /server/models/Deployment.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let _ = require('lodash'); 6 | let deployments = require('../modules/data-access/deployments'); 7 | 8 | class Deployment { 9 | 10 | constructor(data, expectedNodes = undefined) { 11 | _.assign(this, data); 12 | if (expectedNodes !== undefined) { 13 | _.assign(this, { ExpectedNodes: expectedNodes }); 14 | } 15 | } 16 | 17 | static getById(key) { 18 | return deployments.get({ DeploymentID: key }); 19 | } 20 | } 21 | 22 | module.exports = Deployment; 23 | -------------------------------------------------------------------------------- /server/models/EnvironmentType.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let _ = require('lodash'); 6 | let environmentDatabase = require('../modules/environmentDatabase'); 7 | 8 | class EnvironmentType { 9 | 10 | constructor(data) { 11 | _.assign(this, data); 12 | } 13 | 14 | static getByName(name) { 15 | return environmentDatabase.getEnvironmentTypeByName(name); 16 | } 17 | 18 | } 19 | 20 | module.exports = EnvironmentType; 21 | -------------------------------------------------------------------------------- /server/models/Image.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let _ = require('lodash'); 6 | let sender = require('../modules/sender'); 7 | let co = require('co'); 8 | let ScanCrossAccountImages = require('../queryHandlers/ScanCrossAccountImages'); 9 | 10 | class Image { 11 | 12 | constructor(data) { 13 | _.assign(this, data); 14 | } 15 | 16 | static getById(id) { 17 | return co(function* () { 18 | let images = yield sender.sendQuery(ScanCrossAccountImages, { 19 | query: { 20 | name: 'ScanCrossAccountImages' 21 | } 22 | }); 23 | let image = _.find(images, { ImageId: id }); 24 | return new Image(image); 25 | }); 26 | // let image = yield imageProvider.get(awsLaunchConfig.ImageId); 27 | } 28 | 29 | } 30 | 31 | module.exports = Image; 32 | -------------------------------------------------------------------------------- /server/models/Service.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let _ = require('lodash'); 6 | let servicesDb = require('../modules/data-access/services'); 7 | 8 | class Service { 9 | 10 | constructor(data) { 11 | _.assign(this, data); 12 | } 13 | 14 | static getByName(name) { 15 | return servicesDb.get({ ServiceName: name }) 16 | .then(obj => (obj ? new Service(obj) : null)); 17 | } 18 | } 19 | 20 | module.exports = Service; 21 | -------------------------------------------------------------------------------- /server/models/TaggableMixin.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let _ = require('lodash'); 6 | 7 | let TaggableMixin = Base => class extends Base { 8 | getTag(key, defaultValue) { 9 | let tag = _.find(this.Tags, { Key: key }); 10 | if (tag === undefined) { 11 | if (arguments.length <= 1) { 12 | throw new Error(`Can't find tag "${key}"`); 13 | } else { 14 | return defaultValue; 15 | } 16 | } 17 | return tag.Value; 18 | } 19 | 20 | setTag(key, value) { 21 | let tag = this.getTag(key); 22 | if (tag === undefined) { 23 | tag = { 24 | Key: key, 25 | Value: value 26 | }; 27 | this.Tags.push(tag); 28 | } else { 29 | tag.Value = value; 30 | } 31 | } 32 | }; 33 | 34 | module.exports = TaggableMixin; 35 | -------------------------------------------------------------------------------- /server/modules/PackagePathProvider.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let S3PathContract = require('./deployment/S3PathContract'); 6 | let configCache = require('./configurationCache'); 7 | 8 | module.exports = function PackagePathProvider() { 9 | this.getS3Path = function (deployment) { 10 | return configCache 11 | .getEnvironmentTypeByName(deployment.environmentTypeName) 12 | .then((environmentType) => { 13 | let filePath = `${deployment.environmentName}/${deployment.serviceName}`; 14 | let fileName = `${deployment.serviceName}-${deployment.serviceVersion}.zip`; 15 | 16 | return Promise.resolve(new S3PathContract({ 17 | bucket: environmentType.DeploymentBucket, 18 | key: `${filePath}/${fileName}` 19 | })); 20 | }); 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /server/modules/active-directory-adapter/activeDirectoryAdapterConfiguration.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | 7 | module.exports = function ActiveDirectoryAdapterConfiguration() { 8 | let configuration; 9 | 10 | this.get = function getConfiguration() { 11 | if (!configuration) { 12 | configuration = config.getUserValue('local').ActiveDirectory; 13 | } 14 | 15 | return configuration; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /server/modules/active-directory-adapter/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | const mock = require('./activeDirectoryAdapter.mock.js'); 7 | const prod = require('./activeDirectoryAdapter.prod.js'); 8 | 9 | let Implementation = config.get('IS_PRODUCTION') ? prod : mock; 10 | 11 | module.exports = new Implementation(); 12 | -------------------------------------------------------------------------------- /server/modules/amazon-client/masterAccountClient.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let AWS = require('aws-sdk'); 6 | 7 | module.exports = { 8 | createLowLevelDynamoClient: () => Promise.resolve(new AWS.DynamoDB()), 9 | createDynamoClient: () => Promise.resolve(new AWS.DynamoDB.DocumentClient()), 10 | createASGClient: () => Promise.resolve(new AWS.AutoScaling()), 11 | createEC2Client: () => Promise.resolve(new AWS.EC2()), 12 | createIAMClient: () => Promise.resolve(new AWS.IAM()), 13 | createS3Client: () => Promise.resolve(new AWS.S3()), 14 | createSNSClient: () => Promise.resolve(new AWS.SNS()) 15 | }; 16 | -------------------------------------------------------------------------------- /server/modules/amazon-client/myIdentity.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let AWS = require('aws-sdk'); 6 | let memoize = require('../memoize'); 7 | 8 | let myIdentity = memoize(() => (new AWS.STS()).getCallerIdentity().promise()); 9 | 10 | module.exports = myIdentity; 11 | -------------------------------------------------------------------------------- /server/modules/authentication.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | function isUserAuthenticated(user) { 6 | return !!user; 7 | } 8 | 9 | module.exports = { 10 | allowUnknown(request, response, next) { 11 | return next(); 12 | }, 13 | 14 | denyUnauthorized(request, response, next) { 15 | // User not authenticated 16 | if (!isUserAuthenticated(request.user)) { 17 | response.status(403); 18 | response.json({ 19 | error: 'Access denied. Please log in' 20 | }); 21 | } else { 22 | next(); 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /server/modules/authentications/cookieAuthentication.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let userService = require('../user-service'); 6 | let cookieAuthenticationConfiguration = require('./cookieAuthenticationConfiguration'); 7 | 8 | module.exports = { 9 | middleware(req, res, next) { 10 | if (req.user) return next(); 11 | 12 | let cookie = req.cookies[cookieAuthenticationConfiguration.getCookieName()]; 13 | if (!cookie) return next(); 14 | 15 | return userService.getUserByToken(cookie) 16 | .then((user) => { 17 | req.user = user; 18 | req.authenticatedBy = 'cookie'; 19 | next(); 20 | }, () => next()); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /server/modules/authentications/tokenAuthentication.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let userService = require('../user-service'); 6 | 7 | const PATTERN = /bearer\s+(.*)/i; 8 | 9 | module.exports = { 10 | middleware(req, res, next) { 11 | if (req.user) return next(); 12 | 13 | let authorization = req.headers.authorization; 14 | if (!authorization) return next(); 15 | 16 | let match = PATTERN.exec(authorization); 17 | if (!match) return next(); 18 | 19 | return userService.getUserByToken(match[1]) 20 | .then((user) => { 21 | req.user = user; 22 | req.authenticatedBy = 'bearer'; 23 | return next(); 24 | }, (error) => { 25 | res.status(401); 26 | res.send(error.message); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /server/modules/authentications/tokenAuthenticationConfiguration.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let config = require('../../config'); 7 | 8 | function loadConfiguration() { 9 | let localConfig = config.getUserValue('local'); 10 | 11 | assert(localConfig.authentication, 'missing \'authentication\' field in configuration'); 12 | assert(localConfig.authentication.tokenDuration, 'missing \'authentication.tokenDuration\' field in configuration'); 13 | 14 | return { 15 | tokenDuration: localConfig.authentication.tokenDuration 16 | }; 17 | } 18 | 19 | module.exports = { 20 | getTokenDuration: () => { 21 | let configuration = loadConfiguration(); 22 | return configuration.tokenDuration; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /server/modules/authorizers/allow-authenticated.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | exports.getRules = () => Promise.resolve([]); 6 | 7 | exports.docs = { 8 | requiresClusterPermissions: false, 9 | requiresEnvironmentTypePermissions: false 10 | }; 11 | -------------------------------------------------------------------------------- /server/modules/authorizers/deployments.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | let deploymentsHelper = require('../queryHandlersUtil/deployments-helper'); 7 | 8 | module.exports = { 9 | getRules(request) { 10 | return co(function* () { 11 | let key = request.swagger.params.id.value; 12 | let deployment = yield deploymentsHelper.get({ key }); 13 | 14 | return [{ 15 | resource: request.url.replace(/\/+$/, ''), 16 | access: request.method, 17 | clusters: [deployment.Value.OwningCluster], 18 | environmentTypes: [deployment.Value.EnvironmentType] 19 | }]; 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /server/modules/authorizers/instances.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let Instance = require('../../models/Instance'); 6 | let co = require('co'); 7 | 8 | exports.getRules = function getRules(request) { 9 | const id = request.swagger.params.id.value; 10 | 11 | return co(function* _() { 12 | const instance = yield Instance.getById(id); 13 | const owningCluster = instance.getTag('OwningCluster'); 14 | 15 | return [{ 16 | resource: request.url.replace(/\/+$/, ''), 17 | access: request.method, 18 | clusters: [owningCluster] 19 | }]; 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /server/modules/authorizers/none.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | exports.getRules = () => Promise.resolve([]); 6 | 7 | exports.docs = { 8 | requiresClusterPermissions: false, 9 | requiresEnvironmentTypePermissions: false 10 | }; 11 | -------------------------------------------------------------------------------- /server/modules/authorizers/services.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | exports.getRules = (request) => { 6 | let body = request.params.body || request.body; 7 | let cluster = request.params.range || request.params.cluster || body.OwningCluster; 8 | 9 | return Promise.resolve([{ 10 | resource: request.url.replace(/\/+$/, ''), 11 | access: request.method, 12 | clusters: [cluster.toLowerCase()] 13 | }]); 14 | }; 15 | 16 | exports.docs = { 17 | requiresClusterPermissions: true, 18 | requiresEnvironmentTypePermissions: false 19 | }; 20 | -------------------------------------------------------------------------------- /server/modules/authorizers/simple.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | exports.getRules = request => Promise.resolve([{ 6 | resource: request.url.replace(/\/+$/, ''), 7 | access: request.method 8 | }]); 9 | 10 | exports.docs = { 11 | requiresClusterPermissions: false, 12 | requiresEnvironmentTypePermissions: false 13 | }; 14 | -------------------------------------------------------------------------------- /server/modules/awsResourceNameProvider.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../config'); 6 | let resourcePrefix = config.get('EM_AWS_RESOURCE_PREFIX'); 7 | 8 | module.exports = { 9 | getTableName: tableName => `${resourcePrefix}${tableName}` 10 | }; 11 | -------------------------------------------------------------------------------- /server/modules/base64.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const buffer = require('buffer'); 4 | const base64 = 'base64'; 5 | const utf8 = 'utf8'; 6 | 7 | let decode = str => JSON.parse(new buffer.Buffer(str, base64).toString(utf8)); 8 | 9 | let encode = obj => new buffer.Buffer(JSON.stringify(obj), utf8).toString(base64); 10 | 11 | module.exports = { 12 | decode, 13 | encode 14 | }; 15 | -------------------------------------------------------------------------------- /server/modules/clientFactories/autoScalingGroupClientFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const asgResourceFactory = require('../../modules/resourceFactories/asgResourceFactory'); 6 | 7 | module.exports = { 8 | create(parameters) { 9 | return asgResourceFactory.create(undefined, parameters); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /server/modules/clientFactories/ec2InstanceClientFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const ec2InstanceResourceFactory = require('../resourceFactories/ec2InstanceResourceFactory'); 6 | 7 | module.exports = { 8 | create(parameters) { 9 | return ec2InstanceResourceFactory.create(undefined, parameters); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /server/modules/clientFactories/iamRoleClientFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let IAMRoleClient = require('./IAMRoleClient'); 6 | 7 | module.exports = { 8 | create(parameters) { 9 | return new Promise((resolve) => { 10 | let client = new IAMRoleClient(parameters.accountName); 11 | resolve(client); 12 | }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /server/modules/clientFactories/snsTopicClientFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let SNSTopicClient = require('./SNSTopicClient'); 6 | 7 | module.exports = { 8 | create(parameters) { 9 | return new Promise((resolve) => { 10 | let client = new SNSTopicClient(parameters.accountName); 11 | resolve(client); 12 | }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /server/modules/clusterNode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const cluster = require('exp-leader-election'); 4 | const logger = require('./logger'); 5 | const config = require('../config').getUserValue('local'); 6 | 7 | const consulConfig = { 8 | key: 'locks/envmgr/leader', 9 | consul: config.consul 10 | }; 11 | 12 | let leader = false; 13 | 14 | cluster(consulConfig) 15 | .on('gainedLeadership', (...args) => { 16 | leader = true; 17 | logger.debug('Gained leadership', ...args); 18 | }) 19 | .on('error', (err) => { 20 | if (leader) { 21 | logger.debug('Lost leadership'); 22 | } 23 | leader = false; 24 | logger.error('Error during leader election / renewal', err); 25 | }); 26 | 27 | module.exports = { 28 | isLeader: () => { 29 | return leader; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /server/modules/configuration/S3ConfigurationProvider.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let masterAccountClient = require('../amazon-client/masterAccountClient'); 6 | let config = require('../../config'); 7 | 8 | const S3_BUCKET = config.get('EM_AWS_S3_BUCKET'); 9 | const S3_KEY = config.get('EM_AWS_S3_KEY'); 10 | 11 | module.exports = function S3ConfigurationProvider() { 12 | this.get = function getConfigurationFromS3() { 13 | let parameters = { 14 | Bucket: S3_BUCKET, 15 | Key: S3_KEY 16 | }; 17 | 18 | return masterAccountClient 19 | .createS3Client() 20 | .then(client => client.getObject(parameters).promise()) 21 | .then(object => JSON.parse(object.Body.toString('utf8'))); 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /server/modules/consul-client/clientConfig.mock.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = parameters => ( 6 | Promise.resolve({ 7 | host: '10.249.16.74', 8 | port: '8500', 9 | defaults: { 10 | dc: 'tl-c50' 11 | }, 12 | promisify: parameters.promisify 13 | }) 14 | ); 15 | -------------------------------------------------------------------------------- /server/modules/consul-client/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | let consul = require('consul'); 7 | let mock = require('./clientConfig.mock.js'); 8 | let prod = require('./clientConfig.prod.js'); 9 | 10 | let clientConfig = config.get('IS_PRODUCTION') ? prod : mock; 11 | 12 | function createConfig(options) { 13 | return clientConfig(options); 14 | } 15 | 16 | function create(options) { 17 | return createConfig(options).then(newConfig => consul(newConfig)); 18 | } 19 | 20 | module.exports = { createConfig, create }; 21 | -------------------------------------------------------------------------------- /server/modules/consul-node/consul-node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class ConsulNode { 4 | constructor(jsonRepresentation = {}) { 5 | this.json = jsonRepresentation; 6 | } 7 | 8 | getServiceVersion() { 9 | return this.json.ServiceTags.find(st => st.split(':')[0].toLowerCase() === 'version'); 10 | } 11 | 12 | getJson() { 13 | return this.json; 14 | } 15 | } 16 | 17 | module.exports = { 18 | ConsulNode 19 | }; 20 | -------------------------------------------------------------------------------- /server/modules/consulDataStructures.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let { defaultTo, flow, get, groupBy, map, mapValues } = require('lodash/fp'); 4 | 5 | function tagsOf(item) { 6 | function parseTag(str) { 7 | let sepIdx = str.indexOf(':'); 8 | return [str.slice(0, sepIdx), str.slice(sepIdx + 1)]; 9 | } 10 | return flow( 11 | get('Tags'), 12 | map(parseTag), 13 | groupBy(([key]) => key), 14 | mapValues(flow( 15 | map(([, value]) => value))))(item); 16 | } 17 | 18 | let valueOfTag = key => flow(tagsOf, get(key), defaultTo([])); 19 | 20 | module.exports = { 21 | tagsOf, 22 | valueOfTag 23 | }; 24 | -------------------------------------------------------------------------------- /server/modules/data-access/accounts.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'InfraConfigAccounts'; 6 | const TTL = 600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/cachedSingleAccountDynamoTable.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let singleAccountDynamoTable = require('./singleAccountDynamoTable'); 6 | let dynamoTableCache = require('./dynamoTableCache'); 7 | 8 | function factory(physicalTableName, { ttl }) { 9 | let cachedTable = dynamoTableCache(physicalTableName, { ttl }); 10 | return singleAccountDynamoTable(physicalTableName, cachedTable); 11 | } 12 | 13 | module.exports = factory; 14 | -------------------------------------------------------------------------------- /server/modules/data-access/clusters.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'InfraConfigClusters'; 6 | const TTL = 3600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/configEnvironmentTypes.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'ConfigEnvironmentTypes'; 6 | const TTL = 600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/configEnvironments.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'ConfigEnvironments'; 6 | const TTL = 600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/deploymentMaps.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'ConfigDeploymentMaps'; 6 | const TTL = 600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/describeDynamoTable.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let { createLowLevelDynamoClient: DynamoDB } = require('../amazon-client/masterAccountClient'); 6 | let memoize = require('../memoize'); 7 | 8 | function describeTableArn(TableName) { 9 | return DynamoDB().then(dynamo => dynamo.describeTable({ TableName }).promise()); 10 | } 11 | 12 | /** 13 | * @description Return a memoized description of a DynamoDB table 14 | * @param {string} TableName - The name of the table 15 | * @returns {object} - The table description 16 | */ 17 | let describe = memoize(describeTableArn); 18 | 19 | module.exports = describe; 20 | -------------------------------------------------------------------------------- /server/modules/data-access/loadBalancerSettings.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'InfraConfigLBSettings'; 6 | const TTL = 600; // seconds 7 | 8 | let { getTableName: physicalTableName } = require('../awsResourceNameProvider'); 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/notificationSettings.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'ConfigNotificationSettings'; 6 | const TTL = 1200; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/data-access/permissions.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const LOGICAL_TABLE_NAME = 'InfraConfigPermissions'; 6 | const TTL = 600; // seconds 7 | 8 | let physicalTableName = require('../awsResourceNameProvider').getTableName; 9 | let cachedSingleAccountDynamoTable = require('./cachedSingleAccountDynamoTable'); 10 | 11 | module.exports = cachedSingleAccountDynamoTable(physicalTableName(LOGICAL_TABLE_NAME), { ttl: TTL }); 12 | -------------------------------------------------------------------------------- /server/modules/deployment/DeploymentContract.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let deploymentValidators = require('./deploymentValidators'); 6 | let _ = require('lodash'); 7 | 8 | class DeploymentContract { 9 | 10 | constructor(data) { 11 | _.assign(this, data); 12 | } 13 | 14 | validate(configuration) { 15 | // Checking deployment is valid through all validators otherwise return a rejected promise 16 | return deploymentValidators.map(validator => validator.validate(this, configuration)); 17 | } 18 | 19 | } 20 | 21 | 22 | module.exports = DeploymentContract; 23 | -------------------------------------------------------------------------------- /server/modules/deployment/S3PathContract.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = function S3PathContract(options) { 6 | this.bucket = options.bucket; 7 | this.key = options.key; 8 | this.getType = () => this.constructor.name; 9 | }; 10 | -------------------------------------------------------------------------------- /server/modules/deployment/deploymentDefinition.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | function getKeyValue(deployment) { 6 | let deploymentId = deployment.id; 7 | let deploymentKeyValue = { 8 | key: `deployments/${deploymentId}/overall_status`, 9 | value: 'In Progress' 10 | }; 11 | 12 | return Promise.resolve(deploymentKeyValue); 13 | } 14 | 15 | module.exports = { 16 | getKeyValue 17 | }; 18 | -------------------------------------------------------------------------------- /server/modules/deployment/deploymentValidators.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const blueGreenDeploymentValidator = require('./validators/blueGreenDeploymentValidator'); 6 | const uniqueServiceVersionDeploymentValidator = require('./validators/uniqueServiceVersionDeploymentValidator'); 7 | 8 | let validators = [ 9 | blueGreenDeploymentValidator, 10 | uniqueServiceVersionDeploymentValidator 11 | ]; 12 | 13 | module.exports = validators; 14 | -------------------------------------------------------------------------------- /server/modules/deployment/serviceInstallationDefinition.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | function getKeyValue(deployment, s3Path) { 6 | let environmentName = deployment.environmentName; 7 | let serviceName = deployment.serviceName; 8 | let serviceVersion = deployment.serviceVersion; 9 | 10 | let serviceInstallationKeyValue = { 11 | key: `environments/${environmentName}/services/${serviceName}/${serviceVersion}/installation`, 12 | value: { 13 | PackageBucket: s3Path.bucket, 14 | PackageKey: s3Path.key, 15 | InstallationTimeout: 20 // Todo: Should be read from the service definition 16 | } 17 | }; 18 | 19 | return Promise.resolve(serviceInstallationKeyValue); 20 | } 21 | 22 | module.exports = { 23 | getKeyValue 24 | }; 25 | -------------------------------------------------------------------------------- /server/modules/errors/ActiveDirectoryError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function ActiveDirectoryError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/AutoScalingGroupAlreadyExistsError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function AutoScalingGroupAlreadyExistsError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/AutoScalingGroupNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function AutoScalingGroupNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/AwsError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function AwsError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/BadRequestError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function BadRequestError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/ConfigurationError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function ConfigurationError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/DeploymentValidationError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let BaseError = require('./BaseError.class'); 6 | 7 | module.exports = class DeploymentValidationError extends BaseError { 8 | 9 | constructor(message, innerError) { 10 | super(); 11 | this.name = this.constructor.name; 12 | this.message = message; 13 | this.innerError = innerError; 14 | 15 | Error.captureStackTrace(this, this.constructor); 16 | } 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /server/modules/errors/DynamoItemNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function DynamoItemNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/HttpRequestError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function HttpRequestError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/ImageNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function ImageNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InconsistentSlicesStatusError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InconsistentSlicesStatusError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InstanceNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InstanceNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InstanceProfileNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InstanceProfileNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InvalidContractError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InvalidContractError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InvalidCredentialsError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InvalidCredentialsError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InvalidItemSchemaError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InvalidItemSchemaError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/InvalidOperationError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function InvalidOperationError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/KeyPairNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function KeyPairNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/LaunchConfigurationAlreadyExistsError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function LaunchConfigurationAlreadyExistsError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/PackagePreparationError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function PackagePreparationError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/ResourceLockedError.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function ResourceLockedError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/ResourceNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function ResourceNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/RoleNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function RoleNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/errors/TopicNotFoundError.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let util = require('util'); 6 | let BaseError = require('./BaseError.class'); 7 | 8 | module.exports = function TopicNotFoundError(message, innerError) { 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.innerError = innerError; 12 | 13 | Error.captureStackTrace(this, this.constructor); 14 | }; 15 | 16 | util.inherits(module.exports, BaseError); 17 | -------------------------------------------------------------------------------- /server/modules/express-middleware/deprecateMiddleware.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | /** 4 | * Express middleware that adds deprecated=true to a request and sets the Warning HTTP header 5 | * on the response if it is a request to a deprecated route. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | function create(fn) { 11 | return function deprecateMiddleware(req, res, next) { 12 | try { 13 | let warning = fn(req); 14 | if (warning) { 15 | let now = new Date().toUTCString(); 16 | res.locals.deprecated = true; 17 | res.append('Warning', `299 - Deprecated: ${warning} "${now}"`); 18 | } 19 | next(); 20 | } catch (error) { 21 | next(error); 22 | } 23 | }; 24 | } 25 | 26 | module.exports = create; 27 | -------------------------------------------------------------------------------- /server/modules/express-middleware/swaggerNewRelicMiddleware.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const yaml = require('js-yaml'); 5 | const fs = require('fs'); 6 | const apiSpec = yaml.safeLoad(fs.readFileSync('api/swagger.yaml', 'utf8')); 7 | const API_BASE_PATH = apiSpec.basePath; 8 | const isNewRelicInUse = require('../new-relic/check'); 9 | 10 | function newRelicSwaggerMiddleware(req, res, next) { 11 | let newrelic = require('newrelic'); // eslint-disable-line global-require 12 | newrelic.setTransactionName(path.join(API_BASE_PATH, req.path)); 13 | next(); 14 | } 15 | 16 | function newRelicSwaggerMiddlewareNoOp(req, res, next) { 17 | next(); 18 | } 19 | 20 | const swaggerNewRelic = isNewRelicInUse() ? newRelicSwaggerMiddleware : newRelicSwaggerMiddlewareNoOp; 21 | 22 | module.exports = swaggerNewRelic; 23 | -------------------------------------------------------------------------------- /server/modules/functional.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let when = (cond, fnTrue, fnFalse = () => null) => 4 | x => (cond(x) ? fnTrue(x) : fnFalse(x)); 5 | 6 | let hasValue = x => 7 | (x !== null && x !== undefined); 8 | 9 | module.exports = { 10 | when, 11 | hasValue 12 | }; 13 | -------------------------------------------------------------------------------- /server/modules/health-checks/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let resultCodes = require('./resultCodes'); 6 | const ping = require('./library/ping'); 7 | const redis = require('./library/redis'); 8 | 9 | let checks = [ 10 | ping, 11 | redis 12 | // ... add more health checks here 13 | ]; 14 | 15 | module.exports = { 16 | resultCodes, 17 | checks 18 | }; 19 | -------------------------------------------------------------------------------- /server/modules/health-checks/library/ping.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let HealthCheckResults = require('../resultCodes'); 6 | 7 | module.exports = { 8 | url: '/ping', 9 | run: () => { 10 | return Promise.resolve({ 11 | result: HealthCheckResults.SUCCESS 12 | }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /server/modules/health-checks/library/redis.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | 7 | let HealthCheckResults = require('../resultCodes'); 8 | let UserSessionStore = require('../../userSessionStore'); 9 | 10 | function getResult(status) { 11 | if (status === 'wait' || status === 'ready') { 12 | return { result: HealthCheckResults.SUCCESS }; 13 | } 14 | 15 | return { 16 | result: HealthCheckResults.FAIL, 17 | reason: `Redis connection status is '${status}'` 18 | }; 19 | } 20 | 21 | module.exports = { 22 | url: '/redis', 23 | run: () => { 24 | return co(function* () { 25 | let sessionStore = yield UserSessionStore.get(); 26 | return getResult(sessionStore.status()); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /server/modules/health-checks/resultCodes.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = { 6 | SUCCESS: 'Success', 7 | FAIL: 'Failed' 8 | }; 9 | -------------------------------------------------------------------------------- /server/modules/http-server-factory/HttpServerFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let http = require('http'); 6 | 7 | function HttpServerFactory() { 8 | this.create = function (application, parameters) { 9 | return new Promise((resolve) => { 10 | let port = parameters.port; 11 | let server = http.createServer(application); 12 | server.listen(port, () => resolve(server)); 13 | }); 14 | }; 15 | } 16 | 17 | module.exports = HttpServerFactory; 18 | -------------------------------------------------------------------------------- /server/modules/http-server-factory/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | const Ssl = require('./HttpsServerFactory'); 7 | const NoSsl = require('./HttpServerFactory'); 8 | let implementation; 9 | 10 | if (config.get('IS_PRODUCTION') && !config.get('USE_HTTP')) { 11 | implementation = new Ssl(); 12 | } else { 13 | implementation = new NoSsl(); 14 | } 15 | 16 | module.exports = implementation; 17 | -------------------------------------------------------------------------------- /server/modules/new-relic/check.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = () => process.env.NEW_RELIC_APP_NAME !== undefined; 6 | -------------------------------------------------------------------------------- /server/modules/provisioning/Image.class.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | 7 | module.exports = function Image(imageSummary) { 8 | assert(imageSummary, 'Expected "ami" argument not to be null.'); 9 | 10 | this.id = imageSummary.ImageId; 11 | this.creationDate = imageSummary.CreationDate; 12 | this.platform = imageSummary.Platform; 13 | this.name = imageSummary.Name; 14 | this.description = imageSummary.Description; 15 | this.type = imageSummary.AmiType; 16 | this.version = imageSummary.AmiVersion; 17 | this.isCompatibleImage = imageSummary.IsCompatibleImage; 18 | this.encrypted = imageSummary.Encrypted; 19 | this.isStable = imageSummary.IsStable; 20 | this.rootVolumeSize = imageSummary.RootVolumeSize; 21 | }; 22 | -------------------------------------------------------------------------------- /server/modules/provisioning/launchConfiguration/userData/linux-user-data.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | /etc/puppet/tools/machine_boot -t name=,environmenttype=${EnvironmentType},environment=${Environment},securityzone=${SecurityZone},owningcluster=${OwningCluster},role=${Role},contactemail=${ContactEmail},projectcode=${ProjectCode} ${PuppetRole} > /tmp/machine_boot.log 3 | -------------------------------------------------------------------------------- /server/modules/provisioning/launchConfiguration/userData/windows-user-data.txt: -------------------------------------------------------------------------------- 1 | 2 | if(test-path "C:\TTLApps\ttl-appbootstrapper\configure.ps1"){ 3 | Powershell.exe -executionpolicy remotesigned -File C:\TTLApps\ttl-appbootstrapper\configure.ps1 4 | } else { 5 | Powershell.exe -executionpolicy remotesigned -File C:\TTLApps\initial-boot.ps1 6 | } 7 | 8 | ##Creator:${ContactEmail} 9 | ##Environment:${Environment} 10 | ##Owner:${OwningCluster} 11 | ##Role:${Role} 12 | ##PuppetRole:${PuppetRole} 13 | ##DeploymentMaps:[] 14 | ##OwningCluster:${OwningCluster} 15 | ##EnvironmentType:${EnvironmentType} 16 | ##SecurityZone:${SecurityZone} 17 | ##ContactEmail:${ContactEmail} 18 | ##ProjectCode:${ProjectCode} 19 | ##RemovalDate:${RemovalDate} 20 | -------------------------------------------------------------------------------- /server/modules/queryHandlersUtil/getASG.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | const asgResourceFactory = require('../../modules/resourceFactories/asgResourceFactory'); 7 | 8 | function* handleQuery(query) { 9 | // Create an instance of the resource to work with based on the resource 10 | // descriptor and AWS account name. 11 | let parameters = { accountName: query.accountName }; 12 | let resource = yield asgResourceFactory.create(undefined, parameters); 13 | 14 | // Get AutoScalingGroup by name 15 | return resource.get({ name: query.autoScalingGroupName, clearCache: query.clearCache }); 16 | } 17 | 18 | module.exports = co.wrap(handleQuery); 19 | -------------------------------------------------------------------------------- /server/modules/queryHandlersUtil/scanCrossAccount.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let sender = require('../sender'); 6 | let scanCrossAccountFn = require('./scanCrossAccountFn'); 7 | 8 | function scanCrossAccount(query, simpleScanQueryName) { 9 | function queryAccount(account) { 10 | let childQuery = Object.assign({ name: simpleScanQueryName, accountName: account.AccountName }, query); 11 | return sender.sendQuery({ query: childQuery, parent: query }); 12 | } 13 | return scanCrossAccountFn(queryAccount); 14 | } 15 | 16 | module.exports = scanCrossAccount; 17 | -------------------------------------------------------------------------------- /server/modules/queryHandlersUtil/scanCrossAccountFn.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let awsAccounts = require('../awsAccounts'); 6 | let applyFuncToAccounts = require('./applyFuncToAccounts'); 7 | 8 | function scanCrossAccountFn(fn) { 9 | return awsAccounts.all() 10 | .then(handleAccounts); 11 | 12 | function handleAccounts(accounts) { 13 | return applyFuncToAccounts(fn, accounts); 14 | } 15 | } 16 | 17 | module.exports = scanCrossAccountFn; 18 | -------------------------------------------------------------------------------- /server/modules/resourceFactories/AsgResource.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let AsgResourceBase = require('./AsgResourceBase'); 6 | let AutoScalingGroup = require('../../models/AutoScalingGroup'); 7 | 8 | function AsgResource(accountId) { 9 | const base = new AsgResourceBase(accountId); 10 | const self = Object.create(new AsgResourceBase(accountId)); 11 | self.get = function (parameters) { 12 | return base.get(parameters).then(x => new AutoScalingGroup(x)); 13 | }; 14 | self.all = function (parameters) { 15 | return base.all(parameters).then(xs => xs.map(x => new AutoScalingGroup(x))); 16 | }; 17 | return self; 18 | } 19 | 20 | module.exports = AsgResource; 21 | -------------------------------------------------------------------------------- /server/modules/resourceFactories/asgLifeCycleHooksResourceFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let amazonClientFactory = require('../amazon-client/childAccountClient'); 6 | let AsgLifeCycleHooksResource = require('./AsgLifeCycleHooksResource'); 7 | let logger = require('../logger'); 8 | 9 | module.exports = { 10 | 11 | canCreate: resourceDescriptor => resourceDescriptor.type.toLowerCase() === 'asgs-scheduled-actions', 12 | 13 | create: (resourceDescriptor, parameters) => { 14 | logger.debug(`Getting ASG client for account "${parameters.accountName}"...`); 15 | return amazonClientFactory.createASGClient(parameters.accountName) 16 | .then(client => new AsgLifeCycleHooksResource(client)); 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /server/modules/resourceFactories/asgScheduledActionsResourceFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let amazonClientFactory = require('../amazon-client/childAccountClient'); 6 | let AsgScheduledActionsResource = require('./AsgScheduledActionsResource'); 7 | let logger = require('../logger'); 8 | 9 | module.exports = { 10 | 11 | canCreate: resourceDescriptor => resourceDescriptor.type.toLowerCase() === 'asgs-scheduled-actions', 12 | 13 | create: (resourceDescriptor, parameters) => { 14 | logger.debug(`Getting ASG client for account "${parameters.accountName}"...`); 15 | return amazonClientFactory.createASGClient(parameters.accountName) 16 | .then(client => new AsgScheduledActionsResource(client)); 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /server/modules/resourceFactories/securityGroupResourceFactory.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let amazonClientFactory = require('../amazon-client/childAccountClient'); 6 | let SecurityGroupResource = require('./SecurityGroupResource'); 7 | 8 | module.exports = { 9 | 10 | canCreate: resourceDescriptor => 11 | resourceDescriptor.type.toLowerCase() === 'ec2/sg', 12 | 13 | create: (resourceDescriptor, parameters) => 14 | amazonClientFactory.createEC2Client(parameters.accountName).then(client => new SecurityGroupResource(client)) 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /server/modules/schema/AccountName.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "AccountName", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Account Name", 5 | "description": "An Account Name", 6 | "type": "string", 7 | "minLength": 1, 8 | "maxLength": 255, 9 | "pattern": "^[a-zA-Z ][0-9a-zA-Z\\.\\-_]*$" 10 | } -------------------------------------------------------------------------------- /server/modules/schema/AccountNumber.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "AccountNumber", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Account Number", 5 | "description": "An AWS Account Number", 6 | "type": "string", 7 | "minLength": 12, 8 | "maxLength": 12, 9 | "pattern": "^[0-9]+$" 10 | } -------------------------------------------------------------------------------- /server/modules/schema/ConsulConnectCommon.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "ConsulConnectCommon", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "ConsulConnectCommon", 5 | "description": "Set data required to connect to Consul", 6 | "type": "object", 7 | "properties": { 8 | "environment": { "$ref": "EnvironmentName" } 9 | }, 10 | "required": ["environment"] 11 | } -------------------------------------------------------------------------------- /server/modules/schema/ConsulKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "ConsulKey", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Consul Key", 5 | "description": "Consul Key-Value Store Key", 6 | "type": "string", 7 | "minLength": 1, 8 | "maxLength": 255 9 | } -------------------------------------------------------------------------------- /server/modules/schema/EnvironmentName.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "EnvironmentName", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Environment Name", 5 | "description": "An Environment Name", 6 | "type": "string", 7 | "minLength": 1, 8 | "maxLength": 255, 9 | "pattern": "^[a-zA-Z][0-9a-zA-Z\\.\\-_]*$" 10 | } 11 | -------------------------------------------------------------------------------- /server/modules/schema/GetTargetStateQuery.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "GetTargetStateQuery", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Get Target State Query", 5 | "description": "Query the value of the desired target state for a deployment", 6 | "type": "object", 7 | "allOf": [ 8 | { "$ref": "ConsulConnectCommon" }, 9 | { "$ref": "#/definitions/self" } 10 | ], 11 | "definitions": { 12 | "self": { 13 | "type": "object", 14 | "properties": { 15 | "key": { "$ref": "ConsulKey" }, 16 | "name": { 17 | "type": "string", 18 | "pattern": "^GetTargetState$" 19 | }, 20 | "recurse": { 21 | "type": "boolean" 22 | } 23 | }, 24 | "required": ["key", "name"] 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /server/modules/schema/UpdateTargetStateCommand.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "UpdateTargetStateCommand", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "title": "Update Target State Command", 5 | "description": "Update the the target state of a service", 6 | "type": "object", 7 | "allOf": [ 8 | { "$ref": "ConsulConnectCommon" }, 9 | { "$ref": "#/definitions/self" } 10 | ], 11 | "definitions": { 12 | "self": { 13 | "type": "object", 14 | "properties": { 15 | "deploymentId": { 16 | "type": "string" 17 | }, 18 | "key": { "$ref": "ConsulKey" }, 19 | "name": { 20 | "type": "string", 21 | "pattern": "^UpdateTargetState$" 22 | } 23 | }, 24 | "required": ["deploymentId", "key", "name", "value"] 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /server/modules/serverFactoryConfiguration.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let config = require('../config'); 7 | 8 | module.exports = function ServerFactoryConfiguration() { 9 | this.getPort = () => configuration.port; 10 | 11 | let loadConfiguration = function () { 12 | let configuration = config.getUserValue('local'); 13 | 14 | assert(configuration.server, 'missing \'server\' field in configuration'); 15 | assert(configuration.server.port, 'missing \'server.port\' field in configuration'); 16 | return { 17 | port: configuration.server.port 18 | }; 19 | }; 20 | 21 | let configuration = loadConfiguration(); 22 | }; 23 | -------------------------------------------------------------------------------- /server/modules/service-discovery/consul/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let { 6 | getAllServices, 7 | getService, 8 | getServiceHealth, 9 | getAllNodes, 10 | getNodesForService, 11 | getNode, 12 | getNodeHealth 13 | } = require('./consulCatalog'); 14 | 15 | module.exports = { 16 | getAllServices, 17 | getService, 18 | getServiceHealth, 19 | getAllNodes, 20 | getNodesForService, 21 | getNode, 22 | getNodeHealth 23 | }; 24 | -------------------------------------------------------------------------------- /server/modules/service-discovery/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let consulReporter = require('./consul'); 6 | 7 | /** 8 | * Service Discovery abstraction to allow easy switching 9 | * of service discovery frameworks. 10 | */ 11 | module.exports = consulReporter; 12 | -------------------------------------------------------------------------------- /server/modules/service-targets/consul/ConsulManager.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let Promise = require('bluebird'); 6 | let logger = require('../../logger'); 7 | 8 | module.exports = class ConsulManager { 9 | constructor(client) { 10 | this.client = client; 11 | } 12 | 13 | setServerMaintenanceMode(enable) { 14 | logger.debug(`consul: setting maintenance mode to ${enable}`); 15 | let promisified = Promise.promisify(this.client.agent.maintenance, { context: this.client.agent }); 16 | return promisified({ enable, reason: 'Maintanance mode triggered from EnvironmentManager' }).catch((err) => { 17 | throw new Error(`Couldn't connect to consul client: ${err.message}`); 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /server/modules/service-targets/consul/consulMacroManager.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let ConsulManager = require('./ConsulManager'); 6 | let co = require('co'); 7 | let consulClient = require('../../consul-client'); 8 | 9 | function* setInstanceMaintenanceMode(accountName, host, environment, enable) { 10 | let options = { accountName, host, environment }; 11 | let consulManager = yield consulClient.create(options).then(client => new ConsulManager(client)); 12 | 13 | yield consulManager.setServerMaintenanceMode(enable); 14 | } 15 | 16 | module.exports = { 17 | setInstanceMaintenanceMode: co.wrap(setInstanceMaintenanceMode) 18 | }; 19 | -------------------------------------------------------------------------------- /server/modules/service-targets/consul/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let { 6 | setInstanceMaintenanceMode 7 | } = require('./consulMacroManager'); 8 | 9 | let { 10 | getTargetState, 11 | setTargetState, 12 | removeTargetState, 13 | removeRuntimeServerRoleTargetState, 14 | getAllServiceTargets, 15 | getServiceDeploymentCause, 16 | getInstanceServiceDeploymentInfo 17 | } = require('./keyValueStore'); 18 | 19 | module.exports = { 20 | getTargetState, 21 | setTargetState, 22 | removeTargetState, 23 | removeRuntimeServerRoleTargetState, 24 | setInstanceMaintenanceMode, 25 | getAllServiceTargets, 26 | getServiceDeploymentCause, 27 | getInstanceServiceDeploymentInfo 28 | }; 29 | -------------------------------------------------------------------------------- /server/modules/service-targets/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let consulUpdater = require('./consul'); 6 | 7 | /** 8 | * Service Discovery abstraction to allow easy switching 9 | * of service discovery frameworks. 10 | */ 11 | module.exports = consulUpdater; 12 | -------------------------------------------------------------------------------- /server/modules/sns/EnvironmentManagerEvents/getTargetArn.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | module.exports = (ResponseMetadata) => { 6 | if (!ResponseMetadata.TopicArn) { 7 | throw new Error('ResponseMetadata does not contain a TopicArn value to extract.'); 8 | } 9 | 10 | return ResponseMetadata.TopicArn; 11 | }; 12 | -------------------------------------------------------------------------------- /server/modules/sslComponentsRepository/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | const mock = require('./sslComponentsRepository.mock.js'); 7 | const prod = require('./sslComponentsRepository.prod.js'); 8 | 9 | let implementation; 10 | 11 | if (config.get('IS_PRODUCTION')) { 12 | implementation = prod; 13 | } else { 14 | implementation = mock; 15 | } 16 | 17 | module.exports = implementation; 18 | -------------------------------------------------------------------------------- /server/modules/systemUser.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let User = require('./user'); 6 | let systemUser = User.new('System', null, [], [{ Access: 'ADMIN', Resource: '**' }]); 7 | 8 | module.exports = systemUser; 9 | -------------------------------------------------------------------------------- /server/modules/toggleServiceStatus.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let sender = require('./sender'); 6 | let ToggleTargetStatus = require('../commands/services/ToggleTargetStatus'); 7 | 8 | function toggleServiceStatus({ environment, service, slice, enable, serverRole, user }) { 9 | const name = 'ToggleTargetStatus'; 10 | const command = { name, environment, service, slice, enable, serverRole }; 11 | return sender.sendCommand(ToggleTargetStatus, { user, command }); 12 | } 13 | 14 | module.exports = { 15 | toggleServiceStatus 16 | }; 17 | -------------------------------------------------------------------------------- /server/modules/user-service/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../../config'); 6 | const mock = require('./userService.mock'); 7 | const Prod = require('./userService.prod'); 8 | let implementation; 9 | 10 | if (config.get('IS_PRODUCTION')) { 11 | implementation = new Prod(); 12 | } else { 13 | implementation = mock; 14 | } 15 | 16 | module.exports = implementation; 17 | -------------------------------------------------------------------------------- /server/modules/userSessionStore.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let EncryptedRedisStore = require('./data-access/encryptedRedisStore'); 6 | const USER_SESSION_STORE_INDEX = 1; 7 | 8 | let sessionStore; 9 | 10 | function createSessionStore() { 11 | sessionStore = EncryptedRedisStore.createStore(USER_SESSION_STORE_INDEX); 12 | return sessionStore; 13 | } 14 | 15 | module.exports = { 16 | get: () => { return sessionStore || createSessionStore(); } 17 | }; 18 | -------------------------------------------------------------------------------- /server/modules/utilities.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | /** 6 | * Attempts to parse a JSON string into an object, returning null on error 7 | * 8 | * @param raw {String} The raw JSON string 9 | * @returns {Object|null} The parsed Object or null if a parsing error occured. 10 | */ 11 | function safeParseJSON(raw) { 12 | try { 13 | return JSON.parse(raw); 14 | } catch (exception) { 15 | return null; 16 | } 17 | } 18 | 19 | /** 20 | * 21 | * @param date 22 | * @param offset 23 | * @returns {*} 24 | */ 25 | function offsetMilliseconds(date, offset) { 26 | date.setMilliseconds(date.getMilliseconds() + offset); 27 | return date; 28 | } 29 | 30 | module.exports = { safeParseJSON, offsetMilliseconds }; 31 | -------------------------------------------------------------------------------- /server/modules/validate/rule/serviceExists.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let log = require('../../logger'); 6 | let servicesDb = require('../../data-access/services'); 7 | 8 | /* Returns an error in the format specified at http://jsonapi.org/format/#errors 9 | * if the service does not exist. 10 | */ 11 | function serviceExists(service) { 12 | let serviceNotFound = () => ({ 13 | title: 'Service Not Found', 14 | detail: `service name: ${service}` 15 | }); 16 | return servicesDb.get({ ServiceName: service }) 17 | .then((rsp => (rsp ? [] : serviceNotFound())), 18 | (err) => { 19 | log.warn(err); 20 | return serviceNotFound(); 21 | }); 22 | } 23 | 24 | module.exports = serviceExists; 25 | -------------------------------------------------------------------------------- /server/modules/weblink.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const _ = require('lodash'); 4 | const url = require('url'); 5 | 6 | function link(links) { 7 | return _.map(links, (href, rel) => [`<${url.format(href)}>`, `rel="${rel}"`].join('; ')) 8 | .join(', '); 9 | } 10 | 11 | module.exports = { link }; 12 | -------------------------------------------------------------------------------- /server/queryHandlers/GetASGState.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let getASGState = require('../modules/environment-state/getASGState'); 6 | 7 | module.exports = function GetServerState(query) { 8 | return getASGState(query.environmentName, query.asgName); 9 | }; 10 | -------------------------------------------------------------------------------- /server/queryHandlers/GetAutoScalingGroup.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let getASG = require('../modules/queryHandlersUtil/getASG'); 7 | 8 | module.exports = function GetAutoScalingGroup(query) { 9 | assert(query.accountName); 10 | assert(query.autoScalingGroupName); 11 | 12 | return getASG(query); 13 | }; 14 | -------------------------------------------------------------------------------- /server/queryHandlers/GetEnvironmentScheduleStatus.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let moment = require('moment'); 6 | let OpsEnvironment = require('../models/OpsEnvironment'); 7 | 8 | function handler(query) { 9 | return OpsEnvironment.getByName(query.environmentName).then((opsEnvironment) => { 10 | let schedule = opsEnvironment.getScheduleStatus(moment(query.date).toDate()); 11 | return { status: schedule }; 12 | }); 13 | } 14 | 15 | module.exports = handler; 16 | -------------------------------------------------------------------------------- /server/queryHandlers/GetInstanceProfile.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const iamInstanceProfileResourceFactory = require('../modules/resourceFactories/iamInstanceProfileResourceFactory'); 6 | 7 | module.exports = function GetInstanceProfile(query) { 8 | let parameters = { accountName: query.accountName }; 9 | return iamInstanceProfileResourceFactory.create(undefined, parameters).then(resource => 10 | resource.get({ instanceProfileName: query.instanceProfileName }) 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /server/queryHandlers/GetKeyPair.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let keypairFactory = require('../modules/factories/keypairFactory'); 7 | 8 | module.exports = function GetKeyPairQueryHandler({ accountName, keyName }) { 9 | assert(accountName); 10 | assert(keyName); 11 | 12 | let parameters = { accountName }; 13 | return keypairFactory.create(parameters) 14 | .then(resource => resource.get({ keyName })); 15 | }; 16 | -------------------------------------------------------------------------------- /server/queryHandlers/GetRole.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let iamRoleClientFactory = require('../modules/clientFactories/iamRoleClientFactory'); 6 | 7 | module.exports = function GetRoleQueryHandler(query) { 8 | return iamRoleClientFactory 9 | .create({ accountName: query.accountName }) 10 | .then(client => client.get({ roleName: query.roleName })); 11 | }; 12 | -------------------------------------------------------------------------------- /server/queryHandlers/GetServicePortConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let Service = require('../models/Service'); 4 | let _ = require('lodash'); 5 | 6 | const DEFAULT_PORT = 0; 7 | 8 | const getPort = (service, colour) => _.get(service, ['Value', `${colour}Port`]); 9 | 10 | function intOrDefault(maybePort) { 11 | let port = Number(maybePort); 12 | return Number.isInteger(port) ? port : DEFAULT_PORT; 13 | } 14 | 15 | function getServicePortConfig(serviceName) { 16 | let portConfig = { blue: DEFAULT_PORT, green: DEFAULT_PORT }; 17 | 18 | if (serviceName === undefined || serviceName === '') { 19 | return Promise.resolve(portConfig); 20 | } 21 | 22 | return Service.getByName(serviceName.trim()) 23 | .then(service => ({ 24 | blue: intOrDefault(getPort(service, 'Blue')), 25 | green: intOrDefault(getPort(service, 'Green')) 26 | })); 27 | } 28 | 29 | module.exports = getServicePortConfig; 30 | -------------------------------------------------------------------------------- /server/queryHandlers/GetTopic.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let snsTopicClientFactory = require('../modules/clientFactories/snsTopicClientFactory'); 6 | 7 | module.exports = function GetTopicQueryHandler(query) { 8 | return snsTopicClientFactory 9 | .create({ accountName: query.accountName }) 10 | .then(client => client.get({ topicName: query.topicName })); 11 | }; 12 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanAutoScalingGroups.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | const asgResourceFactory = require('../modules/resourceFactories/asgResourceFactory'); 7 | 8 | function* handler(query) { 9 | // Create an instance of the resource to work with based on the resource 10 | // descriptor and AWS account name. 11 | let parameters = { accountName: query.accountName }; 12 | let resource = yield asgResourceFactory.create(undefined, parameters); 13 | 14 | return resource.all({ names: query.autoScalingGroupNames }); 15 | } 16 | 17 | module.exports = co.wrap(handler); 18 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanCrossAccountAutoScalingGroups.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let scanCrossAccount = require('../modules/queryHandlersUtil/scanCrossAccount'); 6 | let ScanAutoScalingGroups = require('./ScanAutoScalingGroups'); 7 | 8 | module.exports = function ScanCrossAccountAutoScalingGroups(query) { 9 | return scanCrossAccount(ScanAutoScalingGroups, query, 'ScanAutoScalingGroups'); 10 | }; 11 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanCrossAccountInstances.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let scanCrossAccount = require('../modules/queryHandlersUtil/scanCrossAccountFn'); 6 | let ScanInstances = require('./ScanInstances'); 7 | 8 | module.exports = function ScanCrossAccountInstances(query) { 9 | return scanCrossAccount(handleInstance); 10 | 11 | function handleInstance({ AccountNumber }) { 12 | return ScanInstances(Object.assign({}, query, { accountName: AccountNumber })); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanInstances.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const ec2InstanceResourceFactory = require('../modules/resourceFactories/ec2InstanceResourceFactory'); 6 | 7 | module.exports = function ScanInstancesQueryHandler({ accountName, filter }) { 8 | return ec2InstanceResourceFactory.create(undefined, { accountName }) 9 | .then(handleResourceFactory); 10 | 11 | function handleResourceFactory(x) { 12 | return x.all({ filter }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanLaunchConfigurations.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const launchConfigurationResourceFactory = require('../modules/resourceFactories/launchConfigurationResourceFactory'); 6 | let co = require('co'); 7 | 8 | function* handler(query) { 9 | // Create an instance of the resource to work with based on the resource 10 | // descriptor and AWS account name. 11 | let parameters = { accountName: query.accountName }; 12 | let resource = yield launchConfigurationResourceFactory.create('launchconfig', parameters); 13 | 14 | // Scan resource items 15 | return resource.all({ names: query.launchConfigurationNames }); 16 | } 17 | 18 | module.exports = co.wrap(handler); 19 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanNginxUpstreams.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | const nginxUpstreamsResourceFactory = require('../modules/resourceFactories/nginxUpstreamsResourceFactory'); 7 | 8 | function* handler(query) { 9 | // Create an instance of the Nginx resource 10 | let resource = yield nginxUpstreamsResourceFactory.create(undefined, {}); 11 | 12 | // Scan resource items 13 | const params = { instanceDomainName: query.instanceDomainName }; 14 | 15 | return resource.all(params); 16 | } 17 | 18 | module.exports = co.wrap(handler); 19 | -------------------------------------------------------------------------------- /server/queryHandlers/ScanSecurityGroups.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | const securityGroupResourceFactory = require('../modules/resourceFactories/securityGroupResourceFactory'); 6 | 7 | module.exports = function ScanSecurityGroupsQueryHandler(query) { 8 | let parameters = { accountName: query.accountName }; 9 | 10 | return securityGroupResourceFactory.create(undefined, parameters).then((resource) => { 11 | let request = { 12 | vpcId: query.vpcId, 13 | groupIds: query.groupIds, 14 | groupNames: query.groupNames 15 | }; 16 | 17 | return resource.scan(request); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /server/queryHandlers/services/GetAllNodes.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let serviceDiscovery = require('../../modules/service-discovery'); 6 | 7 | module.exports = function GetAllNodes(query) { 8 | return serviceDiscovery.getAllNodes(query.environment); 9 | }; 10 | -------------------------------------------------------------------------------- /server/queryHandlers/services/GetNode.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let serviceDiscovery = require('../../modules/service-discovery'); 6 | 7 | module.exports = function GetNode(query) { 8 | return serviceDiscovery.getNode(query.environment, query.nodeName); 9 | }; 10 | -------------------------------------------------------------------------------- /server/queryHandlers/services/GetTargetState.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let co = require('co'); 6 | let serviceTargets = require('../../modules/service-targets'); 7 | let schema = require('../../modules/schema/schema'); 8 | 9 | module.exports = function GetTargetState(query) { 10 | return co(function* () { 11 | yield schema('GetTargetStateQuery').then(x => x.assert(query)); 12 | 13 | let key = query.key; 14 | let recurse = query.recurse; 15 | 16 | return yield serviceTargets.getTargetState(query.environment, { key, recurse }); 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /server/queryHandlers/slices/GetSlicesByService.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let getSlices = require('../../modules/queryHandlersUtil/getSlices'); 7 | let loadBalancerUpstreams = require('../../modules/data-access/loadBalancerUpstreams'); 8 | 9 | module.exports = function GetSlicesByService(query) { 10 | assert.equal(typeof query.environmentName, 'string'); 11 | assert.equal(typeof query.serviceName, 'string'); 12 | 13 | return loadBalancerUpstreams.inEnvironmentWithService(query.environmentName, query.serviceName) 14 | .then(upstreams => getSlices.handleQuery(query, upstreams)); 15 | }; 16 | -------------------------------------------------------------------------------- /server/queryHandlers/slices/GetSlicesByUpstream.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let assert = require('assert'); 6 | let getSlices = require('../../modules/queryHandlersUtil/getSlices'); 7 | let loadBalancerUpstreams = require('../../modules/data-access/loadBalancerUpstreams'); 8 | 9 | module.exports = function GetSlicesByUpstream(query) { 10 | assert.equal(typeof query.environmentName, 'string'); 11 | assert.equal(typeof query.upstreamName, 'string'); 12 | 13 | return loadBalancerUpstreams.inEnvironmentWithUpstream(query.environmentName, query.upstreamName) 14 | .then(upstreams => getSlices.handleQuery(query, upstreams)); 15 | }; 16 | -------------------------------------------------------------------------------- /server/routes/home.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../config'); 6 | let renderer = require('../modules/renderer'); 7 | 8 | const PUBLIC_DIR = config.get('PUBLIC_DIR'); 9 | 10 | renderer.register('home', `${PUBLIC_DIR}/index.html`); 11 | 12 | module.exports = function (request, response) { 13 | renderer.render('home', {}, content => response.send(content)); 14 | }; 15 | -------------------------------------------------------------------------------- /server/scripts/command.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict'; 3 | 4 | function getAction() { 5 | 6 | return process.argv[2]; 7 | 8 | } 9 | 10 | function getParameters() { 11 | 12 | var result = { }; 13 | 14 | process.argv.slice(3).forEach(arg => { 15 | 16 | var keyValue = arg.split("="); 17 | 18 | var key = keyValue[0].toLowerCase(); 19 | if (!key) return; 20 | 21 | var value = keyValue[1] || null; 22 | 23 | result[key] = value; 24 | 25 | }); 26 | 27 | return result; 28 | 29 | } 30 | 31 | module.exports = { 32 | action: getAction(), 33 | parameters: getParameters() 34 | }; -------------------------------------------------------------------------------- /server/scripts/preinstall.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 'use strict'; 3 | 4 | let fs = require('fs'); 5 | let path = require('path'); 6 | 7 | linkModules(); 8 | 9 | function linkModules() { 10 | let dir = 'modules'; 11 | let target = path.join(__dirname, '..', dir); 12 | let link = path.join(__dirname, '..', 'node_modules', dir); 13 | 14 | let ln = (tgt, lnk, i) => { 15 | try { 16 | fs.symlinkSync(target, link, 'junction'); 17 | } catch (error) { 18 | if (error.code === 'EEXIST' && 0 < i) { 19 | fs.unlinkSync(lnk); 20 | ln(tgt, lnk, i - 1); 21 | } else { 22 | throw error; 23 | } 24 | } 25 | }; 26 | 27 | ln(target, link, 1); 28 | } 29 | -------------------------------------------------------------------------------- /server/start: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # This script is called by systemd to start environment-manager 4 | # it sets up the environment and then runs Environment Manager 5 | # in place of the shell 6 | 7 | # Use the EC2 metadata service to look up the AWS region in which this server is running 8 | # and set the region Environment Manager will use when querying its management resources 9 | export EM_AWS_REGION=$(curl --silent --show-error http://169.254.169.254/2016-09-02/dynamic/instance-identity/document | jq --raw-output '.region') 10 | echo "EM_AWS_REGION=${EM_AWS_REGION}" >&2 11 | 12 | exec /usr/bin/node index.js -------------------------------------------------------------------------------- /server/test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'mocha': true 4 | }, 5 | 'rules': { 6 | 'func-names': 0, 7 | 'no-warning-comments': 1, 8 | 'prefer-arrow-callback': 0 9 | } 10 | }; -------------------------------------------------------------------------------- /server/test/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | let config = require('../config'); 6 | let localConfig = require('../configuration.sample'); 7 | 8 | config.setUserValue('local', localConfig); 9 | -------------------------------------------------------------------------------- /server/test/modules/base64Test.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | 'use strict'; 4 | 5 | let should = require('should'); 6 | let base64 = require('../../modules/base64'); 7 | 8 | describe('base64', function () { 9 | describe('when I encode then decode an object', function () { 10 | it('the result is the same as the argument', function () { 11 | let input = { 12 | number: 5.6, 13 | string: 'blah', 14 | array: [1, 2, 3], 15 | object: { a: 1 }, 16 | }; 17 | let result = base64.decode(base64.encode(input)); 18 | result.should.be.eql(input); 19 | }); 20 | }); 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /server/test/modules/emCryptoTest.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* eslint-env mocha */ 4 | 5 | 'use strict' 6 | 7 | require('should'); 8 | let sut = require('../../modules/emCrypto'); 9 | 10 | describe('decrypting an encrypted value', function () { 11 | it('returns the input value', function () { 12 | let input = 'Hello World!'; 13 | let password = 'my simple password'; 14 | let ciphertext = sut.encrypt(password, input); 15 | let plaintext = sut.decrypt(password, ciphertext); 16 | plaintext.should.match(input); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /server/test/modules/instances.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/server/test/modules/instances.spec.js -------------------------------------------------------------------------------- /server/test/modules/service-reporter/service-reporterTest.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 'use strict'; 5 | 6 | let assert = require('assert'); 7 | 8 | describe('service-discovery', function() { 9 | 10 | let sut; 11 | beforeEach(() => sut = require('../../../modules/service-discovery')); 12 | 13 | it('exports the expected API', () => { 14 | assert.notEqual(sut.getAllServices, undefined, 'getAllServices is required'); 15 | assert.notEqual(sut.getService, undefined, 'getService is required'); 16 | assert.notEqual(sut.getAllNodes, undefined, 'getAllNodes is required'); 17 | assert.notEqual(sut.getNode, undefined, 'getNode is required'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /server/test/modules/service-updater/service-updaterTest.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 'use strict'; 5 | 6 | let assert = require('assert'); 7 | 8 | describe('service-targets', function() { 9 | 10 | let sut; 11 | beforeEach(() => sut = require('../../../modules/service-targets')); 12 | 13 | it('exports the expected API', () => { 14 | assert.notEqual(sut.getTargetState, undefined, 'getTargetState is required'); 15 | assert.notEqual(sut.setTargetState, undefined, 'setTargetState is required'); 16 | assert.notEqual(sut.removeTargetState, undefined, 'removeTargetState is required'); 17 | assert.notEqual(sut.setInstanceMaintenanceMode, undefined, 'setInstanceMaintenanceMode is required'); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /server/test/schema-validation/environment-type.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "Subnets": { 3 | "PrivateApp": { 4 | "AvailabilityZoneA": "xxx", 5 | "AvailabilityZoneB": "xxx", 6 | "AvailabilityZoneC": "xxx" 7 | } 8 | }, 9 | "SchemaVersion": 5, 10 | "VpcId": "xxx", 11 | "AWSAccountNumber": "123456789123", 12 | "NamingPattern": "c[0-9]{1,2}", 13 | "DeploymentBucket": "xxx", 14 | "LoadBalancers": [ 15 | "xxx" 16 | ], 17 | "AWSAccountName": "xxx", 18 | "Consul": { 19 | "DataCenter": "xxx", 20 | "SecurityTokenPath": { 21 | "Bucket": "xxx", 22 | "Key": "xxx" 23 | }, 24 | "Servers": [ 25 | "10.249.17.254" 26 | ], 27 | "Port": 8500 28 | }, 29 | "PuppetBranch": "xxx" 30 | } -------------------------------------------------------------------------------- /server/test/test-profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "EM_AWS_REGION": "eu-west-1", 3 | "EM_AWS_RESOURCE_PREFIX": "EnvironmentManagerTests-", 4 | "EM_LOG_LEVEL": "Silent", 5 | "EM_PACKAGES_BUCKET": "EM_PACKAGES_BUCKET" 6 | } 7 | -------------------------------------------------------------------------------- /server/test/utils/fakeLogger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let nothing = () => undefined; 4 | 5 | module.exports = { 6 | debug: nothing, 7 | info: nothing, 8 | log: nothing, 9 | warn: nothing, 10 | error: nothing 11 | }; 12 | -------------------------------------------------------------------------------- /server/test/utils/sinonHelper.js: -------------------------------------------------------------------------------- 1 | /* TODO: enable linting and fix resulting errors */ 2 | /* eslint-disable */ 3 | /* Copyright (c) Trainline Limited, 2016-2017. All rights reserved. See LICENSE.txt in the project root for license information. */ 4 | 'use strict'; 5 | 6 | function SinonHelper() { 7 | 8 | this.getCalls = function (spy) { 9 | 10 | var calls = []; 11 | var call = null; 12 | var index = 0; 13 | 14 | while (!!(call = spy.getCall(index))) { 15 | 16 | calls.push(call); 17 | index++; 18 | 19 | } 20 | 21 | return calls; 22 | 23 | }; 24 | 25 | } 26 | 27 | module.exports = new SinonHelper(); 28 | 29 | -------------------------------------------------------------------------------- /setup/cloudformation/.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | es6: true 3 | node: true 4 | extends: eslint:recommended 5 | parserOptions: 6 | ecmaVersion: 6 -------------------------------------------------------------------------------- /setup/cloudformation/.gitignore: -------------------------------------------------------------------------------- 1 | *.template.json -------------------------------------------------------------------------------- /setup/cloudformation/lambda/.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules/**/*.* 2 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "airbnb", 3 | "validateIndentation": 2 4 | } -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraAsgLambdaScale/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infraAsgLambdaScale", 3 | "version": "0.0.1", 4 | "description": "Test gulp project with governator", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-aws-resource": "pack-zip", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "gulp": "~3.8.10", 14 | "gulp-awslambda": "0.2.2", 15 | "gulp-zip": "~2.0.2", 16 | "pack-zip": "^0.2.2" 17 | } 18 | } -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraAsgLambdaScale/test.js: -------------------------------------------------------------------------------- 1 | var lambda = require("./index.js"); 2 | 3 | //test with a lambda event 4 | 5 | lambda.handler({ detail: { requestParameters: { AutoScalingGroupName: "asgLambdaTest6" } } }, { 6 | fail: function(m) { console.log("fail:" + m) }, 7 | success: function(m) { console.log("Success:" + m) } 8 | }); 9 | 10 | //test with an SNS event: 11 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerAudit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infra-environment-manager-audit", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "Apache-2.0", 8 | "dependencies": {}, 9 | "scripts": { 10 | "build-aws-resource": "pack-zip", 11 | "lint": "eslint", 12 | "test": "tape **/*.spec.js" 13 | }, 14 | "eslintConfig": { 15 | "env": { 16 | "es6": true, 17 | "node": true 18 | }, 19 | "extends": "eslint:recommended", 20 | "parserOptions": { 21 | "ecmaVersion": 6 22 | }, 23 | "rules": { 24 | "no-console": 0 25 | } 26 | }, 27 | "devDependencies": { 28 | "eslint": "^3.4.0", 29 | "pack-zip": "^0.2.2", 30 | "proxyquire": "^1.8.0", 31 | "tape": "^4.7.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerAuditBackup/AwsAccount.js: -------------------------------------------------------------------------------- 1 | function AwsAccount(name, number) { 2 | var $this = this; 3 | 4 | $this.name = name; 5 | $this.number = number; 6 | 7 | $this.toString = function() { 8 | return $this.name + '[' + $this.number + ']'; 9 | }; 10 | } 11 | 12 | module.exports = AwsAccount; 13 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerAuditBackup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infra-environment-manager-backup", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "ISC", 8 | "dependencies": { 9 | "async": "^1.5.2" 10 | }, 11 | "scripts": { 12 | "build-aws-resource": "pack-zip", 13 | "lint": "eslint" 14 | }, 15 | "eslintConfig": { 16 | "env": { 17 | "es6": true, 18 | "node": true 19 | }, 20 | "extends": "eslint:recommended", 21 | "parserOptions": { 22 | "ecmaVersion": 6 23 | }, 24 | "rules": { 25 | "no-console": 0 26 | } 27 | }, 28 | "devDependencies": { 29 | "eslint": "^3.4.0", 30 | "pack-zip": "^0.2.2" 31 | } 32 | } -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerBackup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infra-environment-manager-backup", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "ISC", 8 | "dependencies": { 9 | "async": "^1.5.2" 10 | }, 11 | "devDependencies": { 12 | "pack-zip": "^0.2.2" 13 | }, 14 | "scripts": { 15 | "build-aws-resource": "pack-zip" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerConfigurationChangeAudit/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-disable no-console */ 6 | 7 | console.log('Loading InfraEnvironmentManagerConfigurationChangeAudit function'); 8 | 9 | exports.handler = (event, context, callback) => { 10 | console.log('function InfraEnvironmentManagerConfigurationChangeAudit starting...'); 11 | const attributes = event.Records[0].Sns.MessageAttributes; 12 | const attributeString = Object.keys(attributes).map(key => `${key}:${attributes[key].Value}`).join(','); 13 | console.log('Configuration Change Attributes:', attributeString); 14 | const message = event.Records[0].Sns.Message; 15 | console.log('Configuration Change SNS:', message); 16 | callback(null, message); 17 | }; 18 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerOperationsChangeAudit/index.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Trainline Limited. All rights reserved. See LICENSE.txt in the project root for license information. */ 2 | 3 | 'use strict'; 4 | 5 | /* eslint-disable no-console */ 6 | 7 | console.log('Loading InfraEnvironmentManagerOperationsChangeAudit function'); 8 | 9 | exports.handler = (event, context, callback) => { 10 | console.log('function InfraEnvironmentManagerOperationsChangeAudit starting...'); 11 | const attributes = event.Records[0].Sns.MessageAttributes; 12 | const attributeString = Object.keys(attributes).map(key => `${key}:${attributes[key].Value}`).join(','); 13 | console.log('Operations Change Attributes:', attributeString); 14 | const message = event.Records[0].Sns.Message; 15 | console.log('Operations Change SNS:', message); 16 | callback(null, message); 17 | }; 18 | -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerScheduler/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let archiver = require('archiver-promise'); 4 | let fs = require('fs-extra'); 5 | let co = require('co'); 6 | 7 | let config = require('./package.json'); 8 | 9 | co(function* () { 10 | let { name, version } = config; 11 | let outDir = 'out'; 12 | let destination = `${outDir}/${name}-${version}.zip`; 13 | 14 | yield fs.remove(outDir).then(() => fs.mkdir(outDir)); 15 | yield zipFiles(__dirname, config.files, destination); 16 | 17 | console.log(destination); 18 | }); 19 | 20 | function zipFiles(path, files, destination) { 21 | let archive = archiver(destination); 22 | archive.pipe(fs.createWriteStream(destination)); 23 | files.forEach(file => archive.glob(file, { cwd: path })); 24 | return archive.finalize(); 25 | } -------------------------------------------------------------------------------- /setup/cloudformation/lambda/InfraEnvironmentManagerScheduler/local/config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "em": { 3 | "host": "https://environment-manager-domain-name", 4 | "credentials": { 5 | "username": "username", 6 | "password": "password" 7 | } 8 | }, 9 | "limitToAccounts": [], 10 | "whatIf": true, 11 | "listSkippedInstances": true, 12 | "ignoreASGInstances": false, 13 | "errorOnFailure": true 14 | } -------------------------------------------------------------------------------- /setup/cloudformation/lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --recursive ./test --EM_PROFILE test/test-profile.json", 8 | "test-watch": "mocha --watch --recursive ./test --EM_PROFILE test/test-profile.json" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "aws-sdk": "^2.26.0", 14 | "eslint": "^3.17.1", 15 | "eslint-config-airbnb-base": "^11.1.1", 16 | "eslint-plugin-import": "^2.2.0", 17 | "mocha": "^3.2.0", 18 | "rewire": "^2.5.2", 19 | "sinon": "^1.17.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /setup/cloudformation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "environment-manager-stack", 3 | "version": "0.0.1", 4 | "description": "CloudFormation Stacks for Environment Manager", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node tools/index.js build", 8 | "deploy": "node tools/index.js deploy", 9 | "prepublish": "cfn-packager install-subpackages", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": { 13 | "name": "Platform Development", 14 | "email": "platform.development@thetrainline.com" 15 | }, 16 | "license": "Apache-2.0", 17 | "devDependencies": { 18 | "cfn-packager": "0.1.0" 19 | }, 20 | "dependencies": { 21 | "bluebird": "^3.5.0", 22 | "commander": "^2.9.0", 23 | "globby": "^6.1.0", 24 | "js-yaml": "^3.8.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /setup/consul-acl/consul-deployment-agent.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "": { 4 | "policy": "read" 5 | }, 6 | "environments/": { 7 | "policy": "read" 8 | }, 9 | "deployments/": { 10 | "policy": "write" 11 | } 12 | }, 13 | "service": { 14 | "": { 15 | "policy": "read" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /setup/consul-acl/environment-manager.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "": { 4 | "policy": "read" 5 | }, 6 | "environments/": { 7 | "policy": "write" 8 | }, 9 | "deployments/": { 10 | "policy": "write" 11 | } 12 | }, 13 | "service": { 14 | "": { 15 | "policy": "read" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /setup/consul-acl/upstreamr.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "": { 4 | "policy": "read" 5 | } 6 | }, 7 | "service": { 8 | "": { 9 | "policy": "read" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /setup/data-migration/replicate-dynamodb-table/.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | es6: true 3 | node: true 4 | extends: 5 | - eslint:recommended -------------------------------------------------------------------------------- /setup/data-migration/replicate-dynamodb-table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "replicate-dynamodb-table", 3 | "version": "0.1.0", 4 | "description": "AWS Lambda Function to replicate changes from one DynamoDB table to another", 5 | "main": "index.js", 6 | "author": "merlin.taylor@thetrainline.com", 7 | "license": "Apache-2.0", 8 | "scripts": { 9 | "test": "tap *.spec.js" 10 | }, 11 | "devDependencies": { 12 | "eslint": "^4.5.0", 13 | "proxyquire": "^1.8.0", 14 | "tap": "^10.7.2" 15 | }, 16 | "dependencies": { 17 | "aws-sdk": "^2.102.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /setup/data-migration/touch-dynamodb-table/.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | es6: true 3 | node: true 4 | extends: 5 | - eslint:recommended -------------------------------------------------------------------------------- /setup/data-migration/touch-dynamodb-table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "touch-dynamodb-table", 3 | "version": "0.1.0", 4 | "description": "AWS Lambda Function to touch each record in a DynamoDB table", 5 | "main": "index.js", 6 | "author": "merlin.taylor@thetrainline.com", 7 | "license": "Apache-2.0", 8 | "scripts": { 9 | "test": "tap *.spec.js" 10 | }, 11 | "devDependencies": { 12 | "eslint": "^4.5.0", 13 | "proxyquire": "^1.8.0", 14 | "tap": "^10.7.2" 15 | }, 16 | "dependencies": { 17 | "aws-sdk": "^2.102.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /setup/linux-sample-package.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/setup/linux-sample-package.zip -------------------------------------------------------------------------------- /setup/terraform/upstreamCleaner/aws_iam_role.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "upstream_cleaner" { 2 | name = "upstream_cleaner" 3 | 4 | assume_role_policy = < { 5 | browser.url('http://localhost:8080'); 6 | }, 7 | navigateToEnvironments: () => { 8 | browser.click('#environmentsLink'); 9 | }, 10 | navigateToOperations: () => { 11 | browser.click('#operationsLink'); 12 | }, 13 | navigateToCompare: () => { 14 | browser.click('#compareLink'); 15 | }, 16 | navigateToConfiguration: () => { 17 | browser.click('#configurationLink'); 18 | browser.waitForVisible('#configServicesLink'); 19 | } 20 | }; -------------------------------------------------------------------------------- /test/pages/login-page.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | module.exports = { 4 | logOut: () => { 5 | if (browser.isVisible('#logout')) { 6 | browser.click('#logout > a'); 7 | browser.waitForVisible('#username'); 8 | } 9 | }, 10 | logIn: (username, password) => { 11 | if (browser.isVisible('#logout')) { 12 | browser.click('#logout > a'); 13 | browser.waitForVisible('#username'); 14 | } 15 | browser.setValue('#username', username || 'anyUser'); 16 | browser.setValue('#password', password || 'anyPassword'); 17 | browser.click('#sign-in'); 18 | browser.waitForVisible('#logout'); 19 | } 20 | }; -------------------------------------------------------------------------------- /test/test/test-ui/screenshots/ERROR_chrome_2018-05-15T12-41-26.078Z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trainline/environment-manager/c84623a5f64cebe68a1afd4fec09821cc5d8ddad/test/test/test-ui/screenshots/ERROR_chrome_2018-05-15T12-41-26.078Z.png --------------------------------------------------------------------------------