<commit_before>package unpuzzled

import (
	"testing"

	"os"

	"github.com/stretchr/testify/assert"
)

type argumentAssignemntTest struct {
	Name       string
	Command    *Command
	Arguments  []string
	Validation func(*testing.T, *Command)
}

// Test the general split of arguments between general flags, and nested subcommands.
// Each command should only get the set of arguments between the current command and the next nested subcommand.
func TestArgumentAssignment(t *testing.T) {
	tests := []argumentAssignemntTest{
		argumentAssignemntTest{
			Name: "Basic argument assignment, no subcommands.",
			Command: &Command{
				Name: "main",
			},
			Arguments: []string{"--a=5", "--b", "--c"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"--a=5", "--b", "--c"}, c.args, "Single command should get all the arguments.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple subcommands, call middle command.",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "server",
						Subcommands: []*Command{
							&Command{
								Name: "metrics",
							},
						},
					},
				},
			},
			Arguments: []string{"server", "--random-value=5", "--another-value=b"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"--random-value=5", "--another-value=b"}, c.Subcommands[0].args, "Middle server command should get all the arguments.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple subcommands",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "sub-a",
					},
					&Command{
						Name: "sub-b",
					},
				},
			},
			Arguments: []string{"main-a", "main-b", "sub-a", "--a", "--b", "--c"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"main-a", "main-b"}, c.args, "Main command should get first two args.")
				assert.Equal(t, []string{"--a", "--b", "--c"}, c.Subcommands[0].args, "sub-a command should get the remaining args.")
				assert.Nil(t, c.Subcommands[1].args, "sub-b command should receive no arguments.")

				assert.Equal(t, true, c.Subcommands[0].Active, "sub-a should be marked as active.")
				assert.Equal(t, false, c.Subcommands[1].Active, "sub-b should be marked as inactive.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple Nested Commands",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "sub-a",
						Subcommands: []*Command{
							&Command{
								Name: "sub-b",
								Subcommands: []*Command{
									&Command{
										Name: "sub-b",
									},
								},
							},
						},
					},
				},
			},
			Arguments: []string{"main-a", "sub-a", "--a", "--b", "--c", "sub-b", "--d", "--e", "--f", "sub-b", "--g", "--h"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"main-a"}, c.args, "Main command should get first two args.")
				assert.Equal(t, []string{"--a", "--b", "--c"}, c.Subcommands[0].args, "sub-a command should get the remaining args.")
				assert.Equal(t, []string{"--d", "--e", "--f"}, c.Subcommands[0].Subcommands[0].args, "sub-b command should get the remaining args.")
				assert.Equal(t, []string{"--g", "--h"}, c.Subcommands[0].Subcommands[0].Subcommands[0].args, "sub-c command should get the remaining args.")
			},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			test.Command.assignArguments(test.Arguments)
			test.Validation(t, test.Command)
		})
	}
}

type testExpandedName struct {
	Command       *Command
	ExpectedNames []string
	Name          string
}

// Internally used to bring commands / subcommands into a single namespace.
// Test to make sure these names are properly generated, should be chained together with "."'s.
func TestGetExpandedName(t *testing.T) {
	tests := []testExpandedName{
		testExpandedName{
			Name: "nested",
			Command: &Command{
				Name: "first",
				Subcommands: []*Command{
					&Command{
						Name: "second",
					},
					&Command{
						Name: "third",
					},
				},
			},
			ExpectedNames: []string{
				"first",
				"first.second",
				"first.third",
			},
		},
		testExpandedName{
			Name: "nested",
			Command: &Command{
				Name: "first",
				Subcommands: []*Command{
					&Command{
						Name: "second",
						Subcommands: []*Command{
							&Command{
								Name: "third",
							},
						},
					},
				},
			},
			ExpectedNames: []string{
				"first",
				"first.second",
				"first.second.third",
			},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			test.Command.buildTree(nil)
			var names []string
			loopCommands(test.Command, func(c *Command) {
				names = append(names, c.GetExpandedName())
			}, true)
			assert.Equal(t, test.ExpectedNames, names, "expanded names are not equal.")
		})
	}
}

func loopCommands(command *Command, fn func(*Command), isFirst bool) {
	if isFirst {
		fn(command)
	}
	for _, subcommand := range command.Subcommands {
		fn(subcommand)
		loopCommands(subcommand, fn, false)
	}
}

type testEnvironmentVariables struct {
	Name      string
	Command   *Command
	StrValue  string
	BoolValue bool
	EnvVars   []envVar
}
type envVar struct {
	Key   string
	Value string
}

func TestEnvironmentVariables(t *testing.T) {
	var testString string
	var testBool bool

	tests := []testEnvironmentVariables{
		testEnvironmentVariables{
			Name: "strings and bools",
			Command: &Command{
				Name: "envTest",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
			},
			StrValue:  "a",
			BoolValue: true,
			EnvVars: []envVar{
				envVar{"TEST_VALUE", "a"},
				envVar{"TEST_BOOL", "true"},
			},
		},
		testEnvironmentVariables{
			Name: "ensure false bools",
			Command: &Command{
				Name: "envTest",
				Variables: []Variable{
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
				},
			},
			BoolValue: false,
			StrValue:  "b",
			EnvVars: []envVar{
				envVar{"TEST_VALUE", "b"},
				envVar{"TEST_BOOL", "false"},
			},
		},
	}
	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			for _, envVar := range test.EnvVars {
				os.Setenv(envVar.Key, envVar.Value)
			}

			app := NewApp()
			app.Command = test.Command
			app.args = []string{}
			app.parseCommands()

			assert.Equal(t, test.StrValue, testString, "test string should be the same.")
			assert.Equal(t, test.BoolValue, testBool, "test bool should be the same.")

			for _, envVar := range test.EnvVars {
				assert.NoError(t, os.Unsetenv(envVar.Key), "Should not error while unsetting the env var.")
			}
		})
	}
}

type testCLIFlags struct {
	Name      string
	Command   *Command
	StrValue  string
	BoolValue bool
	Args      []string
}

func TestCLIFlags(t *testing.T) {
	var testString string
	var testBool bool

	tests := []testCLIFlags{
		testCLIFlags{
			Name: "basic",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
			},
			StrValue:  "random",
			BoolValue: true,
			Args:      []string{"path_to_exec", "--test-value=random", "--test-bool=true"},
		},
		testCLIFlags{
			Name: "subommand",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
				Subcommands: []*Command{
					&Command{
						Name: "nested",
						Variables: []Variable{
							&StringVariable{
								Name:        "test-value",
								Destination: &testString,
							},
							&BoolVariable{
								Name:        "test-bool",
								Destination: &testBool,
							},
						},
					},
				},
			},
			StrValue:  "used",
			BoolValue: true,
			Args:      []string{"path_to_exec", "--test-value=ignored", "--test-bool=true", "nested", "--test-value=used"},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			app := NewApp()
			app.Command = test.Command
			app.Run(test.Args)

			assert.Equal(t, test.StrValue, testString, "test string should be the same.")
			assert.Equal(t, test.BoolValue, testBool, "test bool should be the same.")
		})
	}
}

type testLoopActiveCommands struct {
	Name     string
	Command  *Command
	Args     []string
	Expected string
}

// based on the arguments given, test that the loop function actually calls
// all the active commands, and not the nonactive ones.
func TestLoopActiveCommands(t *testing.T) {
	tests := []testLoopActiveCommands{
		testLoopActiveCommands{
			Name: "Single nested",
			Command: &Command{
				Name: "basic",
				Subcommands: []*Command{
					&Command{
						Name: "a",
					},
					&Command{
						Name: "b",
					},
					&Command{
						Name: "c",
					},
				},
			},
			Args:     []string{"basic", "a"},
			Expected: "basic.a.",
		},
		testLoopActiveCommands{
			Name: "Multi nested",
			Command: &Command{
				Name: "basic",
				Subcommands: []*Command{
					&Command{
						Name: "a",
						Subcommands: []*Command{
							&Command{
								Name: "a",
								Subcommands: []*Command{
									&Command{
										Name: "a",
									},
									&Command{
										Name: "b",
									},
								},
							},
							&Command{
								Name: "b",
							},
						},
					},
					&Command{
						Name: "b",
					},
				},
			},
			Args:     []string{"basic", "a", "a", "a"},
			Expected: "basic.a.a.a.",
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			app := NewApp()
			app.Command = test.Command
			app.args = test.Args
			app.parseCommands()
			outString := ""
			app.Command.loopActiveCommands(func(c *Command) {
				outString += c.Name + "."
			})
			assert.Equal(t, test.Expected, outString, "Active command loop names should be equal.")
		})
	}
}
<commit_msg>add a test for default values<commit_after>package unpuzzled

import (
	"testing"

	"os"

	"github.com/stretchr/testify/assert"
)

type argumentAssignemntTest struct {
	Name       string
	Command    *Command
	Arguments  []string
	Validation func(*testing.T, *Command)
}

// Test the general split of arguments between general flags, and nested subcommands.
// Each command should only get the set of arguments between the current command and the next nested subcommand.
func TestArgumentAssignment(t *testing.T) {
	tests := []argumentAssignemntTest{
		argumentAssignemntTest{
			Name: "Basic argument assignment, no subcommands.",
			Command: &Command{
				Name: "main",
			},
			Arguments: []string{"--a=5", "--b", "--c"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"--a=5", "--b", "--c"}, c.args, "Single command should get all the arguments.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple subcommands, call middle command.",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "server",
						Subcommands: []*Command{
							&Command{
								Name: "metrics",
							},
						},
					},
				},
			},
			Arguments: []string{"server", "--random-value=5", "--another-value=b"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"--random-value=5", "--another-value=b"}, c.Subcommands[0].args, "Middle server command should get all the arguments.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple subcommands",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "sub-a",
					},
					&Command{
						Name: "sub-b",
					},
				},
			},
			Arguments: []string{"main-a", "main-b", "sub-a", "--a", "--b", "--c"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"main-a", "main-b"}, c.args, "Main command should get first two args.")
				assert.Equal(t, []string{"--a", "--b", "--c"}, c.Subcommands[0].args, "sub-a command should get the remaining args.")
				assert.Nil(t, c.Subcommands[1].args, "sub-b command should receive no arguments.")

				assert.Equal(t, true, c.Subcommands[0].Active, "sub-a should be marked as active.")
				assert.Equal(t, false, c.Subcommands[1].Active, "sub-b should be marked as inactive.")
			},
		},
		argumentAssignemntTest{
			Name: "Multiple Nested Commands",
			Command: &Command{
				Name: "main",
				Subcommands: []*Command{
					&Command{
						Name: "sub-a",
						Subcommands: []*Command{
							&Command{
								Name: "sub-b",
								Subcommands: []*Command{
									&Command{
										Name: "sub-b",
									},
								},
							},
						},
					},
				},
			},
			Arguments: []string{"main-a", "sub-a", "--a", "--b", "--c", "sub-b", "--d", "--e", "--f", "sub-b", "--g", "--h"},
			Validation: func(t *testing.T, c *Command) {
				assert.Equal(t, []string{"main-a"}, c.args, "Main command should get first two args.")
				assert.Equal(t, []string{"--a", "--b", "--c"}, c.Subcommands[0].args, "sub-a command should get the remaining args.")
				assert.Equal(t, []string{"--d", "--e", "--f"}, c.Subcommands[0].Subcommands[0].args, "sub-b command should get the remaining args.")
				assert.Equal(t, []string{"--g", "--h"}, c.Subcommands[0].Subcommands[0].Subcommands[0].args, "sub-c command should get the remaining args.")
			},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			test.Command.assignArguments(test.Arguments)
			test.Validation(t, test.Command)
		})
	}
}

type testExpandedName struct {
	Command       *Command
	ExpectedNames []string
	Name          string
}

// Internally used to bring commands / subcommands into a single namespace.
// Test to make sure these names are properly generated, should be chained together with "."'s.
func TestGetExpandedName(t *testing.T) {
	tests := []testExpandedName{
		testExpandedName{
			Name: "nested",
			Command: &Command{
				Name: "first",
				Subcommands: []*Command{
					&Command{
						Name: "second",
					},
					&Command{
						Name: "third",
					},
				},
			},
			ExpectedNames: []string{
				"first",
				"first.second",
				"first.third",
			},
		},
		testExpandedName{
			Name: "nested",
			Command: &Command{
				Name: "first",
				Subcommands: []*Command{
					&Command{
						Name: "second",
						Subcommands: []*Command{
							&Command{
								Name: "third",
							},
						},
					},
				},
			},
			ExpectedNames: []string{
				"first",
				"first.second",
				"first.second.third",
			},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			test.Command.buildTree(nil)
			var names []string
			loopCommands(test.Command, func(c *Command) {
				names = append(names, c.GetExpandedName())
			}, true)
			assert.Equal(t, test.ExpectedNames, names, "expanded names are not equal.")
		})
	}
}

func loopCommands(command *Command, fn func(*Command), isFirst bool) {
	if isFirst {
		fn(command)
	}
	for _, subcommand := range command.Subcommands {
		fn(subcommand)
		loopCommands(subcommand, fn, false)
	}
}

type testEnvironmentVariables struct {
	Name      string
	Command   *Command
	StrValue  string
	BoolValue bool
	EnvVars   []envVar
}
type envVar struct {
	Key   string
	Value string
}

func TestEnvironmentVariables(t *testing.T) {
	var testString string
	var testBool bool

	tests := []testEnvironmentVariables{
		testEnvironmentVariables{
			Name: "strings and bools",
			Command: &Command{
				Name: "envTest",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
			},
			StrValue:  "a",
			BoolValue: true,
			EnvVars: []envVar{
				envVar{"TEST_VALUE", "a"},
				envVar{"TEST_BOOL", "true"},
			},
		},
		testEnvironmentVariables{
			Name: "ensure false bools",
			Command: &Command{
				Name: "envTest",
				Variables: []Variable{
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
				},
			},
			BoolValue: false,
			StrValue:  "b",
			EnvVars: []envVar{
				envVar{"TEST_VALUE", "b"},
				envVar{"TEST_BOOL", "false"},
			},
		},
	}
	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			for _, envVar := range test.EnvVars {
				os.Setenv(envVar.Key, envVar.Value)
			}

			app := NewApp()
			app.Command = test.Command
			app.args = []string{}
			app.parseCommands()

			assert.Equal(t, test.StrValue, testString, "test string should be the same.")
			assert.Equal(t, test.BoolValue, testBool, "test bool should be the same.")

			for _, envVar := range test.EnvVars {
				assert.NoError(t, os.Unsetenv(envVar.Key), "Should not error while unsetting the env var.")
			}
		})
	}
}

type testCLIFlags struct {
	Name      string
	Command   *Command
	StrValue  string
	BoolValue bool
	Args      []string
}

func TestCLIFlags(t *testing.T) {
	var testString string
	var testBool bool

	tests := []testCLIFlags{
		testCLIFlags{
			Name: "basic",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
			},
			StrValue:  "random",
			BoolValue: true,
			Args:      []string{"path_to_exec", "--test-value=random", "--test-bool=true"},
		},
		testCLIFlags{
			Name: "subommand",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
					},
				},
				Subcommands: []*Command{
					&Command{
						Name: "nested",
						Variables: []Variable{
							&StringVariable{
								Name:        "test-value",
								Destination: &testString,
							},
							&BoolVariable{
								Name:        "test-bool",
								Destination: &testBool,
							},
						},
					},
				},
			},
			StrValue:  "used",
			BoolValue: true,
			Args:      []string{"path_to_exec", "--test-value=ignored", "--test-bool=true", "nested", "--test-value=used"},
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			app := NewApp()
			app.Command = test.Command
			app.Run(test.Args)

			assert.Equal(t, test.StrValue, testString, "test string should be the same.")
			assert.Equal(t, test.BoolValue, testBool, "test bool should be the same.")
		})
	}
}

type testLoopActiveCommands struct {
	Name     string
	Command  *Command
	Args     []string
	Expected string
}

// based on the arguments given, test that the loop function actually calls
// all the active commands, and not the nonactive ones.
func TestLoopActiveCommands(t *testing.T) {
	tests := []testLoopActiveCommands{
		testLoopActiveCommands{
			Name: "Single nested",
			Command: &Command{
				Name: "basic",
				Subcommands: []*Command{
					&Command{
						Name: "a",
					},
					&Command{
						Name: "b",
					},
					&Command{
						Name: "c",
					},
				},
			},
			Args:     []string{"basic", "a"},
			Expected: "basic.a.",
		},
		testLoopActiveCommands{
			Name: "Multi nested",
			Command: &Command{
				Name: "basic",
				Subcommands: []*Command{
					&Command{
						Name: "a",
						Subcommands: []*Command{
							&Command{
								Name: "a",
								Subcommands: []*Command{
									&Command{
										Name: "a",
									},
									&Command{
										Name: "b",
									},
								},
							},
							&Command{
								Name: "b",
							},
						},
					},
					&Command{
						Name: "b",
					},
				},
			},
			Args:     []string{"basic", "a", "a", "a"},
			Expected: "basic.a.a.a.",
		},
	}

	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			app := NewApp()
			app.Command = test.Command
			app.args = test.Args
			app.parseCommands()
			outString := ""
			app.Command.loopActiveCommands(func(c *Command) {
				outString += c.Name + "."
			})
			assert.Equal(t, test.Expected, outString, "Active command loop names should be equal.")
		})
	}
}

type testDefaultValues struct {
	Name           string
	Command        *Command
	Args           []string
	ExpectedString string
	ExpectedBool   bool
}

func TestDefaultValues(t *testing.T) {
	var testString string
	var testBool bool
	tests := []testDefaultValues{
		testDefaultValues{
			Name: "Basic default test.",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
						Default:     "default-value",
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
						Default:     true,
					},
				},
			},
			Args:           []string{},
			ExpectedBool:   true,
			ExpectedString: "default-value",
		},
		testDefaultValues{
			Name: "Ensure defaults are overridden.",
			Command: &Command{
				Name: "basic",
				Variables: []Variable{
					&StringVariable{
						Name:        "test-value",
						Destination: &testString,
						Default:     "default-value",
					},
					&BoolVariable{
						Name:        "test-bool",
						Destination: &testBool,
						Default:     true,
					},
				},
			},
			Args:           []string{"--test-value=flag-value", "--test-bool=false"},
			ExpectedBool:   false,
			ExpectedString: "flag-value",
		},
	}
	for _, test := range tests {
		t.Run(test.Name, func(t *testing.T) {
			app := NewApp()
			app.Command = test.Command
			app.args = test.Args
			app.parseCommands()
			app.printOverrides()
			assert.Equal(t, test.ExpectedString, testString, "Default value for string must be equal.")
			assert.Equal(t, test.ExpectedBool, testBool, "Default value for string must be equal.")
		})
	}
}
<|endoftext|>


<commit_before>package planbuilder

import (
	"vitess.io/vitess/go/mysql"
	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/vindexes"

	"vitess.io/vitess/go/vt/key"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/engine"
)

// Error messages for CreateView queries
const (
	ViewDifferentKeyspace string = "Select query does not belong to the same keyspace as the view statement"
	ViewComplex           string = "Complex select queries are not supported in create or alter view statements"
	DifferentDestinations string = "Tables or Views specified in the query do not belong to the same destination"
)

// buildGeneralDDLPlan builds a general DDL plan, which can be either normal DDL or online DDL.
// The two behave compeltely differently, and have two very different primitives.
// We want to be able to dynamically choose between normal/online plans according to Session settings.
// However, due to caching of plans, we're unable to make that choice right now. In this function we don't have
// a session context. It's only when we Execute() the primitive that we have that context.
// This is why we return a compound primitive (DDL) which contains fully populated primitives (Send & OnlineDDL),
// and which chooses which of the two to invoke at runtime.
func buildGeneralDDLPlan(sql string, ddlStatement sqlparser.DDLStatement, vschema ContextVSchema) (engine.Primitive, error) {
	if ddlStatement.IsTemporary() {
		return buildTempTablePlan(sql, ddlStatement, vschema)
	}
	normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(sql, ddlStatement, vschema)
	if err != nil {
		return nil, err
	}

	return &engine.DDL{
		Keyspace:  normalDDLPlan.Keyspace,
		SQL:       normalDDLPlan.Query,
		DDL:       ddlStatement,
		NormalDDL: normalDDLPlan,
		OnlineDDL: onlineDDLPlan,
	}, nil
}

func buildTempTablePlan(sql string, stmt sqlparser.DDLStatement, vschema ContextVSchema) (engine.Primitive, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error

	switch stmt.(type) {
	case *sqlparser.CreateTable:
		destination, keyspace, _, err = vschema.TargetDestination(stmt.GetTable().Qualifier.String())
		// Remove the keyspace name as the database name might be different.
		stmt.SetTable("", stmt.GetTable().Name.String())
		if err != nil {
			return nil, err
		}
	case *sqlparser.DropTable:
		destination, keyspace, err = buildDropViewOrTable(vschema, stmt)
		if err != nil {
			return nil, err
		}
	default:
		return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: Temporary table not planned for %s", sql)
	}
	if keyspace.Sharded {
		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Temporary table not supported in sharded keyspace: %s", keyspace.Name)
	}
	query := sql
	// If the query is fully parsed, generate the query from the ast. Otherwise, use the original query
	if stmt.IsFullyParsed() {
		query = sqlparser.String(stmt)
	}

	return &engine.Send{
		Keyspace:          keyspace,
		TargetDestination: destination,
		Query:             query,
		ReservedConn:      true,
	}, nil
}

func buildDDLPlans(sql string, ddlStatement sqlparser.DDLStatement, vschema ContextVSchema) (*engine.Send, *engine.OnlineDDL, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error

	switch ddl := ddlStatement.(type) {
	case *sqlparser.AlterTable, *sqlparser.TruncateTable:
		// For Alter Table and other statements, the table must already exist
		// We should find the target of the query from this tables location
		destination, keyspace, err = findTableDestinationAndKeyspace(vschema, ddlStatement)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.CreateView:
		destination, keyspace, err = buildCreateView(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.AlterView:
		destination, keyspace, err = buildAlterView(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.CreateTable:
		destination, keyspace, _, err = vschema.TargetDestination(ddlStatement.GetTable().Qualifier.String())
		// Remove the keyspace name as the database name might be different.
		ddlStatement.SetTable("", ddlStatement.GetTable().Name.String())
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.DropView, *sqlparser.DropTable:
		destination, keyspace, err = buildDropViewOrTable(vschema, ddlStatement)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.RenameTable:
		destination, keyspace, err = buildRenameTable(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}

	default:
		return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: unexpected statement type: %T", ddlStatement)
	}

	if destination == nil {
		destination = key.DestinationAllShards{}
	}

	query := sql
	// If the query is fully parsed, generate the query from the ast. Otherwise, use the original query
	if ddlStatement.IsFullyParsed() {
		query = sqlparser.String(ddlStatement)
	}

	return &engine.Send{
			Keyspace:          keyspace,
			TargetDestination: destination,
			Query:             query,
			IsDML:             false,
			SingleShardOnly:   false,
		}, &engine.OnlineDDL{
			Keyspace: keyspace,
			DDL:      ddlStatement,
			SQL:      query,
		}, nil
}

func findTableDestinationAndKeyspace(vschema ContextVSchema, ddlStatement sqlparser.DDLStatement) (key.Destination, *vindexes.Keyspace, error) {
	var table *vindexes.Table
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error
	table, _, _, _, destination, err = vschema.FindTableOrVindex(ddlStatement.GetTable())
	if err != nil {
		_, isNotFound := err.(vindexes.NotFoundError)
		if !isNotFound {
			return nil, nil, err
		}
	}
	if table == nil {
		destination, keyspace, _, err = vschema.TargetDestination(ddlStatement.GetTable().Qualifier.String())
		if err != nil {
			return nil, nil, err
		}
		ddlStatement.SetTable("", ddlStatement.GetTable().Name.String())
	} else {
		keyspace = table.Keyspace
		ddlStatement.SetTable("", table.Name.String())
	}
	return destination, keyspace, nil
}

func buildAlterView(vschema ContextVSchema, ddl *sqlparser.AlterView) (key.Destination, *vindexes.Keyspace, error) {
	// For Alter View, we require that the view exist and the select query can be satisfied within the keyspace itself
	// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
	destination, keyspace, err := findTableDestinationAndKeyspace(vschema, ddl)
	if err != nil {
		return nil, nil, err
	}

	var selectPlan engine.Primitive
	selectPlan, err = createInstructionFor(sqlparser.String(ddl.Select), ddl.Select, vschema)
	if err != nil {
		return nil, nil, err
	}
	routePlan, isRoute := selectPlan.(*engine.Route)
	if !isRoute {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	if keyspace.Name != routePlan.GetKeyspaceName() {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewDifferentKeyspace)
	}
	if routePlan.Opcode != engine.SelectUnsharded && routePlan.Opcode != engine.SelectEqualUnique && routePlan.Opcode != engine.SelectScatter {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	sqlparser.Rewrite(ddl.Select, func(cursor *sqlparser.Cursor) bool {
		switch tableName := cursor.Node().(type) {
		case sqlparser.TableName:
			cursor.Replace(sqlparser.TableName{
				Name: tableName.Name,
			})
		}
		return true
	}, nil)
	return destination, keyspace, nil
}

func buildCreateView(vschema ContextVSchema, ddl *sqlparser.CreateView) (key.Destination, *vindexes.Keyspace, error) {
	// For Create View, we require that the keyspace exist and the select query can be satisfied within the keyspace itself
	// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
	destination, keyspace, _, err := vschema.TargetDestination(ddl.ViewName.Qualifier.String())
	if err != nil {
		return nil, nil, err
	}
	ddl.ViewName.Qualifier = sqlparser.NewTableIdent("")

	var selectPlan engine.Primitive
	selectPlan, err = createInstructionFor(sqlparser.String(ddl.Select), ddl.Select, vschema)
	if err != nil {
		return nil, nil, err
	}
	routePlan, isRoute := selectPlan.(*engine.Route)
	if !isRoute {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	if keyspace.Name != routePlan.GetKeyspaceName() {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewDifferentKeyspace)
	}
	if routePlan.Opcode != engine.SelectUnsharded && routePlan.Opcode != engine.SelectEqualUnique && routePlan.Opcode != engine.SelectScatter {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	sqlparser.Rewrite(ddl.Select, func(cursor *sqlparser.Cursor) bool {
		switch tableName := cursor.Node().(type) {
		case sqlparser.TableName:
			cursor.Replace(sqlparser.TableName{
				Name: tableName.Name,
			})
		}
		return true
	}, nil)
	return destination, keyspace, nil
}

func buildDropViewOrTable(vschema ContextVSchema, ddlStatement sqlparser.DDLStatement) (key.Destination, *vindexes.Keyspace, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	for i, tab := range ddlStatement.GetFromTables() {
		var destinationTab key.Destination
		var keyspaceTab *vindexes.Keyspace
		var table *vindexes.Table
		var err error
		table, _, _, _, destinationTab, err = vschema.FindTableOrVindex(tab)

		if err != nil {
			_, isNotFound := err.(vindexes.NotFoundError)
			if !isNotFound {
				return nil, nil, err
			}
		}
		if table == nil {
			destinationTab, keyspaceTab, _, err = vschema.TargetDestination(tab.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			ddlStatement.GetFromTables()[i] = sqlparser.TableName{
				Name: tab.Name,
			}
		} else {
			keyspaceTab = table.Keyspace
			ddlStatement.GetFromTables()[i] = sqlparser.TableName{
				Name: table.Name,
			}
		}

		if destination == nil && keyspace == nil {
			destination = destinationTab
			keyspace = keyspaceTab
		}
		if destination != destinationTab || keyspace != keyspaceTab {
			return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, DifferentDestinations)
		}
	}
	return destination, keyspace, nil
}

func buildRenameTable(vschema ContextVSchema, renameTable *sqlparser.RenameTable) (key.Destination, *vindexes.Keyspace, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace

	for _, tabPair := range renameTable.TablePairs {
		var destinationFrom key.Destination
		var keyspaceFrom *vindexes.Keyspace
		var table *vindexes.Table
		var err error
		table, _, _, _, destinationFrom, err = vschema.FindTableOrVindex(tabPair.FromTable)

		if err != nil {
			_, isNotFound := err.(vindexes.NotFoundError)
			if !isNotFound {
				return nil, nil, err
			}
		}
		if table == nil {
			destinationFrom, keyspaceFrom, _, err = vschema.TargetDestination(tabPair.FromTable.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			tabPair.FromTable = sqlparser.TableName{
				Name: tabPair.FromTable.Name,
			}
		} else {
			keyspaceFrom = table.Keyspace
			tabPair.FromTable = sqlparser.TableName{
				Name: table.Name,
			}
		}

		if tabPair.ToTable.Qualifier.String() != "" {
			_, keyspaceTo, _, err := vschema.TargetDestination(tabPair.ToTable.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			if keyspaceTo.Name != keyspaceFrom.Name {
				return nil, nil, mysql.NewSQLError(1450, mysql.SSUnknownSQLState, "Changing schema from '%s' to '%s' is not allowed", keyspaceFrom.Name, keyspaceTo.Name)
			}
			tabPair.ToTable = sqlparser.TableName{
				Name: tabPair.ToTable.Name,
			}
		}

		if destination == nil && keyspace == nil {
			destination = destinationFrom
			keyspace = keyspaceFrom
		}
		if destination != destinationFrom || keyspace != keyspaceFrom {
			return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, DifferentDestinations)
		}
	}
	return destination, keyspace, nil
}
<commit_msg>fix destination on temp table plan<commit_after>package planbuilder

import (
	"vitess.io/vitess/go/mysql"
	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vtgate/vindexes"

	"vitess.io/vitess/go/vt/key"
	"vitess.io/vitess/go/vt/sqlparser"
	"vitess.io/vitess/go/vt/vtgate/engine"
)

// Error messages for CreateView queries
const (
	ViewDifferentKeyspace string = "Select query does not belong to the same keyspace as the view statement"
	ViewComplex           string = "Complex select queries are not supported in create or alter view statements"
	DifferentDestinations string = "Tables or Views specified in the query do not belong to the same destination"
)

// buildGeneralDDLPlan builds a general DDL plan, which can be either normal DDL or online DDL.
// The two behave compeltely differently, and have two very different primitives.
// We want to be able to dynamically choose between normal/online plans according to Session settings.
// However, due to caching of plans, we're unable to make that choice right now. In this function we don't have
// a session context. It's only when we Execute() the primitive that we have that context.
// This is why we return a compound primitive (DDL) which contains fully populated primitives (Send & OnlineDDL),
// and which chooses which of the two to invoke at runtime.
func buildGeneralDDLPlan(sql string, ddlStatement sqlparser.DDLStatement, vschema ContextVSchema) (engine.Primitive, error) {
	if ddlStatement.IsTemporary() {
		return buildTempTablePlan(sql, ddlStatement, vschema)
	}
	normalDDLPlan, onlineDDLPlan, err := buildDDLPlans(sql, ddlStatement, vschema)
	if err != nil {
		return nil, err
	}

	return &engine.DDL{
		Keyspace:  normalDDLPlan.Keyspace,
		SQL:       normalDDLPlan.Query,
		DDL:       ddlStatement,
		NormalDDL: normalDDLPlan,
		OnlineDDL: onlineDDLPlan,
	}, nil
}

func buildTempTablePlan(sql string, stmt sqlparser.DDLStatement, vschema ContextVSchema) (engine.Primitive, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error

	switch stmt.(type) {
	case *sqlparser.CreateTable:
		destination, keyspace, _, err = vschema.TargetDestination(stmt.GetTable().Qualifier.String())
		// Remove the keyspace name as the database name might be different.
		stmt.SetTable("", stmt.GetTable().Name.String())
		if err != nil {
			return nil, err
		}
	case *sqlparser.DropTable:
		destination, keyspace, err = buildDropViewOrTable(vschema, stmt)
		if err != nil {
			return nil, err
		}
	default:
		return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: Temporary table not planned for %s", sql)
	}

	if keyspace.Sharded {
		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Temporary table not supported in sharded keyspace: %s", keyspace.Name)
	}

	if destination == nil {
		destination = key.DestinationAllShards{}
	}

	query := sql
	// If the query is fully parsed, generate the query from the ast. Otherwise, use the original query
	if stmt.IsFullyParsed() {
		query = sqlparser.String(stmt)
	}

	return &engine.Send{
		Keyspace:          keyspace,
		TargetDestination: destination,
		Query:             query,
		ReservedConn:      true,
	}, nil
}

func buildDDLPlans(sql string, ddlStatement sqlparser.DDLStatement, vschema ContextVSchema) (*engine.Send, *engine.OnlineDDL, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error

	switch ddl := ddlStatement.(type) {
	case *sqlparser.AlterTable, *sqlparser.TruncateTable:
		// For Alter Table and other statements, the table must already exist
		// We should find the target of the query from this tables location
		destination, keyspace, err = findTableDestinationAndKeyspace(vschema, ddlStatement)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.CreateView:
		destination, keyspace, err = buildCreateView(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.AlterView:
		destination, keyspace, err = buildAlterView(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.CreateTable:
		destination, keyspace, _, err = vschema.TargetDestination(ddlStatement.GetTable().Qualifier.String())
		// Remove the keyspace name as the database name might be different.
		ddlStatement.SetTable("", ddlStatement.GetTable().Name.String())
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.DropView, *sqlparser.DropTable:
		destination, keyspace, err = buildDropViewOrTable(vschema, ddlStatement)
		if err != nil {
			return nil, nil, err
		}
	case *sqlparser.RenameTable:
		destination, keyspace, err = buildRenameTable(vschema, ddl)
		if err != nil {
			return nil, nil, err
		}

	default:
		return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: unexpected statement type: %T", ddlStatement)
	}

	if destination == nil {
		destination = key.DestinationAllShards{}
	}

	query := sql
	// If the query is fully parsed, generate the query from the ast. Otherwise, use the original query
	if ddlStatement.IsFullyParsed() {
		query = sqlparser.String(ddlStatement)
	}

	return &engine.Send{
			Keyspace:          keyspace,
			TargetDestination: destination,
			Query:             query,
			IsDML:             false,
			SingleShardOnly:   false,
		}, &engine.OnlineDDL{
			Keyspace: keyspace,
			DDL:      ddlStatement,
			SQL:      query,
		}, nil
}

func findTableDestinationAndKeyspace(vschema ContextVSchema, ddlStatement sqlparser.DDLStatement) (key.Destination, *vindexes.Keyspace, error) {
	var table *vindexes.Table
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	var err error
	table, _, _, _, destination, err = vschema.FindTableOrVindex(ddlStatement.GetTable())
	if err != nil {
		_, isNotFound := err.(vindexes.NotFoundError)
		if !isNotFound {
			return nil, nil, err
		}
	}
	if table == nil {
		destination, keyspace, _, err = vschema.TargetDestination(ddlStatement.GetTable().Qualifier.String())
		if err != nil {
			return nil, nil, err
		}
		ddlStatement.SetTable("", ddlStatement.GetTable().Name.String())
	} else {
		keyspace = table.Keyspace
		ddlStatement.SetTable("", table.Name.String())
	}
	return destination, keyspace, nil
}

func buildAlterView(vschema ContextVSchema, ddl *sqlparser.AlterView) (key.Destination, *vindexes.Keyspace, error) {
	// For Alter View, we require that the view exist and the select query can be satisfied within the keyspace itself
	// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
	destination, keyspace, err := findTableDestinationAndKeyspace(vschema, ddl)
	if err != nil {
		return nil, nil, err
	}

	var selectPlan engine.Primitive
	selectPlan, err = createInstructionFor(sqlparser.String(ddl.Select), ddl.Select, vschema)
	if err != nil {
		return nil, nil, err
	}
	routePlan, isRoute := selectPlan.(*engine.Route)
	if !isRoute {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	if keyspace.Name != routePlan.GetKeyspaceName() {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewDifferentKeyspace)
	}
	if routePlan.Opcode != engine.SelectUnsharded && routePlan.Opcode != engine.SelectEqualUnique && routePlan.Opcode != engine.SelectScatter {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	sqlparser.Rewrite(ddl.Select, func(cursor *sqlparser.Cursor) bool {
		switch tableName := cursor.Node().(type) {
		case sqlparser.TableName:
			cursor.Replace(sqlparser.TableName{
				Name: tableName.Name,
			})
		}
		return true
	}, nil)
	return destination, keyspace, nil
}

func buildCreateView(vschema ContextVSchema, ddl *sqlparser.CreateView) (key.Destination, *vindexes.Keyspace, error) {
	// For Create View, we require that the keyspace exist and the select query can be satisfied within the keyspace itself
	// We should remove the keyspace name from the table name, as the database name in MySQL might be different than the keyspace name
	destination, keyspace, _, err := vschema.TargetDestination(ddl.ViewName.Qualifier.String())
	if err != nil {
		return nil, nil, err
	}
	ddl.ViewName.Qualifier = sqlparser.NewTableIdent("")

	var selectPlan engine.Primitive
	selectPlan, err = createInstructionFor(sqlparser.String(ddl.Select), ddl.Select, vschema)
	if err != nil {
		return nil, nil, err
	}
	routePlan, isRoute := selectPlan.(*engine.Route)
	if !isRoute {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	if keyspace.Name != routePlan.GetKeyspaceName() {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewDifferentKeyspace)
	}
	if routePlan.Opcode != engine.SelectUnsharded && routePlan.Opcode != engine.SelectEqualUnique && routePlan.Opcode != engine.SelectScatter {
		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, ViewComplex)
	}
	sqlparser.Rewrite(ddl.Select, func(cursor *sqlparser.Cursor) bool {
		switch tableName := cursor.Node().(type) {
		case sqlparser.TableName:
			cursor.Replace(sqlparser.TableName{
				Name: tableName.Name,
			})
		}
		return true
	}, nil)
	return destination, keyspace, nil
}

func buildDropViewOrTable(vschema ContextVSchema, ddlStatement sqlparser.DDLStatement) (key.Destination, *vindexes.Keyspace, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace
	for i, tab := range ddlStatement.GetFromTables() {
		var destinationTab key.Destination
		var keyspaceTab *vindexes.Keyspace
		var table *vindexes.Table
		var err error
		table, _, _, _, destinationTab, err = vschema.FindTableOrVindex(tab)

		if err != nil {
			_, isNotFound := err.(vindexes.NotFoundError)
			if !isNotFound {
				return nil, nil, err
			}
		}
		if table == nil {
			destinationTab, keyspaceTab, _, err = vschema.TargetDestination(tab.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			ddlStatement.GetFromTables()[i] = sqlparser.TableName{
				Name: tab.Name,
			}
		} else {
			keyspaceTab = table.Keyspace
			ddlStatement.GetFromTables()[i] = sqlparser.TableName{
				Name: table.Name,
			}
		}

		if destination == nil && keyspace == nil {
			destination = destinationTab
			keyspace = keyspaceTab
		}
		if destination != destinationTab || keyspace != keyspaceTab {
			return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, DifferentDestinations)
		}
	}
	return destination, keyspace, nil
}

func buildRenameTable(vschema ContextVSchema, renameTable *sqlparser.RenameTable) (key.Destination, *vindexes.Keyspace, error) {
	var destination key.Destination
	var keyspace *vindexes.Keyspace

	for _, tabPair := range renameTable.TablePairs {
		var destinationFrom key.Destination
		var keyspaceFrom *vindexes.Keyspace
		var table *vindexes.Table
		var err error
		table, _, _, _, destinationFrom, err = vschema.FindTableOrVindex(tabPair.FromTable)

		if err != nil {
			_, isNotFound := err.(vindexes.NotFoundError)
			if !isNotFound {
				return nil, nil, err
			}
		}
		if table == nil {
			destinationFrom, keyspaceFrom, _, err = vschema.TargetDestination(tabPair.FromTable.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			tabPair.FromTable = sqlparser.TableName{
				Name: tabPair.FromTable.Name,
			}
		} else {
			keyspaceFrom = table.Keyspace
			tabPair.FromTable = sqlparser.TableName{
				Name: table.Name,
			}
		}

		if tabPair.ToTable.Qualifier.String() != "" {
			_, keyspaceTo, _, err := vschema.TargetDestination(tabPair.ToTable.Qualifier.String())
			if err != nil {
				return nil, nil, err
			}
			if keyspaceTo.Name != keyspaceFrom.Name {
				return nil, nil, mysql.NewSQLError(1450, mysql.SSUnknownSQLState, "Changing schema from '%s' to '%s' is not allowed", keyspaceFrom.Name, keyspaceTo.Name)
			}
			tabPair.ToTable = sqlparser.TableName{
				Name: tabPair.ToTable.Name,
			}
		}

		if destination == nil && keyspace == nil {
			destination = destinationFrom
			keyspace = keyspaceFrom
		}
		if destination != destinationFrom || keyspace != keyspaceFrom {
			return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, DifferentDestinations)
		}
	}
	return destination, keyspace, nil
}
<|endoftext|>


<commit_before>package commands

import "os"

// OverrideEnv sets the environment for specific fields.
//
// This is a farce and only to be used when developing locally.
//
// You should use Cloud Secrets or something like that and set environment vars
// dynamically through those at deployment/run time.
//
// This file should not be included in your git repository and should
// be added to .gitignore. I have included it in this repository since
// this is purely demonstrative and the password value is invalid.
func OverrideEnv() error {
	var err error

	err = os.Setenv("DB_NAME", "gab_local")
	if err != nil {
		return err
	}
	err = os.Setenv("DB_USER", "demo_user")
	if err != nil {
		return err
	}
	err = os.Setenv("DB_PASSWORD", "REPLACE_ME")
	if err != nil {
		return err
	}
	err = os.Setenv("DB_HOST", "localhost")
	if err != nil {
		return err
	}
	err = os.Setenv("DB_PORT", "5432")
	if err != nil {
		return err
	}
	err = os.Setenv("DB_SEARCH_PATH", "demo")
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>use constants; add missing environment vars<commit_after>package commands

import "os"

// OverrideEnv sets the environment for specific fields.
//
// This is a farce and only to be used when developing locally.
//
// You should use Cloud Secrets or something like that and set environment vars
// dynamically through those at deployment/run time.
//
// This file should not be included in your git repository and should
// be added to .gitignore. I have included it in this repository since
// this is purely demonstrative and the password and encryption keys are
// invalid.
func OverrideEnv() error {
	var err error

	// minimum accepted log level
	err = os.Setenv(logLevelMinEnv, "trace")
	if err != nil {
		return err
	}

	// log level
	err = os.Setenv(loglevelEnv, "debug")
	if err != nil {
		return err
	}

	// log error stack
	err = os.Setenv(logErrorStackEnv, "true")
	if err != nil {
		return err
	}

	// server port
	err = os.Setenv(portEnv, "8080")
	if err != nil {
		return err
	}

	// database host
	err = os.Setenv(dbHostEnv, "localhost")
	if err != nil {
		return err
	}

	// database port
	err = os.Setenv(dbPortEnv, "5432")
	if err != nil {
		return err
	}

	// database name
	err = os.Setenv(dbNameEnv, "gab_local")
	if err != nil {
		return err
	}

	// database user
	err = os.Setenv(dbUserEnv, "demo_user")
	if err != nil {
		return err
	}

	// database user password
	err = os.Setenv(dbPasswordEnv, "REPLACE_ME")
	if err != nil {
		return err
	}

	// database search path
	err = os.Setenv(dbSearchPath, "demo")
	if err != nil {
		return err
	}

	// encryption key
	err = os.Setenv(encryptKey, "REPLACE_ME")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package common

type ExecuteStatus int

const (
	PHASE_DONE ExecuteStatus = iota
	PHASE_IN_PROGRESS
	PHASE_ERROR
)

type Phase interface {
	CanPreload() bool
	// Second argument is the result from the preload, or `nil` if the phase
	// doesn't preload.
	Execute(Deployment, interface{}) error
}

type PreloadablePhase interface {
	// Returns data specific to the phase for it to use later on; if it
	// returns an error then the deployment is cancelled.
	Preload(Deployment) (interface{}, error)
}
<commit_msg>Remove old phase status enum<commit_after>package common

type Phase interface {
	CanPreload() bool
	// Second argument is the result from the preload, or `nil` if the phase
	// doesn't preload.
	Execute(Deployment, interface{}) error
}

type PreloadablePhase interface {
	// Returns data specific to the phase for it to use later on; if it
	// returns an error then the deployment is cancelled.
	Preload(Deployment) (interface{}, error)
}
<|endoftext|>


<commit_before>package content

import (
	"text/template"
)

const (
	// BootkubeSystemdContents is a service for running bootkube on the bootstrap
	// nodes
	BootkubeSystemdContents = `
[Unit]
Description=Bootstrap a Kubernetes cluster
Wants=kubelet.service
After=kubelet.service

[Service]
WorkingDirectory=/opt/tectonic
RemainAfterExit=true
ExecStart=/usr/local/bin/bootkube.sh

Restart=on-failure
RestartSec=5s
`
)

var (
	// BootkubeShFileTemplate is a script file for running bootkube on the
	// bootstrap nodes.
	BootkubeShFileTemplate = template.Must(template.New("bootkube.sh").Parse(`#!/usr/bin/env bash
set -e

mkdir --parents /etc/kubernetes/{manifests,bootstrap-configs,bootstrap-manifests}

MACHINE_CONFIG_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-operator)
MACHINE_CONFIG_CONTROLLER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-controller)
MACHINE_CONFIG_SERVER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-server)
MACHINE_CONFIG_DAEMON_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-daemon)

KUBE_APISERVER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-apiserver-operator)
KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-controller-manager-operator)
KUBE_SCHEDULER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-scheduler-operator)

OPENSHIFT_HYPERSHIFT_IMAGE=$(podman run --rm {{.ReleaseImage}} image hypershift)
OPENSHIFT_HYPERKUBE_IMAGE=$(podman run --rm {{.ReleaseImage}} image hyperkube)

mkdir --parents ./{bootstrap-manifests,manifests}

if [ ! -d cvo-bootstrap ]
then
	echo "Rendering Cluster Version Operator Manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"{{.ReleaseImage}}" \
		render \
			--output-dir=/assets/cvo-bootstrap \
			--release-image="{{.ReleaseImage}}"

	cp cvo-bootstrap/bootstrap/* bootstrap-manifests/
	cp cvo-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-apiserver-bootstrap ]
then
	echo "Rendering Kubernetes API server core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_APISERVER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-apiserver-operator render \
		--manifest-etcd-serving-ca=etcd-client-ca.crt \
		--manifest-etcd-server-urls={{.EtcdCluster}} \
		--manifest-image=${OPENSHIFT_HYPERSHIFT_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-apiserver-bootstrap \
		--config-output-file=/assets/kube-apiserver-bootstrap/config \
		--config-override-files=/assets/bootkube-config-overrides/kube-apiserver-config-overrides.yaml \
		--disable-phase-2

	cp kube-apiserver-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-apiserver-config.yaml
	cp kube-apiserver-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-apiserver-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-controller-manager-bootstrap ]
then
	echo "Rendering Kubernetes Controller Manager core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-controller-manager-operator render \
		--manifest-image=${OPENSHIFT_HYPERKUBE_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-controller-manager-bootstrap \
		--config-output-file=/assets/kube-controller-manager-bootstrap/config \
		--config-override-files=/assets/bootkube-config-overrides/kube-controller-manager-config-overrides.yaml

	cp kube-controller-manager-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-controller-manager-config.yaml
	cp kube-controller-manager-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-controller-manager-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-scheduler-bootstrap ]
then
	echo "Rendering Kubernetes Scheduler core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_SCHEDULER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-scheduler-operator render \
		--manifest-image=${OPENSHIFT_HYPERKUBE_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-scheduler-bootstrap \
		--config-output-file=/assets/kube-scheduler-bootstrap/config

	cp kube-scheduler-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-scheduler-config.yaml
	cp kube-scheduler-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-scheduler-bootstrap/manifests/* manifests/
fi

# TODO: Remove this when checkpointer, kube-proxy and kube-dns are properly rendered by corresponding operators.
echo "Installing temporary bootstrap manifests..."
cp pod-checkpointer-operator-bootstrap/* manifests/
cp kube-proxy-operator-bootstrap/* manifests/
cp kube-dns-operator-bootstrap/* manifests/

if [ ! -d mco-bootstrap ]
then
	echo "Rendering MCO manifests..."

	# shellcheck disable=SC2154
	podman run \
		--user 0 \
		--volume "$PWD:/assets:z" \
		"${MACHINE_CONFIG_OPERATOR_IMAGE}" \
		bootstrap \
			--etcd-ca=/assets/tls/etcd-client-ca.crt \
			--root-ca=/assets/tls/root-ca.crt \
			--config-file=/assets/manifests/cluster-config.yaml \
			--dest-dir=/assets/mco-bootstrap \
			--machine-config-controller-image=${MACHINE_CONFIG_CONTROLLER_IMAGE} \
			--machine-config-server-image=${MACHINE_CONFIG_SERVER_IMAGE} \
			--machine-config-daemon-image=${MACHINE_CONFIG_DAEMON_IMAGE} \

	# Bootstrap MachineConfigController uses /etc/mcc/bootstrap/manifests/ dir to
	# 1. read the controller config rendered by MachineConfigOperator
	# 2. read the default MachineConfigPools rendered by MachineConfigOperator
	# 3. read any additional MachineConfigs that are needed for the default MachineConfigPools.
	mkdir --parents /etc/mcc/bootstrap/manifests /etc/kubernetes/manifests/
	cp mco-bootstrap/manifests/* /etc/mcc/bootstrap/manifests/
	cp mco-bootstrap/machineconfigoperator-bootstrap-pod.yaml /etc/kubernetes/manifests/

	# /etc/ssl/mcs/tls.{crt, key} are locations for MachineConfigServer's tls assets.
	mkdir --parents /etc/ssl/mcs/
	cp tls/machine-config-server.crt /etc/ssl/mcs/tls.crt
	cp tls/machine-config-server.key /etc/ssl/mcs/tls.key
fi

# We originally wanted to run the etcd cert signer as
# a static pod, but kubelet could't remove static pod
# when API server is not up, so we have to run this as
# podman container.
# See https://github.com/kubernetes/kubernetes/issues/43292

echo "Starting etcd certificate signer..."

trap "podman rm --force etcd-signer" ERR

# shellcheck disable=SC2154
podman run \
	--name etcd-signer \
	--detach \
	--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
	--network host \
	"{{.EtcdCertSignerImage}}" \
	serve \
	--cacrt=/opt/tectonic/tls/etcd-client-ca.crt \
	--cakey=/opt/tectonic/tls/etcd-client-ca.key \
	--servcrt=/opt/tectonic/tls/apiserver.crt \
	--servkey=/opt/tectonic/tls/apiserver.key \
	--address=0.0.0.0:6443 \
	--csrdir=/tmp \
	--peercertdur=26280h \
	--servercertdur=26280h

echo "Waiting for etcd cluster..."

# Wait for the etcd cluster to come up.
# shellcheck disable=SC2154,SC2086
until podman run \
		--rm \
		--network host \
		--name etcdctl \
		--env ETCDCTL_API=3 \
		--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
		"{{.EtcdctlImage}}" \
		/usr/local/bin/etcdctl \
		--dial-timeout=10m \
		--cacert=/opt/tectonic/tls/etcd-client-ca.crt \
		--cert=/opt/tectonic/tls/etcd-client.crt \
		--key=/opt/tectonic/tls/etcd-client.key \
		--endpoints={{.EtcdCluster}} \
		endpoint health
do
	echo "etcdctl failed. Retrying in 5 seconds..."
	sleep 5
done

echo "etcd cluster up. Killing etcd certificate signer..."

podman rm --force etcd-signer
rm --force /etc/kubernetes/manifests/machineconfigoperator-bootstrap-pod.yaml

echo "Starting bootkube..."

# shellcheck disable=SC2154
podman run \
	--rm \
	--volume "$PWD:/assets:z" \
	--volume /etc/kubernetes:/etc/kubernetes:z \
	--network=host \
	--entrypoint=/bootkube \
	"{{.BootkubeImage}}" \
	start --asset-dir=/assets --required-pods kube-system/pod-checkpointer,openshift-kube-apiserver/openshift-kube-apiserver,kube-system/kube-scheduler,kube-system/kube-controller-manager,openshift-cluster-version/cluster-version-operator

# Workaround for https://github.com/opencontainers/runc/pull/1807
touch /opt/tectonic/.bootkube.done
`))
)

var (
	// BootkubeConfigOverrides contains the configuration override files passed to the render commands of the components.
	// These are supposed to be customized by the installer where the config differs from the operator render default.
	BootkubeConfigOverrides = []*template.Template{
		KubeApiserverConfigOverridesTemplate,
		KubeControllerManagerConfigOverridesTemplate,
	}
)

var (
	// KubeApiserverConfigOverridesTemplate are overrides that the installer passes to the default config of the
	// kube-apiserver rendered by the cluster-kube-apiserver-operator.
	KubeApiserverConfigOverridesTemplate = template.Must(template.New("kube-apiserver-config-overrides.yaml").Parse(`
apiVersion: kubecontrolplane.config.openshift.io/v1
kind: KubeAPIServerConfig
kubeletClientInfo:
  ca: "" # kubelet uses self-signed serving certs. TODO: fix kubelet pki
`))

	// KubeControllerManagerConfigOverridesTemplate are overrides that the installer passes to the default config of the
	// kube-controller-manager rendered by the cluster-kube-controller-manager-operator.
	KubeControllerManagerConfigOverridesTemplate = template.Must(template.New("kube-controller-manager-config-overrides.yaml").Parse(`
apiVersion: kubecontrolplane.config.openshift.io/v1
kind: KubeControllerManagerConfig
`))
)
<commit_msg>bootkube: remove apiserver --disable-phase-2 after default change<commit_after>package content

import (
	"text/template"
)

const (
	// BootkubeSystemdContents is a service for running bootkube on the bootstrap
	// nodes
	BootkubeSystemdContents = `
[Unit]
Description=Bootstrap a Kubernetes cluster
Wants=kubelet.service
After=kubelet.service

[Service]
WorkingDirectory=/opt/tectonic
RemainAfterExit=true
ExecStart=/usr/local/bin/bootkube.sh

Restart=on-failure
RestartSec=5s
`
)

var (
	// BootkubeShFileTemplate is a script file for running bootkube on the
	// bootstrap nodes.
	BootkubeShFileTemplate = template.Must(template.New("bootkube.sh").Parse(`#!/usr/bin/env bash
set -e

mkdir --parents /etc/kubernetes/{manifests,bootstrap-configs,bootstrap-manifests}

MACHINE_CONFIG_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-operator)
MACHINE_CONFIG_CONTROLLER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-controller)
MACHINE_CONFIG_SERVER_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-server)
MACHINE_CONFIG_DAEMON_IMAGE=$(podman run --rm {{.ReleaseImage}} image machine-config-daemon)

KUBE_APISERVER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-apiserver-operator)
KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-controller-manager-operator)
KUBE_SCHEDULER_OPERATOR_IMAGE=$(podman run --rm {{.ReleaseImage}} image cluster-kube-scheduler-operator)

OPENSHIFT_HYPERSHIFT_IMAGE=$(podman run --rm {{.ReleaseImage}} image hypershift)
OPENSHIFT_HYPERKUBE_IMAGE=$(podman run --rm {{.ReleaseImage}} image hyperkube)

mkdir --parents ./{bootstrap-manifests,manifests}

if [ ! -d cvo-bootstrap ]
then
	echo "Rendering Cluster Version Operator Manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"{{.ReleaseImage}}" \
		render \
			--output-dir=/assets/cvo-bootstrap \
			--release-image="{{.ReleaseImage}}"

	cp cvo-bootstrap/bootstrap/* bootstrap-manifests/
	cp cvo-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-apiserver-bootstrap ]
then
	echo "Rendering Kubernetes API server core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_APISERVER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-apiserver-operator render \
		--manifest-etcd-serving-ca=etcd-client-ca.crt \
		--manifest-etcd-server-urls={{.EtcdCluster}} \
		--manifest-image=${OPENSHIFT_HYPERSHIFT_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-apiserver-bootstrap \
		--config-output-file=/assets/kube-apiserver-bootstrap/config \
		--config-override-files=/assets/bootkube-config-overrides/kube-apiserver-config-overrides.yaml

	cp kube-apiserver-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-apiserver-config.yaml
	cp kube-apiserver-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-apiserver-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-controller-manager-bootstrap ]
then
	echo "Rendering Kubernetes Controller Manager core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_CONTROLLER_MANAGER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-controller-manager-operator render \
		--manifest-image=${OPENSHIFT_HYPERKUBE_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-controller-manager-bootstrap \
		--config-output-file=/assets/kube-controller-manager-bootstrap/config \
		--config-override-files=/assets/bootkube-config-overrides/kube-controller-manager-config-overrides.yaml

	cp kube-controller-manager-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-controller-manager-config.yaml
	cp kube-controller-manager-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-controller-manager-bootstrap/manifests/* manifests/
fi

if [ ! -d kube-scheduler-bootstrap ]
then
	echo "Rendering Kubernetes Scheduler core manifests..."

	# shellcheck disable=SC2154
	podman run \
		--volume "$PWD:/assets:z" \
		"${KUBE_SCHEDULER_OPERATOR_IMAGE}" \
		/usr/bin/cluster-kube-scheduler-operator render \
		--manifest-image=${OPENSHIFT_HYPERKUBE_IMAGE} \
		--asset-input-dir=/assets/tls \
		--asset-output-dir=/assets/kube-scheduler-bootstrap \
		--config-output-file=/assets/kube-scheduler-bootstrap/config

	cp kube-scheduler-bootstrap/config /etc/kubernetes/bootstrap-configs/kube-scheduler-config.yaml
	cp kube-scheduler-bootstrap/bootstrap-manifests/* bootstrap-manifests/
	cp kube-scheduler-bootstrap/manifests/* manifests/
fi

# TODO: Remove this when checkpointer, kube-proxy and kube-dns are properly rendered by corresponding operators.
echo "Installing temporary bootstrap manifests..."
cp pod-checkpointer-operator-bootstrap/* manifests/
cp kube-proxy-operator-bootstrap/* manifests/
cp kube-dns-operator-bootstrap/* manifests/

if [ ! -d mco-bootstrap ]
then
	echo "Rendering MCO manifests..."

	# shellcheck disable=SC2154
	podman run \
		--user 0 \
		--volume "$PWD:/assets:z" \
		"${MACHINE_CONFIG_OPERATOR_IMAGE}" \
		bootstrap \
			--etcd-ca=/assets/tls/etcd-client-ca.crt \
			--root-ca=/assets/tls/root-ca.crt \
			--config-file=/assets/manifests/cluster-config.yaml \
			--dest-dir=/assets/mco-bootstrap \
			--machine-config-controller-image=${MACHINE_CONFIG_CONTROLLER_IMAGE} \
			--machine-config-server-image=${MACHINE_CONFIG_SERVER_IMAGE} \
			--machine-config-daemon-image=${MACHINE_CONFIG_DAEMON_IMAGE} \

	# Bootstrap MachineConfigController uses /etc/mcc/bootstrap/manifests/ dir to
	# 1. read the controller config rendered by MachineConfigOperator
	# 2. read the default MachineConfigPools rendered by MachineConfigOperator
	# 3. read any additional MachineConfigs that are needed for the default MachineConfigPools.
	mkdir --parents /etc/mcc/bootstrap/manifests /etc/kubernetes/manifests/
	cp mco-bootstrap/manifests/* /etc/mcc/bootstrap/manifests/
	cp mco-bootstrap/machineconfigoperator-bootstrap-pod.yaml /etc/kubernetes/manifests/

	# /etc/ssl/mcs/tls.{crt, key} are locations for MachineConfigServer's tls assets.
	mkdir --parents /etc/ssl/mcs/
	cp tls/machine-config-server.crt /etc/ssl/mcs/tls.crt
	cp tls/machine-config-server.key /etc/ssl/mcs/tls.key
fi

# We originally wanted to run the etcd cert signer as
# a static pod, but kubelet could't remove static pod
# when API server is not up, so we have to run this as
# podman container.
# See https://github.com/kubernetes/kubernetes/issues/43292

echo "Starting etcd certificate signer..."

trap "podman rm --force etcd-signer" ERR

# shellcheck disable=SC2154
podman run \
	--name etcd-signer \
	--detach \
	--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
	--network host \
	"{{.EtcdCertSignerImage}}" \
	serve \
	--cacrt=/opt/tectonic/tls/etcd-client-ca.crt \
	--cakey=/opt/tectonic/tls/etcd-client-ca.key \
	--servcrt=/opt/tectonic/tls/apiserver.crt \
	--servkey=/opt/tectonic/tls/apiserver.key \
	--address=0.0.0.0:6443 \
	--csrdir=/tmp \
	--peercertdur=26280h \
	--servercertdur=26280h

echo "Waiting for etcd cluster..."

# Wait for the etcd cluster to come up.
# shellcheck disable=SC2154,SC2086
until podman run \
		--rm \
		--network host \
		--name etcdctl \
		--env ETCDCTL_API=3 \
		--volume /opt/tectonic/tls:/opt/tectonic/tls:ro,z \
		"{{.EtcdctlImage}}" \
		/usr/local/bin/etcdctl \
		--dial-timeout=10m \
		--cacert=/opt/tectonic/tls/etcd-client-ca.crt \
		--cert=/opt/tectonic/tls/etcd-client.crt \
		--key=/opt/tectonic/tls/etcd-client.key \
		--endpoints={{.EtcdCluster}} \
		endpoint health
do
	echo "etcdctl failed. Retrying in 5 seconds..."
	sleep 5
done

echo "etcd cluster up. Killing etcd certificate signer..."

podman rm --force etcd-signer
rm --force /etc/kubernetes/manifests/machineconfigoperator-bootstrap-pod.yaml

echo "Starting bootkube..."

# shellcheck disable=SC2154
podman run \
	--rm \
	--volume "$PWD:/assets:z" \
	--volume /etc/kubernetes:/etc/kubernetes:z \
	--network=host \
	--entrypoint=/bootkube \
	"{{.BootkubeImage}}" \
	start --asset-dir=/assets --required-pods kube-system/pod-checkpointer,openshift-kube-apiserver/openshift-kube-apiserver,kube-system/kube-scheduler,kube-system/kube-controller-manager,openshift-cluster-version/cluster-version-operator

# Workaround for https://github.com/opencontainers/runc/pull/1807
touch /opt/tectonic/.bootkube.done
`))
)

var (
	// BootkubeConfigOverrides contains the configuration override files passed to the render commands of the components.
	// These are supposed to be customized by the installer where the config differs from the operator render default.
	BootkubeConfigOverrides = []*template.Template{
		KubeApiserverConfigOverridesTemplate,
		KubeControllerManagerConfigOverridesTemplate,
	}
)

var (
	// KubeApiserverConfigOverridesTemplate are overrides that the installer passes to the default config of the
	// kube-apiserver rendered by the cluster-kube-apiserver-operator.
	KubeApiserverConfigOverridesTemplate = template.Must(template.New("kube-apiserver-config-overrides.yaml").Parse(`
apiVersion: kubecontrolplane.config.openshift.io/v1
kind: KubeAPIServerConfig
kubeletClientInfo:
  ca: "" # kubelet uses self-signed serving certs. TODO: fix kubelet pki
`))

	// KubeControllerManagerConfigOverridesTemplate are overrides that the installer passes to the default config of the
	// kube-controller-manager rendered by the cluster-kube-controller-manager-operator.
	KubeControllerManagerConfigOverridesTemplate = template.Must(template.New("kube-controller-manager-config-overrides.yaml").Parse(`
apiVersion: kubecontrolplane.config.openshift.io/v1
kind: KubeControllerManagerConfig
`))
)
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

    http://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 testing

import (
	"fmt"
	"os"
	"strings"
	"testing"

	"k8s.io/apiserver/pkg/util/feature"
)

// VerifyFeatureGatesUnchanged ensures the provided gate does not change any values when tests() are completed.
// Intended to be placed into unit test packages that mess with feature gates.
//
// Example use:
//
// import (
//   "testing"
//
//   utilfeature "k8s.io/apiserver/pkg/util/feature"
//   utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
//   _ "k8s.io/kubernetes/pkg/features"
// )
//
// func TestMain(m *testing.M) {
//   utilfeaturetesting.VerifyFeatureGatesUnchanged(utilfeature.DefaultFeatureGate, m.Run)
// }
func VerifyFeatureGatesUnchanged(gate feature.FeatureGate, tests func() int) {
	originalGates := gate.DeepCopy()
	originalSet := fmt.Sprint(gate)

	rc := tests()

	finalSet := fmt.Sprint(gate)
	if finalSet != originalSet {
		for _, kv := range strings.Split(finalSet, ",") {
			k := strings.Split(kv, "=")[0]
			if originalGates.Enabled(feature.Feature(k)) != gate.Enabled(feature.Feature(k)) {
				fmt.Println(fmt.Sprintf("VerifyFeatureGatesUnchanged: mutated %s feature gate from %v to %v", k, originalGates.Enabled(feature.Feature(k)), gate.Enabled(feature.Feature(k))))
				rc = 1
			}
		}
	}

	if rc != 0 {
		os.Exit(rc)
	}
}

// SetFeatureGateDuringTest sets the specified gate to the specified value, and returns a function that restores the original value.
// Failures to set or restore cause the test to fail.
//
// Example use:
//
// defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.<FeatureName>, true)()
func SetFeatureGateDuringTest(t *testing.T, gate feature.FeatureGate, f feature.Feature, value bool) func() {
	originalValue := gate.Enabled(f)

	if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, value)); err != nil {
		t.Errorf("error setting %s=%v: %v", f, value, err)
	}

	return func() {
		if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, originalValue)); err != nil {
			t.Errorf("error restoring %s=%v: %v", f, originalValue, err)
		}
	}
}
<commit_msg>drop VerifyFeatureGatesUnchanged<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

    http://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 testing

import (
	"fmt"
	"testing"

	"k8s.io/apiserver/pkg/util/feature"
)

// SetFeatureGateDuringTest sets the specified gate to the specified value, and returns a function that restores the original value.
// Failures to set or restore cause the test to fail.
//
// Example use:
//
// defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.<FeatureName>, true)()
func SetFeatureGateDuringTest(t *testing.T, gate feature.FeatureGate, f feature.Feature, value bool) func() {
	originalValue := gate.Enabled(f)

	if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, value)); err != nil {
		t.Errorf("error setting %s=%v: %v", f, value, err)
	}

	return func() {
		if err := gate.(feature.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, originalValue)); err != nil {
			t.Errorf("error restoring %s=%v: %v", f, originalValue, err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Space Monkey, Inc.

package main

import (
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"gopkg.in/spacemonkeygo/dbx.v1/testutil"
)

func TestCompilation(t *testing.T) {
	tw := testutil.Wrap(t)
	data_dir := filepath.Join("testdata", "build")

	fileinfos, err := ioutil.ReadDir(data_dir)
	tw.AssertNoError(err)

	for _, fileinfo := range fileinfos {
		tw.Run(fileinfo.Name(), func(t *testing.T) {
			path := filepath.Join(data_dir, fileinfo.Name())
			testFile(testutil.Wrap(t), path)
		})
	}
}

func testFile(t *testutil.T, file string) {
	defer func() {
		if val := recover(); val != nil {
			t.Fatal(val)
		}
	}()

	dir, err := ioutil.TempDir("", "dbx")
	t.AssertNoError(err)
	defer os.RemoveAll(dir)

	dbx_source, err := ioutil.ReadFile(file)
	t.AssertNoError(err)
	t.Context("dbx", linedSource(dbx_source))
	d := loadDirectives(t, dbx_source)

	dialects := []string{"postgres", "sqlite3"}
	if other := d.lookup("dialects"); other != nil {
		dialects = other
		t.Logf("using dialects: %q", dialects)
	}

	err = golangCmd("", dialects, "", file, dir)
	t.AssertNoError(err)

	go_file := filepath.Join(dir, filepath.Base(file)+".go")
	go_source, err := ioutil.ReadFile(go_file)
	t.AssertNoError(err)
	t.Context("go", linedSource(go_source))

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, go_file, go_source, parser.AllErrors)
	t.AssertNoError(err)

	config := types.Config{
		Importer: importer.Default(),
	}
	_, err = config.Check(dir, fset, []*ast.File{f}, nil)

	if d.has("fail") {
		t.AssertError(err, d.get("fail"))
	} else {
		t.AssertNoError(err)
	}
}
<commit_msg>fix tests<commit_after>// Copyright (C) 2017 Space Monkey, Inc.

package main

import (
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"gopkg.in/spacemonkeygo/dbx.v1/testutil"
)

func TestCompilation(t *testing.T) {
	tw := testutil.Wrap(t)
	data_dir := filepath.Join("testdata", "build")

	fileinfos, err := ioutil.ReadDir(data_dir)
	tw.AssertNoError(err)

	for _, fileinfo := range fileinfos {
		tw.Run(fileinfo.Name(), func(t *testing.T) {
			path := filepath.Join(data_dir, fileinfo.Name())
			testFile(testutil.Wrap(t), path)
		})
	}
}

func testFile(t *testutil.T, file string) {
	defer func() {
		if val := recover(); val != nil {
			t.Fatal(val)
		}
	}()

	dir, err := ioutil.TempDir("", "dbx")
	t.AssertNoError(err)
	defer os.RemoveAll(dir)

	dbx_source, err := ioutil.ReadFile(file)
	t.AssertNoError(err)
	t.Context("dbx", linedSource(dbx_source))
	d := loadDirectives(t, dbx_source)

	dialects := []string{"postgres", "sqlite3"}
	if other := d.lookup("dialects"); other != nil {
		dialects = other
		t.Logf("using dialects: %q", dialects)
	}

	err = golangCmd("", dialects, "", false, file, dir)
	t.AssertNoError(err)

	go_file := filepath.Join(dir, filepath.Base(file)+".go")
	go_source, err := ioutil.ReadFile(go_file)
	t.AssertNoError(err)
	t.Context("go", linedSource(go_source))

	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, go_file, go_source, parser.AllErrors)
	t.AssertNoError(err)

	config := types.Config{
		Importer: importer.Default(),
	}
	_, err = config.Check(dir, fset, []*ast.File{f}, nil)

	if d.has("fail") {
		t.AssertError(err, d.get("fail"))
	} else {
		t.AssertNoError(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
)

func (this *Server) numDynosAtVersion(applicationName, version string, hostStatusMap *map[string]NodeStatus) (int, error) {
	numFound := 0
	for _, nodeStatus := range *hostStatusMap {
		dynos, err := NodeStatusToDynos(&nodeStatus)
		if err != nil {
			return numFound, err
		}
		for _, dyno := range dynos {
			if dyno.Application == applicationName && dyno.Version == version {
				numFound += 1
			}
		}
	}
	return numFound, nil
}

func (this *Server) pruneDynos(nodeStatus NodeStatus, hostStatusMap *map[string]NodeStatus) error {
	cfg, err := this.getConfig(true)
	if err != nil {
		return err
	}

	logger := NewLogger(os.Stdout, "[dyno-cleanup] ")
	//fmt.Fprint(logger, "Pruning inactive dynos\n")

	type Key struct {
		application string
		version     string
		process     string
	}

	appMap := map[Key]bool{}

	appsByName := map[string]*Application{}

	// Build mapping of current expected app-process-versions.
	for _, app := range cfg.Applications {
		appsByName[app.Name] = app
		for process, _ := range app.Processes {
			//fmt.Fprintf(logger, "Existing app found, name=%v version=%v\n", app.Name, app.LastDeploy)
			appMap[Key{app.Name, app.LastDeploy, process}] = true
		}
	}

	e := &Executor{logger}

	dynos, err := NodeStatusToDynos(&nodeStatus)
	if err != nil {
		return err
	}

	// Cleanup running dynos which don't meet our criteria.
	for _, dyno := range dynos {
		destroy := false

		if dyno.State == DYNO_STATE_STOPPED {
			// Cleanup old stopped dynos which haven't already been reclaimed.
			app, ok := appsByName[dyno.Application]
			if ok {
				appVersionNumber, err := app.LastDeployNumber()
				if err != nil {
					// Not that bad
					fmt.Fprintf(logger, "error: failed to parse last deploy version number for app '%v'/'%v', ignoring..\n", app.Name, app.LastDeploy)
				}
				// If dyno is more than 5 revisions behind the latest, kill it.
				if dyno.VersionNumber+5 < appVersionNumber {
					fmt.Fprintf(logger, "stopped app container '%v' is more than 5 versions behind the latest, terminating it (latest version=%v)\n", dyno.Container, app.LastDeploy)
					destroy = true
				}
			} else {
				fmt.Fprintf(logger, "warning: unrecognized application '%v', ignoring..\n", dyno.Application)
			}

		} else if dyno.State == DYNO_STATE_RUNNING {
			key := Key{dyno.Application, dyno.Version, dyno.Process}
			_, ok := appMap[key]
			if !ok {
				// Verify that the app has some dynos running at the current version.
				app, ok := appsByName[dyno.Application]
				if ok {
					if app.TotalRequestedDynos() > 0 {
						numAtCurrentVersion, err := this.numDynosAtVersion(app.Name, app.LastDeploy, hostStatusMap)
						if err != nil {
							return err
						}
						if dyno.Version != app.LastDeploy && numAtCurrentVersion > 0 {
							fmt.Fprintf(logger, "app container '%v' looks like an old version, terminating it (%v dynos running at latest version=%v)\n", dyno.Container, numAtCurrentVersion, app.LastDeploy)
							destroy = true
						} else {
							fmt.Fprintf(logger, "app for container '%v' doesn't appear to have any dynos running at latest version=%v, refusing to take any action\n", dyno.Container, app.LastDeploy)
						}
					} else {
						fmt.Fprintf(logger, "app '%v' has no processes scaled up, terminating it\n", dyno.Application)
						destroy = true
					}
				} else {
					fmt.Fprintf(logger, "warning: unrecognized application '%v', ignoring..\n", dyno.Application)
				}

				if destroy {
					dynoInUseByLoadBalancer, err := this.dynoRoutingActive(&dyno)
					if err != nil {
						return err
					}
					if dynoInUseByLoadBalancer {
						fmt.Fprintf(logger, "app container '%v' is still in use by the current load-balancer configuration, termination cancelled\n", dyno.Container)
						destroy = false
					}
				}
			}
		}

		if destroy {
			// TODO: Add LB config check to ensure that dyno.Node + "-" + dyno.Port does not appear anywhere in the haproxy config.
			//"ssh", DEFAULT_NODE_USERNAME + "@" dyno.Host,
			fmt.Fprintf(logger, "Cleaning up trash name=%v version=%v\n", dyno.Application, dyno.Version)
			go func(dyno Dyno) {
				dyno.Shutdown(e)
			}(dyno)
		}
	}

	return nil
}

// Determine if a Dyno has active routes defined in the current load-balancer configuration.
func (this *Server) dynoRoutingActive(dyno *Dyno) (bool, error) {
	// LB config check to ensure that dyno.Node + "-" + dyno.Port does not appear anywhere in the haproxy config.
	// Non-web dynos have nothing to do with the load-balancer.
	if dyno.Process != "web" {
		return false, nil
	}

	config, err := this.getConfig(true)
	if err != nil {
		return true, err
	}

	// If there aren't any load-balancers, then the dyno certainly isn't being used by one.
	if len(config.LoadBalancers) == 0 {
		return false, nil
	}

	lbConfig, err := this.GetActiveLoadBalancerConfig()
	if err != nil {
		return true, err
	}

	expr := regexp.MustCompile(` backend ` + dyno.Application + ` ([^b]|b[^a]|ba[^c]|bac[^k]|back[^e]|backe[^n]|backen[^d])* ` + dyno.Host + `-` + dyno.Port)
	inUse := expr.MatchString(strings.Replace(lbConfig, "\n", " ", -1))
	return inUse, nil
}

// # Cleanup old zfs container volumes not in use.
// containers=$(sudo lxc-ls --fancy | sed "1,2d" | cut -f1 -d" ") ; for x in $(sudo zfs list | sed "1d" | cut -d" " -f1); do if [ "${x}" = "tank" ] || [ "${x}" = "tank/git" ] || [ "${x}" = "tank/lxc" ]; then echo "skipping bare tank, git, or lxc: ${x}"; continue; fi; if [ -n "$(echo $x | grep '@')" ]; then search=$(echo $x | sed "s/^.*@//"); else search=$(echo $x | sed "s/^[^\/]\{1,\}\///"); fi; if [ -z "$(echo -e "${containers}" | grep "${search}")" ]; then echo "destroying non-container zfs volume: $x" ; sudo zfs destroy $x; fi; done

// containers=$(sudo lxc-ls --fancy | sed "1,2d" | cut -f1 -d" ") ; for x in $(sudo zfs list | sed "1d" | cut -d" " -f1); do if [ "${x}" = "tank" ] || [ "${x}" = "tank/git" ] || [ "${x}" = "tank/lxc" ]; then echo "skipping bare tank, git, or lxc: ${x}"; continue; fi; if [ -n "$(echo $x | grep '@')" ]; then search=$(echo $x | sed "s/^.*@//"); else search=$(echo $x | sed "s/^[^\/]\{1,\}\///"); fi; if [ -z "$(echo -e "${containers}" | grep "${search}")" ]; then echo "destroying non-container zfs volume: $x" ; sudo zfs destroy $x; fi; done

// # Cleanup empty container dirs.
// for dir in $(find /var/lib/lxc/ -maxdepth 1 -type d); do if test "${dir}" = '.' || test -z "$(echo "${dir}" | sed 's/\/var\/lib\/lxc\///')"; then continue; fi; count=$(find "${dir}/rootfs/" | head -n 3 | wc -l); if test $count -eq 1; then echo $dir $count; sudo rm -rf $dir; fi; done

// # Remove some list of containers.
// $ containers='a
// b
// c
// ..
// z'
// $ for c in $containers; do sudo lxc-stop -n $c; sudo lxc-destroy -n $c; sudo lxc-destroy -n $c; sudo lxc-destroy -n $c; done
<commit_msg>Documented additional container/zfs cleanup command.<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
	"strings"
)

func (this *Server) numDynosAtVersion(applicationName, version string, hostStatusMap *map[string]NodeStatus) (int, error) {
	numFound := 0
	for _, nodeStatus := range *hostStatusMap {
		dynos, err := NodeStatusToDynos(&nodeStatus)
		if err != nil {
			return numFound, err
		}
		for _, dyno := range dynos {
			if dyno.Application == applicationName && dyno.Version == version {
				numFound += 1
			}
		}
	}
	return numFound, nil
}

func (this *Server) pruneDynos(nodeStatus NodeStatus, hostStatusMap *map[string]NodeStatus) error {
	cfg, err := this.getConfig(true)
	if err != nil {
		return err
	}

	logger := NewLogger(os.Stdout, "[dyno-cleanup] ")
	//fmt.Fprint(logger, "Pruning inactive dynos\n")

	type Key struct {
		application string
		version     string
		process     string
	}

	appMap := map[Key]bool{}

	appsByName := map[string]*Application{}

	// Build mapping of current expected app-process-versions.
	for _, app := range cfg.Applications {
		appsByName[app.Name] = app
		for process, _ := range app.Processes {
			//fmt.Fprintf(logger, "Existing app found, name=%v version=%v\n", app.Name, app.LastDeploy)
			appMap[Key{app.Name, app.LastDeploy, process}] = true
		}
	}

	e := &Executor{logger}

	dynos, err := NodeStatusToDynos(&nodeStatus)
	if err != nil {
		return err
	}

	// Cleanup running dynos which don't meet our criteria.
	for _, dyno := range dynos {
		destroy := false

		if dyno.State == DYNO_STATE_STOPPED {
			// Cleanup old stopped dynos which haven't already been reclaimed.
			app, ok := appsByName[dyno.Application]
			if ok {
				appVersionNumber, err := app.LastDeployNumber()
				if err != nil {
					// Not that bad
					fmt.Fprintf(logger, "error: failed to parse last deploy version number for app '%v'/'%v', ignoring..\n", app.Name, app.LastDeploy)
				}
				// If dyno is more than 5 revisions behind the latest, kill it.
				if dyno.VersionNumber+5 < appVersionNumber {
					fmt.Fprintf(logger, "stopped app container '%v' is more than 5 versions behind the latest, terminating it (latest version=%v)\n", dyno.Container, app.LastDeploy)
					destroy = true
				}
			} else {
				fmt.Fprintf(logger, "warning: unrecognized application '%v', ignoring..\n", dyno.Application)
			}

		} else if dyno.State == DYNO_STATE_RUNNING {
			key := Key{dyno.Application, dyno.Version, dyno.Process}
			_, ok := appMap[key]
			if !ok {
				// Verify that the app has some dynos running at the current version.
				app, ok := appsByName[dyno.Application]
				if ok {
					if app.TotalRequestedDynos() > 0 {
						numAtCurrentVersion, err := this.numDynosAtVersion(app.Name, app.LastDeploy, hostStatusMap)
						if err != nil {
							return err
						}
						if dyno.Version != app.LastDeploy && numAtCurrentVersion > 0 {
							fmt.Fprintf(logger, "app container '%v' looks like an old version, terminating it (%v dynos running at latest version=%v)\n", dyno.Container, numAtCurrentVersion, app.LastDeploy)
							destroy = true
						} else {
							fmt.Fprintf(logger, "app for container '%v' doesn't appear to have any dynos running at latest version=%v, refusing to take any action\n", dyno.Container, app.LastDeploy)
						}
					} else {
						fmt.Fprintf(logger, "app '%v' has no processes scaled up, terminating it\n", dyno.Application)
						destroy = true
					}
				} else {
					fmt.Fprintf(logger, "warning: unrecognized application '%v', ignoring..\n", dyno.Application)
				}

				if destroy {
					dynoInUseByLoadBalancer, err := this.dynoRoutingActive(&dyno)
					if err != nil {
						return err
					}
					if dynoInUseByLoadBalancer {
						fmt.Fprintf(logger, "app container '%v' is still in use by the current load-balancer configuration, termination cancelled\n", dyno.Container)
						destroy = false
					}
				}
			}
		}

		if destroy {
			// TODO: Add LB config check to ensure that dyno.Node + "-" + dyno.Port does not appear anywhere in the haproxy config.
			//"ssh", DEFAULT_NODE_USERNAME + "@" dyno.Host,
			fmt.Fprintf(logger, "Cleaning up trash name=%v version=%v\n", dyno.Application, dyno.Version)
			go func(dyno Dyno) {
				dyno.Shutdown(e)
			}(dyno)
		}
	}

	return nil
}

// Determine if a Dyno has active routes defined in the current load-balancer configuration.
func (this *Server) dynoRoutingActive(dyno *Dyno) (bool, error) {
	// LB config check to ensure that dyno.Node + "-" + dyno.Port does not appear anywhere in the haproxy config.
	// Non-web dynos have nothing to do with the load-balancer.
	if dyno.Process != "web" {
		return false, nil
	}

	config, err := this.getConfig(true)
	if err != nil {
		return true, err
	}

	// If there aren't any load-balancers, then the dyno certainly isn't being used by one.
	if len(config.LoadBalancers) == 0 {
		return false, nil
	}

	lbConfig, err := this.GetActiveLoadBalancerConfig()
	if err != nil {
		return true, err
	}

	expr := regexp.MustCompile(` backend ` + dyno.Application + ` ([^b]|b[^a]|ba[^c]|bac[^k]|back[^e]|backe[^n]|backen[^d])* ` + dyno.Host + `-` + dyno.Port)
	inUse := expr.MatchString(strings.Replace(lbConfig, "\n", " ", -1))
	return inUse, nil
}

// # Cleanup old zfs container volumes not in use.
// containers=$(sudo lxc-ls --fancy | sed "1,2d" | cut -f1 -d" ") ; for x in $(sudo zfs list | sed "1d" | cut -d" " -f1); do if [ "${x}" = "tank" ] || [ "${x}" = "tank/git" ] || [ "${x}" = "tank/lxc" ]; then echo "skipping bare tank, git, or lxc: ${x}"; continue; fi; if [ -n "$(echo $x | grep '@')" ]; then search=$(echo $x | sed "s/^.*@//"); else search=$(echo $x | sed "s/^[^\/]\{1,\}\///"); fi; if [ -z "$(echo -e "${containers}" | grep "${search}")" ]; then echo "destroying non-container zfs volume: $x" ; sudo zfs destroy $x; fi; done

// containers=$(sudo lxc-ls --fancy | sed "1,2d" | cut -f1 -d" ") ; for x in $(sudo zfs list | sed "1d" | cut -d" " -f1); do if [ "${x}" = "tank" ] || [ "${x}" = "tank/git" ] || [ "${x}" = "tank/lxc" ]; then echo "skipping bare tank, git, or lxc: ${x}"; continue; fi; if [ -n "$(echo $x | grep '@')" ]; then search=$(echo $x | sed "s/^.*@//"); else search=$(echo $x | sed "s/^[^\/]\{1,\}\///"); fi; if [ -z "$(echo -e "${containers}" | grep "${search}")" ]; then echo "destroying non-container zfs volume: $x" ; sudo zfs destroy $x; fi; done

// # Destroy stopped versioned containers (no base images).
// sudo lxc-ls --stopped | grep '_v[0-9]\+' | cut -f1 -d' ' | xargs -n1 sudo lxc-destroy -n

// # Cleanup empty container dirs.
// for dir in $(find /var/lib/lxc/ -maxdepth 1 -type d); do if test "${dir}" = '.' || test -z "$(echo "${dir}" | sed 's/\/var\/lib\/lxc\///')"; then continue; fi; count=$(find "${dir}/rootfs/" | head -n 3 | wc -l); if test $count -eq 1; then echo $dir $count; sudo rm -rf $dir; fi; done

// # Remove some list of containers.
// $ containers='a
// b
// c
// ..
// z'
// $ for c in $containers; do sudo lxc-stop -n $c; sudo lxc-destroy -n $c; sudo lxc-destroy -n $c; sudo lxc-destroy -n $c; done
<|endoftext|>