adjust CLI to runtime 0.2.0-alpha (#21)

* adjust CLI to runtime 0.2.0-alpha

* fix typo

* change to :latest image

* fix action-id to actions-id

* make runtime api version injectible

* remove redundant print
This commit is contained in:
Yaron Schneider 2019-07-25 11:13:04 -07:00 committed by GitHub
parent 4a499aecc6
commit d977e676d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 180 additions and 74 deletions

View File

@ -3,7 +3,7 @@
################################################################################ ################################################################################
GIT_VERSION = $(shell git describe --always --abbrev=7 --dirty) GIT_VERSION = $(shell git describe --always --abbrev=7 --dirty)
TARGETS ?= darwin TARGETS ?= darwin windows linux
ARCH ?= amd64 ARCH ?= amd64
CGO ?= 0 CGO ?= 0
@ -13,11 +13,11 @@ else
CLI_VERSION := edge CLI_VERSION := edge
endif endif
################################################################################ ifdef API_VERSION
# Go build details # RUNTIME_API_VERSION = $(API_VERSION)
################################################################################ else
RUNTIME_API_VERSION = 1.0
BASE_PACKAGE_NAME := github.com/actionscore/cli endif
################################################################################ ################################################################################
# Dependencies # # Dependencies #
@ -41,7 +41,7 @@ deps: dep
build: build:
for t in $(TARGETS); do \ for t in $(TARGETS); do \
CGO_ENABLED=$(CGO) GOOS=$$t GOARCH=$(ARCH) go build \ CGO_ENABLED=$(CGO) GOOS=$$t GOARCH=$(ARCH) go build \
-ldflags "-X $(BASE_PACKAGE_NAME)/pkg/version.version=$(CLI_VERSION)" \ -ldflags "-X main.version=$(CLI_VERSION) -X main.apiVersion=$(RUNTIME_API_VERSION)" \
-o dist/"$$t"_$(ARCH)/actions; \ -o dist/"$$t"_$(ARCH)/actions; \
done; done;

View File

@ -2,7 +2,7 @@
[![Build Status](https://dev.azure.com/azure-octo/Actions/_apis/build/status/builds/cli%20build?branchName=master)](https://dev.azure.com/azure-octo/Actions/_build/latest?definitionId=6&branchName=master) [![Build Status](https://dev.azure.com/azure-octo/Actions/_apis/build/status/builds/cli%20build?branchName=master)](https://dev.azure.com/azure-octo/Actions/_build/latest?definitionId=6&branchName=master)
The Actions CLI allows you to setup Actions on your local dev machine or on a Kubernetes cluster, provides debugging suppors, launches and manages Actions instances. The Actions CLI allows you to setup Actions on your local dev machine or on a Kubernetes cluster, provides debugging support, launches and manages Actions instances.
## Setup ## Setup
@ -12,6 +12,8 @@ The Actions CLI allows you to setup Actions on your local dev machine or on a Ku
### Usage ### Usage
#### Install Actions
To setup Actions on your local machine: To setup Actions on your local machine:
__*Note: For Windows users, run the cmd terminal in administrator mode*__ __*Note: For Windows users, run the cmd terminal in administrator mode*__
@ -29,3 +31,80 @@ $ actions init --kubernetes
⌛ Making the jump to hyperspace... ⌛ Making the jump to hyperspace...
✅ Success! Get ready to rumble ✅ Success! Get ready to rumble
``` ```
#### Launch Actions and your app
The Actions CLI lets you debug easily by launching both Actions and your app.
Logs from both the Actions Runtime and your app will be displayed in real time!
Example of launching Actions with a node app:
```
$ actions run --app-id nodeapp node app.js
```
Example of launching Actions with a node app listening on port 3000:
```
$ actions run --app-id nodeapp --app-port 3000 node app.js
```
Example of launching Actions on port 6000:
```
$ actions run --app-id nodeapp --app-port 3000 --port 6000 node app.js
```
#### Publish/Subscribe
To use pub-sub with your app, make sure that your app has a ```POST``` HTTP endpoint with some name, say ```myevent```.
This sample assumes your app is listening on port 3000.
Launch Actions and your app:
```
$ actions run --app-id nodeapp --app-port 3000 node app.js
```
Publish a message:
```
$ actions publish --app-id nodeapp --topic myevent
```
Publish a message with a payload:
```
$ actions publish --app-id nodeapp --topic myevent --payload '{ "name": "yoda" }'
```
#### Invoking
To test your endpoints with Actions, simply expose any ```POST``` HTTP endpoint.
For this sample, we'll assume a node app listening on port 300 with a ```/mymethod``` endpoint.
Launch Actions and your app:
```
$ actions run --app-id nodeapp --app-port 3000 node app.js
```
Invoke your app:
```
$ actions send --app-id nodeapp --method mymethod
```
#### List
To list all Actions instances running on your machine:
```
$ actions list
```
To list all Actions instances running in a Kubernetes cluster:
```
$ actions list --kubernetes
```

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/actionscore/cli/pkg/api"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -20,8 +21,9 @@ A serverless runtime for hyperscale, distributed systems`,
} }
// Execute adds all child commands to the root command // Execute adds all child commands to the root command
func Execute(version string) { func Execute(version, apiVersion string) {
RootCmd.Version = version RootCmd.Version = version
api.RuntimeAPIVersion = apiVersion
if err := RootCmd.Execute(); err != nil { if err := RootCmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -33,7 +33,7 @@ var RunCmd = &cobra.Command{
return return
} }
actionsRunId := uuid.String() actionsRunID := uuid.String()
if kubernetesMode { if kubernetesMode {
output, err := kubernetes.Run(&kubernetes.RunConfig{ output, err := kubernetes.Run(&kubernetes.RunConfig{
@ -148,7 +148,7 @@ var RunCmd = &cobra.Command{
<-appRunning <-appRunning
rundata.AppendRunData(&rundata.RunData{ rundata.AppendRunData(&rundata.RunData{
ActionsRunId: actionsRunId, ActionsRunId: actionsRunID,
AppId: output.AppID, AppId: output.AppID,
ActionsPort: output.ActionsPort, ActionsPort: output.ActionsPort,
AppPort: appPort, AppPort: appPort,
@ -161,7 +161,7 @@ var RunCmd = &cobra.Command{
<-sigCh <-sigCh
print.InfoStatusEvent(os.Stdout, "\nterminated signal recieved: shutting down") print.InfoStatusEvent(os.Stdout, "\nterminated signal recieved: shutting down")
rundata.ClearRunData(actionsRunId) rundata.ClearRunData(actionsRunID)
err = output.ActionsCMD.Process.Kill() err = output.ActionsCMD.Process.Kill()
if err != nil { if err != nil {

11
main.go
View File

@ -1,12 +1,15 @@
package main package main
import "github.com/actionscore/cli/cmd" import (
"github.com/actionscore/cli/cmd"
)
// Value for version is injected by the build // Values for version and apiVersion are injected by the build
var ( var (
version = "edge" version = ""
apiVersion = "1.0"
) )
func main() { func main() {
cmd.Execute(version) cmd.Execute(version, apiVersion)
} }

6
pkg/api/api.go Normal file
View File

@ -0,0 +1,6 @@
package api
// RuntimeAPIVersion represents the version for the Actions runtime API
var (
RuntimeAPIVersion = "1.0"
)

View File

@ -27,13 +27,13 @@ func List() ([]ListOutput, error) {
l := []ListOutput{} l := []ListOutput{}
for _, p := range podList.Items { for _, p := range podList.Items {
for _, c := range p.Spec.Containers { for _, c := range p.Spec.Containers {
if c.Name == "action" { if c.Name == "actionsrt" {
lo := ListOutput{} lo := ListOutput{}
for i, a := range c.Args { for i, a := range c.Args {
if a == "--app-port" { if a == "--app-port" {
port := c.Args[i+1] port := c.Args[i+1]
lo.AppPort = port lo.AppPort = port
} else if a == "--action-id" { } else if a == "--actions-id" {
id := c.Args[i+1] id := c.Args[i+1]
lo.AppID = id lo.AppID = id
} }

View File

@ -2,21 +2,14 @@ package publish
import ( import (
"bytes" "bytes"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/actionscore/cli/pkg/api"
"github.com/actionscore/cli/pkg/standalone" "github.com/actionscore/cli/pkg/standalone"
) )
type messageEnvelope struct {
Topic string `json:"topic,omitempty"`
Data interface{} `json:"data,omitempty"`
CreatedAt string `json:"createdAt"`
}
func PublishTopic(appID, topic, payload string) error { func PublishTopic(appID, topic, payload string) error {
if topic == "" { if topic == "" {
return errors.New("topic is missing") return errors.New("topic is missing")
@ -31,26 +24,14 @@ func PublishTopic(appID, topic, payload string) error {
for _, lo := range l { for _, lo := range l {
if lo.AppID == appID { if lo.AppID == appID {
m := messageEnvelope{ b := []byte{}
CreatedAt: time.Now().Format(time.RFC3339),
}
if payload != "" { if payload != "" {
var data interface{} b = []byte(payload)
err := json.Unmarshal([]byte(payload), &data)
if err != nil {
return err
}
m.Data = data
} }
b, err := json.Marshal(&m) url := fmt.Sprintf("http://localhost:%s/v%s/publish/%s", fmt.Sprintf("%v", lo.ActionsPort), api.RuntimeAPIVersion, topic)
if err != nil { _, err = http.Post(url, "application/json", bytes.NewBuffer(b))
return err
}
_, err = http.Post(fmt.Sprintf("http://localhost:%s/invoke/%s", fmt.Sprintf("%v", lo.ActionsPort), topic), "application/json", bytes.NewBuffer(b))
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,6 +6,8 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/actionscore/cli/pkg/api"
"github.com/actionscore/cli/pkg/standalone" "github.com/actionscore/cli/pkg/standalone"
) )
@ -17,7 +19,7 @@ func InvokeApp(appID, method, payload string) (string, error) {
for _, lo := range list { for _, lo := range list {
if lo.AppID == appID { if lo.AppID == appID {
r, err := http.Post(fmt.Sprintf("http://localhost:%s/invoke/%s", fmt.Sprintf("%v", lo.ActionsPort), method), "application/json", bytes.NewBuffer([]byte(payload))) r, err := http.Post(fmt.Sprintf("http://localhost:%s/v%s/invoke/%s", fmt.Sprintf("%v", lo.ActionsPort), api.RuntimeAPIVersion, method), "application/json", bytes.NewBuffer([]byte(payload)))
if err != nil { if err != nil {
return "", err return "", err
} }
@ -35,5 +37,5 @@ func InvokeApp(appID, method, payload string) (string, error) {
} }
} }
return "", fmt.Errorf("App id %s not found", appID) return "", fmt.Errorf("App ID %s not found", appID)
} }

View File

@ -30,18 +30,15 @@ type RunOutput struct {
AppCMD *exec.Cmd AppCMD *exec.Cmd
} }
type eventSource struct { type component struct {
APIVersion string `json:"apiVersion"` APIVersion string `json:"apiVersion"`
Kind string `json:"kind"` Kind string `json:"kind"`
Metadata struct { Metadata struct {
Name string `json:"name"` Name string `json:"name"`
} `json:"metadata"` } `json:"metadata"`
Spec struct { Spec struct {
Type string `json:"type"` Type string `json:"type"`
ConnectionInfo struct { ConnectionInfo map[string]string `json:"connectionInfo"`
RedisHost string `json:"redisHost"`
RedisPassword string `json:"redisPassword"`
} `json:"connectionInfo"`
} `json:"spec"` } `json:"spec"`
} }
@ -55,22 +52,22 @@ func getActionsCommand(appID string, actionsPort int, appPort int) (*exec.Cmd, i
actionsPort = port actionsPort = port
} }
actionsCMD := "action" actionsCMD := "actionsrt"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
actionsCMD = actionsCMD + ".exe" actionsCMD = fmt.Sprintf("%s.exe", actionsCMD)
} }
args := []string{"--action-id", appID, "--action-http-port", fmt.Sprintf("%v", actionsPort)} args := []string{"--actions-id", appID, "--actions-http-port", fmt.Sprintf("%v", actionsPort)}
if appPort > -1 { if appPort > -1 {
args = append(args, "--app-port") args = append(args, "--app-port")
args = append(args, fmt.Sprintf("%v", appPort)) args = append(args, fmt.Sprintf("%v", appPort))
} }
args = append(args, "--assigner-address") args = append(args, "--placement-address")
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
args = append(args, "localhost:6050") args = append(args, "localhost:6050")
args = append(args, "--action-grpc-port", "6051") args = append(args, "--actions-grpc-port", "6051")
} else { } else {
args = append(args, "localhost:50005") args = append(args, "localhost:50005")
} }
@ -87,30 +84,61 @@ func getAppCommand(actionsPort int, command string, args []string) (*exec.Cmd, e
return cmd, nil return cmd, nil
} }
func createStateEventSource() error { func createRedisStateStore() error {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
return err return err
} }
es := eventSource{ redisStore := component{
APIVersion: "actions.io/v1alpha1", APIVersion: "actions.io/v1alpha1",
Kind: "EventSource", Kind: "Component",
} }
es.Metadata.Name = "statestore" redisStore.Metadata.Name = "statestore"
es.Spec.Type = "actions.state.redis" redisStore.Spec.Type = "state.redis"
es.Spec.ConnectionInfo.RedisHost = "localhost:6379" redisStore.Spec.ConnectionInfo = map[string]string{}
es.Spec.ConnectionInfo.RedisPassword = "" redisStore.Spec.ConnectionInfo["redisHost"] = "localhost:6379"
redisStore.Spec.ConnectionInfo["redisPassword"] = ""
b, err := yaml.Marshal(&es) b, err := yaml.Marshal(&redisStore)
if err != nil { if err != nil {
return err return err
} }
os.Mkdir(path.Join(wd, "eventsources"), 0777) os.Mkdir(path.Join(wd, "components"), 0777)
err = ioutil.WriteFile(path.Join(path.Join(wd, "components"), "redis.yaml"), b, 0644)
if err != nil {
return err
}
err = ioutil.WriteFile(path.Join(path.Join(wd, "eventsources"), "redis.yaml"), b, 0644) return nil
}
func createRedisPubSub() error {
wd, err := os.Getwd()
if err != nil {
return err
}
redisMessageBus := component{
APIVersion: "actions.io/v1alpha1",
Kind: "Component",
}
redisMessageBus.Metadata.Name = "messagebus"
redisMessageBus.Spec.Type = "pubsub.redis"
redisMessageBus.Spec.ConnectionInfo = map[string]string{}
redisMessageBus.Spec.ConnectionInfo["redisHost"] = "localhost:6379"
redisMessageBus.Spec.ConnectionInfo["password"] = ""
b, err := yaml.Marshal(&redisMessageBus)
if err != nil {
return err
}
os.Mkdir(path.Join(wd, "components"), 0777)
err = ioutil.WriteFile(path.Join(path.Join(wd, "components"), "redis_messagebus.yaml"), b, 0644)
if err != nil { if err != nil {
return err return err
} }
@ -124,7 +152,12 @@ func Run(config *RunConfig) (*RunOutput, error) {
appID = strings.Replace(sillyname.GenerateStupidName(), " ", "-", -1) appID = strings.Replace(sillyname.GenerateStupidName(), " ", "-", -1)
} }
err := createStateEventSource() err := createRedisStateStore()
if err != nil {
return nil, err
}
err = createRedisPubSub()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,9 +17,7 @@ import (
) )
const baseDownloadURL = "https://actionsreleases.blob.core.windows.net/bin" const baseDownloadURL = "https://actionsreleases.blob.core.windows.net/bin"
const actionsImageURL = "actionscore.azurecr.io/actions:latest"
// this should be configurable by versioning
const actionsImageURL = "yaron2/actionsedge:v2"
func Init() error { func Init() error {
dir, err := getActionsDir() dir, err := getActionsDir()
@ -94,7 +92,7 @@ func installAssignerBinary(wg *sync.WaitGroup, errorChan chan<- error, dir strin
osPort = 6050 osPort = 6050
} }
err := runCmd("docker", "run", "--restart", "always", "-d", "-p", fmt.Sprintf("%v:50005", osPort), "--entrypoint", "./assigner", actionsImageURL) err := runCmd("docker", "run", "--restart", "always", "-d", "-p", fmt.Sprintf("%v:50005", osPort), "--entrypoint", "./placement", actionsImageURL)
if err != nil { if err != nil {
errorChan <- err errorChan <- err
return return
@ -105,7 +103,7 @@ func installAssignerBinary(wg *sync.WaitGroup, errorChan chan<- error, dir strin
func installActionsBinary(wg *sync.WaitGroup, errorChan chan<- error, dir string) { func installActionsBinary(wg *sync.WaitGroup, errorChan chan<- error, dir string) {
defer wg.Done() defer wg.Done()
actionsURL := fmt.Sprintf("%s/action_%s_%s.zip", baseDownloadURL, runtime.GOOS, runtime.GOARCH) actionsURL := fmt.Sprintf("%s/actionsrt_%s_%s.zip", baseDownloadURL, runtime.GOOS, runtime.GOARCH)
filepath, err := downloadFile(dir, actionsURL) filepath, err := downloadFile(dir, actionsURL)
if err != nil { if err != nil {
errorChan <- fmt.Errorf("Error downloading actions binary: %s", err) errorChan <- fmt.Errorf("Error downloading actions binary: %s", err)
@ -200,10 +198,12 @@ func moveFileToPath(filepath string) (string, error) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
p := os.Getenv("PATH") p := os.Getenv("PATH")
if !strings.Contains(strings.ToLower(string(p)), strings.ToLower("c:\\actions")) { if !strings.Contains(strings.ToLower(string(p)), strings.ToLower("c:\\actions")) {
runCmd("SETX", "PATH", p+";c:\\actions") err := runCmd("SETX", "PATH", p+";c:\\actions")
if err != nil {
return "", err
}
} }
runCmd("rename", "c:\\actions\\action", "c:\\actions\\action.exe") return "c:\\actions\\actionsrt.exe", nil
return "c:\\actions\\action.exe", nil
} }
destFilePath = path.Join("/usr/local/bin", fileName) destFilePath = path.Join("/usr/local/bin", fileName)