Skip to content

Commit

Permalink
add playground (#20)
Browse files Browse the repository at this point in the history
* add playground

* run npm install

* update docker

* dont ignore babelrc

* update playground

* inception

* disable spellcheck

* change to beta
  • Loading branch information
jwaldrip committed Mar 3, 2017
1 parent 0908742 commit 9b678ab
Show file tree
Hide file tree
Showing 18 changed files with 390 additions and 29 deletions.
10 changes: 10 additions & 0 deletions .babelrc
@@ -0,0 +1,10 @@
{
"presets": [
"es2015",
"stage-0",
"react"
],
"ignore": [
"node_modules"
]
}
6 changes: 6 additions & 0 deletions .dockerignore
@@ -0,0 +1,6 @@
*
!src
!assets
!shard.*
!package.json
!.babelrc
26 changes: 26 additions & 0 deletions .eslintrc
@@ -0,0 +1,26 @@
{
"parser": "babel-eslint",
"plugins": [
"babel",
"react"
],
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"node": true,
"browser": true
},
"rules": {
"react/prop-types": ["error", { ignore: ["relay"] }],
"import/newline-after-import": 0,
"import/no-dynamic-require": 0,
"import/prefer-default-export": 0,
"class-methods-use-this": 0,
"global-require": 0,
},
"extends": ["eslint:recommended", "plugin:react/recommended"]
}
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -4,5 +4,5 @@
/tmp
.idea/
/bin/psykube
/.psykube.yml
/spec/.psykube.yml
/node_modules
16 changes: 16 additions & 0 deletions .psykube.yml
@@ -0,0 +1,16 @@
name: psykube
type: Deployment
registry_user: jwaldrip

command: "playground"

args: [ "--bind=0.0.0.0" ]
ports:
http: 8080
ingress:
tls: true
host: psykube.commercialtribe.ninja

clusters:
default:
context: gke_commercial-tribe_us-east1-c_staging
22 changes: 14 additions & 8 deletions .travis.yml
Expand Up @@ -23,11 +23,17 @@ before_deploy:
- shards build --release
- tar -czC ./bin psykube > ./psykube-$TRAVIS_OS_NAME-$TRAVIS_TAG.tar.gz
deploy:
skip_cleanup: true
provider: releases
api_key:
secure: lxW4Yi1u760t88HseNYRfLb7tIOuqCQZfjVC29ivAZa2i9Utb4yqrc+Q3bgXdcp55DzeiVcwpsUcYf5kgJCwzcrR2+DglYc7r2BBTvPhLABXkRBWA4CVILyblm7UWQAYjAB7VxzWV/IraiYhaPz+0WQsWPY1nQbIycsmHWjuKDe8awl8khA8VfoKj+H33dHwJkVfvKT97t+s6beXYKJ+Er/Y48rhaya2x8ebUiTlLpwYUDN8wrJHGDAuYEQrH5xNeS6PNHnOWLw6u/RYjtLeXjcXBw0b7SzhadrqwBwD4BxYWtnre/trPeVg2QkqNbZi2HF6uftCWKZM5b6I6wbJ5lRxisTdNdZujfmdW6UXw4oeHcR0YGv0g5imN0K0HVvBDSyelmfm1nfRvu0Tnm3KTsWXqXoVjO4ZaWdg4LOVgMH9arArK5sAsmzPvZpcOkRjKb4do8m4Y54XxQeU2/5VewX3BX0+BMXLAwlh3BQIDiNQtMdw+j/wuegvMgREhVmAthDVAVstkiW/PhP6c+TtkmgOQ00d812brt2qNYauGgHYdemkrtR/JL//9pZya65fKkpNnK0JpDCsfKiJc24w/XCruXMarbHXfYKz3mVarqYVQKB79VgtG7ff8ziMWI8IuJ8qlRqTBzfiAjHlZUru5p1nf92aZ8YYssU7y61fcZQ=
file: "./psykube-$TRAVIS_OS_NAME-$TRAVIS_TAG.tar.gz"
on:
repo: CommercialTribe/psykube
tags: true
- provider: releases
skip_cleanup: true
api_key:
secure: lxW4Yi1u760t88HseNYRfLb7tIOuqCQZfjVC29ivAZa2i9Utb4yqrc+Q3bgXdcp55DzeiVcwpsUcYf5kgJCwzcrR2+DglYc7r2BBTvPhLABXkRBWA4CVILyblm7UWQAYjAB7VxzWV/IraiYhaPz+0WQsWPY1nQbIycsmHWjuKDe8awl8khA8VfoKj+H33dHwJkVfvKT97t+s6beXYKJ+Er/Y48rhaya2x8ebUiTlLpwYUDN8wrJHGDAuYEQrH5xNeS6PNHnOWLw6u/RYjtLeXjcXBw0b7SzhadrqwBwD4BxYWtnre/trPeVg2QkqNbZi2HF6uftCWKZM5b6I6wbJ5lRxisTdNdZujfmdW6UXw4oeHcR0YGv0g5imN0K0HVvBDSyelmfm1nfRvu0Tnm3KTsWXqXoVjO4ZaWdg4LOVgMH9arArK5sAsmzPvZpcOkRjKb4do8m4Y54XxQeU2/5VewX3BX0+BMXLAwlh3BQIDiNQtMdw+j/wuegvMgREhVmAthDVAVstkiW/PhP6c+TtkmgOQ00d812brt2qNYauGgHYdemkrtR/JL//9pZya65fKkpNnK0JpDCsfKiJc24w/XCruXMarbHXfYKz3mVarqYVQKB79VgtG7ff8ziMWI8IuJ8qlRqTBzfiAjHlZUru5p1nf92aZ8YYssU7y61fcZQ=
file: "./psykube-$TRAVIS_OS_NAME-$TRAVIS_TAG.tar.gz"
on:
repo: CommercialTribe/psykube
tags: true
- provider: script
skip_cleanup: true
script: ./bin/psykube apply default
on:
repo: CommercialTribe/psykube
tags: true
31 changes: 31 additions & 0 deletions Dockerfile
@@ -0,0 +1,31 @@
FROM crystallang/crystal:0.21.0

# Deps
RUN DEBIAN_FRONTEND=noninteractive apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install curl -y
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN DEBIAN_FRONTEND=noninteractive apt-get install nodejs -y
ADD . /build
WORKDIR /build
ENV NPM_CONFIG_LOGLEVEL warn

# Build
RUN shards build --release
RUN mv ./bin/psykube /usr/local/bin/psykube

# Move back to root
RUN mkdir /tmp/psykube
WORKDIR /tmp/psykube
RUN git init
RUN psykube init
RUN git config --global user.email "engineering@commercialtribe.com"
RUN git config --global user.name "CommercialTribe, Inc."
RUN git add -A
RUN git commit -m "initial commit"

# Cleanup
RUN apt-get remove nodejs -y
RUN apt-get purge
RUN rm -rf /build

ENTRYPOINT [ "/usr/local/bin/psykube" ]
114 changes: 114 additions & 0 deletions assets/app.jsx
@@ -0,0 +1,114 @@
import React from 'react';
import ReactDOM from 'react-dom';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { ocean } from 'react-syntax-highlighter/dist/styles';
import deepSort from 'deep-sort-object';

class App extends React.Component {
constructor(...props){
super(...props)
this.state = {
source: "",
result: "",
fetching: false,
};
}

setSource = e => {
clearTimeout(this.submitTimeout);
this.submitTimeout = setTimeout(this.submit, 100);
const source = e.target.value;
window.location.hash = source ? btoa(source) : '';
this.setState({ source })
}

handleNewHash = () => {
try {
this.setState({ source: atob(window.location.hash.replace(/^#/, "")) }, this.submit)
} catch (e) {
window.location.hash = ''
}
}

submit = () => {
clearTimeout(this.submitTimeout)
this.setState({ fetching: true })
fetch('/generate', {
method: 'POST',
body: this.state.source,
headers: new Headers({
'Content-Type': 'application/yaml'
})
}).then(
r => r.text()
).then(
text => new Promise(
r => r(JSON.stringify(deepSort(JSON.parse(text)), null, 2))
).catch(() => text)
).then(
result => this.setState({ result, fetching: false })
);
}

componentDidMount(){
window.addEventListener('hashchange', this.handleNewHash, false);
this.handleNewHash();
if (this.state.source) {
this.submit();
}
}

componentWillUnmount(){
window.removeEventListener('hashchange', this.handleNewHash, false);
}

render(){
const { fetching, result, source } = this.state;
const margin = 10;
const outerStyle = {
margin: 0,
width: "50vw",
height: "100vh",
float: "left",
overflow: "scroll",
}
const innerStyle = {
fontFamily: "monospace",
fontSize: 14,
height: "100%"
}
const innerInputStyle = {
...innerStyle,
margin,
padding: 0,
overflow: "visible",
width: `calc(100% - ${margin * 2}px)`,
height: `calc(100% - ${margin * 2}px)`,
border: 0,
resize: "none",
backgroundColor: "transparent",
backgroundImage: fetching ? 'url("/duck.png")' : 'none',
backgroundRepeat: "no-repeat",
backgroundPosition: "right top",
transition: "background-image 1s"
}
const sourceStyle = {
...outerStyle,
backgroundColor: "#ccc"
}
return (
<div>
<form style={sourceStyle} onSubmit={this.submit}>
<textarea spellCheck={false} onChange={this.setSource} style={innerInputStyle} value={source} />
</form>
<div style={outerStyle}>
<SyntaxHighlighter language="json" style={ocean} customStyle={innerStyle}>
{result}
</SyntaxHighlighter>
</div>
</div>
)
}
}

ReactDOM.render(<App />, document.getElementById("root"));
7 changes: 7 additions & 0 deletions docker-compose.yml
@@ -0,0 +1,7 @@
version: "3"
services:
web:
build: .
ports:
- 8080:8080
command: playground -b 0.0.0.0
26 changes: 26 additions & 0 deletions package.json
@@ -0,0 +1,26 @@
{
"name": "psykube",
"private": true,
"scripts": {
"build": "browserify assets/app.jsx -t [ babelify ] | uglifyjs 2> /dev/null"
},
"devDependencies": {
"babel-eslint": "^7.1.1",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"babel-preset-stage-0": "^6.22.0",
"babelify": "^7.3.0",
"browserify": "^14.1.0",
"deep-sort-object": "^1.0.1",
"eslint": "^3.16.1",
"eslint-plugin-babel": "^4.1.0",
"eslint-plugin-react": "^6.10.0",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"store": "^2.0.3",
"uglify-js": "^2.8.4"
},
"dependencies": {
"react-syntax-highlighter": "^5.0.0"
}
}
Binary file added psykube-ico.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions shard.lock
Expand Up @@ -4,6 +4,10 @@ shards:
github: jwaldrip/admiral.cr
version: 1.1.1

crouter:
github: jreinert/crouter
commit: 982f0986f9ceb6203742df475066c7ed9da8584c

crustache:
github: MakeNowJust/crustache
version: 2.3.0
Expand Down
4 changes: 3 additions & 1 deletion shard.yml
@@ -1,5 +1,5 @@
name: psykube
version: 1.5.2.8
version: 1.5.3.0.beta1

authors:
- Jason Waldrip <jwaldrip@commercialtribe.com>
Expand All @@ -13,6 +13,8 @@ dependencies:
github: MakeNowJust/crustache
admiral:
github: jwaldrip/admiral.cr
crouter:
github: jreinert/crouter

license: MIT

Expand Down
1 change: 1 addition & 0 deletions src/psykube/cli.cr
Expand Up @@ -22,4 +22,5 @@ class Psykube::CLI < Admiral::Command
register_sub_command history, Commands::History
register_sub_command rollback, Commands::Rollback
register_sub_command scale, Commands::Scale
register_sub_command playground, Commands::Playground
end
5 changes: 4 additions & 1 deletion src/psykube/cli/generate.cr
Expand Up @@ -8,9 +8,12 @@ class Psykube::Commands::Generate < Admiral::Command

define_help description: "Generate the kubernetes manifests."
define_flag image, description: "Override the docker image."
define_flag pretty : Bool, description: "Prettyify the output", default: true

def run
puts generator.to_json
if (io = @output_io).is_a?(IO::FileDescriptor)
flags.pretty ? generator.to_pretty_json(io) : generator.to_json(io)
end
rescue e : Generator::ValidationError
panic "Error: #{e.message}".colorize(:red)
end
Expand Down
65 changes: 65 additions & 0 deletions src/psykube/cli/playground.cr
@@ -0,0 +1,65 @@
require "ecr"
require "crouter"
require "http/server"
require "admiral"
require "./concerns/*"
{% system "npm install" %}

class Psykube::Commands::Playground < Admiral::Command
struct GenerateController
private getter context : HTTP::Server::Context
private getter params : HTTP::Params

def initialize(@context, @params)
end

def generate
if (body = context.request.body)
gen = Psykube::Generator::List.new(body)
gen.cluster_name = gen.manifest.clusters.keys.first? || "default"
gen.to_json(context.response)
end
rescue e : YAML::ParseException | Crustache::ParseError
context.response.status_code = 422
context.response << e.message
end
end

class Router < Crouter::Router
post "/generate", "GenerateController#generate"

get "/favicon.png" do
context.response.content_type = "image/png"
context.response << {{ `cat #{__DIR__}/../../../psykube-ico.png`.stringify }}
end

get "/duck.png" do
context.response.content_type = "image/png"
context.response << {{ `cat #{__DIR__}/../../../psykube-ico.png`.stringify }}
end

get "/app.js" do
context.response.content_type = "application/js"
context.response << {{ `npm run -s build`.stringify }}
end

get "/" do
context.response << {{ `cat #{__DIR__}/playground/index.html`.stringify }}
end
end

define_help description: "Start the playground."

define_flag bind, "The address to bind to.", short: 'b', default: "127.0.0.1"
define_flag port : Int32, "The port to bind to.", short: 'p', default: 8080

def run
handlers = [
HTTP::ErrorHandler.new,
HTTP::LogHandler.new,
Router.new,
]
puts "Listening on #{flags.bind}:#{flags.port}"
HTTP::Server.new(flags.bind, flags.port, handlers).listen
end
end

0 comments on commit 9b678ab

Please sign in to comment.