Files
zt/ziti/run/quickstart.go
dovholuknfandGitHub caecfbb007 two small log-related changes (#2923)
* two small log-related changes

* fix wrong directive
2025-03-24 14:26:53 -04:00

792 lines
25 KiB
Go

/*
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 run
import (
"context"
"crypto/tls"
"fmt"
"github.com/openziti/ziti/ziti/cmd/edge"
"github.com/openziti/ziti/ziti/enroll"
"io"
"net"
"net/http"
"os"
"os/signal"
"os/user"
"path"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/google/uuid"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/ziti/common/version"
"github.com/openziti/ziti/controller/rest_client/cluster"
edgeSubCmd "github.com/openziti/ziti/controller/subcmd"
"github.com/openziti/ziti/ziti/cmd/agentcli"
"github.com/openziti/ziti/ziti/cmd/api"
"github.com/openziti/ziti/ziti/cmd/common"
"github.com/openziti/ziti/ziti/cmd/create"
"github.com/openziti/ziti/ziti/cmd/helpers"
"github.com/openziti/ziti/ziti/cmd/pki"
"github.com/openziti/ziti/ziti/constants"
"github.com/openziti/ziti/ziti/util"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
type QuickstartOpts struct {
Username string
Password string
AlreadyInitialized bool
Home string
ControllerAddress string
ControllerPort uint16
RouterAddress string
RouterPort uint16
out io.Writer
errOut io.Writer
cleanOnExit bool
TrustDomain string
isHA bool
InstanceID string
ClusterMember string
joinCommand bool
verbose bool
nonVoter bool
routerless bool
}
func addCommonQuickstartFlags(cmd *cobra.Command, options *QuickstartOpts) {
currentCtrlAddy := helpers.GetCtrlEdgeAdvertisedAddress()
currentCtrlPort := helpers.GetCtrlEdgeAdvertisedPort()
currentRouterAddy := helpers.GetRouterAdvertisedAddress()
currentRouterPort := helpers.GetZitiEdgeRouterPort()
defaultCtrlPort, _ := strconv.ParseInt(constants.DefaultCtrlEdgeAdvertisedPort, 10, 16)
defaultRouterPort, _ := strconv.ParseInt(constants.DefaultZitiEdgeRouterPort, 10, 16)
cmd.Flags().StringVarP(&options.Username, "username", "u", "", "admin username, default: admin")
cmd.Flags().StringVarP(&options.Password, "password", "p", "", "admin password, default: admin")
cmd.Flags().StringVar(&options.Home, "home", "", "permanent directory")
cmd.Flags().StringVar(&options.ControllerAddress, "ctrl-address", "", "sets the advertised address for the control plane and API. current: "+currentCtrlAddy)
cmd.Flags().Uint16Var(&options.ControllerPort, "ctrl-port", uint16(defaultCtrlPort), "sets the port to use for the control plane and API. current: "+currentCtrlPort)
cmd.Flags().StringVar(&options.RouterAddress, "router-address", "", "sets the advertised address for the integrated router. current: "+currentRouterAddy)
cmd.Flags().Uint16Var(&options.RouterPort, "router-port", uint16(defaultRouterPort), "sets the port to use for the integrated router. current: "+currentRouterPort)
cmd.Flags().BoolVar(&options.routerless, "no-router", false, "specifies the quickstart should not start a router")
cmd.Flags().BoolVar(&options.verbose, "verbose", false, "Show additional output.")
}
func addQuickstartHaFlags(cmd *cobra.Command, options *QuickstartOpts) {
cmd.Flags().StringVar(&options.TrustDomain, "trust-domain", "", "the specified trust domain to be used in SPIFFE ids.")
cmd.Flags().StringVar(&options.InstanceID, "instance-id", "", "specifies a unique instance id for use in ha mode.")
}
// NewQuickStartCmd creates a command object for the "create" command
func NewQuickStartCmd(out io.Writer, errOut io.Writer, context context.Context) *cobra.Command {
options := &QuickstartOpts{}
cmd := &cobra.Command{
Use: "quickstart",
Short: "runs a Controller and Router in quickstart mode",
Long: "runs a Controller and Router in quickstart mode with a temporary directory; suitable for testing and development",
Run: func(cmd *cobra.Command, args []string) {
options.out = out
options.errOut = errOut
options.TrustDomain = "quickstart"
options.InstanceID = "quickstart"
err := options.run(context)
if err != nil {
logrus.Fatal(err)
}
},
}
addCommonQuickstartFlags(cmd, options)
cmd.AddCommand(NewQuickStartJoinClusterCmd(out, errOut, context))
cmd.AddCommand(NewQuickStartHaCmd(out, errOut, context))
return cmd
}
func NewQuickStartHaCmd(out io.Writer, errOut io.Writer, context context.Context) *cobra.Command {
options := &QuickstartOpts{}
cmd := &cobra.Command{
Use: "ha",
Short: "runs a Controller and Router in quickstart HA mode and creates the first cluster member",
Long: "runs a Controller and Router in quickstart HA mode and creates the first cluster member with a temporary directory; suitable for testing and development",
Run: func(cmd *cobra.Command, args []string) {
options.out = out
options.errOut = errOut
options.isHA = true
if options.TrustDomain == "" {
options.TrustDomain = uuid.New().String()
fmt.Println("Trust domain was not supplied. Using a random trust domain: " + options.TrustDomain)
}
err := options.run(context)
if err != nil {
logrus.Fatal(err)
}
},
}
addCommonQuickstartFlags(cmd, options)
addQuickstartHaFlags(cmd, options)
cmd.Hidden = true
return cmd
}
func NewQuickStartJoinClusterCmd(out io.Writer, errOut io.Writer, context context.Context) *cobra.Command {
options := &QuickstartOpts{}
cmd := &cobra.Command{
Use: "join",
Short: "runs a Controller and Router in quickstart mode and joins an existing cluster",
Long: "runs a Controller and Router in quickstart mode and joins an existing cluster with a temporary directory; suitable for testing and development",
Run: func(cmd *cobra.Command, args []string) {
options.out = out
options.errOut = errOut
err := options.join(context)
if err != nil {
logrus.Fatal(err)
}
},
}
addCommonQuickstartFlags(cmd, options)
addQuickstartHaFlags(cmd, options)
cmd.Flags().StringVarP(&options.ClusterMember, "cluster-member", "m", "", "address of a cluster member. required. example tls:localhost:1280")
cmd.Flags().BoolVar(&options.nonVoter, "non-voting", false, "used with ha mode. specifies the member is a non-voting member")
cmd.Hidden = true
return cmd
}
func (o *QuickstartOpts) cleanupHome() {
if o.cleanOnExit {
fmt.Println("Removing temp directory at: " + o.Home)
_ = os.RemoveAll(o.Home)
} else {
fmt.Println("Environment left intact at: " + o.Home)
}
}
func (o *QuickstartOpts) join(ctx context.Context) error {
if strings.TrimSpace(o.InstanceID) == "" {
logrus.Fatalf("the instance-id is required when joining a cluster")
}
if strings.TrimSpace(o.Home) == "" {
logrus.Fatalf("the home directory must be specified when joining an existing cluster. the root-ca is used to create the server's pki")
}
if o.ClusterMember == "" {
logrus.Fatalf("--cluster-member is required")
}
o.isHA = true
o.joinCommand = true
return o.run(ctx)
}
func (o *QuickstartOpts) run(ctx context.Context) error {
if o.verbose {
pfxlog.GlobalInit(logrus.DebugLevel, pfxlog.DefaultOptions().Color())
}
//set env vars
if o.Home == "" {
tmpDir, _ := os.MkdirTemp("", "quickstart")
o.Home = tmpDir
o.cleanOnExit = true
logrus.Infof("temporary --home '%s'", o.Home)
} else {
//normalize path
if strings.HasPrefix(o.Home, "~") {
usr, err := user.Current()
if err != nil {
return fmt.Errorf("could not find user's home directory")
}
home := usr.HomeDir
// Replace only the first instance of ~ in case it appears later in the path
o.Home = strings.Replace(o.Home, "~", home, 1)
}
logrus.Infof("permanent --home '%s' will not be removed on exit", o.Home)
}
if o.ControllerAddress != "" {
_ = os.Setenv(constants.CtrlAdvertisedAddressVarName, o.ControllerAddress)
_ = os.Setenv(constants.CtrlEdgeAdvertisedAddressVarName, o.ControllerAddress)
}
if o.ControllerPort > 0 {
_ = os.Setenv(constants.CtrlAdvertisedPortVarName, strconv.Itoa(int(o.ControllerPort)))
_ = os.Setenv(constants.CtrlEdgeAdvertisedPortVarName, strconv.Itoa(int(o.ControllerPort)))
}
if o.RouterAddress != "" {
_ = os.Setenv(constants.ZitiEdgeRouterAdvertisedAddressVarName, o.RouterAddress)
}
if o.RouterPort > 0 {
_ = os.Setenv(constants.ZitiEdgeRouterPortVarName, strconv.Itoa(int(o.RouterPort)))
_ = os.Setenv(constants.ZitiEdgeRouterListenerBindPortVarName, strconv.Itoa(int(o.RouterPort)))
}
if o.Username == "" {
o.Username = "admin"
}
if o.Password == "" {
o.Password = "admin"
}
if o.InstanceID == "" {
o.InstanceID = uuid.New().String()
}
ctrlYaml := path.Join(o.instHome(), "ctrl.yaml")
routerName := "router-" + o.InstanceID
//ZITI_HOME=/tmp ziti create config controller | grep -v "#" | sed -E 's/^ *$//g' | sed '/^$/d'
_ = os.Setenv("ZITI_HOME", o.Home)
pkiLoc := path.Join(o.Home, "pki")
rootLoc := path.Join(pkiLoc, "root-ca")
pkiIntermediateName := o.scopedName("intermediate-ca")
pkiServerName := o.scopedNameOff("server")
pkiClientName := o.scopedNameOff("client")
intermediateLoc := path.Join(pkiLoc, pkiIntermediateName)
_ = os.Setenv("ZITI_PKI_CTRL_CA", path.Join(rootLoc, "certs", "root-ca.cert"))
_ = os.Setenv("ZITI_PKI_CTRL_KEY", path.Join(intermediateLoc, "keys", pkiServerName+".key"))
_ = os.Setenv("ZITI_PKI_CTRL_SERVER_CERT", path.Join(intermediateLoc, "certs", pkiServerName+".chain.pem"))
_ = os.Setenv("ZITI_PKI_CTRL_CERT", path.Join(intermediateLoc, "certs", pkiClientName+".chain.pem"))
_ = os.Setenv("ZITI_PKI_SIGNER_CERT", path.Join(intermediateLoc, "certs", pkiIntermediateName+".cert"))
_ = os.Setenv("ZITI_PKI_SIGNER_KEY", path.Join(intermediateLoc, "keys", pkiIntermediateName+".key"))
routerNameFromEnv := os.Getenv(constants.ZitiEdgeRouterNameVarName)
if routerNameFromEnv != "" {
routerName = routerNameFromEnv
}
dbDir := path.Join(o.instHome(), "db")
if _, err := os.Stat(dbDir); !os.IsNotExist(err) {
o.AlreadyInitialized = true
} else {
_ = os.MkdirAll(dbDir, 0o700)
logrus.Debugf("made directory '%s'", dbDir)
o.createMinimalPki()
_ = os.Setenv("ZITI_HOME", o.instHome())
ctrl := create.NewCmdCreateConfigController()
args := []string{
fmt.Sprintf("--output=%s", ctrlYaml),
}
if o.isHA {
args = append(args, "--clustered")
}
ctrl.SetArgs(args)
err = ctrl.Execute()
if err != nil {
logrus.Fatal(err)
}
if !o.isHA {
initCmd := edgeSubCmd.NewEdgeInitializeCmd(version.GetCmdBuildInfo())
initCmd.SetArgs([]string{
fmt.Sprintf("--username=%s", o.Username),
fmt.Sprintf("--password=%s", o.Password),
ctrlYaml,
})
initErr := initCmd.Execute()
if initErr != nil {
logrus.Fatal(initErr)
}
}
}
fmt.Println("Starting controller...")
go func() {
runCtrl := NewRunControllerCmd()
runCtrl.SetArgs([]string{
ctrlYaml,
})
runCtrlErr := runCtrl.Execute()
if runCtrlErr != nil {
logrus.Fatal(runCtrlErr)
}
}()
fmt.Println("Controller running...")
ctrlAddy := helpers.GetCtrlEdgeAdvertisedAddress()
ctrlPort := helpers.GetCtrlEdgeAdvertisedPort()
ctrlUrl := fmt.Sprintf("https://%s:%s", ctrlAddy, ctrlPort)
c := make(chan error)
timeout, _ := time.ParseDuration("30s")
go waitForController(ctrlUrl, c)
select {
case <-c:
//completed normally
logrus.Info("Controller online. Continuing...")
case <-time.After(timeout):
o.cleanupHome()
return fmt.Errorf("timed out waiting for controller: %s", ctrlUrl)
}
if o.isHA {
p := common.NewOptionsProvider(o.out, o.errOut)
fmt.Println("waiting three seconds for controller to become ready...")
if !o.joinCommand {
maxRetries := 5
for attempt := 1; attempt <= maxRetries; attempt++ {
fmt.Printf("initializing controller at port: %d\n", o.ControllerPort)
agentInitCmd := agentcli.NewAgentClusterInit(p)
pid := os.Getpid()
args := []string{
o.Username,
o.Password,
o.Username,
fmt.Sprintf("--pid=%d", pid),
}
agentInitCmd.SetArgs(args)
agentInitErr := agentInitCmd.Execute()
if agentInitErr != nil {
if attempt < maxRetries {
fmt.Println("initialization failed. waiting two seconds and trying again")
time.Sleep(2 * time.Second) // Wait before retrying
} else {
fmt.Println("Max retries reached. Failing.")
return agentInitErr
}
} else {
break
}
}
} else {
agentJoinCmd := agentcli.NewAgentClusterAdd(p)
args := []string{
o.ClusterMember,
fmt.Sprintf("--pid=%d", os.Getpid()),
fmt.Sprintf("--voter=%t", !o.nonVoter),
}
agentJoinCmd.SetArgs(args)
addChan := make(chan struct{})
addTimeout := time.Second * 30
go func() {
o.waitForLeader()
agentJoinErr := agentJoinCmd.Execute()
if agentJoinErr != nil {
logrus.Fatal(agentJoinErr)
}
close(addChan)
}()
select {
case <-addChan:
//completed normally
logrus.Info("Add command successful. continuing...")
case <-time.After(addTimeout):
o.cleanupHome()
return fmt.Errorf("timed out adding to cluster")
}
}
}
erConfigFile := path.Join(o.instHome(), routerName+".yaml")
err := o.configureRouter(routerName, erConfigFile, ctrlUrl)
if err != nil {
return err
}
o.runRouter(erConfigFile)
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGTERM)
if !o.routerless {
r := make(chan struct{})
timeout, _ = time.ParseDuration("30s")
logrus.Infof("waiting for router at: %s:%d", o.RouterAddress, o.RouterPort)
go waitForRouter(o.RouterAddress, o.RouterPort, r)
select {
case <-r:
//completed normally
case <-time.After(timeout):
o.cleanupHome()
return fmt.Errorf("timed out waiting for router on port: %d", o.RouterPort)
}
}
if o.isHA {
go func() {
time.Sleep(3 * time.Second) // output this after a bit...
nextInstId := incrementStringSuffix(o.InstanceID)
fmt.Println()
o.printDetails()
fmt.Println("=======================================================================================")
fmt.Println("Quickly add another member to this cluster using: ")
fmt.Printf(" ziti edge quickstart join \\\n")
fmt.Printf(" --ctrl-port %d \\\n", o.ControllerPort+1)
fmt.Printf(" --router-port %d \\\n", o.RouterPort+1)
fmt.Printf(" --home \"%s\" \\\n", o.Home)
fmt.Printf(" --trust-domain=\"%s\" \\\n", o.TrustDomain)
fmt.Printf(" --cluster-member tls:%s:%s\\ \n", ctrlAddy, ctrlPort)
fmt.Printf(" --instance-id \"%s\"\n", nextInstId)
fmt.Println("=======================================================================================")
fmt.Println()
}()
} else {
fmt.Println()
o.printDetails()
fmt.Println("=======================================================================================")
}
select {
case <-ch:
fmt.Println("Signal to shutdown received")
case <-ctx.Done():
fmt.Println("Cancellation request received")
}
o.cleanupHome()
return nil
}
func (o *QuickstartOpts) printDetails() {
fmt.Println("=======================================================================================")
fmt.Println("controller and router started.")
fmt.Println(" controller located at : " + helpers.GetCtrlAdvertisedAddress() + ":" + strconv.Itoa(int(o.ControllerPort)))
fmt.Println(" router located at : " + helpers.GetRouterAdvertisedAddress() + ":" + strconv.Itoa(int(o.RouterPort)))
fmt.Println(" config dir located at : " + o.Home)
fmt.Println(" configured trust domain: " + o.TrustDomain)
fmt.Printf(" instance pid : %d\n", os.Getpid())
}
func (o *QuickstartOpts) configureRouter(routerName string, configFile string, ctrlUrl string) error {
if o.routerless {
return nil
}
if !o.AlreadyInitialized {
loginCmd := edge.NewLoginCmd(o.out, o.errOut)
loginCmd.SetArgs([]string{
ctrlUrl,
fmt.Sprintf("--username=%s", o.Username),
fmt.Sprintf("--password=%s", o.Password),
"-y",
})
if o.joinCommand {
o.waitForLeader()
}
loginErr := loginCmd.Execute()
if loginErr != nil {
logrus.Fatal(loginErr)
}
o.configureOverlay()
time.Sleep(1 * time.Second)
var erJwt string
// ziti edge create edge-router ${ZITI_HOSTNAME}-edge-router -o ${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.jwt -t -a public
createErCmd := edge.NewCreateEdgeRouterCmd(o.out, o.errOut)
erJwt = path.Join(o.Home, routerName+".jwt")
createErCmd.SetArgs([]string{
routerName,
fmt.Sprintf("--jwt-output-file=%s", erJwt),
"--tunneler-enabled",
fmt.Sprintf("--role-attributes=%s", "public"),
})
o.waitForLeader() //wait for a leader before doing anything
createErErr := createErCmd.Execute()
if createErErr != nil {
logrus.Fatal(createErErr)
}
// ziti create config router edge --routerName ${ZITI_HOSTNAME}-edge-router >${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.yaml
opts := &create.CreateConfigRouterOptions{}
data := &create.ConfigTemplateValues{}
data.PopulateConfigValues()
opts.IsHA = o.isHA
create.SetZitiRouterIdentity(&data.Router, routerName)
erCfg := create.NewCmdCreateConfigRouterEdge(opts, data)
erCfg.SetArgs([]string{
fmt.Sprintf("--routerName=%s", routerName),
fmt.Sprintf("--output=%s", configFile),
})
o.waitForLeader()
erCfgErr := erCfg.Execute()
if erCfgErr != nil {
logrus.Fatal(erCfgErr)
}
// ziti router enroll ${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.yaml --jwt ${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.jwt
erEnroll := enroll.NewEnrollEdgeRouterCmd()
erEnroll.SetArgs([]string{
configFile,
fmt.Sprintf("--jwt=%s", erJwt),
})
o.waitForLeader() //needed?
erEnrollErr := erEnroll.Execute()
if erEnrollErr != nil {
return erEnrollErr
}
}
return nil
}
func (o *QuickstartOpts) runRouter(configFile string) {
if o.routerless {
return
}
go func() {
// ziti router run ${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.yaml &> ${ZITI_HOME}/${ZITI_HOSTNAME}-edge-router.log &
erRunCmd := NewRunRouterCmd()
erRunCmd.SetArgs([]string{
configFile,
})
o.waitForLeader() //needed?
erRunCmdErr := erRunCmd.Execute()
if erRunCmdErr != nil {
logrus.Fatal(erRunCmdErr)
}
}()
}
func (o *QuickstartOpts) createMinimalPki() {
where := path.Join(o.Home, "pki")
fmt.Println("emitting a minimal PKI")
sid := fmt.Sprintf("spiffe://%s/controller/%s", o.TrustDomain, o.InstanceID)
//ziti pki create ca --pki-root="$pkiDir" --ca-file="root-ca" --ca-name="root-ca" --spiffe-id="whatever"
if o.joinCommand {
// indicates we are joining a cluster. don't emit a root-ca, expect it'll be there or error
} else {
rootCaPath := path.Join(where, "root-ca", "certs", "root-ca.cert")
rootCa, statErr := os.Stat(rootCaPath)
if statErr != nil {
if !os.IsNotExist(statErr) {
logrus.Warnf("could not check for root-ca: %v", statErr)
}
}
if rootCa == nil {
ca := pki.NewCmdPKICreateCA(o.out, o.errOut)
rootCaArgs := []string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-file=%s", "root-ca"),
fmt.Sprintf("--ca-name=%s", "root-ca"),
fmt.Sprintf("--trust-domain=%s", o.TrustDomain),
}
ca.SetArgs(rootCaArgs)
pkiErr := ca.Execute()
if pkiErr != nil {
logrus.Fatal(pkiErr)
}
} else {
logrus.Infof("%s exists and will be reused", rootCaPath)
}
}
//ziti pki create intermediate --pki-root "$pkiDir" --ca-name "root-ca" --intermediate-name "intermediate-ca" --intermediate-file "intermediate-ca" --max-path-len "1" --spiffe-id="whatever"
intermediate := pki.NewCmdPKICreateIntermediate(o.out, o.errOut)
intermediate.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", "root-ca"),
fmt.Sprintf("--intermediate-name=%s", o.scopedName("intermediate-ca")),
fmt.Sprintf("--intermediate-file=%s", o.scopedName("intermediate-ca")),
"--max-path-len=1",
})
intErr := intermediate.Execute()
if intErr != nil {
logrus.Fatal(intErr)
}
//ziti pki create server --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --server-name "server" --server-file "server" --dns "localhost,${ZITI_HOSTNAME}" --spiffe-id="whatever"
svr := pki.NewCmdPKICreateServer(o.out, o.errOut)
var ips = "127.0.0.1,::1"
ipOverride := os.Getenv("ZITI_CTRL_EDGE_IP_OVERRIDE")
if ipOverride != "" {
ips = ips + "," + ipOverride
}
args := []string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", o.scopedName("intermediate-ca")),
fmt.Sprintf("--server-name=%s", o.InstanceID),
fmt.Sprintf("--server-file=%s", o.scopedNameOff("server")),
fmt.Sprintf("--dns=%s,%s", "localhost", helpers.GetCtrlAdvertisedAddress()),
fmt.Sprintf("--ip=%s", ips),
fmt.Sprintf("--spiffe-id=%s", sid),
}
svr.SetArgs(args)
svrErr := svr.Execute()
if svrErr != nil {
logrus.Fatal(svrErr)
}
//ziti pki create client --pki-root="${ZITI_HOME}/pki" --ca-name "intermediate-ca" --client-name "client" --client-file "client" --key-file "server" --spiffe-id="whatever"
client := pki.NewCmdPKICreateClient(o.out, o.errOut)
client.SetArgs([]string{
fmt.Sprintf("--pki-root=%s", where),
fmt.Sprintf("--ca-name=%s", o.scopedName("intermediate-ca")),
fmt.Sprintf("--client-name=%s", o.InstanceID),
fmt.Sprintf("--client-file=%s", o.scopedNameOff("client")),
fmt.Sprintf("--key-file=%s", o.scopedNameOff("server")),
fmt.Sprintf("--spiffe-id=%s", sid),
})
clientErr := client.Execute()
if clientErr != nil {
logrus.Fatal(clientErr)
}
}
func waitForController(ctrlUrl string, done chan error) {
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{Transport: tr}
for {
r, e := client.Get(ctrlUrl)
if e != nil || r == nil || r.StatusCode != 200 {
time.Sleep(50 * time.Millisecond)
} else {
break
}
}
done <- nil
}
func waitForRouter(address string, port uint16, done chan struct{}) {
for {
addr := fmt.Sprintf("%s:%d", address, port)
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err == nil {
_ = conn.Close()
fmt.Printf("Router is available on %s:%d\n", address, port)
close(done)
return
}
time.Sleep(25 * time.Millisecond)
}
}
func (o *QuickstartOpts) scopedNameOff(name string) string {
return name
}
func (o *QuickstartOpts) scopedName(name string) string {
if o.InstanceID != "" {
return name + "-" + o.InstanceID
} else {
return name
}
}
func (o *QuickstartOpts) instHome() string {
if o.isHA {
return path.Join(o.Home, o.InstanceID)
}
return o.Home
}
func (o *QuickstartOpts) configureOverlay() {
if o.joinCommand {
return
}
// Allow all identities to use any edge router with the "public" attribute
// ziti edge create edge-router-policy all-endpoints-public-routers --edge-router-roles "#public" --identity-roles "#all"
erpCmd := edge.NewCreateEdgeRouterPolicyCmd(o.out, o.errOut)
erpCmd.SetArgs([]string{
"all-endpoints-public-routers",
fmt.Sprintf("--edge-router-roles=%s", "#public"),
fmt.Sprintf("--identity-roles=%s", "#all"),
})
erpCmdErr := erpCmd.Execute()
if erpCmdErr != nil {
logrus.Fatal(erpCmdErr)
}
// # Allow all edge-routers to access all services
// ziti edge create service-edge-router-policy all-routers-all-services --edge-router-roles "#all" --service-roles "#all"
serpCmd := edge.NewCreateServiceEdgeRouterPolicyCmd(o.out, o.errOut)
serpCmd.SetArgs([]string{
"all-routers-all-services",
fmt.Sprintf("--edge-router-roles=%s", "#all"),
fmt.Sprintf("--service-roles=%s", "#all"),
})
o.waitForLeader()
serpCmdErr := serpCmd.Execute()
if serpCmdErr != nil {
logrus.Fatal(serpCmdErr)
}
}
type raftListMembersAction struct {
api.Options
}
func (o *QuickstartOpts) waitForLeader() bool {
for {
p := common.NewOptionsProvider(o.out, o.errOut)
action := &raftListMembersAction{
Options: api.Options{CommonOptions: p()},
}
client, err := util.NewFabricManagementClient(action)
if err != nil {
return false
}
members, err := client.Cluster.ClusterListMembers(&cluster.ClusterListMembersParams{
Context: context.Background(),
})
if err != nil {
return false
}
for _, m := range members.Payload.Data {
if m.Leader != nil && *m.Leader {
time.Sleep(500 * time.Millisecond) // this just gives time for the leader to 'settle' -- shouldn't be necessary
return true
}
}
time.Sleep(50 * time.Millisecond)
}
}
func incrementStringSuffix(input string) string {
// Regular expression to capture the numeric suffix
re := regexp.MustCompile(`(\d+)$`)
match := re.FindStringSubmatch(input)
if len(match) == 0 {
return uuid.New().String()
}
numStr := match[1]
numLength := len(numStr)
num, _ := strconv.Atoi(numStr)
num++
incremented := fmt.Sprintf("%0*d", numLength, num)
return strings.TrimSuffix(input, numStr) + incremented
}