Skip to content

Commit

Permalink
initial cutover from oss
Browse files Browse the repository at this point in the history
  • Loading branch information
anotherchrisberry authored and tomaslin committed Mar 16, 2018
1 parent 1852a0b commit ad1747e
Show file tree
Hide file tree
Showing 88 changed files with 5,258 additions and 0 deletions.
6 changes: 6 additions & 0 deletions domain/ITitusCredentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ICredentials } from '@spinnaker/core';

export interface ITitusCredentials extends ICredentials {
awsAccount: string;
awsVpc: string;
}
1 change: 1 addition & 0 deletions domain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ITitusCredentials';
39 changes: 39 additions & 0 deletions help/titus.help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {module} from 'angular';
import {HELP_CONTENTS_REGISTRY, HelpContentsRegistry} from '@spinnaker/core';

const helpContents: {[key: string]: string} = {
'titus.deploy.runtimeLimitSecs': '<p>Maximum amount of time (in seconds) a batch job is allowed to run</p>',
'titus.deploy.retries': '<p>Number of times to retry this job</p>',
'titus.deploy.propertyFile': '<p>(Optional) Configures the name to the file used to pass in properties to later stages in the Spinnaker pipeline. The file must be saved into the /logs directory during execution</p>',
'titus.deploy.iamProfile': 'AWS IAM instance profile to assign to this service',
'titus.deploy.capacityGroup': 'Used by Titus to ensure capacity guarantees, defaults to the application name if not provided',
'titus.deploy.network': 'Amount of networking bandwidth to allocate in Mbps',
'titus.deploy.allocateIP': 'If selected, specifies an IP to be allocated for each of your job’s containers',
'titus.deploy.softConstraints': 'Soft constraints are enforced on a best efforts basis. For example, if tasks can’t be perfectly balanced across zones, the best available balance is achieved without keeping the tasks pending for execution.',
'titus.deploy.hardConstraints': 'Constraints must be met and tasks will not be launched if constraint can’t be perfectly met',
'titus.deploy.efs': 'if completed, allows you to specify an EFS volume to attach to each Task that gets created for the Job',
'titus.deploy.mountPoint': '(Required) A valid directory to mount the volume, e.g, <samp>/efs</samp>. Invalid locations are <samp>/</samp>, <samp>/data</samp>, and <samp>/logs</samp> as these are reserved directories.',
'titus.deploy.efsId': '(Required) The EFS file system ID, e.g. <samp> fs-0208c74b</samp>.',
'titus.job.waitForCompletion': 'if unchecked, marks the stage as successful right away without waiting for the job to complete',
'titus.bake.fromGitTrigger': 'If checked, gets git details from the specified git trigger. The pipeline will fail when ran manually',
'titus.bake.repositoryUrl': 'Url to the git repository containing the code to create the Docker image from, <samp>ssh://git@stash.corp.netflix.com:7999/SPKR/orca.git</samp> or <samp>ssh://git@github.com/spinnaker/orca.git</samp>',
'titus.bake.repositoryHash': '(Optional) The hashcode to the git commit for the image',
'titus.bake.repositoryBranch': '(Optional) The branch in git to build the image from',
'titus.bake.repositoryDirectory': '(Optional) If specified, will build the image from the Dockerfile contained in this directory. Default to project root.',
'titus.bake.imageOrganization': '(Optional) The organization to which this image belongs to, e.g. <samp>spinnaker</samp> for <samp>spinnaker/igor</samp>Defaults to none.',
'titus.bake.imageName': '(Optional) The name for the image, e.g. <samp>igor</samp> for <samp>spinnaker/igor</samp>Defaults to [git project name].[git repo name].',
'titus.bake.tags': '(Optional) Comma separated. By default, the <samp>latest</samp> tag is updated. Adds additional tags to label this image <samp>1.0.0-unstable,1.0.0-rc1</samp>',
'titus.bake.buildParameters': '(Optional) Build time variables to be passed to the Docker image. These are the set of values passed to --build-args in the command line.',
'titus.serverGroup.traffic': `
<p>Enables the "inService" scaling process, which is used by Spinnaker and discovery services to determine if the server group is enabled.</p>
<p>Will be automatically enabled when any non "custom" deployment strategy is selected.</p>`,
'titus.deploy.securityGroups': 'AWS Security Groups to assign to this service. Security groups are set only if <samp>Allocate IP?</samp> has been selected and are assigned to the Titus AWS Elastic Network Interface.',
'titus.job.securityGroups': 'AWS Security Groups to assign to this job',

};

export const TITUS_HELP = 'spinnaker.titus.help.contents';
module(TITUS_HELP, [HELP_CONTENTS_REGISTRY])
.run((helpContentsRegistry: HelpContentsRegistry) => {
Object.keys(helpContents).forEach(key => helpContentsRegistry.register(key, helpContents[key]));
});
260 changes: 260 additions & 0 deletions instance/details/instance.details.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
'use strict';

const angular = require('angular');
import { defaults, filter } from 'lodash';

import {
ACCOUNT_SERVICE,
CLOUD_PROVIDER_REGISTRY,
CONFIRMATION_MODAL_SERVICE,
INSTANCE_READ_SERVICE,
INSTANCE_WRITE_SERVICE,
RECENT_HISTORY_SERVICE
} from '@spinnaker/core';

module.exports = angular.module('spinnaker.instance.detail.titus.controller', [
require('angular-ui-router').default,
require('angular-ui-bootstrap'),
ACCOUNT_SERVICE,
INSTANCE_WRITE_SERVICE,
INSTANCE_READ_SERVICE,
CONFIRMATION_MODAL_SERVICE,
RECENT_HISTORY_SERVICE,
CLOUD_PROVIDER_REGISTRY,
])
.controller('titusInstanceDetailsCtrl', function ($scope, $q, $state, $uibModal, accountService,
instanceWriter, confirmationModalService, recentHistoryService,
cloudProviderRegistry, instanceReader, instance, app) {

// needed for standalone instances
$scope.detailsTemplateUrl = cloudProviderRegistry.getValue('titus', 'instance.detailsTemplateUrl');

$scope.state = {
loading: true,
standalone: app.isStandalone
};

$scope.application = app;

function extractHealthMetrics(instance, latest) {
// do not backfill on standalone instances
if (app.isStandalone) {
instance.health = latest.health;
}

instance.health = instance.health || [];
var displayableMetrics = instance.health.filter(
function(metric) {
return metric.state !== 'Unknown';
});

// backfill details where applicable
if (latest.health) {
displayableMetrics.forEach(function (metric) {
var detailsMatch = latest.health.filter(function (latestHealth) {
return latestHealth.type === metric.type;
});
if (detailsMatch.length) {
defaults(metric, detailsMatch[0]);
}
});
}
$scope.healthMetrics = displayableMetrics;
}

function retrieveInstance() {
var extraData = {};
var instanceSummary, loadBalancers, account, region, vpcId;
app.serverGroups.data.some(function (serverGroup) {
return serverGroup.instances.some(function (possibleInstance) {
if (possibleInstance.id === instance.instanceId) {
instanceSummary = possibleInstance;
loadBalancers = serverGroup.loadBalancers;
account = serverGroup.account;
region = serverGroup.region;
extraData.serverGroup = serverGroup.name;
return true;
}
});
});

if (instanceSummary && account && region) {
extraData.account = account;
extraData.region = region;
recentHistoryService.addExtraDataToLatest('instances', extraData);
return instanceReader.getInstanceDetails(account, region, instance.instanceId).then(function(details) {
$scope.state.loading = false;
extractHealthMetrics(instanceSummary, details);
$scope.instance = defaults(details, instanceSummary);
$scope.instance.account = account;
$scope.instance.region = region;
$scope.instance.vpcId = vpcId;
$scope.instance.loadBalancers = loadBalancers;
$scope.baseIpAddress = $scope.instance.placement.containerIp || $scope.instance.placement.host;
$scope.instance.externalIpAddress = $scope.instance.placement.host;
getBastionAddressForAccount($scope.instance.account, $scope.instance.region);
},
autoClose
);
}

if (!instanceSummary) {
$scope.instanceIdNotFound = instance.instanceId;
$scope.state.loading = false;
}
return $q.when(null);
}

function autoClose() {
if ($scope.$$destroyed) {
return;
}
$state.params.allowModalToStayOpen = true;
$state.go('^', null, {location: 'replace'});
}

this.canRegisterWithLoadBalancer = function() {
return false;
};

this.canDeregisterFromLoadBalancer = function() {
return false;
};

this.canRegisterWithDiscovery = function() {
let healthMetrics = $scope.instance.health || [];
var discoveryHealth = healthMetrics.filter(function(health) {
return health.type === 'Discovery';
});
return discoveryHealth.length ? discoveryHealth[0].state === 'OutOfService' : false;
};

this.terminateInstance = function terminateInstance() {
var instance = $scope.instance;
instance.instanceId = instance.id;
var taskMonitor = {
application: app,
title: 'Terminating ' + instance.instanceId,
onTaskComplete: function() {
if ($state.includes('**.instanceDetails', {id: instance.instanceId})) {
$state.go('^');
}
}
};

var submitMethod = function () {
let params = {cloudProvider: 'titus'};
if (instance.serverGroup) {
params.managedInstanceGroupName = instance.serverGroup;
}
return instanceWriter.terminateInstance(instance, app, params);
};

confirmationModalService.confirm({
header: 'Really terminate ' + instance.id + '?',
buttonText: 'Terminate ' + instance.id,
account: instance.account,
provider: 'titus',
taskMonitorConfig: taskMonitor,
submitMethod: submitMethod
});
};

this.registerInstanceWithLoadBalancer = function registerInstanceWithLoadBalancer() {
// Do nothing
};

this.deregisterInstanceFromLoadBalancer = function deregisterInstanceFromLoadBalancer() {
// Do nothing
};

this.enableInstanceInDiscovery = function enableInstanceInDiscovery() {
var instance = $scope.instance;
instance.instanceId = instance.id;

var taskMonitor = {
application: app,
title: 'Enabling ' + instance.instanceId + ' in discovery'
};

var submitMethod = function () {
return instanceWriter.enableInstanceInDiscovery(instance, app);
};

confirmationModalService.confirm({
header: 'Really enable ' + instance.instanceId + ' in discovery?',
buttonText: 'Enable ' + instance.instanceId,
account: instance.account,
taskMonitorConfig: taskMonitor,
submitMethod: submitMethod
});
};

this.disableInstanceInDiscovery = function disableInstanceInDiscovery() {
var instance = $scope.instance;
instance.instanceId = instance.id;

var taskMonitor = {
application: app,
title: 'Disabling ' + instance.instanceId + ' in discovery'
};

var submitMethod = function () {
return instanceWriter.disableInstanceInDiscovery(instance, app);
};

confirmationModalService.confirm({
header: 'Really disable ' + instance.instanceId + ' in discovery?',
buttonText: 'Disable ' + instance.instanceId,
provider: 'titus',
account: instance.account,
taskMonitorConfig: taskMonitor,
submitMethod: submitMethod
});
};

this.hasHealthState = function hasHealthState(healthProviderType, state) {
let healthMetrics = $scope.instance.health || [];
return (healthMetrics.some(function (health) {
return health.type === healthProviderType && health.state === state;
})
);
};

let getBastionAddressForAccount = (account, region) => {
return accountService.getAccountDetails(account).then((details) => {
this.bastionHost = details.bastionHost || 'unknown';
this.apiEndpoint = filter(details.regions, {name: region})[0].endpoint;
this.titusUiEndpoint = this.apiEndpoint.replace('titusapi', 'titus-ui').replace('http', 'https').replace('7101', '7001');
if (region !== 'us-east-1') {
this.bastionStack = '-stack ' + this.apiEndpoint.split('.' + region)[0].replace('http://titusapi.', '');
} else {
this.bastionStack = '';
}

$scope.sshLink = `ssh -t ${this.bastionHost} 'titus-ssh ${this.bastionStack} -region ${$scope.instance.region} -id ${$scope.instance.id}'`;
});
};

this.hasPorts = () => {
return Object.keys($scope.instance.resources.ports).length > 0;
};

let initialize = app.isStandalone ?
retrieveInstance() :
app.serverGroups.ready().then(retrieveInstance);

initialize.then(() => {
// Two things to look out for here:
// 1. If the retrieveInstance call completes *after* the user has navigated away from the view, there
// is no point in subscribing to the refresh
// 2. If this is a standalone instance, there is no application that will refresh
if (!$scope.$$destroyed && !app.isStandalone) {
app.serverGroups.onRefresh($scope, retrieveInstance);
}
});

$scope.account = instance.account;

}
);
Loading

0 comments on commit ad1747e

Please sign in to comment.