Merge pull request #1906 from openziti/ctrl-load-test

Load test for controller with links, terminators, clients and traffic
This commit is contained in:
Paul Lorenz
2024-04-09 17:24:18 -04:00
committed by GitHub
32 changed files with 1479 additions and 84 deletions
-26
View File
@@ -383,29 +383,3 @@ func (self *testConfig) GetVersionProvider() versions.VersionProvider {
func (self *testConfig) GetCloseNotify() <-chan struct{} {
return self.closeNotify
}
//
//type testDbProvider struct {
// ctx *TestContext
//}
//
//func (p *testDbProvider) GetDb() boltz.Db {
// return p.ctx.GetDb()
//}
//
//func (p *testDbProvider) GetStores() *Stores {
// return p.ctx.n.GetStores()
//}
//
//func (p *testDbProvider) GetServiceCache() network.Cache {
// return p
//}
//
//func (p *testDbProvider) NotifyRouterRenamed(_, _ string) {}
//
//func (p *testDbProvider) RemoveFromCache(_ string) {
//}
//
//func (p *testDbProvider) GetManagers() *network.Managers {
// return p.ctx.n.Managers
//}
+2 -2
View File
@@ -15,13 +15,14 @@ require (
github.com/openziti/agent v1.0.16
github.com/openziti/channel/v2 v2.0.125
github.com/openziti/edge-api v0.26.14
github.com/openziti/fablab v0.5.42
github.com/openziti/fablab v0.5.49
github.com/openziti/foundation/v2 v2.0.42
github.com/openziti/identity v1.0.74
github.com/openziti/sdk-golang v0.23.15
github.com/openziti/storage v0.2.34
github.com/openziti/transport/v2 v2.0.128
github.com/openziti/ziti v0.28.3
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/pkg/errors v0.9.1
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/sirupsen/logrus v1.9.3
@@ -143,7 +144,6 @@ require (
github.com/openziti/x509-claims v1.0.3 // indirect
github.com/openziti/xweb/v2 v2.1.0 // indirect
github.com/openziti/ziti-db-explorer v1.1.3 // indirect
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/parallaxsecond/parsec-client-go v0.0.0-20221025095442-f0a77d263cf9 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/sftp v1.13.6 // indirect
+2 -2
View File
@@ -596,8 +596,8 @@ github.com/openziti/dilithium v0.3.3 h1:PLgQ6PMNLSTzCFbX/h98cmudgz/cU6TmjdSv5NAP
github.com/openziti/dilithium v0.3.3/go.mod h1:vsCjI2AU/hon9e+dLhUFbCNGesJDj2ASgkySOcpmvjo=
github.com/openziti/edge-api v0.26.14 h1:BpxKavDVdykOY7GSdKXi7klv9Im4vw7bTMujz3wFE1Q=
github.com/openziti/edge-api v0.26.14/go.mod h1:aTRqB9J/6ISbw9+YWjzD0tvWAXY98m8yWcbiGNqAQTU=
github.com/openziti/fablab v0.5.42 h1:vENJKfEba2T4sSLwlKDL/IzBYfY8iHnhc4umf6IESiY=
github.com/openziti/fablab v0.5.42/go.mod h1:HDT06y1QX8kO8ZQrgHvZmJsvc8iRybESGtlDLDII4ks=
github.com/openziti/fablab v0.5.49 h1:uYmBQBS7auPeUlQBpnrU+ktDFhUTOklwwftBBqXcaDk=
github.com/openziti/fablab v0.5.49/go.mod h1:TAXN6lhTnmjL4nwkio3Ql96Lf5gnyeSbm7uV+CilsPo=
github.com/openziti/foundation/v2 v2.0.42 h1:cXZFql3xY92AHPCk/wqIoSUX9EHgegwPrs5bhFE2jNg=
github.com/openziti/foundation/v2 v2.0.42/go.mod h1:a+REYnK9bZ2cvmOiuBLp57MjQqn3U5cHhJcDMLMO8rE=
github.com/openziti/identity v1.0.74 h1:BUCKjGXIdoEHLL1+kO/smitZvGwP9dOUpjVEJFtSSBc=
@@ -0,0 +1,194 @@
v: 3
db: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/ctrl.db
identity:
cert: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}-server.chain.pem
key: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/keys/{{ .Component.Id }}-server.key
ca: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}.chain.pem
network:
enableLegacyLinkMgmt: true
tls:
handshakeTimeout: 30s
# the endpoint that routers will connect to the controller over.
ctrl:
listener: tls:0.0.0.0:6262
options:
advertiseAddress: tls:{{ .Host.PublicIp }}:6262
# (optional) settings
# set the maximum number of connect requests that are buffered and waiting to be acknowledged (1 to 5000, default 1000)
#maxQueuedConnects: 50
# the maximum number of connects that have begun hello synchronization (1 to 1000, default 16)
#maxOutstandingConnects: 100
# the number of milliseconds to wait before a hello synchronization fails and closes the connection (30ms to 60000ms, default: 1000ms)
#connectTimeoutMs: 3000
# Sets the control channel write timeout. A write timeout will close the control channel, so the router will reconnect
#writeTimeout: 15s
# A listener address which will be sent to connecting routers in order to change their configured controller
# address. If defined, routers will update address configuration to immediately use the new address for future
# connections. The value of newListener must be resolvable both via DNS and validate via certificates
#newListener: tls:localhost:6262
events:
jsonLogger:
subscriptions:
# - type: fabric.routers
# - type: fabric.terminators
# - type: metrics
# sourceFilter: .*
# metricFilter: .*egress.*m1_rate*
# - type: fabric.circuits
# include:
# - created
# - type: edge.sessions
# include:
# - created
# - type: edge.apiSessions
# - type: fabric.usage
# - type: services
# - type: fabric.usage
- type: edge.entityCounts
interval: 1m
handler:
type: file
format: json
path: /home/ubuntu/logs/events.json
healthChecks:
boltCheck:
# How often to try entering a bolt read tx. Defaults to 30 seconds
interval: 30s
# When to timeout the check. Defaults to 15 seconds
timeout: 15s
# How long to wait before starting the check. Defaults to 15 seconds
initialDelay: 15s
# By having an 'edge' section defined, the ziti-controller will attempt to parse the edge configuration. Removing this
# section, commenting out, or altering the name of the section will cause the edge to not run.
edge:
# This section represents the configuration of the Edge API that is served over HTTPS
api:
#(optional, default 90s) Alters how frequently heartbeat and last activity values are persisted
# activityUpdateInterval: 90s
#(optional, default 250) The number of API Sessions updated for last activity per transaction
# activityUpdateBatchSize: 250
# sessionTimeout - optional, default 10m
# The number of minutes before an Edge API session will timeout. Timeouts are reset by
# API requests and connections that are maintained to Edge Routers
sessionTimeout: 30m
# address - required
# The default address (host:port) to use for enrollment for the Client API. This value must match one of the addresses
# defined in a bind point's address field for the `edge-client` API in the web section.
address: {{ .Host.PublicIp }}:1280
# enrollment - required
# A section containing settings pertaining to enrollment.
enrollment:
# signingCert - required
# A Ziti Identity configuration section that specifically makes use of the cert and key fields to define
# a signing certificate from the PKI that the Ziti environment is using to sign certificates. The signingCert.cert
# will be added to the /.well-known CA store that is used to bootstrap trust with the Ziti Controller.
signingCert:
cert: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}.cert
key: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/keys/{{ .Component.Id }}.key
# edgeIdentity - optional
# A section for identity enrollment specific settings
edgeIdentity:
# duration - optional, default 5m
# The length of time that a Ziti Edge Identity enrollment should remain valid. After
# this duration, the enrollment will expire and not longer be usable.
duration: 1h
# edgeRouter - Optional
# A section for edge router enrollment specific settings.
edgeRouter:
# duration - optional, default 5m
# The length of time that a Ziti Edge Router enrollment should remain valid. After
# this duration, the enrollment will expire and not longer be usable.
duration: 1h
# web - optional
# Defines webListeners that will be hosted by the controller. Each webListener can host many APIs and be bound to many
# bind points.
web:
# name - required
# Provides a name for this listener, used for logging output. Not required to be unique, but is highly suggested.
- name: all-apis-localhost
# bindPoints - required
# One or more bind points are required. A bind point specifies an interface (interface:port string) that defines
# where on the host machine the webListener will listen and the address (host:port) that should be used to
# publicly address the webListener(i.e. mydomain.com, localhost, 127.0.0.1). This public address may be used for
# incoming address resolution as well as used in responses in the API.
bindPoints:
#interface - required
# A host:port string on which network interface to listen on. 0.0.0.0 will listen on all interfaces
- interface: 0.0.0.0:1280
# address - required
# The public address that external incoming requests will be able to resolve. Used in request processing and
# response content that requires full host:port/path addresses.
address: {{ .Host.PublicIp }}:1280
# newAddress - optional
# A host:port string which will be sent out as an HTTP header "ziti-new-address" if specified. If the header
# is present, clients should update location configuration to immediately use the new address for future
# connections. The value of newAddress must be resolvable both via DNS and validate via certificates
#newAddress: localhost:1280
# identity - optional
# Allows the webListener to have a specific identity instead of defaulting to the root `identity` section.
# identity:
# cert: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ctrl-client.cert.pem
# server_cert: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ctrl-server.cert.pem
# key: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/private/ctrl.key.pem
# ca: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ca-chain.cert.pem
# options - optional
# Allows the specification of webListener level options - mainly dealing with HTTP/TLS settings. These options are
# used for all http servers started by the current webListener.
options:
# idleTimeout - optional, default 5000ms
# The maximum amount of idle time in milliseconds allowed for pipelined HTTP requests. Setting this too high
# can cause resources on the host to be consumed as clients remain connected and idle. Lowering this value
# will cause clients to reconnect on subsequent HTTPs requests.
idleTimeout: 5000ms #http timeouts, new
# readTimeout - optional, default 5000ms
# The maximum amount of time in milliseconds http servers will wait to read the first incoming requests. A higher
# value risks consuming resources on the host with clients that are acting bad faith or suffering from high latency
# or packet loss. A lower value can risk losing connections to high latency/packet loss clients.
readTimeout: 5000ms
# writeTimeout - optional, default 10000ms
# The total maximum time in milliseconds that the http server will wait for a single requests to be received and
# responded too. A higher value can allow long running requests to consume resources on the host. A lower value
# can risk ending requests before the server has a chance to respond.
writeTimeout: 100000ms
# minTLSVersion - optional, default TSL1.2
# The minimum version of TSL to support
minTLSVersion: TLS1.2
# maxTLSVersion - optional, default TSL1.3
# The maximum version of TSL to support
maxTLSVersion: TLS1.3
# apis - required
# Allows one or more APIs to be bound to this webListener
apis:
# binding - required
# Specifies an API to bind to this webListener. Built-in APIs are
# - health-checks
# - edge-management
# - edge-client
# - fabric-management
- binding: health-checks
- binding: fabric
- binding: edge-management
- binding: edge-client
- binding: edge-oidc
@@ -0,0 +1,78 @@
{{$ssh_username := .Model.MustVariable "credentials.ssh.username"}}
{{$identity := .Component.Id}}
{{$router_ip := .Host.PublicIp}}
v: 3
enableDebugOps: true
identity:
cert: /home/{{$ssh_username}}/router-pki/{{$identity}}-client.cert
server_cert: /home/{{$ssh_username}}/router-pki/{{$identity}}-server.cert
key: /home/{{$ssh_username}}/router-pki/{{$identity}}.key
ca: /home/{{$ssh_username}}/router-pki/{{$identity}}-server.chain.pem
tls:
handshakeTimeout: 30s
ctrl:
endpoints: {{ range $host := .Model.MustSelectHosts "component.ctrl" 1 }}
- tls:{{ $host.PublicIp }}:6262{{end}}
startupTimeout: 5m
healthChecks:
ctrlPingCheck:
# How often to ping the controller over the control channel. Defaults to 30 seconds
interval: 30s
# When to timeout the ping. Defaults to 15 seconds
timeout: 15s
# How long to wait before pinging the controller. Defaults to 15 seconds
initialDelay: 15s
metrics:
reportInterval: 15s
messageQueueSize: 10
link:
{{if .Component.HasTag "public"}}
listeners:
- binding: transport
bind: tls:0.0.0.0:60{{printf "%02d" .Component.ScaleIndex }}
advertise: tls:{{$router_ip}}:60{{printf "%02d" .Component.ScaleIndex }}
{{end}}
dialers:
- binding: transport
options:
connectTimeout: 30s
listeners:
{{if .Component.HasTag "tunneler"}}
- binding: tunnel
options:
mode: host
{{end}}
- binding: edge
address: tls:0.0.0.0:62{{printf "%02d" .Component.ScaleIndex }}
options:
# (required) The public hostname and port combination that Ziti SDKs should connect on. Previously this was in the chanIngress section.
advertise: {{ .Host.PublicIp }}:62{{printf "%02d" .Component.ScaleIndex }}
# By having an 'edge' section defined, the ziti-router will attempt to parse the edge configuration. Removing this
# section, commenting out, or altering the name of the section will cause the router to no longer operate as an Edge
# Router.
edge:
# (required) Information used to generate the initial registration CSR. For documentation on these fields please
# refer to the openssl documentation. These values MUST be supplied and have no defaults.
csr:
country: US
province: NC
locality: Charlotte
organization: NetFoundry
organizationalUnit: Ziti
# (required) SANs that this Gateways certs should contain. At least one IP or DNS SAN should be defined that matches
# the edge listeners "advertise" value from the "listeners" section.
sans:
ip:
- {{ .Host.PublicIp }}
+382
View File
@@ -0,0 +1,382 @@
/*
Copyright 2019 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 main
import (
"bufio"
"embed"
_ "embed"
"fmt"
"github.com/openziti/fablab"
"github.com/openziti/fablab/kernel/lib/actions"
"github.com/openziti/fablab/kernel/lib/actions/component"
"github.com/openziti/fablab/kernel/lib/actions/host"
"github.com/openziti/fablab/kernel/lib/actions/semaphore"
"github.com/openziti/fablab/kernel/lib/binding"
"github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/aws_ssh_key"
"github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/semaphore"
"github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/terraform"
distribution "github.com/openziti/fablab/kernel/lib/runlevel/3_distribution"
"github.com/openziti/fablab/kernel/lib/runlevel/3_distribution/rsync"
aws_ssh_key2 "github.com/openziti/fablab/kernel/lib/runlevel/6_disposal/aws_ssh_key"
"github.com/openziti/fablab/kernel/lib/runlevel/6_disposal/terraform"
"github.com/openziti/fablab/kernel/model"
"github.com/openziti/fablab/resources"
"github.com/openziti/foundation/v2/stringz"
"github.com/openziti/ziti/zititest/models/test_resources"
"github.com/openziti/ziti/zititest/zitilab"
"github.com/openziti/ziti/zititest/zitilab/actions/edge"
"github.com/openziti/ziti/zititest/zitilab/models"
"go.etcd.io/bbolt"
"os"
"path"
"strings"
"time"
)
const (
EnrollIdentitiesAction = "enrollIdentities"
)
const (
useLatestZitiOnRouters = true
)
//go:embed configs
var configResource embed.FS
var dbStrategyInstance = &dbStrategy{
routerMappings: map[string]string{},
}
type dbStrategy struct {
routerMappings map[string]string
}
func (self *dbStrategy) ProcessDbModel(tx *bbolt.Tx, m *model.Model, builder *models.ZitiDbBuilder) error {
dbFile := self.GetDbFile(m)
dbDir := path.Dir(dbFile)
mapFile := path.Join(dbDir, "edge-router-mapping.csv")
mapContents, err := os.ReadFile(mapFile)
if err != nil {
return fmt.Errorf("failed to read edge router mapping file (%w)", err)
}
if err = self.loadErMap(string(mapContents)); err != nil {
return fmt.Errorf("unable to parse router mapping file (%w)", err)
}
if err = self.ProcessEdgeRouters(tx, m, builder); err != nil {
return err
}
if err = self.CreateEnrollIdentitiesAction(tx, m, builder); err != nil {
return err
}
return nil
}
func (self *dbStrategy) loadErMap(csv string) error {
sc := bufio.NewScanner(strings.NewReader(csv))
for sc.Scan() {
parts := strings.Split(sc.Text(), ",")
if len(parts) >= 2 {
routerId := parts[0]
nfVersion := parts[1]
zitiVersion := nfZitiVersionMap[nfVersion]
self.routerMappings[routerId] = zitiVersion
}
}
return nil
}
func (self *dbStrategy) GetDbFile(m *model.Model) string {
return m.MustStringVariable("db_file")
}
func (self *dbStrategy) ProcessEdgeRouters(tx *bbolt.Tx, m *model.Model, builder *models.ZitiDbBuilder) error {
ids, _, err := builder.GetStores().EdgeRouter.QueryIds(tx, "true limit none")
if err != nil {
return err
}
versions := map[string]int{}
hostsPerRegion := ((len(ids) / len(sites)) / 20) + 1
hostIdF := func(regionId string, idx int) string {
return fmt.Sprintf("%s-r%d", regionId[:len(regionId)-2], idx)
}
echoServerIdF := func(regionId string, idx int) string {
return fmt.Sprintf("%s-echo%d", regionId[:len(regionId)-2], idx)
}
simCount := 0
for _, site := range sites {
regionId := site[:len(site)-1]
region := m.Regions[regionId]
if region != nil && region.Site != site {
return fmt.Errorf("trying to add region for site %v, but one exists, with different site", site)
}
if region == nil {
region = &model.Region{
Id: regionId,
Scope: model.Scope{Tags: model.Tags{}},
Region: regionId,
Site: site,
Hosts: model.Hosts{},
}
m.Regions[regionId] = region
}
for i := 0; i < 10; i++ {
simCount++
simId := fmt.Sprintf("sim%02d", simCount)
region.Hosts[simId] = &model.Host{
InstanceType: "t3.small",
Components: model.Components{
simId: {
Scope: model.Scope{Tags: model.Tags{"sim"}},
Type: &zitilab.SimpleSimType{
ConfigPath: "identities",
},
},
},
}
}
for i := 0; i < hostsPerRegion; i++ {
hostId := hostIdF(regionId, i)
routerHost := &model.Host{
Id: hostId,
Scope: model.Scope{Tags: model.Tags{}},
Region: region,
Components: model.Components{
echoServerIdF(regionId, i): {
Scope: model.Scope{Tags: model.Tags{"server"}},
Type: &zitilab.EchoServerType{
Port: 8888,
},
},
},
InstanceType: "t3.large",
ScaleIndex: uint32(i),
}
region.Hosts[hostId] = routerHost
}
}
siteIdx := 0
hostIdx := 0
for _, id := range ids {
er, err := builder.GetStores().EdgeRouter.LoadById(tx, id)
if err != nil {
return err
}
if siteIdx >= len(sites) {
siteIdx = 0
hostIdx++
if hostIdx >= hostsPerRegion {
hostIdx = 0
}
}
site := sites[siteIdx]
regionId := site[:len(site)-1]
region := m.Regions[regionId]
hostId := hostIdF(regionId, hostIdx)
routerHost := region.Hosts[hostId]
siteIdx++
version := ""
if !useLatestZitiOnRouters {
version = self.routerMappings[id]
if replacement, found := versionMapping[version]; found {
version = replacement
}
versions[version] = versions[version] + 1
}
routerComponent := &model.Component{
Scope: model.Scope{Tags: model.Tags{"router", "pre-created"}},
Type: &zitilab.RouterType{
Version: version,
},
Host: routerHost,
ScaleIndex: uint32(len(routerHost.Components)),
}
routerHost.Components[er.Id] = routerComponent
if er.IsTunnelerEnabled {
routerComponent.Scope.Tags = append(routerComponent.Scope.Tags, "tunneler")
}
if stringz.Contains(er.RoleAttributes, "public") {
routerComponent.Scope.Tags = append(routerComponent.Scope.Tags, "public")
}
}
return nil
}
func (self *dbStrategy) CreateEnrollIdentitiesAction(tx *bbolt.Tx, m *model.Model, builder *models.ZitiDbBuilder) error {
ids, _, err := builder.GetStores().Identity.QueryIds(tx, `type != "Router" limit none`)
if err != nil {
return err
}
m.Actions[EnrollIdentitiesAction] = func(m *model.Model) model.Action {
return model.ActionFunc(func(run model.Run) error {
return provisionIdentities(ids, run)
})
}
return nil
}
var m = &model.Model{
Id: "db-ctrl-test",
Scope: model.Scope{
Defaults: model.Variables{
"environment": "ctrl-test",
"credentials": model.Variables{
"aws": model.Variables{
"managed_key": true,
},
"ssh": model.Variables{
"username": "ubuntu",
},
"edge": model.Variables{
"username": "admin",
},
},
},
},
StructureFactories: []model.Factory{
&models.ZitiDbBuilder{Strategy: dbStrategyInstance},
},
Resources: model.Resources{
resources.Configs: resources.SubFolder(configResource, "configs"),
resources.Binaries: os.DirFS(path.Join(os.Getenv("GOPATH"), "bin")),
resources.Terraform: test_resources.TerraformResources(),
},
Regions: model.Regions{
"us-east-1": {
Region: "us-east-1",
Site: "us-east-1c",
Hosts: model.Hosts{
"ctrl": {
InstanceType: "c5.9xlarge",
InstanceResourceType: "ondemand_iops",
EC2: model.EC2Host{
Volume: model.EC2Volume{
Type: "gp3",
SizeGB: 16,
IOPS: 3000,
},
},
Components: model.Components{
"ctrl": {
Scope: model.Scope{Tags: model.Tags{"ctrl"}},
Type: &zitilab.ControllerType{},
},
},
},
},
},
},
Actions: model.ActionBinders{
"bootstrap": model.ActionBinder(func(m *model.Model) model.Action {
workflow := actions.Workflow()
workflow.AddAction(component.StopInParallel("*", 200))
workflow.AddAction(host.GroupExec("*", 25, "rm -f logs/*"))
workflow.AddAction(component.Start("#ctrl"))
workflow.AddAction(semaphore.Sleep(2 * time.Second))
workflow.AddAction(edge.Login("#ctrl"))
workflow.AddAction(model.ActionFunc(provisionRouters))
workflow.AddAction(m.ExecuteAction("syncRouterJwts"))
workflow.AddAction(model.ActionFunc(enrollRouters))
workflow.AddAction(m.ExecuteAction(EnrollIdentitiesAction))
workflow.AddAction(model.ActionFunc(func(run model.Run) error {
src := model.MakeBuildPath("identities")
return rsync.RsyncSelected("component.sim", src, "identities").Execute(run)
}))
return workflow
}),
"syncRouterJwts": model.Bind(model.ActionFunc(func(run model.Run) error {
src := model.MakeBuildPath("router-jwts")
return rsync.RsyncSelected("component.router", src, "router-jwts").Execute(run)
})),
"stop": model.Bind(component.StopInParallel("*", 200)),
"clean": model.Bind(actions.Workflow(
component.StopInParallel("*", 200),
host.GroupExec("*", 25, "rm -f logs/*"),
)),
"login": model.Bind(edge.Login("#ctrl")),
},
Infrastructure: model.Stages{
aws_ssh_key.Express(),
terraform_0.Express(),
semaphore_0.Ready(90 * time.Second),
},
Distribution: model.Stages{
distribution.DistributeSshKey("*"),
rsync.RsyncStaged(),
model.StageActionF(func(run model.Run) error {
quickRun, _ := run.GetModel().GetBoolVariable("quick_run")
_, targetedSync := run.GetModel().Scope.GetVariable("sync.target")
if !quickRun && !targetedSync {
dbFile := dbStrategyInstance.GetDbFile(run.GetModel())
deferred := rsync.NewRsyncHost("#ctrl", dbFile, "/home/ubuntu/ctrl.db")
return deferred.Execute(run)
}
return nil
}),
},
Disposal: model.Stages{
terraform.Dispose(),
aws_ssh_key2.Dispose(),
},
}
func main() {
m.AddActivationActions("bootstrap")
model.AddBootstrapExtension(binding.AwsCredentialsLoader)
model.AddBootstrapExtension(aws_ssh_key.KeyManager)
fablab.InitModel(m)
fablab.Run()
}
@@ -0,0 +1,145 @@
/*
Copyright 2019 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 main
var versionMapping = map[string]string{
"": "0.31.4",
"0.30.4": "0.30.5",
"0.31.0": "0.31.4",
"0.29.0": "0.28.1",
"0.25.2": "0.28.1",
"0.26.10": "0.28.1",
}
var sites = []string{
"us-east-1c",
"us-west-2b",
"eu-west-2a",
"eu-central-1a",
"ap-southeast-2a",
}
var nfZitiVersionMap = map[string]string{
"7.3.54": "0.24.3",
"7.3.77": "0.26.7",
"7.3.36": "0.20.13",
"7.3.45": "0.22.7",
"7.3.91": "0.28.1",
"7.3.58": "0.24.8",
"8.0.96": "0.31.0",
"7.3.32": "0.20.9",
"8.0.95": "0.30.5",
"7.3.92": "0.28.4",
"7.3.69": "0.25.13",
"7.3.57": "0.24.7",
"7.3.98": "0.31.4",
"7.3.71": "0.26.1",
"7.3.93": "0.29.0",
"7.3.22": "0.19.8",
"7.3.43": "0.22.4",
"7.3.31": "0.20.8",
"7.3.24": "0.19.10",
"7.3.87": "0.27.7",
"7.3.55": "0.24.4",
"7.3.86": "0.27.6",
"7.3.4": "0.17.5",
"7.3.25": "0.19.11",
"7.3.74": "0.26.4",
"7.3.81": "0.27.0",
"7.3.12": "0.18.9",
"8.0.98": "0.31.4",
"7.3.7": "0.18.3",
"7.3.51": "0.23.1",
"7.3.13": "0.18.10",
"7.3.5": "0.18.0",
"7.3.48": "0.22.10",
"7.3.30": "0.20.3",
"7.3.9": "0.18.5",
"7.3.94": "0.30.4",
"7.3.23": "0.19.9",
"7.3.89": "0.27.9",
"7.3.41": "0.22.2",
"8.0.94": "0.30.4",
"7.3.62": "0.25.2",
"7.3.19": "0.19.5",
"7.3.65": "0.25.5",
"7.3.67": "0.25.8",
"7.3.11": "0.18.8",
"7.3.73": "0.26.3",
"7.3.61": "0.24.13",
"7.3.14": "0.19.0",
"7.3.96": "0.31.0",
"7.3.82": "0.27.2",
"7.3.34": "0.20.11",
"7.3.78": "0.26.8",
"7.3.50": "0.23.0",
"7.3.90": "0.28.0",
"7.3.44": "0.22.6",
"7.3.56": "0.24.6",
"7.3.35": "0.20.12",
"7.3.64": "0.25.4",
"8.0.99": "0.32.2",
"7.3.49": "0.22.11",
"7.3.59": "0.24.9",
"7.3.88": "0.27.8",
"7.3.97": "0.31.2",
"7.3.52": "0.24.1",
"7.3.99": "0.32.2",
"7.3.29": "0.20.2",
"7.3.72": "0.26.2",
"7.3.37": "0.20.14",
"7.3.53": "0.24.2",
"7.3.10": "0.18.7",
"7.3.33": "0.20.10",
"7.3.26": "0.19.12",
"7.3.15": "0.19.1",
"7.3.38": "0.21.0",
"7.3.63": "0.25.3",
"7.3.46": "0.22.8",
"7.3.28": "0.20.1",
"7.3.75": "0.26.5",
"7.3.39": "0.22.0",
"8.0.100": "0.33.1",
"7.3.60": "0.24.12",
"7.3.95": "0.30.5",
"7.3.21": "0.19.7",
"8.0.85": "0.27.5",
"7.3.18": "0.19.4",
"7.3.6": "0.18.2",
"7.3.76": "0.26.6",
"7.3.42": "0.22.3",
"7.3.16": "0.19.2",
"7.3.20": "0.19.6",
"8.0.97": "0.31.2",
"7.3.70": "0.26.0",
"7.3.68": "0.25.10",
"7.3.66": "0.25.6",
"7.3.84": "0.27.4",
"7.3.8": "0.18.4",
"7.3.27": "0.19.13",
"8.0.93": "0.29.0",
"7.3.85": "0.27.5",
"7.3.17": "0.19.3",
"7.3.80": "0.26.11",
"7.3.100": "0.33.1",
"7.3.79": "0.26.10",
"7.3.40": "0.22.1",
"8.0.92": "0.28.4",
"7.3.83": "0.27.3",
"8.0.91": "0.28.1",
"7.3.47": "0.22.9",
}
+99
View File
@@ -0,0 +1,99 @@
/*
Copyright 2019 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 main
import (
"fmt"
"github.com/openziti/fablab/kernel/lib/parallel"
"github.com/openziti/fablab/kernel/model"
"github.com/openziti/ziti/zititest/zitilab"
zitilib_actions "github.com/openziti/ziti/zititest/zitilab/actions"
"github.com/openziti/ziti/zititest/zitilab/cli"
"os"
"path/filepath"
)
func provisionIdentities(identities []string, run model.Run) error {
var tasks []parallel.Task
identitiesDir := model.MakeBuildPath("identities")
_ = os.MkdirAll(identitiesDir, 0770)
for _, v := range identities {
id := v
task := func() error {
jwtFileName := filepath.Join(identitiesDir, id+".jwt")
args := []string{"create", "enrollment", "ott", "--jwt-output-file", jwtFileName, "--", id}
if err := zitilib_actions.EdgeExec(run.GetModel(), args...); err != nil {
return err
}
if _, err := cli.Exec(m, "edge", "enroll", "--", jwtFileName); err != nil {
return fmt.Errorf("failed to enroll %s (%w)", id, err)
}
return nil
}
tasks = append(tasks, task)
}
if err := parallel.Execute(tasks, 10); err != nil {
return err
}
return nil
}
func provisionRouters(run model.Run) error {
routerPkiDir := model.MakeBuildPath("router-jwts")
_ = os.MkdirAll(routerPkiDir, 0770)
return run.GetModel().ForEachComponent(".router.pre-created", 15, func(c *model.Component) error {
jwtFileName := filepath.Join(routerPkiDir, c.Id+".jwt")
args := []string{"re-enroll", "edge-router", "-j", "--jwt-output-file", jwtFileName, "--", c.Id}
return zitilib_actions.EdgeExec(c.GetModel(), args...)
})
}
func enrollRouters(run model.Run) error {
zitiVersion := ""
ctrls := run.GetModel().SelectComponents(".ctrl")
for _, ctrl := range ctrls {
if ctrl.Type != nil {
zitiVersion = ctrl.Type.GetVersion()
break
}
}
return run.GetModel().ForEachHost("component.router", 15, func(h *model.Host) error {
var cmds []string
cmds = append(cmds, "mkdir -p /home/ubuntu/router-pki/")
hostUser := h.GetSshUser()
for _, c := range h.Components {
var routerType *zitilab.RouterType
var ok bool
if routerType, ok = c.Type.(*zitilab.RouterType); !ok || !c.HasTag("pre-created") {
continue
}
remoteJwt := fmt.Sprintf("/home/%s/router-jwts/%s.jwt", hostUser, c.Id)
tmpl := "set -o pipefail; %s router enroll /home/ubuntu/fablab/cfg/%s -j %s 2>&1 | tee /home/ubuntu/logs/%s.router.enroll.log "
cmd := fmt.Sprintf(tmpl, zitilab.GetZitiBinaryPath(c, zitiVersion), routerType.GetConfigName(c), remoteJwt, c.Id)
cmds = append(cmds, cmd)
}
return h.ExecLogOnlyOnError(cmds...)
})
}
+1 -1
View File
@@ -74,7 +74,7 @@ func (d dbStrategy) PostProcess(router *db.EdgeRouter, c *model.Component) {
}
func (d dbStrategy) ProcessDbModel(tx *bbolt.Tx, m *model.Model, builder *models.ZitiDbBuilder) error {
if err := builder.CreateEdgeRouterHosts(tx, m); err != nil {
if err := builder.CreateEdgeRouterHosts(tx, m, d); err != nil {
return err
}
return d.CreateIdentityHosts(tx, m, builder)
+1 -1
View File
@@ -100,7 +100,7 @@ var m = &model.Model{
}),
model.FactoryFunc(func(m *model.Model) error {
return m.ForEachHost("component.router", 1, func(host *model.Host) error {
host.InstanceType = "c5.xlarge"
host.InstanceType = "t3.large"
return nil
})
}),
+1 -1
View File
@@ -58,7 +58,7 @@ func (self scaleStrategy) GetEntityCount(entity model.Entity) uint32 {
type dbStrategy struct{}
func (d dbStrategy) ProcessDbModel(tx *bbolt.Tx, m *model.Model, builder *models.ZitiDbBuilder) error {
return builder.CreateEdgeRouterHosts(tx, m)
return builder.CreateEdgeRouterHosts(tx, m, d)
}
func (d dbStrategy) GetDbFile(*model.Model) string {
+1 -1
View File
@@ -68,7 +68,7 @@ func (self *stageFactory) Build(m *model.Model) error {
distribution.Locations(models.HasRouterComponent, "logs"),
distribution.Locations(models.LoopListenerTag, "logs"),
distribution.Locations(models.LoopDialerTag, "logs"),
rsync.Rsync(1),
rsync.RsyncStaged(),
}
// Run the bootstrap and start actions
+3 -1
View File
@@ -208,7 +208,9 @@ var Model = &model.Model{
},
"echo-server": {
Scope: model.Scope{Tags: model.Tags{"sdk-app", "service"}},
Type: &zitilab.EchoServerType{},
Type: &zitilab.EchoServerType{
BindService: "echo",
},
},
"iperf-server-ert": {
Scope: model.Scope{Tags: model.Tags{"iperf", "service"}},
@@ -34,6 +34,11 @@ module "{{ $regionId }}_host_{{ $hostId }}" {
spot_price = "{{ $host.SpotPrice }}"
spot_type = "{{ $host.SpotType }}"
name = "{{ $region.Model.MustVariable "environment" }}.{{ $host.Id }}"
{{ if not (eq $host.EC2.Volume.Type "") }}
volume_type = "{{ $host.EC2.Volume.Type }}"
volume_size = "{{ $host.EC2.Volume.SizeGB }}"
volume_iops = "{{ $host.EC2.Volume.IOPS }}"
{{ end }}
}
output "{{ $regionId }}_host_{{ $hostId }}_public_ip" { value = module.{{ $regionId }}_host_{{ $hostId }}.public_ip }
@@ -0,0 +1,58 @@
variable "name" {}
variable "access_key" {}
variable "secret_key" {}
variable "environment_tag" { default = "" }
variable "instance_type" {}
variable "key_name" {}
variable "key_path" {}
variable "region" {}
variable "security_group_id" {}
variable "ssh_user" { default = "ubuntu" }
variable "subnet_id" {}
variable "spot_price" {}
variable "spot_type" {}
variable "volume_type" { default = "gp2" }
variable "volume_size" { default = 4 }
variable "volume_iops" {}
output "public_ip" { value = aws_instance.fablab.public_ip }
output "private_ip" { value = aws_instance.fablab.private_ip }
provider "aws" {
access_key = var.access_key
secret_key = var.secret_key
region = var.region
}
data "aws_ami" "ami" {
most_recent = true
owners = ["self"]
filter {
name = "name"
values = ["ziti-tests-*"]
}
}
resource "aws_instance" "fablab" {
ami = data.aws_ami.ami.id
instance_type = var.instance_type
key_name = var.key_name
vpc_security_group_ids = [var.security_group_id]
subnet_id = var.subnet_id
associate_public_ip_address = true
root_block_device {
volume_type = var.volume_type
volume_size = var.volume_size
iops = var.volume_iops
}
tags = {
Name = var.name
cost_environment = "non-production"
cost_category = "CICD"
cost_team_owner = "ADVDEV"
source = "fablab"
}
}
+215
View File
@@ -0,0 +1,215 @@
/*
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 main
import (
"fmt"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/edge-api/rest_model"
"github.com/openziti/sdk-golang/ziti"
"github.com/openziti/ziti/common/eid"
cmap "github.com/orcaman/concurrent-map/v2"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"math/rand"
"os"
"path/filepath"
"slices"
"time"
)
type simpleSimAction struct {
verbose bool
logFormatter string
configFile string
authenticated cmap.ConcurrentMap[string, time.Time]
}
func newSimpleSimCmd() *cobra.Command {
action := &simpleSimAction{
authenticated: cmap.New[time.Time](),
}
cmd := &cobra.Command{
Use: "simple-sim <config file or directory>",
Short: "Generates traffic to one more ziti identities",
Args: cobra.ExactArgs(1),
Run: action.run,
}
// allow interspersing positional args and flags
cmd.Flags().SetInterspersed(true)
cmd.Flags().BoolVarP(&action.verbose, "verbose", "v", false, "Enable verbose logging")
cmd.Flags().StringVar(&action.logFormatter, "log-formatter", "", "Specify log formatter [json|pfxlog|text]")
cmd.Flags().StringVarP(&action.configFile, "identity", "i", "", "Specify the Ziti identity to use. If not specified the Ziti listener won't be started")
cmd.Flags().SetInterspersed(true)
return cmd
}
func (self *simpleSimAction) initLogging() {
logLevel := logrus.InfoLevel
if self.verbose {
logLevel = logrus.DebugLevel
}
options := pfxlog.DefaultOptions().SetTrimPrefix("github.com/openziti/").NoColor()
pfxlog.GlobalInit(logLevel, options)
switch self.logFormatter {
case "pfxlog":
pfxlog.SetFormatter(pfxlog.NewFormatter(pfxlog.DefaultOptions().SetTrimPrefix("github.com/openziti/").StartingToday()))
case "json":
pfxlog.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02T15:04:05.000Z"})
case "text":
pfxlog.SetFormatter(&logrus.TextFormatter{})
default:
// let logrus do its own thing
}
}
func (self *simpleSimAction) run(_ *cobra.Command, args []string) {
self.initLogging()
log := pfxlog.Logger()
fsInfo, err := os.Stat(args[0])
if err != nil {
log.WithError(err).Fatalf("unable to stat config file/dir %s", args[0])
return
}
simCount := 0
if fsInfo.IsDir() {
files, err := os.ReadDir(args[0])
if err != nil {
log.WithError(err).Fatalf("failed to scan directory %s:", args[0])
return
}
for _, file := range files {
if filepath.Ext(file.Name()) == ".json" {
fn, err := filepath.Abs(filepath.Join(args[0], file.Name()))
if err != nil {
log.Fatalf("failed to listing file %s: %v", file.Name(), err)
}
self.startSimForIdentity(fn)
simCount++
}
}
}
for {
time.Sleep(time.Second * 5)
pfxlog.Logger().Infof("%d/%d identities authenticated", self.authenticated.Count(), simCount)
}
}
func (self *simpleSimAction) startSimForIdentity(path string) {
log := pfxlog.Logger().WithField("config", path)
zitiConfig, err := ziti.NewConfigFromFile(path)
if err != nil {
log.WithError(err).Fatal("unable to load ziti identity")
return
}
zitiContext, err := ziti.NewContext(zitiConfig)
if err != nil {
log.WithError(err).Fatal("could not create sdk context from config")
}
go self.runSim(path, zitiContext)
}
func (self *simpleSimAction) runSim(path string, ctx ziti.Context) {
log := pfxlog.Logger().WithField("config", path)
var lastLog time.Time
cycle := 0
for {
cycle++
validServices := 0
dialSuccesses := 0
services, err := ctx.GetServices()
if err != nil {
self.authenticated.Remove(ctx.GetId())
log.WithField("cycle", cycle).WithError(err).Error("unable to list services")
time.Sleep(time.Second * 5)
continue
} else {
self.authenticated.Set(ctx.GetId(), time.Now())
}
for _, service := range services {
if !slices.Contains(service.Permissions, rest_model.DialBindDial) {
continue
}
validServices++
svcLog := log.WithField("svc", *service.Name).WithField("cycle", cycle)
conn, err := ctx.Dial(*service.Name)
if err != nil {
svcLog.WithError(err).Error("unable to dial service")
} else {
randomString := eid.New()
if _, err = conn.Write([]byte(randomString)); err != nil {
svcLog.WithError(err).Error("unable to write")
} else {
result := make([]byte, len(randomString))
n, err := conn.Read(result)
if err != nil {
svcLog.WithError(err).Error("unable to read")
} else {
result = result[:n]
if string(result) != randomString {
svcLog.WithError(err).Error("%s != %s", randomString, result)
} else {
dialSuccesses++
}
}
}
}
if conn != nil {
if err = conn.Close(); err != nil {
svcLog.WithError(err).Error("error closing conn")
}
time.Sleep(time.Millisecond * 250)
}
}
if time.Since(lastLog) > time.Minute {
log.WithField("serviceCount", len(services)).
WithField("cycle", cycle).
WithField("successfulDials", fmt.Sprintf("%d/%d", dialSuccesses, validServices)).
Info("completed")
lastLog = time.Now()
}
time.Sleep(time.Duration(500+rand.Intn(1000)) * time.Second)
}
}
func main() {
cli := newSimpleSimCmd()
if err := cli.Execute(); err != nil {
pfxlog.Logger().Fatal(err.Error())
}
}
-4
View File
@@ -19,8 +19,6 @@
package main
import (
"math/rand"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/transport/v2"
"github.com/openziti/transport/v2/tcp"
@@ -28,7 +26,6 @@ import (
"github.com/openziti/transport/v2/transwarp"
"github.com/openziti/transport/v2/transwarptls"
"github.com/openziti/transport/v2/wss"
"github.com/openziti/foundation/v2/info"
"github.com/openziti/ziti/zititest/ziti-fabric-test/subcmd"
_ "github.com/openziti/ziti/zititest/ziti-fabric-test/subcmd/client"
_ "github.com/openziti/ziti/zititest/ziti-fabric-test/subcmd/loop2"
@@ -46,6 +43,5 @@ func init() {
}
func main() {
rand.Seed(info.NowInMilliseconds())
subcmd.Execute()
}
+1 -1
View File
@@ -39,7 +39,7 @@ func Exec(m *model.Model, args ...string) (string, error) {
ziticli.SetArgs(args)
logrus.Infof("executing: %s", strings.Join(args, " "))
if err := ziticli.Execute(); err != nil {
logrus.Errorf("err executing command, err:[%e]", err)
logrus.WithError(err).Error("err executing command")
}
return cliOut.String(), nil
+18 -2
View File
@@ -38,7 +38,7 @@ func getZitiProcessFilter(c *model.Component, zitiType string) func(string) bool
func startZitiComponent(c *model.Component, zitiType string, version string, configName string) error {
user := c.GetHost().GetSshUser()
binaryPath := getZitiBinaryPath(c, version)
binaryPath := GetZitiBinaryPath(c, version)
configPath := fmt.Sprintf("/home/%s/fablab/cfg/%s", user, configName)
logsPath := fmt.Sprintf("/home/%s/logs/%s.log", user, c.Id)
@@ -74,7 +74,7 @@ func canonicalizeGoAppVersion(version *string) {
}
}
func getZitiBinaryPath(c *model.Component, version string) string {
func GetZitiBinaryPath(c *model.Component, version string) string {
return getBinaryPath(c, "ziti", version)
}
@@ -114,3 +114,19 @@ func reEnrollIdentity(run model.Run, c *model.Component, zitiBinaryPath string,
return c.GetHost().ExecLogOnlyOnError(cmd)
}
func setupDnsForTunneler(c *model.Component) error {
key := "ziti_tunnel.resolve_setup_done"
if _, found := c.Host.Data[key]; !found {
cmds := []string{
"sudo sed -i 's/#DNS=/DNS=127.0.0.1/g' /etc/systemd/resolved.conf",
"sudo systemctl restart systemd-resolved",
}
if err := c.Host.ExecLogOnlyOnError(cmds...); err != nil {
return err
}
c.Host.Data[key] = true
return nil
}
return nil
}
+20 -4
View File
@@ -45,6 +45,14 @@ type ControllerType struct {
DNSNames []string
}
func (self *ControllerType) Label() string {
return "ziti-controller"
}
func (self *ControllerType) GetVersion() string {
return self.Version
}
func (self *ControllerType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
}
@@ -104,7 +112,15 @@ func (self *ControllerType) IsRunning(_ model.Run, c *model.Component) (bool, er
return len(pids) > 0, nil
}
func (self *ControllerType) Start(_ model.Run, c *model.Component) error {
func (self *ControllerType) Start(r model.Run, c *model.Component) error {
isRunninng, err := self.IsRunning(r, c)
if err != nil {
return err
}
if isRunninng {
fmt.Printf("controller %s already started\n", c.Id)
return nil
}
return startZitiComponent(c, "controller", self.Version, self.getConfigName(c))
}
@@ -113,7 +129,7 @@ func (self *ControllerType) Stop(_ model.Run, c *model.Component) error {
}
func (self *ControllerType) GetBinaryPath(c *model.Component) string {
return getZitiBinaryPath(c, self.Version)
return GetZitiBinaryPath(c, self.Version)
}
func (self *ControllerType) InitStandalone(run model.Run, c *model.Component) error {
@@ -130,7 +146,7 @@ func (self *ControllerType) InitStandalone(run model.Run, c *model.Component) er
factory := c.GetHost().NewSshConfigFactory()
binaryPath := getZitiBinaryPath(c, self.Version)
binaryPath := GetZitiBinaryPath(c, self.Version)
configPath := fmt.Sprintf("/home/%s/fablab/cfg/%s", factory.User(), self.getConfigName(c))
tmpl := "rm -f /home/%v/fablab/ctrl.db && set -o pipefail; %s controller --log-formatter pfxlog edge init %s -u %s -p %s 2>&1 | tee logs/controller.edge.init.log"
@@ -142,7 +158,7 @@ func (self *ControllerType) InitRaft(run model.Run, c *model.Component) error {
count := 0
err := run.GetModel().ForEachComponent("*", 1, func(other *model.Component) error {
if _, ok := c.Type.(*ControllerType); ok {
binaryPath := getZitiBinaryPath(c, self.Version)
binaryPath := GetZitiBinaryPath(c, self.Version)
tmpl := "%s agent cluster add %v --id %v"
cmd := fmt.Sprintf(tmpl, binaryPath, "tls:"+other.Host.PublicIp+":6262", other.Id)
count++
+41 -5
View File
@@ -1,3 +1,19 @@
/*
Copyright 2019 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 zitilab
import (
@@ -13,8 +29,18 @@ var _ model.ComponentType = (*EchoServerType)(nil)
type EchoServerMode int
type EchoServerType struct {
Version string
LocalPath string
Version string
LocalPath string
Port uint16
BindService string
}
func (self *EchoServerType) Label() string {
return "echo-server"
}
func (self *EchoServerType) GetVersion() string {
return self.Version
}
func (self *EchoServerType) InitType(*model.Component) {
@@ -48,7 +74,7 @@ func (self *EchoServerType) IsRunning(_ model.Run, c *model.Component) (bool, er
func (self *EchoServerType) Start(run model.Run, c *model.Component) error {
user := c.GetHost().GetSshUser()
binaryPath := getZitiBinaryPath(c, self.Version)
binaryPath := GetZitiBinaryPath(c, self.Version)
configPath := fmt.Sprintf("/home/%s/fablab/cfg/%s.json", user, c.Id)
logsPath := fmt.Sprintf("/home/%s/logs/%s.log", user, c.Id)
@@ -57,8 +83,18 @@ func (self *EchoServerType) Start(run model.Run, c *model.Component) error {
ha = "--ha"
}
serviceCmd := fmt.Sprintf("nohup %s demo echo-server --cli-agent-alias %s %s -i %s > %s 2>&1 &",
binaryPath, c.Id, ha, configPath, logsPath)
serviceHostingFlags := ""
if self.BindService != "" {
serviceHostingFlags = fmt.Sprintf("-i %s -s %s", configPath, self.BindService)
}
portFlag := ""
if self.Port > 0 {
portFlag = fmt.Sprintf("-p %d", self.Port)
}
serviceCmd := fmt.Sprintf("nohup %s demo echo-server --cli-agent-alias %s %s %s %s > %s 2>&1 &",
binaryPath, c.Id, ha, serviceHostingFlags, portFlag, logsPath)
value, err := c.GetHost().ExecLogged(serviceCmd)
if err != nil {
+8
View File
@@ -29,6 +29,14 @@ type IPerfServerType struct {
Port uint16
}
func (self *IPerfServerType) Label() string {
return "iperf-server"
}
func (self *IPerfServerType) GetVersion() string {
return "os-provided"
}
func (self *IPerfServerType) Dump() any {
return map[string]string{
"type_id": "iperf-server",
+26 -14
View File
@@ -19,7 +19,6 @@ package zitilab
import (
"fmt"
"github.com/openziti/fablab/kernel/lib"
"github.com/openziti/fablab/kernel/lib/actions/host"
"github.com/openziti/fablab/kernel/model"
zitilib_actions "github.com/openziti/ziti/zititest/zitilab/actions"
"github.com/openziti/ziti/zititest/zitilab/stageziti"
@@ -45,6 +44,14 @@ type RouterType struct {
LocalPath string
}
func (self *RouterType) Label() string {
return "ziti-router"
}
func (self *RouterType) GetVersion() string {
return self.Version
}
func (self *RouterType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
}
@@ -68,12 +75,7 @@ func (self *RouterType) Dump() any {
func (self *RouterType) InitializeHost(run model.Run, c *model.Component) error {
if self.isTunneler(c) {
cmds := []string{
"sudo sed -i 's/#DNS=/DNS=127.0.0.1/g' /etc/systemd/resolved.conf",
"sudo systemctl restart systemd-resolved",
"mkdir -p /home/ubuntu/logs",
}
return host.Exec(c.GetHost(), cmds...).Execute(run)
return setupDnsForTunneler(c)
}
return nil
}
@@ -84,7 +86,7 @@ func (self *RouterType) StageFiles(r model.Run, c *model.Component) error {
configSource = "router.yml.tmpl"
}
configName := self.getConfigName(c)
configName := self.GetConfigName(c)
if err := lib.GenerateConfigForComponent(c, self.ConfigSourceFS, configSource, configName, r); err != nil {
return err
@@ -97,7 +99,7 @@ func (self *RouterType) isTunneler(c *model.Component) bool {
return c.HasLocalOrAncestralTag("tunneler")
}
func (self *RouterType) getConfigName(c *model.Component) string {
func (self *RouterType) GetConfigName(c *model.Component) string {
configName := self.ConfigName
if configName == "" {
configName = c.Id + ".yml"
@@ -126,7 +128,7 @@ func (self *RouterType) Start(r model.Run, c *model.Component) error {
fmt.Printf("router %s already started\n", c.Id)
return nil
}
return startZitiComponent(c, "router", self.Version, self.getConfigName(c))
return startZitiComponent(c, "router", self.Version, self.GetConfigName(c))
}
func (self *RouterType) Stop(run model.Run, c *model.Component) error {
@@ -174,15 +176,15 @@ func (self *RouterType) CreateAndEnroll(run model.Run, c *model.Component) error
}
tmpl := "set -o pipefail; %s router enroll /home/ubuntu/fablab/cfg/%s -j %s 2>&1 | tee /home/ubuntu/logs/%s.router.enroll.log "
cmd := fmt.Sprintf(tmpl, getZitiBinaryPath(c, self.Version), self.getConfigName(c), remoteJwt, c.Id)
cmd := fmt.Sprintf(tmpl, GetZitiBinaryPath(c, self.Version), self.GetConfigName(c), remoteJwt, c.Id)
return c.GetHost().ExecLogOnlyOnError(cmd)
}
func (self *RouterType) ReEnroll(_ model.Run, c *model.Component) error {
func (self *RouterType) ReEnroll(run model.Run, c *model.Component) error {
jwtFileName := filepath.Join(model.ConfigBuild(), c.Id+".jwt")
args := []string{"re-enroll", "edge-router", c.Id, "-j", "--jwt-output-file", jwtFileName}
args := []string{"re-enroll", "edge-router", "-j", "--jwt-output-file", jwtFileName, "--", c.Id}
if err := zitilib_actions.EdgeExec(c.GetModel(), args...); err != nil {
return err
@@ -193,8 +195,18 @@ func (self *RouterType) ReEnroll(_ model.Run, c *model.Component) error {
return err
}
zitiVersion := self.Version
ctrls := run.GetModel().SelectComponents(".ctrl")
for _, ctrl := range ctrls {
if ctrl.Type != nil {
zitiVersion = ctrl.Type.GetVersion()
break
}
}
tmpl := "set -o pipefail; %s router enroll /home/ubuntu/fablab/cfg/%s -j %s 2>&1 | tee /home/ubuntu/logs/%s.router.enroll.log "
cmd := fmt.Sprintf(tmpl, getZitiBinaryPath(c, self.Version), self.getConfigName(c), remoteJwt, c.Id)
cmd := fmt.Sprintf(tmpl, GetZitiBinaryPath(c, zitiVersion), self.GetConfigName(c), remoteJwt, c.Id)
return c.GetHost().ExecLogOnlyOnError(cmd)
}
+94
View File
@@ -0,0 +1,94 @@
/*
Copyright 2019 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 zitilab
import (
"fmt"
"github.com/openziti/fablab/kernel/model"
"github.com/openziti/ziti/zititest/zitilab/stageziti"
"github.com/sirupsen/logrus"
"strings"
)
var _ model.ComponentType = (*SimpleSimType)(nil)
type SimpleSimType struct {
LocalPath string
ConfigPath string
}
func (self *SimpleSimType) Label() string {
return "simple-sim"
}
func (self *SimpleSimType) GetVersion() string {
return "local"
}
func (self *SimpleSimType) Dump() any {
return map[string]string{
"type_id": "simple-sim",
"local_path": self.LocalPath,
"config_path": self.ConfigPath,
}
}
func (self *SimpleSimType) StageFiles(r model.Run, c *model.Component) error {
return stageziti.StageLocalOnce(r, "simple-sim", c, self.LocalPath)
}
func (self *SimpleSimType) getProcessFilter() func(string) bool {
return func(s string) bool {
return strings.Contains(s, "simple-sim")
}
}
func (self *SimpleSimType) IsRunning(_ model.Run, c *model.Component) (bool, error) {
pids, err := c.GetHost().FindProcesses(self.getProcessFilter())
if err != nil {
return false, err
}
return len(pids) > 0, nil
}
func (self *SimpleSimType) Start(_ model.Run, c *model.Component) error {
user := c.GetHost().GetSshUser()
binaryPath := getBinaryPath(c, "simple-sim", "")
logsPath := fmt.Sprintf("/home/%s/logs/%s.log", user, c.Id)
configPath := fmt.Sprintf("/home/%s/%s", user, self.ConfigPath)
serviceCmd := fmt.Sprintf("%s %s --log-formatter pfxlog > %s 2>&1 &",
binaryPath, configPath, logsPath)
value, err := c.Host.ExecLogged(
"rm -f "+logsPath,
serviceCmd)
if err != nil {
return err
}
if len(value) > 0 {
logrus.Infof("output [%s]", strings.Trim(value, " \t\r\n"))
}
return nil
}
func (self *SimpleSimType) Stop(_ model.Run, c *model.Component) error {
return c.GetHost().KillProcesses("-TERM", self.getProcessFilter())
}
+24
View File
@@ -1,3 +1,19 @@
/*
Copyright 2019 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 zitilab
import (
@@ -15,6 +31,14 @@ type ZCatType struct {
LocalPath string
}
func (self *ZCatType) Label() string {
return "zcat"
}
func (self *ZCatType) GetVersion() string {
return self.Version
}
func (self *ZCatType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
}
@@ -34,6 +34,14 @@ type ZitiEdgeTunnelType struct {
ConfigPathF func(c *model.Component) string
}
func (self *ZitiEdgeTunnelType) Label() string {
return "ziti-edge-tunnel"
}
func (self *ZitiEdgeTunnelType) GetVersion() string {
return self.Version
}
func (self *ZitiEdgeTunnelType) GetActions() map[string]model.ComponentAction {
return map[string]model.ComponentAction{
ZitiTunnelActionsReEnroll: model.ComponentActionF(self.ReEnroll),
@@ -125,5 +133,5 @@ func (self *ZitiEdgeTunnelType) Stop(_ model.Run, c *model.Component) error {
}
func (self *ZitiEdgeTunnelType) ReEnroll(run model.Run, c *model.Component) error {
return reEnrollIdentity(run, c, getZitiBinaryPath(c, self.ZitiVersion), self.GetConfigPath(c))
return reEnrollIdentity(run, c, GetZitiBinaryPath(c, self.ZitiVersion), self.GetConfigPath(c))
}
+11 -14
View File
@@ -56,6 +56,14 @@ type ZitiTunnelType struct {
ConfigPathF func(c *model.Component) string
}
func (self *ZitiTunnelType) Label() string {
return "ziti-tunnel"
}
func (self *ZitiTunnelType) GetVersion() string {
return self.Version
}
func (self *ZitiTunnelType) GetActions() map[string]model.ComponentAction {
return map[string]model.ComponentAction{
ZitiTunnelActionsReEnroll: model.ComponentActionF(self.ReEnroll),
@@ -80,18 +88,7 @@ func (self *ZitiTunnelType) StageFiles(r model.Run, c *model.Component) error {
func (self *ZitiTunnelType) InitializeHost(_ model.Run, c *model.Component) error {
if self.Mode == ZitiTunnelModeTproxy {
key := "ziti_tunnel.resolve_setup_done"
if _, found := c.Host.Data[key]; !found {
cmds := []string{
"sudo sed -i 's/#DNS=/DNS=127.0.0.1/g' /etc/systemd/resolved.conf",
"sudo systemctl restart systemd-resolved",
}
if err := c.Host.ExecLogOnlyOnError(cmds...); err != nil {
return err
}
c.Host.Data[key] = true
return nil
}
return setupDnsForTunneler(c)
}
return nil
}
@@ -120,7 +117,7 @@ func (self *ZitiTunnelType) Start(_ model.Run, c *model.Component) error {
user := c.GetHost().GetSshUser()
binaryPath := getZitiBinaryPath(c, self.Version)
binaryPath := GetZitiBinaryPath(c, self.Version)
configPath := self.GetConfigPath(c)
logsPath := fmt.Sprintf("/home/%s/logs/%s.log", user, c.Id)
@@ -151,5 +148,5 @@ func (self *ZitiTunnelType) Stop(_ model.Run, c *model.Component) error {
}
func (self *ZitiTunnelType) ReEnroll(run model.Run, c *model.Component) error {
return reEnrollIdentity(run, c, getZitiBinaryPath(c, self.Version), self.GetConfigPath(c))
return reEnrollIdentity(run, c, GetZitiBinaryPath(c, self.Version), self.GetConfigPath(c))
}
@@ -47,6 +47,14 @@ type ZrokControllerType struct {
PreCreateClients string
}
func (self *ZrokControllerType) Label() string {
return "zrok-controller"
}
func (self *ZrokControllerType) GetVersion() string {
return self.Version
}
func (self *ZrokControllerType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
}
@@ -46,6 +46,14 @@ type ZrokFrontendType struct {
ZrokCtrlSelector string
}
func (self *ZrokFrontendType) Label() string {
return "zrok-frontend"
}
func (self *ZrokFrontendType) GetVersion() string {
return self.Version
}
func (self *ZrokFrontendType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
if self.ZrokCtrlSelector == "" {
@@ -39,6 +39,14 @@ type ZrokLoopTestType struct {
Pacing time.Duration
}
func (self *ZrokLoopTestType) Label() string {
return "zrok-loop-test"
}
func (self *ZrokLoopTestType) GetVersion() string {
return self.Version
}
func (self *ZrokLoopTestType) InitType(*model.Component) {
canonicalizeGoAppVersion(&self.Version)
if self.Iterations == 0 {
+7 -4
View File
@@ -15,9 +15,12 @@ import (
type ZitiDbBuilderStrategy interface {
GetDbFile(m *model.Model) string
ProcessDbModel(tx *bbolt.Tx, m *model.Model, builder *ZitiDbBuilder) error
}
type ZitiEdgeRouterStrategy interface {
GetSite(router *db.EdgeRouter) (string, bool)
PostProcess(router *db.EdgeRouter, c *model.Component)
ProcessDbModel(tx *bbolt.Tx, m *model.Model, builder *ZitiDbBuilder) error
}
type ZitiDbBuilder struct {
@@ -63,7 +66,7 @@ func (self *ZitiDbBuilder) Build(m *model.Model) error {
})
}
func (self *ZitiDbBuilder) CreateEdgeRouterHosts(tx *bbolt.Tx, m *model.Model) error {
func (self *ZitiDbBuilder) CreateEdgeRouterHosts(tx *bbolt.Tx, m *model.Model, erStrategy ZitiEdgeRouterStrategy) error {
ids, _, err := self.stores.EdgeRouter.QueryIds(tx, "true limit none")
if err != nil {
return err
@@ -75,7 +78,7 @@ func (self *ZitiDbBuilder) CreateEdgeRouterHosts(tx *bbolt.Tx, m *model.Model) e
return err
}
if site, useEdgeRouter := self.Strategy.GetSite(er); useEdgeRouter {
if site, useEdgeRouter := erStrategy.GetSite(er); useEdgeRouter {
regionId := site[:len(site)-1]
var region *model.Region
@@ -114,7 +117,7 @@ func (self *ZitiDbBuilder) CreateEdgeRouterHosts(tx *bbolt.Tx, m *model.Model) e
}
host.Components[er.Id] = component
self.Strategy.PostProcess(er, component)
erStrategy.PostProcess(er, component)
}
}
return nil
+9
View File
@@ -64,6 +64,15 @@ func StageZrok(run model.Run, component *model.Component, version string, source
})
}
func StageLocalOnce(run model.Run, executable string, component *model.Component, source string) error {
op := fmt.Sprintf("install.%s-local", executable)
return run.DoOnce(op, func() error {
return StageExecutable(run, executable, component, "", source, func() error {
return fmt.Errorf("unable to fetch %s, as it a local-only application", executable)
})
})
}
func StageExecutable(run model.Run, executable string, component *model.Component, version string, source string, fallbackF func() error) error {
fileName := executable
if version != "" {