From 8b9e8bdc0fa7c9c4bb4024567d562af558385e1e Mon Sep 17 00:00:00 2001 From: Shulhan Date: Thu, 4 Jun 2020 02:50:57 +0700 Subject: all: add magic command "#require:" Magic word `#require:` will ensure that the next statement will always executed when its skipped with start number. For example, given following script with line number 1: #require: 2: echo a 3: echo b 4: #require: 5: echo c ``` executing `awwan local script.aww 3`, will always execute line number 2 `echo a`, but not line number 5 (because its before line start 3). --- CHANGELOG.adoc | 21 ++++++++++- README.adoc | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- README.md | 78 ++++++++++++++++++++++++++++++++++++++-- awwan.go | 1 + command.go | 30 ++++++++++++++++ script.go | 16 +++++++++ 6 files changed, 244 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index db58a89..ab5972c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,6 +1,25 @@ = Changelog for awwan -== awwan 0.1.1 (2020.06.xx) +== awwan 0.2.0 (2020.06.xx) + +=== New features + +* all: add magic command "#require:" + +Magic word `#require:` will ensure that the next statement will always +executed when its skipped with start number. +For example, given following script with line number + + 1: #require: + 2: echo a + 3: echo b + 4: #require: + 5: echo c +``` + +executing `awwan local script.aww 3`, will always execute line +number 2 `echo a`, but not line number 5 (because its before line start 3). + === Enhancements diff --git a/README.adoc b/README.adoc index f69a268..8a740ab 100644 --- a/README.adoc +++ b/README.adoc @@ -85,8 +85,23 @@ $ awwan play cloud/myserver/script.aww 5 - The awwan script is similar to shell script. Each line started with '#' is a comment, except for special, magic words. -There are three magic words in the script: `#get:`, `#get!`, `#put:`, and -`#put!`. +There are five magic words in the script: `#require:`, `#get:`, `#get!`, +`#put:`, and `#put!`. + +Magic word `#require:` will ensure that the next statement will always +executed when its skipped with start number. +For example, given following script with line number + +``` +1: #require: +2: echo a +3: echo b +4: #require: +5: echo c +``` + +executing `awwan local script.aww 3`, will always execute line number 2 `echo +a`, but not line number 5 (because its before line start 3). Magic word `#get:` will copy file from remote server to your local file system. @@ -226,21 +241,29 @@ that match with "development" in current ".ssh/config" or in "~/.ssh/config". == EXAMPLE +To give you the taste of the idea, I will show you an example using the +working directory $WORKDIR as our base directory. + Let say that we have the working remote server named "myserver" at IP address -"1.2.3.4" using username "arch" on port "2222" in the current ".ssh/config" -file +"1.2.3.4" using username "arch" on port "2222". + +In the $WORKDIR, create directory ".ssh" and "config" file, ---- +$ mkdir -p .ssh +$ cat > .ssh/config < awwan.env < myserver/test.aww < myserver/test <>> arch@1.2.3.4:2222: 1: echo myserver myserver @@ -275,7 +310,6 @@ test 100% 9 0.4KB/s 00 >>> arch@1.2.3.4:2222: 3: cat /tmp/test Hi arch! - ---- That's it. @@ -355,6 +389,64 @@ Any keys that are duplicate will be merged and the last one will overwrite the previous one. +=== Use case of magic command `#require:` + +The magic command `#require:` is added to prevent running local command using +different project or configuration. + +The use case was derived from experience with `gcloud` and `kubectl` commands. +When you have more than one projects in GCP, you need to make sure that the +command that you run is using correct configuration. + +Here is the example of deploying Cloud Functions using local awwan script, + +``` +1: #require: +2: gcloud config configurations activate {{.Val "gcloud::config"}} +3: +4: ## Create PubSub topic. +5: +6: gcloud pubsub topics create {{.Val "CloudFunctions:log2slack:pubsub_topic"}} +7: +8: ## Create Logger Sink to Route the log to PubSub topic. +9: +10: gcloud logging sinks create {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +11: pubsub.googleapis.com/projects/{{.Val "gcloud::project"}}/topics/{{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +12: --log-filter=severity>=WARNING +13: +14: ## Create Cloud Functions to forward log to Slack. +15: +16: gcloud functions deploy Log2Slack \ +17: --source {{.ScriptDir}} \ +18: --entry-point Log2Slack \ +19: --runtime go113 \ +20: --trigger-topic {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +21: --set-env-vars SLACK_WEBHOOK_URL={{.Val "slack::slack_webhook_url"}} \ +22: --ingress-settings internal-only \ +23: --max-instances=5 +24: +25: ## Test the chains by publishing a message to Topic... +26: +27: gcloud pubsub topics \ +28: publish {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +29: --message='Hello World!' +``` + +When executing statement at line number 6, 10, 16 or 27 we need to make sure +that it always using the correct environment "gcloud::config", + + +``` +$ awwan local awwan/playground/CloudFunctions/log2slack/local.deploy.aww 27 +2020/06/04 01:48:38 >>> loading "/xxx//awwan.env" ... +2020/06/04 01:48:38 >>> loading "/xxx/awwan/dev/awwan.env" ... +2020/06/04 01:48:38 --- require 2: gcloud config configurations activate dev + +Activated [dev]. +2020/06/04 01:48:38 >>> local 29: gcloud pubsub topics publish logs +--message='Hello World!' +``` + == BUGS Shell pipe "|", "<", or ">" does not work in the script, yet. diff --git a/README.md b/README.md index 7a16dac..7d2fe5e 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,23 @@ $ awwan play cloud/myserver/script.aww 5 - The awwan script is similar to shell script. Each line started with '#' is a comment, except for special, magic words. -There are three magic words in the script: `#get:`, `#get!`, `#put:`, and -`#put!`. +There are five magic words in the script: `#require:`, `#get:`, `#get!`, +`#put:`, and `#put!`. + +Magic word `#require:` will ensure that the next statement will always +executed when its skipped with start number. +For example, given following script with line number + +``` +1: #require: +2: echo a +3: echo b +4: #require: +5: echo c +``` + +executing `awwan local script.aww 3`, will always execute line number 2 `echo +a`, but not line number 5 (because its before line start 3). Magic word `#get:` will copy file from remote server to your local file system. @@ -367,6 +382,65 @@ Any keys that are duplicate will be merged and the last one will overwrite the previous one. +### Use case of magic command `#require:` + +The magic command `#require:` is added to prevent running local command using +different project or configuration. + +The use case was derived from experience with `gcloud` and `kubectl` commands. +When you have more than one projects in GCP, you need to make sure that the +command that you run is using correct configuration. + +Here is the example of deploying Cloud Functions using local awwan script, + +``` +1: #require: +2: gcloud config configurations activate {{.Val "gcloud::config"}} +3: +4: ## Create PubSub topic. +5: +6: gcloud pubsub topics create {{.Val "CloudFunctions:log2slack:pubsub_topic"}} +7: +8: ## Create Logger Sink to Route the log to PubSub topic. +9: +10: gcloud logging sinks create {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +11: pubsub.googleapis.com/projects/{{.Val "gcloud::project"}}/topics/{{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +12: --log-filter=severity>=WARNING +13: +14: ## Create Cloud Functions to forward log to Slack. +15: +16: gcloud functions deploy Log2Slack \ +17: --source {{.ScriptDir}} \ +18: --entry-point Log2Slack \ +19: --runtime go113 \ +20: --trigger-topic {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +21: --set-env-vars SLACK_WEBHOOK_URL={{.Val "slack::slack_webhook_url"}} \ +22: --ingress-settings internal-only \ +23: --max-instances=5 +24: +25: ## Test the chains by publishing a message to Topic... +26: +27: gcloud pubsub topics \ +28: publish {{.Val "CloudFunctions:log2slack:pubsub_topic"}} \ +29: --message='Hello World!' +``` + +When executing statement at line number 6, 10, 16 or 27 we need to make sure +that it always using the correct environment "gcloud::config", + +``` +$ awwan local awwan/playground/CloudFunctions/log2slack/local.deploy.aww 27 +2020/06/04 01:48:38 >>> loading "/xxx//awwan.env" ... +2020/06/04 01:48:38 >>> loading "/xxx/awwan/dev/awwan.env" ... +2020/06/04 01:48:38 --- require 2: gcloud config configurations activate dev + +Activated [dev]. +2020/06/04 01:48:38 >>> local 29: gcloud pubsub topics publish logs +--message='Hello World!' +``` + + + ## BUGS Shell pipe "|", "<", or ">" does not work in the script, yet. diff --git a/awwan.go b/awwan.go index 9db0b74..af16608 100644 --- a/awwan.go +++ b/awwan.go @@ -18,4 +18,5 @@ var ( cmdMagicPut = []byte("#put:") cmdMagicSudoGet = []byte("#get!") cmdMagicSudoPut = []byte("#put!") + cmdMagicRequire = []byte("#require:") ) diff --git a/command.go b/command.go index f116a5b..d2f9634 100644 --- a/command.go +++ b/command.go @@ -106,6 +106,11 @@ func (cmd *Command) doPlay() { } }() + err = cmd.executeRequires() + if err != nil { + log.Fatal(err) + } + cmd.executeScript() } @@ -125,6 +130,10 @@ func (cmd *Command) doLocal() { } }() + err = cmd.executeRequires() + if err != nil { + log.Fatal(err) + } cmd.executeLocalScript() } @@ -297,6 +306,27 @@ func (cmd *Command) executeLocalScript() { } } +// +// executeRequires run the #require: statements. +// +func (cmd *Command) executeRequires() (err error) { + for x := 0; x < cmd.env.scriptStart; x++ { + stmt := cmd.script.requires[x] + if len(stmt) == 0 { + continue + } + + log.Printf("--- require %d: %s\n\n", x, stmt) + + err = exec.Run(string(stmt), os.Stdout, os.Stderr) + if err != nil { + return err + } + } + + return nil +} + func (cmd *Command) executeScript() { for x := cmd.env.scriptStart; x <= cmd.env.scriptEnd; x++ { stmt := cmd.script.Statements[x] diff --git a/script.go b/script.go index 4180c3e..5660050 100644 --- a/script.go +++ b/script.go @@ -17,6 +17,7 @@ import ( // script define the content of ".aww" file, line by line. // type script struct { + requires [][]byte Statements [][]byte } @@ -37,6 +38,8 @@ func newScript(env *Environment, path string) *script { env.scriptEnd = len(s.Statements) - 1 } + s.parseMagicRequire() + return s } @@ -115,3 +118,16 @@ func (s *script) join() { x = y } } + +func (s *script) parseMagicRequire() { + s.requires = make([][]byte, len(s.Statements)) + + for x, stmt := range s.Statements { + if !bytes.HasPrefix(stmt, cmdMagicRequire) { + continue + } + if len(s.Statements) > x+1 { + s.requires[x+1] = bytes.TrimSpace(s.Statements[x+1]) + } + } +} -- cgit v1.3