diff options
| author | Shulhan <m.shulhan@gmail.com> | 2020-06-04 02:50:57 +0700 |
|---|---|---|
| committer | Shulhan <m.shulhan@gmail.com> | 2020-06-04 02:50:57 +0700 |
| commit | 8b9e8bdc0fa7c9c4bb4024567d562af558385e1e (patch) | |
| tree | 65b47a25f2c07aa0ed616aacc947917face41014 | |
| parent | 9bf6863448581351232dcdc5aa06a50907542f60 (diff) | |
| download | awwan-8b9e8bdc0fa7c9c4bb4024567d562af558385e1e.tar.xz | |
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).
| -rw-r--r-- | CHANGELOG.adoc | 21 | ||||
| -rw-r--r-- | README.adoc | 110 | ||||
| -rw-r--r-- | README.md | 78 | ||||
| -rw-r--r-- | awwan.go | 1 | ||||
| -rw-r--r-- | command.go | 30 | ||||
| -rw-r--r-- | 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 <<EOF Host myserver Hostname 1.2.3.4 User arch Port 2222 - IdentityFile ~/.ssh/id_rsa + IdentityFile .ssh/myserver +EOF ---- -and the environment file "awwan.env" +Still in the $WORKDIR, create the environment file "awwan.env" ---- +$ cat > awwan.env <<EOF [all] user = arch host = myserver @@ -248,26 +271,38 @@ host = myserver [whitelist "ip"] alpha = 1.2.3.4/32 beta = 2.3.4.5/32 +EOF ---- -and script file "test.aww", +Inside the $WORKDIR we create the directory that match with our server name +and a script file "test.aww", ---- +$ mkdir -p myserver +$ cat > myserver/test.aww <<EOF echo {{.Val "all::host"}}` #put: {{.ScriptDir}}/test /tmp/ cat /tmp/test +EOF ---- and a template file "test", ---- +$ cat > myserver/test <<EOF Hi {{.Val "all::user"}}! +EOF ---- -When executed, it will print the following output to terminal, +When executed from start to end like these, ---- $ awwan play myserver/test.aww 1 - +---- + +it will print the following output to terminal, + +---- >>> 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. @@ -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. @@ -18,4 +18,5 @@ var ( cmdMagicPut = []byte("#put:") cmdMagicSudoGet = []byte("#get!") cmdMagicSudoPut = []byte("#put!") + cmdMagicRequire = []byte("#require:") ) @@ -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] @@ -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]) + } + } +} |
