├── .gitattributes ├── .github ├── actions │ ├── common_setup │ │ └── action.yaml │ └── push_docker_images │ │ └── action.yaml └── workflows │ ├── build.yaml │ └── check_documentation.yaml ├── .gitignore ├── .hlint.yaml ├── .nova └── Configuration.json ├── .vscode └── settings.json ├── ChangeLog.md ├── Development_guide.md ├── LICENSE ├── Makefile ├── README.md ├── Release_checklist.md ├── cabal.project ├── charts └── octopod │ ├── .gitignore │ ├── .helmignore │ ├── Chart.lock │ ├── Chart.yaml │ ├── README.md │ ├── index.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── cli-secret.yaml │ ├── deployment.yaml │ ├── ingress-app.yaml │ ├── ingress-powerapp.yaml │ ├── ingress-ui.yaml │ ├── ingress-ws.yaml │ ├── nginx-configmap.yaml │ ├── octopod-configmap.yaml │ ├── rbac.yaml │ ├── service.yaml │ └── serviceaccount.yaml │ └── values.yaml ├── default.nix ├── dev └── default.nix ├── docs ├── Makefile ├── README.md ├── diagrams │ ├── images │ │ ├── app-architecture.png │ │ ├── sample_architecture.png │ │ ├── sample_deployment.png │ │ ├── technical-architecture-archive.png │ │ ├── technical-architecture-cleanup.png │ │ ├── technical-architecture-create.png │ │ ├── technical-architecture-deployment-states-fsm.png │ │ ├── technical-architecture-restore.png │ │ └── technical-architecture-update.png │ └── src │ │ ├── sample_architecture.puml │ │ ├── sample_deployment.puml │ │ ├── technical-architecture-archive.mmd │ │ ├── technical-architecture-cleanup.mmd │ │ ├── technical-architecture-create.mmd │ │ ├── technical-architecture-deployment-states-fsm.mmd │ │ ├── technical-architecture-restore.mmd │ │ └── technical-architecture-update.mmd ├── en │ ├── Control_scripts.md │ ├── Helm-based_deployment_guide.md │ ├── Integration.md │ ├── Octo_user_guide.md │ ├── Octopod_deployment_guide.md │ ├── Overview.md │ ├── PM_case_study.md │ ├── README.md │ ├── Security_model.md │ ├── Tech_case_study.md │ └── Technical_architecture.md ├── images │ ├── break1.png │ ├── break2.png │ ├── dev1.png │ ├── dev2.png │ ├── env1.png │ ├── env2.png │ ├── octopod_app_keys_list.png │ ├── octopod_archive.png │ ├── octopod_blank.png │ ├── octopod_deployment_blank.png │ ├── octopod_deployment_filled.png │ ├── octopod_filled.png │ ├── octopod_in_octopod_deployment.png │ └── wordpress_blank.png └── style.css ├── fourmolu.yaml ├── helm-control-scripts ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md └── src │ ├── app_keys.rs │ ├── app_overrides.rs │ ├── archive.rs │ ├── archive_check.rs │ ├── check.rs │ ├── cleanup.rs │ ├── config_check.rs │ ├── create_update.rs │ ├── deployment_keys.rs │ ├── deployment_overrides.rs │ ├── info.rs │ ├── init.rs │ ├── lib.rs │ └── unarchive.rs ├── hie.yaml ├── img ├── demo.gif ├── logo.svg ├── release_workflow.png └── typeable_logo.svg ├── migrations ├── deploy │ ├── .gitkeep │ ├── add_archived_status.sql │ ├── add_deployment_overrides_and_deployment_log_overrides.sql │ ├── add_detailed_failures.sql │ ├── add_duration_and_stdout_and_stderr_to_deployment_logs.sql │ ├── archived_flag.sql │ ├── create_deployment_metadata.sql │ ├── deployment_logs.sql │ ├── deployments.sql │ ├── migrate_2.0.sql │ ├── move_tag_to_overrides.sql │ ├── remove_archived_column.sql │ ├── rename-delete-to-archive.sql │ ├── rename_delete_to_archive_2.sql │ ├── rename_elements_of_scope_enum.sql │ └── status_and_status_updated_at.sql ├── revert │ ├── .gitkeep │ ├── add_archived_status.sql │ ├── add_deployment_overrides_and_deployment_log_overrides.sql │ ├── add_detailed_failures.sql │ ├── add_duration_and_stdout_and_stderr_to_deployment_logs.sql │ ├── archived_flag.sql │ ├── create_deployment_metadata.sql │ ├── deployment_logs.sql │ ├── deployments.sql │ ├── migrate_2.0.sql │ ├── move_tag_to_overrides.sql │ ├── remove_archived_column.sql │ ├── rename-delete-to-archive.sql │ ├── rename_delete_to_archive_2.sql │ ├── rename_elements_of_scope_enum.sql │ └── status_and_status_updated_at.sql ├── sqitch.conf ├── sqitch.plan └── verify │ ├── .gitkeep │ ├── add_archived_status.sql │ ├── add_deployment_overrides_and_deployment_log_overrides.sql │ ├── add_detailed_failures.sql │ ├── add_duration_and_stdout_and_stderr_to_deployment_logs.sql │ ├── archived_flag.sql │ ├── create_deployment_metadata.sql │ ├── deployment_logs.sql │ ├── deployments.sql │ ├── migrate_2.0.sql │ ├── move_tag_to_overrides.sql │ ├── remove_archived_column.sql │ ├── rename-delete-to-archive.sql │ ├── rename_delete_to_archive_2.sql │ ├── rename_elements_of_scope_enum.sql │ └── status_and_status_updated_at.sql ├── nix ├── ci.nix ├── default.nix ├── sources.json └── sources.nix ├── octo-cli ├── LICENSE ├── Setup.hs ├── octo-cli.cabal └── src │ ├── Main.hs │ ├── Octopod │ ├── CLI.hs │ ├── CLI │ │ └── Args.hs │ └── PowerAPI │ │ └── Auth │ │ └── Client.hs │ └── Text │ └── Layout │ └── Table │ └── Extras.hs ├── octopod-api ├── LICENSE ├── Setup.hs ├── octopod-api.cabal └── src │ └── Octopod │ ├── API.hs │ ├── API │ └── WebSocket.hs │ └── PowerAPI.hs ├── octopod-backend ├── LICENSE ├── Setup.hs ├── app │ └── Main.hs ├── octopod-backend.cabal └── src │ ├── Control │ ├── CacheMap.hs │ └── Octopod │ │ └── DeploymentLock.hs │ ├── Octopod │ ├── PowerAPI │ │ └── Auth │ │ │ └── Server.hs │ ├── Schema.hs │ ├── Server.hs │ └── Server │ │ ├── Args.hs │ │ ├── ControlScriptUtils.hs │ │ ├── Logging.hs │ │ └── Posix.hs │ ├── Orphans.hs │ └── Types.hs ├── octopod-common ├── CHANGELOG.md ├── LICENSE ├── Setup.hs ├── octopod-common.cabal └── src │ ├── Common │ ├── Orphans.hs │ ├── Types.hs │ ├── Utils.hs │ └── Validation.hs │ ├── Control │ └── Searchable.hs │ └── Data │ └── ConfigTree.hs ├── octopod-css ├── .stylelintrc.json ├── default.nix ├── development │ ├── images │ │ ├── dialog-delete.png │ │ ├── dialog-delete@2x.png │ │ ├── null-data.png │ │ ├── null-data@2x.png │ │ ├── null-search.png │ │ └── null-search@2x.png │ ├── layouts │ │ ├── index.html │ │ ├── staging.html │ │ └── standalone-loading.html │ ├── markups │ │ ├── _action.html │ │ ├── _bar.html │ │ ├── _break.html │ │ ├── _button.html │ │ ├── _collapse.html │ │ ├── _dash.html │ │ ├── _drop.html │ │ ├── _expander.html │ │ ├── _external.html │ │ ├── _input.html │ │ ├── _keys-and-values.html │ │ ├── _listing.html │ │ ├── _loading.html │ │ ├── _notification.html │ │ ├── _null.html │ │ ├── _spot.html │ │ ├── _status.html │ │ └── _title.html │ ├── scripts │ │ ├── _classic-popup.js │ │ ├── _collapse.js │ │ ├── _data.js │ │ ├── _drop.js │ │ ├── _expander.js │ │ ├── _popup.js │ │ └── _sort.js │ ├── styles │ │ ├── _action.css │ │ ├── _bar.css │ │ ├── _body.css │ │ ├── _break.css │ │ ├── _browserupgrade.css │ │ ├── _button.css │ │ ├── _classic-popup.css │ │ ├── _collapse.css │ │ ├── _container.css │ │ ├── _dash.css │ │ ├── _data.css │ │ ├── _deployment.css │ │ ├── _dialog.css │ │ ├── _drop.css │ │ ├── _editable-row.css │ │ ├── _expander.css │ │ ├── _external.css │ │ ├── _has-changes.css │ │ ├── _header.css │ │ ├── _html.css │ │ ├── _input.css │ │ ├── _key-custom-edited.css │ │ ├── _key-default-edited.css │ │ ├── _key-default-pristine.css │ │ ├── _key-deleted.css │ │ ├── _listing.css │ │ ├── _loading.css │ │ ├── _log.css │ │ ├── _nav.css │ │ ├── _no-data.css │ │ ├── _no-deployment.css │ │ ├── _no-log.css │ │ ├── _no-page.css │ │ ├── _no-table.css │ │ ├── _notification.css │ │ ├── _null.css │ │ ├── _overrides.css │ │ ├── _padded.css │ │ ├── _page.css │ │ ├── _popup.css │ │ ├── _root.css │ │ ├── _row.css │ │ ├── _sort.css │ │ ├── _spot.css │ │ ├── _status.css │ │ ├── _table.css │ │ ├── _tag.css │ │ ├── _title.css │ │ ├── _universal.css │ │ ├── _value-deleted.css │ │ ├── _value-edited.css │ │ ├── _value-pristine.css │ │ ├── _value-unknown.css │ │ ├── _visuallyhidden.css │ │ └── style.css │ ├── vectors │ │ ├── button-add.svg │ │ ├── button-loader.svg │ │ ├── button-save.svg │ │ ├── classic-popup-close.svg │ │ ├── collapse-project.svg │ │ ├── common-use-archive.svg │ │ ├── common-use-edit-white.svg │ │ ├── common-use-edit.svg │ │ ├── common-use-logs.svg │ │ ├── common-use-restore.svg │ │ ├── dash-add-active.svg │ │ ├── dash-add-hover.svg │ │ ├── dash-add.svg │ │ ├── dash-back-active.svg │ │ ├── dash-back-hover.svg │ │ ├── dash-back.svg │ │ ├── dash-next-active.svg │ │ ├── dash-next-hover.svg │ │ ├── dash-next.svg │ │ ├── drop-actions-active.svg │ │ ├── drop-actions-hover.svg │ │ ├── drop-actions.svg │ │ ├── expander-active.svg │ │ ├── expander-hover.svg │ │ ├── expander-larger-active.svg │ │ ├── expander-larger-hover.svg │ │ ├── expander-larger.svg │ │ ├── expander.svg │ │ ├── external-hover-active.svg │ │ ├── external.svg │ │ ├── input-search.svg │ │ ├── loading-alternate.svg │ │ ├── loading-enlarged-alternate.svg │ │ ├── loading-enlarged.svg │ │ ├── loading.svg │ │ ├── notification-close-danger-active.svg │ │ ├── notification-close-danger-hover.svg │ │ ├── notification-close-danger.svg │ │ ├── notification-close-success-active.svg │ │ ├── notification-close-success-hover.svg │ │ ├── notification-close-success.svg │ │ ├── popup-close-active.svg │ │ ├── popup-close-hover.svg │ │ ├── popup-close.svg │ │ ├── spot-loader.svg │ │ ├── spot-undo.svg │ │ ├── spot.svg │ │ ├── status-archived.svg │ │ ├── status-failure.svg │ │ ├── status-pending.svg │ │ └── status-success.svg │ └── vendors │ │ ├── jquery │ │ └── jquery-3.2.1.min.js │ │ ├── normalize │ │ └── normalize.css │ │ └── outline │ │ ├── index.html │ │ └── outline.js ├── favicons │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── apple-icon-precomposed.png │ ├── apple-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── manifest.json │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ └── ms-icon-70x70.png ├── gulpfile.js ├── package.json └── yarn.lock ├── octopod-frontend ├── CHANGELOG.md ├── LICENSE ├── Setup.hs ├── index.html ├── octopod-frontend.cabal └── src │ ├── Data │ ├── Text │ │ └── Search.hs │ ├── UniqMap.hs │ └── WorkingOverrides.hs │ ├── Frontend │ ├── API.hs │ ├── Classes.hs │ ├── GHCJS.hs │ ├── Route.hs │ ├── UIKit.hs │ ├── UIKit │ │ └── Button │ │ │ ├── Action.hs │ │ │ ├── Common.hs │ │ │ ├── Dash.hs │ │ │ ├── Expander.hs │ │ │ ├── Large.hs │ │ │ ├── Sort.hs │ │ │ ├── Static.hs │ │ │ └── Tree.hs │ └── Utils.hs │ ├── Main.hs │ ├── Page │ ├── ClassicPopup.hs │ ├── Deployment.hs │ ├── Deployments.hs │ ├── Elements │ │ └── Links.hs │ └── Popup │ │ ├── EditDeployment.hs │ │ └── NewDeployment.hs │ ├── Reflex │ ├── Dom │ │ ├── AsyncEvent.hs │ │ └── Renderable.hs │ └── MultiEventWriter │ │ └── Class.hs │ └── Servant │ └── Reflex │ └── Extra.hs ├── octopod_local_install.sh ├── release.sh ├── shell.nix └── stack.yaml /.gitattributes: -------------------------------------------------------------------------------- 1 | examples/**/*.rs -linguist-documentation 2 | *.tpl -linguist-detectable 3 | *.html -linguist-detectable 4 | *.css -linguist-detectable 5 | *.js -linguist-detectable 6 | -------------------------------------------------------------------------------- /.github/actions/common_setup/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Common setup" 2 | description: "Common setup steps" 3 | inputs: 4 | authToken: 5 | description: "Cachix auth token" 6 | required: true 7 | runs: 8 | using: "composite" 9 | steps: 10 | - name: Install skopeo 11 | shell: bash 12 | run: | 13 | sudo apt-get -y update 14 | sudo apt-get -y install skopeo 15 | 16 | - name: Increase swap 17 | shell: bash 18 | run: | 19 | free -h 20 | 21 | sudo swapoff /mnt/swapfile 22 | sudo fallocate -l 12G /mnt/swapfile 23 | sudo mkswap /mnt/swapfile 24 | sudo swapon /mnt/swapfile 25 | 26 | free -h 27 | 28 | - name: Install Nix 29 | uses: cachix/install-nix-action@v16 30 | with: 31 | extra_nix_config: | 32 | trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= 33 | substituters = https://hydra.iohk.io https://cache.nixos.org/ 34 | system-features = kvm 35 | 36 | - name: Login to Cachix 37 | uses: cachix/cachix-action@v10 38 | with: 39 | name: typeable 40 | authToken: ${{ inputs.authToken }} 41 | -------------------------------------------------------------------------------- /.github/actions/push_docker_images/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Push Docker Images" 2 | description: "Push Docker Images" 3 | inputs: 4 | tag: 5 | description: "The docker image tag to push." 6 | required: true 7 | docker_username: 8 | description: "The Docker Hub username" 9 | required: true 10 | docker_password: 11 | description: "The Docker Hub password" 12 | required: true 13 | runs: 14 | using: "composite" 15 | steps: 16 | - name: Login to DockerHub 17 | uses: docker/login-action@v1.10.0 18 | with: 19 | username: ${{ inputs.docker_username }} 20 | password: ${{ inputs.docker_password }} 21 | 22 | - name: Push Docker Images to DockerHub 23 | shell: bash 24 | run: | 25 | skopeo copy docker-archive:$(nix-build ./nix -j auto -A octopod-server-container) docker://typeable/octopod:${{ inputs.tag }} 26 | skopeo copy docker-archive:$(nix-build ./nix -j auto -A octo-cli-container) docker://typeable/octo:${{ inputs.tag }} 27 | -------------------------------------------------------------------------------- /.github/workflows/check_documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - develop 7 | pull_request: 8 | schedule: 9 | - cron: "0 0 * * *" 10 | 11 | jobs: 12 | build: 13 | name: Check Markdown Documentation 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v1 17 | 18 | - name: Set up linter 19 | run: | 20 | yarn add remark-cli remark-lint-mdash-style https://github.com/typeable/remark-validate-links#anchors remark-preset-lint-recommended remark-lint-no-dead-urls 21 | 22 | - name: Run linter 23 | run: | 24 | yarn run remark -f -u validate-links -u remark-lint-mdash-style -u remark-lint-final-newline -u remark-lint-list-item-bullet-indent -u remark-lint-no-blockquote-without-marker -u remark-lint-ordered-list-marker-style -u remark-lint-no-literal-urls -u remark-lint-hard-break-spaces -u remark-lint-no-duplicate-definitions -u remark-lint-no-heading-content-indent -u remark-lint-no-inline-padding -u remark-lint-no-shortcut-reference-image -u remark-lint-no-shortcut-reference-link -u remark-lint-no-undefined-references -u remark-lint-no-unused-definitions -u remark-lint-no-dead-urls docs README.md charts 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/ 2 | package.yaml 3 | result 4 | dms-docker 5 | *~ 6 | dist-newstyle 7 | octopod-config.json 8 | frontend-result 9 | octopod-css/node_modules 10 | dev/certs/* 11 | tmp/* 12 | octopod-css/production 13 | .idea/ -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | # HLint configuration file 2 | # https://github.com/ndmitchell/hlint 3 | ########################## 4 | 5 | # This file contains a template configuration file, which is typically 6 | # placed as .hlint.yaml in the root of your project 7 | 8 | # Specify additional command line arguments 9 | # 10 | # - arguments: [--color, --cpp-simple, -XQuasiQuotes] 11 | 12 | # Control which extensions/flags/modules/functions can be used 13 | # 14 | # - extensions: 15 | # - default: false # all extension are banned by default 16 | # - name: [PatternGuards, ViewPatterns] # only these listed extensions can be used 17 | # - {name: CPP, within: CrossPlatform} # CPP can only be used in a given module 18 | # 19 | # - flags: 20 | # - {name: -w, within: []} # -w is allowed nowhere 21 | # 22 | # - modules: 23 | # - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set' 24 | # - {name: Control.Arrow, within: []} # Certain modules are banned entirely 25 | # 26 | # - functions: 27 | # - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules 28 | 29 | # Add custom hints for this project 30 | # 31 | # Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar" 32 | # - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x} 33 | 34 | # Turn on hints that are off by default 35 | # 36 | # Ban "module X(module X) where", to require a real export list 37 | # - warn: {name: Use explicit module export list} 38 | # 39 | # Replace a $ b $ c with a . b $ c 40 | # - group: {name: dollar, enabled: true} 41 | # 42 | # Generalise map to fmap, ++ to <> 43 | # - group: {name: generalise, enabled: true} 44 | 45 | # Ignore some builtin hints 46 | # - ignore: {name: Use let} 47 | # - ignore: {name: Use const, within: SpecialModule} # Only within certain modules 48 | 49 | - ignore: { name: Replace case with maybe } 50 | - ignore: { name: Use if } 51 | # Define some custom infix operators 52 | # - fixity: infixr 3 ~^#^~ 53 | 54 | # To generate a suitable file for HLint do: 55 | # $ hlint --default > .hlint.yaml 56 | -------------------------------------------------------------------------------- /.nova/Configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "env-generator" : "nix-shell --run env", 3 | "haskell.checkProject" : false, 4 | "haskell.formattingProvider" : "fourmolu" 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", 3 | "haskell.formattingProvider": "fourmolu", 4 | "haskell.plugin.hlint.diagnosticsOn": false, 5 | "haskell.hlint.logLevel": "none", 6 | "haskell.hlint.run": "never", 7 | "haskell.serverExecutablePath": "haskell-language-server", 8 | "editor.formatOnSave": true 9 | } 10 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for octopod 2 | 3 | ## Version 1.0 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Typeable LLC (c) 2020 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build-backend build-octo-cli build-frontend backend-docs repl shell ghcid ghcid-cli ghcid-frontend push-octopod run-backend-dev run-frontend-dev 2 | 3 | build-backend: 4 | nix-build . -A octopod-backend.components.exes.octopod-exe -j auto 5 | 6 | build-octo-cli: 7 | nix-build . -A octo-cli.components.exes.octo -j auto 8 | 9 | build-frontend: 10 | nix-build . -A octopod-frontend-pretty -o frontend-result -j auto 11 | 12 | backend-docs: 13 | nix-build . -A octopod-backend.components.library.doc -j auto 14 | 15 | repl: 16 | nix-shell --run "cabal repl lib:octopod-backend" -j auto 17 | 18 | shell: 19 | nix-shell -j auto 20 | 21 | ghcid-backend: 22 | nix-shell --run 'ghcid -c "cabal new-repl octopod-backend"' -j auto 23 | 24 | ghcid-cli: 25 | nix-shell --run 'ghcid -c "cabal new-repl octo-cli"' -j auto 26 | 27 | ghcid-frontend: 28 | nix-shell --run 'ghcid -c "cabal new-repl octopod-frontend -fdevelopment --ghc-options=-Wwarn" --warnings --test 'Main.main'' -j auto 29 | 30 | push-octopod: 31 | ./build.sh build-and-push latest 32 | 33 | run-backend-dev: 34 | `nix-build dev -A backend -j auto` 35 | 36 | run-frontend-dev: 37 | `nix-build dev -A frontend -j auto` 38 | 39 | run-frontend-prod: 40 | `nix-build dev -A frontend --arg prod true -j auto` 41 | 42 | run-caddy-for-ghcid: 43 | `nix-build dev -A caddyForGhcid -j auto` 44 | -------------------------------------------------------------------------------- /Release_checklist.md: -------------------------------------------------------------------------------- 1 | # Release checklist 2 | 3 | 1. Update the referenced tags in the documentation 4 | 2. If there were changes to control scripts: 5 | 1. _**\**_ 6 | 3. [Dispatch the "Build and release" workflow](https://github.com/typeable/octopod/actions/workflows/build.yaml), specifying the version you are releasing: 7 | 8 | ![](./img/release_workflow.png) 9 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: 2 | octo-cli/ 3 | octopod-api/ 4 | octopod-backend/ 5 | octopod-common/ 6 | octopod-frontend/ 7 | 8 | allow-newer: 9 | servant-reflex:servant, 10 | servant-reflex:servant-auth, 11 | servant-reflex:reflex 12 | 13 | source-repository-package 14 | type: git 15 | location: https://github.com/ilyakooo0/obelisk 16 | tag: e3c45d230bfc60351c82e8674bc190349fcfae3a 17 | subdir: lib/route 18 | --sha256: 1syczkabq8yz7fw5jpmax15770bbb12wgdmm3lbgvh4m9qvfmblg 19 | 20 | source-repository-package 21 | type: git 22 | location: https://github.com/obsidiansystems/obelisk 23 | tag: 7ad33cbe3e84b209e83c505ce25486445bbd602e 24 | subdir: lib/tabulation 25 | --sha256: 0dlk8y6rxc87crw7764zq2py7nqn38lw496ca1y893m9gdq8qdkz 26 | 27 | source-repository-package 28 | type: git 29 | location: https://github.com/obsidiansystems/obelisk 30 | tag: 7ad33cbe3e84b209e83c505ce25486445bbd602e 31 | subdir: lib/executable-config/lookup 32 | --sha256: 0dlk8y6rxc87crw7764zq2py7nqn38lw496ca1y893m9gdq8qdkz 33 | 34 | source-repository-package 35 | type: git 36 | location: https://github.com/typeable/ordered-containers.git 37 | tag: 3eb05fb2f44fe482e9092aff447e9c68fbd6a7f6 38 | --sha256: 0q3cp3nar41bqh2ldl1ykzaycf0bjxh0n58za5g19x4g7ab8h9gm 39 | 40 | package reflex-dom 41 | flags: +use-warp 42 | -------------------------------------------------------------------------------- /charts/octopod/.gitignore: -------------------------------------------------------------------------------- 1 | charts -------------------------------------------------------------------------------- /charts/octopod/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/octopod/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: postgresql 3 | repository: https://charts.bitnami.com/bitnami 4 | version: 10.5.3 5 | digest: sha256:d8ba564b767cbf73a4ca87cb3b97e0a75bc813ba0a58a1b0bd6c7154a608e783 6 | generated: "2021-07-16T15:31:01.633560857+03:00" 7 | -------------------------------------------------------------------------------- /charts/octopod/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: octopod 3 | description: An opensource self-hosted solution for managing multiple deployments in a Kubernetes cluster. 4 | type: application 5 | version: 0.6.11 6 | appVersion: 1.5.2 7 | keywords: 8 | - kubernetes 9 | - octopod 10 | home: https://github.com/typeable/octopod 11 | sources: 12 | - https://github.com/typeable/octopod 13 | maintainers: 14 | - name: Alex Sizov 15 | email: a.sizov@typeable.io 16 | dependencies: 17 | - name: postgresql 18 | version: 10.5.3 19 | repository: https://charts.bitnami.com/bitnami 20 | condition: postgresql.enabled 21 | -------------------------------------------------------------------------------- /charts/octopod/index.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | entries: 3 | postgresql: 4 | - annotations: 5 | category: Database 6 | apiVersion: v2 7 | appVersion: 11.12.0 8 | created: "2022-08-23T14:43:53.959078+03:00" 9 | dependencies: 10 | - name: common 11 | repository: https://charts.bitnami.com/bitnami 12 | version: 1.x.x 13 | description: Chart for PostgreSQL, an object-relational database management system 14 | (ORDBMS) with an emphasis on extensibility and on standards-compliance. 15 | digest: afece60469d73b9cc4b578fff9b7627dd6ae609be50a04dabac5ad0be5105f33 16 | home: https://github.com/bitnami/charts/tree/master/bitnami/postgresql 17 | icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png 18 | keywords: 19 | - postgresql 20 | - postgres 21 | - database 22 | - sql 23 | - replication 24 | - cluster 25 | maintainers: 26 | - email: containers@bitnami.com 27 | name: Bitnami 28 | - email: cedric@desaintmartin.fr 29 | name: desaintmartin 30 | name: postgresql 31 | sources: 32 | - https://github.com/bitnami/bitnami-docker-postgresql 33 | - https://www.postgresql.org/ 34 | urls: 35 | - charts/postgresql-10.5.3.tgz 36 | version: 10.5.3 37 | generated: "2022-08-23T14:43:53.955624+03:00" 38 | -------------------------------------------------------------------------------- /charts/octopod/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 🐙 Thank you for installing Octopod! 2 | 3 | 🖥️ You can access the Web UI here: {{ include "httpScheme" . }}://{{ include "uiIngressHost" . }} 4 | 5 | 🎛 To access Octopod via octo CLI you can use this URL: {{ include "httpScheme" . }}://{{ include "powerAppIngressHost" . }} 6 | 7 | 🔑 To get your CLI secret you need to execute this command: 8 | kubectl -n {{ .Release.Namespace }} get secret {{ include "octopodCliAuthSecretName" . }} -o jsonpath='{.data.cli-secret}' | base64 -d 9 | 10 | {{ if and (not .Values.octopod.cliAuthSecret) (not .Values.vault.enabled) .Release.IsUpgrade }} 11 | {{ fail "To upgrade you need to set the octopod.cliAuthSecret variable with your current secret" }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /charts/octopod/templates/cli-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.vault.enabled }} 2 | apiVersion: v1 3 | kind: Secret 4 | type: Opaque 5 | metadata: 6 | name: {{ include "octopodCliAuthSecretName" . }} 7 | labels: 8 | {{- include "octopod.labels" . | nindent 4 }} 9 | data: 10 | cli-secret: {{ include "octopodCliAuthSecret" . | b64enc }} 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /charts/octopod/templates/ingress-powerapp.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "octopod.fullname" . -}} 3 | {{- $kubeVersion := .Capabilities.KubeVersion.Version }} 4 | {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} 5 | apiVersion: networking.k8s.io/v1 6 | {{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} 7 | apiVersion: networking.k8s.io/v1beta1 8 | {{ else }} 9 | apiVersion: extensions/v1beta1 10 | {{ end }} 11 | kind: Ingress 12 | metadata: 13 | name: {{ $fullName }}-powerapp 14 | labels: 15 | {{- include "octopod.labels" . | nindent 4 }} 16 | annotations: 17 | {{- if .Values.ingress.tls.enabled }} 18 | kubernetes.io/tls-acme: "true" 19 | cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.clusterIssuer | quote }} 20 | {{- end }} 21 | kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass | quote }} 22 | nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" 23 | nginx.ingress.kubernetes.io/proxy-send-timeout: "600" 24 | nginx.ingress.kubernetes.io/proxy-read-timeout: "600" 25 | nginx.ingress.kubernetes.io/proxy-next-upstream: "http_502 error timeout" 26 | {{- with .Values.ingress.powerApp.annotations }} 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | spec: 30 | {{- if .Values.ingress.tls.enabled }} 31 | tls: 32 | - hosts: 33 | - {{ include "powerAppIngressHost" . }} 34 | secretName: {{ $fullName }}-powerapp-tls 35 | {{- end }} 36 | rules: 37 | - host: {{ include "powerAppIngressHost" . }} 38 | http: 39 | paths: 40 | - path: / 41 | pathType: ImplementationSpecific 42 | backend: 43 | service: 44 | name: {{ $fullName }} 45 | port: 46 | number: {{ .Values.service.ports.powerApp }} 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /charts/octopod/templates/ingress-ui.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "octopod.fullname" . -}} 3 | {{- $kubeVersion := .Capabilities.KubeVersion.Version }} 4 | {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} 5 | apiVersion: networking.k8s.io/v1 6 | {{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} 7 | apiVersion: networking.k8s.io/v1beta1 8 | {{ else }} 9 | apiVersion: extensions/v1beta1 10 | {{ end }} 11 | kind: Ingress 12 | metadata: 13 | name: {{ $fullName }}-ui 14 | labels: 15 | {{- include "octopod.labels" . | nindent 4 }} 16 | annotations: 17 | {{- if .Values.ingress.tls.enabled }} 18 | kubernetes.io/tls-acme: "true" 19 | cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.clusterIssuer | quote }} 20 | {{- end }} 21 | kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass | quote }} 22 | nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" 23 | nginx.ingress.kubernetes.io/proxy-send-timeout: "600" 24 | nginx.ingress.kubernetes.io/proxy-read-timeout: "600" 25 | nginx.ingress.kubernetes.io/proxy-next-upstream: "http_502 error timeout" 26 | {{- with .Values.ingress.ui.annotations }} 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | spec: 30 | {{- if .Values.ingress.tls.enabled }} 31 | tls: 32 | - hosts: 33 | - {{ include "uiIngressHost" . }} 34 | secretName: {{ $fullName }}-ui-tls 35 | {{- end }} 36 | rules: 37 | - host: {{ include "uiIngressHost" . }} 38 | http: 39 | paths: 40 | - path: / 41 | pathType: ImplementationSpecific 42 | backend: 43 | service: 44 | name: {{ $fullName }} 45 | port: 46 | number: {{ .Values.service.ports.ui }} 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /charts/octopod/templates/ingress-ws.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "octopod.fullname" . -}} 3 | {{- $kubeVersion := .Capabilities.KubeVersion.Version }} 4 | {{ if semverCompare ">= 1.19.0-0" $kubeVersion }} 5 | apiVersion: networking.k8s.io/v1 6 | {{ else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }} 7 | apiVersion: networking.k8s.io/v1beta1 8 | {{ else }} 9 | apiVersion: extensions/v1beta1 10 | {{ end }} 11 | kind: Ingress 12 | metadata: 13 | name: {{ $fullName }}-ws 14 | labels: 15 | {{- include "octopod.labels" . | nindent 4 }} 16 | annotations: 17 | {{- if .Values.ingress.tls.enabled }} 18 | kubernetes.io/tls-acme: "true" 19 | cert-manager.io/cluster-issuer: {{ .Values.ingress.tls.clusterIssuer | quote }} 20 | {{- end }} 21 | kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass | quote }} 22 | nginx.ingress.kubernetes.io/proxy-connect-timeout: "600" 23 | nginx.ingress.kubernetes.io/proxy-send-timeout: "600" 24 | nginx.ingress.kubernetes.io/proxy-read-timeout: "600" 25 | nginx.ingress.kubernetes.io/proxy-next-upstream: "http_502 error timeout" 26 | {{- with .Values.ingress.ws.annotations }} 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | spec: 30 | {{- if .Values.ingress.tls.enabled }} 31 | tls: 32 | - hosts: 33 | - {{ include "wsIngressHost" . }} 34 | secretName: {{ $fullName }}-ws-tls 35 | {{- end }} 36 | rules: 37 | - host: {{ include "wsIngressHost" . }} 38 | http: 39 | paths: 40 | - path: / 41 | pathType: ImplementationSpecific 42 | backend: 43 | service: 44 | name: {{ $fullName }} 45 | port: 46 | number: {{ .Values.service.ports.ws }} 47 | {{- end }} 48 | -------------------------------------------------------------------------------- /charts/octopod/templates/nginx-configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- $octopodAppAuthPassword := include "octopodUiAuthSecret" . -}} 2 | {{- $etag := sha256sum (now | date "Mon Jan 2 15:04:05 MSK 2021") -}} 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "octopod.fullname" . }}-nginx-config 7 | labels: 8 | {{- include "octopod.labels" . | nindent 4 }} 9 | data: 10 | app.conf: | 11 | server { 12 | listen 80 default_server; 13 | server_name _; 14 | root /www; 15 | index index.html; 16 | error_page 404 =200 /index.html; 17 | charset utf-8; 18 | location ~* \.(html|css|js|xml)$ { 19 | gzip_static on; 20 | add_header 'ETag' '{{ $etag }}'; 21 | } 22 | } 23 | config.json: | 24 | { 25 | "app_url": "{{ printf "%s://%s" (include "httpScheme" .) (include "appIngressHost" . )}}", 26 | "ws_url": "{{ printf "%s://%s" (include "wsScheme" .) (include "wsIngressHost" .) }}", 27 | "app_auth": "Basic {{ printf "octopod:%s" $octopodAppAuthPassword | b64enc }}", 28 | {{- if .Values.kubernetesDashboard.enabled }} 29 | "kubernetes_dashboard_url_template": "{{ .Values.kubernetesDashboard.url }}/#/search?namespace={{ .Values.octopod.deploymentNamespace }}&q=" 30 | {{- else }} 31 | "kubernetes_dashboard_url_template": null 32 | {{- end }} 33 | } 34 | --- 35 | apiVersion: v1 36 | kind: Secret 37 | type: Opaque 38 | metadata: 39 | name: {{ include "octopodAppAuthSecretName" . }} 40 | labels: 41 | {{- include "octopod.labels" . | nindent 4 }} 42 | data: 43 | auth: {{ htpasswd "octopod" $octopodAppAuthPassword | b64enc }} 44 | -------------------------------------------------------------------------------- /charts/octopod/templates/octopod-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "octopod.fullname" . }} 5 | labels: 6 | {{- include "octopod.labels" . | nindent 4 }} 7 | data: 8 | PROJECT_NAME: {{ .Values.octopod.projectName | quote }} 9 | BASE_DOMAIN: {{ .Values.octopod.baseDomain | quote }} 10 | NAMESPACE: {{ .Values.octopod.deploymentNamespace | quote }} 11 | STATUS_UPDATE_TIMEOUT: {{ .Values.octopod.statusUpdateTimeout | quote }} 12 | ARCHIVE_RETENTION: {{ .Values.octopod.archiveRetention | quote }} 13 | CREATION_COMMAND: {{ printf "%s/create" (include "controlScriptsPath" .) | quote }} 14 | UPDATE_COMMAND: {{ printf "%s/update" (include "controlScriptsPath" .) | quote }} 15 | ARCHIVE_COMMAND: {{ printf "%s/archive" (include "controlScriptsPath" .) | quote }} 16 | CHECKING_COMMAND: {{ printf "%s/check" (include "controlScriptsPath" .) | quote }} 17 | CLEANUP_COMMAND: {{ printf "%s/cleanup" (include "controlScriptsPath" .) | quote }} 18 | ARCHIVE_CHECKING_COMMAND: {{ printf "%s/archive_check" (include "controlScriptsPath" .) | quote }} 19 | CONFIG_CHECKING_COMMAND: {{ printf "%s/config_check" (include "controlScriptsPath" .) | quote }} 20 | INFO_COMMAND: {{ printf "%s/info" (include "controlScriptsPath" .) | quote }} 21 | DEPLOYMENT_CONFIG_COMMAND: {{ printf "%s/deployment_overrides" (include "controlScriptsPath" .) | quote }} 22 | DEPLOYMENT_KEYS_COMMAND: {{ printf "%s/deployment_keys" (include "controlScriptsPath" .) | quote }} 23 | APPLICATION_CONFIG_COMMAND: {{ printf "%s/app_overrides" (include "controlScriptsPath" .) | quote }} 24 | APPLICATION_KEYS_COMMAND: {{ printf "%s/app_keys" (include "controlScriptsPath" .) | quote }} 25 | UNARCHIVE_COMMAND: {{ printf "%s/unarchive" (include "controlScriptsPath" .) | quote }} 26 | {{- range $name, $value := .Values.octopod.env }} 27 | {{ $name }}: {{ $value | quote }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /charts/octopod/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.rbac.create (eq .Values.serviceAccount.create true) -}} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ include "octopod.fullname" . }} 6 | labels: 7 | {{- include "octopod.labels" . | nindent 4 }} 8 | rules: 9 | - apiGroups: 10 | - '*' 11 | resources: 12 | - '*' 13 | verbs: 14 | - '*' 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRoleBinding 18 | metadata: 19 | name: {{ include "octopod.fullname" . }} 20 | labels: 21 | {{- include "octopod.labels" . | nindent 4 }} 22 | roleRef: 23 | kind: ClusterRole 24 | apiGroup: rbac.authorization.k8s.io 25 | name: {{ include "octopod.fullname" . }} 26 | subjects: 27 | - kind: ServiceAccount 28 | name: {{ include "octopod.serviceAccountName" . }} 29 | namespace: {{ .Release.Namespace }} 30 | {{- end -}} 31 | -------------------------------------------------------------------------------- /charts/octopod/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "octopod.fullname" . }} 5 | labels: 6 | {{- include "octopod.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | {{- range $portName, $portNumber := .Values.service.ports }} 11 | - name: octopod-{{ $portName | lower }} 12 | port: {{ $portNumber }} 13 | targetPort: {{ $portNumber }} 14 | {{- end }} 15 | selector: 16 | {{- include "octopod.selectorLabels" . | nindent 4 }} 17 | -------------------------------------------------------------------------------- /charts/octopod/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "octopod.serviceAccountName" . }} 6 | labels: 7 | {{- include "octopod.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: docs mermaid plantuml 2 | 3 | docs: mermaid plantuml 4 | 5 | mermaid: 6 | # to install mmdc use https://github.com/mermaidjs/mermaid.cli 7 | # 8 | # mmdc does not support stateDiagram-v2 (technical-architecture-deployment-statuses-fsm.mmd), 9 | # use https://mermaid-js.github.io/mermaid-live-editor to render it 10 | for src in `ls diagrams/src/*.mmd | grep -v technical-architecture-deployment-statuses-fsm.mmd`; do \ 11 | name=`basename $$src .mmd`; \ 12 | mmdc -i $$src -o "diagrams/images/$$name.png" --scale 4 --cssFile style.css; \ 13 | done 14 | 15 | plantuml: 16 | # to install plantuml go to https://plantuml.com/command-line 17 | # 18 | # plantuml takes output paths relative to input file 19 | for src in `ls diagrams/src/*.puml`; do \ 20 | name=`basename $$src .mmd`; \ 21 | plantuml -I $$src -o "../../diagrams/images" -tpng; \ 22 | done 23 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 🐙📑 Octopod documentation 2 | 3 | ## 🔭 High-level notes 4 | - [🐙 Overview](en/Overview.md) 5 | - [🧑‍🔬 Project managment case study](en/PM_case_study.md) 6 | - [🧑‍💻 Technical case study](en/Tech_case_study.md) 7 | 8 | ## 🛠️ Technical documentation 9 | - [🏗 Technical architecture](en/Technical_architecture.md) 10 | - [⚙️ Control script guide](en/Control_scripts.md) 11 | - [🔧🐙 Octopod deployment guide](en/Octopod_deployment_guide.md) 12 | - [🔧🚀 Helm-based Octopod project setup](en/Helm-based_deployment_guide.md) 13 | - [🐙🎛 octo CLI user guide](en/Octo_user_guide.md) 14 | - [🤖 CI integration](en/Integration.md) 15 | - [🔒 Octopod security model](en/Security_model.md) 16 | -------------------------------------------------------------------------------- /docs/diagrams/images/app-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/app-architecture.png -------------------------------------------------------------------------------- /docs/diagrams/images/sample_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/sample_architecture.png -------------------------------------------------------------------------------- /docs/diagrams/images/sample_deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/sample_deployment.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-archive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-archive.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-cleanup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-cleanup.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-create.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-deployment-states-fsm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-deployment-states-fsm.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-restore.png -------------------------------------------------------------------------------- /docs/diagrams/images/technical-architecture-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/diagrams/images/technical-architecture-update.png -------------------------------------------------------------------------------- /docs/diagrams/src/sample_architecture.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | top to bottom direction 4 | 5 | database "Redis" as OBR 6 | database "Postgres" as OBP 7 | boundary "nginx" as OBN 8 | 9 | rectangle "Server" as OBSer 10 | 11 | OBSer -down-> OBR 12 | OBSer -down-> OBP 13 | OBN -right-> OBSer 14 | 15 | 16 | @enduml 17 | -------------------------------------------------------------------------------- /docs/diagrams/src/sample_deployment.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | top to bottom direction 4 | 5 | node "Kubernetes cluster" { 6 | boundary "Kube API Server" as K8sAPI 7 | 8 | rectangle "Kubernetes" as K8s 9 | 10 | K8sAPI -> K8s 11 | 12 | cloud "Orange button staging" as OBS #OldLace { 13 | frame "Common infrastructure" #lightcyan { 14 | database "Redis" as OBR #PowderBlue 15 | database "Postgres" as OBP #PowderBlue 16 | boundary "nginx" as OBN #PowderBlue 17 | } 18 | 19 | rectangle "Server with an orange button" as OBSer #Wheat 20 | 21 | OBSer -down-> OBR 22 | OBSer -down-> OBP 23 | OBN -right-> OBSer 24 | } 25 | 26 | cloud "Green button staging" as GBS #technology { 27 | frame "Common infrastructure " #lightcyan { 28 | database "Redis" as GBR #PowderBlue 29 | database "Postgres" as GBP #PowderBlue 30 | boundary "nginx" as GBN #PowderBlue 31 | } 32 | 33 | rectangle "Server with a green button" as GBSer #Greenyellow 34 | 35 | GBSer -down-> GBR 36 | GBSer -down-> GBP 37 | GBN -right-> GBSer 38 | } 39 | 40 | K8s -down-> OBS : create the staging 41 | K8s -down-> GBS : create the staging 42 | } 43 | 44 | 45 | node Octopod { 46 | boundary "Web UI" as UI 47 | rectangle "Octopod Server" as OctoS 48 | rectangle "Staging control scripts" as SCS 49 | 50 | UI -> OctoS 51 | OctoS -> SCS : delegates k8s logic to control script 52 | } 53 | 54 | SCS -down-> K8sAPI : set up the stagings 55 | 56 | 57 | actor Developer 58 | 59 | Developer -down-> UI : Specifies the git comit hash to deploy in the web UI 60 | 61 | @enduml 62 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-archive.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant User 3 | participant Octopod Frontend 4 | participant Octopod Backend 5 | participant Control Scipts 6 | participant Octopod DB 7 | 8 | User ->>+ Octopod Frontend: Archive deployment 9 | activate Octopod Frontend 10 | 11 | Octopod Frontend ->>+ Octopod Backend: Archive deployment 12 | 13 | 14 | Octopod Backend ->>+ Control Scipts: archive – Archive the deployment 15 | Control Scipts -->>- Octopod Backend: Deployment archive started 16 | 17 | Octopod Backend ->>+ Octopod DB: Set deployment state to ArchivePending 18 | Octopod DB -->>- Octopod Backend: Deployment status set 19 | 20 | loop 21 | Octopod Backend ->>+ Control Scipts: archive_check – Is the deployment archived? 22 | Control Scipts -->>- Octopod Backend: deployment is not archived 23 | end 24 | 25 | Octopod Backend ->>+ Control Scipts: archive_check – Is the deployment archived? 26 | Control Scipts -->>- Octopod Backend: deployment is archived! 27 | 28 | Octopod Backend ->>+ Octopod DB: Set deployment state to Archived 29 | Octopod DB -->>- Octopod Backend: Deployment status set 30 | 31 | deactivate Octopod Backend 32 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-cleanup.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant Octopod Backend 3 | participant Control Scipts 4 | participant Octopod DB 5 | 6 | activate Octopod Backend 7 | 8 | Octopod Backend ->>+ Control Scipts: cleanup – Clean up deployment resouces 9 | Control Scipts -->>- Octopod Backend: Deployment resouces cleaned up 10 | 11 | Octopod Backend ->>+ Octopod DB: Delete everything related to the deployment 12 | Octopod DB -->>- Octopod Backend: Everything deleted 13 | 14 | deactivate Octopod Backend 15 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-create.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant User 3 | participant Octopod Frontend 4 | participant Octopod Backend 5 | participant Control Scipts 6 | participant Octopod DB 7 | 8 | User ->>+ Octopod Frontend: Create deployment 9 | activate Octopod Frontend 10 | 11 | Octopod Frontend ->>+ Octopod Backend: Create deployment 12 | Octopod Backend ->>+ Control Scipts: config_check – Check deployment config 13 | Control Scipts ->>- Octopod Backend: Deployment config OK 14 | Octopod Backend ->>+ Octopod DB: Create deployment 15 | Octopod DB -->>- Octopod Backend: Deployment Created 16 | 17 | Octopod Backend -->> Octopod Frontend: Delpoyment created 18 | Octopod Frontend -->>- User: Close deployment popup 19 | 20 | Octopod Backend ->>+ Control Scipts: info – Get deployment metadata 21 | Control Scipts -->>- Octopod Backend: Deployment metadata 22 | 23 | Octopod Backend ->>+ Octopod DB: Set deployment metadata 24 | Octopod DB -->>- Octopod Backend: Deployment metadata set 25 | 26 | 27 | Octopod Backend ->>+ Control Scipts: create – Create the delpoyment 28 | Control Scipts -->>- Octopod Backend: Deployment creation started 29 | 30 | loop 31 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 32 | Control Scipts -->>- Octopod Backend: deployment is not up 33 | end 34 | 35 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 36 | Control Scipts -->>- Octopod Backend: deployment is up! 37 | 38 | Octopod Backend ->>+ Octopod DB: Set deployment state to Running 39 | Octopod DB -->>- Octopod Backend: Deployment status set 40 | 41 | deactivate Octopod Backend 42 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-deployment-states-fsm.mmd: -------------------------------------------------------------------------------- 1 | stateDiagram-v2 2 | [*] --> CreatePending: create 3 | Running --> UpdatePending: update 4 | Failure --> UpdatePending: update 5 | Running --> ArchivePending: archive 6 | Failure --> ArchivePending: archive 7 | Archived --> CreatePending: restore 8 | Archived --> [*]: cleanup 9 | Running --> Failure: 30s passed and 'check' said "nok" 10 | Failure --> Running: 30s passed and 'check' said "ok" 11 | CreatePending --> Running: 5m passed and 'check' said "ok" 12 | CreatePending --> Failure: 5m passed and 'check' said "nok" 13 | UpdatePending --> Running: 5m passed and 'check' said "ok" 14 | UpdatePending --> Failure: 5m passed and 'check' said "nok" 15 | ArchivePending --> Archived: 30s passed and 'archive_check' said "ok" 16 | ArchivePending --> ArchivePending: 30s passed and 'archive_check' said "nok" 17 | Archived --> CleanupFailed 18 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-restore.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant User 3 | participant Octopod Frontend 4 | participant Octopod Backend 5 | participant Control Scipts 6 | participant Octopod DB 7 | 8 | User ->>+ Octopod Frontend: Restore deployment 9 | activate Octopod Frontend 10 | 11 | Octopod Frontend ->>+ Octopod Backend: Restore deployment 12 | Octopod Backend ->>+ Control Scipts: config_check – Check deployment config 13 | Control Scipts ->>- Octopod Backend: Deployment config OK 14 | 15 | Octopod Backend -->> Octopod Frontend: Delpoyment restore started 16 | Octopod Frontend -->>- User: Deployment restore started 17 | 18 | 19 | Octopod Backend ->>+ Control Scipts: info – Get deployment metadata 20 | Control Scipts -->>- Octopod Backend: Deployment metadata 21 | 22 | Octopod Backend ->>+ Octopod DB: Set deployment metadata 23 | Octopod DB -->>- Octopod Backend: Deployment metadata set 24 | 25 | Octopod Backend ->>+ Control Scipts: unarchive – Unarchive the delpoyment 26 | Control Scipts -->>- Octopod Backend: Deployment unarchiving started 27 | 28 | Octopod Backend ->>+ Octopod DB: Set deployment state to CreatePending 29 | Octopod DB -->>- Octopod Backend: Deployment status set 30 | 31 | loop 32 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 33 | Control Scipts -->>- Octopod Backend: deployment is not up 34 | end 35 | 36 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 37 | Control Scipts -->>- Octopod Backend: deployment is up! 38 | 39 | Octopod Backend ->>+ Octopod DB: Set deployment state to Running 40 | Octopod DB -->>- Octopod Backend: Deployment status set 41 | 42 | deactivate Octopod Backend 43 | -------------------------------------------------------------------------------- /docs/diagrams/src/technical-architecture-update.mmd: -------------------------------------------------------------------------------- 1 | sequenceDiagram 2 | participant User 3 | participant Octopod Frontend 4 | participant Octopod Backend 5 | participant Control Scipts 6 | participant Octopod DB 7 | 8 | User ->>+ Octopod Frontend: Update deployment 9 | activate Octopod Frontend 10 | 11 | Octopod Frontend ->>+ Octopod Backend: Update deployment 12 | Octopod Backend ->>+ Control Scipts: config_check – Check deployment config 13 | Control Scipts ->>- Octopod Backend: Deployment config OK 14 | 15 | Octopod Backend -->> Octopod Frontend: Delpoyment update started 16 | Octopod Frontend -->>- User: Close deployment popup 17 | 18 | 19 | Octopod Backend ->>+ Octopod DB: Update deployment config 20 | Octopod DB -->>- Octopod Backend: Deployment config updated 21 | 22 | 23 | Octopod Backend ->>+ Control Scipts: info – Get deployment metadata 24 | Control Scipts -->>- Octopod Backend: Deployment metadata 25 | 26 | Octopod Backend ->>+ Octopod DB: Set deployment metadata 27 | Octopod DB -->>- Octopod Backend: Deployment metadata set 28 | 29 | Octopod Backend ->>+ Control Scipts: update – Update the delpoyment 30 | Control Scipts -->>- Octopod Backend: Deployment update started 31 | 32 | Octopod Backend ->>+ Octopod DB: Set deployment state to UpdatePending 33 | Octopod DB -->>- Octopod Backend: Deployment status set 34 | 35 | loop 36 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 37 | Control Scipts -->>- Octopod Backend: deployment is not up 38 | end 39 | 40 | Octopod Backend ->>+ Control Scipts: check – Is the deployment up? 41 | Control Scipts -->>- Octopod Backend: deployment is up! 42 | 43 | Octopod Backend ->>+ Octopod DB: Set deployment state to Running 44 | Octopod DB -->>- Octopod Backend: Deployment status set 45 | 46 | deactivate Octopod Backend 47 | -------------------------------------------------------------------------------- /docs/en/Integration.md: -------------------------------------------------------------------------------- 1 | # Integration into existing CI/CD pipelines 2 | 3 | 4 | You likely already have some form of CI integration with your version control system, such as *GitHub Action* or *Travis CI*, to run various checks on your code. Most of these services are set up by providing what is essentially just a shell script that is run under specific conditions. 5 | 6 | You might want to automate deployments even further ― you might want deployments to be automatically created and updated when developers create and update *Pull Requests*. 7 | 8 | _Octopod_ can be interacted with through the _octo CLI_ tool. This tool can be easily called from within a *CI* script. 9 | 10 | ## ✨ Creating deployments 11 | 12 | To create a deployment (given that you have already obtained a *Docker Image* and uploaded it to your _Image Registry_ in one of the previous *CI* steps) you simply need to call _octo CLI_ with the following arguments: 13 | 14 | ```bash 15 | octo create -n $NAME -t $IMAGE_TAG 16 | ``` 17 | 18 | `$NAME` is the name of the deployment you want to create. You can set it to be the name of the branch for example. 19 | 20 | `$IMAGE_TAG` is the _tag_ of the docker image you want to deploy. 21 | 22 | ## 🚀 Updating deployments 23 | 24 | Updating deployments is done using the same arguments, but you need to call the `create` command, instead of the `update` command: 25 | 26 | ```bash 27 | octo update -n $NAME -t $IMAGE_TAG 28 | ``` 29 | 30 |
31 | 32 |

33 | Star the project of you like it 34 |

35 | 36 |

37 | -------------------------------------------------------------------------------- /docs/en/README.md: -------------------------------------------------------------------------------- 1 | # 🐙📑 Octopod documentation 2 | 3 | ## 🔭 High-level notes 4 | - [🐙 Overview](Overview.md) 5 | - [🧑‍🔬 Project managment case study](PM_case_study.md) 6 | - [🧑‍💻 Technical case study](Tech_case_study.md) 7 | 8 | ## 🛠️ Technical documentation 9 | - [🏗 Technical architecture](Technical_architecture.md) 10 | - [⚙️ Control scripts guide](Control_scripts.md) 11 | - [🔧🐙 Octopod deployment guide](Octopod_deployment_guide.md) 12 | - [🔧🚀 Helm-based Octopod project setup](Helm-based_deployment_guide.md) 13 | - [🐙🎛 octo CLI user guide](Octo_user_guide.md) 14 | - [🤖 CI integration](Integration.md) 15 | - [🔒 Octopod security model](Security_model.md) 16 | -------------------------------------------------------------------------------- /docs/images/break1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/break1.png -------------------------------------------------------------------------------- /docs/images/break2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/break2.png -------------------------------------------------------------------------------- /docs/images/dev1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/dev1.png -------------------------------------------------------------------------------- /docs/images/dev2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/dev2.png -------------------------------------------------------------------------------- /docs/images/env1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/env1.png -------------------------------------------------------------------------------- /docs/images/env2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/env2.png -------------------------------------------------------------------------------- /docs/images/octopod_app_keys_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_app_keys_list.png -------------------------------------------------------------------------------- /docs/images/octopod_archive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_archive.png -------------------------------------------------------------------------------- /docs/images/octopod_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_blank.png -------------------------------------------------------------------------------- /docs/images/octopod_deployment_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_deployment_blank.png -------------------------------------------------------------------------------- /docs/images/octopod_deployment_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_deployment_filled.png -------------------------------------------------------------------------------- /docs/images/octopod_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_filled.png -------------------------------------------------------------------------------- /docs/images/octopod_in_octopod_deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/octopod_in_octopod_deployment.png -------------------------------------------------------------------------------- /docs/images/wordpress_blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/docs/images/wordpress_blank.png -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: white; 3 | } 4 | 5 | .actor { 6 | stroke: #ccccff; 7 | fill: #ececff; 8 | } 9 | 10 | text.actor { 11 | fill: black; 12 | stroke: none; 13 | font-family: Helvetica, Helvetica; 14 | } 15 | 16 | .actor-line { 17 | stroke: grey; 18 | } 19 | 20 | .messageLine0 { 21 | stroke-width: 1.5; 22 | stroke-dasharray: '2 2'; 23 | marker-end: 'url(#arrowhead)'; 24 | stroke: black; 25 | } 26 | 27 | .messageLine1 { 28 | stroke-width: 1.5; 29 | stroke-dasharray: '2 2'; 30 | stroke: black; 31 | } 32 | 33 | #arrowhead { 34 | fill: black; 35 | } 36 | 37 | .messageText { 38 | fill: black; 39 | stroke: none; 40 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 41 | font-size: 19px; 42 | } 43 | 44 | .labelBox { 45 | stroke: #ccccff; 46 | fill: #ececff; 47 | } 48 | 49 | .labelText { 50 | fill: black; 51 | stroke: none; 52 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 53 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 54 | font-size: 19px; 55 | } 56 | 57 | .loopText { 58 | fill: black; 59 | stroke: none; 60 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 61 | } 62 | 63 | .loopLine { 64 | stroke-width: 2; 65 | stroke-dasharray: '2 2'; 66 | marker-end: 'url(#arrowhead)'; 67 | stroke: #ccccff; 68 | } 69 | 70 | .note { 71 | stroke: #decc93; 72 | fill: #fff5ad; 73 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 74 | font-size: 19px; 75 | } 76 | 77 | .noteText { 78 | font-size: 19px; 79 | fill: black; 80 | stroke: none; 81 | } 82 | 83 | .sectionTitle, 84 | .noteText, 85 | .taskText, 86 | .titleText, 87 | .taskTextOutsideRight { 88 | font-size: 19px; 89 | font-family: Helvetica, 'trebuchet ms', verdana, arial; 90 | } -------------------------------------------------------------------------------- /fourmolu.yaml: -------------------------------------------------------------------------------- 1 | indentation: 2 2 | comma-style: leading # for lists, tuples etc. - can also be 'trailing' 3 | record-brace-space: true # rec {x = 1} vs. rec{x = 1} 4 | indent-wheres: true # 'false' means save space by only half-indenting the 'where' keyword 5 | diff-friendly-import-export: false # 'false' uses Ormolu-style lists 6 | respectful: true # don't be too opinionated about newlines etc. 7 | haddock-style: single-line # '--' vs. '{-' 8 | newlines-between-decls: 1 # number of newlines between top-level declarations 9 | -------------------------------------------------------------------------------- /helm-control-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /helm-control-scripts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "helm-control-scripts" 3 | version = "0.2.3" 4 | authors = ["Aleksei Sizov "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | structopt = "0.3" 11 | log = "0.4" 12 | env_logger = "0.8.4" 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | serde_yaml = "0.8" 16 | envy = "0.4" 17 | yaml-rust = "0.4" 18 | kube = { version = "0.58.1", features = ["derive", "jsonpatch"]} 19 | k8s-openapi = { version = "0.12.0", features = ["v1_19"]} 20 | tokio = { version = "1.7.0", features = ["full"] } 21 | dkregistry = { git = "https://github.com/camallo/dkregistry-rs", rev = "854d0da53bef5dd85b5e901123e85d43af97c74e"} # Version in crates uses old tokio 22 | regex = "1" 23 | rusoto_core = "0.47.0" 24 | rusoto_ecr = "0.47.0" 25 | schemars = "0.8.6" 26 | json-patch = "0.2.6" 27 | 28 | [[bin]] 29 | name = "create" 30 | path = "src/create_update.rs" 31 | 32 | [[bin]] 33 | name = "update" 34 | path = "src/create_update.rs" 35 | 36 | [[bin]] 37 | name = "archive" 38 | path = "src/archive.rs" 39 | 40 | [[bin]] 41 | name = "info" 42 | path = "src/info.rs" 43 | 44 | [[bin]] 45 | name = "cleanup" 46 | path = "src/cleanup.rs" 47 | 48 | [[bin]] 49 | name = "check" 50 | path = "src/check.rs" 51 | 52 | [[bin]] 53 | name = "archive_check" 54 | path = "src/archive_check.rs" 55 | 56 | [[bin]] 57 | name = "init" 58 | path = "src/init.rs" 59 | 60 | [[bin]] 61 | name = "config_check" 62 | path = "src/config_check.rs" 63 | 64 | [[bin]] 65 | name = "app_overrides" 66 | path = "src/app_overrides.rs" 67 | 68 | [[bin]] 69 | name = "deployment_overrides" 70 | path = "src/deployment_overrides.rs" 71 | 72 | [[bin]] 73 | name = "deployment_keys" 74 | path = "src/deployment_keys.rs" 75 | 76 | [[bin]] 77 | name = "app_keys" 78 | path = "src/app_keys.rs" 79 | 80 | [[bin]] 81 | name = "unarchive" 82 | path = "src/unarchive.rs" 83 | -------------------------------------------------------------------------------- /helm-control-scripts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3 2 | ARG HELM_BIN=/utils/helm 3 | ENV HELM_BIN=$HELM_BIN 4 | 5 | ADD https://get.helm.sh/helm-v3.6.0-linux-amd64.tar.gz /tmp/helm.tar.gz 6 | 7 | RUN tar -xf /tmp/helm.tar.gz -C /tmp &&\ 8 | mkdir -p /utils &&\ 9 | cp /tmp/linux-amd64/helm $HELM_BIN &&\ 10 | chmod +x $HELM_BIN &&\ 11 | rm -r /tmp/* 12 | 13 | ADD target/x86_64-unknown-linux-musl/release/archive /utils/ 14 | ADD target/x86_64-unknown-linux-musl/release/archive_check /utils/ 15 | ADD target/x86_64-unknown-linux-musl/release/check /utils/ 16 | ADD target/x86_64-unknown-linux-musl/release/cleanup /utils/ 17 | ADD target/x86_64-unknown-linux-musl/release/create /utils/ 18 | ADD target/x86_64-unknown-linux-musl/release/info /utils/ 19 | ADD target/x86_64-unknown-linux-musl/release/update /utils/ 20 | ADD target/x86_64-unknown-linux-musl/release/init /utils/ 21 | ADD target/x86_64-unknown-linux-musl/release/config_check /utils/ 22 | ADD target/x86_64-unknown-linux-musl/release/app_overrides /utils/ 23 | ADD target/x86_64-unknown-linux-musl/release/app_keys /utils/ 24 | ADD target/x86_64-unknown-linux-musl/release/deployment_overrides /utils/ 25 | ADD target/x86_64-unknown-linux-musl/release/deployment_keys /utils/ 26 | ADD target/x86_64-unknown-linux-musl/release/unarchive /utils/ 27 | -------------------------------------------------------------------------------- /helm-control-scripts/src/app_keys.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let cli_opts = CliOpts::from_args(); 10 | info!("Cli options received {:?}", &cli_opts); 11 | let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs); 12 | helm_init(&envs, &deployment_parameters); 13 | let helm_values = HelmCmd { 14 | name: envs.helm_bin, 15 | mode: HelmMode::ShowValues, 16 | release_name: String::from(""), 17 | namespace: String::from(""), 18 | deployment_parameters: deployment_parameters, 19 | overrides: vec![], 20 | }; 21 | info!("Generated Helm args: {:?}", &helm_values.args()); 22 | match helm_values.run_stdout() { 23 | Ok(status) => { 24 | print!("{}", print_keys(helm_values_as_keys(status))); 25 | } 26 | Err(status) => { 27 | error!("Error during helm execution"); 28 | panic!("{:?}", status); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /helm-control-scripts/src/app_overrides.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap(); 10 | print!("{}", print_kv(default_values.app_overrides())); 11 | } 12 | -------------------------------------------------------------------------------- /helm-control-scripts/src/create_update.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stdout).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let cli_opts = CliOpts::from_args(); 10 | info!("Cli options received {:?}", &cli_opts); 11 | let overrides = match overrides(&cli_opts, &envs) { 12 | Some(inner) => inner, 13 | None => vec![], 14 | }; 15 | 16 | let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs); 17 | helm_init(&envs, &deployment_parameters); 18 | let release_name = match cli_opts.name { 19 | Some(name) => name, 20 | None => { 21 | error!("mandatory name argument was not provided"); 22 | panic!(); 23 | } 24 | }; 25 | let helm_cmd = HelmCmd { 26 | name: envs.helm_bin, 27 | mode: HelmMode::UpgradeInstall, 28 | release_name: release_name, 29 | namespace: cli_opts.namespace, 30 | deployment_parameters: deployment_parameters, 31 | overrides: overrides, 32 | }; 33 | info!("Generated Helm args: {:?}", &helm_cmd.args()); 34 | match helm_cmd.run() { 35 | Ok(_status) => info!("Success!"), 36 | Err(status) => { 37 | error!("Error during helm execution"); 38 | panic!("{:?}", status); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /helm-control-scripts/src/deployment_keys.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap(); 10 | print!("{}", print_keys(Some(default_values.deployment_keys()))); 11 | } 12 | -------------------------------------------------------------------------------- /helm-control-scripts/src/deployment_overrides.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap(); 10 | print!("{}", print_kv(Some(default_values.deployment_overrides()))); 11 | } 12 | -------------------------------------------------------------------------------- /helm-control-scripts/src/info.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stderr).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let cli_opts = CliOpts::from_args(); 10 | info!("Cli options received {:?}", &cli_opts); 11 | let overrides = match overrides(&cli_opts, &envs) { 12 | Some(inner) => inner, 13 | None => vec![], 14 | }; 15 | 16 | let deployment_parameters = HelmDeploymentParameters::new(&cli_opts, &envs); 17 | helm_init(&envs, &deployment_parameters); 18 | let release_name = match cli_opts.name { 19 | Some(name) => name, 20 | None => { 21 | error!("mandatory name argument was not provided"); 22 | panic!(); 23 | } 24 | }; 25 | let default_name = String::from(&release_name); 26 | let helm_template = HelmCmd { 27 | name: envs.helm_bin, 28 | mode: HelmMode::Template, 29 | release_name: release_name, 30 | namespace: cli_opts.namespace, 31 | deployment_parameters: deployment_parameters, 32 | overrides: overrides, 33 | }; 34 | match helm_template.run_stdout() { 35 | Ok(status) => { 36 | let (_deployments, _statefulsets, ingresses, old_ingresses, _postgresqls, _kafkas) = match parse_to_k8s(status) { 37 | Ok((deployments, statefulsets, ingresses, old_ingresses, postgresqls, kafkas)) => (deployments, statefulsets, ingresses, old_ingresses, postgresqls, kafkas), 38 | Err(err) => panic!("{}", err) 39 | }; 40 | print!("{}", print_kv(ingresses_to_hosts(ingresses, old_ingresses, default_name))); 41 | } 42 | Err(status) => { 43 | error!("Error during helm execution"); 44 | panic!("{:?}", status); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /helm-control-scripts/src/init.rs: -------------------------------------------------------------------------------- 1 | use helm_control_scripts::lib::*; 2 | 3 | fn main() { 4 | let mut log_builder = Builder::from_default_env(); 5 | log_builder.target(Target::Stdout).filter(None, LevelFilter::Info).init(); 6 | info!("Utils version {}", env!("CARGO_PKG_VERSION")); 7 | let envs = EnvVars::parse(); 8 | info!("Env variables received {:?}", &envs); 9 | let default_values: DefaultValues = serde_json::from_str(&envs.defaults).unwrap(); 10 | let deployment_parameters = HelmDeploymentParameters::new_env_only(&default_values, &envs); 11 | match &envs.helm_on_init_only { 12 | Some(enabled) => { 13 | if *enabled { 14 | helm_repo_add_update(&envs, &deployment_parameters); 15 | } else { 16 | info!("Skipping helm initialization since HELM_ON_INIT_ONLY is false"); 17 | } 18 | }, 19 | None => info!("Skipping helm initialization since HELM_ON_INIT_ONLY is not set"), 20 | } 21 | info!("Success!"); 22 | } 23 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | - path: "octo-cli/src" 4 | component: "octo-cli:exe:octo" 5 | 6 | - path: "octopod-api/src" 7 | component: "lib:octopod-api" 8 | 9 | - path: "octopod-backend/src" 10 | component: "lib:octopod-backend" 11 | 12 | - path: "octopod-backend/app" 13 | component: "octopod-backend:exe:octopod-exe" 14 | 15 | - path: "octopod-backend/test" 16 | component: "octopod-backend:test:octopod-test" 17 | 18 | - path: "octopod-common/src" 19 | component: "lib:octopod-common" 20 | 21 | - path: "octopod-frontend/src" 22 | component: "octopod-frontend:exe:frontend" 23 | -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/img/demo.gif -------------------------------------------------------------------------------- /img/release_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/img/release_workflow.png -------------------------------------------------------------------------------- /img/typeable_logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrations/deploy/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/migrations/deploy/.gitkeep -------------------------------------------------------------------------------- /migrations/deploy/add_archived_status.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:add_archived_status to pg 2 | 3 | BEGIN; 4 | 5 | COMMIT; 6 | 7 | ALTER TYPE statuses ADD VALUE 'Archived' AFTER 'DeletePending'; 8 | -------------------------------------------------------------------------------- /migrations/deploy/add_detailed_failures.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:add_detailed_failures to pg 2 | BEGIN; 3 | 4 | 5 | ALTER TYPE statuses RENAME VALUE 'Failure' TO 'GenericFailure'; 6 | 7 | 8 | ALTER TYPE statuses ADD VALUE IF NOT EXISTS 'TagMismatch'; 9 | 10 | 11 | ALTER TYPE statuses ADD VALUE IF NOT EXISTS 'PartialAvailability'; 12 | 13 | 14 | COMMIT; 15 | -------------------------------------------------------------------------------- /migrations/deploy/add_duration_and_stdout_and_stderr_to_deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:add_duration_and_stdout_and_stderr_to_deployment_logs to pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployment_logs ADD duration INT NOT NULL DEFAULT 0; 6 | ALTER TABLE deployment_logs ADD stdout TEXT NOT NULL DEFAULT ''; 7 | ALTER TABLE deployment_logs ADD stderr TEXT NOT NULL DEFAULT ''; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /migrations/deploy/archived_flag.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:archived_flag to pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployments ADD archived BOOLEAN NOT NULL DEFAULT 'f'; 6 | ALTER TABLE deployments ADD archived_at TIMESTAMPTZ; 7 | 8 | CREATE INDEX deployments_archived_archived_at_idx ON deployments USING BTREE (archived, archived_at); 9 | 10 | 11 | ALTER TABLE deployment_logs ADD archived BOOLEAN NOT NULL DEFAULT 'f'; 12 | 13 | 14 | COMMIT; 15 | 16 | ALTER TYPE actions ADD VALUE 'delete' AFTER 'update'; 17 | ALTER TYPE actions ADD VALUE 'restore' AFTER 'delete'; 18 | -------------------------------------------------------------------------------- /migrations/deploy/create_deployment_metadata.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:create_deployment_metadata to pg 2 | 3 | BEGIN; 4 | 5 | CREATE TABLE deployment_metadata ( 6 | id BIGSERIAL PRIMARY KEY, 7 | deployment_id BIGINT NOT NULL REFERENCES deployments(id), 8 | key TEXT NOT NULL, 9 | value TEXT NOT NULL, 10 | created_at TIMESTAMPTZ NOT NULL DEFAULT now(), 11 | updated_at TIMESTAMPTZ NOT NULL DEFAULT now() 12 | ); 13 | 14 | CREATE UNIQUE INDEX deployment_metadata_deployment_id_key_key ON deployment_metadata USING BTREE (deployment_id, key); 15 | 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /migrations/deploy/deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:deployment_logs to pg 2 | 3 | BEGIN; 4 | 5 | CREATE TYPE actions AS ENUM ('create', 'edit', 'update'); 6 | 7 | CREATE TABLE deployment_logs ( 8 | id BIGSERIAL PRIMARY KEY, 9 | deployment_id BIGINT NOT NULL REFERENCES deployments(id), 10 | action actions, 11 | tag TEXT, 12 | envs TEXT, 13 | exit_code SMALLINT NOT NULL, 14 | created_at TIMESTAMPTZ NOT NULL DEFAULT now() 15 | ); 16 | 17 | COMMIT; 18 | -------------------------------------------------------------------------------- /migrations/deploy/deployments.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:deployments to pg 2 | 3 | BEGIN; 4 | 5 | CREATE TABLE deployments ( 6 | id BIGSERIAL PRIMARY KEY, 7 | name TEXT NOT NULL, 8 | tag TEXT NOT NULL, 9 | envs TEXT NOT NULL, 10 | created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), 11 | updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() 12 | ); 13 | 14 | CREATE UNIQUE INDEX deployments_name_key ON deployments USING BTREE (name); 15 | 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /migrations/deploy/move_tag_to_overrides.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:move_tag_to_overrides to pg 2 | 3 | BEGIN; 4 | 5 | 6 | UPDATE deployments as d 7 | SET app_overrides = d.app_overrides || 8 | jsonb_build_array(jsonb_build_array('image.tag', jsonb_build_object('tag', 'ValueAdded', 'contents', d.tag))); 9 | 10 | ALTER TABLE "deployments" DROP COLUMN "tag"; 11 | 12 | UPDATE deployment_logs as d 13 | SET app_overrides = d.app_overrides || 14 | jsonb_build_array(jsonb_build_array('image.tag', jsonb_build_object('tag', 'ValueAdded', 'contents', d.tag))); 15 | 16 | ALTER TABLE "deployment_logs" DROP COLUMN "tag"; 17 | 18 | COMMIT; 19 | -------------------------------------------------------------------------------- /migrations/deploy/remove_archived_column.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:remove_archived_column to pg 2 | BEGIN; 3 | 4 | 5 | ALTER TABLE "public"."deployments" 6 | DROP COLUMN "archived"; 7 | 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /migrations/deploy/rename-delete-to-archive.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:rename-delete-to-archive to pg 2 | BEGIN; 3 | 4 | 5 | ALTER TYPE statuses RENAME VALUE 'DeletePending' TO 'ArchivePending'; 6 | 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /migrations/deploy/rename_delete_to_archive_2.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:rename_delete_to_archive_2 to pg 2 | 3 | BEGIN; 4 | 5 | ALTER TYPE actions RENAME VALUE 'delete' TO 'archive'; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /migrations/deploy/rename_elements_of_scope_enum.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:rename_elements_of_scope_enum to pg 2 | 3 | BEGIN; 4 | 5 | ALTER TYPE scopes RENAME VALUE 'App' to 'ApplicationScope'; 6 | ALTER TYPE scopes RENAME VALUE 'Staging' to 'DeploymentScope'; 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /migrations/deploy/status_and_status_updated_at.sql: -------------------------------------------------------------------------------- 1 | -- Deploy octopod:status_and_status_updated_at to pg 2 | 3 | BEGIN; 4 | 5 | CREATE TYPE statuses as ENUM ('Running', 'Failure', 'CreatePending', 'UpdatePending', 'DeletePending'); 6 | 7 | ALTER TABLE deployments ADD status statuses NOT NULL DEFAULT 'Running'; 8 | ALTER TABLE deployments ADD status_updated_at TIMESTAMPTZ NOT NULL DEFAULT now(); 9 | ALTER TABLE deployments ADD checked_at TIMESTAMPTZ NOT NULL DEFAULT now(); 10 | 11 | CREATE INDEX deployments_checked_at_idx ON deployments USING BTREE (checked_at); 12 | 13 | COMMIT; 14 | -------------------------------------------------------------------------------- /migrations/revert/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/migrations/revert/.gitkeep -------------------------------------------------------------------------------- /migrations/revert/add_archived_status.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:add_archived_status from pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add DDLs here. 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /migrations/revert/add_deployment_overrides_and_deployment_log_overrides.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:add_deployment_overrides_and_deployment_log_overrides from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployments ADD envs TEXT NOT NULL DEFAULT ''; 6 | ALTER TABLE deployment_logs ADD envs TEXT NOT NULL DEFAULT ''; 7 | 8 | DO LANGUAGE 'plpgsql' $$ 9 | DECLARE 10 | _r RECORD; 11 | _r2 RECORD; 12 | _buf TEXT; 13 | BEGIN 14 | FOR _r IN SELECT DISTINCT deployment_id AS id FROM deployment_overrides LOOP 15 | _buf = ''; 16 | FOR _r2 IN SELECT key || '=' || value AS env FROM deployment_overrides WHERE deployment_id = _r.id ORDER BY id LOOP 17 | _buf = _buf || _r2.env || E'\n'; 18 | END LOOP; 19 | UPDATE deployments SET envs = rtrim(_buf, E'\n') WHERE id = _r.id; 20 | END LOOP; 21 | END 22 | $$; 23 | 24 | DO LANGUAGE 'plpgsql' $$ 25 | DECLARE 26 | _r RECORD; 27 | _r2 RECORD; 28 | _buf TEXT; 29 | BEGIN 30 | FOR _r IN SELECT DISTINCT deployment_log_id AS id FROM deployment_log_overrides LOOP 31 | _buf = ''; 32 | FOR _r2 IN SELECT key || '=' || value AS env FROM deployment_log_overrides WHERE deployment_log_id = _r.id ORDER BY id LOOP 33 | _buf = _buf || _r2.env || E'\n'; 34 | END LOOP; 35 | UPDATE deployment_logs SET envs = rtrim(_buf, E'\n') WHERE id = _r.id; 36 | END LOOP; 37 | END 38 | $$; 39 | 40 | DROP TABLE deployment_overrides; 41 | DROP TABLE deployment_log_overrides; 42 | 43 | DROP TYPE scopes; 44 | DROP TYPE visibilities; 45 | 46 | COMMIT; 47 | -------------------------------------------------------------------------------- /migrations/revert/add_detailed_failures.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:add_detailed_failures from pg 2 | BEGIN; 3 | 4 | 5 | ALTER TYPE statuses RENAME VALUE 'GenericFailure' TO 'Failure'; 6 | 7 | -- NOTE: not a full revert. Removing enum values is a pain. 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /migrations/revert/add_duration_and_stdout_and_stderr_to_deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:add_duration_and_stdout_and_stderr_to_deployment_logs from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployment_logs DROP duration; 6 | ALTER TABLE deployment_logs DROP stdout; 7 | ALTER TABLE deployment_logs DROP stderr; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /migrations/revert/archived_flag.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:archived_flag from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployments DROP archived; 6 | ALTER TABLE deployments DROP archived_at; 7 | 8 | ALTER TABLE deployment_logs DROP archived; 9 | 10 | COMMIT; 11 | -------------------------------------------------------------------------------- /migrations/revert/create_deployment_metadata.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:create_deployment_metadata from pg 2 | 3 | BEGIN; 4 | 5 | DROP TABLE deployment_metadata; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /migrations/revert/deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:deployment_logs from pg 2 | 3 | BEGIN; 4 | 5 | DROP TABLE deployment_logs; 6 | 7 | DROP TYPE actions; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /migrations/revert/deployments.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:deployments from pg 2 | 3 | BEGIN; 4 | 5 | DROP TABLE deployments; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /migrations/revert/migrate_2.0.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:migrate_2.0 from pg 2 | 3 | BEGIN; 4 | 5 | DO LANGUAGE 'plpgsql' 6 | $$ 7 | BEGIN 8 | RAISE EXCEPTION 'Revert not supported for this migration'; 9 | END 10 | $$; 11 | 12 | COMMIT; 13 | -------------------------------------------------------------------------------- /migrations/revert/move_tag_to_overrides.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:move_tag_to_overrides from pg 2 | BEGIN; 3 | ALTER TABLE deployments 4 | ADD COLUMN tag text; 5 | UPDATE deployments AS d 6 | SET tag = COALESCE( 7 | ( 8 | WITH appOvs AS ( 9 | SELECT jsonb_array_elements(d."app_overrides") AS res 10 | ) 11 | SELECT a.res->1->>'contents' 12 | FROM appOvs AS a 13 | WHERE (a.res->>0) = 'image.tag' 14 | ), 15 | '' 16 | ); 17 | UPDATE deployments AS d 18 | SET app_overrides = COALESCE( 19 | ( 20 | WITH appOvs AS ( 21 | SELECT jsonb_array_elements(d."app_overrides") AS res 22 | ) 23 | SELECT jsonb_agg(a.res) 24 | FROM appOvs AS a 25 | WHERE (a.res->>0) != 'image.tag' 26 | ), 27 | '[]' 28 | ); 29 | ALTER TABLE deployments 30 | ALTER COLUMN tag 31 | SET NOT NULL; 32 | -- 33 | ALTER TABLE deployment_logs 34 | ADD COLUMN tag text; 35 | UPDATE deployment_logs AS d 36 | SET tag = COALESCE( 37 | ( 38 | WITH appOvs AS ( 39 | SELECT jsonb_array_elements(d."app_overrides") AS res 40 | ) 41 | SELECT a.res->1->>'contents' 42 | FROM appOvs AS a 43 | WHERE (a.res->>0) = 'image.tag' 44 | ), 45 | '' 46 | ); 47 | UPDATE deployment_logs AS d 48 | SET app_overrides = COALESCE( 49 | ( 50 | WITH appOvs AS ( 51 | SELECT jsonb_array_elements(d."app_overrides") AS res 52 | ) 53 | SELECT jsonb_agg(a.res) 54 | FROM appOvs AS a 55 | WHERE (a.res->>0) != 'image.tag' 56 | ), 57 | '[]' 58 | ); 59 | ALTER TABLE deployment_logs 60 | ALTER COLUMN tag 61 | SET NOT NULL; 62 | COMMIT; 63 | -------------------------------------------------------------------------------- /migrations/revert/remove_archived_column.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:remove_archived_column from pg 2 | BEGIN; 3 | 4 | 5 | ALTER TABLE "public"."deployments" ADD COLUMN "archived" bool NOT NULL DEFAULT 'false'; 6 | 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /migrations/revert/rename-delete-to-archive.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:rename-delete-to-archive from pg 2 | BEGIN; 3 | 4 | 5 | ALTER TYPE statuses RENAME VALUE 'ArchivePending' TO 'DeletePending'; 6 | 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /migrations/revert/rename_delete_to_archive_2.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:rename_delete_to_archive_2 from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TYPE actions RENAME VALUE 'archive' TO 'delete'; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /migrations/revert/rename_elements_of_scope_enum.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:rename_elements_of_scope_enum from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TYPE scopes RENAME VALUE 'ApplicationScope' to 'App'; 6 | ALTER TYPE scopes RENAME VALUE 'DeploymentScope' to 'Staging'; 7 | 8 | COMMIT; 9 | -------------------------------------------------------------------------------- /migrations/revert/status_and_status_updated_at.sql: -------------------------------------------------------------------------------- 1 | -- Revert octopod:status_and_status_updated_at from pg 2 | 3 | BEGIN; 4 | 5 | ALTER TABLE deployments DROP status; 6 | ALTER TABLE deployments DROP status_updated_at; 7 | ALTER TABLE deployments DROP checked_at; 8 | 9 | DROP TYPE statuses; 10 | 11 | COMMIT; 12 | -------------------------------------------------------------------------------- /migrations/sqitch.conf: -------------------------------------------------------------------------------- 1 | [core] 2 | engine = pg 3 | # plan_file = sqitch.plan 4 | # top_dir = . 5 | # [engine "pg"] 6 | # target = db:pg: 7 | # registry = sqitch 8 | # client = psql 9 | -------------------------------------------------------------------------------- /migrations/sqitch.plan: -------------------------------------------------------------------------------- 1 | %syntax-version=1.0.0 2 | %project=octopod 3 | %uri=https://github.com/sqitchers/sqitch-intro/ 4 | 5 | deployments 2020-02-09T15:47:32Z Typeable LLC # Creates deployments table. 6 | deployment_logs 2020-02-28T21:35:30Z Typeable LLC # Creates deployment_logs table. 7 | archived_flag 2020-06-09T20:30:45Z Typeable LLC # Add archived flag to 'deployments' and 'deployment_logs' tables 8 | add_duration_and_stdout_and_stderr_to_deployment_logs 2020-06-26T09:30:35Z Typeable LLC # Add 'duration', 'stdout' and 'stderr' fields to 'deployment_logs' table 9 | status_and_status_updated_at 2020-07-06T08:08:10Z Typeable LLC # Add status, status_updated_at, checked_at to 'deployments' table 10 | add_deployment_overrides_and_deployment_log_overrides 2020-07-17T08:53:17Z Typeable LLC # Add 'deployment_overrides' and 'deployment_log_overrides' tables 11 | add_archived_status 2020-07-28T11:40:30Z Typeable LLC # Add 'Archived' to statuses 12 | create_deployment_metadata 2020-08-14T12:11:42Z Typeable LLC # Create 'deployment_metadata' table 13 | rename_elements_of_scope_enum 2020-08-19T09:38:40Z Typeable LLC # Rename elements of 'scope' enum 14 | rename-delete-to-archive 2020-11-23T10:53:10Z Ilya # Renamed delete to archive 15 | rename_delete_to_archive_2 2020-11-26T08:28:58Z Typeable LLC # Renamed delete to archive 16 | add_detailed_failures 2021-02-04T11:01:15Z Typeable LLC # Added more failure states 17 | remove_archived_column 2021-01-28T18:44:54Z Typeable LLC # Removed 'archived' column 18 | migrate_2.0 2021-08-11T17:09:37Z Typeable LLC # Migrate to 2.0-style tables. (Less tables) 19 | move_tag_to_overrides 2021-09-25T14:29:43Z Typeable LLC # Remove tag as a special field, making it an override field. 20 | -------------------------------------------------------------------------------- /migrations/verify/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/migrations/verify/.gitkeep -------------------------------------------------------------------------------- /migrations/verify/add_archived_status.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:add_archived_status on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/add_deployment_overrides_and_deployment_log_overrides.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:add_deployment_overrides_and_deployment_log_overrides on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/add_detailed_failures.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:add_detailed_failures on pg 2 | BEGIN; 3 | 4 | 5 | SELECT ('GenericFailure'::statuses, 6 | 'TagMismatch'::statuses, 7 | 'PartialAvailability'::statuses); 8 | 9 | 10 | ROLLBACK; 11 | -------------------------------------------------------------------------------- /migrations/verify/add_duration_and_stdout_and_stderr_to_deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:add_duration_and_stdout_and_stderr_to_deployment_logs on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/archived_flag.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:archived_flag on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/create_deployment_metadata.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:create_deployment_metadata on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/deployment_logs.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:deployment_logs on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/deployments.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:deployments on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/migrate_2.0.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:migrate_2.0 on pg 2 | 3 | BEGIN; 4 | 5 | SELECT 6 | id, 7 | name, 8 | tag, 9 | app_overrides, 10 | deployment_overrides, 11 | created_at, 12 | updated_at, 13 | archived_at, 14 | status, 15 | status_updated_at, 16 | checked_at, 17 | links 18 | FROM deployments 19 | WHERE false; 20 | 21 | SELECT 22 | id, 23 | deployment_id, 24 | action, 25 | tag, 26 | exit_code, 27 | created_at, 28 | archived, 29 | duration, 30 | stdout, 31 | stderr, 32 | app_overrides, 33 | deployment_overrides 34 | FROM deployment_logs 35 | WHERE false; 36 | 37 | ROLLBACK; 38 | -------------------------------------------------------------------------------- /migrations/verify/move_tag_to_overrides.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:move_tag_to_overrides on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/remove_archived_column.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:remove_archived_column on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/rename-delete-to-archive.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:rename-delete-to-archive on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/rename_delete_to_archive_2.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:rename_delete_to_archive_2 on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/rename_elements_of_scope_enum.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:rename_elements_of_scope_enum on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /migrations/verify/status_and_status_updated_at.sql: -------------------------------------------------------------------------------- 1 | -- Verify octopod:status_and_status_updated_at on pg 2 | 3 | BEGIN; 4 | 5 | -- XXX Add verifications here. 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /nix/ci.nix: -------------------------------------------------------------------------------- 1 | { sources ? import ./sources.nix 2 | , pkgs ? import sources.nixpkgs { } 3 | }: 4 | pkgs.mkShell { 5 | packages = [ pkgs.haskellPackages.fourmolu ]; 6 | } 7 | -------------------------------------------------------------------------------- /octo-cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Typeable LLC 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Typeable LLC nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /octo-cli/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | 3 | main = defaultMain 4 | -------------------------------------------------------------------------------- /octo-cli/src/Main.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Octopod.CLI 4 | 5 | main :: IO () 6 | main = runOcto 7 | -------------------------------------------------------------------------------- /octo-cli/src/Octopod/PowerAPI/Auth/Client.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | module Octopod.PowerAPI.Auth.Client 4 | ( IsAuth (..), 5 | AuthContext (..), 6 | ) 7 | where 8 | 9 | import Data.Proxy 10 | import Octopod.PowerAPI 11 | import Servant.API hiding (addHeader) 12 | import Servant.Auth 13 | import Servant.Client.Core 14 | 15 | class IsAuth a where 16 | data AuthContext a 17 | applyAuth :: AuthContext a -> Request -> Request 18 | 19 | instance (IsAuth a, HasClient m api) => HasClient m (Auth '[a] x :> api) where 20 | type Client m (Auth '[a] x :> api) = AuthContext a -> Client m api 21 | clientWithRoute pm Proxy req val = 22 | clientWithRoute pm (Proxy :: Proxy api) (applyAuth val req) 23 | 24 | hoistClientMonad pm _ f cl = hoistClientMonad pm (Proxy :: Proxy api) f . cl 25 | 26 | instance IsAuth AuthHeaderAuth where 27 | data AuthContext AuthHeaderAuth = AuthHeaderAuthCtx String 28 | applyAuth (AuthHeaderAuthCtx h) req = addHeader "Authorization" h req 29 | -------------------------------------------------------------------------------- /octo-cli/src/Text/Layout/Table/Extras.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | module Text.Layout.Table.Extras 4 | ( 5 | ) 6 | where 7 | 8 | import Data.Text 9 | import qualified Data.Text as T 10 | import Text.Layout.Table.Cell 11 | import Text.Layout.Table.Primitives.AlignInfo 12 | import Text.Layout.Table.StringBuilder 13 | 14 | instance Cell Text where 15 | dropLeft = T.drop 16 | dropRight = T.dropEnd 17 | visibleLength = T.length 18 | measureAlignment p xs = case T.break p xs of 19 | (ls, rs) -> 20 | AlignInfo (T.length ls) $ 21 | if T.null rs 22 | then Nothing 23 | else Just $ T.length rs - 1 24 | buildCell = stringB . T.unpack 25 | 26 | instance StringBuilder Text where 27 | stringB = T.pack 28 | charB = T.singleton 29 | replicateCharB x c = T.replicate x (T.singleton c) 30 | -------------------------------------------------------------------------------- /octopod-api/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Typeable LLC 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Typeable LLC nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /octopod-api/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | 3 | main = defaultMain 4 | -------------------------------------------------------------------------------- /octopod-api/octopod-api.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.4 2 | 3 | name: octopod-api 4 | version: 0.1.0.0 5 | description: Please see the README on GitHub at 6 | homepage: https://github.com/typeable/octopod 7 | bug-reports: https://github.com/typeable/octopod/issues 8 | license: BSD-3-Clause 9 | license-file: LICENSE 10 | author: Typeable 11 | maintainer: octopod@typeable.io 12 | 13 | library 14 | exposed-modules: 15 | Octopod.API 16 | Octopod.API.WebSocket 17 | Octopod.PowerAPI 18 | default-extensions: 19 | BlockArguments 20 | ConstraintKinds 21 | DataKinds 22 | DeriveFunctor 23 | DeriveGeneric 24 | DerivingVia 25 | DuplicateRecordFields 26 | FlexibleContexts 27 | GeneralizedNewtypeDeriving 28 | LambdaCase 29 | OverloadedStrings 30 | ScopedTypeVariables 31 | StandaloneDeriving 32 | TypeApplications 33 | TypeOperators 34 | build-depends: 35 | base, 36 | servant, 37 | octopod-common, 38 | servant-auth, 39 | text, 40 | if !impl(ghcjs) 41 | build-depends: 42 | servant-websockets, 43 | aeson, 44 | hs-source-dirs: src 45 | default-language: Haskell2010 46 | ghc-options: 47 | -Weverything 48 | -Wno-implicit-prelude 49 | -Wno-missing-safe-haskell-mode 50 | -Wno-safe 51 | -Wno-prepositive-qualified-module 52 | -Wno-missing-import-lists 53 | -Wno-all-missed-specialisations 54 | -Wno-missing-local-signatures 55 | -Wno-partial-fields 56 | -Wno-unsafe 57 | -Wno-monomorphism-restriction 58 | -Wno-missed-specialisations 59 | -------------------------------------------------------------------------------- /octopod-api/src/Octopod/API/WebSocket.hs: -------------------------------------------------------------------------------- 1 | {- ORMOLU_DISABLE -} 2 | {-# LANGUAGE CPP #-} 3 | 4 | module Octopod.API.WebSocket 5 | ( ApiWSPath, 6 | #ifndef ghcjs_HOST_OS 7 | WebSocketAPI, 8 | #endif 9 | ) 10 | where 11 | 12 | #ifndef ghcjs_HOST_OS 13 | 14 | import Data.Aeson 15 | import Servant.API 16 | import Servant.API.WebSocketConduit 17 | 18 | #endif 19 | 20 | type ApiWSPath = "event" 21 | 22 | #ifndef ghcjs_HOST_OS 23 | 24 | -- | WS API 25 | type WebSocketAPI = ApiWSPath :> WebSocketSource Value 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /octopod-backend/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2020 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /octopod-backend/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | 3 | main = defaultMain 4 | -------------------------------------------------------------------------------- /octopod-backend/app/Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | 3 | module Main (main) where 4 | 5 | import qualified Data.Text as T 6 | import Development.GitRev 7 | import Octopod.Server 8 | 9 | main :: IO () 10 | main = runOctopodServer (T.pack $gitHash) 11 | -------------------------------------------------------------------------------- /octopod-backend/src/Control/Octopod/DeploymentLock.hs: -------------------------------------------------------------------------------- 1 | module Control.Octopod.DeploymentLock 2 | ( LockedDeployments, 3 | initLockedDeployments, 4 | withLockedDeployment, 5 | isDeploymentLocked, 6 | ) 7 | where 8 | 9 | import Common.Types 10 | import Control.Arrow 11 | import Control.Exception.Lifted 12 | import Control.Monad.Base 13 | import Control.Monad.Reader 14 | import Control.Monad.Trans.Control 15 | import Data.Generics.Product.Typed 16 | import Data.IORef.Lifted 17 | import Data.Set (Set) 18 | import qualified Data.Set as S 19 | 20 | newtype LockedDeployments = LockedDeployments (IORef (Set DeploymentName)) 21 | 22 | initLockedDeployments :: MonadBase IO m => m LockedDeployments 23 | initLockedDeployments = LockedDeployments <$> newIORef S.empty 24 | 25 | withLockedDeployment :: 26 | ( MonadReader r m 27 | , HasType LockedDeployments r 28 | , MonadBaseControl IO m 29 | ) => 30 | DeploymentName -> 31 | -- | The conflict handler. Gets called if the deployment is 32 | -- already being processed. 33 | m a -> 34 | -- | The actions to be performed when the deployment is locked. 35 | m a -> 36 | m a 37 | withLockedDeployment dName conflict act = do 38 | LockedDeployments ref <- asks getTyped 39 | bracket 40 | (atomicModifyIORef' ref (S.insert dName &&& S.notMember dName)) 41 | (\ok -> when ok $ atomicModifyIORef' ref (S.delete dName &&& const ())) 42 | (\ok -> if ok then act else conflict) 43 | 44 | isDeploymentLocked :: 45 | (MonadReader r m, HasType LockedDeployments r, MonadBase IO m) => 46 | DeploymentName -> 47 | m Bool 48 | isDeploymentLocked dName = do 49 | LockedDeployments ref <- asks getTyped 50 | S.member dName <$> readIORef ref 51 | -------------------------------------------------------------------------------- /octopod-backend/src/Octopod/PowerAPI/Auth/Server.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | module Octopod.PowerAPI.Auth.Server 4 | ( AuthHeader (..), 5 | ) 6 | where 7 | 8 | import Data.ByteString (ByteString) 9 | import Network.Wai 10 | import Octopod.PowerAPI 11 | import Servant.Auth.Server 12 | import Servant.Auth.Server.Internal.Class 13 | 14 | newtype AuthHeader = AuthHeader ByteString 15 | 16 | instance IsAuth AuthHeaderAuth () where 17 | type AuthArgs AuthHeaderAuth = '[AuthHeader] 18 | runAuth _ _ (AuthHeader h) = AuthCheck $ \req -> 19 | pure $ case lookup "Authorization" $ requestHeaders req of 20 | Just v | v == h -> Authenticated () 21 | _ -> Indefinite 22 | 23 | deriving anyclass instance ToJWT () 24 | -------------------------------------------------------------------------------- /octopod-backend/src/Octopod/Server/Args.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | --Module : Octopod.Server.Args 3 | --Description : Octopod Server arguments parser utils. 4 | module Octopod.Server.Args 5 | ( Args (..), 6 | OctopodOpts (..), 7 | parseArgs, 8 | ) 9 | where 10 | 11 | import Control.Monad.IO.Class 12 | import Data.ByteString (ByteString) 13 | import Data.Coerce 14 | import Options.Generic 15 | 16 | import Types 17 | 18 | -- | Octopod Server arguments definition. 19 | data Args = Args 20 | { -- | port for octo CLI 21 | port :: Int 22 | , -- | port for Web UI (HTTP) 23 | uiPort :: Int 24 | , -- | port for Web UI (WS) 25 | wsPort :: Int 26 | , -- | database connection string 27 | db :: ByteString 28 | , -- | database pool size 29 | dbPoolSize :: Int 30 | } 31 | deriving stock (Generic, Show) 32 | 33 | instance ParseRecord Args where 34 | parseRecord = parseRecordWithModifiers lispCaseModifiers 35 | 36 | -- | Parsed Octopod Server arguments definition. 37 | data OctopodOpts = OctopodOpts 38 | { -- | port for Octopod Server 39 | octopodPort :: ServerPort 40 | , -- | port for UI (HTTP) 41 | octopodUIPort :: ServerPort 42 | , -- | port for UI (WS) 43 | octopodWSPort :: ServerPort 44 | , -- | database connection string 45 | octopodDB :: DBConnectionString 46 | , -- | database pool size 47 | octopodDBPoolSize :: DBPoolSize 48 | } 49 | deriving stock (Show) 50 | 51 | -- | Parses Octopod Server arguments. 52 | parseArgs :: MonadIO m => m OctopodOpts 53 | parseArgs = do 54 | args <- getRecord "Octopod.Server" 55 | pure $ 56 | OctopodOpts 57 | (coerce $ port args) 58 | (coerce $ uiPort args) 59 | (coerce $ wsPort args) 60 | (coerce $ db args) 61 | (coerce $ dbPoolSize args) 62 | -------------------------------------------------------------------------------- /octopod-backend/src/Octopod/Server/Posix.hs: -------------------------------------------------------------------------------- 1 | module Octopod.Server.Posix (installShutdownHandler) where 2 | 3 | import Control.Monad.Trans.Control 4 | import Data.Aeson 5 | import Data.Traversable 6 | import Octopod.Server.Logging 7 | import System.Posix.Signals 8 | 9 | newtype SigContext = SigContext Signal 10 | 11 | instance ToObject SigContext where 12 | toObject (SigContext signal) = "signal" .= show signal 13 | 14 | instance LogItem SigContext where 15 | payloadKeys _ _ = AllKeys 16 | 17 | -- | Installs the given shutdown handler for the specified signals. 18 | installShutdownHandler :: 19 | (KatipContext m, MonadBaseControl IO m, StM m () ~ ()) => 20 | [Signal] -> 21 | m () -> 22 | m [Handler] 23 | installShutdownHandler signals action = 24 | for signals $ \signal -> liftBaseWith $ \run -> 25 | installHandler signal (Catch $ run (handler signal)) Nothing 26 | where 27 | handler signal = katipAddNamespace "signal handler" $ 28 | katipAddContext (SigContext signal) $ do 29 | logLocM InfoS "Shutdown initiated by signal" 30 | action 31 | -------------------------------------------------------------------------------- /octopod-backend/src/Orphans.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -fno-warn-orphans #-} 2 | 3 | module Orphans () where 4 | 5 | import Common.Types 6 | import Data.Maybe 7 | import Rel8 8 | 9 | parseTypeInformationFromMapping :: (Eq a, Eq b, DBType b, Show b, Show a) => [(a, b)] -> TypeInformation a 10 | parseTypeInformationFromMapping m = 11 | parseTypeInformation 12 | (\v -> maybe (Left $ "unknown value: " <> show v) Right . flip lookup reversedM $ v) 13 | (\v -> fromMaybe (error $ "forgot case: " <> show v) . flip lookup m $ v) 14 | typeInformation 15 | where 16 | reversedM = (\(x, y) -> (y, x)) <$> m 17 | 18 | deriving via JSONBEncoded (Overrides l) instance (DBType (Overrides l)) 19 | 20 | deriving newtype instance DBType DeploymentId 21 | deriving newtype instance DBEq DeploymentId 22 | 23 | deriving newtype instance DBType DeploymentName 24 | deriving newtype instance DBEq DeploymentName 25 | 26 | instance DBType Action where 27 | typeInformation = parseTypeInformationFromMapping actionText 28 | 29 | deriving newtype instance DBType ArchivedFlag 30 | 31 | deriving newtype instance DBType Duration 32 | 33 | deriving newtype instance DBType Timestamp 34 | 35 | deriving newtype instance DBType ProjectName 36 | 37 | deriving anyclass instance DBEq DeploymentStatus 38 | instance DBType DeploymentStatus where 39 | typeInformation = parseTypeInformationFromMapping deploymentStatusText 40 | 41 | deriving via JSONBEncoded DeploymentMetadata instance DBType DeploymentMetadata 42 | 43 | deriving newtype instance DBType Stdout 44 | deriving newtype instance DBType Stderr 45 | 46 | deriving newtype instance DBType ActionId 47 | deriving newtype instance DBEq ActionId 48 | -------------------------------------------------------------------------------- /octopod-common/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Revision history for common 2 | 3 | ## 0.1.0.0 -- YYYY-mm-dd 4 | 5 | * First version. Released on an unsuspecting world. 6 | -------------------------------------------------------------------------------- /octopod-common/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Typeable LLC 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Typeable LLC nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /octopod-common/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | 3 | main = defaultMain 4 | -------------------------------------------------------------------------------- /octopod-common/src/Common/Orphans.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-orphans #-} 2 | 3 | module Common.Orphans () where 4 | 5 | import Control.DeepSeq 6 | import Data.Aeson 7 | import Data.Map.Internal 8 | import Data.Map.Ordered.Internal 9 | import qualified Data.Map.Ordered.Strict as OM 10 | import GHC.Generics (Generic) 11 | 12 | deriving stock instance Generic (Map k v) 13 | 14 | deriving stock instance Generic (OMap k v) 15 | deriving anyclass instance (NFData k, NFData v) => NFData (OMap k v) 16 | instance (ToJSON k, ToJSON v) => ToJSON (OMap k v) where 17 | toJSON = toJSON . OM.assocs 18 | 19 | instance (FromJSON k, FromJSON v, Ord k) => FromJSON (OMap k v) where 20 | parseJSON = fmap OM.fromList . parseJSON 21 | -------------------------------------------------------------------------------- /octopod-common/src/Common/Utils.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | --Module : Common.Utils 3 | --Description : Common utils for backend and frontend. 4 | -- 5 | --This module contains common utils between the backend and the frontend. 6 | module Common.Utils 7 | ( (<^.>), 8 | (<^?>), 9 | (<^..>), 10 | dfiName, 11 | isPending, 12 | ) 13 | where 14 | 15 | import Control.Lens 16 | import Data.Generics.Product 17 | import Data.Monoid 18 | 19 | import Common.Types 20 | 21 | -- Lens convenience helpers 22 | (<^.>) :: Functor f => f a -> Getting b a b -> f b 23 | (<^.>) fa l = fa <&> (^. l) 24 | 25 | (<^?>) :: Functor f => f a -> Getting (First b) a b -> f (Maybe b) 26 | (<^?>) fa l = fa <&> (^? l) 27 | 28 | (<^..>) :: Functor f => f a -> Getting (Endo [b]) a b -> f [b] 29 | (<^..>) fa t = fa <&> (^.. t) 30 | 31 | infixl 8 <^.>, <^..>, <^?> 32 | 33 | -- | Gets name from deployment full info. 34 | dfiName :: 35 | Functor f => 36 | (DeploymentName -> f DeploymentName) -> 37 | DeploymentFullInfo -> 38 | f DeploymentFullInfo 39 | dfiName = field @"deployment" . field' @"name" 40 | 41 | -- | Checks that deployment status is pending. 42 | isPending :: DeploymentStatus -> Bool 43 | isPending = \case 44 | Running -> False 45 | Failure _ -> False 46 | Archived -> False 47 | CreatePending -> True 48 | UpdatePending -> True 49 | ArchivePending -> True 50 | CleanupFailed -> True 51 | -------------------------------------------------------------------------------- /octopod-common/src/Common/Validation.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | --Module : Common.Validation 3 | --Description : Common validations for backend and frontend. 4 | -- 5 | --This module contains common validations between the backend and the frontend. 6 | module Common.Validation 7 | ( isNameValid, 8 | ) 9 | where 10 | 11 | import Common.Types 12 | import Data.Char 13 | import qualified Data.Text as T 14 | 15 | -- | Validates a deployment name. 16 | isNameValid :: DeploymentName -> Bool 17 | isNameValid (DeploymentName (T.uncons -> Just (n, nn))) = 18 | let l = T.length nn 19 | in l > 0 && l < 16 && isAsciiLower n && T.all (\c -> c == '-' || isAsciiLower c || isDigit c) nn 20 | isNameValid _ = False 21 | -------------------------------------------------------------------------------- /octopod-css/default.nix: -------------------------------------------------------------------------------- 1 | { pkgsSrc ? (import ./.. { }).pkgsSrc 2 | , sources ? import ../nix/sources.nix 3 | , nix-filter ? import sources.nix-filter 4 | , system ? builtins.currentSystem 5 | }: 6 | let 7 | pkgs = pkgsSrc { 8 | overlays = [ (self: super: { nodejs = self.nodejs-10_x; }) ]; 9 | inherit system; 10 | }; 11 | production-css = pkgs.mkYarnPackage { 12 | name = "octopod-css"; 13 | src = nix-filter { 14 | root = ./.; 15 | name = "octopod-css"; 16 | include = with nix-filter; [ 17 | (inDirectory ./development) 18 | (inDirectory ./favicons) 19 | ./gulpfile.js 20 | ./package.json 21 | ./.stylelintrc.json 22 | ]; 23 | }; 24 | yarnLock = ./yarn.lock; 25 | buildPhase = "yarn run gulp"; 26 | doDist = false; 27 | distPhase = " "; 28 | installPhase = '' 29 | runHook preInstall 30 | 31 | cp -av deps/octopod-css/production $out 32 | 33 | runHook postInstall 34 | ''; 35 | }; 36 | in 37 | pkgs.runCommand "octopod-production-assets" 38 | { } '' 39 | mkdir $out 40 | cp -av ${./.}/favicons/* $out 41 | mkdir $out/static 42 | cp -av ${production-css}/images $out/static 43 | cp -av ${production-css}/styles $out/static 44 | cp -av ${production-css}/vendors $out/static 45 | '' 46 | -------------------------------------------------------------------------------- /octopod-css/development/images/dialog-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/dialog-delete.png -------------------------------------------------------------------------------- /octopod-css/development/images/dialog-delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/dialog-delete@2x.png -------------------------------------------------------------------------------- /octopod-css/development/images/null-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/null-data.png -------------------------------------------------------------------------------- /octopod-css/development/images/null-data@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/null-data@2x.png -------------------------------------------------------------------------------- /octopod-css/development/images/null-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/null-search.png -------------------------------------------------------------------------------- /octopod-css/development/images/null-search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typeable/octopod/13098523fefac65725f0becaa4c0321f32ccd07f/octopod-css/development/images/null-search@2x.png -------------------------------------------------------------------------------- /octopod-css/development/layouts/standalone-loading.html: -------------------------------------------------------------------------------- 1 | 37 | 38 |
39 | 40 | 41 | 42 | Loading... 43 |
44 | -------------------------------------------------------------------------------- /octopod-css/development/markups/_bar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | .bar 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |

.bar

21 | 22 | 23 |
24 | ENVIRONENT: production 25 |
26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /octopod-css/development/markups/_external.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | .external 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |

.external

21 | 22 | 23 | 24 | app.feature-name.project.name.com lorem ipsume dolor sit amen 25 | 26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /octopod-css/development/markups/_spot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | .spot 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |

.spot

21 | 22 | 25 | 26 | 27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /octopod-css/development/markups/_title.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | .title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |

.title

21 | 22 | 23 | 24 |

All deployments

25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_collapse.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | var animationTime = 300; 4 | 5 | $('.collapse__head').on('click', function () { 6 | var $collapse = $(this).parent('.collapse'); 7 | 8 | if( ! $collapse.hasClass('collapse--expanded') ) { 9 | $collapse.addClass('collapse--expanded'); 10 | $collapse.children('.collapse__body').slideDown(animationTime); 11 | } else { 12 | $collapse.children('.collapse__body').slideUp(animationTime); 13 | $collapse.removeClass('collapse--expanded'); 14 | } 15 | 16 | }); 17 | 18 | })(jQuery); 19 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_data.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | $('.data__show-archive').on('click', function () { 4 | $('.data__archive').toggleClass('data__archive--open'); 5 | }); 6 | 7 | })(jQuery); 8 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_drop.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | $('.drop__handler').on('click', function (event) { 4 | $(this).parents('.drop').toggleClass('drop--expanded'); 5 | event.stopPropagation(); 6 | }); 7 | 8 | 9 | /* Закрываем по клику вне ( goo.gl/SJG2Hw ) */ 10 | 11 | $(document).on('click', function(event) { 12 | if (!$(event.target).closest('.drop').length) { 13 | $('.drop--expanded').removeClass('drop--expanded'); 14 | } 15 | }); 16 | 17 | 18 | /* Закрываем по Esc */ 19 | 20 | $(document).on('keyup', function(event) { 21 | if (event.keyCode === 27) { 22 | $('.drop--expanded').removeClass('drop--expanded'); 23 | } 24 | }); 25 | 26 | })(jQuery); 27 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_expander.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | $('.expander').on('click', function (event) { 4 | $(this).toggleClass('expander--open'); 5 | event.stopPropagation(); 6 | }); 7 | 8 | })(jQuery); 9 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_popup.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | $('.popup-handler').on('click', function (event) { 4 | event.preventDefault(); 5 | var popupId = $(this).attr('href') ? $(this).attr('href') : $(this).attr('data-href') 6 | $( popupId ).addClass('popup--visible'); 7 | event.stopPropagation(); 8 | }); 9 | 10 | 11 | /* Закрываем по крестику */ 12 | 13 | $('.popup__close').on('click', function(event){ 14 | event.preventDefault(); 15 | $(this).parents('.popup').removeClass('popup--visible'); 16 | }); 17 | 18 | 19 | /* Закрываем по клику вне ( goo.gl/SJG2Hw ) */ 20 | 21 | $('.popup__overlay').on('click', function(event) { 22 | $('.popup--visible').removeClass('popup--visible'); 23 | }); 24 | 25 | 26 | /* Закрываем по Esc */ 27 | 28 | $(document).on('keyup', function(event) { 29 | if (event.keyCode === 27) { 30 | $('.popup--visible').removeClass('popup--visible'); 31 | } 32 | }); 33 | 34 | 35 | })(jQuery); 36 | -------------------------------------------------------------------------------- /octopod-css/development/scripts/_sort.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | $('.sort').on('click', function () { 4 | if( ! $(this).hasClass('sort--active') ) { 5 | $('.sort--active').removeClass('sort--active').removeClass('sort--desc').removeClass('sort--asc'); 6 | $(this).addClass('sort--active').addClass('sort--desc'); 7 | } else { 8 | if( $(this).hasClass('sort--asc') ) { 9 | $(this).removeClass('sort--asc').addClass('sort--desc'); 10 | } else if( $(this).hasClass('sort--desc') ) { 11 | $(this).removeClass('sort--desc').addClass('sort--asc'); 12 | } 13 | } 14 | }); 15 | 16 | })(jQuery); 17 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_action.css: -------------------------------------------------------------------------------- 1 | .action { 2 | display: inline-block; 3 | vertical-align: top; 4 | width: 100%; 5 | border: none; 6 | padding: 16px 32px 16px 16px; 7 | background-color: transparent; 8 | background-repeat: no-repeat; 9 | background-position: 16px center; 10 | font-family: inherit; 11 | font-size: 14px; 12 | line-height: 16px; 13 | color: #000000; 14 | white-space: nowrap; 15 | text-align: left; 16 | text-decoration: none; 17 | cursor: pointer; 18 | } 19 | 20 | .action:not(.action--disabled):hover { 21 | background-color: var(--grey); 22 | } 23 | 24 | .action:not(.action--disabled):active { 25 | background-color: var(--grey-active); 26 | } 27 | 28 | 29 | 30 | 31 | .action--archive { 32 | padding-left: 50px; 33 | background-image: url("../vectors/common-use-archive.svg"); 34 | } 35 | 36 | .action--edit { 37 | padding-left: 50px; 38 | background-image: url("../vectors/common-use-edit.svg"); 39 | } 40 | 41 | .action--restore { 42 | padding-left: 50px; 43 | background-image: url("../vectors/common-use-restore.svg"); 44 | } 45 | 46 | .action--logs { 47 | padding-left: 50px; 48 | background-image: url("../vectors/common-use-logs.svg"); 49 | } 50 | 51 | 52 | .action--disabled { 53 | opacity: 0.5; 54 | cursor: default; 55 | } 56 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_bar.css: -------------------------------------------------------------------------------- 1 | .bar { 2 | display: inline-block; 3 | vertical-align: top; 4 | padding: 4px 6px; 5 | background-color: #EEEEEE; 6 | border-radius: 4px; 7 | font-size: 12px; 8 | line-height: 16px; 9 | max-width: 100%; 10 | white-space: nowrap; 11 | overflow: hidden; 12 | text-overflow: ellipsis; 13 | } 14 | .bar b { 15 | font-weight: 500; 16 | } 17 | 18 | 19 | 20 | .bar--larger { 21 | padding: 4px 8px; 22 | font-size: 16px; 23 | line-height: 24px; 24 | } 25 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_body.css: -------------------------------------------------------------------------------- 1 | body { 2 | height: 100%; 3 | background-color: #FFFFFF; 4 | font-family: "Roboto", sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_break.css: -------------------------------------------------------------------------------- 1 | .break { 2 | word-break: break-all; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_browserupgrade.css: -------------------------------------------------------------------------------- 1 | .browserupgrade { 2 | background: #CCCCCC; 3 | color: #000000; 4 | padding: 4px 10px; 5 | } -------------------------------------------------------------------------------- /octopod-css/development/styles/_classic-popup.css: -------------------------------------------------------------------------------- 1 | .html-lock { 2 | overflow: hidden; 3 | } 4 | 5 | .classic-popup { 6 | position: fixed; 7 | z-index: 800; 8 | left: 0; 9 | top: 0; 10 | width: 100%; 11 | height: 100%; 12 | overflow: auto; 13 | background: rgba(0, 0, 0, 0.5); 14 | display: none; 15 | } 16 | 17 | .classic-popup__container { 18 | display: table; 19 | table-layout: fixed; 20 | width: 100%; 21 | height: 100%; 22 | } 23 | 24 | .classic-popup__viewport { 25 | display: table-cell; 26 | width: 100%; 27 | height: 100%; 28 | text-align: center; 29 | vertical-align: middle; 30 | } 31 | 32 | .classic-popup__slot { 33 | position: relative; 34 | display: inline-block; 35 | vertical-align: top; 36 | text-align: left; 37 | margin: 20px; 38 | } 39 | 40 | .classic-popup__close { 41 | position: absolute; 42 | right: 16px; 43 | top: 16px; 44 | width: 40px; 45 | height: 40px; 46 | background-color: transparent; 47 | background-image: url("../vectors/classic-popup-close.svg"); 48 | background-size: 24px 24px; 49 | background-repeat: no-repeat; 50 | background-position: center center; 51 | padding: 0; 52 | border: none; 53 | text-indent: 200%; 54 | white-space: nowrap; 55 | overflow: hidden; 56 | cursor: pointer; 57 | } 58 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_container.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-left: 24px; 3 | padding-right: 24px; 4 | } 5 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_dash.css: -------------------------------------------------------------------------------- 1 | .dash { 2 | padding: 12px 0 12px 24px; 3 | background-color: transparent; 4 | background-size: 16px 16px; 5 | background-repeat: no-repeat; 6 | background-position: left center; 7 | border: none; 8 | font-family: inherit; 9 | font-weight: 500; 10 | font-size: 14px; 11 | line-height: 16px; 12 | letter-spacing: 0.4px; 13 | text-decoration: none; 14 | text-transform: uppercase; 15 | color: var(--primary); 16 | cursor: pointer; 17 | } 18 | 19 | .dash:hover { 20 | color: var(--primary-hover); 21 | } 22 | 23 | .dash:active { 24 | color: var(--primary-active); 25 | } 26 | 27 | 28 | .dash--smaller { 29 | font-size: 12px; 30 | padding-left: 20px; 31 | } 32 | 33 | /* stylelint-disable block-opening-brace-space-before */ 34 | .dash--add { background-image: url("../vectors/dash-add.svg"); } 35 | .dash--add:not(.dash--disabled):hover { background-image: url("../vectors/dash-add-hover.svg"); } 36 | .dash--add:not(.dash--disabled):active { background-image: url("../vectors/dash-add-active.svg"); } 37 | 38 | .dash--back { background-image: url("../vectors/dash-back.svg"); } 39 | .dash--back:not(.dash--disabled):hover { background-image: url("../vectors/dash-back-hover.svg"); } 40 | .dash--back:not(.dash--disabled):active { background-image: url("../vectors/dash-back-active.svg"); } 41 | 42 | .dash--next { 43 | padding-left: 0; 44 | padding-right: 20px; 45 | background-position: right center; 46 | } 47 | 48 | .dash--next { background-image: url("../vectors/dash-next.svg"); } 49 | .dash--next:not(.dash--disabled):hover { background-image: url("../vectors/dash-next-hover.svg"); } 50 | .dash--next:not(.dash--disabled):active { background-image: url("../vectors/dash-next-active.svg"); } 51 | /* stylelint-enable */ 52 | 53 | 54 | .dash--disabled { 55 | opacity: 0.5; 56 | cursor: default; 57 | } 58 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_data.css: -------------------------------------------------------------------------------- 1 | .data { 2 | flex-shrink: 0; 3 | } 4 | .data__primary { 5 | 6 | } 7 | 8 | .data__show-archive { 9 | margin-top: 18px; 10 | align-self: flex-start; 11 | } 12 | 13 | .data__archive { 14 | display: none; 15 | } 16 | 17 | .data__archive--open { 18 | display: block; 19 | } 20 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_deployment.css: -------------------------------------------------------------------------------- 1 | .deployment { 2 | padding: 24px; 3 | margin-bottom: 274px; 4 | } 5 | 6 | .page__body .deployment { 7 | padding: 0; 8 | } 9 | 10 | .deployment__output { 11 | margin-bottom: 32px; 12 | } 13 | 14 | .deployment__summary { 15 | display: flex; 16 | align-self: flex-start; 17 | } 18 | .deployment__stat { 19 | min-width: 120px; 20 | margin-left: 32px; 21 | } 22 | 23 | .deployment__stat:first-child { 24 | margin-left: 0; 25 | } 26 | .deployment__param { 27 | display: block; 28 | margin-top: 0; 29 | margin-bottom: 0; 30 | font-weight: 500; 31 | font-size: 14px; 32 | line-height: 16px; 33 | } 34 | 35 | .deployment__value { 36 | margin-top: 8px; 37 | font-size: 16px; 38 | line-height: 24px; 39 | } 40 | .deployment__value .status { 41 | border-top: 4px solid transparent; 42 | border-bottom: 4px solid transparent; 43 | } 44 | 45 | .deployment__section { 46 | margin-top: 32px; 47 | } 48 | .deployment__sub-heading { 49 | margin-top: 0; 50 | margin-bottom: 0; 51 | font-weight: 500; 52 | font-size: 14px; 53 | line-height: 16px; 54 | } 55 | 56 | .deployment__widget { 57 | margin-top: 16px; 58 | font-size: 16px; 59 | line-height: 24px; 60 | } 61 | .deployment__widget .row:first-child { 62 | margin-top: 0; 63 | } 64 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_dialog.css: -------------------------------------------------------------------------------- 1 | .dialog { 2 | width: 400px; 3 | padding: 40px 24px; 4 | background-color: #FFFFFF; 5 | border-radius: 16px; 6 | } 7 | 8 | .dialog--archive::before { 9 | content: ""; 10 | display: block; 11 | width: 185px; 12 | height: 120px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | background-image: url("../images/dialog-delete.png"); 16 | background-size: contain; 17 | background-repeat: no-repeat; 18 | background-position: center center; 19 | } 20 | 21 | @media (min-resolution: 2dppx) { 22 | 23 | .dialog--archive::before { 24 | background-image: url("../images/dialog-delete@2x.png"); 25 | } 26 | } 27 | .dialog__content { 28 | margin-top: 25px; 29 | font-weight: bold; 30 | font-size: 16px; 31 | line-height: 20px; 32 | text-align: center; 33 | } 34 | 35 | .dialog__footer { 36 | margin-top: 16px; 37 | display: flex; 38 | align-items: flex-start; 39 | justify-content: center; 40 | } 41 | .dialog__action { 42 | margin-left: 8px; 43 | } 44 | 45 | .dialog__action:first-child { 46 | margin-left: 0; 47 | } 48 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_drop.css: -------------------------------------------------------------------------------- 1 | .drop { 2 | position: relative; 3 | display: inline-block; 4 | vertical-align: top; 5 | } 6 | .drop__handler { 7 | border: none; 8 | padding: 0; 9 | background-color: transparent; 10 | cursor: pointer; 11 | } 12 | 13 | .drop__dropdown { 14 | position: absolute; 15 | z-index: 500; 16 | right: 0; 17 | top: 100%; 18 | min-width: 100%; 19 | opacity: 0; 20 | visibility: hidden; 21 | transition: opacity var(--transition-basic), 22 | visibility var(--transition-basic); 23 | } 24 | 25 | 26 | 27 | .drop--expanded { 28 | 29 | } 30 | .drop--expanded .drop__dropdown { 31 | opacity: 1; 32 | visibility: visible; 33 | } 34 | 35 | 36 | 37 | 38 | .drop--actions { 39 | 40 | } 41 | .drop--actions .drop__handler { 42 | width: 40px; 43 | height: 40px; 44 | background-image: url("../vectors/drop-actions.svg"); 45 | background-size: 24px 24px; 46 | background-repeat: no-repeat; 47 | background-position: center center; 48 | text-indent: 200%; 49 | white-space: nowrap; 50 | overflow: hidden; 51 | } 52 | 53 | .drop--actions .drop__handler:hover { 54 | background-image: url("../vectors/drop-actions-hover.svg"); 55 | } 56 | 57 | .drop--actions .drop__handler:active, 58 | .drop--actions.drop--expanded .drop__handler { 59 | background-image: url("../vectors/drop-actions-active.svg"); 60 | } 61 | 62 | .drop--actions .drop__dropdown { 63 | margin-top: 8px; 64 | margin-right: -17px; 65 | padding-top: 8px; 66 | padding-bottom: 8px; 67 | background-color: #FFFFFF; 68 | box-shadow: var(--shadow_1); 69 | border-radius: 2px; 70 | } 71 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_editable-row.css: -------------------------------------------------------------------------------- 1 | .editable-row { 2 | position: relative; 3 | margin-top: 8px; 4 | display: flex; 5 | align-self: flex-start; 6 | } 7 | 8 | .editable-row:first-child { 9 | margin-top: 0; 10 | } 11 | .editable-row__key { 12 | width: 400px; 13 | } 14 | 15 | .editable-row__value { 16 | margin-left: 16px; 17 | flex-grow: 1; 18 | } 19 | 20 | .editable-row__delete { 21 | margin-left: 2px; 22 | } 23 | 24 | 25 | .editable-row__placeholder { 26 | position: relative; 27 | width: 400px; 28 | height: 40px; 29 | padding: 14px 0; 30 | box-shadow: inset 0 -1px 0 rgba(176, 176, 176, 0.2); 31 | } 32 | 33 | .editable-row__placeholder::before { 34 | position: relative; 35 | content: ""; 36 | display: block; 37 | height: 12px; 38 | width: 350px; 39 | border-radius: 2px; 40 | background: rgba(238, 238, 238, 0.5); 41 | } 42 | 43 | .editable-row__placeholder:first-child { 44 | margin-right: 16px; 45 | } 46 | 47 | .editable-row__placeholder:first-child::before { 48 | width: 200px; 49 | } 50 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_external.css: -------------------------------------------------------------------------------- 1 | .external { 2 | padding-right: 24px; 3 | background-image: url("../vectors/external.svg"); 4 | background-size: 12px 12px; 5 | background-repeat: no-repeat; 6 | background-position: right 6px center; 7 | color: inherit; 8 | text-decoration: none; 9 | cursor: pointer; 10 | } 11 | 12 | .external:hover { 13 | color: var(--primary); 14 | background-color: #E2E2E2; 15 | background-image: url("../vectors/external-hover-active.svg"); 16 | } 17 | 18 | .external:active { 19 | color: var(--primary); 20 | background-color: #D6D6D6; 21 | background-image: url("../vectors/external-hover-active.svg"); 22 | } 23 | 24 | .external.bar--larger { 25 | background-position: right 8px center; 26 | } 27 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_has-changes.css: -------------------------------------------------------------------------------- 1 | .has-changes { 2 | text-decoration: underline; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | padding-top: 24px; 3 | padding-bottom: 24px; 4 | background-color: #3671E3; 5 | box-shadow: var(--shadow_1); 6 | color: #FFFFFF; 7 | } 8 | .header__wrap { 9 | display: flex; 10 | align-items: center; 11 | } 12 | .header__logo { 13 | margin-top: 0; 14 | margin-bottom: 0; 15 | border-right: 1px solid #709AEE; 16 | padding-right: 15px; 17 | font-weight: bold; 18 | font-size: 20px; 19 | line-height: 24px; 20 | letter-spacing: 0.85px; 21 | text-transform: uppercase; 22 | } 23 | 24 | .header__project { 25 | margin-left: -1px; 26 | border-left: 1px solid #709AEE; 27 | padding-left: 16px; 28 | font-weight: 500; 29 | font-size: 16px; 30 | line-height: 24px; 31 | } 32 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_html.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | min-width: 1000px; 4 | } 5 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_key-custom-edited.css: -------------------------------------------------------------------------------- 1 | .key-custom-edited { 2 | font-weight: 500 !important; 3 | text-decoration: underline !important; 4 | } 5 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_key-default-edited.css: -------------------------------------------------------------------------------- 1 | .key-default-edited { 2 | font-weight: 300 !important; 3 | font-style: italic !important; 4 | text-decoration: underline !important; 5 | } 6 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_key-default-pristine.css: -------------------------------------------------------------------------------- 1 | .key-default-pristine { 2 | font-weight: 300 !important; 3 | font-style: italic !important; 4 | } 5 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_key-deleted.css: -------------------------------------------------------------------------------- 1 | .key-deleted { 2 | font-weight: 300 !important; 3 | font-style: italic !important; 4 | text-decoration: line-through; 5 | color: #D3D3D3; 6 | } 7 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_log.css: -------------------------------------------------------------------------------- 1 | .log { 2 | margin-top: 0; 3 | margin-bottom: 0; 4 | display: block; 5 | padding: 24px; 6 | font-family: "Roboto Mono", monospace; 7 | font-size: 12px; 8 | line-height: 16px; 9 | word-break: break-all; 10 | } 11 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_nav.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | display: inline-flex; 3 | align-items: flex-start; 4 | } 5 | .nav__link { 6 | position: relative; 7 | display: inline-block; 8 | vertical-align: top; 9 | background-color: transparent; 10 | border: none; 11 | padding: 21px 16px 19px; 12 | font-family: inherit; 13 | font-size: 14px; 14 | line-height: 16px; 15 | letter-spacing: 0.75px; 16 | text-transform: uppercase; 17 | cursor: pointer; 18 | } 19 | 20 | .nav__link:hover { 21 | color: var(--primary-active); 22 | } 23 | 24 | .nav__link--current { 25 | font-weight: 500; 26 | color: var(--primary-active); 27 | } 28 | .nav__link--current::after { 29 | content: ""; 30 | position: absolute; 31 | left: 16px; 32 | right: 16px; 33 | bottom: 0; 34 | display: block; 35 | height: 4px; 36 | border-top-left-radius: 4px; 37 | border-top-right-radius: 4px; 38 | background-color: var(--primary-active); 39 | } 40 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_no-data.css: -------------------------------------------------------------------------------- 1 | .no-data { 2 | flex-grow: 1; 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | padding-top: 50px; 7 | padding-bottom: 40px; 8 | } 9 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_no-deployment.css: -------------------------------------------------------------------------------- 1 | .no-deployment { 2 | height: 100%; 3 | padding-bottom: 72px; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_no-log.css: -------------------------------------------------------------------------------- 1 | .no-log { 2 | height: 100%; 3 | padding-bottom: 72px; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_no-page.css: -------------------------------------------------------------------------------- 1 | .no-page { 2 | display: table; 3 | vertical-align: top; 4 | width: 100%; 5 | height: calc(100% - 72px); /* minus header */ 6 | } 7 | .no-page__inner { 8 | display: table-cell; 9 | vertical-align: middle; 10 | text-align: center; 11 | width: 100%; 12 | height: 100%; 13 | padding-top: 40px; 14 | padding-bottom: 40px; 15 | } 16 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_no-table.css: -------------------------------------------------------------------------------- 1 | .no-table { 2 | background-color: transparent !important; 3 | cursor: auto !important; 4 | } 5 | .no-table td { 6 | vertical-align: middle; 7 | text-align: center; 8 | height: 352px; 9 | padding-top: 50px; 10 | padding-bottom: 50px; 11 | } 12 | .no-table .loading { 13 | display: inline-block !important; /* конфликт со стилями лоадера */ 14 | vertical-align: top !important; /* который вставляется в первый */ 15 | margin-top: 0 !important; /* столбец всместо .status */ 16 | } 17 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_notification.css: -------------------------------------------------------------------------------- 1 | .notification { 2 | position: relative; 3 | padding: 15px 46px 15px 16px; 4 | border: 1px solid; 5 | border-radius: 4px; 6 | font-size: 14px; 7 | line-height: 20px; 8 | } 9 | .notification__close { 10 | position: absolute; 11 | right: 0; 12 | top: 0; 13 | bottom: 0; 14 | width: 46px; 15 | padding: 0; 16 | border: none; 17 | background-color: transparent; 18 | background-size: 24px 24px; 19 | background-repeat: no-repeat; 20 | background-position: center center; 21 | text-indent: 200%; 22 | white-space: nowrap; 23 | overflow: hidden; 24 | cursor: pointer; 25 | } 26 | 27 | .notification--success { 28 | background-color: rgba(15, 127, 18, 0.1); 29 | color: var(--green); 30 | } 31 | .notification--success .notification__close { 32 | background-image: url("../vectors/notification-close-success.svg"); 33 | } 34 | .notification--success:hover .notification__close { 35 | background-image: url("../vectors/notification-close-success-hover.svg"); 36 | } 37 | .notification--success:active .notification__close { 38 | background-image: url("../vectors/notification-close-success-active.svg"); 39 | } 40 | 41 | .notification--danger { 42 | background-color: rgba(212, 42, 42, 0.1); 43 | color: var(--red); 44 | } 45 | .notification--danger .notification__close { 46 | background-image: url("../vectors/notification-close-danger.svg"); 47 | } 48 | .notification--danger:hover .notification__close { 49 | background-image: url("../vectors/notification-close-danger-hover.svg"); 50 | } 51 | .notification--danger:active .notification__close { 52 | background-image: url("../vectors/notification-close-danger-active.svg"); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_null.css: -------------------------------------------------------------------------------- 1 | .null { 2 | display: inline-block; 3 | vertical-align: top; 4 | } 5 | .null::before { 6 | content: ""; 7 | display: block; 8 | margin: 0 auto 22px; 9 | background-size: contain; 10 | background-repeat: no-repeat; 11 | background-position: center center; 12 | } 13 | 14 | .null__heading { 15 | margin-top: 0; 16 | margin-bottom: 0; 17 | display: block; 18 | font-weight: bold; 19 | font-size: 16px; 20 | line-height: 20px; 21 | text-align: center; 22 | } 23 | 24 | .null__message { 25 | margin-top: 14px; 26 | font-size: 14px; 27 | line-height: 16px; 28 | color: var(--dark-grey); 29 | text-align: center; 30 | } 31 | 32 | 33 | .null--data { 34 | padding-bottom: 8px; 35 | } 36 | .null--data::before { 37 | width: 174px; 38 | height: 120px; 39 | background-image: url("../images/null-data.png"); 40 | } 41 | 42 | @media (min-resolution: 2dppx) { 43 | .null--data::before { 44 | background-image: url("../images/null-data@2x.png"); 45 | } 46 | } 47 | 48 | 49 | .null--search { 50 | 51 | } 52 | .null--search::before { 53 | width: 120px; 54 | height: 120px; 55 | background-image: url("../images/null-search.png"); 56 | } 57 | 58 | @media (min-resolution: 2dppx) { 59 | .null--search::before { 60 | background-image: url("../images/null-search@2x.png"); 61 | } 62 | } 63 | 64 | .null--search .null__message { 65 | margin-top: 6px; 66 | } 67 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_overrides.css: -------------------------------------------------------------------------------- 1 | .overrides { 2 | 3 | } 4 | .overrides__add { 5 | padding: 8px 0 8px 24px !important; 6 | } 7 | 8 | .overrides__list { 9 | 10 | } 11 | 12 | .overrides__tree { 13 | margin-top: 16px; 14 | margin-left: 16px; 15 | } 16 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_padded.css: -------------------------------------------------------------------------------- 1 | .padded { 2 | padding-left: 24px; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_page.css: -------------------------------------------------------------------------------- 1 | .page { 2 | display: flex; 3 | flex-direction: column; 4 | height: calc(100% - 72px); /* minus header */ 5 | padding-top: 24px; 6 | } 7 | .page__wrap { 8 | display: flex; 9 | flex-direction: column; 10 | min-height: 100%; 11 | } 12 | .page__output { 13 | flex-shrink: 0; 14 | margin-bottom: 24px; 15 | } 16 | 17 | .page__back { 18 | align-self: flex-start; 19 | margin-top: -8px; 20 | margin-bottom: -8px; 21 | } 22 | 23 | .page__head { 24 | flex-shrink: 0; 25 | display: flex; 26 | align-items: flex-start; 27 | margin-bottom: 16px; 28 | } 29 | .page__heading { 30 | margin-top: 5px; 31 | margin-bottom: 0; 32 | } 33 | 34 | .page__note { 35 | margin-left: 15px; 36 | margin-top: 17px; 37 | font-size: 14px; 38 | line-height: 16px; 39 | color: var(--dark-grey); 40 | } 41 | .page__action { 42 | margin-left: auto; 43 | } 44 | 45 | .page__action + .page__action { 46 | margin-left: 8px; 47 | } 48 | 49 | .page__action:first-child { 50 | margin-left: 0; 51 | } 52 | 53 | .page__action--search { 54 | margin-right: 8px; 55 | } 56 | 57 | .page__search { 58 | margin-left: auto; 59 | width: 240px; 60 | } 61 | 62 | .page__add-deployment { 63 | margin-left: 16px; 64 | } 65 | 66 | .page__body { 67 | flex-grow: 1; 68 | display: flex; 69 | flex-direction: column; 70 | padding-bottom: 24px; 71 | } 72 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_root.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable declaration-colon-space-after */ 2 | :root { 3 | --primary: #3671E3; 4 | --primary-hover: #3A6CCC; 5 | --primary-active: #3964B8; 6 | --primary-disabled: #99B7F3; 7 | 8 | --grey: #EEEEEE; 9 | --grey-hover: #E2E2E2; 10 | --grey-active: #D6D6D6; 11 | 12 | --dark-grey: #757575; 13 | --dark-grey-50pct: #757575; 14 | 15 | --orange: #FC5830; 16 | 17 | --red: #D42A2A; 18 | --green: #0F7F12; 19 | 20 | --black: #212121; 21 | 22 | --shadow_1: 0 2px 2px rgba(0, 0, 0, 0.24), 23 | 0 0 2px rgba(0, 0, 0, 0.12); 24 | 25 | --transition-basic: 0.1s cubic-bezier(0.075, 0.82, 0.165, 1); 26 | } 27 | /* stylelint-enable declaration-colon-space-after */ 28 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_row.css: -------------------------------------------------------------------------------- 1 | .row { 2 | margin-top: 8px; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_sort.css: -------------------------------------------------------------------------------- 1 | .sort { 2 | position: relative; 3 | display: inline-block; 4 | vertical-align: top; 5 | border: none; 6 | background: none; 7 | margin: -8px; 8 | padding: 8px; 9 | font-family: inherit; 10 | font-weight: 500; 11 | font-size: 14px; 12 | line-height: 16px; 13 | text-decoration: underline; 14 | color: #000000; 15 | cursor: pointer; 16 | } 17 | .sort::after { 18 | position: absolute; 19 | right: 12px; 20 | top: 14px; 21 | content: ""; 22 | display: block; 23 | border-left: 4px solid transparent; 24 | border-right: 4px solid transparent; 25 | opacity: 0; 26 | } 27 | 28 | .sort:hover { 29 | color: var(--primary); 30 | } 31 | 32 | .sort:active { 33 | 34 | } 35 | 36 | 37 | .sort--desc { 38 | padding-right: 24px; 39 | } 40 | .sort--desc::after { 41 | border-top: 4px solid; 42 | } 43 | 44 | .sort--active.sort--desc:hover { 45 | 46 | } 47 | .sort--active.sort--desc:hover::after { 48 | 49 | } 50 | 51 | 52 | .sort--asc { 53 | padding-right: 24px; 54 | } 55 | .sort--asc::after { 56 | border-bottom: 4px solid; 57 | } 58 | 59 | .sort--active.sort--asc:hover { 60 | 61 | } 62 | .sort--active.sort--asc:hover::after { 63 | 64 | } 65 | 66 | 67 | 68 | .sort--active { 69 | 70 | } 71 | .sort--active::after { 72 | opacity: 1; 73 | } 74 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_spot.css: -------------------------------------------------------------------------------- 1 | .spot { 2 | display: inline-block; 3 | vertical-align: top; 4 | border: none; 5 | background-color: transparent; 6 | background-repeat: no-repeat; 7 | background-position: center center; 8 | background-size: 24px 24px; 9 | padding: 0; 10 | width: 40px; 11 | height: 40px; 12 | cursor: pointer; 13 | text-indent: 200%; 14 | white-space: nowrap; 15 | overflow: hidden; 16 | opacity: 0.5; 17 | } 18 | 19 | .spot--cancel { 20 | background-image: url("../vectors/spot.svg"); 21 | } 22 | 23 | .spot--cancel:hover { 24 | opacity: 1; 25 | } 26 | 27 | .spot--undo { 28 | opacity: 1; 29 | background-image: url("../vectors/spot-undo.svg"); 30 | } 31 | 32 | .spot--loader { 33 | opacity: 1; 34 | background-image: url("../vectors/spot-loader.svg"); 35 | animation: loading 2.2s linear infinite; 36 | } -------------------------------------------------------------------------------- /octopod-css/development/styles/_status.css: -------------------------------------------------------------------------------- 1 | .status { 2 | background-size: 16px 16px; 3 | background-repeat: no-repeat; 4 | background-position: left center; 5 | padding-left: 21px; 6 | font-weight: bold; 7 | font-size: 12px; 8 | line-height: 16px; 9 | letter-spacing: 0.4px; 10 | text-transform: uppercase; 11 | } 12 | 13 | .status--success { 14 | color: #0F7F12; 15 | background-image: url("../vectors/status-success.svg"); 16 | } 17 | 18 | .status--pending { 19 | color: var(--primary); 20 | background-image: url("../vectors/status-pending.svg"); 21 | } 22 | 23 | .status--failure { 24 | color: var(--orange); 25 | background-image: url("../vectors/status-failure.svg"); 26 | } 27 | 28 | .status--archived { 29 | color: var(--red); 30 | background-image: url("../vectors/status-archived.svg"); 31 | } 32 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_tag.css: -------------------------------------------------------------------------------- 1 | .tag { 2 | width: 400px; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_title.css: -------------------------------------------------------------------------------- 1 | .title { 2 | font-weight: inherit; 3 | font-size: 28px; 4 | line-height: 32px; 5 | } 6 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_universal.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | *::before { 6 | box-sizing: border-box; 7 | } 8 | 9 | *::after { 10 | box-sizing: border-box; 11 | } -------------------------------------------------------------------------------- /octopod-css/development/styles/_value-deleted.css: -------------------------------------------------------------------------------- 1 | .value-deleted { 2 | font-weight: 300 !important; 3 | text-decoration: line-through; 4 | color: #D3D3D3; 5 | } 6 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_value-edited.css: -------------------------------------------------------------------------------- 1 | .value-edited { 2 | font-weight: 500 !important; 3 | text-decoration: underline !important; 4 | } 5 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_value-pristine.css: -------------------------------------------------------------------------------- 1 | .value-pristine { 2 | font-weight: 300 !important; 3 | } 4 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_value-unknown.css: -------------------------------------------------------------------------------- 1 | .value-unknown { 2 | display: inline-block; 3 | vertical-align: baseline; 4 | width: 92px; 5 | height: 0.5715em; 6 | background-color: #F3F3F3; 7 | border-radius: 2px; 8 | margin-right: 4px; 9 | text-indent: 200%; 10 | white-space: nowrap; 11 | overflow: hidden; 12 | } 13 | -------------------------------------------------------------------------------- /octopod-css/development/styles/_visuallyhidden.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Hide only visually, but have it available for screen readers: 3 | * https://snook.ca/archives/html_and_css/hiding-content-for-accessibility 4 | * 5 | * 1. For long content, line feeds are not interpreted as spaces and small width 6 | * causes content to wrap 1 word per line: 7 | * https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe 8 | */ 9 | 10 | .visuallyhidden { 11 | border: 0; 12 | clip: rect(0 0 0 0); 13 | -webkit-clip-path: inset(50%); 14 | clip-path: inset(50%); 15 | height: 1px; 16 | margin: -1px; 17 | overflow: hidden; 18 | padding: 0; 19 | position: absolute; 20 | width: 1px; 21 | white-space: nowrap; /* 1 */ 22 | } 23 | 24 | /* 25 | * Extends the .visuallyhidden class to allow the element 26 | * to be focusable when navigated to via the keyboard: 27 | * https://www.drupal.org/node/897638 28 | */ 29 | 30 | .visuallyhidden.focusable:active, 31 | .visuallyhidden.focusable:focus { 32 | clip: auto; 33 | -webkit-clip-path: none; 34 | clip-path: none; 35 | height: auto; 36 | margin: 0; 37 | overflow: visible; 38 | position: static; 39 | width: auto; 40 | white-space: inherit; 41 | } -------------------------------------------------------------------------------- /octopod-css/development/styles/style.css: -------------------------------------------------------------------------------- 1 | @import url("../vendors/normalize/normalize.css"); 2 | @import url("_body.css"); 3 | @import url("_browserupgrade.css"); 4 | @import url("_button.css"); 5 | @import url("_root.css"); 6 | @import url("_universal.css"); 7 | @import url("_visuallyhidden.css"); 8 | @import url("_container.css"); 9 | @import url("_header.css"); 10 | @import url("_status.css"); 11 | @import url("_bar.css"); 12 | @import url("_external.css"); 13 | @import url("_expander.css"); 14 | @import url("_action.css"); 15 | @import url("_drop.css"); 16 | @import url("_input.css"); 17 | @import url("_spot.css"); 18 | @import url("_title.css"); 19 | @import url("_page.css"); 20 | @import url("_table.css"); 21 | @import url("_listing.css"); 22 | @import url("_sort.css"); 23 | @import url("_notification.css"); 24 | @import url("_null.css"); 25 | @import url("_html.css"); 26 | @import url("_no-page.css"); 27 | @import url("_popup.css"); 28 | @import url("_deployment.css"); 29 | @import url("_tag.css"); 30 | @import url("_overrides.css"); 31 | @import url("_dash.css"); 32 | @import url("_log.css"); 33 | @import url("_no-deployment.css"); 34 | @import url("_data.css"); 35 | @import url("_no-data.css"); 36 | @import url("_classic-popup.css"); 37 | @import url("_dialog.css"); 38 | @import url("_no-table.css"); 39 | @import url("_loading.css"); 40 | @import url("_no-log.css"); 41 | @import url("_nav.css"); 42 | @import url("_break.css"); 43 | @import url("_key-default-pristine.css"); 44 | @import url("_key-default-edited.css"); 45 | @import url("_key-custom-edited.css"); 46 | @import url("_key-deleted.css"); 47 | @import url("_value-pristine.css"); 48 | @import url("_value-edited.css"); 49 | @import url("_value-deleted.css"); 50 | @import url("_value-pristine.css"); 51 | @import url("_value-edited.css"); 52 | @import url("_collapse.css"); 53 | @import url("_row.css"); 54 | @import url("_padded.css"); 55 | @import url("_has-changes.css"); 56 | @import url("_editable-row.css"); 57 | @import url("_value-unknown.css"); 58 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/button-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/button-loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/button-save.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/classic-popup-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/collapse-project.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/common-use-archive.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/common-use-edit-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/common-use-edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/common-use-logs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/common-use-restore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-add-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-add-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-back-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-back-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-next-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-next-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/dash-next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/drop-actions-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/drop-actions-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/drop-actions.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander-larger-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander-larger-hover.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander-larger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/expander.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/external-hover-active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/external.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/input-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/loading-alternate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/loading-enlarged-alternate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/loading-enlarged.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/loading.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-danger-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-danger-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-danger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-success-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-success-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/notification-close-success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/popup-close-active.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/popup-close-hover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/popup-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/spot-loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/spot-undo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/spot.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/status-archived.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/status-failure.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/status-pending.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vectors/status-success.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /octopod-css/development/vendors/outline/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | outline.js 5 | 6 | 39 | 40 | 41 |

Fork of outline.js

42 | 43 | Test link 44 | 45 |

46 | 47 | 48 | 49 |

50 | 51 | Test span 52 | 53 |

54 | 55 | 56 | /must have it's own focus effect because otline is lost on click 57 | 58 |

59 | 60 | 64 | 65 | /need to make an outline for this thing using CSS and it should be possible to cancel it on click using this library 66 | 67 | 68 | -------------------------------------------------------------------------------- /octopod-css/development/vendors/outline/outline.js: -------------------------------------------------------------------------------- 1 | // outline.js 2 | // based on http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/ 3 | // original: https://github.com/lindsayevans/outline.js 4 | // this one is a fork: https://github.com/shchukin/outline.js 5 | 6 | (function(d){ 7 | 8 | var style_element = d.createElement('STYLE'), 9 | dom_events = 'addEventListener' in d, 10 | add_event_listener = function(type, callback){ 11 | // Basic cross-browser event handling 12 | if(dom_events){ 13 | d.addEventListener(type, callback); 14 | }else{ 15 | d.attachEvent('on' + type, callback); 16 | } 17 | }, 18 | set_css = function(css_text){ 19 | // Handle setting of