AOEpeople/StackFormation

View on GitHub
src/StackFormation/Stack.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace StackFormation;

use StackFormation\Exception\BlueprintReferenceNotFoundException;
use StackFormation\Exception\OutputNotFoundException;
use StackFormation\Exception\ParameterNotFoundException;
use StackFormation\Exception\ResourceNotFoundException;
use StackFormation\Exception\TagNotFoundException;
use StackFormation\Helper\Cache;
use StackFormation\Helper\Div;

class Stack {

    CONST METADATA_KEY = 'StackFormation';
    CONST METADATA_KEY_BLUEPRINT = 'Blueprint';
    CONST METADATA_KEY_ENVVARS = 'EnvironmentVariables';

    CONST STATUS_NO_UPDATES_PERFORMED = 'NO_UPDATES_PERFORMED';
    CONST STATUS_STACK_GONE = 'STACK_GONE';

    /**
     * @var string
     */
    protected $data;

    /**
     * @var \Aws\CloudFormation\CloudFormationClient
     */
    protected $cfnClient;

    /**
     * @var Cache
     */
    protected $cache;

    public function __construct($data, \Aws\CloudFormation\CloudFormationClient $cfnClient)
    {
        $this->data = $data;
        $this->cfnClient = $cfnClient;
        $this->cache = new Cache();
    }

    public function getName()
    {
        return $this->data['StackName'];
    }

    public function getDescription()
    {
        return $this->data['Description'];
    }

    public function getStatus()
    {
        return $this->data['StackStatus'];
    }

    public function isInProgress()
    {
        return strpos($this->getStatus(), 'IN_PROGRESS') !== false;
    }

    public function getParameter($key)
    {
        $parameters = $this->getParameters();
        if (!isset($parameters[$key])) {
            throw new ParameterNotFoundException("Parameter '$key' not found in stack '{$this->getName()}'");
        }
        if ($parameters[$key] == '****') {
            throw new \Exception("Trying to retrieve a 'NoEcho' value (Key: '$key')");
        }
        return $parameters[$key];
    }

    /**
     * Get parameter values
     *
     * @return array
     * @throws \Exception
     */
    public function getParameters()
    {
        if (!isset($this->data['Parameters'])) {
            return [];
        }
        return Div::flatten($this->data['Parameters'], 'ParameterKey', 'ParameterValue');
    }

    /**
     * Get output
     *
     * @param $key
     * @return string
     * @throws \Exception
     */
    public function getOutput($key)
    {
        $outputs = $this->getOutputs();
        if (!isset($outputs[$key])) {
            throw new OutputNotFoundException("Output '$key' not found in stack '{$this->getName()}'");
        }
        if ($outputs[$key] == '****') {
            throw new \Exception("Trying to retrieve a 'NoEcho' value (Key: '$key')");
        }
        return $outputs[$key];
    }

    /**
     * Get outputs
     *
     * @return array
     */
    public function  getOutputs()
    {
        if (!isset($this->data['Outputs'])) {
            return [];
        }
        return Div::flatten($this->data['Outputs'], 'OutputKey', 'OutputValue');
    }

    /**
     * Get resource
     *
     * @param $key
     * @return string
     * @throws \Exception
     */
    public function getResource($key)
    {
        $resources = $this->getResources();
        if (!isset($resources[$key])) {
            throw new ResourceNotFoundException("Resource '$key' not found in stack '{$this->getName()}'");
        }
        return $resources[$key];
    }

    /**
     * Ger resources
     *
     * @return array
     * @throws \Exception
     */
    public function getResources()
    {
        return $this->cache->get(__METHOD__, function () {
            $resources = [];
            $res = $this->cfnClient->describeStackResources(['StackName' => $this->getName()])->search('StackResources[]');
            if (is_array($res)) {
                foreach ($res as $resource) {
                    $resources[$resource['LogicalResourceId']] = isset($resource['PhysicalResourceId']) ? $resource['PhysicalResourceId'] : '';
                }
            }
            return $resources;
        });
    }

    public function getTag($key)
    {
        $tags = $this->getTags();
        if (!isset($tags[$key])) {
            throw new TagNotFoundException("Tag '$key' not found in stack '{$this->getName()}'");
        }
        return $tags[$key];
    }

    public function getTags()
    {
        $tags = [];
        $res = $this->data['Tags'];
        if (is_array($res)) {
            foreach ($res as $tag) {
                $tags[$tag['Key']] = $tag['Value'];
            }
        }
        return $tags;
    }

    /**
     * @return array
     */
    public function getEvents()
    {
        $res = $this->cfnClient->describeStackEvents(['StackName' => $this->getName()]);
        $events = [];
        foreach ($res->search('StackEvents[]') as $event) {
            $events[$event['EventId']] = [
                'Timestamp' => (string)$event['Timestamp'],
                'Status' => $event['ResourceStatus'],
                'ResourceType' => $event['ResourceType'],
                'LogicalResourceId' => $event['LogicalResourceId'],
                'ResourceStatus' => $event['ResourceStatus'],
                'ResourceStatusReason' => isset($event['ResourceStatusReason']) ? $event['ResourceStatusReason'] : '',
            ];
        }
        return array_reverse($events, true);
    }

    public function getBlueprintName()
    {
        $blueprintReference = $this->getBlueprintReference();
        if (!isset($blueprintReference[self::METADATA_KEY_BLUEPRINT])) {
            throw new \Exception('No bleprint name found in blueprint reference for stack ' . $this->getName());
        }
        return $blueprintReference[self::METADATA_KEY_BLUEPRINT];
    }

    public function getUsedEnvVars()
    {
        $blueprintReference = $this->getBlueprintReference();
        if (!isset($blueprintReference[self::METADATA_KEY_ENVVARS])) {
            throw new \Exception('No env vars found in blueprint reference for stack ' . $this->getName());
        }
        return $blueprintReference[self::METADATA_KEY_ENVVARS];
    }

    /**
     * @return array
     * @throws \Exception
     */
    protected function getBlueprintReference()
    {
        return $this->cache->get(__METHOD__, function () {
            $template = $this->getTemplate();
            $decodedTemplate = json_decode($template, true);
            if (!isset($decodedTemplate['Metadata']) || !isset($decodedTemplate['Metadata'][self::METADATA_KEY])) {
                throw new BlueprintReferenceNotFoundException('No blueprint reference found');
            }
            return $decodedTemplate['Metadata'][self::METADATA_KEY];
        });
    }

    public function getTemplate()
    {
        return $this->cache->get(__METHOD__, function () {
            $res = $this->cfnClient->getTemplate(['StackName' => $this->getName()]);
            return $res->get('TemplateBody');
        });
    }

    ///**
    // * @param \Symfony\Component\Console\Output\OutputInterface $output
    // * @param StackFactory $stackFactory
    // * @param bool $deleteOnTerminate
    // * @return int
    // * @deprecated
    // */
    //public function observe(
    //    \Symfony\Component\Console\Output\OutputInterface $output,
    //    \StackFormation\StackFactory $stackFactory,
    //    $deleteOnTerminate=false)
    //{
    //    $observer = new Observer($this, $stackFactory, $output);
    //    if ($deleteOnTerminate) {
    //        $observer->deleteOnSignal();
    //    }
    //    return $observer->observeStackActivity();
    //}

    public function cancelUpdate()
    {
        $this->cfnClient->cancelUpdateStack(['StackName' => $this->getName()]);
        return $this;
    }

    public function continueUpdateRollback()
    {
        $this->cfnClient->continueUpdateRollback(['StackName' => $this->getName()]);
        return $this;
    }

    public function delete()
    {
        $this->cfnClient->deleteStack(['StackName' => $this->getName()]);
        return $this;
    }

}