Compare commits
2 Commits
master
...
spike/comp
Author | SHA1 | Date | |
---|---|---|---|
|
90bd588ed6 | ||
|
f1a83885f8 |
@ -1,9 +0,0 @@
|
||||
artifacts
|
||||
reports
|
||||
.nyc_output
|
||||
coverage
|
||||
dist
|
||||
styleguide
|
||||
build
|
||||
consoles/*/lib/app
|
||||
node_modules
|
10
.eslintrc
10
.eslintrc
@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "joyent-portal",
|
||||
"rules": {
|
||||
"no-console": 1,
|
||||
"new-cap": 0,
|
||||
"jsx-a11y/href-no-hash": 0,
|
||||
"no-negated-condition": 1,
|
||||
"camelcase": 1
|
||||
}
|
||||
}
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.sketch filter=lfs diff=lfs merge=lfs -text
|
25
.github/COMMIT_GUIDELINES.md
vendored
25
.github/COMMIT_GUIDELINES.md
vendored
@ -1,25 +0,0 @@
|
||||
# Commit Guidelines
|
||||
|
||||
```
|
||||
<type><(scope)?>: <msg>
|
||||
```
|
||||
|
||||
Where type is one of:
|
||||
|
||||
```
|
||||
build
|
||||
chore
|
||||
ci
|
||||
docs
|
||||
feat
|
||||
fix
|
||||
perf
|
||||
refactor
|
||||
revert
|
||||
style
|
||||
test
|
||||
```
|
||||
|
||||
And where scope is one of ui-toolkit, my-joy-beta, cloudapi-gql, boilerplate, and create-instance.
|
||||
|
||||
_The recommended method to commit should be by running npm run commit._
|
17
.github/ISSUE_TEMPLATE.md
vendored
17
.github/ISSUE_TEMPLATE.md
vendored
@ -1,17 +0,0 @@
|
||||
## I'm submitting a...
|
||||
|
||||
* [ ] bug report
|
||||
* [ ] feature request
|
||||
* [ ] design request
|
||||
|
||||
## What is the current behavior?
|
||||
|
||||
## If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem
|
||||
|
||||
## What is the expected behavior?
|
||||
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
|
||||
## If the current behavior is a bug, please provide your browser
|
||||
|
||||
## Other information
|
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,14 +0,0 @@
|
||||
**Please check if the PR fulfills these requirements**
|
||||
|
||||
* [ ] The commit message follows our [guidelines](https://github.com/yldio/joyent-portal/blob/master/.github/COMMIT_GUIDELINES.md)
|
||||
* [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
|
||||
**What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
||||
|
||||
**Does this PR close an issue?** (If not please create one)
|
||||
|
||||
**What is the new behavior (if this is a feature change)?**
|
||||
|
||||
**Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
||||
|
||||
**Other information**
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -118,6 +118,9 @@ Session.vim
|
||||
# temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# auto-generated tag files
|
||||
tags
|
||||
|
||||
|
||||
### Windows ###
|
||||
# Windows image file caches
|
||||
@ -151,19 +154,3 @@ $RECYCLE.BIN/
|
||||
tap-xunit
|
||||
/ui/dist
|
||||
|
||||
_todo
|
||||
packages/*/dist
|
||||
prototypes/*/dist
|
||||
packages/*/buid
|
||||
prototypes/*/buid
|
||||
packages/*/.next
|
||||
prototypes/*/.next
|
||||
packages/ui-toolkit/styleguide/
|
||||
packages/ui-toolkit/.snapguidist/
|
||||
packages/*/package-lock.json
|
||||
prototypes/*/package-lock.json
|
||||
|
||||
_env*
|
||||
keys*
|
||||
/packages/*/public/index.html
|
||||
/consoles/*/public/index.html
|
||||
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"ignoreDevDependencies": true,
|
||||
"allowedPackages": [
|
||||
{
|
||||
"name": "colors",
|
||||
"extraFieldsForDocumentation":
|
||||
"Licence is MIT, but was not found by tool: https://github.com/Marak/colors.js/blob/v0.5.1/MIT-LICENSE.txt",
|
||||
"date": "17 January 2017",
|
||||
"reason": "MIT Licenced"
|
||||
}
|
||||
],
|
||||
"allowedLicenses": [
|
||||
"CC-BY-4.0",
|
||||
"CC0-1.0",
|
||||
"MIT",
|
||||
"ISC",
|
||||
"Apache",
|
||||
"BSD",
|
||||
"WTF",
|
||||
"Public Domain",
|
||||
"MPL",
|
||||
"Unlicense"
|
||||
]
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
.git/*
|
||||
.DS_Store
|
||||
|
||||
license
|
||||
yarn.lock
|
||||
.travis.yml
|
||||
|
||||
.yarnclean
|
||||
.eslintignore
|
||||
.prettierignore
|
||||
.npmignore
|
||||
.gitignore
|
||||
.dockerignore
|
||||
|
||||
dist
|
||||
build
|
||||
packages/*/lib/app
|
||||
consoles/*/lib/app
|
||||
|
||||
*.ico
|
||||
*.html
|
||||
*.log
|
||||
*.svg
|
||||
*.map
|
||||
*.png
|
||||
*.snap
|
||||
*.ttf
|
||||
*.sh
|
||||
*.txt
|
31
.prettierrc
31
.prettierrc
@ -1,31 +0,0 @@
|
||||
{
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false,
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
".prettierrc",
|
||||
".eslintrc",
|
||||
".babelrc",
|
||||
".tern-project",
|
||||
".stylelintrc",
|
||||
".lighthouserc"
|
||||
],
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["package.json"],
|
||||
"options": {
|
||||
"printWidth": 180
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"libs": ["ecmascript", "browser"],
|
||||
"plugins": {
|
||||
"doc_comment": true,
|
||||
"local-scope": true,
|
||||
"jsx": true,
|
||||
"node": true,
|
||||
"webpack": {
|
||||
"configPath": "./webpack/index.js"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '9'
|
||||
script:
|
||||
- yarn run test:ci
|
15
.vscode/launch.json
vendored
15
.vscode/launch.json
vendored
@ -1,15 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3069/",
|
||||
"webRoot": "${workspaceRoot}"
|
||||
}
|
||||
]
|
||||
}
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1 +0,0 @@
|
||||
{}
|
36
.yarnclean
36
.yarnclean
@ -1,36 +0,0 @@
|
||||
# test directories
|
||||
__tests__
|
||||
test
|
||||
tests
|
||||
powered-test
|
||||
|
||||
# asset directories
|
||||
docs
|
||||
doc
|
||||
website
|
||||
images
|
||||
assets
|
||||
|
||||
# code coverage directories
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# build scripts
|
||||
Makefile
|
||||
Gulpfile.js
|
||||
Gruntfile.js
|
||||
|
||||
# configs
|
||||
.tern-project
|
||||
.gitattributes
|
||||
.editorconfig
|
||||
.*ignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
.flowconfig
|
||||
.documentup.json
|
||||
.yarn-metadata.json
|
||||
|
||||
# misc
|
||||
*.gz
|
||||
*.md
|
33
CONTRIBUTING.md
Normal file
33
CONTRIBUTING.md
Normal file
@ -0,0 +1,33 @@
|
||||
## Development Workflow
|
||||
|
||||
### Small Feature Development
|
||||
|
||||
Contributors who have write access to the repository will practise continuous
|
||||
delivery (CD as known from now on in this document).
|
||||
|
||||
We will define CD in this document as a method of developing a feature per commit
|
||||
with an encapsulating test that proves that the functionality is working, the
|
||||
contributor will test their code locally and if all is passing will push to *master*.
|
||||
|
||||
For contributors that do not have write access, follow the same conventions but
|
||||
open a Pull Request instead.
|
||||
|
||||
### Large changesets
|
||||
|
||||
When larger changes need to be made, or the work that is carried out spans multiple
|
||||
components / services of the application at the same time a single commit will
|
||||
not suffice.
|
||||
|
||||
In this scenario, the contributor should open a pull request instead.
|
||||
|
||||
## Commit messages
|
||||
|
||||
Follow [Git blessed](http://chris.beams.io/posts/git-commit/)
|
||||
|
||||
1. Separate subject from body with a blank line
|
||||
2. Limit the subject line to 50 characters
|
||||
3. Capitalize the subject line
|
||||
4. Do not end the subject line with a period
|
||||
5. Use the imperative mood in the subject line
|
||||
6. Wrap the body at 72 characters
|
||||
7. Use the body to explain what and why vs. how
|
57
Makefile
Normal file
57
Makefile
Normal file
@ -0,0 +1,57 @@
|
||||
.PHONY: check
|
||||
check:
|
||||
@yarn install --prefer-offline
|
||||
-@./bin/setup
|
||||
|
||||
.PHONE: licence
|
||||
licence:
|
||||
./node_modules/.bin/license-to-fail ./licence.js
|
||||
make licence-check
|
||||
|
||||
SUBDIRS := $(dir $(wildcard */Makefile))
|
||||
TARGETS := install clean test test-ci lint lint-ci licence-check# whatever else, but must not contain '/'
|
||||
|
||||
# foo/.all bar/.all foo/.clean bar/.clean
|
||||
SUBDIRS_TARGETS := \
|
||||
$(foreach t,$(TARGETS),$(addsuffix $t,$(SUBDIRS)))
|
||||
|
||||
.PHONY: $(TARGETS) $(SUBDIRS_TARGETS)
|
||||
|
||||
# static pattern rule, expands into:
|
||||
# all clean: %: foo/.% bar/.%
|
||||
$(TARGETS): %: $(addsuffix %,$(SUBDIRS))
|
||||
@echo 'Done "$*" target'
|
||||
|
||||
# here, for foo/.all:
|
||||
# $(@D) is foo
|
||||
# $(@F) is .all, with leading period
|
||||
# $(@F:.%=%) is just all
|
||||
$(SUBDIRS_TARGETS):
|
||||
$(MAKE) --no-print-directory -C $(@D) $(@F:.%=%)
|
||||
|
||||
DIFF := $(lastword $(subst /, ,${CIRCLE_COMPARE_URL}))
|
||||
CHANGED_FILES := $(subst /, , $(dir $(shell git diff --name-only $(DIFF))))
|
||||
CHANGES := $(patsubst %, %/, $(sort $(filter $(subst /, ,$(SUBDIRS)), $(CHANGED_FILES))))
|
||||
.PHONY: diff
|
||||
diff:
|
||||
echo $(CHANGES)
|
||||
|
||||
BUILDS := build push
|
||||
|
||||
# foo/.all bar/.all foo/.clean bar/.clean
|
||||
BUILDS_TARGETS := \
|
||||
$(foreach t,$(BUILDS),$(addsuffix $t,$(CHANGES)))
|
||||
|
||||
.PHONY: $(BUILDS) $(BUILDS_TARGETS)
|
||||
|
||||
# static pattern rule, expands into:
|
||||
# all clean: %: foo/.% bar/.%
|
||||
$(BUILDS): %: $(addsuffix %,$(CHANGES))
|
||||
@echo 'Done "$*" target'
|
||||
|
||||
# here, for foo/.all:
|
||||
# $(@D) is foo
|
||||
# $(@F) is .all, with leading period
|
||||
# $(@F:.%=%) is just all
|
||||
$(BUILDS_TARGETS):
|
||||
$(MAKE) --no-print-directory -C $(@D) $(@F:.%=%)
|
120
README.md
Normal file
120
README.md
Normal file
@ -0,0 +1,120 @@
|
||||
[![CircleCI](https://circleci.com/gh/yldio/joyent-portal.svg?style=shield&circle-token=0bbeaaafc4868c707ca0ed0568f5193a04daddb4)](https://circleci.com/gh/yldio/joyent-portal)
|
||||
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)
|
||||
|
||||
# Prototype Triton Portal
|
||||
|
||||
This is a prototype project intended to explore some ideas that might contribute to new capabilities and a new user experience for managing applications on [Joyent's Triton](https://www.joyent.com/triton).
|
||||
|
||||
**This is not intended for general use and is completely unsupported.**
|
||||
|
||||
## Our Principles
|
||||
We have designed this product with these principles in mind (to be completed).
|
||||
|
||||
## Our Design Library
|
||||
We use inVision craft for our design components, you can downoad this library [here](https://drive.google.com/open?id=0Bw56g3tFwIuWOXNHUDZmRmQ3ZlE) and submit proposed changes to our Sketch wireframes [here](https://drive.google.com/open?id=0B1oWObk56wa5cE5iY2JWNmI2djg).
|
||||
|
||||
## Documentation
|
||||
Our workshop meeting notes are kept in this [Google Drive folder](https://drive.google.com/open?id=0B1oWObk56wa5eklBNGFlWFRFOHM). Our meeting notes are kept in this [Google Drive folder](https://drive.google.com/open?id=0B1oWObk56wa5N1VzZjhZWWpDTTQ).
|
||||
|
||||
## Development
|
||||
|
||||
If you would like to contribute to the project, the recommended way to setup is to
|
||||
insure that you have docker installed, and optionally have a triton account and profile
|
||||
setup using the triton tool.
|
||||
|
||||
Currently requires [yarn](https://yarnpkg.com/en/docs/install) for installing dependencies,
|
||||
as well as `docker` and `docker-compose` are installed correctly, this can be done by
|
||||
running `make`, make continues without any errors, then you are good to go. [node-triton](https://github.com/joyent/node-triton)
|
||||
is also needed if deployment to Triton is required.
|
||||
|
||||
```
|
||||
make && make install
|
||||
```
|
||||
|
||||
Then to run each individual component locally (subject to change).
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
## Run services
|
||||
|
||||
To run the stack locally:
|
||||
|
||||
```sh
|
||||
docker-compose -f local-compose.yml up -d
|
||||
```
|
||||
|
||||
This will run the front-end at [http://127.0.0.1:8000](http://127.0.0.1:8000),
|
||||
the UI framework at [http://127.0.0.1:8001](http://127.0.0.1:8001),
|
||||
|
||||
## Project Management
|
||||
|
||||
This project is using [Github Projects](https://www.youtube.com/watch?v=C6MGKHkNtxU) for organisation and development of the Joyent Dashboard.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
Currently we are using this repository as a monolithic catch-all for all project communication, development and designs.
|
||||
We will also include multiple PoC's of various bits of functionality from UI's prototypes to API development.
|
||||
|
||||
```
|
||||
.
|
||||
├── cloudapi-graphql
|
||||
├── docs
|
||||
├── frontend
|
||||
├── nginx
|
||||
├── ui
|
||||
└── spikes
|
||||
```
|
||||
|
||||
### cloudapi-graphql
|
||||
|
||||
An implementation of the [Joyent CloudAPI](https://apidocs.joyent.com/cloudapi/) in GraphQL.
|
||||
|
||||
### Docs
|
||||
|
||||
Documentation about the project, mainly focused on information for the technical runnings of this project.
|
||||
Can be view online at the [documentation website](http://docs.svc.f4b20699-b323-4452-9091-977895896da6.eu-ams-1.triton.zone/)
|
||||
|
||||
### frontend
|
||||
|
||||
The client side code with a dev-server, this also includes the production server for the meantime, however we are looking at moving towards a deployment of the build artifacts to manta, and another server to host these assets.
|
||||
|
||||
### nginx
|
||||
|
||||
Nginx will be sitting in-front of the `ui` service, allowing the `ui` to scale out.
|
||||
|
||||
### ui
|
||||
|
||||
Code for the reusable UI framework.
|
||||
|
||||
### spikes
|
||||
|
||||
Implementation examples from spikes, this directory is experimental and is likely broken.
|
||||
|
||||
### Git LFS
|
||||
- We are using Git LFS to track large files, such as design files in Sketch.
|
||||
- Make sure you have this downloaded locally
|
||||
`brew install git-lfs`
|
||||
|
||||
#### Helpful tips
|
||||
- If there is an error cloning to a new machine, or there is an error cloning in the Circle CI process run `git lfs push origin master --all` from a machine that has it already checked out.
|
||||
|
||||
### Sketch Pre Commit
|
||||
|
||||
A pre-commit hook has been added to generate a PNG shot of each .sketch file "page".
|
||||
|
||||
To use, make sure the following are installed:
|
||||
- [Sketch Toolbox](http://sketchtoolbox.com/)
|
||||
- [Sketch Measure Plugin](https://github.com/utom/sketch-measure)
|
||||
|
||||
|
||||
Then add following to your `.git/config`
|
||||
|
||||
```
|
||||
[diff "sketchtool"]
|
||||
textconv = "sketchtool dump"
|
||||
cachetextconv = true
|
||||
```
|
30
bin/deploy
Executable file
30
bin/deploy
Executable file
@ -0,0 +1,30 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude - make bash behave sanely
|
||||
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# Beware of CDPATH gotchas causing cd not to work correctly when a user
|
||||
# has set this in their environment
|
||||
# https://bosker.wordpress.com/2012/02/12/bash-scripters-beware-of-the-cdpath/
|
||||
unset CDPATH
|
||||
|
||||
readonly INCLUDE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
# shellcheck source=bin/setup-tools
|
||||
. "${INCLUDE}"/setup-tools
|
||||
|
||||
echo ">> running triton deploy with docker-compose"
|
||||
ensure_command triton
|
||||
ensure_triton_cns_is_enabled
|
||||
get_triton_details
|
||||
write_env_file
|
||||
|
||||
export DOCKER_HOST=$_DOCKER_HOST
|
||||
export DOCKER_CERT_PATH=$_DOCKER_CERT_PATH
|
||||
# Do not TLS verify for now, incompatibilities between circleci and joyent
|
||||
export DOCKER_TLS_VERIFY=
|
||||
|
||||
docker-compose pull
|
||||
COMPOSE_PROJECT_NAME=${CIRCLE_BRANCH} docker-compose up -d
|
10
bin/docker-login
Executable file
10
bin/docker-login
Executable file
@ -0,0 +1,10 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude - make bash behave sanely
|
||||
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
echo ">> Logging into $_DOCKER_REGISTRY"
|
||||
docker login -e="." -u="$_DOCKER_LOGIN_USERNAME" -p="$_DOCKER_LOGIN_PASSWORD" "$_DOCKER_REGISTRY"
|
203
bin/history-check
Executable file
203
bin/history-check
Executable file
@ -0,0 +1,203 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude - make bash behave sanely
|
||||
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
||||
#
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
#
|
||||
# Globals
|
||||
#
|
||||
remember_git_start_and_end() {
|
||||
HEAD="$(git rev-parse HEAD)"
|
||||
ROOT="$(git log --pretty=format:%H | tail -n 1)"
|
||||
}
|
||||
|
||||
#
|
||||
# Utilities
|
||||
#
|
||||
die() {
|
||||
local msg="$@"
|
||||
[[ -z "${msg}" ]] || {
|
||||
tput setaf 1 # red
|
||||
tput bold
|
||||
echo "${msg}"
|
||||
tput sgr0 # reset
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
error() {
|
||||
local msg="$@"
|
||||
echo -n '| '
|
||||
tput setaf 1 # red
|
||||
echo -n ' ✖'
|
||||
tput sgr0 # reset
|
||||
echo " ${msg}"
|
||||
}
|
||||
|
||||
success() {
|
||||
local msg="$@"
|
||||
echo -n '| '
|
||||
tput setaf 2 # green
|
||||
echo -n ' ✓'
|
||||
tput sgr0 # reset
|
||||
echo " ${msg}"
|
||||
}
|
||||
|
||||
log_commit() {
|
||||
echo "○ $@"
|
||||
}
|
||||
|
||||
# Check a command is present
|
||||
ensure_command() {
|
||||
local cmd="$1"
|
||||
|
||||
command -v "${cmd}" > /dev/null 2>&1 || {
|
||||
die "Couldn't find required command: ${cmd}"
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Signal handling
|
||||
#
|
||||
cleanup() {
|
||||
git reset --hard "${HEAD}" > /dev/null 2>&1
|
||||
rm -f $$_commit_message
|
||||
}
|
||||
|
||||
trap cleanup SIGHUP SIGINT SIGTERM
|
||||
|
||||
#
|
||||
# Git helpers
|
||||
#
|
||||
|
||||
# Go back one commit in history (first parent for merges)
|
||||
step_back_one_commit() {
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
log_commit "$(git rev-parse HEAD)"
|
||||
}
|
||||
|
||||
current_commit_message() {
|
||||
GIT_PAGER= git log --format=%B -n 1
|
||||
}
|
||||
|
||||
current_commit_sha() {
|
||||
git rev-parse HEAD
|
||||
}
|
||||
|
||||
exit_if_not_git_repo() {
|
||||
local gitroot="$(git rev-parse --show-toplevel 2> /dev/null)"
|
||||
|
||||
[[ "${gitroot}" == "" ]] && die 'Current directory is not in a repository'
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Checks
|
||||
#
|
||||
check_commit_message() {
|
||||
local lineno=0
|
||||
local length=0
|
||||
local succeded=1
|
||||
|
||||
while read -r line ; do
|
||||
let succeded=1
|
||||
let lineno+=1
|
||||
length=${#line}
|
||||
|
||||
[[ "${lineno}" -eq "1" ]] && {
|
||||
[[ "${length}" -gt 50 ]] && {
|
||||
error "Commit message: Subject line longer than 50 characters";
|
||||
succeded=0
|
||||
};
|
||||
|
||||
[[ ! "${line}" =~ ^[A-Z].*$ ]] && {
|
||||
error "Commit message: Subject line not capitalised";
|
||||
succeded=0
|
||||
};
|
||||
|
||||
[[ "${line}" == *. ]] && {
|
||||
error "Commit message: Subject line ended with a full stop";
|
||||
succeded=0
|
||||
};
|
||||
}
|
||||
|
||||
[[ "${lineno}" -eq "2" ]] && [[ -n "${line}" ]] && {
|
||||
error "Commit message: Subject line not separated by a blank line";
|
||||
succeded=0;
|
||||
};
|
||||
|
||||
[[ "${lineno}" -gt "1" ]] && [[ "${length}" -gt "72" ]] && {
|
||||
error "Commit message: Body not wrapped at 72 characters";
|
||||
succeded=0
|
||||
};
|
||||
done < $$_commit_message
|
||||
|
||||
[[ "${succeded}" -eq "1" ]] && success "Commit message"
|
||||
return 0
|
||||
}
|
||||
|
||||
run_checks() {
|
||||
current_commit_message > $$_commit_message
|
||||
check_commit_message
|
||||
rm -f $$_commit_message
|
||||
set +e
|
||||
npm run lint > /dev/null 2>&1
|
||||
if [[ "$?" -eq 0 ]]; then
|
||||
success 'Lint'
|
||||
else
|
||||
error 'Lint: script did not exit successfully'
|
||||
fi
|
||||
npm test > /dev/null 2>&1
|
||||
if [[ "$?" -eq 0 ]]; then
|
||||
success 'Test'
|
||||
else
|
||||
error 'Test: script did not exit successfully'
|
||||
fi
|
||||
set -e
|
||||
}
|
||||
|
||||
check_project() {
|
||||
exit_if_not_git_repo
|
||||
|
||||
[[ -f './package.json' ]] || {
|
||||
die 'This does not appear to be a node project'
|
||||
}
|
||||
|
||||
[[ -z "$(json -f package.json 'scripts.lint')" ]] && {
|
||||
die 'There is no lint script in the package.json'
|
||||
}
|
||||
|
||||
[[ -z "$(json -f package.json 'scripts.test')" ]] && {
|
||||
die 'There is no test script in the package.json'
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
traverse_history() {
|
||||
while [[ "${ROOT}" != "$(current_commit_sha)" ]] ; do
|
||||
run_checks
|
||||
step_back_one_commit
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
ensure_command git
|
||||
ensure_command tail
|
||||
ensure_command npm
|
||||
ensure_command json
|
||||
check_project
|
||||
remember_git_start_and_end
|
||||
log_commit "HEAD: $(current_commit_sha)"
|
||||
traverse_history
|
||||
run_checks
|
||||
log_commit "ROOT: $(current_commit_sha)"
|
||||
cleanup
|
||||
|
||||
# vim: syntax=sh et ts=2 sts=2 sw=2
|
16
bin/on-changes-publish-ui
Executable file
16
bin/on-changes-publish-ui
Executable file
@ -0,0 +1,16 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude
|
||||
#
|
||||
set -euo pipefail
|
||||
set -x
|
||||
# Set internal field seperator to `/` to split up urls
|
||||
IFS='/'
|
||||
|
||||
read -ra ADDR <<< "$CIRCLE_COMPARE_URL"
|
||||
|
||||
if [[ "$(git diff --name-only """${ADDR[-1]}""")" == *"ui/"* ]]
|
||||
then
|
||||
make -C ui publish | sed '/NPM_TOKEN/d'
|
||||
fi
|
60
bin/pre-commit.hook
Executable file
60
bin/pre-commit.hook
Executable file
@ -0,0 +1,60 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude - make bash behave sanely
|
||||
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# Make pushd & popd silent
|
||||
pushd () {
|
||||
command pushd "$@" > /dev/null
|
||||
}
|
||||
|
||||
popd () {
|
||||
command popd "$@" > /dev/null
|
||||
}
|
||||
|
||||
export EXIT_CODE=0
|
||||
|
||||
|
||||
function lint_changed() {
|
||||
# Allow lint to be ran from outside of the root directory
|
||||
local git_root
|
||||
git_root=$(git rev-parse --show-cdup)
|
||||
git_root=${git_root:-./}
|
||||
|
||||
local subdirs
|
||||
subdirs=$(find "$git_root" -maxdepth 2 -mindepth 2 -name 'Makefile' -printf '%h\n')
|
||||
|
||||
|
||||
for directory in $subdirs
|
||||
do
|
||||
pushd "$directory"
|
||||
|
||||
local npm_bin="node_modules/.bin"
|
||||
local eslint="$npm_bin/eslint"
|
||||
|
||||
function lint() {
|
||||
local to_lint
|
||||
to_lint=$(git diff --staged --diff-filter=ACMTUXB --name-only -- '*.j'{s,sx})
|
||||
echo $to_lint
|
||||
echo $eslint
|
||||
|
||||
if [ "$to_lint" ]; then
|
||||
$eslint "$to_lint" -c ".eslintrc" || EXIT_CODE=$?
|
||||
fi
|
||||
}
|
||||
|
||||
lint
|
||||
popd
|
||||
done
|
||||
}
|
||||
|
||||
lint_changed
|
||||
if [[ "$EXIT_CODE" -gt "0" ]]; then
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
make test
|
||||
|
||||
echo "⚡️ changed files pass eslint! ⚡️"
|
23
bin/setup
Executable file
23
bin/setup
Executable file
@ -0,0 +1,23 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
#
|
||||
# Prelude
|
||||
#
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
# Beware of CDPATH gotchas causing cd not to work correctly when a user
|
||||
# has set this in their environment
|
||||
# https://bosker.wordpress.com/2012/02/12/bash-scripters-beware-of-the-cdpath/
|
||||
unset CDPATH
|
||||
|
||||
readonly INCLUDE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
# shellcheck source=bin/setup-tools
|
||||
. "${INCLUDE}"/setup-tools
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
ensure_prerequisites
|
||||
get_triton_details
|
||||
check_docker_config
|
||||
write_env_file
|
105
bin/setup-tools
Normal file
105
bin/setup-tools
Normal file
@ -0,0 +1,105 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
# setup.sh - Checks that all the required tools are present and that they are
|
||||
# appropriately configured for deploying to Triton.
|
||||
#
|
||||
# Adapted from https://github.com/autopilotpattern/mysql/blob/master/setup.sh
|
||||
#
|
||||
|
||||
#
|
||||
# Prelude
|
||||
#
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
#
|
||||
# Utilities
|
||||
#
|
||||
die() {
|
||||
local msg="$*"
|
||||
[[ -z "${msg}" ]] || {
|
||||
echo
|
||||
tput setaf 1 # red
|
||||
tput bold
|
||||
echo "${msg}"
|
||||
tput sgr0 # reset
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
#
|
||||
# Check functions
|
||||
#
|
||||
ensure_command() {
|
||||
local cmd="$1"
|
||||
|
||||
command -v "${cmd}" > /dev/null 2>&1 || {
|
||||
die "Couldn't find required command: ${cmd}"
|
||||
}
|
||||
}
|
||||
|
||||
get_triton_details() {
|
||||
TRITON_USER=$(triton profile get | awk -F": " '/account:/{print $2}')
|
||||
TRITON_DC=$(triton profile get | awk -F"/" '/url:/{print $3}' | awk -F'.' '{print $1}')
|
||||
TRITON_URL=$(triton profile get | awk -F' ' '/url:/{print $2}')
|
||||
TRITON_ACCOUNT=$(triton account get | awk -F": " '/id:/{print $2}')
|
||||
TRITON_KEY=$(triton profile get | awk -F' ' '/keyId:/{print $2}')
|
||||
}
|
||||
|
||||
check_docker_config() {
|
||||
[[ "${DOCKER_HOST:=unset}" == "unset" ]] && {
|
||||
echo "Run \"docker-compose -f local-compose.yml up\" to run locally"
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
ensure_docker_config_matches_triton_config_and_capture_triton_details() {
|
||||
local docker_user
|
||||
docker_user=$(docker info 2>&1 | awk -F": " '/SDCAccount:/{print $2}')
|
||||
local docker_dc
|
||||
docker_dc="$(echo "${DOCKER_HOST}" | awk -F"/" '{print $3}' | awk -F'.' '{print $1}')"
|
||||
get_triton_details
|
||||
[[ ! "$docker_user" = "$TRITON_USER" ]] || [[ ! "$docker_dc" = "$TRITON_DC" ]] && {
|
||||
echo "Docker user: ${docker_user}"
|
||||
echo "Triton user: ${TRITON_USER}"
|
||||
echo "Docker data center: ${docker_dc}"
|
||||
echo "Triton data center: ${TRITON_DC}"
|
||||
die "Your Triton config does not match your Docker configuration."
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ensure_triton_cns_is_enabled() {
|
||||
local triton_cns_enabled
|
||||
triton_cns_enabled=$(triton account get | awk -F": " '/cns/{print $2}')
|
||||
[[ "$triton_cns_enabled" == "true" ]] || {
|
||||
die "Triton CNS is required and not enabled."
|
||||
}
|
||||
}
|
||||
|
||||
write_env_file() {
|
||||
if [[ -f .env ]] ; then
|
||||
echo "Env file already exists, not overwriting"
|
||||
else
|
||||
echo '# Consul discovery via Triton CNS' >> .env
|
||||
[[ "${DOCKER_HOST:=unset}" == "*docker.joyent.com*" ]] || {
|
||||
echo CONSUL="consul.svc.${TRITON_ACCOUNT}.${TRITON_DC}.cns.joyent.com" \
|
||||
>> .env
|
||||
}
|
||||
echo SDC_KEY_ID=${TRITON_KEY} >> .env
|
||||
echo SDC_ACCOUNT=${TRITON_ACCOUNT} >> .env
|
||||
echo SDC_URL=${TRITON_URL} >> .env
|
||||
echo >> .env
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_prerequisites() {
|
||||
ensure_command docker
|
||||
ensure_command docker-compose
|
||||
ensure_command triton
|
||||
}
|
||||
|
||||
# vim: syntax=sh et ts=2 sts=2 sw=2
|
57
bin/sketch-previews.rb
Executable file
57
bin/sketch-previews.rb
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env ruby
|
||||
require 'fileutils'
|
||||
|
||||
PROGNAME = 'pre-commit (auto-generate sketch previews)'
|
||||
|
||||
def main
|
||||
progress "Installing sketchtool"
|
||||
system!(%W[/Applications/Sketch.app/Contents/Resources/sketchtool/install.sh])
|
||||
|
||||
progress "Looking for changed or added .sketch files"
|
||||
diff_output = capture!(%W[git diff --name-only --cached --pretty=format:])
|
||||
sketch_files = diff_output.split("\n").grep(/\.sketch\z/)
|
||||
if sketch_files.empty?
|
||||
progress "No sketch files to create preview images for in this commit!"
|
||||
end
|
||||
puts sketch_files
|
||||
|
||||
sketch_files.each do |f|
|
||||
unless File.exist?(f)
|
||||
progress "#{f} does not exist (anymore?)"
|
||||
next
|
||||
end
|
||||
|
||||
png_output_dir = f.sub(/\.sketch\z/, '') + '-sketch-previews'
|
||||
progress "deleting old previews"
|
||||
FileUtils.rm_rf(png_output_dir)
|
||||
|
||||
progress "exporting pages"
|
||||
cmd = %W[sketchtool --overwriting=YES --output=#{png_output_dir} export pages #{f}]
|
||||
system!(cmd)
|
||||
|
||||
progress "adding to git"
|
||||
system!(%W[git add #{png_output_dir}])
|
||||
end
|
||||
end
|
||||
|
||||
def system!(cmd)
|
||||
puts "running: #{cmd.join(' ')}"
|
||||
abort failure_message(cmd) unless system(*cmd)
|
||||
end
|
||||
|
||||
def capture!(cmd)
|
||||
puts "capturing: #{cmd.join(' ')}"
|
||||
result = IO.popen(cmd) { |io| io.read }
|
||||
abort failure_message(cmd) unless $?.success?
|
||||
result
|
||||
end
|
||||
|
||||
def failure_message(cmd)
|
||||
"#{PROGNAME}: command failed: #{cmd.join(' ')}"
|
||||
end
|
||||
|
||||
def progress(msg)
|
||||
puts "#{PROGNAME}: #{msg}"
|
||||
end
|
||||
|
||||
main
|
@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { homedir } = require('os');
|
||||
const { join } = require('path');
|
||||
|
||||
const { SDC_KEY_PATH } = process.env;
|
||||
|
||||
process.env.SDC_KEY_PATH = SDC_KEY_PATH || join(homedir(), './.ssh/id_rsa');
|
@ -1,20 +0,0 @@
|
||||
module.exports = [
|
||||
{
|
||||
name: 'Logout',
|
||||
slug: 'logout',
|
||||
description: 'Do the daggum logout',
|
||||
url: '/logout'
|
||||
},
|
||||
{
|
||||
name: 'Change Password',
|
||||
slug: 'change-password',
|
||||
description: 'Change yer own password',
|
||||
url: '/password'
|
||||
},
|
||||
{
|
||||
name: 'Account',
|
||||
slug: 'account',
|
||||
description: 'Your account information',
|
||||
url: '/account'
|
||||
}
|
||||
];
|
@ -1,89 +0,0 @@
|
||||
module.exports = [
|
||||
{
|
||||
name: 'Compute',
|
||||
services: [
|
||||
{
|
||||
name: 'VMs & Containers',
|
||||
slug: 'instances',
|
||||
description: 'Run VMs and bare metal containers'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Network',
|
||||
services: [
|
||||
{
|
||||
name: 'VLANs',
|
||||
slug: 'vlans',
|
||||
description: 'Wire your application your way'
|
||||
},
|
||||
{
|
||||
name: 'Subnets',
|
||||
slug: 'subnets',
|
||||
description: 'A network for everything'
|
||||
},
|
||||
{
|
||||
name: 'Firewall Rules',
|
||||
slug: 'firewall',
|
||||
description: 'Control the bits coming and going'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Storage',
|
||||
services: [
|
||||
{
|
||||
name: 'Triton Object Storage',
|
||||
slug: 'object-storage',
|
||||
description: 'Modern cloud object storage',
|
||||
tags: ["'note'='was Manta'"]
|
||||
},
|
||||
{
|
||||
name: 'S3 Compatibility Bridge',
|
||||
slug: 's3-bridge',
|
||||
description: 'Modern storage, legacy compability'
|
||||
},
|
||||
{
|
||||
name: 'Triton Volumes',
|
||||
slug: 'volumes',
|
||||
description: 'Network filesystems for your apps',
|
||||
tags: ["'is-new'='true'"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Access',
|
||||
services: [
|
||||
{
|
||||
name: 'Role Based Access Control',
|
||||
slug: 'rbac',
|
||||
description: 'Manage users within your account'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Help & Support',
|
||||
services: [
|
||||
{
|
||||
name: 'Service Status',
|
||||
slug: 'status',
|
||||
description: 'Find out about the status of our services'
|
||||
},
|
||||
{
|
||||
name: 'Contact Support',
|
||||
slug: 'contact-support',
|
||||
description: 'Chat to us via phone or email'
|
||||
},
|
||||
{
|
||||
name: 'Support Plans',
|
||||
slug: 'support-plans',
|
||||
description: 'Write here about Support Plans'
|
||||
},
|
||||
{
|
||||
name: 'Getting Started',
|
||||
slug: 'getting-started',
|
||||
description: 'Write here about Getting Started'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@ -1,86 +0,0 @@
|
||||
module.exports = [
|
||||
{
|
||||
name: 'Ashburn, Virginia, USA',
|
||||
continent: 'NORTH_AMERICA',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'us-east-1',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'us-east-2',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'us-east-3',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Las Vegas, Nevada, USA',
|
||||
continent: 'NORTH_AMERICA',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'us-sw-1',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Emeryville, California, USA',
|
||||
continent: 'NORTH_AMERICA',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'us-west-1',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Amsterdam, Netherlands',
|
||||
continent: 'EUROPE',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'us-ams-1',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Singapore',
|
||||
continent: 'ASIA',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'ap-sg-1',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'ap-sg-2',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'ap-sg-3',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Seoul, South Korea',
|
||||
continent: 'ASIA',
|
||||
datacenters: [
|
||||
{
|
||||
name: 'ap-kr-1',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'ap-kr-2',
|
||||
url: 'http://localhost'
|
||||
},
|
||||
{
|
||||
name: 'ap-kr-3',
|
||||
url: 'http://localhost'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
@ -1,72 +0,0 @@
|
||||
// Requires .env.js file with the following exports:
|
||||
// SDC_URL, SDC_KEY_ID, SDC_KEY_PATH
|
||||
require('./.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const Hapi = require('hapi');
|
||||
const H2O2 = require('h2o2');
|
||||
const Execa = require('execa');
|
||||
const Path = require('path');
|
||||
const Fs = require('fs');
|
||||
|
||||
const { PORT = 4000 } = process.env;
|
||||
const ROOT = Path.join(__dirname, 'src');
|
||||
|
||||
const calcPort = i => Number(PORT) + Number(i) + 1;
|
||||
|
||||
const namespaces = Fs.readdirSync(ROOT)
|
||||
.filter(filename => /.js$/.test(filename))
|
||||
.map(filename => filename.replace(/.js$/, ''))
|
||||
.filter(filename => !['index', 'server'].includes(filename));
|
||||
|
||||
const routes = namespaces.map((namespace, i) => ({
|
||||
method: '*',
|
||||
path: `/${namespace}/{params*}`,
|
||||
handler: {
|
||||
proxy: {
|
||||
uri: `{protocol}://0.0.0.0:${calcPort(i)}/${namespace}/{params}`
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
namespaces.forEach((namespace, i) => {
|
||||
const child = Execa('node', [namespace], {
|
||||
cwd: ROOT,
|
||||
cleanup: true,
|
||||
env: Object.assign({}, process.env, {
|
||||
PORT: calcPort(i),
|
||||
PREFIX: namespace
|
||||
})
|
||||
});
|
||||
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
||||
});
|
||||
|
||||
Main(async () => {
|
||||
const server = Hapi.server({
|
||||
port: PORT,
|
||||
routes: {
|
||||
cors: {
|
||||
origin: ['*'],
|
||||
credentials: true,
|
||||
additionalHeaders: ['Cookie', 'X-CSRF-Token']
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
log: ['error'],
|
||||
request: ['error']
|
||||
}
|
||||
});
|
||||
|
||||
await server.register({
|
||||
plugin: H2O2
|
||||
});
|
||||
|
||||
routes.map(route => server.route(route));
|
||||
|
||||
await server.start();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`server started at http://0.0.0.0:${server.info.port}`);
|
||||
});
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "joyent-portal-bundle",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"license": "MPL-2.0",
|
||||
"scripts": {
|
||||
"dev": "NODE_ENV=development PORT=4000 node index.js",
|
||||
"build:test": "echo 0",
|
||||
"build:lib": "echo 0",
|
||||
"build:bundle": "echo 0",
|
||||
"prepublish": "echo 0",
|
||||
"test": "echo 0",
|
||||
"test:ci": "echo 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"apr-main": "^4.0.3",
|
||||
"cloudapi-gql": "^8.0.0",
|
||||
"execa": "^0.10.0",
|
||||
"graphi": "^5.7.0",
|
||||
"h2o2": "^8.1.2",
|
||||
"hapi": "^17.5.0",
|
||||
"hapi-triton-auth": "^3.0.0",
|
||||
"hapi-webconsole-nav": "^2.1.0",
|
||||
"my-joy-images": "*",
|
||||
"my-joy-instances": "*",
|
||||
"my-joy-navigation": "*",
|
||||
"my-joy-service-groups": "*",
|
||||
"my-joy-templates": "*",
|
||||
"tsg-graphql": "^1.0.0"
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const CloudApiGql = require('cloudapi-gql');
|
||||
const Graphi = require('graphi');
|
||||
const Url = require('url');
|
||||
|
||||
const Server = require('./server');
|
||||
const Ui = require('my-joy-images');
|
||||
|
||||
const {
|
||||
PORT = 4003,
|
||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
||||
PREFIX = 'images',
|
||||
DC_NAME,
|
||||
SDC_URL,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID
|
||||
} = process.env;
|
||||
|
||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
const apiBaseUrl = SDC_URL;
|
||||
|
||||
Main(async () => {
|
||||
const server = await Server({
|
||||
PORT,
|
||||
BASE_URL
|
||||
});
|
||||
|
||||
await server.register([
|
||||
{
|
||||
plugin: Graphi,
|
||||
options: {
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
authStrategy: 'sso'
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: CloudApiGql,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Ui
|
||||
}
|
||||
]);
|
||||
|
||||
await server.start();
|
||||
});
|
@ -1,64 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const CloudApiGql = require('cloudapi-gql');
|
||||
const Graphi = require('graphi');
|
||||
const Url = require('url');
|
||||
|
||||
const Server = require('./server');
|
||||
const Ui = require('my-joy-instances');
|
||||
|
||||
const {
|
||||
PORT = 4002,
|
||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
||||
PREFIX = 'instances',
|
||||
DC_NAME,
|
||||
SDC_URL,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID
|
||||
} = process.env;
|
||||
|
||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
const apiBaseUrl = SDC_URL;
|
||||
|
||||
Main(async () => {
|
||||
const server = await Server({
|
||||
PORT,
|
||||
BASE_URL
|
||||
});
|
||||
|
||||
await server.register([
|
||||
{
|
||||
plugin: Graphi,
|
||||
options: {
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
authStrategy: 'sso'
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: CloudApiGql,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Ui
|
||||
}
|
||||
]);
|
||||
|
||||
await server.start();
|
||||
});
|
@ -1,72 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const Nav = require('hapi-webconsole-nav');
|
||||
const Graphi = require('graphi');
|
||||
const Url = require('url');
|
||||
|
||||
const Server = require('./server');
|
||||
const Ui = require('my-joy-navigation');
|
||||
|
||||
const Regions = require('../data/regions');
|
||||
const Categories = require('../data/categories');
|
||||
const Account = require('../data/account');
|
||||
|
||||
const {
|
||||
PORT = 4001,
|
||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
||||
PREFIX = 'navigation',
|
||||
DC_NAME,
|
||||
SDC_URL,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID
|
||||
} = process.env;
|
||||
|
||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
const apiBaseUrl = SDC_URL;
|
||||
const baseUrl = BASE_URL;
|
||||
|
||||
Main(async () => {
|
||||
const server = await Server({
|
||||
PORT,
|
||||
BASE_URL
|
||||
});
|
||||
|
||||
await server.register([
|
||||
{
|
||||
plugin: Graphi,
|
||||
options: {
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
authStrategy: 'sso'
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Nav,
|
||||
options: {
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl,
|
||||
dcName,
|
||||
baseUrl,
|
||||
regions: Regions,
|
||||
accountServices: Account,
|
||||
categories: Categories
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Ui
|
||||
}
|
||||
]);
|
||||
|
||||
await server.start();
|
||||
});
|
@ -1,84 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Hapi = require('hapi');
|
||||
const Sso = require('hapi-triton-auth');
|
||||
|
||||
const {
|
||||
COOKIE_PASSWORD,
|
||||
COOKIE_DOMAIN,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID,
|
||||
SDC_URL
|
||||
} = process.env;
|
||||
|
||||
module.exports = async ({ PORT, BASE_URL }) => {
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
const apiBaseUrl = SDC_URL;
|
||||
const ssoUrl = 'https://sso.joyent.com/login';
|
||||
const baseUrl = BASE_URL;
|
||||
const isDev = true;
|
||||
|
||||
const permissions = {
|
||||
cloudapi: ['/my/*']
|
||||
};
|
||||
|
||||
const cookie = {
|
||||
password: COOKIE_PASSWORD,
|
||||
domain: COOKIE_DOMAIN,
|
||||
isSecure: false,
|
||||
isHttpOnly: true,
|
||||
ttl: 1000 * 60 * 60 // 1 hour
|
||||
};
|
||||
|
||||
const server = Hapi.server({
|
||||
port: PORT,
|
||||
routes: {
|
||||
cors: {
|
||||
origin: ['*'],
|
||||
credentials: true,
|
||||
additionalHeaders: ['Cookie', 'X-CSRF-Token']
|
||||
}
|
||||
},
|
||||
debug: {
|
||||
log: ['error'],
|
||||
request: ['error']
|
||||
}
|
||||
});
|
||||
|
||||
server.events.on('log', (event, tags) => {
|
||||
if (tags.error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(event);
|
||||
}
|
||||
});
|
||||
|
||||
server.events.on('request', (request, event) => {
|
||||
const { tags } = event;
|
||||
if (tags.includes('error') && event.data && event.data.errors) {
|
||||
event.data.errors.forEach(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await server.register({
|
||||
plugin: Sso,
|
||||
options: {
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl,
|
||||
ssoUrl,
|
||||
permissions,
|
||||
baseUrl,
|
||||
isDev,
|
||||
cookie
|
||||
}
|
||||
});
|
||||
|
||||
server.auth.default('sso');
|
||||
|
||||
return server;
|
||||
};
|
@ -1,78 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const CloudApiGql = require('cloudapi-gql');
|
||||
const Tsg = require('tsg-graphql');
|
||||
const Graphi = require('graphi');
|
||||
const Url = require('url');
|
||||
|
||||
const Server = require('./server');
|
||||
const Ui = require('my-joy-service-groups');
|
||||
|
||||
const {
|
||||
PORT = 4004,
|
||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
||||
PREFIX = 'service-groups',
|
||||
DC_NAME,
|
||||
TSG_URL = 'http://0.0.0.0:3000',
|
||||
SDC_URL,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID
|
||||
} = process.env;
|
||||
|
||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
|
||||
Main(async () => {
|
||||
const server = await Server({
|
||||
PORT,
|
||||
BASE_URL
|
||||
});
|
||||
|
||||
await server.register([
|
||||
{
|
||||
plugin: Graphi,
|
||||
options: {
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
authStrategy: 'sso'
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Tsg,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl: TSG_URL,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: CloudApiGql,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl: SDC_URL,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Ui
|
||||
}
|
||||
]);
|
||||
|
||||
await server.start();
|
||||
});
|
@ -1,78 +0,0 @@
|
||||
require('../.env.js');
|
||||
|
||||
const Main = require('apr-main');
|
||||
const CloudApiGql = require('cloudapi-gql');
|
||||
const Tsg = require('tsg-graphql');
|
||||
const Graphi = require('graphi');
|
||||
const Url = require('url');
|
||||
|
||||
const Server = require('./server');
|
||||
const Ui = require('my-joy-templates');
|
||||
|
||||
const {
|
||||
PORT = 4005,
|
||||
BASE_URL = `http://0.0.0.0:${PORT}`,
|
||||
PREFIX = 'templates',
|
||||
DC_NAME,
|
||||
TSG_URL = 'http://0.0.0.0:3000',
|
||||
SDC_URL,
|
||||
SDC_KEY_PATH,
|
||||
SDC_ACCOUNT,
|
||||
SDC_KEY_ID
|
||||
} = process.env;
|
||||
|
||||
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
|
||||
const keyPath = SDC_KEY_PATH;
|
||||
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
|
||||
|
||||
Main(async () => {
|
||||
const server = await Server({
|
||||
PORT,
|
||||
BASE_URL
|
||||
});
|
||||
|
||||
await server.register([
|
||||
{
|
||||
plugin: Graphi,
|
||||
options: {
|
||||
graphqlPath: '/graphql',
|
||||
graphiqlPath: '/graphiql',
|
||||
authStrategy: 'sso'
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Tsg,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl: TSG_URL,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: CloudApiGql,
|
||||
options: {
|
||||
authStrategy: 'sso',
|
||||
keyPath,
|
||||
keyId,
|
||||
apiBaseUrl: SDC_URL,
|
||||
dcName
|
||||
},
|
||||
routes: {
|
||||
prefix: `/${PREFIX}`
|
||||
}
|
||||
},
|
||||
{
|
||||
plugin: Ui
|
||||
}
|
||||
]);
|
||||
|
||||
await server.start();
|
||||
});
|
51
circle.yml
Normal file
51
circle.yml
Normal file
@ -0,0 +1,51 @@
|
||||
## Customize the test machine
|
||||
machine:
|
||||
pre:
|
||||
- git config --global user.email "circleci@joyent.zone"
|
||||
- git config --global user.name "circlebot"
|
||||
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
|
||||
|
||||
services:
|
||||
- docker
|
||||
node:
|
||||
version: 7.7.3
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- sudo curl -L https://github.com/docker/compose/releases/download/1.8.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
|
||||
- sudo chmod +x /usr/local/bin/docker-compose
|
||||
- yarn global add triton@4.15.0 || cat /home/ubuntu/.yarn-config/global/yarn-error.log
|
||||
- echo '{"url":"https://eu-ams-1.api.joyent.com","account":"'$SDC_ACCOUNT'","keyId":"c3:30:35:9b:85:48:73:44:31:cc:4b:2e:6a:00:16:e2","name":"eu-ams-1","curr":true}' | triton profile create -f -
|
||||
- triton env --docker eu-ams-1
|
||||
- mkdir -p ${CIRCLE_TEST_REPORTS}/tap-xunit/
|
||||
- echo -e "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
||||
override:
|
||||
- make && make install
|
||||
# Install git-lfs - TODO: Move to make task
|
||||
- curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
|
||||
- sudo apt-get install git-lfs
|
||||
- ssh git@github.com git-lfs-authenticate yldio/joyent-portal.git download
|
||||
- git config credential.helper manager
|
||||
- git lfs pull
|
||||
|
||||
test:
|
||||
override:
|
||||
- make -j2 lint-ci test-ci
|
||||
|
||||
deployment:
|
||||
development:
|
||||
branch: master
|
||||
commands:
|
||||
- ./bin/docker-login
|
||||
- ./bin/on-changes-publish-ui
|
||||
- make -j2 build
|
||||
- make -j2 push
|
||||
- ./bin/deploy
|
||||
staging:
|
||||
tag: /release.*/
|
||||
commands:
|
||||
- CIRCLE_BRANCH=staging ./bin/docker-login
|
||||
- CIRCLE_BRANCH=staging ./bin/on-changes-publish-ui
|
||||
- CIRCLE_BRANCH=staging make -j2 build
|
||||
- CIRCLE_BRANCH=staging make -j2 push
|
||||
- CIRCLE_BRANCH=staging ./bin/deploy
|
27
cloudapi-graphql/.eslintrc
Normal file
27
cloudapi-graphql/.eslintrc
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"prettier/prettier": ["error", {
|
||||
"useTabs": false,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": false,
|
||||
"parser": "flow",
|
||||
"semi": true
|
||||
}]
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
}
|
||||
}
|
50
cloudapi-graphql/.gitignore
vendored
Normal file
50
cloudapi-graphql/.gitignore
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Vim files:
|
||||
*.sw*
|
||||
|
||||
# Mac OS dirty files
|
||||
.DS_Store
|
1
cloudapi-graphql/Dockerfile
Normal file
1
cloudapi-graphql/Dockerfile
Normal file
@ -0,0 +1 @@
|
||||
FROM quay.io/yldio/alpine-node-containerpilot:6.9.4-3
|
54
cloudapi-graphql/Makefile
Normal file
54
cloudapi-graphql/Makefile
Normal file
@ -0,0 +1,54 @@
|
||||
NAME := $(lastword $(subst /, ,$(CURDIR)))
|
||||
|
||||
bindir := $(shell yarn bin)
|
||||
AVA := $(bindir)/ava
|
||||
NYC := $(bindir)/nyc
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
yarn install --prefer-offline
|
||||
|
||||
.PHONY: install-production
|
||||
install-production:
|
||||
yarn install --production --pure-lockfile --prefer-offline
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf node_modules
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(AVA) $(TEST_ARGS)
|
||||
|
||||
XUNIT_DIR := ${CIRCLE_TEST_REPORTS}/tap-xunit
|
||||
XUNIT := $(bindir)/tap-xunit
|
||||
XUNIT_OUTPUT := >> ${CIRCLE_TEST_REPORTS}/tap-xunit/xunit-$(NAME)
|
||||
.PHONY: test-ci
|
||||
test-ci:
|
||||
mkdir -p $(XUNIT_DIR)
|
||||
$(NYC) $(AVA) -t | $(XUNIT) $(XUNIT_OUTPUT).xml
|
||||
|
||||
.PHONY: start
|
||||
start:
|
||||
yarn run start
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
docker build -t quay.io/yldio/joyent-dashboard-$(NAME):$(CIRCLE_BRANCH) .
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
docker push quay.io/yldio/joyent-dashboard-$(NAME)
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
$(bindir)/eslint .
|
||||
|
||||
.PHONY: lint-ci
|
||||
lint-ci:
|
||||
mkdir -p $(XUNIT_DIR)
|
||||
-$(bindir)/eslint . --format tap | $(XUNIT) $(XUNIT_OUTPUT)-lint.xml
|
||||
|
||||
.PHONY: licence-check
|
||||
licence-check:
|
||||
../node_modules/.bin/license-to-fail ../licence.js
|
166
cloudapi-graphql/README.md
Normal file
166
cloudapi-graphql/README.md
Normal file
@ -0,0 +1,166 @@
|
||||
[![Docker Repository on Quay](https://quay.io/repository/yldio/joyent-dashboard-cloudapi-graphql/status?token=bddd694a-a913-4b66-b7bc-fb71992672c4 "Docker Repository on Quay")](https://quay.io/repository/yldio/joyent-dashboard-cloudapi-graphql)
|
||||
# cloudapi-graphql
|
||||
|
||||
Proof-of-Concept of the [Joyent Cloud API](https://apidocs.joyent.com/cloudapi/) running on GraphQL.
|
||||
|
||||
## Setup
|
||||
|
||||
### Setup Credentials
|
||||
|
||||
Create `credentials.json`:
|
||||
|
||||
```json5
|
||||
{
|
||||
"url": "https://us-sw-1.api.joyentcloud.com",
|
||||
"keyId": "", //public key fingerprint ex: 35:jh:42:56...
|
||||
"account": "", // account ex: raoulmillais
|
||||
"user": "" // sub-account ex: ramitos
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively you can just use ENV variables:
|
||||
|
||||
```
|
||||
SDC_URL
|
||||
SDC_ACCOUNT
|
||||
SDC_USER
|
||||
SDC_KEY_ID
|
||||
```
|
||||
|
||||
As a third option you can use a `.env` file.
|
||||
|
||||
### Install Dependencies and run
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Visit GraphiQL
|
||||
|
||||
Go-to http://0.0.0.0:4000/graphql to use the REPL with interactive documentation.
|
||||
|
||||
![GraphiQL](https://cloud.githubusercontent.com/assets/524382/19242455/1e371978-8f0b-11e6-9563-d6f5b93fa63c.png)
|
||||
|
||||
## API
|
||||
|
||||
- [x] Account
|
||||
- [x] GetAccount
|
||||
- [x] UpdateAccount
|
||||
- [x] Keys
|
||||
- [x] ListKeys
|
||||
- [x] GetKey
|
||||
- [x] CreateKey
|
||||
- [x] DeleteKey
|
||||
- [x] Users
|
||||
- [x] ListUsers
|
||||
- [x] GetUser
|
||||
- [x] CreateUser
|
||||
- [x] UpdateUser
|
||||
- [ ] ChangeUserPassword
|
||||
- [x] DeleteUser
|
||||
- [x] Roles
|
||||
- [x] ListRoles
|
||||
- [x] GetRole
|
||||
- [x] CreateRole
|
||||
- [x] UpdateRole
|
||||
- [x] DeleteRole
|
||||
- [x] Role Tags
|
||||
- [x] SetRoleTags
|
||||
- [x] Policies
|
||||
- [x] ListPolicies
|
||||
- [x] GetPolicy
|
||||
- [x] CreatePolicy
|
||||
- [x] UpdatePolicy
|
||||
- [x] DeletePolicy
|
||||
- [x] User SSH Keys
|
||||
- [x] ListUserKeys
|
||||
- [x] GetUserKey
|
||||
- [x] CreateUserKey
|
||||
- [x] DeleteUserKey
|
||||
- [ ] Config
|
||||
- [ ] GetConfig
|
||||
- [ ] UpdateConfig
|
||||
- [x] Datacenters
|
||||
- [x] ListDatacenters
|
||||
- [x] GetDatacenter
|
||||
- [x] Services
|
||||
- [x] ListServices
|
||||
- [x] Images
|
||||
- [x] ListImages
|
||||
- [x] GetImage
|
||||
- [x] DeleteImage
|
||||
- [x] ExportImage
|
||||
- [x] CreateImageFromMachine
|
||||
- [ ] UpdateImage
|
||||
- [x] Packages
|
||||
- [x] ListPackages
|
||||
- [x] GetPackage
|
||||
- [x] Instances
|
||||
- [x] ListMachines
|
||||
- [x] GetMachine
|
||||
- [x] CreateMachine
|
||||
- [x] StopMachine
|
||||
- [x] StartMachine
|
||||
- [x] RebootMachine
|
||||
- [ ] ResizeMachine
|
||||
- [ ] RenameMachine
|
||||
- [x] EnableMachineFirewall
|
||||
- [x] DisableMachineFirewall
|
||||
- [x] CreateMachineSnapshot
|
||||
- [x] StartMachineFromSnapshot
|
||||
- [x] ListMachineSnapshots
|
||||
- [x] GetMachineSnapshot
|
||||
- [x] DeleteMachineSnapshot
|
||||
- [ ] UpdateMachineMetadata
|
||||
- [ ] ListMachineMetadata
|
||||
- [ ] GetMachineMetadata
|
||||
- [ ] DeleteMachineMetadata
|
||||
- [ ] DeleteAllMachineMetadata
|
||||
- [x] AddMachineTags
|
||||
- [x] ReplaceMachineTags
|
||||
- [ ] ListMachineTags
|
||||
- [x] GetMachineTag
|
||||
- [x] DeleteMachineTag
|
||||
- [x] DeleteMachineTags
|
||||
- [x] DeleteMachine
|
||||
- [x] MachineAudit
|
||||
- [ ] Analytics
|
||||
- [ ] DescribeAnalytics
|
||||
- [ ] ListInstrumentations
|
||||
- [ ] GetInstrumentation
|
||||
- [ ] GetInstrumentationValue
|
||||
- [ ] GetInstrumentationHeatmap
|
||||
- [ ] GetInstrumentationHeatmapDetails
|
||||
- [ ] CreateInstrumentation
|
||||
- [ ] DeleteInstrumentation
|
||||
- [x] FirewallRules
|
||||
- [x] Firewall Rule Syntax
|
||||
- [x] ListFirewallRules
|
||||
- [x] GetFirewallRule
|
||||
- [x] CreateFirewallRule
|
||||
- [x] UpdateFirewallRule
|
||||
- [x] EnableFirewallRule
|
||||
- [x] DisableFirewallRule
|
||||
- [x] DeleteFirewallRule
|
||||
- [x] ListMachineFirewallRules
|
||||
- [x] ListFirewallRuleMachines
|
||||
- [ ] Fabrics
|
||||
- [ ] ListFabricVLANs
|
||||
- [ ] CreateFabricVLAN
|
||||
- [ ] GetFabricVLAN
|
||||
- [ ] UpdateFabricVLAN
|
||||
- [ ] DeleteFabricVLAN
|
||||
- [ ] ListFabricNetworks
|
||||
- [ ] CreateFabricNetwork
|
||||
- [ ] GetFabricNetwork
|
||||
- [ ] DeleteFabricNetwork
|
||||
- [x] Networks
|
||||
- [x] ListNetworks
|
||||
- [x] GetNetwork
|
||||
- [ ] Nics
|
||||
- [ ] ListNics
|
||||
- [ ] GetNic
|
||||
- [ ] AddNic
|
||||
- [ ] RemoveNic
|
||||
|
38
cloudapi-graphql/etc/containerpilot.json
Normal file
38
cloudapi-graphql/etc/containerpilot.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"consul": "{{ .CONSUL }}:8500",
|
||||
"services": [{
|
||||
"name": "joyent-portal-cloudapi-graphql",
|
||||
"port": 4000,
|
||||
"health": "/usr/bin/curl -o /dev/null --fail -s http://localhost:4000/graphql",
|
||||
"poll": 3,
|
||||
"ttl": 10
|
||||
}],
|
||||
"telemetry": {
|
||||
"port": 9090,
|
||||
"sensors": [{
|
||||
"name": "graphql_memory_percent",
|
||||
"help": "percentage of memory used",
|
||||
"type": "gauge",
|
||||
"poll": 5,
|
||||
"check": ["/bin/sensors", "memory"]
|
||||
}, {
|
||||
"name": "graphql_cpu_load",
|
||||
"help": "cpu load",
|
||||
"type": "gauge",
|
||||
"poll": 5,
|
||||
"check": ["/bin/sensors", "cpu"]
|
||||
}, {
|
||||
"name": "graphql_disk_capacity",
|
||||
"help": "disk capacity",
|
||||
"type": "gauge",
|
||||
"poll": 60,
|
||||
"check": ["/bin/sensors", "diskcapacity"]
|
||||
}, {
|
||||
"name": "graphql_disk_usage",
|
||||
"help": "disk usage",
|
||||
"type": "gauge",
|
||||
"poll": 60,
|
||||
"check": ["/bin/sensors", "diskusage"]
|
||||
}]
|
||||
}
|
||||
}
|
31
cloudapi-graphql/package.json
Normal file
31
cloudapi-graphql/package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "cloudapi-graphql",
|
||||
"private": true,
|
||||
"license": "MPL-2.0",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint src --fix",
|
||||
"start": "node src/index.js",
|
||||
"test": "make test"
|
||||
},
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.10",
|
||||
"dotenv": "^4.0.0",
|
||||
"express": "^4.15.2",
|
||||
"express-graphql": "^0.6.4",
|
||||
"got": "^6.7.1",
|
||||
"graphql": "^0.9.3",
|
||||
"smartdc-auth": "^2.5.2",
|
||||
"triton": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.19.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-prettier": "^1.7.0",
|
||||
"eslint-plugin-prettier": "^2.0.1",
|
||||
"nyc": "^10.2.0",
|
||||
"prettier": "^1.2.2",
|
||||
"tap-xunit": "^1.7.0"
|
||||
}
|
||||
}
|
9
cloudapi-graphql/src/api/account.js
Normal file
9
cloudapi-graphql/src/api/account.js
Normal file
@ -0,0 +1,9 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.get = () => {
|
||||
return request('getAccount');
|
||||
};
|
||||
|
||||
module.exports.update = ctx => {
|
||||
return request('updateAccount', ctx);
|
||||
};
|
5
cloudapi-graphql/src/api/config.js
Normal file
5
cloudapi-graphql/src/api/config.js
Normal file
@ -0,0 +1,5 @@
|
||||
// const request = require('./request');
|
||||
|
||||
module.exports.get = () => {
|
||||
// return request('', ctx);
|
||||
};
|
5
cloudapi-graphql/src/api/datacenters.js
Normal file
5
cloudapi-graphql/src/api/datacenters.js
Normal file
@ -0,0 +1,5 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports = () => {
|
||||
return request('listDatacenters');
|
||||
};
|
9
cloudapi-graphql/src/api/fabrics.js
Normal file
9
cloudapi-graphql/src/api/fabrics.js
Normal file
@ -0,0 +1,9 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listFirewallRules', {});
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getFirewallRule', ctx);
|
||||
};
|
37
cloudapi-graphql/src/api/firewall-rules.js
Normal file
37
cloudapi-graphql/src/api/firewall-rules.js
Normal file
@ -0,0 +1,37 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listFirewallRules', {});
|
||||
};
|
||||
|
||||
module.exports.listByMachine = ctx => {
|
||||
return request('listMachineFirewallRules', ctx);
|
||||
};
|
||||
|
||||
module.exports.listMachines = ctx => {
|
||||
return request('listFirewallRuleMachines', ctx);
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getFirewallRule', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createFirewallRule', ctx);
|
||||
};
|
||||
|
||||
module.exports.update = ctx => {
|
||||
return request('updateFirewallRule', ctx);
|
||||
};
|
||||
|
||||
module.exports.enable = ctx => {
|
||||
return request('enableFirewallRule', ctx);
|
||||
};
|
||||
|
||||
module.exports.disable = ctx => {
|
||||
return request('disableFirewallRule', ctx);
|
||||
};
|
||||
|
||||
module.exports.destroy = ctx => {
|
||||
return request('deleteFirewallRule', ctx);
|
||||
};
|
25
cloudapi-graphql/src/api/images.js
Normal file
25
cloudapi-graphql/src/api/images.js
Normal file
@ -0,0 +1,25 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = ctx => {
|
||||
return request('listImages', ctx);
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getImage', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createImageFromMachine', ctx);
|
||||
};
|
||||
|
||||
// module.exports.update = (ctx) => {
|
||||
// return request('UpdateImage', ctx);
|
||||
// };
|
||||
|
||||
module.exports.destroy = uuid => {
|
||||
return request('deleteImage', uuid);
|
||||
};
|
||||
|
||||
// module.exports.xport = (uuid) => {
|
||||
// return request('deleteImage', uuid);
|
||||
// };
|
16
cloudapi-graphql/src/api/index.js
Normal file
16
cloudapi-graphql/src/api/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
account: require('./account'),
|
||||
users: require('./users'),
|
||||
policies: require('./policies'),
|
||||
roles: require('./roles'),
|
||||
keys: require('./keys'),
|
||||
datacenters: require('./datacenters'),
|
||||
services: require('./services'),
|
||||
images: require('./images'),
|
||||
packages: require('./packages'),
|
||||
machines: require('./machines'),
|
||||
firewallRules: require('./firewall-rules'),
|
||||
// fabrics: require('./fabrics'),
|
||||
networks: require('./networks'),
|
||||
nics: require('./nics')
|
||||
};
|
35
cloudapi-graphql/src/api/keys.js
Normal file
35
cloudapi-graphql/src/api/keys.js
Normal file
@ -0,0 +1,35 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports = {
|
||||
user: {
|
||||
list: ctx => {
|
||||
return request('listUserKeys', ctx);
|
||||
},
|
||||
get: ctx => {
|
||||
return request('getUserKey', ctx);
|
||||
},
|
||||
create: ctx => {
|
||||
return request('createUserKey', ctx);
|
||||
},
|
||||
destroy: ctx => {
|
||||
return request('deleteUserKey', ctx);
|
||||
}
|
||||
},
|
||||
account: {
|
||||
list: () => {
|
||||
return request('listKeys', {});
|
||||
},
|
||||
|
||||
get: ctx => {
|
||||
return request('getKey', ctx);
|
||||
},
|
||||
|
||||
create: ctx => {
|
||||
return request('createKey', ctx);
|
||||
},
|
||||
|
||||
destroy: ctx => {
|
||||
return request('deleteKey', ctx);
|
||||
}
|
||||
}
|
||||
};
|
108
cloudapi-graphql/src/api/machines.js
Normal file
108
cloudapi-graphql/src/api/machines.js
Normal file
@ -0,0 +1,108 @@
|
||||
const request = require('./request');
|
||||
|
||||
const snapshots = {
|
||||
list: ctx => {
|
||||
return request('listMachineSnapshots', ctx);
|
||||
},
|
||||
get: ctx => {
|
||||
return request('getMachineSnapshot', ctx);
|
||||
},
|
||||
create: ctx => {
|
||||
return request('createMachineSnapshot', ctx);
|
||||
},
|
||||
destroy: ctx => {
|
||||
return request('deleteMachineSnapshot', ctx);
|
||||
}
|
||||
};
|
||||
|
||||
const metadata = {
|
||||
list: ctx => {
|
||||
return request('', ctx);
|
||||
},
|
||||
get: ctx => {
|
||||
return request('', ctx);
|
||||
},
|
||||
update: ctx => {
|
||||
return request('', ctx);
|
||||
},
|
||||
destroy: ctx => {
|
||||
return request('', ctx);
|
||||
}
|
||||
};
|
||||
|
||||
const firewall = {
|
||||
enable: ctx => {
|
||||
return request('enableMachineFirewall', ctx);
|
||||
},
|
||||
disable: ctx => {
|
||||
return request('disableMachineFirewall', ctx);
|
||||
}
|
||||
};
|
||||
|
||||
const tags = {
|
||||
list: ctx => {
|
||||
return request('listMachineTags', ctx);
|
||||
},
|
||||
get: ctx => {
|
||||
return request('getMachineTag', ctx);
|
||||
},
|
||||
add: ctx => {
|
||||
return request('addMachineTags', ctx);
|
||||
},
|
||||
replace: ctx => {
|
||||
return request('replaceMachineTags', ctx);
|
||||
},
|
||||
destroy: ctx => {
|
||||
const method = ctx.tag ? 'deleteMachineTag' : 'deleteMachineTags';
|
||||
return request(method, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.list = ctx => {
|
||||
return request('listMachines', ctx);
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getMachine', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createMachine', ctx);
|
||||
};
|
||||
|
||||
module.exports.stop = ctx => {
|
||||
return request('stopMachine', ctx);
|
||||
};
|
||||
|
||||
module.exports.start = uuid => {
|
||||
return request('startMachine', uuid);
|
||||
};
|
||||
|
||||
module.exports.startFromSnapshot = ctx => {
|
||||
return request('startMachineFromSnapshot', ctx);
|
||||
};
|
||||
|
||||
module.exports.reboot = ctx => {
|
||||
return request('rebootMachine', ctx);
|
||||
};
|
||||
|
||||
module.exports.resize = ctx => {
|
||||
return request('', ctx);
|
||||
};
|
||||
|
||||
module.exports.rename = ctx => {
|
||||
return request('', ctx);
|
||||
};
|
||||
|
||||
module.exports.destroy = ctx => {
|
||||
return request('deleteMachine', ctx);
|
||||
};
|
||||
|
||||
module.exports.audit = ctx => {
|
||||
return request('machineAudit', ctx);
|
||||
};
|
||||
|
||||
module.exports.snapshots = snapshots;
|
||||
module.exports.metadata = metadata;
|
||||
module.exports.firewall = firewall;
|
||||
module.exports.tags = tags;
|
9
cloudapi-graphql/src/api/networks.js
Normal file
9
cloudapi-graphql/src/api/networks.js
Normal file
@ -0,0 +1,9 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listNetworks');
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getNetwork', ctx);
|
||||
};
|
9
cloudapi-graphql/src/api/nics.js
Normal file
9
cloudapi-graphql/src/api/nics.js
Normal file
@ -0,0 +1,9 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listNics');
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getNic', ctx);
|
||||
};
|
9
cloudapi-graphql/src/api/packages.js
Normal file
9
cloudapi-graphql/src/api/packages.js
Normal file
@ -0,0 +1,9 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = ctx => {
|
||||
return request('listPackages', ctx);
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getPackage', ctx);
|
||||
};
|
21
cloudapi-graphql/src/api/policies.js
Normal file
21
cloudapi-graphql/src/api/policies.js
Normal file
@ -0,0 +1,21 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listPolicies');
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getPolicy', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createPolicy', ctx);
|
||||
};
|
||||
|
||||
module.exports.update = ctx => {
|
||||
return request('updatePolicy', ctx);
|
||||
};
|
||||
|
||||
module.exports.destroy = ctx => {
|
||||
return request('deletePolicy', ctx);
|
||||
};
|
40
cloudapi-graphql/src/api/request.js
Normal file
40
cloudapi-graphql/src/api/request.js
Normal file
@ -0,0 +1,40 @@
|
||||
const credentials = require('../credentials');
|
||||
const auth = require('smartdc-auth');
|
||||
const cloudapi = require('triton/lib/cloudapi2');
|
||||
const bunyan = require('bunyan');
|
||||
const pkg = require('../../package.json');
|
||||
|
||||
var log = bunyan.createLogger({
|
||||
name: pkg.name
|
||||
});
|
||||
|
||||
var client = cloudapi.createClient({
|
||||
log: log,
|
||||
url: credentials.url,
|
||||
account: credentials.account,
|
||||
user: credentials.user,
|
||||
sign: auth.cliSigner({
|
||||
log: log,
|
||||
keyId: credentials.keyId,
|
||||
user: credentials.account,
|
||||
subuser: credentials.user
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = (method, args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fn = client[method].bind(client);
|
||||
|
||||
const cb = (err, res) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(res);
|
||||
};
|
||||
|
||||
return args ? fn(args, cb) : fn(cb);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.client = client;
|
31
cloudapi-graphql/src/api/roles.js
Normal file
31
cloudapi-graphql/src/api/roles.js
Normal file
@ -0,0 +1,31 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listRoles');
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getRole', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createRole', ctx);
|
||||
};
|
||||
|
||||
module.exports.set = ctx => {
|
||||
const id = ctx.id ? `/${ctx.id}` : '';
|
||||
const resource = `/${request.client.account}/${ctx.resource}${id}`;
|
||||
|
||||
return request('setRoleTags', {
|
||||
roleTags: ctx.role,
|
||||
resource
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.update = ctx => {
|
||||
return request('updateRole', ctx);
|
||||
};
|
||||
|
||||
module.exports.destroy = ctx => {
|
||||
return request('deleteRole', ctx);
|
||||
};
|
5
cloudapi-graphql/src/api/services.js
Normal file
5
cloudapi-graphql/src/api/services.js
Normal file
@ -0,0 +1,5 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports = () => {
|
||||
return request('listServices');
|
||||
};
|
21
cloudapi-graphql/src/api/users.js
Normal file
21
cloudapi-graphql/src/api/users.js
Normal file
@ -0,0 +1,21 @@
|
||||
const request = require('./request');
|
||||
|
||||
module.exports.list = () => {
|
||||
return request('listUsers');
|
||||
};
|
||||
|
||||
module.exports.get = ctx => {
|
||||
return request('getUser', ctx);
|
||||
};
|
||||
|
||||
module.exports.create = ctx => {
|
||||
return request('createUser', ctx);
|
||||
};
|
||||
|
||||
module.exports.destroy = ctx => {
|
||||
return request('deleteUser', ctx);
|
||||
};
|
||||
|
||||
module.exports.update = ctx => {
|
||||
return request('updateUser', ctx);
|
||||
};
|
27
cloudapi-graphql/src/credentials.js
Normal file
27
cloudapi-graphql/src/credentials.js
Normal file
@ -0,0 +1,27 @@
|
||||
const json = (() => {
|
||||
try {
|
||||
const res = require('dotenv').config({
|
||||
path: '../.env',
|
||||
silent: true
|
||||
});
|
||||
|
||||
if (res.error) {
|
||||
throw res.error;
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
return require('../credentials.json');
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
url: process.env.SDC_URL || json.SDC_URL || json.url || '',
|
||||
account: process.env.SDC_ACCOUNT || json.SDC_ACCOUNT || json.account || '',
|
||||
user: process.env.SDC_USER || json.SDC_USER || json.user || '',
|
||||
keyId: process.env.SDC_KEY_ID || json.SDC_KEY_ID || json.keyId || ''
|
||||
};
|
8
cloudapi-graphql/src/endpoint.js
Normal file
8
cloudapi-graphql/src/endpoint.js
Normal file
@ -0,0 +1,8 @@
|
||||
const graphqlHTTP = require('express-graphql');
|
||||
const schema = require('./schema');
|
||||
|
||||
module.exports = graphqlHTTP(() => ({
|
||||
schema: schema,
|
||||
graphiql: true,
|
||||
pretty: true
|
||||
}));
|
14
cloudapi-graphql/src/index.js
Normal file
14
cloudapi-graphql/src/index.js
Normal file
@ -0,0 +1,14 @@
|
||||
const express = require('express');
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use('/graphql', require('./endpoint'));
|
||||
|
||||
const server = app.listen(4000, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
|
||||
console.log(`Listening at http://0.0.0.0:${server.address().port}/graphql`);
|
||||
});
|
10
cloudapi-graphql/src/schema/index.js
Normal file
10
cloudapi-graphql/src/schema/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
const graphql = require('graphql');
|
||||
const mutation = require('./mutations');
|
||||
const query = require('./queries');
|
||||
|
||||
const { GraphQLSchema } = graphql;
|
||||
|
||||
module.exports = new GraphQLSchema({
|
||||
query,
|
||||
mutation
|
||||
});
|
56
cloudapi-graphql/src/schema/mutations/account.js
Normal file
56
cloudapi-graphql/src/schema/mutations/account.js
Normal file
@ -0,0 +1,56 @@
|
||||
const AccountType = require('../types/login');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLBoolean, GraphQLString } = require('graphql');
|
||||
|
||||
module.exports.updateAccount = {
|
||||
type: AccountType,
|
||||
description: 'Update your account details',
|
||||
args: {
|
||||
email: {
|
||||
type: GraphQLString
|
||||
},
|
||||
company_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
first_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
last_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
address: {
|
||||
type: GraphQLString
|
||||
},
|
||||
postal_code: {
|
||||
type: GraphQLString
|
||||
},
|
||||
city: {
|
||||
type: GraphQLString
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString
|
||||
},
|
||||
country: {
|
||||
type: GraphQLString
|
||||
},
|
||||
phone: {
|
||||
type: GraphQLString
|
||||
},
|
||||
cns_enabled: {
|
||||
type: GraphQLBoolean
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.account.get().then(account => {
|
||||
return api.account.update(
|
||||
Object.assign(account, args, {
|
||||
firstName: args.first_name || account.firstName,
|
||||
lastName: args.first_name || account.lastName,
|
||||
companyName: args.company_name || account.companyName,
|
||||
postalCode: args.postal_code || account.postalCode
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
98
cloudapi-graphql/src/schema/mutations/firewall-rules.js
Normal file
98
cloudapi-graphql/src/schema/mutations/firewall-rules.js
Normal file
@ -0,0 +1,98 @@
|
||||
const FirewallRuleType = require('../types/firewall-rule');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLID, GraphQLBoolean, GraphQLString } = require('graphql');
|
||||
|
||||
module.exports.createFirewallRule = {
|
||||
type: FirewallRuleType,
|
||||
description: "Adds a new firewall rule for the specified account. This rule will be added to all the account's instances where it may be necessary",
|
||||
args: {
|
||||
enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Indicates if the rule is enabled (optional, false by default)'
|
||||
},
|
||||
rule: {
|
||||
type: GraphQLString,
|
||||
description: 'Firewall rule text'
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'Human-readable description for the rule (optional)'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.firewallRules.create({
|
||||
rule: args.rule,
|
||||
description: args.description,
|
||||
enabled: !!args.enabled
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.updateFirewallRule = {
|
||||
type: FirewallRuleType,
|
||||
description: 'Updates the given rule record and -- depending on rule contents -- adds/removes/updates the rule on all the required instances',
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Firewall rule id'
|
||||
},
|
||||
enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Indicates if the rule is enabled (optional, false by default)'
|
||||
},
|
||||
rule: {
|
||||
type: GraphQLString,
|
||||
description: 'Firewall rule text'
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'Human-readable description for the rule (optional)'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.firewallRules.update(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.enableFirewallRule = {
|
||||
type: FirewallRuleType,
|
||||
description: 'Enables the given firewall rule if it is disabled',
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Firewall rule id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.firewallRules.enable(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.disableFirewallRule = {
|
||||
type: FirewallRuleType,
|
||||
description: 'Disables the given firewall rule if it is enabled',
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Firewall rule id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.firewallRules.disable(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteFirewallRule = {
|
||||
type: FirewallRuleType,
|
||||
description: 'Removes the given firewall rule from all the required instances',
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Firewall rule id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.firewallRules.destroy(args);
|
||||
}
|
||||
};
|
54
cloudapi-graphql/src/schema/mutations/images.js
Normal file
54
cloudapi-graphql/src/schema/mutations/images.js
Normal file
@ -0,0 +1,54 @@
|
||||
const AccountType = require('../types/login');
|
||||
const DynamicObjectType = require('../types/dynamic-object');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLString,
|
||||
GraphQLList,
|
||||
GraphQLNonNull,
|
||||
GraphQLID
|
||||
} = require('graphql');
|
||||
|
||||
module.exports.createImage = {
|
||||
type: AccountType,
|
||||
description: 'Create a new custom image from an instance',
|
||||
args: {
|
||||
machine: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The prepared and stopped instance UUID from which the image is to be created'
|
||||
},
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The name of the custom image, e.g. "my-image". Maximum 512 characters. However, typical names should be much shorter, e.g. 5-20 characters'
|
||||
},
|
||||
version: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The version of the custom image, e.g. "1.0.0". Maximum 128 characters'
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'A short prose description of this image. Maximum 512 characters'
|
||||
},
|
||||
homepage: {
|
||||
type: GraphQLString,
|
||||
description: 'Homepage URL where users can find more information about the image. Maximum 128 characters'
|
||||
},
|
||||
eula: {
|
||||
type: GraphQLString,
|
||||
description: 'URL of the End User License Agreement (EULA) for the image. Maximum 128 characters'
|
||||
},
|
||||
acl: {
|
||||
type: new GraphQLList(GraphQLID),
|
||||
description: 'An array of user/account UUIDs to which to give read access to a private image. I.e. this is only relevant for images with public === false'
|
||||
},
|
||||
tags: {
|
||||
type: DynamicObjectType,
|
||||
description: 'An object of key/value pairs that allows clients to categorize images by any given criteria'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { create } = api.images;
|
||||
|
||||
return create(args);
|
||||
}
|
||||
};
|
16
cloudapi-graphql/src/schema/mutations/index.js
Normal file
16
cloudapi-graphql/src/schema/mutations/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { GraphQLObjectType } = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'RootMutationType',
|
||||
fields: Object.assign(
|
||||
require('./account'),
|
||||
require('./keys'),
|
||||
require('./users'),
|
||||
require('./roles'),
|
||||
require('./policies'),
|
||||
require('./machines'),
|
||||
require('./images'),
|
||||
require('./firewall-rules'),
|
||||
require('./snapshots')
|
||||
)
|
||||
});
|
49
cloudapi-graphql/src/schema/mutations/keys.js
Normal file
49
cloudapi-graphql/src/schema/mutations/keys.js
Normal file
@ -0,0 +1,49 @@
|
||||
const KeyType = require('../types/key');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLNonNull, GraphQLString, GraphQLID } = require('graphql');
|
||||
|
||||
module.exports.createKey = {
|
||||
type: KeyType,
|
||||
description: 'Uploads a new OpenSSH key to Triton for use in HTTP signing and SSH',
|
||||
args: {
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLString)
|
||||
},
|
||||
key: {
|
||||
type: new GraphQLNonNull(GraphQLString)
|
||||
},
|
||||
userId: {
|
||||
type: GraphQLID,
|
||||
description: 'UserId to add this key to. Leaving this in blank will add the key to the account'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const _api = args.userId ? api.keys.user : api.keys.account;
|
||||
return _api.create(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteKey = {
|
||||
type: GraphQLID,
|
||||
description: 'Deletes a single SSH key, by name or fingerprint',
|
||||
args: {
|
||||
name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
fingerprint: {
|
||||
type: GraphQLString
|
||||
},
|
||||
userId: {
|
||||
type: GraphQLID,
|
||||
description: 'UserId who this key belongs to. Leaving this in blank will delete an account key'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const _api = args.userId ? api.keys.user : api.keys.account;
|
||||
|
||||
return _api.destroy(args).then(() => {
|
||||
return args.name || args.fingerprint;
|
||||
});
|
||||
}
|
||||
};
|
320
cloudapi-graphql/src/schema/mutations/machines.js
Normal file
320
cloudapi-graphql/src/schema/mutations/machines.js
Normal file
@ -0,0 +1,320 @@
|
||||
const MachineType = require('../types/machine');
|
||||
const DynamicObjectType = require('../types/dynamic-object');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLNonNull,
|
||||
GraphQLString,
|
||||
GraphQLBoolean,
|
||||
GraphQLID,
|
||||
GraphQLList
|
||||
} = require('graphql');
|
||||
|
||||
module.exports.createMachine = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to provision an instance',
|
||||
args: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Friendly name for this instance; default is the first 8 characters of the machine id'
|
||||
},
|
||||
package: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'Id of the package to use on provisioning, obtained from ListPackages'
|
||||
},
|
||||
image: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The image UUID (from images { id })'
|
||||
},
|
||||
networks: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: 'Desired networks ids (from networks { id })'
|
||||
},
|
||||
locality: {
|
||||
type: MachineType.locality,
|
||||
description: 'Optionally specify which instances the new instance should be near or far from'
|
||||
},
|
||||
metadata: {
|
||||
type: DynamicObjectType,
|
||||
description: 'An arbitrary set of metadata key/value pairs can be set at provision time'
|
||||
},
|
||||
tags: {
|
||||
type: DynamicObjectType,
|
||||
description: 'An arbitrary set of tags can be set at provision time'
|
||||
},
|
||||
firewall_enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Completely enable or disable firewall for this instance. Default is false'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const resolveNames = (obj = {}, namespace) => {
|
||||
return Object.keys(obj).reduce((all, name) => {
|
||||
return Object.assign(all, {
|
||||
[`${namespace}.${name}`]: obj[name]
|
||||
});
|
||||
}, {});
|
||||
};
|
||||
|
||||
const tags = resolveNames(args.tags, 'tag');
|
||||
const metadata = resolveNames(args.tags, 'metadata');
|
||||
|
||||
const machine = Object.assign(
|
||||
{
|
||||
name: args.name,
|
||||
package: args['package'],
|
||||
image: args.image,
|
||||
networks: args.networks,
|
||||
locality: args.locality,
|
||||
firewall_enabled: args.firewall_enabled
|
||||
},
|
||||
tags,
|
||||
metadata
|
||||
);
|
||||
|
||||
return api.machines.create(machine);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.startMachine = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to boot up an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.start(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.startMachineFromSnapshot = {
|
||||
type: MachineType,
|
||||
description: 'If an instance is in the "stopped" state, you can choose to start the instance from the referenced snapshot',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The snapshot id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.startFromSnapshot(args).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.stopMachine = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to shut down an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.stop(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.rebootMachine = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to reboot an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.reboot(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteMachine = {
|
||||
type: DynamicObjectType,
|
||||
description: 'Allows you to completely destroy an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.destroy(args.id);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.auditMachine = {
|
||||
type: new GraphQLList(DynamicObjectType),
|
||||
description: "Provides a list of an instance's accomplished actions. Results are sorted from newest to oldest action",
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.machines.destroy(args.id);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.setMachineFirewall = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to set the firewall state for an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
enabled: {
|
||||
type: new GraphQLNonNull(GraphQLBoolean)
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { firewall } = api.machines;
|
||||
|
||||
const fn = args.enabled ? firewall.enable : firewall.disable;
|
||||
|
||||
return fn(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.enableMachineFirewall = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to enable the firewall for an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { firewall } = api.machines;
|
||||
|
||||
return firewall.enable(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.disableMachineFirewall = {
|
||||
type: MachineType,
|
||||
description: 'Allows you to completely disable the firewall of an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { firewall } = api.machines;
|
||||
|
||||
return firewall.disable(args.id).then(machine => {
|
||||
if (machine) {
|
||||
return machine;
|
||||
}
|
||||
|
||||
return api.machines.get(args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.addMachineTags = {
|
||||
type: DynamicObjectType,
|
||||
description: 'Set tags on the given instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
tags: {
|
||||
type: new GraphQLNonNull(DynamicObjectType),
|
||||
description: 'Tag name/value pairs'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { tags } = api.machines;
|
||||
|
||||
return tags.add(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.replaceMachineTags = {
|
||||
type: DynamicObjectType,
|
||||
description: 'Fully replace all tags on an instance with the given tags',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
tags: {
|
||||
type: new GraphQLNonNull(DynamicObjectType),
|
||||
description: 'Tag name/value pairs'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { tags } = api.machines;
|
||||
|
||||
return tags.replace(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteMachineTags = {
|
||||
type: DynamicObjectType,
|
||||
description: 'Deletes tags from an instance',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
tag: {
|
||||
type: GraphQLString,
|
||||
description: 'Tag name to remove. If value is not supplied, all machine tags are removed'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { tags } = api.machines;
|
||||
|
||||
return tags.destroy(args);
|
||||
}
|
||||
};
|
71
cloudapi-graphql/src/schema/mutations/policies.js
Normal file
71
cloudapi-graphql/src/schema/mutations/policies.js
Normal file
@ -0,0 +1,71 @@
|
||||
const PolicyType = require('../types/policy');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLNonNull,
|
||||
GraphQLString,
|
||||
GraphQLID,
|
||||
GraphQLList
|
||||
} = require('graphql');
|
||||
|
||||
module.exports.createPolicy = {
|
||||
type: PolicyType,
|
||||
description: 'Creates a new account policy',
|
||||
args: {
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The policy name'
|
||||
},
|
||||
rules: {
|
||||
type: new GraphQLNonNull(new GraphQLList(GraphQLString)),
|
||||
description: 'One or more Aperture sentences to be added to the current policy'
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'A description for this policy (Optional)'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.policies.create(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.updatePolicy = {
|
||||
type: PolicyType,
|
||||
description: 'Upgrades an existing account policy. Everything but id can be modified',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
},
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The policy name'
|
||||
},
|
||||
rules: {
|
||||
type: new GraphQLNonNull(new GraphQLList(GraphQLString)),
|
||||
description: 'One or more Aperture sentences to be added to the current policy'
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'A description for this policy (Optional)'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.policies.update(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deletePolicy = {
|
||||
type: GraphQLID,
|
||||
description: 'Delete an RBAC policy',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.policies.destroy(args).then(() => {
|
||||
return args.id;
|
||||
});
|
||||
}
|
||||
};
|
103
cloudapi-graphql/src/schema/mutations/roles.js
Normal file
103
cloudapi-graphql/src/schema/mutations/roles.js
Normal file
@ -0,0 +1,103 @@
|
||||
const RoleType = require('../types/role');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLNonNull,
|
||||
GraphQLString,
|
||||
GraphQLID,
|
||||
GraphQLList
|
||||
} = require('graphql');
|
||||
|
||||
module.exports.createRole = {
|
||||
type: RoleType,
|
||||
description: 'Create a new role for your account',
|
||||
args: {
|
||||
name: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: "The role's name"
|
||||
},
|
||||
policies: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's policies to be given to this role (Optional)"
|
||||
},
|
||||
members: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's user logins to be added to this role (Optional)"
|
||||
},
|
||||
default_members: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's user logins to be added to this role and have it enabled by default (Optional)"
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.roles.create(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.updateRole = {
|
||||
type: RoleType,
|
||||
description: 'Modifies an account role. Anything but id can be modified',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: "The role's name"
|
||||
},
|
||||
policies: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's policies to be given to this role (Optional)"
|
||||
},
|
||||
members: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's user logins to be added to this role (Optional)"
|
||||
},
|
||||
default_members: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: "This account's user logins to be added to this role and have it enabled by default (Optional)"
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.roles.update(args);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteRole = {
|
||||
type: GraphQLID,
|
||||
description: 'Remove a role',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.roles.destroy(args).then(() => {
|
||||
return args.id;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.setRoleTags = {
|
||||
type: RoleType.tag,
|
||||
description: "Sets the given role tags to the provided resource path. resource_path can be the path to any of the CloudAPI resources described in this document: account, keys, users, roles, policies, user's ssh keys, datacenters, images, packages, instances, analytics, instrumentations, firewall rules and networks.",
|
||||
args: {
|
||||
resource: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
description: 'The resource type e.g. `machines`, `policies`...'
|
||||
},
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'The resource id'
|
||||
},
|
||||
role: {
|
||||
type: new GraphQLNonNull(new GraphQLList(GraphQLString)),
|
||||
description: 'The list role-tags to be added to this resource'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { set } = api.roles;
|
||||
|
||||
return set(args);
|
||||
}
|
||||
};
|
60
cloudapi-graphql/src/schema/mutations/snapshots.js
Normal file
60
cloudapi-graphql/src/schema/mutations/snapshots.js
Normal file
@ -0,0 +1,60 @@
|
||||
const SnapshotType = require('../types/snapshot');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLNonNull, GraphQLString, GraphQLID } = require('graphql');
|
||||
|
||||
module.exports.createSnapshot = {
|
||||
type: SnapshotType,
|
||||
description: 'Allows you to take a snapshot of a machine instance',
|
||||
args: {
|
||||
machine: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'The name to assign to the new snapshot'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { snapshot: { create, get } } = api.machines;
|
||||
|
||||
const newArgs = {
|
||||
id: args.machine,
|
||||
name: args.name
|
||||
};
|
||||
|
||||
return create(newArgs).then(snapshot => {
|
||||
if (snapshot) {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
return get(newArgs);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteSnapshot = {
|
||||
type: GraphQLID,
|
||||
description: 'Deletes the specified snapshot of an instance',
|
||||
args: {
|
||||
machine: {
|
||||
type: new GraphQLNonNull(GraphQLID),
|
||||
description: 'The machine id'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'The name to assign to the new snapshot'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { snapshot: { destroy } } = api.machines;
|
||||
|
||||
const newArgs = {
|
||||
id: args.machine,
|
||||
name: args.name
|
||||
};
|
||||
|
||||
return destroy(newArgs).then(() => args.name);
|
||||
}
|
||||
};
|
144
cloudapi-graphql/src/schema/mutations/users.js
Normal file
144
cloudapi-graphql/src/schema/mutations/users.js
Normal file
@ -0,0 +1,144 @@
|
||||
const UserType = require('../types/login');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLNonNull, GraphQLString, GraphQLID } = require('graphql');
|
||||
|
||||
module.exports.createUser = {
|
||||
type: UserType,
|
||||
description: 'Creates a new user under an account',
|
||||
args: {
|
||||
login: {
|
||||
type: new GraphQLNonNull(GraphQLString)
|
||||
},
|
||||
email: {
|
||||
type: new GraphQLNonNull(GraphQLString)
|
||||
},
|
||||
password: {
|
||||
type: new GraphQLNonNull(GraphQLString)
|
||||
},
|
||||
company_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
first_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
last_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
address: {
|
||||
type: GraphQLString
|
||||
},
|
||||
postal_code: {
|
||||
type: GraphQLString
|
||||
},
|
||||
city: {
|
||||
type: GraphQLString
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString
|
||||
},
|
||||
country: {
|
||||
type: GraphQLString
|
||||
},
|
||||
phone: {
|
||||
type: GraphQLString
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.users.create(
|
||||
Object.assign(args, {
|
||||
firstName: args.first_name,
|
||||
lastName: args.first_name,
|
||||
companyName: args.company_name,
|
||||
postalCode: args.postal_code
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.deleteUser = {
|
||||
type: GraphQLID,
|
||||
description: 'Remove a user',
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.users.destroy(args).then(() => {
|
||||
return args.id;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.updateUser = {
|
||||
type: UserType,
|
||||
description: "Update a user's modifiable properties",
|
||||
args: {
|
||||
id: {
|
||||
type: new GraphQLNonNull(GraphQLID)
|
||||
},
|
||||
login: {
|
||||
type: GraphQLString
|
||||
},
|
||||
email: {
|
||||
type: GraphQLString
|
||||
},
|
||||
company_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
first_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
last_name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
address: {
|
||||
type: GraphQLString
|
||||
},
|
||||
postal_code: {
|
||||
type: GraphQLString
|
||||
},
|
||||
city: {
|
||||
type: GraphQLString
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString
|
||||
},
|
||||
country: {
|
||||
type: GraphQLString
|
||||
},
|
||||
phone: {
|
||||
type: GraphQLString
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
return api.users.update(
|
||||
Object.assign(args, {
|
||||
firstName: args.first_name,
|
||||
lastName: args.first_name,
|
||||
companyName: args.company_name,
|
||||
postalCode: args.postal_code
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// module.exports.changeUserPassword = {
|
||||
// type: UserType,
|
||||
// description: 'This is a separate rule for password changes, so different policies can be used for an user trying to modify other data, or only their own password',
|
||||
// args: {
|
||||
// id: {
|
||||
// type: new GraphQLNonNull(GraphQLID)
|
||||
// },
|
||||
// password: {
|
||||
// type: GraphQLString
|
||||
// },
|
||||
// password_confirmation: {
|
||||
// type: GraphQLString
|
||||
// }
|
||||
// },
|
||||
// resolve: (root, args) => {
|
||||
// return api.users.updatePassword(args);
|
||||
// }
|
||||
// };
|
13
cloudapi-graphql/src/schema/queries/account.js
Normal file
13
cloudapi-graphql/src/schema/queries/account.js
Normal file
@ -0,0 +1,13 @@
|
||||
const AccountType = require('../types/login');
|
||||
const api = require('../../api');
|
||||
|
||||
module.exports = {
|
||||
type: AccountType,
|
||||
resolve() {
|
||||
return api.account.get().then(account => {
|
||||
return Object.assign(account, {
|
||||
isUser: false
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
19
cloudapi-graphql/src/schema/queries/datacenters.js
Normal file
19
cloudapi-graphql/src/schema/queries/datacenters.js
Normal file
@ -0,0 +1,19 @@
|
||||
const DatacenterType = require('../types/datacenter');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(DatacenterType),
|
||||
resolve() {
|
||||
return api.datacenters().then(datacenters => {
|
||||
return Object.keys(datacenters).map(name => {
|
||||
return {
|
||||
url: datacenters[name],
|
||||
name
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
12
cloudapi-graphql/src/schema/queries/fabrics.js
Normal file
12
cloudapi-graphql/src/schema/queries/fabrics.js
Normal file
@ -0,0 +1,12 @@
|
||||
const FabricType = require('../types/fabrics');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(FabricType),
|
||||
resolve() {
|
||||
return api.fabrics.list();
|
||||
}
|
||||
};
|
20
cloudapi-graphql/src/schema/queries/firewall-rules.js
Normal file
20
cloudapi-graphql/src/schema/queries/firewall-rules.js
Normal file
@ -0,0 +1,20 @@
|
||||
const FirewallRuleType = require('../types/firewall-rule');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(FirewallRuleType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Filter on id'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.firewallRules;
|
||||
|
||||
return !args.id ? list() : get(args.id).then(rule => [rule]);
|
||||
}
|
||||
};
|
52
cloudapi-graphql/src/schema/queries/images.js
Normal file
52
cloudapi-graphql/src/schema/queries/images.js
Normal file
@ -0,0 +1,52 @@
|
||||
const ImageType = require('../types/image');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLBoolean, GraphQLString, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(ImageType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Filter on id'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on "friendly" name'
|
||||
},
|
||||
os: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the underlying operating system'
|
||||
},
|
||||
version: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the version'
|
||||
},
|
||||
public: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Filter public/private images'
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on image state. By default only active images are shown. Use "all" to list all images'
|
||||
},
|
||||
owner: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on owner UUID'
|
||||
},
|
||||
type: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on image type'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.images;
|
||||
|
||||
return args.id
|
||||
? get({
|
||||
id: args.id
|
||||
}).then(img => [img])
|
||||
: list(args);
|
||||
}
|
||||
};
|
20
cloudapi-graphql/src/schema/queries/index.js
Normal file
20
cloudapi-graphql/src/schema/queries/index.js
Normal file
@ -0,0 +1,20 @@
|
||||
const { GraphQLObjectType } = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'RootQueryType',
|
||||
fields: {
|
||||
account: require('./account'),
|
||||
users: require('./users'),
|
||||
policies: require('./policies'),
|
||||
roles: require('./roles'),
|
||||
datacenters: require('./datacenters'),
|
||||
services: require('./services'),
|
||||
images: require('./images'),
|
||||
packages: require('./packages'),
|
||||
machines: require('./machines'),
|
||||
firewallRules: require('./firewall-rules'),
|
||||
// fabrics: require('./fabrics')
|
||||
networks: require('./networks')
|
||||
// nics: require('./nics')
|
||||
}
|
||||
});
|
74
cloudapi-graphql/src/schema/queries/machines.js
Normal file
74
cloudapi-graphql/src/schema/queries/machines.js
Normal file
@ -0,0 +1,74 @@
|
||||
const MachineType = require('../types/machine');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLInt, GraphQLList, GraphQLString, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(MachineType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID
|
||||
},
|
||||
brand: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the type of instance (e.g. lx)'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Machine name to find (will make your list size 1, or 0 if nothing found)'
|
||||
},
|
||||
image: {
|
||||
type: GraphQLString,
|
||||
description: 'Image id; returns instances provisioned with that image'
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the current state (e.g. running)'
|
||||
},
|
||||
memory: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on the current size of the RAM deployed (in MiB)'
|
||||
},
|
||||
tombstone: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on instances destroyed in the last N minutes'
|
||||
},
|
||||
first: {
|
||||
type: GraphQLInt,
|
||||
description: 'Return a max of N instances; default is 1000 (which is also the maximum allowable result set size)'
|
||||
},
|
||||
after: {
|
||||
type: GraphQLInt,
|
||||
description: 'Get a `first` number of instances starting at this offset'
|
||||
},
|
||||
tags: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: 'Filter on existing tags'
|
||||
},
|
||||
docker: {
|
||||
type: GraphQLString,
|
||||
description: 'Whether to only list Docker instances, or only non-Docker instances, if present. Defaults to showing all instances.'
|
||||
},
|
||||
credentials: {
|
||||
type: GraphQLString,
|
||||
description: 'Whether to include the generated credentials for instances, if present. Defaults to false'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.machines;
|
||||
|
||||
const { after, first } = args;
|
||||
|
||||
const newArgs = Object.assign(args, {
|
||||
limit: first,
|
||||
offset: after
|
||||
});
|
||||
|
||||
return args.id
|
||||
? get({
|
||||
id: args.id
|
||||
}).then(machine => [machine])
|
||||
: list(newArgs);
|
||||
}
|
||||
};
|
19
cloudapi-graphql/src/schema/queries/networks.js
Normal file
19
cloudapi-graphql/src/schema/queries/networks.js
Normal file
@ -0,0 +1,19 @@
|
||||
const NetworkType = require('../types/network');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(NetworkType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.networks;
|
||||
|
||||
return !args.id ? list() : get(args).then(network => [network]);
|
||||
}
|
||||
};
|
19
cloudapi-graphql/src/schema/queries/nics.js
Normal file
19
cloudapi-graphql/src/schema/queries/nics.js
Normal file
@ -0,0 +1,19 @@
|
||||
const NicType = require('../types/nic');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLString } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(NicType),
|
||||
args: {
|
||||
mac: {
|
||||
type: GraphQLString
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.nics;
|
||||
|
||||
return !args.id ? list() : get(args).then(nic => [nic]);
|
||||
}
|
||||
};
|
56
cloudapi-graphql/src/schema/queries/packages.js
Normal file
56
cloudapi-graphql/src/schema/queries/packages.js
Normal file
@ -0,0 +1,56 @@
|
||||
const PackageType = require('../types/package');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLInt, GraphQLList, GraphQLString, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(PackageType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Filter on package id'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the "friendly" name'
|
||||
},
|
||||
memory: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on how much memory will by available (in MiB)'
|
||||
},
|
||||
disk: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on how much disk space will be available (in MiB)'
|
||||
},
|
||||
swap: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on how much swap space will be available (in MiB)'
|
||||
},
|
||||
lwps: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on maximum number of light-weight processes (threads) allowed'
|
||||
},
|
||||
vcpus: {
|
||||
type: GraphQLInt,
|
||||
description: 'Filter on number of vCPUs'
|
||||
},
|
||||
version: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the version'
|
||||
},
|
||||
group: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the group belonging to'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.packages;
|
||||
|
||||
return args.id
|
||||
? get({
|
||||
id: args.id
|
||||
}).then(pkg => [pkg])
|
||||
: list(args);
|
||||
}
|
||||
};
|
20
cloudapi-graphql/src/schema/queries/policies.js
Normal file
20
cloudapi-graphql/src/schema/queries/policies.js
Normal file
@ -0,0 +1,20 @@
|
||||
const PolicyType = require('../types/policy');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(PolicyType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: '`id` of the `PolicyType` to filter'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.policies;
|
||||
|
||||
return !args.id ? list() : get(args).then(policy => [policy]);
|
||||
}
|
||||
};
|
20
cloudapi-graphql/src/schema/queries/roles.js
Normal file
20
cloudapi-graphql/src/schema/queries/roles.js
Normal file
@ -0,0 +1,20 @@
|
||||
const RoleType = require('../types/role');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(RoleType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: '`id` or `name` of the `RoleType` to filter'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.roles;
|
||||
|
||||
return !args.id ? list() : get(args).then(role => [role]);
|
||||
}
|
||||
};
|
19
cloudapi-graphql/src/schema/queries/services.js
Normal file
19
cloudapi-graphql/src/schema/queries/services.js
Normal file
@ -0,0 +1,19 @@
|
||||
const ServiceType = require('../types/service');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(ServiceType),
|
||||
resolve() {
|
||||
return api.services().then(services => {
|
||||
return Object.keys(services).map(name => {
|
||||
return {
|
||||
url: services[name],
|
||||
name
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
26
cloudapi-graphql/src/schema/queries/users.js
Normal file
26
cloudapi-graphql/src/schema/queries/users.js
Normal file
@ -0,0 +1,26 @@
|
||||
const UserType = require('../types/login');
|
||||
const graphql = require('graphql');
|
||||
const api = require('../../api');
|
||||
|
||||
const { GraphQLList, GraphQLID } = graphql;
|
||||
|
||||
module.exports = {
|
||||
type: new GraphQLList(UserType),
|
||||
args: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: '`id` or `login` of the `UserType` to filter'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const { list, get } = api.users;
|
||||
|
||||
return !args.id
|
||||
? list()
|
||||
: get(args).then(user => [user]).then(user => {
|
||||
return Object.assign(user, {
|
||||
isUser: true
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
54
cloudapi-graphql/src/schema/types/audit.js
Normal file
54
cloudapi-graphql/src/schema/types/audit.js
Normal file
@ -0,0 +1,54 @@
|
||||
const DynamicObjectType = require('./dynamic-object');
|
||||
|
||||
const { GraphQLString, GraphQLObjectType, GraphQLBoolean } = require('graphql');
|
||||
|
||||
const CallerType = new GraphQLObjectType({
|
||||
name: 'CallerType',
|
||||
fields: {
|
||||
type: {
|
||||
type: GraphQLString,
|
||||
description: 'Authentication type for the action request. One of "basic", "operator", "signature" or "token"'
|
||||
},
|
||||
user: {
|
||||
type: GraphQLString,
|
||||
description: 'When the authentication type is "basic", this member will be present and include user login'
|
||||
},
|
||||
ip: {
|
||||
type: GraphQLString,
|
||||
description: 'The IP addresses this from which the action was requested. Not present if type is "operator"'
|
||||
},
|
||||
keyId: {
|
||||
type: GraphQLString,
|
||||
description: 'When authentication type is either "signature" or "token", SSH key identifier'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'AuditType',
|
||||
fields: {
|
||||
action: {
|
||||
type: GraphQLString,
|
||||
description: 'The name of the action'
|
||||
},
|
||||
parameters: {
|
||||
type: DynamicObjectType,
|
||||
description: 'The original set of parameters sent when the action was requested'
|
||||
},
|
||||
success: {
|
||||
type: GraphQLBoolean,
|
||||
description: "`true` or `false`, depending on the action's success",
|
||||
resolve: root => {
|
||||
return root.success === 'yes';
|
||||
}
|
||||
},
|
||||
caller: {
|
||||
type: CallerType,
|
||||
description: 'Account requesting the action'
|
||||
},
|
||||
time: {
|
||||
type: GraphQLString,
|
||||
description: 'When the action finished'
|
||||
}
|
||||
}
|
||||
});
|
14
cloudapi-graphql/src/schema/types/datacenter.js
Normal file
14
cloudapi-graphql/src/schema/types/datacenter.js
Normal file
@ -0,0 +1,14 @@
|
||||
const { GraphQLString, GraphQLObjectType } = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'DatacenterType',
|
||||
fields: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'location of the datacenter'
|
||||
},
|
||||
url: {
|
||||
type: GraphQLString
|
||||
}
|
||||
}
|
||||
});
|
41
cloudapi-graphql/src/schema/types/dynamic-object.js
Normal file
41
cloudapi-graphql/src/schema/types/dynamic-object.js
Normal file
@ -0,0 +1,41 @@
|
||||
const { GraphQLScalarType, Kind } = require('graphql');
|
||||
|
||||
const kinds = {
|
||||
[Kind.STRING]: ast => {
|
||||
return ast.value;
|
||||
},
|
||||
[Kind.BOOLEAN]: ast => {
|
||||
return kinds[Kind.STRING](ast);
|
||||
},
|
||||
[Kind.INT]: ast => {
|
||||
return Number(ast.value);
|
||||
},
|
||||
[Kind.FLOAT]: ast => {
|
||||
return kinds[Kind.INT](ast);
|
||||
},
|
||||
[Kind.OBJECT]: ast => {
|
||||
const value = Object.create(null);
|
||||
ast.fields.forEach(field => {
|
||||
value[field.name.value] = parseLiteral(field.value);
|
||||
});
|
||||
|
||||
return value;
|
||||
},
|
||||
[Kind.LIST]: ast => {
|
||||
return ast.values.map(parseLiteral);
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/taion/graphql-type-json/blob/master/src/index.js
|
||||
const parseLiteral = ast => {
|
||||
const kind = kinds[ast.kind];
|
||||
return kind ? kinds[ast.kind](ast) : null;
|
||||
};
|
||||
|
||||
// from http://stackoverflow.com/a/34229603
|
||||
module.exports = new GraphQLScalarType({
|
||||
name: 'DynamicObjectType',
|
||||
serialize: v => v,
|
||||
parseValue: v => v,
|
||||
parseLiteral: parseLiteral
|
||||
});
|
19
cloudapi-graphql/src/schema/types/fabric.js
Normal file
19
cloudapi-graphql/src/schema/types/fabric.js
Normal file
@ -0,0 +1,19 @@
|
||||
const { GraphQLString, GraphQLObjectType, GraphQLInt } = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'FabricsType',
|
||||
fields: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'A unique name to identify the VLAN'
|
||||
},
|
||||
vlan_id: {
|
||||
type: GraphQLInt,
|
||||
description: "A number from 0-4095 that indicates the VLAN's id"
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'An optional description of the VLAN'
|
||||
}
|
||||
}
|
||||
});
|
90
cloudapi-graphql/src/schema/types/firewall-rule.js
Normal file
90
cloudapi-graphql/src/schema/types/firewall-rule.js
Normal file
@ -0,0 +1,90 @@
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLString,
|
||||
GraphQLBoolean,
|
||||
GraphQLObjectType,
|
||||
GraphQLList,
|
||||
GraphQLID,
|
||||
GraphQLInt
|
||||
} = require('graphql');
|
||||
|
||||
const FirewallRuleSyntaxType = new GraphQLObjectType({
|
||||
name: 'FirewallRuleSyntaxType',
|
||||
fields: {
|
||||
text: {
|
||||
type: GraphQLString
|
||||
},
|
||||
from: {
|
||||
type: GraphQLString
|
||||
},
|
||||
to: {
|
||||
type: GraphQLString
|
||||
},
|
||||
action: {
|
||||
type: GraphQLString
|
||||
},
|
||||
protocol: {
|
||||
type: GraphQLString
|
||||
},
|
||||
port: {
|
||||
type: GraphQLInt
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'FirewallRuleType',
|
||||
// function to allow circular dependencies
|
||||
fields: () => ({
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Unique identifier for this rule'
|
||||
},
|
||||
enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Indicates if the rule is enabled',
|
||||
resolve: root => {
|
||||
return !!root.enabled;
|
||||
}
|
||||
},
|
||||
rule: {
|
||||
type: FirewallRuleSyntaxType,
|
||||
description: 'Firewall rule',
|
||||
resolve: ({ rule }) => {
|
||||
const regex = /from (.*?) to (.*?) (allow|deny) (.*?) port (\d*)/i;
|
||||
const tokens = rule.match(regex);
|
||||
|
||||
return {
|
||||
from: tokens[1],
|
||||
to: tokens[2],
|
||||
action: tokens[3],
|
||||
protocol: tokens[4],
|
||||
port: tokens[5],
|
||||
text: rule
|
||||
};
|
||||
}
|
||||
},
|
||||
global: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Indicates if the rule is global',
|
||||
resolve: root => {
|
||||
return !!root.global;
|
||||
}
|
||||
},
|
||||
description: {
|
||||
type: GraphQLString,
|
||||
description: 'Human-readable description for the rule'
|
||||
},
|
||||
machines: {
|
||||
// circular dependency
|
||||
type: new GraphQLList(require('./machine')),
|
||||
description: 'Lists all instances a firewall rule is applied to',
|
||||
resolve: root => {
|
||||
return api.firewallRules.listMachines({
|
||||
id: root.id
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
116
cloudapi-graphql/src/schema/types/image.js
Normal file
116
cloudapi-graphql/src/schema/types/image.js
Normal file
@ -0,0 +1,116 @@
|
||||
const DynamicObjectType = require('./dynamic-object');
|
||||
|
||||
const {
|
||||
GraphQLBoolean,
|
||||
GraphQLString,
|
||||
GraphQLObjectType,
|
||||
GraphQLInt,
|
||||
GraphQLList,
|
||||
GraphQLID
|
||||
} = require('graphql');
|
||||
|
||||
const ErrorType = new GraphQLObjectType({
|
||||
name: 'ErrorType',
|
||||
fields: {
|
||||
code: {
|
||||
type: GraphQLString,
|
||||
description: 'A CamelCase string code for this error, e.g. "PrepareImageDidNotRun". See GetImage docs for a table of error.code values'
|
||||
},
|
||||
message: {
|
||||
type: GraphQLString,
|
||||
description: 'A short description of the image creation failure'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ImageFileType = new GraphQLObjectType({
|
||||
name: 'ImageFileType',
|
||||
fields: {
|
||||
compression: {
|
||||
type: GraphQLString,
|
||||
description: 'The type of file compression used for the image file. One of "bzip2", "gzip", "none"'
|
||||
},
|
||||
sha1: {
|
||||
type: GraphQLString,
|
||||
description: 'SHA-1 hex digest of the file content. Used for corruption checking'
|
||||
},
|
||||
size: {
|
||||
type: GraphQLInt,
|
||||
description: 'File size in bytes'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'ImageType',
|
||||
description: 'An image contains the software packages that will be available on newly-provisioned instance. In the case of hardware virtual machines, the image also includes the operating system',
|
||||
fields: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Unique id for this image'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'The "friendly" name for this image'
|
||||
},
|
||||
os: {
|
||||
type: GraphQLString,
|
||||
description: 'The underlying operating system for this image'
|
||||
},
|
||||
version: {
|
||||
type: GraphQLString,
|
||||
description: 'The version for this image'
|
||||
},
|
||||
type: {
|
||||
type: GraphQLString,
|
||||
description: 'What kind of image this is. The values differ after v8.0.0+'
|
||||
},
|
||||
requirements: {
|
||||
type: DynamicObjectType,
|
||||
description: 'Contains a grouping of various minimum requirements for provisioning an instance with this image. For example "password" indicates that a password must be provided'
|
||||
},
|
||||
homepage: {
|
||||
type: GraphQLString,
|
||||
description: 'The URL for a web page with more detailed information for this image'
|
||||
},
|
||||
files: {
|
||||
type: new GraphQLList(ImageFileType),
|
||||
description: 'An array of image files that make up each image. Currently only a single file per image is supported'
|
||||
},
|
||||
published_at: {
|
||||
type: GraphQLString,
|
||||
description: 'The time this image has been made publicly available'
|
||||
},
|
||||
owner: {
|
||||
type: GraphQLString,
|
||||
description: 'The UUID of the user who owns this image'
|
||||
},
|
||||
public: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Indicates if this image is publicly available',
|
||||
resolve: root => {
|
||||
return !!root['public'];
|
||||
}
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString,
|
||||
description: 'The current state of the image. One of "active", "unactivated", "disabled", "creating", "failed"'
|
||||
},
|
||||
tags: {
|
||||
type: DynamicObjectType,
|
||||
description: 'An object of key/value pairs that allows clients to categorize images by any given criteria'
|
||||
},
|
||||
eula: {
|
||||
type: GraphQLString,
|
||||
description: 'URL of the End User License Agreement (EULA) for the image'
|
||||
},
|
||||
acl: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: 'Access Control List. An array of account UUIDs given access to a private image. The field is only relevant to private images'
|
||||
},
|
||||
error: {
|
||||
type: ErrorType,
|
||||
description: 'If state=="failed", resulting from CreateImageFromMachine failure, then there may be an error object of the form {"code": "<string error code>", "message": "<string desc>"}'
|
||||
}
|
||||
}
|
||||
});
|
16
cloudapi-graphql/src/schema/types/key.js
Normal file
16
cloudapi-graphql/src/schema/types/key.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { GraphQLString, GraphQLObjectType } = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'KeyType',
|
||||
fields: {
|
||||
name: {
|
||||
type: GraphQLString
|
||||
},
|
||||
fingerprint: {
|
||||
type: GraphQLString
|
||||
},
|
||||
key: {
|
||||
type: GraphQLString
|
||||
}
|
||||
}
|
||||
});
|
108
cloudapi-graphql/src/schema/types/login.js
Normal file
108
cloudapi-graphql/src/schema/types/login.js
Normal file
@ -0,0 +1,108 @@
|
||||
const KeyType = require('./key');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLBoolean,
|
||||
GraphQLString,
|
||||
GraphQLList,
|
||||
GraphQLObjectType,
|
||||
GraphQLID
|
||||
} = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'LoginType',
|
||||
fields: {
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Unique id for this user/account'
|
||||
},
|
||||
login: {
|
||||
type: GraphQLString,
|
||||
description: 'Account/Sub-user login name'
|
||||
},
|
||||
email: {
|
||||
type: GraphQLString,
|
||||
description: 'Email address'
|
||||
},
|
||||
company_name: {
|
||||
type: GraphQLString,
|
||||
resolve: root => {
|
||||
return !!root.company_name || root.companyName;
|
||||
}
|
||||
},
|
||||
first_name: {
|
||||
type: GraphQLString,
|
||||
resolve: root => {
|
||||
return !!root.first_name || root.firstName;
|
||||
}
|
||||
},
|
||||
last_name: {
|
||||
type: GraphQLString,
|
||||
resolve: root => {
|
||||
return !!root.last_name || root.lastName;
|
||||
}
|
||||
},
|
||||
address: {
|
||||
type: GraphQLString
|
||||
},
|
||||
postal_code: {
|
||||
type: GraphQLString,
|
||||
resolve: root => {
|
||||
return !!root.postal_code || root.postalCode;
|
||||
}
|
||||
},
|
||||
city: {
|
||||
type: GraphQLString
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString
|
||||
},
|
||||
country: {
|
||||
type: GraphQLString
|
||||
},
|
||||
phone: {
|
||||
type: GraphQLString
|
||||
},
|
||||
cns_enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'true if Triton CNS is enabled for account',
|
||||
resolve: root => {
|
||||
return root.isUser ? null : !!root.triton_cns_enabled;
|
||||
}
|
||||
},
|
||||
keys: {
|
||||
type: new GraphQLList(KeyType),
|
||||
description: 'Get keys for user/account',
|
||||
args: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on key name'
|
||||
},
|
||||
fingerprint: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on key fingerprint'
|
||||
}
|
||||
},
|
||||
resolve(root, args) {
|
||||
const _api = root.isUser ? api.keys.user : api.keys.account;
|
||||
|
||||
const { list, get } = _api;
|
||||
|
||||
const newArgs = Object.assign(args, {
|
||||
userId: root.id
|
||||
});
|
||||
|
||||
const filtered = args.name || args.fingerprint;
|
||||
return !filtered ? list(newArgs) : get(newArgs).then(key => [key]);
|
||||
}
|
||||
},
|
||||
updated: {
|
||||
type: GraphQLString,
|
||||
description: "When this user/account's details was last updated"
|
||||
},
|
||||
created: {
|
||||
type: GraphQLString,
|
||||
description: 'When this user/account was created'
|
||||
}
|
||||
}
|
||||
});
|
162
cloudapi-graphql/src/schema/types/machine.js
Normal file
162
cloudapi-graphql/src/schema/types/machine.js
Normal file
@ -0,0 +1,162 @@
|
||||
const DynamicObjectType = require('./dynamic-object');
|
||||
const SnapshotType = require('./snapshot');
|
||||
const api = require('../../api');
|
||||
|
||||
const {
|
||||
GraphQLBoolean,
|
||||
GraphQLString,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLObjectType,
|
||||
GraphQLInt,
|
||||
GraphQLList,
|
||||
GraphQLID
|
||||
} = require('graphql');
|
||||
|
||||
module.exports = new GraphQLObjectType({
|
||||
name: 'MachineType',
|
||||
description: 'An image contains the software packages that will be available on newly-provisioned instance. In the case of hardware virtual machines, the image also includes the operating system',
|
||||
// function to allow circular dependencies
|
||||
fields: () => ({
|
||||
id: {
|
||||
type: GraphQLID,
|
||||
description: 'Unique id for this instance'
|
||||
},
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'The "friendly" name for this instance'
|
||||
},
|
||||
brand: {
|
||||
type: GraphQLString,
|
||||
description: 'The type of instance (e.g. lx)'
|
||||
},
|
||||
state: {
|
||||
type: GraphQLString,
|
||||
description: 'The current state of this instance (e.g. running)'
|
||||
},
|
||||
image: {
|
||||
type: GraphQLString,
|
||||
description: 'The image id this instance was provisioned with'
|
||||
},
|
||||
memory: {
|
||||
type: GraphQLInt,
|
||||
description: 'The amount of RAM this instance has (in MiB)'
|
||||
},
|
||||
disk: {
|
||||
type: GraphQLInt,
|
||||
description: 'The amount of disk this instance has (in MiB)'
|
||||
},
|
||||
metadata: {
|
||||
type: DynamicObjectType,
|
||||
description: 'Any additional metadata this instance has'
|
||||
},
|
||||
tags: {
|
||||
type: DynamicObjectType,
|
||||
description: 'Any tags this instance has',
|
||||
args: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the name of the tag'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { tags: { get } } = api.machines;
|
||||
|
||||
return !args.name
|
||||
? root.tags
|
||||
: get({
|
||||
id: root.id,
|
||||
tag: args.name
|
||||
}).then(value => {
|
||||
return {
|
||||
[args.name]: value
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
created: {
|
||||
type: GraphQLString,
|
||||
description: 'When this instance was created'
|
||||
},
|
||||
updated: {
|
||||
type: GraphQLString,
|
||||
description: "When this instance's details was last updated"
|
||||
},
|
||||
docker: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Whether this instance is a Docker container, if present',
|
||||
resolve: root => {
|
||||
return !!root.docker;
|
||||
}
|
||||
},
|
||||
ips: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: 'The IP addresses this instance has'
|
||||
},
|
||||
networks: {
|
||||
type: new GraphQLList(GraphQLString),
|
||||
description: 'The network UUIDs of the nics this instance has'
|
||||
},
|
||||
primaryIp: {
|
||||
type: GraphQLString,
|
||||
description: 'IP address of the primary nic of this instance'
|
||||
},
|
||||
firewall_enabled: {
|
||||
type: GraphQLBoolean,
|
||||
description: 'Whether firewall rules are enforced on this instance',
|
||||
resolve: root => {
|
||||
return !!root.firewall_enabled;
|
||||
}
|
||||
},
|
||||
firewall_rules: {
|
||||
// circular dependency
|
||||
type: new GraphQLList(require('./firewall-rule')),
|
||||
description: 'List of FirewallRules affecting this machine',
|
||||
resolve: root => {
|
||||
return api.firewallRules.listByMachine(root.id);
|
||||
}
|
||||
},
|
||||
compute_node: {
|
||||
type: GraphQLString,
|
||||
description: 'UUID of the server on which the instance is located'
|
||||
},
|
||||
package: {
|
||||
type: GraphQLString,
|
||||
description: 'The id or name of the package used to create this instance'
|
||||
},
|
||||
snapshots: {
|
||||
type: new GraphQLList(SnapshotType),
|
||||
description: 'The snapshots based on this instance',
|
||||
args: {
|
||||
name: {
|
||||
type: GraphQLString,
|
||||
description: 'Filter on the name of the snapshot'
|
||||
}
|
||||
},
|
||||
resolve: (root, args) => {
|
||||
const { snapshot: { list, get } } = api.machines;
|
||||
|
||||
return !args.id
|
||||
? list(root)
|
||||
: get({
|
||||
id: root.id,
|
||||
name: args.name
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
module.exports.locality = new GraphQLInputObjectType({
|
||||
name: 'LocalityType',
|
||||
fields: {
|
||||
strict: {
|
||||
type: GraphQLBoolean
|
||||
},
|
||||
near: {
|
||||
type: new GraphQLList(GraphQLID)
|
||||
},
|
||||
far: {
|
||||
type: new GraphQLList(GraphQLID)
|
||||
}
|
||||
}
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user