mirror of
https://github.com/openziti/ziti.git
synced 2026-10-12 23:32:02 +00:00
Add support for ziti-for-each
This commit is contained in:
@@ -6,7 +6,7 @@ Before start
|
||||
|
||||
Part 1 - single router, single server
|
||||
1. show zcat and echo-server
|
||||
2. create first edge router
|
||||
2. create first edge router, mark with demo attribute
|
||||
3. client setup
|
||||
4. do sdk demo
|
||||
5. look at terminators
|
||||
@@ -14,4 +14,16 @@ Part 1 - single router, single server
|
||||
7. look at terminators
|
||||
|
||||
Part 2 - multi-router, single server
|
||||
1. Add additional edge router, mark with demo attribute
|
||||
2. start edge router 2
|
||||
3. Do sdk demo
|
||||
4. show multiple terminators
|
||||
5. Make sure it's tunneler ready
|
||||
a. make sure it's a tunneler
|
||||
b. Add identity attributes: ziti edge update identity edge-router-2 -a echo,echo-host
|
||||
6. Do tunneler demo
|
||||
7. Show multiple terminators
|
||||
|
||||
Part 3 - multi-router, multi-server, HS
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ ziti edge login
|
||||
```action:ziti
|
||||
ziti edge delete service echo
|
||||
ziti edge delete config echo-host
|
||||
ziti edge delete identities zcat echo-host
|
||||
ziti edge delete identities echo-host
|
||||
ziti edge delete service-policies echo-bind
|
||||
ziti edge delete edge-router-policies echo
|
||||
ziti edge delete service-edge-router-policies echo
|
||||
@@ -48,18 +48,13 @@ ziti edge delete service-edge-router-policies echo
|
||||
ziti edge create service echo -c echo-host -a echo
|
||||
```
|
||||
|
||||
## Select an edge router to host this service on
|
||||
## Update edge-routers
|
||||
|
||||
```action:select-edge-router
|
||||
Pick the name of an edge router to use. It will be referenced in this tutorial as ${edgeRouterName}
|
||||
```
|
||||
Make sure demo edge routers are tunneler enabled and the associated identity has the `echo-host` attribute
|
||||
|
||||
## Update edge router attributes
|
||||
|
||||
Update the select edge router with the `echo-host` attribute, so it gets tied into the policies.
|
||||
|
||||
```action:ziti templatize=true
|
||||
ziti edge update identity ${edgeRouterName} -a echo-host
|
||||
```action:ziti-for-each type=edge-routers minCount=1 maxCount=2 filter='anyOf(roleAttributes)="demo"'
|
||||
ziti edge update edge-router ${entityName} --tunneler-enabled
|
||||
ziti edge update identity ${entityName} --role-attributes echo-host
|
||||
```
|
||||
|
||||
## Configure policies
|
||||
|
||||
@@ -81,6 +81,7 @@ func (self *simpleRouterTunnelerHosted) run() error {
|
||||
t.RegisterActionHandler("keep-session-alive", &actions.KeepSessionAliveAction{})
|
||||
t.RegisterActionHandler("ziti-create-config", &actions.ZitiCreateConfigAction{})
|
||||
t.RegisterActionHandler("select-edge-router", &actions.SelectEdgeRouterAction{})
|
||||
t.RegisterActionHandler("ziti-for-each", &actions.ZitiForEach{})
|
||||
|
||||
return t.Run(simpleRouterTunnelerHostedScriptSource)
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ func runListEdgeRouters(roleFilters []string, roleSemantic string, options *api.
|
||||
if roleSemantic != "" {
|
||||
params.Add("roleSemantic", roleSemantic)
|
||||
}
|
||||
children, paging, err := api.ListEntitiesOfType("edge", "edge-routers", params, options.OutputJSONResponse, options.Out, options.Timeout, options.Verbose)
|
||||
children, paging, err := api.ListEntitiesOfType(util.EdgeAPI, "edge-routers", params, options.OutputJSONResponse, options.Out, options.Timeout, options.Verbose)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -679,16 +679,19 @@ func outputIdentities(o *api.Options, children []*gabs.Container, pagingInfo *ap
|
||||
return nil
|
||||
}
|
||||
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleRounded)
|
||||
t.AppendHeader(table.Row{"ID", "Name", "Type", "Attributes"})
|
||||
|
||||
for _, entity := range children {
|
||||
id, _ := entity.Path("id").Data().(string)
|
||||
name, _ := entity.Path("name").Data().(string)
|
||||
typeName, _ := entity.Path("type.name").Data().(string)
|
||||
roleAttributes := entity.Path("roleAttributes").String()
|
||||
if _, err := fmt.Fprintf(o.Out, "id: %v name: %v type: %v role attributes: %v\n", id, name, typeName, roleAttributes); err != nil {
|
||||
return err
|
||||
}
|
||||
wrapper := api.Wrap(entity)
|
||||
t.AppendRow(table.Row{
|
||||
wrapper.String("id"),
|
||||
wrapper.String("name"),
|
||||
wrapper.String("type.name"),
|
||||
strings.Join(wrapper.StringSlice("roleAttributes"), "\n")})
|
||||
}
|
||||
pagingInfo.Output(o)
|
||||
api.RenderTable(o, t, pagingInfo)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright NetFoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"github.com/openziti/ziti/ziti/cmd/ziti/cmd/api"
|
||||
"github.com/openziti/ziti/ziti/cmd/ziti/tutorial"
|
||||
"github.com/openziti/ziti/ziti/cmd/ziti/util"
|
||||
"github.com/pkg/errors"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ZitiForEach struct{}
|
||||
|
||||
func (self *ZitiForEach) Execute(ctx *tutorial.ActionContext) error {
|
||||
ctx.Headers["templatize"] = "true"
|
||||
apiType, found := ctx.Headers["api"]
|
||||
if !found {
|
||||
apiType = string(util.EdgeAPI)
|
||||
}
|
||||
entityType := ctx.Headers["type"]
|
||||
|
||||
params := url.Values{}
|
||||
filter, found := ctx.Headers["filter"]
|
||||
if found {
|
||||
params.Add("filter", filter)
|
||||
}
|
||||
|
||||
minCount := 1
|
||||
if count, found := ctx.Headers["minCount"]; found {
|
||||
val, err := strconv.Atoi(count)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "couldn't parse minCount, invalid value '%v'", count)
|
||||
}
|
||||
if val < 0 {
|
||||
return errors.Wrapf(err, "invalid minCount, invalid value '%v', must >= 0", count)
|
||||
}
|
||||
minCount = val
|
||||
}
|
||||
|
||||
maxCount := 1
|
||||
if count, found := ctx.Headers["maxCount"]; found {
|
||||
val, err := strconv.Atoi(count)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "couldn't parse maxCount, invalid value '%v'", count)
|
||||
}
|
||||
if val < minCount {
|
||||
return errors.Wrapf(err, "invalid maxCount '%v', must >= minCount of %v", count, minCount)
|
||||
}
|
||||
maxCount = val
|
||||
}
|
||||
|
||||
entities, _, err := api.ListEntitiesOfType(util.API(apiType), entityType, params, false, os.Stdout, 1, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(entities) < minCount {
|
||||
return errors.Errorf("expected at least %v %v, only found %v", minCount, entityType, len(entities))
|
||||
}
|
||||
|
||||
if len(entities) > maxCount {
|
||||
return errors.Errorf("expected at most %v %v, only found %v", maxCount, entityType, len(entities))
|
||||
}
|
||||
|
||||
runner := ZitiRunnerAction{}
|
||||
originalBody := ctx.Body
|
||||
for _, entity := range entities {
|
||||
wrapper := api.Wrap(entity)
|
||||
id := wrapper.String("id")
|
||||
name := wrapper.String("name")
|
||||
|
||||
ctx.Runner.AddVariable("entityId", id)
|
||||
ctx.Runner.AddVariable("entityName", name)
|
||||
if err := runner.Execute(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Runner.ClearVariable("entityId")
|
||||
ctx.Runner.ClearVariable("entityName")
|
||||
ctx.Body = originalBody
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/valyala/fasttemplate"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -22,6 +23,14 @@ const (
|
||||
actionMarker = "f572d396fae9206628714fb2ce00f72e94f2258f"
|
||||
)
|
||||
|
||||
var debug bool
|
||||
|
||||
func init() {
|
||||
if val, found := os.LookupEnv("DEBUG"); found && strings.EqualFold(val, "true") {
|
||||
debug = true
|
||||
}
|
||||
}
|
||||
|
||||
func NewRunner() *Runner {
|
||||
result := &Runner{
|
||||
actionHandlers: map[string]ActionHandler{},
|
||||
@@ -70,6 +79,10 @@ func (self *Runner) AddVariable(name string, val interface{}) {
|
||||
self.variables[name] = val
|
||||
}
|
||||
|
||||
func (self *Runner) ClearVariable(name string) {
|
||||
delete(self.variables, name)
|
||||
}
|
||||
|
||||
func (self *Runner) Run(source []byte) error {
|
||||
markdownSource, err := self.transformSource(source)
|
||||
if err != nil {
|
||||
@@ -147,7 +160,10 @@ func (self *Runner) transformSource(source []byte) ([]byte, error) {
|
||||
body = strings.TrimSpace(string(actionSource[nlIndex+1 : endIndex]))
|
||||
}
|
||||
actionDef := strings.TrimSpace(actionHeader)
|
||||
action, headers := self.parseAction(actionDef)
|
||||
action, headers, err := self.parseAction(actionDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handler, ok := self.actionHandlers[action]
|
||||
if !ok {
|
||||
@@ -171,26 +187,74 @@ func (self *Runner) transformSource(source []byte) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Runner) parseAction(s string) (string, map[string]string) {
|
||||
func (self *Runner) parseAction(s string) (string, map[string]string, error) {
|
||||
var action string
|
||||
headers := map[string]string{}
|
||||
|
||||
idx := strings.IndexByte(s, ' ')
|
||||
if idx < 1 {
|
||||
return s, headers
|
||||
return s, headers, nil
|
||||
}
|
||||
|
||||
action = s[:idx]
|
||||
rest := s[idx+1:]
|
||||
for _, kv := range strings.Split(rest, " ") {
|
||||
vals := strings.Split(kv, "=")
|
||||
if len(vals) < 2 {
|
||||
headers[vals[0]] = ""
|
||||
for len(rest) > 0 {
|
||||
idx = strings.IndexByte(rest, '=')
|
||||
if idx < 0 {
|
||||
headers[rest] = ""
|
||||
if debug {
|
||||
fmt.Printf("%v: added %v=%v\n", action, rest, headers[rest])
|
||||
}
|
||||
rest = ""
|
||||
} else if idx == 0 {
|
||||
return "", nil, errors.Errorf("unable to parse action %v, unexpected =", action)
|
||||
} else {
|
||||
headers[vals[0]] = vals[1]
|
||||
key := rest[:idx]
|
||||
rest = rest[idx+1:]
|
||||
if rest[0] == '\'' {
|
||||
rest = rest[1:]
|
||||
closingQuoteIdx := strings.IndexByte(rest, '\'')
|
||||
if closingQuoteIdx < 0 {
|
||||
return "", nil, errors.Errorf("unable to parse action %v, unclosed single-quote", action)
|
||||
}
|
||||
headers[key] = rest[:closingQuoteIdx]
|
||||
if debug {
|
||||
fmt.Printf("%v: added %v=%v\n", action, key, headers[key])
|
||||
}
|
||||
rest = rest[closingQuoteIdx+1:]
|
||||
if len(rest) > 0 {
|
||||
if rest[0] != ' ' {
|
||||
return "", nil, errors.Errorf("unable to parse action %v, expected space after closing singe-quote", action)
|
||||
} else {
|
||||
rest = rest[1:]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
idx = strings.IndexByte(rest, ' ')
|
||||
if idx < 0 {
|
||||
headers[key] = rest
|
||||
if debug {
|
||||
fmt.Printf("%v: added %v=%v\n", action, key, headers[key])
|
||||
}
|
||||
rest = ""
|
||||
} else if idx == 0 {
|
||||
headers[key] = ""
|
||||
if debug {
|
||||
fmt.Printf("%v: added %v=%v\n", action, key, headers[key])
|
||||
}
|
||||
rest = rest[1:]
|
||||
} else {
|
||||
headers[key] = rest[:idx]
|
||||
if debug {
|
||||
fmt.Printf("%v: added %v=%v\n", action, key, headers[key])
|
||||
}
|
||||
rest = rest[idx+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return action, headers
|
||||
|
||||
return action, headers, nil
|
||||
}
|
||||
|
||||
func (self *Runner) actionIds() []string {
|
||||
|
||||
Reference in New Issue
Block a user