<commit_before>import * as angular from 'angular';
import _ from 'lodash';

import {
  AccountService,
  DeploymentStrategyRegistry,
  INSTANCE_TYPE_SERVICE,
  NameUtils,
  SubnetReader,
} from '@spinnaker/core';

import { AWSProviderSettings } from 'amazon/aws.settings';
import { AWS_SERVER_GROUP_CONFIGURATION_SERVICE } from './serverGroupConfiguration.service';

export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE =
  'spinnaker.amazon.serverGroupCommandBuilder.service';
export const name = AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE; // for backwards compatibility
angular
  .module(AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE, [
    INSTANCE_TYPE_SERVICE,
    AWS_SERVER_GROUP_CONFIGURATION_SERVICE,
  ])
  .factory('awsServerGroupCommandBuilder', [
    '$q',
    'instanceTypeService',
    'awsServerGroupConfigurationService',
    function($q, instanceTypeService, awsServerGroupConfigurationService) {
      function buildNewServerGroupCommand(application, defaults) {
        defaults = defaults || {};
        const credentialsLoader = AccountService.getCredentialsKeyedByAccount('aws');

        const defaultCredentials =
          defaults.account || application.defaultCredentials.aws || AWSProviderSettings.defaults.account;
        const defaultRegion = defaults.region || application.defaultRegions.aws || AWSProviderSettings.defaults.region;
        const defaultSubnet = defaults.subnet || AWSProviderSettings.defaults.subnetType || '';

        const preferredZonesLoader = AccountService.getAvailabilityZonesForAccountAndRegion(
          'aws',
          defaultCredentials,
          defaultRegion,
        );

        return $q
          .all({
            preferredZones: preferredZonesLoader,
            credentialsKeyedByAccount: credentialsLoader,
          })
          .then(function(asyncData) {
            const availabilityZones = asyncData.preferredZones;

            const credentials = asyncData.credentialsKeyedByAccount[defaultCredentials];
            const keyPair = credentials ? credentials.defaultKeyPair : null;
            const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});

            let defaultIamRole = AWSProviderSettings.defaults.iamRole || 'BaseIAMRole';
            defaultIamRole = defaultIamRole.replace('{{application}}', application.name);

            const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

            const command = {
              application: application.name,
              credentials: defaultCredentials,
              region: defaultRegion,
              strategy: '',
              capacity: {
                min: 1,
                max: 1,
                desired: 1,
              },
              targetHealthyDeployPercentage: 100,
              cooldown: 10,
              enabledMetrics: [],
              healthCheckType: 'EC2',
              healthCheckGracePeriod: 600,
              instanceMonitoring: false,
              ebsOptimized: false,
              selectedProvider: 'aws',
              iamRole: defaultIamRole,
              terminationPolicies: ['Default'],
              vpcId: null,
              subnetType: defaultSubnet,
              availabilityZones: availabilityZones,
              keyPair: keyPair,
              suspendedProcesses: [],
              securityGroups: [],
              stack: '',
              freeFormDetails: '',
              spotPrice: '',
              tags: {},
              useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
              copySourceCustomBlockDeviceMappings: false, // default to using block device mappings from current instance type
              viewState: {
                instanceProfile: 'custom',
                useAllImageSelection: false,
                useSimpleCapacity: true,
                usePreferredZones: true,
                mode: defaults.mode || 'create',
                disableStrategySelection: true,
                dirty: {},
                submitButtonLabel: getSubmitButtonLabel(defaults.mode || 'create'),
              },
            };

            if (
              application.attributes &&
              application.attributes.platformHealthOnlyShowOverride &&
              application.attributes.platformHealthOnly
            ) {
              command.interestingHealthProviderNames = ['Amazon'];
            }

            if (AWSProviderSettings.serverGroups && AWSProviderSettings.serverGroups.enableIMDSv2) {
              /**
               * Older SDKs do not support IMDSv2. A timestamp can be optionally configured at which any apps created after can safely default to using IMDSv2.
               */
              const appAgeRequirement = AWSProviderSettings.serverGroups.defaultIMDSv2AppAgeLimit;
              const creationDate = application.attributes.createTs;

              command.requireIMDSv2 =
                appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement ? true : false;
            }

            return command;
          });
      }

      function buildServerGroupCommandFromPipeline(application, originalCluster) {
        const pipelineCluster = _.cloneDeep(originalCluster);
        const region = Object.keys(pipelineCluster.availabilityZones)[0];
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType(
          'aws',
          pipelineCluster.instanceType,
        );
        const commandOptions = { account: pipelineCluster.account, region: region };
        const asyncLoader = $q.all({
          command: buildNewServerGroupCommand(application, commandOptions),
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function(asyncData) {
          const command = asyncData.command;
          const zones = pipelineCluster.availabilityZones[region];
          const usePreferredZones = zones.join(',') === command.availabilityZones.join(',');

          const viewState = {
            instanceProfile: asyncData.instanceProfile,
            disableImageSelection: true,
            useSimpleCapacity:
              pipelineCluster.capacity.min === pipelineCluster.capacity.max &&
              pipelineCluster.useSourceCapacity !== true,
            usePreferredZones: usePreferredZones,
            mode: 'editPipeline',
            submitButtonLabel: 'Done',
            templatingEnabled: true,
            existingPipelineCluster: true,
            dirty: {},
          };

          const viewOverrides = {
            region: region,
            credentials: pipelineCluster.account,
            availabilityZones: pipelineCluster.availabilityZones[region],
            iamRole: pipelineCluster.iamRole,
            viewState: viewState,
          };

          pipelineCluster.strategy = pipelineCluster.strategy || '';

          return angular.extend({}, command, pipelineCluster, viewOverrides);
        });
      }

      // Only used to prepare view requiring template selecting
      function buildNewServerGroupCommandForPipeline() {
        return $q.when({
          viewState: {
            requiresTemplateSelection: true,
          },
        });
      }

      function getSubmitButtonLabel(mode) {
        switch (mode) {
          case 'createPipeline':
            return 'Add';
          case 'editPipeline':
            return 'Done';
          case 'clone':
            return 'Clone';
          default:
            return 'Create';
        }
      }

      function buildUpdateServerGroupCommand(serverGroup) {
        const command = {
          type: 'modifyAsg',
          asgs: [{ asgName: serverGroup.name, region: serverGroup.region }],
          cooldown: serverGroup.asg.defaultCooldown,
          enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map(m => m.metric),
          healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
          healthCheckType: serverGroup.asg.healthCheckType,
          terminationPolicies: angular.copy(serverGroup.asg.terminationPolicies),
          credentials: serverGroup.account,
        };
        awsServerGroupConfigurationService.configureUpdateCommand(command);
        return command;
      }

      function buildServerGroupCommandFromExisting(application, serverGroup, mode = 'clone') {
        const preferredZonesLoader = AccountService.getPreferredZonesByAccount('aws');
        const subnetsLoader = SubnetReader.listSubnets();

        const serverGroupName = NameUtils.parseServerGroupName(serverGroup.asg.autoScalingGroupName);

        const instanceType = serverGroup.launchConfig
          ? serverGroup.launchConfig.instanceType
          : serverGroup.launchTemplate
          ? serverGroup.launchTemplate.launchTemplateData.instanceType
          : null;
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType('aws', instanceType);

        const asyncLoader = $q.all({
          preferredZones: preferredZonesLoader,
          subnets: subnetsLoader,
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function(asyncData) {
          const zones = serverGroup.asg.availabilityZones.sort();
          let usePreferredZones = false;
          const preferredZonesForAccount = asyncData.preferredZones[serverGroup.account];
          if (preferredZonesForAccount) {
            const preferredZones = preferredZonesForAccount[serverGroup.region].sort();
            usePreferredZones = zones.join(',') === preferredZones.join(',');
          }

          // These processes should never be copied over, as the affect launching instances and enabling traffic
          const enabledProcesses = ['Launch', 'Terminate', 'AddToLoadBalancer'];

          const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
          const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

          const existingTags = {};
          // These tags are applied by Clouddriver (if configured to do so), regardless of what the user might enter
          // Might be worth feature flagging this if it turns out other folks are hard-coding these values
          const reservedTags = ['spinnaker:application', 'spinnaker:stack', 'spinnaker:details'];
          if (serverGroup.asg.tags) {
            serverGroup.asg.tags
              .filter(t => !reservedTags.includes(t.key))
              .forEach(tag => {
                existingTags[tag.key] = tag.value;
              });
          }

          const command = {
            application: application.name,
            strategy: '',
            stack: serverGroupName.stack,
            freeFormDetails: serverGroupName.freeFormDetails,
            credentials: serverGroup.account,
            cooldown: serverGroup.asg.defaultCooldown,
            enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map(m => m.metric),
            healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
            healthCheckType: serverGroup.asg.healthCheckType,
            terminationPolicies: serverGroup.asg.terminationPolicies,
            loadBalancers: serverGroup.asg.loadBalancerNames,
            region: serverGroup.region,
            useSourceCapacity: false,
            capacity: {
              min: serverGroup.asg.minSize,
              max: serverGroup.asg.maxSize,
              desired: serverGroup.asg.desiredCapacity,
            },
            targetHealthyDeployPercentage: 100,
            availabilityZones: zones,
            selectedProvider: 'aws',
            source: {
              account: serverGroup.account,
              region: serverGroup.region,
              asgName: serverGroup.asg.autoScalingGroupName,
            },
            suspendedProcesses: (serverGroup.asg.suspendedProcesses || [])
              .map(process => process.processName)
              .filter(name => !enabledProcesses.includes(name)),
            tags: Object.assign({}, serverGroup.tags, existingTags),
            targetGroups: serverGroup.targetGroups,
            useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
            copySourceCustomBlockDeviceMappings: mode === 'clone', // default to using block device mappings if not cloning
            viewState: {
              instanceProfile: asyncData.instanceProfile,
              useAllImageSelection: false,
              useSimpleCapacity: serverGroup.asg.minSize === serverGroup.asg.maxSize,
              usePreferredZones: usePreferredZones,
              mode: mode,
              submitButtonLabel: getSubmitButtonLabel(mode),
              isNew: false,
              dirty: {},
            },
          };

          if (
            application.attributes &&
            application.attributes.platformHealthOnlyShowOverride &&
            application.attributes.platformHealthOnly
          ) {
            command.interestingHealthProviderNames = ['Amazon'];
          }

          if (mode === 'editPipeline') {
            command.useSourceCapacity = true;
            command.viewState.useSimpleCapacity = false;
            command.strategy = 'redblack';
            const redblack = DeploymentStrategyRegistry.getStrategy('redblack');
            redblack.initializationMethod && redblack.initializationMethod(command);
            command.suspendedProcesses = [];
          }

          const vpcZoneIdentifier = serverGroup.asg.vpczoneIdentifier;
          if (vpcZoneIdentifier !== '') {
            const subnetId = vpcZoneIdentifier.split(',')[0];
            const subnet = _.chain(asyncData.subnets)
              .find({ id: subnetId })
              .value();
            command.subnetType = subnet.purpose;
            command.vpcId = subnet.vpcId;
          } else {
            command.subnetType = '';
            command.vpcId = null;
          }

          if (serverGroup.launchConfig) {
            angular.extend(command, {
              instanceType: serverGroup.launchConfig.instanceType,
              iamRole: serverGroup.launchConfig.iamInstanceProfile,
              keyPair: serverGroup.launchConfig.keyName,
              associatePublicIpAddress: serverGroup.launchConfig.associatePublicIpAddress,
              ramdiskId: serverGroup.launchConfig.ramdiskId,
              instanceMonitoring: serverGroup.launchConfig.instanceMonitoring.enabled,
              ebsOptimized: serverGroup.launchConfig.ebsOptimized,
              spotPrice: serverGroup.launchConfig.spotPrice,
            });
            if (serverGroup.launchConfig.userData) {
              command.base64UserData = serverGroup.launchConfig.userData;
            }
            command.viewState.imageId = serverGroup.launchConfig.imageId;
          }

          if (serverGroup.launchTemplate) {
            const { launchTemplateData } = serverGroup.launchTemplate;
            const maxPrice =
              launchTemplateData.instanceMarketOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions.maxPrice;
            const ipv6Addresses = _.flatMap(launchTemplateData.networkInterfaces, i => i.ipv6Addresses) || [];

            angular.extend(command, {
              instanceType: launchTemplateData.instanceType,
              iamRole: launchTemplateData.iamInstanceProfile.name,
              keyPair: launchTemplateData.keyName,
              associateIPv6Address: ipv6Addresses.length > 0,
              ramdiskId: launchTemplateData.ramdiskId,
              instanceMonitoring: launchTemplateData.monitoring.enabled,
              ebsOptimized: launchTemplateData.ebsOptimized,
              spotPrice: maxPrice || undefined,
              requireIMDSv2:
                launchTemplateData.metadataOptions && launchTemplateData.metadataOptions.httpsTokens === 'required',
            });

            if (launchTemplateData.userData) {
              command.base64UserData = launchTemplateData.userData;
            }
            command.viewState.imageId = launchTemplateData.imageId;
          }

          if (mode === 'clone' && serverGroup.image && serverGroup.image.name) {
            command.amiName = serverGroup.image.name;
          }

          if (serverGroup.launchConfig && serverGroup.launchConfig.securityGroups.length) {
            command.securityGroups = serverGroup.launchConfig.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.securityGroups.length) {
            command.securityGroups = serverGroup.launchTemplate.launchTemplateData.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.networkInterfaces) {
            const networkInterface =
              serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find(ni => ni.deviceIndex === 0) || {};
            command.securityGroups = networkInterface.groups;
          }

          return command;
        });
      }

      return {
        buildNewServerGroupCommand: buildNewServerGroupCommand,
        buildServerGroupCommandFromExisting: buildServerGroupCommandFromExisting,
        buildNewServerGroupCommandForPipeline: buildNewServerGroupCommandForPipeline,
        buildServerGroupCommandFromPipeline: buildServerGroupCommandFromPipeline,
        buildUpdateServerGroupCommand: buildUpdateServerGroupCommand,
      };
    },
  ]);
<commit_msg>fix(aws/serverGroup): Optimize cloning logic (#8560)<commit_after>import * as angular from 'angular';
import _ from 'lodash';

import {
  AccountService,
  DeploymentStrategyRegistry,
  INSTANCE_TYPE_SERVICE,
  NameUtils,
  SubnetReader,
} from '@spinnaker/core';

import { AWSProviderSettings } from 'amazon/aws.settings';
import { AWS_SERVER_GROUP_CONFIGURATION_SERVICE } from './serverGroupConfiguration.service';

export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE =
  'spinnaker.amazon.serverGroupCommandBuilder.service';
export const name = AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE; // for backwards compatibility
angular
  .module(AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE, [
    INSTANCE_TYPE_SERVICE,
    AWS_SERVER_GROUP_CONFIGURATION_SERVICE,
  ])
  .factory('awsServerGroupCommandBuilder', [
    '$q',
    'instanceTypeService',
    'awsServerGroupConfigurationService',
    function($q, instanceTypeService, awsServerGroupConfigurationService) {
      function buildNewServerGroupCommand(application, defaults) {
        defaults = defaults || {};
        const credentialsLoader = AccountService.getCredentialsKeyedByAccount('aws');

        const defaultCredentials =
          defaults.account || application.defaultCredentials.aws || AWSProviderSettings.defaults.account;
        const defaultRegion = defaults.region || application.defaultRegions.aws || AWSProviderSettings.defaults.region;
        const defaultSubnet = defaults.subnet || AWSProviderSettings.defaults.subnetType || '';

        const preferredZonesLoader = AccountService.getAvailabilityZonesForAccountAndRegion(
          'aws',
          defaultCredentials,
          defaultRegion,
        );

        return $q
          .all({
            preferredZones: preferredZonesLoader,
            credentialsKeyedByAccount: credentialsLoader,
          })
          .then(function(asyncData) {
            const availabilityZones = asyncData.preferredZones;

            const credentials = asyncData.credentialsKeyedByAccount[defaultCredentials];
            const keyPair = credentials ? credentials.defaultKeyPair : null;
            const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});

            let defaultIamRole = AWSProviderSettings.defaults.iamRole || 'BaseIAMRole';
            defaultIamRole = defaultIamRole.replace('{{application}}', application.name);

            const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

            const command = {
              application: application.name,
              credentials: defaultCredentials,
              region: defaultRegion,
              strategy: '',
              capacity: {
                min: 1,
                max: 1,
                desired: 1,
              },
              targetHealthyDeployPercentage: 100,
              cooldown: 10,
              enabledMetrics: [],
              healthCheckType: 'EC2',
              healthCheckGracePeriod: 600,
              instanceMonitoring: false,
              ebsOptimized: false,
              selectedProvider: 'aws',
              iamRole: defaultIamRole,
              terminationPolicies: ['Default'],
              vpcId: null,
              subnetType: defaultSubnet,
              availabilityZones: availabilityZones,
              keyPair: keyPair,
              suspendedProcesses: [],
              securityGroups: [],
              stack: '',
              freeFormDetails: '',
              spotPrice: '',
              tags: {},
              useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
              copySourceCustomBlockDeviceMappings: false, // default to using block device mappings from current instance type
              viewState: {
                instanceProfile: 'custom',
                useAllImageSelection: false,
                useSimpleCapacity: true,
                usePreferredZones: true,
                mode: defaults.mode || 'create',
                disableStrategySelection: true,
                dirty: {},
                submitButtonLabel: getSubmitButtonLabel(defaults.mode || 'create'),
              },
            };

            if (
              application.attributes &&
              application.attributes.platformHealthOnlyShowOverride &&
              application.attributes.platformHealthOnly
            ) {
              command.interestingHealthProviderNames = ['Amazon'];
            }

            if (AWSProviderSettings.serverGroups && AWSProviderSettings.serverGroups.enableIMDSv2) {
              /**
               * Older SDKs do not support IMDSv2. A timestamp can be optionally configured at which any apps created after can safely default to using IMDSv2.
               */
              const appAgeRequirement = AWSProviderSettings.serverGroups.defaultIMDSv2AppAgeLimit;
              const creationDate = application.attributes.createTs;

              command.requireIMDSv2 =
                appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement ? true : false;
            }

            return command;
          });
      }

      function buildServerGroupCommandFromPipeline(application, originalCluster) {
        const pipelineCluster = _.cloneDeep(originalCluster);
        const region = Object.keys(pipelineCluster.availabilityZones)[0];
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType(
          'aws',
          pipelineCluster.instanceType,
        );
        const commandOptions = { account: pipelineCluster.account, region: region };
        const asyncLoader = $q.all({
          command: buildNewServerGroupCommand(application, commandOptions),
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function(asyncData) {
          const command = asyncData.command;
          const zones = pipelineCluster.availabilityZones[region];
          const usePreferredZones = zones.join(',') === command.availabilityZones.join(',');

          const viewState = {
            instanceProfile: asyncData.instanceProfile,
            disableImageSelection: true,
            useSimpleCapacity:
              pipelineCluster.capacity.min === pipelineCluster.capacity.max &&
              pipelineCluster.useSourceCapacity !== true,
            usePreferredZones: usePreferredZones,
            mode: 'editPipeline',
            submitButtonLabel: 'Done',
            templatingEnabled: true,
            existingPipelineCluster: true,
            dirty: {},
          };

          const viewOverrides = {
            region: region,
            credentials: pipelineCluster.account,
            availabilityZones: pipelineCluster.availabilityZones[region],
            iamRole: pipelineCluster.iamRole,
            viewState: viewState,
          };

          pipelineCluster.strategy = pipelineCluster.strategy || '';

          return angular.extend({}, command, pipelineCluster, viewOverrides);
        });
      }

      // Only used to prepare view requiring template selecting
      function buildNewServerGroupCommandForPipeline() {
        return $q.when({
          viewState: {
            requiresTemplateSelection: true,
          },
        });
      }

      function getSubmitButtonLabel(mode) {
        switch (mode) {
          case 'createPipeline':
            return 'Add';
          case 'editPipeline':
            return 'Done';
          case 'clone':
            return 'Clone';
          default:
            return 'Create';
        }
      }

      function buildUpdateServerGroupCommand(serverGroup) {
        const command = {
          type: 'modifyAsg',
          asgs: [{ asgName: serverGroup.name, region: serverGroup.region }],
          cooldown: serverGroup.asg.defaultCooldown,
          enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map(m => m.metric),
          healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
          healthCheckType: serverGroup.asg.healthCheckType,
          terminationPolicies: angular.copy(serverGroup.asg.terminationPolicies),
          credentials: serverGroup.account,
        };
        awsServerGroupConfigurationService.configureUpdateCommand(command);
        return command;
      }

      function buildServerGroupCommandFromExisting(application, serverGroup, mode = 'clone') {
        const preferredZonesLoader = AccountService.getPreferredZonesByAccount('aws');
        const subnetsLoader = SubnetReader.listSubnets();

        const serverGroupName = NameUtils.parseServerGroupName(serverGroup.asg.autoScalingGroupName);

        const instanceType = serverGroup.launchConfig
          ? serverGroup.launchConfig.instanceType
          : serverGroup.launchTemplate
          ? serverGroup.launchTemplate.launchTemplateData.instanceType
          : null;
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType('aws', instanceType);

        const asyncLoader = $q.all({
          preferredZones: preferredZonesLoader,
          subnets: subnetsLoader,
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function(asyncData) {
          const zones = serverGroup.asg.availabilityZones.sort();
          let usePreferredZones = false;
          const preferredZonesForAccount = asyncData.preferredZones[serverGroup.account];
          if (preferredZonesForAccount) {
            const preferredZones = preferredZonesForAccount[serverGroup.region].sort();
            usePreferredZones = zones.join(',') === preferredZones.join(',');
          }

          // These processes should never be copied over, as the affect launching instances and enabling traffic
          const enabledProcesses = ['Launch', 'Terminate', 'AddToLoadBalancer'];

          const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
          const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

          const existingTags = {};
          // These tags are applied by Clouddriver (if configured to do so), regardless of what the user might enter
          // Might be worth feature flagging this if it turns out other folks are hard-coding these values
          const reservedTags = ['spinnaker:application', 'spinnaker:stack', 'spinnaker:details'];
          if (serverGroup.asg.tags) {
            serverGroup.asg.tags
              .filter(t => !reservedTags.includes(t.key))
              .forEach(tag => {
                existingTags[tag.key] = tag.value;
              });
          }

          const command = {
            application: application.name,
            strategy: '',
            stack: serverGroupName.stack,
            freeFormDetails: serverGroupName.freeFormDetails,
            credentials: serverGroup.account,
            cooldown: serverGroup.asg.defaultCooldown,
            enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map(m => m.metric),
            healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
            healthCheckType: serverGroup.asg.healthCheckType,
            terminationPolicies: serverGroup.asg.terminationPolicies,
            loadBalancers: serverGroup.asg.loadBalancerNames,
            region: serverGroup.region,
            useSourceCapacity: false,
            capacity: {
              min: serverGroup.asg.minSize,
              max: serverGroup.asg.maxSize,
              desired: serverGroup.asg.desiredCapacity,
            },
            targetHealthyDeployPercentage: 100,
            availabilityZones: zones,
            selectedProvider: 'aws',
            source: {
              account: serverGroup.account,
              region: serverGroup.region,
              asgName: serverGroup.asg.autoScalingGroupName,
            },
            suspendedProcesses: (serverGroup.asg.suspendedProcesses || [])
              .map(process => process.processName)
              .filter(name => !enabledProcesses.includes(name)),
            tags: Object.assign({}, serverGroup.tags, existingTags),
            targetGroups: serverGroup.targetGroups,
            useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
            copySourceCustomBlockDeviceMappings: mode === 'clone', // default to using block device mappings if not cloning
            viewState: {
              instanceProfile: asyncData.instanceProfile,
              useAllImageSelection: false,
              useSimpleCapacity: serverGroup.asg.minSize === serverGroup.asg.maxSize,
              usePreferredZones: usePreferredZones,
              mode: mode,
              submitButtonLabel: getSubmitButtonLabel(mode),
              isNew: false,
              dirty: {},
            },
          };

          if (
            application.attributes &&
            application.attributes.platformHealthOnlyShowOverride &&
            application.attributes.platformHealthOnly
          ) {
            command.interestingHealthProviderNames = ['Amazon'];
          }

          if (mode === 'editPipeline') {
            command.useSourceCapacity = true;
            command.viewState.useSimpleCapacity = false;
            command.strategy = 'redblack';
            const redblack = DeploymentStrategyRegistry.getStrategy('redblack');
            redblack.initializationMethod && redblack.initializationMethod(command);
            command.suspendedProcesses = [];
          }

          const vpcZoneIdentifier = serverGroup.asg.vpczoneIdentifier;
          if (vpcZoneIdentifier !== '') {
            const subnetId = vpcZoneIdentifier.split(',')[0];
            const subnet = _.chain(asyncData.subnets)
              .find({ id: subnetId })
              .value();
            command.subnetType = subnet.purpose;
            command.vpcId = subnet.vpcId;
          } else {
            command.subnetType = '';
            command.vpcId = null;
          }

          if (serverGroup.launchConfig) {
            angular.extend(command, {
              instanceType: serverGroup.launchConfig.instanceType,
              iamRole: serverGroup.launchConfig.iamInstanceProfile,
              keyPair: serverGroup.launchConfig.keyName,
              associatePublicIpAddress: serverGroup.launchConfig.associatePublicIpAddress,
              ramdiskId: serverGroup.launchConfig.ramdiskId,
              instanceMonitoring: serverGroup.launchConfig.instanceMonitoring.enabled,
              ebsOptimized: serverGroup.launchConfig.ebsOptimized,
              spotPrice: serverGroup.launchConfig.spotPrice,
            });
            if (serverGroup.launchConfig.userData) {
              command.base64UserData = serverGroup.launchConfig.userData;
            }
            command.viewState.imageId = serverGroup.launchConfig.imageId;
          }

          if (serverGroup.launchTemplate) {
            const { launchTemplateData } = serverGroup.launchTemplate;
            const maxPrice =
              launchTemplateData.instanceMarketOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions.maxPrice;
            const { ipv6AddressCount } = launchTemplateData.networkInterfaces[0];

            angular.extend(command, {
              instanceType: launchTemplateData.instanceType,
              iamRole: launchTemplateData.iamInstanceProfile.name,
              keyPair: launchTemplateData.keyName,
              associateIPv6Address: Boolean(ipv6AddressCount),
              ramdiskId: launchTemplateData.ramdiskId,
              instanceMonitoring: launchTemplateData.monitoring.enabled,
              ebsOptimized: launchTemplateData.ebsOptimized,
              spotPrice: maxPrice || undefined,
              requireIMDSv2: Boolean(
                launchTemplateData.metadataOptions && launchTemplateData.metadataOptions.httpsTokens === 'required',
              ),
            });

            if (launchTemplateData.userData) {
              command.base64UserData = launchTemplateData.userData;
            }
            command.viewState.imageId = launchTemplateData.imageId;
          }

          if (mode === 'clone' && serverGroup.image && serverGroup.image.name) {
            command.amiName = serverGroup.image.name;
          }

          if (serverGroup.launchConfig && serverGroup.launchConfig.securityGroups.length) {
            command.securityGroups = serverGroup.launchConfig.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.securityGroups.length) {
            command.securityGroups = serverGroup.launchTemplate.launchTemplateData.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.networkInterfaces) {
            const networkInterface =
              serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find(ni => ni.deviceIndex === 0) || {};
            command.securityGroups = networkInterface.groups;
          }

          return command;
        });
      }

      return {
        buildNewServerGroupCommand: buildNewServerGroupCommand,
        buildServerGroupCommandFromExisting: buildServerGroupCommandFromExisting,
        buildNewServerGroupCommandForPipeline: buildNewServerGroupCommandForPipeline,
        buildServerGroupCommandFromPipeline: buildServerGroupCommandFromPipeline,
        buildUpdateServerGroupCommand: buildUpdateServerGroupCommand,
      };
    },
  ]);
<|endoftext|>


<commit_before>var url = "https://claritk.firebaseio.com/";
var firebase = new Firebase("https://claritk.firebaseio.com/");
var firebaseRooms = new Firebase("https://claritk.firebaseio.com/Rooms");
var listening;

// This function may not need to be used
function addDevice(room, userIp) {
	var newDevice = new Firebase(url+"Rooms/"+room+"/"+userIp);
	newDevice.push(0);
}

function pushResult(room, userIp, parseData) {
	console.log(room + "/" + userIp)
	var pushData = new Firebase(url+"Rooms/"+room+"/"+userIp);
	pushData.push({confidence: parseData['confidence'],
					transcript: parseData['transcript']});

}

function setListening(room, bool) {
	var newListener = new Firebase(url+"Rooms/"+room+"/LISTENING");
	newListener.set(bool);
}

function notifyDevices(room) {
	var listenDir = new Firebase(url+"Rooms/"+room+"/LISTENING");
	listenDir.on('value', function(snapshot){
		alert(snapshot.val());
		if (snapshot.val()) {
			startListening();
		}
	});
}

function getResults(room) {
	var results = new Firebase(url+"Rooms/"+room);
	var ipKeys = [];
	var valuePairs = [];
	results.once("value", function(snapshot){ // Array of objects of IP/Keys to a specified room
		//console.log(snapshot.val());
		$.each(snapshot.val(), function(index, value){ // Array of all {confidence, transcript} pair
			if (index != "LISTENING") {
				console.log(index);
				console.log(value);
				$.each(value, function(index, value){
					console.log(value['confidence'] +": " + value['transcript']);
				});
				// valuePairs.push({
				// 	transcript: value['confidence'],
				// 	confidence: value['transcript']
				// });

			}
			//ipKeys.push(valuePairs);
		});
	});
	//return ipKeys;
}

function init(){
	ROOM_KEY = 'roomkey';
	DEVICE_KEY = '192.1.1.1';
}

<commit_msg>getResults works<commit_after>var url = "https://claritk.firebaseio.com/";
var firebase = new Firebase("https://claritk.firebaseio.com/");
var firebaseRooms = new Firebase("https://claritk.firebaseio.com/Rooms");
var listening;

// This function may not need to be used
function addDevice(room, userIp) {
	var newDevice = new Firebase(url+"Rooms/"+room+"/"+userIp);
	newDevice.push(0);
}

function pushResult(room, userIp, parseData) {
	console.log(room + "/" + userIp)
	var pushData = new Firebase(url+"Rooms/"+room+"/"+userIp);
	pushData.push({confidence: parseData['confidence'],
					transcript: parseData['transcript']});

}

function setListening(room, bool) {
	var newListener = new Firebase(url+"Rooms/"+room+"/LISTENING");
	newListener.set(bool);
}

function notifyDevices(room) {
	var listenDir = new Firebase(url+"Rooms/"+room+"/LISTENING");
	listenDir.on('value', function(snapshot){
		alert(snapshot.val());
		if (snapshot.val()) {
			startListening();
		}
	});
}

function getResults(room) {
	var results = new Firebase(url+"Rooms/"+room);
	var valuePairs = [];
	results.once("value", function(snapshot){ // Array of objects of IP/Keys to a specified room
		//console.log(snapshot.val());
		$.each(snapshot.val(), function(index, value){ // Array of all {confidence, transcript} pair

			if (index != "LISTENING") {
				var resultArray = [];
				console.log("key: "+index);
				//valuePairs.push({key:index});
				$.each(value, function(index, value){
					console.log(value['confidence'] +": " + value['transcript']);
					resultArray.push({results:
						{transcript: value['transcript'], confidence: value['confidence']}
					});
				});
				var device = {
					key: index,
					results: resultArray
				}
				valuePairs.push(device);
			}
		});
	});
	return valuePairs;
}

function init(){
	ROOM_KEY = 'roomkey';
	DEVICE_KEY = '192-1-1-1';
}

<|endoftext|>


<commit_before>'use strict';

var request = require('request');
var config = require('konfig')();
var q = require('q');
var utility = require('../libs/utility.js');
var pouchdb = require('pouchdb');
var DB_URL = config.app.DB_URL;
var STOCK_COUNT_DB = config.app.STOCK_COUNT_DB;
var APP_CONFIG_DB = config.app.APP_CONFIG_DB;
var DAILY = 1;
var WEEKLY = 7;
var BI_WEEKLY = 14;
var MONTHLY = 30;

var getDueDate = function(interval, reminderDay, date){
  var today = new Date();
  var currentDate = date || today;
  var countDate;
  interval = parseInt(interval);

  switch (interval) {
    case DAILY:
      var temp = new Date(currentDate);
      countDate = new Date(temp.getFullYear(), temp.getMonth(), temp.getDate());
      break;
    case WEEKLY:
      countDate = utility.getWeekRangeByDate(currentDate, reminderDay).reminderDate;
      if(currentDate.getTime() < countDate.getTime()){
        //current week count date is not yet due, return previous week count date..
        countDate = new Date(countDate.getFullYear(), countDate.getMonth(), countDate.getDate() - interval);
      }
      break;
    case BI_WEEKLY:
      countDate = utility.getWeekRangeByDate(currentDate, reminderDay).reminderDate;
      if (currentDate.getTime() < countDate.getTime()) {
        //current week count date is not yet due, return last bi-weekly count date
        countDate = new Date(countDate.getFullYear(), countDate.getMonth(), countDate.getDate() - interval);
      }
      break;
    case MONTHLY:
      var monthlyDate = (currentDate.getTime() === today.getTime())? 1 : currentDate.getDate();
      countDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), monthlyDate);
      break;
    default:
      throw 'unknown stock count interval.';
  }
  return countDate
};

var addNextDueDate = function(res){
  var result = [];
  res.rows.forEach(function(row){
    row = row.value;
    if(!row.stockCountInterval || !row.reminderDay){
      return false;
    }
    row.nextDueDate = getDueDate(row.stockCountInterval, row.reminderDay, new Date());
    result.push(row);
  });
  return result;
};

var getLatestEvents = function(events){
  var facilityLatestDate = {};
  events.rows.forEach(function(row){
    row = row.value;
    var latestDate = facilityLatestDate[row.facility];
    var eventDate = new Date(row.countDate);
    if(!latestDate || (latestDate <= eventDate)){
      latestDate = eventDate;
    }
    facilityLatestDate[row.facility] = latestDate;
  });
  return facilityLatestDate;
};

var getFacilitiesPending = function(facilities, latestEvents){
  var pendingList = [];
  facilities.forEach(function(row){
    var latestDate = latestEvents[row.facilityId];
    if(!latestDate || ((new Date(latestDate) < new Date(row.nextDueDate)) && (new Date() > new Date(row.nextDueDate)))){
      pendingList.push(row);
    }
  });
  return pendingList;
};

var reminder = {
  getPending: function (d) {
    var date = new Date(d);
    var url = DB_URL + APP_CONFIG_DB;
    var appConfigView = 'app_config/by_facility';
    var appConfigDB = pouchdb(url);

    var scUrl = DB_URL + STOCK_COUNT_DB;
    var scDB = pouchdb(scUrl);
    var scView = 'stockcount/by_countdate_and_facility';

    return appConfigDB.query(appConfigView)
      .then(function(res){
        var facilities = addNextDueDate(res);
        var SINCE_LAST_DAYS = 30;
        var startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - SINCE_LAST_DAYS);
        return scDB.query(scView, { startkey: startDate.toJSON() })
          .then(function(res){
            var latestEventDates = getLatestEvents(res);
            console.log(latestEventDates);
            return getFacilitiesPending(facilities, latestEventDates);
          });
      });
  },
  getReminderMsg: function(){
    return 'Stock count is due, please count your stock.';
  }
};

module.exports = reminder;<commit_msg>changed reminder message<commit_after>'use strict';

var request = require('request');
var config = require('konfig')();
var q = require('q');
var utility = require('../libs/utility.js');
var pouchdb = require('pouchdb');
var DB_URL = config.app.DB_URL;
var STOCK_COUNT_DB = config.app.STOCK_COUNT_DB;
var APP_CONFIG_DB = config.app.APP_CONFIG_DB;
var DAILY = 1;
var WEEKLY = 7;
var BI_WEEKLY = 14;
var MONTHLY = 30;

var getDueDate = function(interval, reminderDay, date){
  var today = new Date();
  var currentDate = date || today;
  var countDate;
  interval = parseInt(interval);

  switch (interval) {
    case DAILY:
      var temp = new Date(currentDate);
      countDate = new Date(temp.getFullYear(), temp.getMonth(), temp.getDate());
      break;
    case WEEKLY:
      countDate = utility.getWeekRangeByDate(currentDate, reminderDay).reminderDate;
      if(currentDate.getTime() < countDate.getTime()){
        //current week count date is not yet due, return previous week count date..
        countDate = new Date(countDate.getFullYear(), countDate.getMonth(), countDate.getDate() - interval);
      }
      break;
    case BI_WEEKLY:
      countDate = utility.getWeekRangeByDate(currentDate, reminderDay).reminderDate;
      if (currentDate.getTime() < countDate.getTime()) {
        //current week count date is not yet due, return last bi-weekly count date
        countDate = new Date(countDate.getFullYear(), countDate.getMonth(), countDate.getDate() - interval);
      }
      break;
    case MONTHLY:
      var monthlyDate = (currentDate.getTime() === today.getTime())? 1 : currentDate.getDate();
      countDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), monthlyDate);
      break;
    default:
      throw 'unknown stock count interval.';
  }
  return countDate
};

var addNextDueDate = function(res){
  var result = [];
  res.rows.forEach(function(row){
    row = row.value;
    if(!row.stockCountInterval || !row.reminderDay){
      return false;
    }
    row.nextDueDate = getDueDate(row.stockCountInterval, row.reminderDay, new Date());
    result.push(row);
  });
  return result;
};

var getLatestEvents = function(events){
  var facilityLatestDate = {};
  events.rows.forEach(function(row){
    row = row.value;
    var latestDate = facilityLatestDate[row.facility];
    var eventDate = new Date(row.countDate);
    if(!latestDate || (latestDate <= eventDate)){
      latestDate = eventDate;
    }
    facilityLatestDate[row.facility] = latestDate;
  });
  return facilityLatestDate;
};

var getFacilitiesPending = function(facilities, latestEvents){
  var pendingList = [];
  facilities.forEach(function(row){
    var latestDate = latestEvents[row.facilityId];
    if(!latestDate || ((new Date(latestDate) < new Date(row.nextDueDate)) && (new Date() > new Date(row.nextDueDate)))){
      pendingList.push(row);
    }
  });
  return pendingList;
};

var reminder = {
  getPending: function (d) {
    var date = new Date(d);
    var url = DB_URL + APP_CONFIG_DB;
    var appConfigView = 'app_config/by_facility';
    var appConfigDB = pouchdb(url);

    var scUrl = DB_URL + STOCK_COUNT_DB;
    var scDB = pouchdb(scUrl);
    var scView = 'stockcount/by_countdate_and_facility';

    return appConfigDB.query(appConfigView)
      .then(function(res){
        var facilities = addNextDueDate(res);
        var SINCE_LAST_DAYS = 30;
        var startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() - SINCE_LAST_DAYS);
        return scDB.query(scView, { startkey: startDate.toJSON() })
          .then(function(res){
            var latestEventDates = getLatestEvents(res);
            console.log(latestEventDates);
            return getFacilitiesPending(facilities, latestEventDates);
          });
      });
  },
  getReminderMsg: function(){
    return 'Stock Count is due. Please, use LoMIS to send in your stock count!';
  }
};

module.exports = reminder;<|endoftext|>


<commit_before>import * as angular from 'angular';
import _ from 'lodash';

import {
  AccountService,
  DeploymentStrategyRegistry,
  INSTANCE_TYPE_SERVICE,
  NameUtils,
  SubnetReader,
} from '@spinnaker/core';

import { AWSProviderSettings } from 'amazon/aws.settings';
import { AWS_SERVER_GROUP_CONFIGURATION_SERVICE } from './serverGroupConfiguration.service';

export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE =
  'spinnaker.amazon.serverGroupCommandBuilder.service';
export const name = AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE; // for backwards compatibility
angular
  .module(AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE, [
    INSTANCE_TYPE_SERVICE,
    AWS_SERVER_GROUP_CONFIGURATION_SERVICE,
  ])
  .factory('awsServerGroupCommandBuilder', [
    '$q',
    'instanceTypeService',
    'awsServerGroupConfigurationService',
    function ($q, instanceTypeService, awsServerGroupConfigurationService) {
      function buildNewServerGroupCommand(application, defaults) {
        defaults = defaults || {};
        const credentialsLoader = AccountService.getCredentialsKeyedByAccount('aws');

        const defaultCredentials =
          defaults.account || application.defaultCredentials.aws || AWSProviderSettings.defaults.account;
        const defaultRegion = defaults.region || application.defaultRegions.aws || AWSProviderSettings.defaults.region;
        const defaultSubnet = defaults.subnet || AWSProviderSettings.defaults.subnetType || '';

        const preferredZonesLoader = AccountService.getAvailabilityZonesForAccountAndRegion(
          'aws',
          defaultCredentials,
          defaultRegion,
        );

        return $q
          .all({
            preferredZones: preferredZonesLoader,
            credentialsKeyedByAccount: credentialsLoader,
          })
          .then(function (asyncData) {
            const availabilityZones = asyncData.preferredZones;

            const credentials = asyncData.credentialsKeyedByAccount[defaultCredentials];
            const keyPair = credentials ? credentials.defaultKeyPair : null;
            const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});

            let defaultIamRole = AWSProviderSettings.defaults.iamRole || 'BaseIAMRole';
            defaultIamRole = defaultIamRole.replace('{{application}}', application.name);

            const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

            const command = {
              application: application.name,
              credentials: defaultCredentials,
              region: defaultRegion,
              strategy: '',
              capacity: {
                min: 1,
                max: 1,
                desired: 1,
              },
              targetHealthyDeployPercentage: 100,
              cooldown: 10,
              enabledMetrics: [],
              healthCheckType: 'EC2',
              healthCheckGracePeriod: 600,
              instanceMonitoring: false,
              ebsOptimized: false,
              selectedProvider: 'aws',
              iamRole: defaultIamRole,
              terminationPolicies: ['Default'],
              vpcId: null,
              subnetType: defaultSubnet,
              availabilityZones: availabilityZones,
              keyPair: keyPair,
              suspendedProcesses: [],
              securityGroups: [],
              stack: '',
              freeFormDetails: '',
              spotPrice: '',
              tags: {},
              useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
              copySourceCustomBlockDeviceMappings: false, // default to using block device mappings from current instance type
              viewState: {
                instanceProfile: 'custom',
                useAllImageSelection: false,
                useSimpleCapacity: true,
                usePreferredZones: true,
                mode: defaults.mode || 'create',
                disableStrategySelection: true,
                dirty: {},
                submitButtonLabel: getSubmitButtonLabel(defaults.mode || 'create'),
              },
            };

            if (
              application.attributes &&
              application.attributes.platformHealthOnlyShowOverride &&
              application.attributes.platformHealthOnly
            ) {
              command.interestingHealthProviderNames = ['Amazon'];
            }

            if (AWSProviderSettings.serverGroups && AWSProviderSettings.serverGroups.enableIMDSv2) {
              /**
               * Older SDKs do not support IMDSv2. A timestamp can be optionally configured at which any apps created after can safely default to using IMDSv2.
               */
              const appAgeRequirement = AWSProviderSettings.serverGroups.defaultIMDSv2AppAgeLimit;
              const creationDate = application.attributes.createTs;

              command.requireIMDSv2 =
                appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement ? true : false;
            }

            return command;
          });
      }

      function buildServerGroupCommandFromPipeline(application, originalCluster) {
        const pipelineCluster = _.cloneDeep(originalCluster);
        const region = Object.keys(pipelineCluster.availabilityZones)[0];
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType(
          'aws',
          pipelineCluster.instanceType,
        );
        const commandOptions = { account: pipelineCluster.account, region: region };
        const asyncLoader = $q.all({
          command: buildNewServerGroupCommand(application, commandOptions),
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function (asyncData) {
          const command = asyncData.command;
          const zones = pipelineCluster.availabilityZones[region];
          const usePreferredZones = zones.join(',') === command.availabilityZones.join(',');

          const viewState = {
            instanceProfile: asyncData.instanceProfile,
            disableImageSelection: true,
            useSimpleCapacity:
              pipelineCluster.capacity.min === pipelineCluster.capacity.max &&
              pipelineCluster.useSourceCapacity !== true,
            usePreferredZones: usePreferredZones,
            mode: 'editPipeline',
            submitButtonLabel: 'Done',
            templatingEnabled: true,
            existingPipelineCluster: true,
            dirty: {},
          };

          const viewOverrides = {
            region: region,
            credentials: pipelineCluster.account,
            availabilityZones: pipelineCluster.availabilityZones[region],
            iamRole: pipelineCluster.iamRole,
            viewState: viewState,
          };

          pipelineCluster.strategy = pipelineCluster.strategy || '';

          return angular.extend({}, command, pipelineCluster, viewOverrides);
        });
      }

      // Only used to prepare view requiring template selecting
      function buildNewServerGroupCommandForPipeline() {
        return $q.when({
          viewState: {
            requiresTemplateSelection: true,
          },
        });
      }

      function getSubmitButtonLabel(mode) {
        switch (mode) {
          case 'createPipeline':
            return 'Add';
          case 'editPipeline':
            return 'Done';
          case 'clone':
            return 'Clone';
          default:
            return 'Create';
        }
      }

      function buildUpdateServerGroupCommand(serverGroup) {
        const command = {
          type: 'modifyAsg',
          asgs: [{ asgName: serverGroup.name, region: serverGroup.region }],
          cooldown: serverGroup.asg.defaultCooldown,
          enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map((m) => m.metric),
          healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
          healthCheckType: serverGroup.asg.healthCheckType,
          terminationPolicies: angular.copy(serverGroup.asg.terminationPolicies),
          credentials: serverGroup.account,
        };
        awsServerGroupConfigurationService.configureUpdateCommand(command);
        return command;
      }

      function buildServerGroupCommandFromExisting(application, serverGroup, mode = 'clone') {
        const preferredZonesLoader = AccountService.getPreferredZonesByAccount('aws');
        const subnetsLoader = SubnetReader.listSubnets();

        const serverGroupName = NameUtils.parseServerGroupName(serverGroup.asg.autoScalingGroupName);

        const instanceType = serverGroup.launchConfig
          ? serverGroup.launchConfig.instanceType
          : serverGroup.launchTemplate
          ? serverGroup.launchTemplate.launchTemplateData.instanceType
          : null;
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType('aws', instanceType);

        const asyncLoader = $q.all({
          preferredZones: preferredZonesLoader,
          subnets: subnetsLoader,
          instanceProfile: instanceTypeCategoryLoader,
        });

        return asyncLoader.then(function (asyncData) {
          const zones = serverGroup.asg.availabilityZones.sort();
          let usePreferredZones = false;
          const preferredZonesForAccount = asyncData.preferredZones[serverGroup.account];
          if (preferredZonesForAccount) {
            const preferredZones = preferredZonesForAccount[serverGroup.region].sort();
            usePreferredZones = zones.join(',') === preferredZones.join(',');
          }

          // These processes should never be copied over, as the affect launching instances and enabling traffic
          const enabledProcesses = ['Launch', 'Terminate', 'AddToLoadBalancer'];

          const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
          const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

          const existingTags = {};
          // These tags are applied by Clouddriver (if configured to do so), regardless of what the user might enter
          // Might be worth feature flagging this if it turns out other folks are hard-coding these values
          const reservedTags = ['spinnaker:application', 'spinnaker:stack', 'spinnaker:details'];
          if (serverGroup.asg.tags) {
            serverGroup.asg.tags
              .filter((t) => !reservedTags.includes(t.key))
              .forEach((tag) => {
                existingTags[tag.key] = tag.value;
              });
          }

          const command = {
            application: application.name,
            strategy: '',
            stack: serverGroupName.stack,
            freeFormDetails: serverGroupName.freeFormDetails,
            credentials: serverGroup.account,
            cooldown: serverGroup.asg.defaultCooldown,
            enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map((m) => m.metric),
            healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
            healthCheckType: serverGroup.asg.healthCheckType,
            terminationPolicies: serverGroup.asg.terminationPolicies,
            loadBalancers: serverGroup.asg.loadBalancerNames,
            region: serverGroup.region,
            useSourceCapacity: false,
            capacity: {
              min: serverGroup.asg.minSize,
              max: serverGroup.asg.maxSize,
              desired: serverGroup.asg.desiredCapacity,
            },
            targetHealthyDeployPercentage: 100,
            availabilityZones: zones,
            selectedProvider: 'aws',
            source: {
              account: serverGroup.account,
              region: serverGroup.region,
              asgName: serverGroup.asg.autoScalingGroupName,
            },
            suspendedProcesses: (serverGroup.asg.suspendedProcesses || [])
              .map((process) => process.processName)
              .filter((name) => !enabledProcesses.includes(name)),
            tags: Object.assign({}, serverGroup.tags, existingTags),
            targetGroups: serverGroup.targetGroups,
            useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
            copySourceCustomBlockDeviceMappings: mode === 'clone', // default to using block device mappings if not cloning
            viewState: {
              instanceProfile: asyncData.instanceProfile,
              useAllImageSelection: false,
              useSimpleCapacity: serverGroup.asg.minSize === serverGroup.asg.maxSize,
              usePreferredZones: usePreferredZones,
              mode: mode,
              submitButtonLabel: getSubmitButtonLabel(mode),
              isNew: false,
              dirty: {},
            },
          };

          if (
            application.attributes &&
            application.attributes.platformHealthOnlyShowOverride &&
            application.attributes.platformHealthOnly
          ) {
            command.interestingHealthProviderNames = ['Amazon'];
          }

          if (mode === 'editPipeline') {
            command.useSourceCapacity = true;
            command.viewState.useSimpleCapacity = false;
            command.strategy = 'redblack';
            const redblack = DeploymentStrategyRegistry.getStrategy('redblack');
            redblack.initializationMethod && redblack.initializationMethod(command);
            command.suspendedProcesses = [];
          }

          const vpcZoneIdentifier = serverGroup.asg.vpczoneIdentifier;
          if (vpcZoneIdentifier !== '') {
            const subnetId = vpcZoneIdentifier.split(',')[0];
            const subnet = _.chain(asyncData.subnets).find({ id: subnetId }).value();
            command.subnetType = subnet.purpose;
            command.vpcId = subnet.vpcId;
          } else {
            command.subnetType = '';
            command.vpcId = null;
          }

          if (serverGroup.launchConfig) {
            angular.extend(command, {
              instanceType: serverGroup.launchConfig.instanceType,
              iamRole: serverGroup.launchConfig.iamInstanceProfile,
              keyPair: serverGroup.launchConfig.keyName,
              associatePublicIpAddress: serverGroup.launchConfig.associatePublicIpAddress,
              ramdiskId: serverGroup.launchConfig.ramdiskId,
              instanceMonitoring: serverGroup.launchConfig.instanceMonitoring.enabled,
              ebsOptimized: serverGroup.launchConfig.ebsOptimized,
              spotPrice: serverGroup.launchConfig.spotPrice,
            });
            if (serverGroup.launchConfig.userData) {
              command.base64UserData = serverGroup.launchConfig.userData;
            }
            command.viewState.imageId = serverGroup.launchConfig.imageId;
          }

          if (serverGroup.launchTemplate) {
            const { launchTemplateData } = serverGroup.launchTemplate;
            const maxPrice =
              launchTemplateData.instanceMarketOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions &&
              launchTemplateData.instanceMarketOptions.spotOptions.maxPrice;
            const { ipv6AddressCount } =
              launchTemplateData.networkInterfaces &&
              launchTemplateData.networkInterfaces.length &&
              launchTemplateData.networkInterfaces[0];

            angular.extend(command, {
              instanceType: launchTemplateData.instanceType,
              iamRole: launchTemplateData.iamInstanceProfile.name,
              keyPair: launchTemplateData.keyName,
              associateIPv6Address: Boolean(ipv6AddressCount),
              ramdiskId: launchTemplateData.ramdiskId,
              instanceMonitoring: launchTemplateData.monitoring.enabled,
              ebsOptimized: launchTemplateData.ebsOptimized,
              spotPrice: maxPrice || undefined,
              requireIMDSv2: Boolean(
                launchTemplateData.metadataOptions && launchTemplateData.metadataOptions.httpsTokens === 'required',
              ),
            });

            command.viewState.imageId = launchTemplateData.imageId;
          }

          if (mode === 'clone' && serverGroup.image && serverGroup.image.name) {
            command.amiName = serverGroup.image.name;
          }

          if (serverGroup.launchConfig && serverGroup.launchConfig.securityGroups.length) {
            command.securityGroups = serverGroup.launchConfig.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.securityGroups.length) {
            command.securityGroups = serverGroup.launchTemplate.launchTemplateData.securityGroups;
          }

          if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.networkInterfaces) {
            const networkInterface =
              serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) || {};
            command.securityGroups = networkInterface.groups;
          }

          return command;
        });
      }

      return {
        buildNewServerGroupCommand: buildNewServerGroupCommand,
        buildServerGroupCommandFromExisting: buildServerGroupCommandFromExisting,
        buildNewServerGroupCommandForPipeline: buildNewServerGroupCommandForPipeline,
        buildServerGroupCommandFromPipeline: buildServerGroupCommandFromPipeline,
        buildUpdateServerGroupCommand: buildUpdateServerGroupCommand,
      };
    },
  ]);
<commit_msg>chore(titus/serverGroup): Migrate from $q.all({}) to $q.all([])<commit_after>import * as angular from 'angular';
import _ from 'lodash';

import {
  AccountService,
  DeploymentStrategyRegistry,
  INSTANCE_TYPE_SERVICE,
  NameUtils,
  SubnetReader,
} from '@spinnaker/core';

import { AWSProviderSettings } from 'amazon/aws.settings';
import { AWS_SERVER_GROUP_CONFIGURATION_SERVICE } from './serverGroupConfiguration.service';

export const AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE =
  'spinnaker.amazon.serverGroupCommandBuilder.service';
export const name = AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE; // for backwards compatibility
angular
  .module(AMAZON_SERVERGROUP_CONFIGURE_SERVERGROUPCOMMANDBUILDER_SERVICE, [
    INSTANCE_TYPE_SERVICE,
    AWS_SERVER_GROUP_CONFIGURATION_SERVICE,
  ])
  .factory('awsServerGroupCommandBuilder', [
    '$q',
    'instanceTypeService',
    'awsServerGroupConfigurationService',
    function ($q, instanceTypeService, awsServerGroupConfigurationService) {
      function buildNewServerGroupCommand(application, defaults) {
        defaults = defaults || {};
        const credentialsLoader = AccountService.getCredentialsKeyedByAccount('aws');

        const defaultCredentials =
          defaults.account || application.defaultCredentials.aws || AWSProviderSettings.defaults.account;
        const defaultRegion = defaults.region || application.defaultRegions.aws || AWSProviderSettings.defaults.region;
        const defaultSubnet = defaults.subnet || AWSProviderSettings.defaults.subnetType || '';

        const preferredZonesLoader = AccountService.getAvailabilityZonesForAccountAndRegion(
          'aws',
          defaultCredentials,
          defaultRegion,
        );

        return $q
          .all([preferredZonesLoader, credentialsLoader])
          .then(function ([preferredZones, credentialsKeyedByAccount]) {
            const credentials = credentialsKeyedByAccount[defaultCredentials];
            const keyPair = credentials ? credentials.defaultKeyPair : null;
            const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});

            let defaultIamRole = AWSProviderSettings.defaults.iamRole || 'BaseIAMRole';
            defaultIamRole = defaultIamRole.replace('{{application}}', application.name);

            const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

            const command = {
              application: application.name,
              credentials: defaultCredentials,
              region: defaultRegion,
              strategy: '',
              capacity: {
                min: 1,
                max: 1,
                desired: 1,
              },
              targetHealthyDeployPercentage: 100,
              cooldown: 10,
              enabledMetrics: [],
              healthCheckType: 'EC2',
              healthCheckGracePeriod: 600,
              instanceMonitoring: false,
              ebsOptimized: false,
              selectedProvider: 'aws',
              iamRole: defaultIamRole,
              terminationPolicies: ['Default'],
              vpcId: null,
              subnetType: defaultSubnet,
              availabilityZones: preferredZones,
              keyPair: keyPair,
              suspendedProcesses: [],
              securityGroups: [],
              stack: '',
              freeFormDetails: '',
              spotPrice: '',
              tags: {},
              useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
              copySourceCustomBlockDeviceMappings: false, // default to using block device mappings from current instance type
              viewState: {
                instanceProfile: 'custom',
                useAllImageSelection: false,
                useSimpleCapacity: true,
                usePreferredZones: true,
                mode: defaults.mode || 'create',
                disableStrategySelection: true,
                dirty: {},
                submitButtonLabel: getSubmitButtonLabel(defaults.mode || 'create'),
              },
            };

            if (
              application.attributes &&
              application.attributes.platformHealthOnlyShowOverride &&
              application.attributes.platformHealthOnly
            ) {
              command.interestingHealthProviderNames = ['Amazon'];
            }

            if (AWSProviderSettings.serverGroups && AWSProviderSettings.serverGroups.enableIMDSv2) {
              /**
               * Older SDKs do not support IMDSv2. A timestamp can be optionally configured at which any apps created after can safely default to using IMDSv2.
               */
              const appAgeRequirement = AWSProviderSettings.serverGroups.defaultIMDSv2AppAgeLimit;
              const creationDate = application.attributes.createTs;

              command.requireIMDSv2 =
                appAgeRequirement && creationDate && Number(creationDate) > appAgeRequirement ? true : false;
            }

            return command;
          });
      }

      function buildServerGroupCommandFromPipeline(application, originalCluster) {
        const pipelineCluster = _.cloneDeep(originalCluster);
        const region = Object.keys(pipelineCluster.availabilityZones)[0];
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType(
          'aws',
          pipelineCluster.instanceType,
        );
        const commandOptions = { account: pipelineCluster.account, region: region };
        const asyncLoader = $q.all([
          buildNewServerGroupCommand(application, commandOptions),
          instanceTypeCategoryLoader,
        ]);

        return asyncLoader.then(function ([command, instanceProfile]) {
          const zones = pipelineCluster.availabilityZones[region];
          const usePreferredZones = zones.join(',') === command.availabilityZones.join(',');

          const viewState = {
            instanceProfile,
            disableImageSelection: true,
            useSimpleCapacity:
              pipelineCluster.capacity.min === pipelineCluster.capacity.max &&
              pipelineCluster.useSourceCapacity !== true,
            usePreferredZones: usePreferredZones,
            mode: 'editPipeline',
            submitButtonLabel: 'Done',
            templatingEnabled: true,
            existingPipelineCluster: true,
            dirty: {},
          };

          const viewOverrides = {
            region: region,
            credentials: pipelineCluster.account,
            availabilityZones: pipelineCluster.availabilityZones[region],
            iamRole: pipelineCluster.iamRole,
            viewState: viewState,
          };

          pipelineCluster.strategy = pipelineCluster.strategy || '';

          return angular.extend({}, command, pipelineCluster, viewOverrides);
        });
      }

      // Only used to prepare view requiring template selecting
      function buildNewServerGroupCommandForPipeline() {
        return $q.when({
          viewState: {
            requiresTemplateSelection: true,
          },
        });
      }

      function getSubmitButtonLabel(mode) {
        switch (mode) {
          case 'createPipeline':
            return 'Add';
          case 'editPipeline':
            return 'Done';
          case 'clone':
            return 'Clone';
          default:
            return 'Create';
        }
      }

      function buildUpdateServerGroupCommand(serverGroup) {
        const command = {
          type: 'modifyAsg',
          asgs: [{ asgName: serverGroup.name, region: serverGroup.region }],
          cooldown: serverGroup.asg.defaultCooldown,
          enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map((m) => m.metric),
          healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
          healthCheckType: serverGroup.asg.healthCheckType,
          terminationPolicies: angular.copy(serverGroup.asg.terminationPolicies),
          credentials: serverGroup.account,
        };
        awsServerGroupConfigurationService.configureUpdateCommand(command);
        return command;
      }

      function buildServerGroupCommandFromExisting(application, serverGroup, mode = 'clone') {
        const preferredZonesLoader = AccountService.getPreferredZonesByAccount('aws');
        const subnetsLoader = SubnetReader.listSubnets();

        const serverGroupName = NameUtils.parseServerGroupName(serverGroup.asg.autoScalingGroupName);

        const instanceType = serverGroup.launchConfig
          ? serverGroup.launchConfig.instanceType
          : serverGroup.launchTemplate
          ? serverGroup.launchTemplate.launchTemplateData.instanceType
          : null;
        const instanceTypeCategoryLoader = instanceTypeService.getCategoryForInstanceType('aws', instanceType);

        return $q
          .all([preferredZonesLoader, subnetsLoader, instanceTypeCategoryLoader])
          .then(function ([preferredZones, subnets, instanceProfile]) {
            const zones = serverGroup.asg.availabilityZones.sort();
            let usePreferredZones = false;
            const preferredZonesForAccount = preferredZones[serverGroup.account];
            if (preferredZonesForAccount) {
              const preferredZones = preferredZonesForAccount[serverGroup.region].sort();
              usePreferredZones = zones.join(',') === preferredZones.join(',');
            }

            // These processes should never be copied over, as the affect launching instances and enabling traffic
            const enabledProcesses = ['Launch', 'Terminate', 'AddToLoadBalancer'];

            const applicationAwsSettings = _.get(application, 'attributes.providerSettings.aws', {});
            const useAmiBlockDeviceMappings = applicationAwsSettings.useAmiBlockDeviceMappings || false;

            const existingTags = {};
            // These tags are applied by Clouddriver (if configured to do so), regardless of what the user might enter
            // Might be worth feature flagging this if it turns out other folks are hard-coding these values
            const reservedTags = ['spinnaker:application', 'spinnaker:stack', 'spinnaker:details'];
            if (serverGroup.asg.tags) {
              serverGroup.asg.tags
                .filter((t) => !reservedTags.includes(t.key))
                .forEach((tag) => {
                  existingTags[tag.key] = tag.value;
                });
            }

            const command = {
              application: application.name,
              strategy: '',
              stack: serverGroupName.stack,
              freeFormDetails: serverGroupName.freeFormDetails,
              credentials: serverGroup.account,
              cooldown: serverGroup.asg.defaultCooldown,
              enabledMetrics: _.get(serverGroup, 'asg.enabledMetrics', []).map((m) => m.metric),
              healthCheckGracePeriod: serverGroup.asg.healthCheckGracePeriod,
              healthCheckType: serverGroup.asg.healthCheckType,
              terminationPolicies: serverGroup.asg.terminationPolicies,
              loadBalancers: serverGroup.asg.loadBalancerNames,
              region: serverGroup.region,
              useSourceCapacity: false,
              capacity: {
                min: serverGroup.asg.minSize,
                max: serverGroup.asg.maxSize,
                desired: serverGroup.asg.desiredCapacity,
              },
              targetHealthyDeployPercentage: 100,
              availabilityZones: zones,
              selectedProvider: 'aws',
              source: {
                account: serverGroup.account,
                region: serverGroup.region,
                asgName: serverGroup.asg.autoScalingGroupName,
              },
              suspendedProcesses: (serverGroup.asg.suspendedProcesses || [])
                .map((process) => process.processName)
                .filter((name) => !enabledProcesses.includes(name)),
              tags: Object.assign({}, serverGroup.tags, existingTags),
              targetGroups: serverGroup.targetGroups,
              useAmiBlockDeviceMappings: useAmiBlockDeviceMappings,
              copySourceCustomBlockDeviceMappings: mode === 'clone', // default to using block device mappings if not cloning
              viewState: {
                instanceProfile,
                useAllImageSelection: false,
                useSimpleCapacity: serverGroup.asg.minSize === serverGroup.asg.maxSize,
                usePreferredZones: usePreferredZones,
                mode: mode,
                submitButtonLabel: getSubmitButtonLabel(mode),
                isNew: false,
                dirty: {},
              },
            };

            if (
              application.attributes &&
              application.attributes.platformHealthOnlyShowOverride &&
              application.attributes.platformHealthOnly
            ) {
              command.interestingHealthProviderNames = ['Amazon'];
            }

            if (mode === 'editPipeline') {
              command.useSourceCapacity = true;
              command.viewState.useSimpleCapacity = false;
              command.strategy = 'redblack';
              const redblack = DeploymentStrategyRegistry.getStrategy('redblack');
              redblack.initializationMethod && redblack.initializationMethod(command);
              command.suspendedProcesses = [];
            }

            const vpcZoneIdentifier = serverGroup.asg.vpczoneIdentifier;
            if (vpcZoneIdentifier !== '') {
              const subnetId = vpcZoneIdentifier.split(',')[0];
              const subnet = _.chain(subnets).find({ id: subnetId }).value();
              command.subnetType = subnet.purpose;
              command.vpcId = subnet.vpcId;
            } else {
              command.subnetType = '';
              command.vpcId = null;
            }

            if (serverGroup.launchConfig) {
              angular.extend(command, {
                instanceType: serverGroup.launchConfig.instanceType,
                iamRole: serverGroup.launchConfig.iamInstanceProfile,
                keyPair: serverGroup.launchConfig.keyName,
                associatePublicIpAddress: serverGroup.launchConfig.associatePublicIpAddress,
                ramdiskId: serverGroup.launchConfig.ramdiskId,
                instanceMonitoring: serverGroup.launchConfig.instanceMonitoring.enabled,
                ebsOptimized: serverGroup.launchConfig.ebsOptimized,
                spotPrice: serverGroup.launchConfig.spotPrice,
              });
              if (serverGroup.launchConfig.userData) {
                command.base64UserData = serverGroup.launchConfig.userData;
              }
              command.viewState.imageId = serverGroup.launchConfig.imageId;
            }

            if (serverGroup.launchTemplate) {
              const { launchTemplateData } = serverGroup.launchTemplate;
              const maxPrice =
                launchTemplateData.instanceMarketOptions &&
                launchTemplateData.instanceMarketOptions.spotOptions &&
                launchTemplateData.instanceMarketOptions.spotOptions.maxPrice;
              const { ipv6AddressCount } =
                launchTemplateData.networkInterfaces &&
                launchTemplateData.networkInterfaces.length &&
                launchTemplateData.networkInterfaces[0];

              angular.extend(command, {
                instanceType: launchTemplateData.instanceType,
                iamRole: launchTemplateData.iamInstanceProfile.name,
                keyPair: launchTemplateData.keyName,
                associateIPv6Address: Boolean(ipv6AddressCount),
                ramdiskId: launchTemplateData.ramdiskId,
                instanceMonitoring: launchTemplateData.monitoring.enabled,
                ebsOptimized: launchTemplateData.ebsOptimized,
                spotPrice: maxPrice || undefined,
                requireIMDSv2: Boolean(
                  launchTemplateData.metadataOptions && launchTemplateData.metadataOptions.httpsTokens === 'required',
                ),
              });

              command.viewState.imageId = launchTemplateData.imageId;
            }

            if (mode === 'clone' && serverGroup.image && serverGroup.image.name) {
              command.amiName = serverGroup.image.name;
            }

            if (serverGroup.launchConfig && serverGroup.launchConfig.securityGroups.length) {
              command.securityGroups = serverGroup.launchConfig.securityGroups;
            }

            if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.securityGroups.length) {
              command.securityGroups = serverGroup.launchTemplate.launchTemplateData.securityGroups;
            }

            if (serverGroup.launchTemplate && serverGroup.launchTemplate.launchTemplateData.networkInterfaces) {
              const networkInterface =
                serverGroup.launchTemplate.launchTemplateData.networkInterfaces.find((ni) => ni.deviceIndex === 0) ||
                {};
              command.securityGroups = networkInterface.groups;
            }

            return command;
          });
      }

      return {
        buildNewServerGroupCommand: buildNewServerGroupCommand,
        buildServerGroupCommandFromExisting: buildServerGroupCommandFromExisting,
        buildNewServerGroupCommandForPipeline: buildNewServerGroupCommandForPipeline,
        buildServerGroupCommandFromPipeline: buildServerGroupCommandFromPipeline,
        buildUpdateServerGroupCommand: buildUpdateServerGroupCommand,
      };
    },
  ]);
<|endoftext|>


<commit_before>/**
 * @Author: liushaobo2005@163.com
 * @Date: 2017.3.1 下午 3:33
 * @Desc: 公共组件 - QATest
 * @Name: QATest.js
 * @LifeCycle：http://www.tuicool.com/articles/nu6zInB
 */

import React, {Component, PropTypes} from "react";
import {Dimensions, ListView, StyleSheet, Text, View} from "react-native";
import {Actions} from "react-native-router-flux";
import TitleBar from "../../components/bar/TitleBar";
import Icon from "react-native-vector-icons/FontAwesome";
import * as actions from "../../../common/actions";
import {bindActionCreators} from "redux";
import {connect} from "react-redux";

const {height, width} = Dimensions.get('window');
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});

class QATest extends Component {

    /**
     * 父组件传入的属性值
     * @type {{style: *, account: *, name: *, isTrue: *, callback: *}}
     */
    static propTypes = {
        style: View.propTypes.style,
        account: PropTypes.number,
        name: PropTypes.string,
        isTrue: PropTypes.bool,
        callback: PropTypes.func,
    };

    /**
     * 父组件传入的数据
     * @type {{data: {}}}
     */
    static defaultProps = {
        data: {}
    };

    /**
     * 构造函数
     * @param props
     * @param context
     */
    constructor(props, context) {
        console.log("QATest constructor()");
        super(props, context);
        this.state = {};
    }

    /**
     * 当前组件渲染次数
     * @type {number}
     */
    renderCount = 0;

    /**
     * 组件加载前
     * 生命周期中仅被调用1次，可以使用SetState
     */
    componentWillMount() {
        console.log("QATest componentWillMount()");
    }

    /**
     * 组件加载后
     * 生命周期中仅被调用1次，可以使用SetState
     * 用于网络请求和页面渲染
     */
    componentDidMount() {
        console.log("QATest componentDidMount()");
        //this.props.actions.getQATestScenesList();
    }

    /**
     * 父组件属性值变更监听
     * 可以使用SetState
     * @param newProps
     */
    componentWillReceiveProps(newProps) {
        console.log("QATest componentWillReceiveProps():, newProps");
    }

    /**
     * 属性值/状态值变更后，是否需要刷新页面
     * @param nextProps 是即将被设置的属性，旧的属性还是可以通过 this.props 来获取。
     * @param nextState 表示组件即将更新的状态值。
     * @returns {boolean} 默认true, 返回值决定是否需要更新组件，如果 true 表示需要更新，继续走后面的更新流程。
     */
    shouldComponentUpdate(nextProps, nextState) {
        let isUpdate = (this.props != nextProps) || (this.state != nextState);
        console.log("QATest shouldComponentUpdate():" , isUpdate);
        return isUpdate;
    }

    /**
     * 如果组件状态或者属性改变，并且shouldComponentUpdate返回为 true
     * @param nextProps 更新之后的属性
     * @param nextState 更新之后的状态
     */
    componentWillUpdate(nextProps, nextState) {
        console.log("QATest componentWillUpdate()");
    }

    /**
     * 更新完成界面之后通知
     * @param prevProps 更新之前的属性
     * @param prevState 更新之前的状态
     * @returns {boolean}
     */
    componentDidUpdate(prevProps, prevState) {
        console.log("QATest componentDidUpdate()");
    }

    /**
     * 组件即将卸载前调用
     * 在这个函数中，可以做一些组件相关的清理工作，例如取消计时器、网络请求等。
     */
    componentDidUnMount() {
        console.log("QATest componentDidUnMount()");

    }

    onPress = ()=> {
    };

    renderRow(rowData, sectionId, rowId) {

        return (
            <View style={QATestStyles.btnList}>
                <Icon.Button name="star" backgroundColor="#aaa" onPress={() => {
                    try {
                        Actions[rowId]()
                    } catch (e) {
                        alert(e.message)
                    }
                }}>
                    <Text style={{fontFamily: 'Arial', fontSize: 15}}>{rowData}</Text>
                </Icon.Button>
            </View>
        )
    }

    /**
     * 组件更新
     * @returns {XML}
     */
    render() {
        this.renderCount++;
        console.log("QATest render() renderCount:" , this.renderCount);
        return (
            <View style={[QATestStyles.container,this.props.style]}>
                <TitleBar
                    label="品质测试"
                    labelStyle={{backgroundColor:"transparent",color:"black"}}
                    leftView={<Icon.Button name="list-ul" size={25} color="#166AF6" backgroundColor="transparent"
                                           onPress={()=>{alert("click android logo")}}/>}
                    rightView={<Icon.Button name="undo" size={25} color="#999" backgroundColor="transparent"
                                            onPress={()=>{alert("click share icon")}}/>}
                    style={{height: 45}}/>
                    <Text onPress={()=>{this.onPress()}}>{JSON.stringify(config)}</Text>

                <ListView
                    enableEmptySections={true}
                    dataSource={ds.cloneWithRows(this.props.state.data.test)}
                    renderRow={this.renderRow}
                />
            </View>
        );
    }

}

const QATestStyles = StyleSheet.create({
    container: {
        flex:1,
    },
    scrollView: {
        width:width,
        height:height*2
    },
    btnList: {
        margin: 3,
    }
});

/**
 * 把this.state关联到this.props.state
 * @param state
 * @returns {{state: *}}
 */
function mapStateToProps(state) {
    return {
        state: state.router
    }
}

/**
 * 把actions.user_info, dispatch通过type关联到一起
 * @param dispatch
 * @returns {{actions: (A|B|M|N)}}
 */
function mapDispatchToProps(dispatch) {
    console.log("HomePage actions:, actions");
    return {
        actions: bindActionCreators(actions, dispatch),
    }
}

/**
 * 把mapStateToProps, mapDispatchToProps绑定到MainRouter组件上
 */
export default connect(mapStateToProps, mapDispatchToProps)(QATest);<commit_msg>modify logic. @liushaobo2005@163.com<commit_after>/**
 * @Author: liushaobo2005@163.com
 * @Date: 2017.3.1 下午 3:33
 * @Desc: 公共组件 - QATest
 * @Name: QATest.js
 * @LifeCycle：http://www.tuicool.com/articles/nu6zInB
 */

import React, {Component, PropTypes} from "react";
import {Dimensions, ListView, StyleSheet, Text, View} from "react-native";
import {Actions} from "react-native-router-flux";
import TitleBar from "../../components/bar/TitleBar";
import Icon from "react-native-vector-icons/FontAwesome";
import * as actions from "../../../common/actions";
import {bindActionCreators} from "redux";
import {connect} from "react-redux";

const {height, width} = Dimensions.get('window');
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});

class QATest extends Component {

    /**
     * 父组件传入的属性值
     * @type {{style: *, account: *, name: *, isTrue: *, callback: *}}
     */
    static propTypes = {
        style: View.propTypes.style,
        account: PropTypes.number,
        name: PropTypes.string,
        isTrue: PropTypes.bool,
        callback: PropTypes.func,
    };

    /**
     * 父组件传入的数据
     * @type {{data: {}}}
     */
    static defaultProps = {
        data: {}
    };

    /**
     * 构造函数
     * @param props
     * @param context
     */
    constructor(props, context) {
        console.log("QATest constructor()");
        super(props, context);
        this.state = {};
    }

    /**
     * 当前组件渲染次数
     * @type {number}
     */
    renderCount = 0;

    /**
     * 组件加载前
     * 生命周期中仅被调用1次，可以使用SetState
     */
    componentWillMount() {
        console.log("QATest componentWillMount()");
    }

    /**
     * 组件加载后
     * 生命周期中仅被调用1次，可以使用SetState
     * 用于网络请求和页面渲染
     */
    componentDidMount() {
        console.log("QATest componentDidMount()");
        //this.props.actions.getQATestScenesList();
    }

    /**
     * 父组件属性值变更监听
     * 可以使用SetState
     * @param newProps
     */
    componentWillReceiveProps(newProps) {
        console.log("QATest componentWillReceiveProps():, newProps");
    }

    /**
     * 属性值/状态值变更后，是否需要刷新页面
     * @param nextProps 是即将被设置的属性，旧的属性还是可以通过 this.props 来获取。
     * @param nextState 表示组件即将更新的状态值。
     * @returns {boolean} 默认true, 返回值决定是否需要更新组件，如果 true 表示需要更新，继续走后面的更新流程。
     */
    shouldComponentUpdate(nextProps, nextState) {
        //let isUpdate = (this.props != nextProps) || (this.state != nextState);
        let isUpdate = this.state != nextState;
        console.log("QATest shouldComponentUpdate():" , isUpdate);
        return isUpdate;
    }

    /**
     * 如果组件状态或者属性改变，并且shouldComponentUpdate返回为 true
     * @param nextProps 更新之后的属性
     * @param nextState 更新之后的状态
     */
    componentWillUpdate(nextProps, nextState) {
        console.log("QATest componentWillUpdate()");
    }

    /**
     * 更新完成界面之后通知
     * @param prevProps 更新之前的属性
     * @param prevState 更新之前的状态
     * @returns {boolean}
     */
    componentDidUpdate(prevProps, prevState) {
        console.log("QATest componentDidUpdate()");
    }

    /**
     * 组件即将卸载前调用
     * 在这个函数中，可以做一些组件相关的清理工作，例如取消计时器、网络请求等。
     */
    componentDidUnMount() {
        console.log("QATest componentDidUnMount()");

    }

    onPress = ()=> {
    };

    renderRow(rowData, sectionId, rowId) {

        return (
            <View style={QATestStyles.btnList}>
                <Icon.Button name="star" backgroundColor="#aaa" onPress={() => {
                    try {
                        Actions[rowId]()
                    } catch (e) {
                        alert(e.message)
                    }
                }}>
                    <Text style={{fontFamily: 'Arial', fontSize: 15}}>{rowData}</Text>
                </Icon.Button>
            </View>
        )
    }

    /**
     * 组件更新
     * @returns {XML}
     */
    render() {
        this.renderCount++;
        console.log("QATest render() renderCount:" , this.renderCount);
        return (
            <View style={[QATestStyles.container,this.props.style]}>
                <TitleBar
                    label="品质测试"
                    labelStyle={{backgroundColor:"transparent",color:"black"}}
                    leftView={<Icon.Button name="list-ul" size={25} color="#166AF6" backgroundColor="transparent"
                                           onPress={()=>{alert("click android logo")}}/>}
                    rightView={<Icon.Button name="undo" size={25} color="#999" backgroundColor="transparent"
                                            onPress={()=>{alert("click share icon")}}/>}
                    style={{height: 45}}/>
                    <Text onPress={()=>{this.onPress()}}>{JSON.stringify(config)}</Text>

                <ListView
                    enableEmptySections={true}
                    dataSource={ds.cloneWithRows(this.props.state.data.test)}
                    renderRow={this.renderRow}
                />
            </View>
        );
    }

}

const QATestStyles = StyleSheet.create({
    container: {
        flex:1,
    },
    scrollView: {
        width:width,
        height:height*2
    },
    btnList: {
        margin: 3,
    }
});

/**
 * 把this.state关联到this.props.state
 * @param state
 * @returns {{state: *}}
 */
function mapStateToProps(state) {
    return {
        state: state.router
    }
}

/**
 * 把actions.user_info, dispatch通过type关联到一起
 * @param dispatch
 * @returns {{actions: (A|B|M|N)}}
 */
function mapDispatchToProps(dispatch) {
    console.log("HomePage actions:, actions");
    return {
        actions: bindActionCreators(actions, dispatch),
    }
}

/**
 * 把mapStateToProps, mapDispatchToProps绑定到MainRouter组件上
 */
export default connect(mapStateToProps, mapDispatchToProps)(QATest);<|endoftext|>


<commit_before>const isLegacy = func => typeof func === 'object';

const handleLegacyValidation = (func, value, props) => {
  if (func.validate(value, props)) return Promise.resolve();
  return Promise.reject(func.message());
};

const validator = validationFunctions => (value, props) => {
  const validations = Array.isArray(validationFunctions) ? validationFunctions : [validationFunctions];
  const handleValidation = func => (
    isLegacy(func) ? handleLegacyValidation(func, value, props) : func(value, props)
  );
  const results = validations.map(handleValidation);
  return Promise.all(results);
};

export default validator;
<commit_msg>moves handleValidations to the top of function<commit_after>const isLegacy = func => typeof func === 'object';

const handleLegacyValidation = (func, value, props) => {
  if (func.validate(value, props)) return Promise.resolve();
  return Promise.reject(func.message());
};

const validator = validationFunctions => (value, props) => {
  const handleValidation = func => (
    isLegacy(func) ? handleLegacyValidation(func, value, props) : func(value, props)
  );
  const validations = Array.isArray(validationFunctions) ? validationFunctions : [validationFunctions];
  const results = validations.map(handleValidation);
  return Promise.all(results);
};

export default validator;
<|endoftext|>


<commit_before>(function () {
    "use strict";
    IssueTrackingSystem.controller('User', ['$scope', '$location', 'authentication', '$sessionStorage',
        function ($scope, $location, authentication, $sessionStorage) {

            $scope.loginUserInSystem = function (user) {
                authentication.loginUser(user)
                    .then(function (loggedUser) {
                        $sessionStorage.isAuthenticated = true;
                        $sessionStorage.access_token = loggedUser.access_token;
                        $sessionStorage.token_type = loggedUser.token_type;
                        $sessionStorage.user = loggedUser.userName;
                        $location.path('/');
                    });
            };

            $scope.registerUserInSystem = function (user) {
                authentication.registerUser(user)
                    .then(function (registeredUser) {
                        $sessionStorage.isAuthenticated = true;
                        $sessionStorage.access_token = registeredUser.access_token;
                        $sessionStorage.token_type = registeredUser.token_type;
                        $sessionStorage.user = registeredUser.userName;
                        $location.path('/');
                    });
            };

            $scope.logoutUser = function () {
                authentication.logout();
                $location.path('/login');
            };
        }]);
}());<commit_msg>change session variable<commit_after>(function () {
    "use strict";
    IssueTrackingSystem.controller('User', ['$scope', '$location', 'authentication', '$sessionStorage',
        function ($scope, $location, authentication, $sessionStorage) {

            $scope.loginUserInSystem = function (user) {
                authentication.loginUser(user)
                    .then(function (loggedUser) {
                        $sessionStorage.isAuthenticated = true;
                        $sessionStorage.access_token = loggedUser.data.access_token;
                        $sessionStorage.token_type = loggedUser.data.token_type;
                        $sessionStorage.username = loggedUser.data.userName;
                        $location.path('/login');
                    });
            };

            $scope.registerUserInSystem = function (user) {
                authentication.registerUser(user)
                    .then(function (registeredUser) {
                        $sessionStorage.isAuthenticated = true;
                        $sessionStorage.access_token = registeredUser.data.access_token;
                        $sessionStorage.token_type = registeredUser.data.token_type;
                        $sessionStorage.username = registeredUser.data.userName;
                        $location.path('/register');
                    });
            };

            $scope.logoutUser = function () {
                authentication.logout();
                $location.path('/logout');
            };
        }]);
}());<|endoftext|>