Introduce new kubeconfig workflow
The proposed workflow is more generalized and conforms with other k8s tools like kubectl in the way of using kubeconfig. airshipctl will use single kubeconfig file (by default located at ~/.kube/config), and this file will be managed (adding/removing contexts) via KRM function named kubeconfig-manager. Change-Id: I9764eaf9665eaac5543b91c6c876094dfe23a86f Signed-off-by: Ruslan Aliev <raliev@mirantis.com> Closes: #666
This commit is contained in:
parent
0a09456562
commit
6dc39f0735
1
Makefile
1
Makefile
|
@ -121,6 +121,7 @@ docker-image_DOCKERFILE:=Dockerfile
|
|||
# need to be called from the root of the repo.
|
||||
applier_IS_INDEPENDED:=true
|
||||
kubeval-validator_IS_INDEPENDED:=true
|
||||
kubeconfig-manager_IS_INDEPENDED:=true
|
||||
clusterctl_IS_INDEPENDED:=true
|
||||
clusterctl-v0.3_IS_INDEPENDED:=true
|
||||
toolbox-virsh_IS_INDEPENDED:=true
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
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 cluster
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: (kkalynovskyi) Add more description when more subcommands are added
|
||||
clusterLong = `
|
||||
Provides capabilities for interacting with a Kubernetes cluster,
|
||||
such as getting status and deploying initial infrastructure.
|
||||
`
|
||||
)
|
||||
|
||||
// NewClusterCommand creates a command for interacting with a Kubernetes cluster.
|
||||
func NewClusterCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
clusterRootCmd := &cobra.Command{
|
||||
Use: "cluster",
|
||||
Short: "Airshipctl command to manage kubernetes clusters",
|
||||
Long: clusterLong[1:],
|
||||
}
|
||||
|
||||
clusterRootCmd.AddCommand(NewStatusCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(NewGetKubeconfigCommand(cfgFactory))
|
||||
clusterRootCmd.AddCommand(NewListCommand(cfgFactory))
|
||||
|
||||
return clusterRootCmd
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
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 cluster_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestNewClusterCommand(t *testing.T) {
|
||||
tests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "cluster-cmd-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: cluster.NewClusterCommand(nil),
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
testutil.RunTest(t, testcase)
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
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 cluster
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
getKubeconfigLong = `
|
||||
Retrieves kubeconfig of the cluster(s) and prints it to stdout.
|
||||
|
||||
If you specify single CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
||||
will have its context defined.
|
||||
|
||||
If you specify multiple CLUSTER_NAME args, kubeconfig will contain contexts for all of them, but current one
|
||||
won't be specified.
|
||||
|
||||
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||
`
|
||||
getKubeconfigExample = `
|
||||
Retrieve target-cluster kubeconfig
|
||||
# airshipctl cluster get-kubeconfig target-cluster
|
||||
|
||||
Retrieve kubeconfig for the entire site; the kubeconfig will have context for every cluster
|
||||
# airshipctl cluster get-kubeconfig
|
||||
|
||||
Specify a file where kubeconfig should be written
|
||||
# airshipctl cluster get-kubeconfig --file ~/my-kubeconfig
|
||||
|
||||
Merge site kubeconfig with existing kubeconfig file.
|
||||
Keep in mind that this can override a context if it has the same name
|
||||
Airshipctl will overwrite the contents of the file, if you want merge with existing file, specify "--merge" flag
|
||||
# airshipctl cluster get-kubeconfig --file ~/.airship/kubeconfig --merge
|
||||
`
|
||||
)
|
||||
|
||||
// NewGetKubeconfigCommand creates a command which retrieves cluster kubeconfig
|
||||
func NewGetKubeconfigCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
opts := &cluster.GetKubeconfigCommand{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "get-kubeconfig [CLUSTER_NAME...]",
|
||||
Short: "Airshipctl command to retrieve kubeconfig for a desired cluster(s)",
|
||||
Long: getKubeconfigLong[1:],
|
||||
Args: GetKubeconfArgs(opts),
|
||||
Example: getKubeconfigExample,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return opts.RunE(cfgFactory, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.StringVarP(
|
||||
&opts.File,
|
||||
"file",
|
||||
"f",
|
||||
"",
|
||||
"specify where to write kubeconfig file. If flag isn't specified, airshipctl will write it to stdout",
|
||||
)
|
||||
flags.BoolVar(
|
||||
&opts.Merge,
|
||||
"merge",
|
||||
false,
|
||||
"specify if you want to merge kubeconfig with the one that exists at --file location",
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// GetKubeconfArgs extracts one or less arguments from command line, and saves it as name
|
||||
func GetKubeconfArgs(opts *cluster.GetKubeconfigCommand) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
for _, arg := range args {
|
||||
opts.ClusterNames = append(opts.ClusterNames, arg)
|
||||
}
|
||||
|
||||
return cobra.MinimumNArgs(0)(cmd, args)
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
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 cluster_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster"
|
||||
pkgcluster "opendev.org/airship/airshipctl/pkg/cluster"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestNewKubeConfigCommandCmd(t *testing.T) {
|
||||
tests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "cluster-get-kubeconfig-cmd-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: cluster.NewGetKubeconfigCommand(nil),
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
testutil.RunTest(t, testcase)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetKubeconfArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedErrStr string
|
||||
expectedClusterNames []string
|
||||
}{
|
||||
{
|
||||
name: "success one cluster specified",
|
||||
args: []string{"cluster01"},
|
||||
expectedClusterNames: []string{"cluster01"},
|
||||
},
|
||||
{
|
||||
name: "success no cluster specified",
|
||||
},
|
||||
{
|
||||
name: "success two cluster specified",
|
||||
args: []string{"cluster01", "cluster02"},
|
||||
expectedClusterNames: []string{"cluster01", "cluster02"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := &pkgcluster.GetKubeconfigCommand{}
|
||||
args := cluster.GetKubeconfArgs(cmd)
|
||||
err := args(cluster.NewGetKubeconfigCommand(nil), tt.args)
|
||||
if tt.expectedErrStr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErrStr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedClusterNames, cmd.ClusterNames)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
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 cluster
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/phase"
|
||||
)
|
||||
|
||||
const (
|
||||
listLong = `
|
||||
Retrieve and list the defined clusters in table form or display just the cluster names. The contents of the
|
||||
table would include cluster name, kubeconfig context and parent cluster name.
|
||||
`
|
||||
|
||||
listExample = `
|
||||
Retrieve list of clusters
|
||||
# airshipctl cluster list --airshipconf /tmp/airconfig
|
||||
# airshipctl cluster list -o table
|
||||
# airshipctl cluster list -o name
|
||||
`
|
||||
)
|
||||
|
||||
// NewListCommand creates a command which retrieves list of clusters
|
||||
func NewListCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
o := &phase.ClusterListCommand{Factory: cfgFactory}
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "Airshipctl command to get and list defined clusters",
|
||||
Long: listLong[1:],
|
||||
Example: listExample,
|
||||
RunE: listRunE(o),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&o.Format, "output", "o", "name", "output formats. Supported options are 'table' and 'name'")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// listRunE returns a function to cobra command to be executed in runtime
|
||||
func listRunE(o *phase.ClusterListCommand) func(
|
||||
cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
o.Writer = cmd.OutOrStdout()
|
||||
return o.RunE()
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
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 cluster_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestNewListCommand(t *testing.T) {
|
||||
tests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "cluster-list-cmd-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: cluster.NewListCommand(nil),
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
testutil.RunTest(t, testcase)
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
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 cluster
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/errors"
|
||||
)
|
||||
|
||||
// NewStatusCommand creates a command which reports the statuses of a cluster's deployed components.
|
||||
func NewStatusCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var kubeconfig string
|
||||
statusCmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Retrieve statuses of deployed cluster components",
|
||||
RunE: clusterStatusRunE,
|
||||
}
|
||||
|
||||
statusCmd.Flags().StringVar(
|
||||
&kubeconfig,
|
||||
"kubeconfig",
|
||||
"",
|
||||
"Path to kubeconfig associated with cluster being managed")
|
||||
|
||||
return statusCmd
|
||||
}
|
||||
|
||||
func clusterStatusRunE(cmd *cobra.Command, args []string) error {
|
||||
return errors.ErrNotImplemented{What: "Cluster Status"}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
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 cluster_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/cluster"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestNewClusterStatusCmd(t *testing.T) {
|
||||
tests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "cluster-status-cmd-with-help",
|
||||
CmdLine: "--help",
|
||||
Cmd: cluster.NewStatusCommand(nil),
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
testutil.RunTest(t, testcase)
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
Provides capabilities for interacting with a Kubernetes cluster,
|
||||
such as getting status and deploying initial infrastructure.
|
||||
|
||||
Usage:
|
||||
cluster [command]
|
||||
|
||||
Available Commands:
|
||||
get-kubeconfig Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||
help Help about any command
|
||||
list Airshipctl command to get and list defined clusters
|
||||
status Retrieve statuses of deployed cluster components
|
||||
|
||||
Flags:
|
||||
-h, --help help for cluster
|
||||
|
||||
Use "cluster [command] --help" for more information about a command.
|
|
@ -1,8 +0,0 @@
|
|||
Retrieve statuses of deployed cluster components
|
||||
|
||||
Usage:
|
||||
status [flags]
|
||||
|
||||
Flags:
|
||||
-h, --help help for status
|
||||
--kubeconfig string Path to kubeconfig associated with cluster being managed
|
|
@ -1,35 +0,0 @@
|
|||
Retrieves kubeconfig of the cluster(s) and prints it to stdout.
|
||||
|
||||
If you specify single CLUSTER_NAME, kubeconfig will have a CurrentContext set to CLUSTER_NAME and
|
||||
will have its context defined.
|
||||
|
||||
If you specify multiple CLUSTER_NAME args, kubeconfig will contain contexts for all of them, but current one
|
||||
won't be specified.
|
||||
|
||||
If you don't specify CLUSTER_NAME, kubeconfig will have multiple contexts for every cluster
|
||||
in the airship site. Context names will correspond to cluster names. CurrentContext will be empty.
|
||||
|
||||
Usage:
|
||||
get-kubeconfig [CLUSTER_NAME...] [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
Retrieve target-cluster kubeconfig
|
||||
# airshipctl cluster get-kubeconfig target-cluster
|
||||
|
||||
Retrieve kubeconfig for the entire site; the kubeconfig will have context for every cluster
|
||||
# airshipctl cluster get-kubeconfig
|
||||
|
||||
Specify a file where kubeconfig should be written
|
||||
# airshipctl cluster get-kubeconfig --file ~/my-kubeconfig
|
||||
|
||||
Merge site kubeconfig with existing kubeconfig file.
|
||||
Keep in mind that this can override a context if it has the same name
|
||||
Airshipctl will overwrite the contents of the file, if you want merge with existing file, specify "--merge" flag
|
||||
# airshipctl cluster get-kubeconfig --file ~/.airship/kubeconfig --merge
|
||||
|
||||
|
||||
Flags:
|
||||
-f, --file string specify where to write kubeconfig file. If flag isn't specified, airshipctl will write it to stdout
|
||||
-h, --help help for get-kubeconfig
|
||||
--merge specify if you want to merge kubeconfig with the one that exists at --file location
|
|
@ -1,17 +0,0 @@
|
|||
Retrieve and list the defined clusters in table form or display just the cluster names. The contents of the
|
||||
table would include cluster name, kubeconfig context and parent cluster name.
|
||||
|
||||
Usage:
|
||||
list [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
Retrieve list of clusters
|
||||
# airshipctl cluster list --airshipconf /tmp/airconfig
|
||||
# airshipctl cluster list -o table
|
||||
# airshipctl cluster list -o name
|
||||
|
||||
|
||||
Flags:
|
||||
-h, --help help for list
|
||||
-o, --output string output formats. Supported options are 'table' and 'name' (default "name")
|
|
@ -15,6 +15,8 @@
|
|||
package phase
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
|
@ -23,8 +25,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// TODO (kkalynovskyi) when different phase executors will be implemented, and their description is more clear,
|
||||
// add documentation here. also consider adding dynamic phase descriptions based on executors.
|
||||
runLong = `
|
||||
Run a phase such as controlplane-ephemeral, remotedirect-ephemeral, initinfra-ephemeral, etc...
|
||||
To list the phases associated with a site, run 'airshipctl phase list'.
|
||||
|
@ -38,6 +38,7 @@ Run initinfra phase
|
|||
// NewRunCommand creates a command to run specific phase
|
||||
func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
p := &phase.RunCommand{Factory: cfgFactory}
|
||||
p.Options.Kubeconfig = os.Getenv("KUBECONFIG")
|
||||
f := &phase.RunFlags{}
|
||||
|
||||
runCmd := &cobra.Command{
|
||||
|
@ -51,6 +52,10 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
|||
// Go through all the flags that have been set
|
||||
fn := func(flag *pflag.Flag) {
|
||||
switch flag.Name {
|
||||
case "kubeconfig":
|
||||
p.Options.Kubeconfig = f.Kubeconfig
|
||||
case "context":
|
||||
p.Options.Context = f.Context
|
||||
case "dry-run":
|
||||
p.Options.DryRun = f.DryRun
|
||||
case "wait-timeout":
|
||||
|
@ -62,6 +67,16 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
|||
},
|
||||
}
|
||||
flags := runCmd.Flags()
|
||||
flags.StringVar(
|
||||
&f.Kubeconfig,
|
||||
"kubeconfig",
|
||||
"",
|
||||
"Path to the kubeconfig file to use for phase's purposes")
|
||||
flags.StringVar(
|
||||
&f.Context,
|
||||
"context",
|
||||
"",
|
||||
"The name of the kubeconfig context to use")
|
||||
flags.BoolVar(&f.DryRun, "dry-run", false, "simulate phase execution")
|
||||
flags.DurationVar(&f.Timeout, "wait-timeout", 0, "wait timeout")
|
||||
return runCmd
|
||||
|
|
|
@ -11,6 +11,8 @@ Run initinfra phase
|
|||
|
||||
|
||||
Flags:
|
||||
--context string The name of the kubeconfig context to use
|
||||
--dry-run simulate phase execution
|
||||
-h, --help help for run
|
||||
--kubeconfig string Path to the kubeconfig file to use for phase's purposes
|
||||
--wait-timeout duration wait timeout
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
package plan
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
|
@ -39,6 +41,7 @@ Perform a dry run of a plan
|
|||
// NewRunCommand creates a command which execute a particular phase plan
|
||||
func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
r := &phase.PlanRunCommand{Factory: cfgFactory}
|
||||
r.Options.Kubeconfig = os.Getenv("KUBECONFIG")
|
||||
f := &phase.RunFlags{}
|
||||
|
||||
runCmd := &cobra.Command{
|
||||
|
@ -51,6 +54,8 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
|||
r.PlanID.Name = args[0]
|
||||
fn := func(flag *pflag.Flag) {
|
||||
switch flag.Name {
|
||||
case "kubeconfig":
|
||||
r.Options.Kubeconfig = f.Kubeconfig
|
||||
case "dry-run":
|
||||
r.Options.DryRun = f.DryRun
|
||||
case "wait-timeout":
|
||||
|
@ -63,6 +68,11 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
|
|||
}
|
||||
|
||||
flags := runCmd.Flags()
|
||||
flags.StringVar(
|
||||
&f.Kubeconfig,
|
||||
"kubeconfig",
|
||||
"",
|
||||
"Path to the kubeconfig file to use for plan's purposes")
|
||||
flags.BoolVar(&f.DryRun, "dry-run", false, "simulate phase execution")
|
||||
flags.DurationVar(&f.Timeout, "wait-timeout", 0, "wait timeout")
|
||||
return runCmd
|
||||
|
|
|
@ -16,4 +16,5 @@ Perform a dry run of a plan
|
|||
Flags:
|
||||
--dry-run simulate phase execution
|
||||
-h, --help help for run
|
||||
--kubeconfig string Path to the kubeconfig file to use for plan's purposes
|
||||
--wait-timeout duration wait timeout
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"opendev.org/airship/airshipctl/cmd/baremetal"
|
||||
"opendev.org/airship/airshipctl/cmd/cluster"
|
||||
"opendev.org/airship/airshipctl/cmd/completion"
|
||||
"opendev.org/airship/airshipctl/cmd/config"
|
||||
"opendev.org/airship/airshipctl/cmd/document"
|
||||
|
@ -77,7 +76,6 @@ func NewRootCommand(out io.Writer) (*cobra.Command, *RootOptions) {
|
|||
// default commands to airshipctl
|
||||
func AddDefaultAirshipCTLCommands(cmd *cobra.Command, factory cfg.Factory) *cobra.Command {
|
||||
cmd.AddCommand(baremetal.NewBaremetalCommand(factory))
|
||||
cmd.AddCommand(cluster.NewClusterCommand(factory))
|
||||
cmd.AddCommand(completion.NewCompletionCommand())
|
||||
cmd.AddCommand(document.NewDocumentCommand(factory))
|
||||
cmd.AddCommand(config.NewConfigCommand(factory))
|
||||
|
|
|
@ -7,7 +7,6 @@ Usage:
|
|||
|
||||
Available Commands:
|
||||
baremetal Airshipctl command to manage bare metal host(s)
|
||||
cluster Airshipctl command to manage kubernetes clusters
|
||||
completion Airshipctl command to generate completion script for the specified shell (bash or zsh)
|
||||
config Airshipctl command to manage airshipctl config file
|
||||
document Airshipctl command to manage site manifest documents
|
||||
|
|
|
@ -27,7 +27,6 @@ SEE ALSO
|
|||
~~~~~~~~
|
||||
|
||||
* :ref:`airshipctl baremetal <airshipctl_baremetal>` - Airshipctl command to manage bare metal host(s)
|
||||
* :ref:`airshipctl cluster <airshipctl_cluster>` - Airshipctl command to manage kubernetes clusters
|
||||
* :ref:`airshipctl completion <airshipctl_completion>` - Airshipctl command to generate completion script for the specified shell (bash or zsh)
|
||||
* :ref:`airshipctl config <airshipctl_config>` - Airshipctl command to manage airshipctl config file
|
||||
* :ref:`airshipctl document <airshipctl_document>` - Airshipctl command to manage site manifest documents
|
||||
|
|
|
@ -33,6 +33,5 @@ SEE ALSO
|
|||
|
||||
* :ref:`airshipctl <airshipctl>` - A unified command line tool for management of end-to-end kubernetes cluster deployment on cloud infrastructure environments.
|
||||
* :ref:`airshipctl cluster get-kubeconfig <airshipctl_cluster_get-kubeconfig>` - Airshipctl command to retrieve kubeconfig for a desired cluster(s)
|
||||
* :ref:`airshipctl cluster list <airshipctl_cluster_list>` - Airshipctl command to get and list defined clusters
|
||||
* :ref:`airshipctl cluster status <airshipctl_cluster_status>` - Retrieve statuses of deployed cluster components
|
||||
|
||||
|
|
|
@ -7,5 +7,4 @@ cluster
|
|||
|
||||
airshipctl_cluster
|
||||
airshipctl_cluster_get-kubeconfig
|
||||
airshipctl_cluster_list
|
||||
airshipctl_cluster_status
|
||||
|
|
|
@ -7,7 +7,6 @@ Commands
|
|||
|
||||
airshipctl
|
||||
baremetal/index
|
||||
cluster/index
|
||||
completion/index
|
||||
config/index
|
||||
document/index
|
||||
|
|
|
@ -32,8 +32,10 @@ Options
|
|||
|
||||
::
|
||||
|
||||
--context string The name of the kubeconfig context to use
|
||||
--dry-run simulate phase execution
|
||||
-h, --help help for run
|
||||
--kubeconfig string Path to the kubeconfig file to use for phase's purposes
|
||||
--wait-timeout duration wait timeout
|
||||
|
||||
Options inherited from parent commands
|
||||
|
|
|
@ -37,6 +37,7 @@ Options
|
|||
|
||||
--dry-run simulate phase execution
|
||||
-h, --help help for run
|
||||
--kubeconfig string Path to the kubeconfig file to use for plan's purposes
|
||||
--wait-timeout duration wait timeout
|
||||
|
||||
Options inherited from parent commands
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
@ -323,52 +323,6 @@ Kubeconfig api object example
|
|||
client-certificate-data: <client-cert-data>
|
||||
client-key-data: <client-key-data>
|
||||
|
||||
Cluster Map
|
||||
-----------
|
||||
|
||||
Cluster map defines parent-child relationship between clusters, allows
|
||||
dynamic kubeconfig for clusters. When kubeconfig must be sourced from
|
||||
a parent cluster, cluster map will be used to find parent cluster and
|
||||
request kubeconfig from it.
|
||||
|
||||
Also, ClusterAPIRef is a part of Cluster Map and will be used to find
|
||||
cluster object in kubernetes parent cluster. It also maps a clusterapi
|
||||
name and namespaces for a given cluster. In the below example object,
|
||||
the cluster api ref describes the reference to the cluster api object.
|
||||
The cluster `workload01` in the cluster map has an clusterAPIRef to
|
||||
corresponding cluster-api cluster object (kind: cluster) with name workload01
|
||||
and inside namespace tenant01-namespace.
|
||||
|
||||
Cluster map is defined in `Phase bundle <#phase-bundle>`__ as a document.
|
||||
|
||||
- `Cluster map API object source code
|
||||
<https://godoc.org/opendev.org/airship/airshipctl/pkg/api/v1alpha1#ClusterMap>`__
|
||||
- `Cluster map interface source code
|
||||
<https://pkg.go.dev/opendev.org/airship/airshipctl/pkg/phase/executors#ClusterctlExecutor>`__
|
||||
|
||||
Example of cluster map
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
target-cluster:
|
||||
parent: ephemeral-cluster
|
||||
# dynamicKubeConf: false # default value
|
||||
ephemeral-cluster: {}
|
||||
workload01:
|
||||
parent: target-cluster
|
||||
dynamicKubeConf: true
|
||||
clusterAPIRef:
|
||||
name: workload01
|
||||
namespace: tenant01-namespace
|
||||
|
||||
Metadata file
|
||||
-------------
|
||||
|
||||
|
|
6
go.mod
6
go.mod
|
@ -24,12 +24,12 @@ require (
|
|||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
k8s.io/api v0.21.1
|
||||
k8s.io/apiextensions-apiserver v0.21.1
|
||||
k8s.io/apimachinery v0.21.1
|
||||
k8s.io/cli-runtime v0.21.1
|
||||
k8s.io/client-go v0.21.1
|
||||
k8s.io/kubectl v0.21.1
|
||||
opendev.org/airship/go-redfish v0.0.0-20211004183611-3c3d7c6ba009
|
||||
|
|
11
go.sum
11
go.sum
|
@ -689,8 +689,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -758,8 +758,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
|
@ -770,8 +770,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
|
@ -92,7 +92,7 @@ func (c *Config) Run(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||
objs = append(objs, &unstructured.Unstructured{Object: m})
|
||||
}
|
||||
|
||||
f := factoryFromKubeConfig(c.Kubeconfig, c.Context)
|
||||
f := factoryFromKubeConfig("/kubeconfig", c.Context)
|
||||
statusPoller, err := poller.NewStatusPoller(f, c.WaitOptions.Conditions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
ARG GO_IMAGE=quay.io/airshipit/golang:1.16.8-alpine
|
||||
ARG PLUGINS_RELEASE_IMAGE=quay.io/airshipit/alpine:3.13.5
|
||||
|
||||
FROM ${GO_IMAGE} as function
|
||||
ARG GOPROXY=""
|
||||
# Inject custom root certificate authorities if needed
|
||||
# Docker does not have a good conditional copy statement and requires that a source file exists
|
||||
# to complete the copy function without error. Therefore the README.md file will be copied to
|
||||
# the image every time even if there are no .crt files.
|
||||
COPY ./certs/* /usr/local/share/ca-certificates/
|
||||
RUN update-ca-certificates
|
||||
ENV PATH "/usr/local/go/bin:$PATH"
|
||||
ENV CGO_ENABLED=0
|
||||
WORKDIR /go/src/
|
||||
COPY image/go.mod image/go.sum ./
|
||||
RUN go mod download
|
||||
COPY image/ ./
|
||||
RUN go build -v -o /usr/local/bin/config-function ./
|
||||
|
||||
FROM ${PLUGINS_RELEASE_IMAGE} as release
|
||||
# Inject custom root certificate authorities if needed
|
||||
# Docker does not have a good conditional copy statement and requires that a source file exists
|
||||
# to complete the copy function without error. Therefore the README.md file will be copied to
|
||||
# the image every time even if there are no .crt files.
|
||||
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
|
||||
COPY ./certs/* /usr/local/share/ca-certificates/
|
||||
RUN update-ca-certificates
|
||||
COPY --from=function /usr/local/bin/config-function /usr/local/bin/config-function
|
||||
ENV HOME=/home/kubeconfig-manager
|
||||
WORKDIR $HOME
|
||||
RUN chmod -R a+w $HOME
|
||||
CMD ["config-function"]
|
|
@ -0,0 +1,21 @@
|
|||
# Kubeconfig Manager
|
||||
|
||||
This is a KRM function which retrieves and modifies kubeconfig
|
||||
based on appropriate options.
|
||||
|
||||
## Function implementation
|
||||
|
||||
The function is implemented as an [image](image), and built using `make docker-image-kubeconfig-manager`.
|
||||
|
||||
### Function configuration
|
||||
|
||||
As input options, the KRM function receives a struct with options how to get or modify kubeconfig.
|
||||
See the `KubeconfigOptions` struct definition in v1alpha1 airshipctl API for the documentation.
|
||||
|
||||
## Function invocation
|
||||
|
||||
The function invoked by airshipctl command via `airshipctl phase run`:
|
||||
|
||||
airshipctl phase run <phase_name>
|
||||
|
||||
if a phase has GenericContainer executor defined along with KubeconfigOptions.
|
|
@ -0,0 +1,6 @@
|
|||
# Additional Docker image root certificate authorities
|
||||
If you require additional certificate authorities for your Docker image:
|
||||
* Add ASCII PEM encoded .crt files to this directory
|
||||
* The files will be copied into your docker image at build time.
|
||||
|
||||
To update manually copy the .crt files to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.
|
|
@ -0,0 +1,15 @@
|
|||
module opendev.org/airship/airshipctl/krm-functions/kubeconfig-manager/image
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
|
||||
github.com/imdario/mergo v0.3.12
|
||||
k8s.io/apimachinery v0.21.1
|
||||
k8s.io/cli-runtime v0.21.1
|
||||
k8s.io/client-go v0.21.1
|
||||
k8s.io/kubectl v0.21.1
|
||||
sigs.k8s.io/kustomize/api v0.8.11 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0
|
||||
)
|
|
@ -0,0 +1,761 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
|
||||
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c=
|
||||
k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
|
||||
k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs=
|
||||
k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
|
||||
k8s.io/cli-runtime v0.21.1 h1:Oj/iZxa7LLXrhzShaLNF4rFJEIEBTDHj0dJw4ra2vX4=
|
||||
k8s.io/cli-runtime v0.21.1/go.mod h1:TI9Bvl8lQWZB2KqE91QLCp9AZE4l29zNFnj/x4IX4Fw=
|
||||
k8s.io/client-go v0.21.1 h1:bhblWYLZKUu+pm50plvQF8WpY6TXdRRtcS/K9WauOj4=
|
||||
k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
|
||||
k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
|
||||
k8s.io/component-base v0.21.1 h1:iLpj2btXbR326s/xNQWmPNGu0gaYSjzn7IN/5i28nQw=
|
||||
k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA=
|
||||
k8s.io/component-helpers v0.21.1/go.mod h1:FtC1flbiQlosHQrLrRUulnKxE4ajgWCGy/67fT2GRlQ=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
|
||||
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kubectl v0.21.1 h1:ySEusoeSgSDSiSBncDMsNrthSa3OSlXqT4R2rf1VFTw=
|
||||
k8s.io/kubectl v0.21.1/go.mod h1:PMYR88MqESuysBM/MX+Vu4JbX/50nY4d4kny+SPEI2U=
|
||||
k8s.io/metrics v0.21.1/go.mod h1:pyDVLsLe++FIGDBFU80NcW4xMFsuiVTWL8Zfi7+PpNo=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
|
||||
sigs.k8s.io/kustomize/api v0.8.11 h1:LzQzlq6Z023b+mBtc6v72N2mSHYmN8x7ssgbf/hv0H8=
|
||||
sigs.k8s.io/kustomize/api v0.8.11/go.mod h1:a77Ls36JdfCWojpUqR6m60pdGY1AYFix4AH83nJtY1g=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0=
|
||||
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo=
|
||||
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
|
||||
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
kubeconfigFile = "/kubeconfig"
|
||||
defaultTimeout = 30 * time.Second
|
||||
|
||||
kubeconfigAPIVersion = "airshipit.org/v1alpha1"
|
||||
kubeconfigKind = "KubeConfig"
|
||||
kubeconfigKey = "config"
|
||||
)
|
||||
|
||||
// Options holds main options to perform different actions with kubeconfig
|
||||
type Options struct {
|
||||
Action string `yaml:"action,omitempty"`
|
||||
Context string `yaml:"context,omitempty"`
|
||||
CapiOptions CAPIOptions `yaml:"capiOptions,omitempty"`
|
||||
}
|
||||
|
||||
// CAPIOptions is used to retrieve cluster's kubeconfig from secret
|
||||
type CAPIOptions struct {
|
||||
ManagedClusterName string `yaml:"clusterName,omitempty"`
|
||||
ParentContext string `yaml:"parentContext,omitempty"`
|
||||
ManagedClusterNamespace string `yaml:"namespace,omitempty"`
|
||||
Timeout string `yaml:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
func factoryFromKubeConfig(path, context, timeout string) cmdutil.Factory {
|
||||
kf := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
|
||||
kf.KubeConfig = &path
|
||||
kf.Context = &context
|
||||
kf.Timeout = &timeout
|
||||
return cmdutil.NewFactory(cmdutil.NewMatchVersionFlags(kf))
|
||||
}
|
||||
|
||||
func coreV1ClientSet(f cmdutil.Factory) (corev1.CoreV1Interface, error) {
|
||||
clientSet, err := f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clientSet.CoreV1(), nil
|
||||
}
|
||||
|
||||
// Decrypt reads decrypted input with yaml nodes and extracts kubeconfigs from them
|
||||
func Decrypt(to *clientcmdapi.Config, nodes []*yaml.RNode) error {
|
||||
var configs []*clientcmdapi.Config
|
||||
for _, node := range nodes {
|
||||
if node.GetApiVersion() == kubeconfigAPIVersion && node.GetKind() == kubeconfigKind {
|
||||
nodeMap, err := node.Map()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfgData, ok := nodeMap[kubeconfigKey]; ok {
|
||||
data, err := yaml.Marshal(cfgData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := clientcmd.Load(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Merge(configs, to)
|
||||
}
|
||||
|
||||
// Merge merges multiple kubeconfigs into dst one
|
||||
func Merge(kubeconfigs []*clientcmdapi.Config, dst *clientcmdapi.Config) error {
|
||||
// first merge all of our maps
|
||||
mapConfig := clientcmdapi.NewConfig()
|
||||
for _, kubeconfig := range kubeconfigs {
|
||||
if err := mergo.Merge(mapConfig, kubeconfig, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// merge all of the struct values in the reverse order so that priority is given correctly
|
||||
// errors are not added to the list the second time
|
||||
nonMapConfig := clientcmdapi.NewConfig()
|
||||
for i := len(kubeconfigs) - 1; i >= 0; i-- {
|
||||
kubeconfig := kubeconfigs[i]
|
||||
if err := mergo.Merge(nonMapConfig, kubeconfig, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
|
||||
// get the values we expect.
|
||||
config := clientcmdapi.NewConfig()
|
||||
if err := mergo.Merge(config, mapConfig, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mergo.Merge(config, nonMapConfig, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = *config
|
||||
return nil
|
||||
}
|
||||
|
||||
// FromSecret returns KubeSource type, uses client interface to kubernetes cluster
|
||||
func (o *CAPIOptions) FromSecret(to *clientcmdapi.Config) error {
|
||||
client, err := coreV1ClientSet(factoryFromKubeConfig(kubeconfigFile, o.ParentContext, "30s"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.ManagedClusterName == "" {
|
||||
return fmt.Errorf("clusterName is empty")
|
||||
}
|
||||
if o.ManagedClusterNamespace == "" {
|
||||
o.ManagedClusterNamespace = "default"
|
||||
}
|
||||
|
||||
data, exist, secretName := new([]byte), new(bool), fmt.Sprintf("%s-kubeconfig", o.ManagedClusterName)
|
||||
fn := func() (bool, error) {
|
||||
secret, err := client.Secrets(o.ManagedClusterNamespace).Get(context.Background(), secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("get kubeconfig from secret failed, retrying, reason: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if *data, *exist = secret.Data["value"]; *exist && len(*data) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return true, fmt.Errorf("malformed config: %s", o.ManagedClusterName)
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(o.Timeout)
|
||||
if err != nil || duration == 0 {
|
||||
duration = defaultTimeout
|
||||
}
|
||||
|
||||
if err = wait.PollImmediate(time.Second, duration, fn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := clientcmd.Load(*data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Merge([]*clientcmdapi.Config{cfg, to}, to)
|
||||
}
|
||||
|
||||
func save(config *clientcmdapi.Config) error {
|
||||
data, err := clientcmd.Write(*config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(kubeconfigFile, data, 0600)
|
||||
}
|
||||
|
||||
// Run prepares config, applier and performs apply process
|
||||
func (c *Options) Run(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
config, err := clientcmd.LoadFromFile(kubeconfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "config: %v\n", *c)
|
||||
switch c.Action {
|
||||
case "decrypt":
|
||||
if err := Decrypt(config, nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "get":
|
||||
if err := c.CapiOptions.FromSecret(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "show":
|
||||
if c.Context != "" {
|
||||
config.CurrentContext = c.Context
|
||||
if err := clientcmdapi.MinifyConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
data, err := clientcmd.Write(*config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(os.Stderr, "%s\n", string(data))
|
||||
return nil, err
|
||||
case "remove":
|
||||
// TBD
|
||||
}
|
||||
return nil, save(config)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Fprintf(os.Stderr, "welcome to kubeconfig manager!\n")
|
||||
cfg := &Options{}
|
||||
cmd := command.Build(framework.SimpleProcessor{Filter: kio.FilterFunc(cfg.Run), Config: cfg},
|
||||
command.StandaloneDisabled, false)
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
# 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
|
||||
|
@ -12,9 +10,22 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -ex
|
||||
|
||||
KUBECONFIG=${KUBECONFIG}:${TGT_KUBECONFIG} kubectl config view --flatten > /tmp/kubeconfig-trans
|
||||
|
||||
cat /tmp/kubeconfig-trans > ${TGT_KUBECONFIG}
|
||||
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
metadata:
|
||||
name: get-kubeconfig
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
spec:
|
||||
type: krm
|
||||
image: quay.io/airshipit/kubeconfig-manager:latest
|
||||
hostNetwork: true
|
||||
mounts:
|
||||
- type: bind
|
||||
src: ~/.kube/config
|
||||
dst: /kubeconfig
|
||||
rw: true
|
||||
config: |
|
||||
action: get
|
||||
contextName: target-cluster
|
|
@ -35,6 +35,8 @@ spec:
|
|||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
context:
|
||||
type: string
|
||||
images:
|
||||
additionalProperties:
|
||||
description: ImageMeta is part of clusterctl config
|
||||
|
@ -73,6 +75,8 @@ spec:
|
|||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
kubeconfig:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
move-options:
|
||||
|
@ -82,6 +86,8 @@ spec:
|
|||
description: Namespace where the objects describing the workload cluster
|
||||
exists. If unspecified, the current namespace will be used.
|
||||
type: string
|
||||
targetContext:
|
||||
type: string
|
||||
type: object
|
||||
providers:
|
||||
items:
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.2
|
||||
creationTimestamp: null
|
||||
name: clustermaps.airshipit.org
|
||||
spec:
|
||||
group: airshipit.org
|
||||
names:
|
||||
kind: ClusterMap
|
||||
listKind: ClusterMapList
|
||||
plural: clustermaps
|
||||
singular: clustermap
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ClusterMap represents cluster defined for this manifest
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
map:
|
||||
additionalProperties:
|
||||
description: Cluster uniquely identifies a cluster and its parent cluster
|
||||
properties:
|
||||
kubeconfigSources:
|
||||
description: KubeconfigContext is the context in kubeconfig, default
|
||||
is equals to clusterMap key
|
||||
items:
|
||||
description: KubeconfigSource describes source of the kubeconfig
|
||||
properties:
|
||||
bundle:
|
||||
description: KubeconfigSourceBundle get kubeconfig from bundle
|
||||
properties:
|
||||
contextName:
|
||||
type: string
|
||||
type: object
|
||||
clusterAPI:
|
||||
description: KubeconfigSourceClusterAPI get kubeconfig from
|
||||
clusterAPI parent cluster
|
||||
properties:
|
||||
clusterNamespacedName:
|
||||
description: NamespacedName is a name combined with namespace
|
||||
to uniquely identify objects
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
timeout:
|
||||
type: string
|
||||
type: object
|
||||
filesystem:
|
||||
description: KubeconfigSourceFilesystem get kubeconfig from
|
||||
filesystem path
|
||||
properties:
|
||||
contextName:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
type: object
|
||||
type:
|
||||
description: KubeconfigSourceType type of source
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
parent:
|
||||
description: Parent is a key in ClusterMap.Map that identifies the
|
||||
name of the parent(management) cluster
|
||||
type: string
|
||||
required:
|
||||
- kubeconfigSources
|
||||
type: object
|
||||
description: Keys in this map MUST correspond to context names in kubeconfigs
|
||||
provided
|
||||
type: object
|
||||
metadata:
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -12,5 +12,4 @@ resources:
|
|||
- wait_bmh
|
||||
- wait_label_node
|
||||
- check_ingress_ctrl
|
||||
- merge_kubeconfig
|
||||
- wait_machines_ready
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
configMapGenerator:
|
||||
- name: merge-kubeconfig
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
files:
|
||||
- script=kubectl_merge_kubeconfig.sh
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
target-cluster:
|
||||
parent: ephemeral-cluster
|
||||
kubeconfigSources:
|
||||
- type: "filesystem"
|
||||
filesystem:
|
||||
path: ~/.airship/kubeconfig
|
||||
contextName: target-cluster
|
||||
- type: "bundle"
|
||||
bundle:
|
||||
contextName: target-cluster
|
||||
- type: "clusterAPI"
|
||||
clusterAPI:
|
||||
clusterNamespacedName:
|
||||
name: target-cluster
|
||||
namespace: target-infra
|
||||
ephemeral-cluster:
|
||||
kubeconfigSources:
|
||||
- type: "filesystem"
|
||||
filesystem:
|
||||
path: ~/.airship/kubeconfig
|
||||
contextName: ephemeral-cluster
|
||||
- type: "bundle"
|
||||
bundle:
|
||||
contextName: ephemeral-cluster
|
|
@ -78,6 +78,7 @@ metadata:
|
|||
name: clusterctl_move
|
||||
move-options:
|
||||
namespace: target-infra
|
||||
targetContext: target-cluster
|
||||
action: move
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
|
@ -608,19 +609,19 @@ metadata:
|
|||
airshipit.org/deploy-k8s: "false"
|
||||
spec:
|
||||
type: krm
|
||||
image: quay.io/airshipit/toolbox:latest
|
||||
image: quay.io/airshipit/kubeconfig-manager:latest
|
||||
hostNetwork: true
|
||||
envVars:
|
||||
- TGT_KUBECONFIG=/tmp-kubeconfig
|
||||
mounts:
|
||||
- type: bind
|
||||
src: ~/.airship/kubeconfig
|
||||
dst: /tmp-kubeconfig
|
||||
dst: /kubeconfig
|
||||
rw: true
|
||||
configRef:
|
||||
kind: ConfigMap
|
||||
name: merge-kubeconfig
|
||||
apiVersion: v1
|
||||
config: |
|
||||
action: get
|
||||
capiOptions:
|
||||
clusterName: target-cluster
|
||||
parentContext: ephemeral-cluster
|
||||
namespace: target-infra
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
|
@ -636,3 +637,21 @@ configRef:
|
|||
kind: ConfigMap
|
||||
name: wait_machines_ready
|
||||
apiVersion: v1
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
metadata:
|
||||
name: kubeconfig-decrypt
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
spec:
|
||||
type: krm
|
||||
image: quay.io/airshipit/kubeconfig-manager:latest
|
||||
hostNetwork: true
|
||||
mounts:
|
||||
- type: bind
|
||||
src: ~/.airship/kubeconfig
|
||||
dst: /kubeconfig
|
||||
rw: true
|
||||
config: |
|
||||
action: decrypt
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
resources:
|
||||
- phases.yaml
|
||||
- executors.yaml
|
||||
- cluster-map.yaml
|
||||
- ../function/clusterctl
|
||||
# Scripts for generic containers
|
||||
- ../function/phase-helpers
|
||||
|
|
|
@ -12,6 +12,17 @@ config:
|
|||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: Phase
|
||||
metadata:
|
||||
name: secret-get
|
||||
config:
|
||||
executorRef:
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
name: kubeconfig-decrypt
|
||||
documentEntryPoint: kubeconfig
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: Phase
|
||||
metadata:
|
||||
name: initinfra-networking-ephemeral
|
||||
clusterName: ephemeral-cluster
|
||||
|
@ -123,7 +134,7 @@ apiVersion: airshipit.org/v1alpha1
|
|||
kind: Phase
|
||||
metadata:
|
||||
name: clusterctl-move
|
||||
clusterName: target-cluster
|
||||
clusterName: ephemeral-cluster
|
||||
config:
|
||||
siteWideKubeconfig: true
|
||||
executorRef:
|
||||
|
@ -493,7 +504,6 @@ apiVersion: airshipit.org/v1alpha1
|
|||
kind: Phase
|
||||
metadata:
|
||||
name: kubectl-merge-kubeconfig
|
||||
clusterName: target-cluster
|
||||
config:
|
||||
executorRef:
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
target-cluster:
|
||||
parent: ephemeral-cluster
|
||||
kubeconfigSources:
|
||||
- type: "filesystem"
|
||||
filesystem:
|
||||
path: ~/.airship/kubeconfig
|
||||
contextName: target-cluster
|
||||
- type: "bundle"
|
||||
bundle:
|
||||
contextName: target-cluster
|
||||
- type: "clusterAPI"
|
||||
clusterAPI:
|
||||
clusterNamespacedName:
|
||||
name: target-cluster
|
||||
namespace: target-infra
|
|
@ -10,7 +10,6 @@ patchesJson6902:
|
|||
path: infrastructure-providers.json
|
||||
patchesStrategicMerge:
|
||||
- plan_patch.yaml
|
||||
- cluster_map_patch.yaml
|
||||
- executor_patch.yaml
|
||||
transformers:
|
||||
- ../../../function/clusterctl/replacements
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
resources:
|
||||
- ../kubeconfig
|
||||
- ../../../type/gating/phases
|
||||
- ../target/catalogues
|
||||
- catalogue.yaml
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ClusterMap represents cluster defined for this manifest
|
||||
type ClusterMap struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
// Keys in this map MUST correspond to context names in kubeconfigs provided
|
||||
Map map[string]*Cluster `json:"map,omitempty"`
|
||||
}
|
||||
|
||||
// Cluster uniquely identifies a cluster and its parent cluster
|
||||
type Cluster struct {
|
||||
// Parent is a key in ClusterMap.Map that identifies the name of the parent(management) cluster
|
||||
Parent string `json:"parent,omitempty"`
|
||||
// KubeconfigContext is the context in kubeconfig, default is equals to clusterMap key
|
||||
Sources []KubeconfigSource `json:"kubeconfigSources"`
|
||||
}
|
||||
|
||||
// KubeconfigSource describes source of the kubeconfig
|
||||
type KubeconfigSource struct {
|
||||
Type KubeconfigSourceType `json:"type"`
|
||||
FileSystem KubeconfigSourceFilesystem `json:"filesystem,omitempty"`
|
||||
Bundle KubeconfigSourceBundle `json:"bundle,omitempty"`
|
||||
ClusterAPI KubeconfigSourceClusterAPI `json:"clusterAPI,omitempty"`
|
||||
}
|
||||
|
||||
// KubeconfigSourceType type of source
|
||||
type KubeconfigSourceType string
|
||||
|
||||
const (
|
||||
// KubeconfigSourceTypeFilesystem is used when you want kubeconfig to be taken from local filesystem
|
||||
KubeconfigSourceTypeFilesystem KubeconfigSourceType = "filesystem"
|
||||
// KubeconfigSourceTypeBundle use config document bundle to get kubeconfig
|
||||
KubeconfigSourceTypeBundle KubeconfigSourceType = "bundle"
|
||||
// KubeconfigSourceTypeClusterAPI use ClusterAPI to get kubeconfig, parent cluster must be specified
|
||||
KubeconfigSourceTypeClusterAPI KubeconfigSourceType = "clusterAPI"
|
||||
)
|
||||
|
||||
// KubeconfigSourceFilesystem get kubeconfig from filesystem path
|
||||
type KubeconfigSourceFilesystem struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Context string `json:"contextName,omitempty"`
|
||||
}
|
||||
|
||||
// KubeconfigSourceClusterAPI get kubeconfig from clusterAPI parent cluster
|
||||
type KubeconfigSourceClusterAPI struct {
|
||||
NamespacedName `json:"clusterNamespacedName,omitempty"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// KubeconfigSourceBundle get kubeconfig from bundle
|
||||
type KubeconfigSourceBundle struct {
|
||||
Context string `json:"contextName,omitempty"`
|
||||
}
|
||||
|
||||
// NamespacedName is a name combined with namespace to uniquely identify objects
|
||||
type NamespacedName struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// DefaultClusterMap can be used to safely unmarshal ClusterMap object without nil pointers
|
||||
func DefaultClusterMap() *ClusterMap {
|
||||
return &ClusterMap{
|
||||
Map: make(map[string]*Cluster),
|
||||
}
|
||||
}
|
|
@ -27,6 +27,8 @@ type Clusterctl struct {
|
|||
|
||||
Providers []*Provider `json:"providers,omitempty"`
|
||||
Action ActionType `json:"action,omitempty"`
|
||||
Kubeconfig string `json:"kubeconfig,omitempty"`
|
||||
Context string `json:"context,omitempty"`
|
||||
InitOptions *InitOptions `json:"init-options,omitempty"`
|
||||
MoveOptions *MoveOptions `json:"move-options,omitempty"`
|
||||
// AdditionalComponentVariables are variables that will be available to clusterctl
|
||||
|
@ -98,7 +100,8 @@ const (
|
|||
type MoveOptions struct {
|
||||
// Namespace where the objects describing the workload cluster exists. If unspecified, the current
|
||||
// namespace will be used.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
TargetContext string `json:"targetContext,omitempty"`
|
||||
}
|
||||
|
||||
// DefaultClusterctl can be used to safely unmarshal Clusterctl object without nil pointers
|
||||
|
|
|
@ -47,7 +47,6 @@ func init() {
|
|||
&KubeConfig{},
|
||||
&KubernetesApply{},
|
||||
&IsoConfiguration{},
|
||||
&ClusterMap{},
|
||||
&ReplacementTransformer{},
|
||||
&Templater{},
|
||||
&BootConfiguration{},
|
||||
|
|
|
@ -376,66 +376,6 @@ func (in ChartSpec) DeepCopy() ChartSpec {
|
|||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||
*out = *in
|
||||
if in.Sources != nil {
|
||||
in, out := &in.Sources, &out.Sources
|
||||
*out = make([]KubeconfigSource, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
|
||||
func (in *Cluster) DeepCopy() *Cluster {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Cluster)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClusterMap) DeepCopyInto(out *ClusterMap) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
if in.Map != nil {
|
||||
in, out := &in.Map, &out.Map
|
||||
*out = make(map[string]*Cluster, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *Cluster
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(Cluster)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterMap.
|
||||
func (in *ClusterMap) DeepCopy() *ClusterMap {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClusterMap)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ClusterMap) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Clusterctl) DeepCopyInto(out *Clusterctl) {
|
||||
*out = *in
|
||||
|
@ -1052,70 +992,6 @@ func (in *KubeConfig) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeconfigSource) DeepCopyInto(out *KubeconfigSource) {
|
||||
*out = *in
|
||||
out.FileSystem = in.FileSystem
|
||||
out.Bundle = in.Bundle
|
||||
out.ClusterAPI = in.ClusterAPI
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigSource.
|
||||
func (in *KubeconfigSource) DeepCopy() *KubeconfigSource {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeconfigSource)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeconfigSourceBundle) DeepCopyInto(out *KubeconfigSourceBundle) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigSourceBundle.
|
||||
func (in *KubeconfigSourceBundle) DeepCopy() *KubeconfigSourceBundle {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeconfigSourceBundle)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeconfigSourceClusterAPI) DeepCopyInto(out *KubeconfigSourceClusterAPI) {
|
||||
*out = *in
|
||||
out.NamespacedName = in.NamespacedName
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigSourceClusterAPI.
|
||||
func (in *KubeconfigSourceClusterAPI) DeepCopy() *KubeconfigSourceClusterAPI {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeconfigSourceClusterAPI)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeconfigSourceFilesystem) DeepCopyInto(out *KubeconfigSourceFilesystem) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigSourceFilesystem.
|
||||
func (in *KubeconfigSourceFilesystem) DeepCopy() *KubeconfigSourceFilesystem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeconfigSourceFilesystem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubernetesApply) DeepCopyInto(out *KubernetesApply) {
|
||||
*out = *in
|
||||
|
@ -1241,21 +1117,6 @@ func (in *MoveOptions) DeepCopy() *MoveOptions {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespacedName) DeepCopyInto(out *NamespacedName) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedName.
|
||||
func (in *NamespacedName) DeepCopy() *NamespacedName {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespacedName)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Network) DeepCopyInto(out *Network) {
|
||||
*out = *in
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
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 clustermap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
)
|
||||
|
||||
// ErrParentNotFound returned when requested cluster is not defined or doesn't have a parent
|
||||
type ErrParentNotFound struct {
|
||||
Child string
|
||||
Map *v1alpha1.ClusterMap
|
||||
}
|
||||
|
||||
func (e ErrParentNotFound) Error() string {
|
||||
return fmt.Sprintf("failed to find a parent for cluster %s in cluster map %v", e.Child, e.Map)
|
||||
}
|
||||
|
||||
// ErrClusterNotInMap returned when requested cluster is not defined in cluster map
|
||||
type ErrClusterNotInMap struct {
|
||||
Child string
|
||||
Map *v1alpha1.ClusterMap
|
||||
}
|
||||
|
||||
func (e ErrClusterNotInMap) Error() string {
|
||||
return fmt.Sprintf("cluster '%s' is not defined in cluster map %v", e.Child, e.Map)
|
||||
}
|
||||
|
||||
// ErrClusterCircularDependency returned for circular dependencies
|
||||
type ErrClusterCircularDependency struct {
|
||||
Parent string
|
||||
Map *v1alpha1.ClusterMap
|
||||
}
|
||||
|
||||
func (e ErrClusterCircularDependency) Error() string {
|
||||
return fmt.Sprintf("%v contains cluster referenced as both parent and child: %s", e.Map, e.Parent)
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
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 clustermap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
)
|
||||
|
||||
// DefaultClusterAPIObjNamespace is a default namespace used for cluster-api cluster object
|
||||
const DefaultClusterAPIObjNamespace = "target-infra"
|
||||
|
||||
// WriteOptions has format in which we want to print the output(table/yaml/cluster name)
|
||||
type WriteOptions struct {
|
||||
Format string
|
||||
}
|
||||
|
||||
// ClusterMap interface that allows to list all clusters, find its parent, namespace,
|
||||
// check if dynamic kubeconfig is enabled.
|
||||
// TODO use typed cluster names
|
||||
type ClusterMap interface {
|
||||
ParentCluster(string) (string, error)
|
||||
ValidateClusterMap() error
|
||||
AllClusters() []string
|
||||
ClusterKubeconfigContext(string) (string, error)
|
||||
Sources(string) ([]v1alpha1.KubeconfigSource, error)
|
||||
Write(io.Writer, WriteOptions) error
|
||||
}
|
||||
|
||||
// clusterMap allows to view clusters and relationship between them
|
||||
type clusterMap struct {
|
||||
apiMap *v1alpha1.ClusterMap
|
||||
}
|
||||
|
||||
var _ ClusterMap = clusterMap{}
|
||||
|
||||
// NewClusterMap returns ClusterMap interface
|
||||
func NewClusterMap(cMap *v1alpha1.ClusterMap) ClusterMap {
|
||||
return clusterMap{apiMap: cMap}
|
||||
}
|
||||
|
||||
// ParentCluster finds a parent cluster for provided child
|
||||
func (cm clusterMap) ParentCluster(child string) (string, error) {
|
||||
currentCluster, exists := cm.apiMap.Map[child]
|
||||
if !exists {
|
||||
return "", ErrClusterNotInMap{Child: child, Map: cm.apiMap}
|
||||
}
|
||||
if currentCluster.Parent == "" {
|
||||
return "", ErrParentNotFound{Child: child, Map: cm.apiMap}
|
||||
}
|
||||
return currentCluster.Parent, nil
|
||||
}
|
||||
|
||||
// Validates a clustermap has valid parent-child map structure
|
||||
func (cm clusterMap) ValidateClusterMap() error {
|
||||
clusterMap := cm.AllClusters()
|
||||
for _, childCluster := range clusterMap {
|
||||
var parentClusters []string
|
||||
var currentChild string = childCluster
|
||||
for {
|
||||
currentCluster, _ := cm.apiMap.Map[currentChild]
|
||||
for _, c := range parentClusters {
|
||||
if c == currentCluster.Parent {
|
||||
// Quit on parent whos also child
|
||||
return ErrClusterCircularDependency{Parent: childCluster, Map: cm.apiMap}
|
||||
}
|
||||
}
|
||||
// Quit loop once top level of current cluster is reached
|
||||
if currentCluster.Parent == "" {
|
||||
break
|
||||
}
|
||||
parentClusters = append(parentClusters, currentCluster.Parent)
|
||||
currentChild = currentCluster.Parent
|
||||
}
|
||||
}
|
||||
|
||||
// Return success if there are no conflicts
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllClusters returns all clusters in a map
|
||||
func (cm clusterMap) AllClusters() []string {
|
||||
clusters := []string{}
|
||||
for k := range cm.apiMap.Map {
|
||||
clusters = append(clusters, k)
|
||||
}
|
||||
return clusters
|
||||
}
|
||||
|
||||
// ClusterKubeconfigContext returns name of the context in kubeconfig corresponding to a given cluster
|
||||
func (cm clusterMap) ClusterKubeconfigContext(clusterName string) (string, error) {
|
||||
_, exists := cm.apiMap.Map[clusterName]
|
||||
|
||||
if !exists {
|
||||
return "", ErrClusterNotInMap{Map: cm.apiMap, Child: clusterName}
|
||||
}
|
||||
|
||||
return clusterName, nil
|
||||
}
|
||||
|
||||
func (cm clusterMap) Sources(clusterName string) ([]v1alpha1.KubeconfigSource, error) {
|
||||
cluster, ok := cm.apiMap.Map[clusterName]
|
||||
if !ok {
|
||||
return nil, ErrClusterNotInMap{Child: clusterName, Map: cm.apiMap}
|
||||
}
|
||||
return cluster.Sources, nil
|
||||
}
|
||||
|
||||
// Write prints the cluster list in table/name output format
|
||||
func (cm clusterMap) Write(writer io.Writer, wo WriteOptions) error {
|
||||
if wo.Format == "table" {
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 8, 1, ' ', 0)
|
||||
fmt.Fprintf(w, "NAME\tKUBECONFIG CONTEXT\tPARENT CLUSTER\n")
|
||||
for clustername, cluster := range cm.apiMap.Map {
|
||||
kubeconfig, err := cm.ClusterKubeconfigContext(clustername)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n",
|
||||
clustername, kubeconfig, cluster.Parent)
|
||||
}
|
||||
w.Flush()
|
||||
} else if wo.Format == "name" {
|
||||
clusterList := cm.AllClusters()
|
||||
for _, clusterName := range clusterList {
|
||||
if _, err := writer.Write([]byte(clusterName + "\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
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 clustermap_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
)
|
||||
|
||||
func TestClusterMap(t *testing.T) {
|
||||
targetCluster := "target"
|
||||
ephemeraCluster := "ephemeral"
|
||||
workloadCluster := "workload"
|
||||
workloadClusterNoParent := "workload without parent"
|
||||
workloadClusterAPIRefName := "workload-cluster-api"
|
||||
workloadClusterAPIRefNamespace := "some-namespace"
|
||||
apiMap := &v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
targetCluster: {
|
||||
Parent: ephemeraCluster,
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeBundle,
|
||||
},
|
||||
},
|
||||
},
|
||||
ephemeraCluster: {},
|
||||
workloadCluster: {
|
||||
Parent: targetCluster,
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeClusterAPI,
|
||||
ClusterAPI: v1alpha1.KubeconfigSourceClusterAPI{
|
||||
NamespacedName: v1alpha1.NamespacedName{
|
||||
Name: workloadClusterAPIRefName,
|
||||
Namespace: workloadClusterAPIRefNamespace,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
workloadClusterNoParent: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeClusterAPI,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cMap := clustermap.NewClusterMap(apiMap)
|
||||
require.NotNil(t, cMap)
|
||||
|
||||
t.Run("ephemeral parent", func(t *testing.T) {
|
||||
parent, err := cMap.ParentCluster(targetCluster)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ephemeraCluster, parent)
|
||||
})
|
||||
|
||||
t.Run("no cluster found", func(t *testing.T) {
|
||||
parent, err := cMap.ParentCluster("does not exist")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", parent)
|
||||
})
|
||||
|
||||
t.Run("target parent", func(t *testing.T) {
|
||||
parent, err := cMap.ParentCluster(workloadCluster)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, targetCluster, parent)
|
||||
})
|
||||
|
||||
t.Run("ephemeral no parent", func(t *testing.T) {
|
||||
parent, err := cMap.ParentCluster(ephemeraCluster)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", parent)
|
||||
})
|
||||
|
||||
t.Run("Validate Circular Clustermap", func(t *testing.T) {
|
||||
// Create new map with circular dependency
|
||||
circularAPIMap := &v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{},
|
||||
}
|
||||
for key, value := range apiMap.Map {
|
||||
newValue := *value
|
||||
circularAPIMap.Map[key] = &newValue
|
||||
}
|
||||
circularAPIMap.Map["ephemeral"].Parent = "workload"
|
||||
cMapCircular := clustermap.NewClusterMap(circularAPIMap)
|
||||
err := cMapCircular.ValidateClusterMap()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Validate all Clustermaps", func(t *testing.T) {
|
||||
// Check child clusterID against map of parent clusterID map
|
||||
err := cMap.ValidateClusterMap()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("all clusters", func(t *testing.T) {
|
||||
clusters := cMap.AllClusters()
|
||||
assert.Len(t, clusters, 4)
|
||||
})
|
||||
|
||||
t.Run("kubeconfig context", func(t *testing.T) {
|
||||
kubeContext, err := cMap.ClusterKubeconfigContext(targetCluster)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, targetCluster, kubeContext)
|
||||
})
|
||||
|
||||
t.Run("kubeconfig context error", func(t *testing.T) {
|
||||
_, err := cMap.ClusterKubeconfigContext("does not exist")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("sources match", func(t *testing.T) {
|
||||
sources, err := cMap.Sources(workloadCluster)
|
||||
assert.NoError(t, err)
|
||||
expectedSources := apiMap.Map[workloadCluster].Sources
|
||||
assert.Equal(t, expectedSources, sources)
|
||||
})
|
||||
|
||||
t.Run("sources no cluster found", func(t *testing.T) {
|
||||
_, err := cMap.Sources("does not exist")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_clusterMap_Write(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
wr := bufio.NewWriter(&b)
|
||||
targetCluster := "target"
|
||||
ephemeraCluster := "ephemeral"
|
||||
apiMap := &v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
targetCluster: {
|
||||
Parent: ephemeraCluster,
|
||||
},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
wo clustermap.WriteOptions
|
||||
wantWriter string
|
||||
expectedOut string
|
||||
expectedErr string
|
||||
writer io.Writer
|
||||
}{
|
||||
{
|
||||
name: "success table",
|
||||
wo: clustermap.WriteOptions{Format: "table"},
|
||||
expectedOut: "NAME KUBECONFIG CONTEXT PARENT CLUSTER" +
|
||||
"\ntarget target ephemeral\n",
|
||||
writer: wr,
|
||||
},
|
||||
{
|
||||
name: "writer nil",
|
||||
wo: clustermap.WriteOptions{Format: "table"},
|
||||
writer: nil,
|
||||
expectedOut: "",
|
||||
},
|
||||
}
|
||||
rStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
os.Stdout = w
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cMap := clustermap.NewClusterMap(apiMap)
|
||||
err := cMap.Write(tt.writer, tt.wo)
|
||||
w.Close()
|
||||
if tt.expectedErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErr)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
out, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
require.Error(t, err)
|
||||
}
|
||||
os.Stdout = rStdout
|
||||
assert.Equal(t, tt.expectedOut, string(out))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
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 cluster
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/phase"
|
||||
)
|
||||
|
||||
// GetKubeconfigCommand holds options for get kubeconfig command
|
||||
type GetKubeconfigCommand struct {
|
||||
ClusterNames []string
|
||||
File string
|
||||
Merge bool
|
||||
}
|
||||
|
||||
// RunE creates new kubeconfig interface object from secret, options hold the writer and merge(bool)
|
||||
// to merge the kubeconfig. Writer in options is ignored if a file is provided.
|
||||
func (cmd *GetKubeconfigCommand) RunE(cfgFactory config.Factory, writer io.Writer) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
helper, err := phase.NewHelper(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cMap, err := helper.ClusterMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var siteWide bool
|
||||
if len(cmd.ClusterNames) == 0 {
|
||||
siteWide = true
|
||||
}
|
||||
|
||||
kubeconf := kubeconfig.NewBuilder().
|
||||
WithBundle(helper.PhaseConfigBundle()).
|
||||
WithClusterMap(cMap).
|
||||
WithClusterNames(cmd.ClusterNames...).
|
||||
WithTempRoot(helper.WorkDir()).
|
||||
SiteWide(siteWide).
|
||||
Build()
|
||||
|
||||
if cmd.File != "" {
|
||||
return kubeconf.WriteFile(cmd.File, kubeconfig.WriteOptions{Merge: cmd.Merge})
|
||||
}
|
||||
return kubeconf.Write(writer)
|
||||
}
|
|
@ -20,8 +20,9 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/util"
|
||||
)
|
||||
|
||||
// ContextOptions holds all configurable options for context
|
||||
|
@ -104,7 +105,7 @@ func (o *ContextOptions) Print(cfg *Config, w io.Writer) error {
|
|||
}
|
||||
fmt.Fprintf(w, string(data))
|
||||
case "table":
|
||||
out := printers.GetNewTabWriter(w)
|
||||
out := util.GetNewTabWriter(w)
|
||||
defer out.Flush()
|
||||
|
||||
toPrint := []string{}
|
||||
|
|
|
@ -36,16 +36,6 @@ import (
|
|||
|
||||
const (
|
||||
singleSecretBundleOutput = `---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
testCluster: {}
|
||||
...
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
|
|
|
@ -1,316 +0,0 @@
|
|||
/*
|
||||
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 kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/fs"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/utils"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
// NewBuilder returns instance of kubeconfig builder.
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{
|
||||
siteKubeconf: emptyConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
// Builder is an object that allows to build a kubeconfig based on various provided sources
|
||||
// such as path to kubeconfig, path to bundle that should contain kubeconfig and parent cluster
|
||||
type Builder struct {
|
||||
siteWide bool
|
||||
clusterNames []string
|
||||
root string
|
||||
|
||||
bundle document.Bundle
|
||||
client corev1.CoreV1Interface
|
||||
clusterMap clustermap.ClusterMap
|
||||
fs fs.FileSystem
|
||||
siteKubeconf *api.Config
|
||||
}
|
||||
|
||||
// WithBundle allows to set document.Bundle object that should contain kubeconfig api object
|
||||
func (b *Builder) WithBundle(bundle document.Bundle) *Builder {
|
||||
b.bundle = bundle
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClusterMap allows to set a parent cluster, that can be used to extract kubeconfig for target cluster
|
||||
func (b *Builder) WithClusterMap(cMap clustermap.ClusterMap) *Builder {
|
||||
b.clusterMap = cMap
|
||||
return b
|
||||
}
|
||||
|
||||
// WithClusterNames allows to reach to a cluster to download kubeconfig from there
|
||||
func (b *Builder) WithClusterNames(clusterNames ...string) *Builder {
|
||||
b.clusterNames = clusterNames
|
||||
return b
|
||||
}
|
||||
|
||||
// WithTempRoot allows to set temp root for kubeconfig
|
||||
func (b *Builder) WithTempRoot(root string) *Builder {
|
||||
b.root = root
|
||||
return b
|
||||
}
|
||||
|
||||
// WithFilesystem allows to set filesystem
|
||||
func (b *Builder) WithFilesystem(fs fs.FileSystem) *Builder {
|
||||
b.fs = fs
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCoreV1Client allows to set core v1 client, use for unit tests only
|
||||
func (b *Builder) WithCoreV1Client(c corev1.CoreV1Interface) *Builder {
|
||||
b.client = c
|
||||
return b
|
||||
}
|
||||
|
||||
// SiteWide allows to build kubeconfig for the entire site.
|
||||
// If set to true ClusterName will be ignored, since all clusters are requested.
|
||||
func (b *Builder) SiteWide(t bool) *Builder {
|
||||
b.siteWide = t
|
||||
return b
|
||||
}
|
||||
|
||||
// Build site kubeconfig, ignores, but logs, errors that happen when building individual
|
||||
// kubeconfigs. We need this behavior because, some clusters may not yet be deployed
|
||||
// and their kubeconfig is inaccessible yet, but will be accessible at later phases
|
||||
// If builder can't build kubeconfig for specific cluster, its context will not be present
|
||||
// in final kubeconfig. User of kubeconfig, will receive error stating that context doesn't exist
|
||||
// To request site-wide kubeconfig use builder method SiteWide(true).
|
||||
// To request a single cluster kubeconfig use methods WithClusterName("my-cluster").SiteWide(false)
|
||||
// ClusterName is ignored if SiteWide(true) is used.
|
||||
func (b *Builder) Build() Interface {
|
||||
return NewKubeConfig(b.build, InjectFileSystem(b.fs), InjectTempRoot(b.root))
|
||||
}
|
||||
|
||||
func (b *Builder) build() ([]byte, error) {
|
||||
if err := b.buildKubeconfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clientcmd.Write(*b.siteKubeconf)
|
||||
}
|
||||
|
||||
func (b *Builder) buildKubeconfig() error {
|
||||
log.Debugf("Getting requested kubeconfig")
|
||||
for _, clusterID := range (map[bool][]string{true: b.clusterMap.AllClusters(), false: b.clusterNames})[b.siteWide] {
|
||||
log.Debugf("Getting kubeconfig for cluster '%s'", clusterID)
|
||||
// buildOne merges context into site kubeconfig
|
||||
ctx, _, err := b.buildOne(clusterID)
|
||||
if !b.siteWide && len(b.clusterNames) == 1 {
|
||||
b.siteKubeconf.CurrentContext = ctx
|
||||
}
|
||||
if IsErrAllSourcesFailedErr(err) {
|
||||
log.Debugf("All kubeconfig sources failed for cluster '%s', error '%v', skipping it",
|
||||
clusterID, err)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Builder) buildOne(clusterID string) (string, *api.Config, error) {
|
||||
destContext, err := b.clusterMap.ClusterKubeconfigContext(clusterID)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// use already built kubeconfig context, to avoid doing work multiple times
|
||||
built, oneKubeconf := b.alreadyBuilt(destContext)
|
||||
if built {
|
||||
log.Debugf("Kubeconfig for cluster '%s' is already built, using it", clusterID)
|
||||
return destContext, oneKubeconf, nil
|
||||
}
|
||||
|
||||
sources, err := b.clusterMap.Sources(clusterID)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
for _, source := range sources {
|
||||
oneKubeconf, sourceErr := b.trySource(clusterID, destContext, source)
|
||||
if sourceErr == nil {
|
||||
// Merge source context into site kubeconfig
|
||||
log.Debugf("Merging kubecontext for cluster '%s', into site kubeconfig", clusterID)
|
||||
if err = mergeContextAPI(destContext, destContext, b.siteKubeconf, oneKubeconf); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return destContext, oneKubeconf, err
|
||||
}
|
||||
// if error, log it and ignore it. missing problem with one kubeconfig should not
|
||||
// effect other clusters, which don't depend on it. If they do depend on it, their calls
|
||||
// will fail because the context will be missing. Combination with a log message will make
|
||||
// it clear where the problem is.
|
||||
log.Debugf("Received error while trying kubeconfig source for cluster '%s', source type '%s', error '%v'",
|
||||
clusterID, source.Type, sourceErr)
|
||||
}
|
||||
// return empty not nil kubeconfig without error.
|
||||
return "", nil, &ErrAllSourcesFailed{ClusterName: clusterID}
|
||||
}
|
||||
|
||||
func (b *Builder) trySource(clusterID, dstContext string, source v1alpha1.KubeconfigSource) (*api.Config, error) {
|
||||
var getter KubeSourceFunc
|
||||
// TODO add sourceContext defaults
|
||||
var sourceContext string
|
||||
switch source.Type {
|
||||
case v1alpha1.KubeconfigSourceTypeFilesystem:
|
||||
getter = FromFile(source.FileSystem.Path, b.fs)
|
||||
sourceContext = source.FileSystem.Context
|
||||
case v1alpha1.KubeconfigSourceTypeBundle:
|
||||
getter = FromBundle(b.bundle)
|
||||
sourceContext = source.Bundle.Context
|
||||
case v1alpha1.KubeconfigSourceTypeClusterAPI:
|
||||
getter = b.fromClusterAPI(clusterID, source.ClusterAPI)
|
||||
default:
|
||||
// TODO add validation for fast fails to clustermap interface instead of this
|
||||
return nil, &ErrUnknownKubeconfigSourceType{Type: string(source.Type)}
|
||||
}
|
||||
kubeBytes, err := getter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return extractContext(dstContext, sourceContext, kubeBytes)
|
||||
}
|
||||
|
||||
func (b *Builder) fromClusterAPI(clusterName string, ref v1alpha1.KubeconfigSourceClusterAPI) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
log.Debugf("Getting kubeconfig from cluster API for cluster '%s'", clusterName)
|
||||
parentCluster, err := b.clusterMap.ParentCluster(clusterName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parentContext, parentKubeconf, err := b.buildOne(parentCluster)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parentKubeconfig := NewKubeConfig(FromConfig(parentKubeconf), InjectFileSystem(b.fs))
|
||||
|
||||
f, cleanup, err := parentKubeconfig.GetFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
if b.client == nil {
|
||||
clientSet, err := utils.FactoryFromKubeConfig(f, parentContext, utils.SetTimeout("30s")).KubernetesClientSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.client = clientSet.CoreV1()
|
||||
}
|
||||
|
||||
log.Debugf("Getting child kubeconfig from parent, parent context '%s', parent kubeconfig '%s'",
|
||||
parentContext, f)
|
||||
return FromSecret(b.client, &v1alpha1.GetKubeconfigOptions{
|
||||
Timeout: ref.Timeout,
|
||||
ManagedClusterNamespace: ref.Namespace,
|
||||
ManagedClusterName: ref.Name,
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) alreadyBuilt(clusterContext string) (bool, *api.Config) {
|
||||
kubeconfBytes, err := clientcmd.Write(*b.siteKubeconf)
|
||||
if err != nil {
|
||||
log.Debugf("Received error when converting kubeconfig to bytes, ignoring kubeconfig. Error: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// resulting and existing context names must be the same, otherwise error will be returned
|
||||
clusterKubeconfig, err := extractContext(clusterContext, clusterContext, kubeconfBytes)
|
||||
if err != nil {
|
||||
log.Debugf("Received error when extracting context, ignoring kubeconfig. Error: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, clusterKubeconfig
|
||||
}
|
||||
|
||||
func extractContext(destContext, sourceContext string, src []byte) (*api.Config, error) {
|
||||
srcKubeconf, err := clientcmd.Load(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dstKubeconf := emptyConfig()
|
||||
return dstKubeconf, mergeContextAPI(destContext, sourceContext, dstKubeconf, srcKubeconf)
|
||||
}
|
||||
|
||||
// merges two kubeconfigs
|
||||
func mergeContextAPI(destContext, sourceContext string, dst, src *api.Config) error {
|
||||
if len(src.Contexts) > 1 && sourceContext == "" {
|
||||
// When more than one context, we don't know which to choose
|
||||
return &ErrKubeconfigMergeFailed{
|
||||
Message: "kubeconfig has multiple contexts, don't know which to choose, " +
|
||||
"please specify contextName in clusterMap cluster kubeconfig source",
|
||||
}
|
||||
}
|
||||
|
||||
var context *api.Context
|
||||
context, exists := src.Contexts[sourceContext]
|
||||
switch {
|
||||
case exists:
|
||||
case sourceContext == "" && len(src.Contexts) == 1:
|
||||
for _, context = range src.Contexts {
|
||||
log.Debugf("Using context '%v' to merge kubeconfig", context)
|
||||
}
|
||||
default:
|
||||
return &ErrKubeconfigMergeFailed{
|
||||
Message: fmt.Sprintf("source context '%s' does not exist in source kubeconfig", sourceContext),
|
||||
}
|
||||
}
|
||||
dst.Contexts[destContext] = context
|
||||
|
||||
// TODO design logic to make authinfo keys unique, they can overlap, or human error can occur
|
||||
user, exists := src.AuthInfos[context.AuthInfo]
|
||||
if !exists {
|
||||
return &ErrKubeconfigMergeFailed{
|
||||
Message: fmt.Sprintf("user '%s' does not exist in source kubeconfig", context.AuthInfo),
|
||||
}
|
||||
}
|
||||
dst.AuthInfos[context.AuthInfo] = user
|
||||
|
||||
// TODO design logic to make cluster keys unique, they can overlap, or human error can occur
|
||||
cluster, exists := src.Clusters[context.Cluster]
|
||||
if !exists {
|
||||
return &ErrKubeconfigMergeFailed{
|
||||
Message: fmt.Sprintf("cluster '%s' does not exist in source kubeconfig", context.Cluster),
|
||||
}
|
||||
}
|
||||
dst.Clusters[context.Cluster] = cluster
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func emptyConfig() *api.Config {
|
||||
return &api.Config{
|
||||
Contexts: make(map[string]*api.Context),
|
||||
AuthInfos: make(map[string]*api.AuthInfo),
|
||||
Clusters: make(map[string]*api.Cluster),
|
||||
}
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
/*
|
||||
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 kubeconfig_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/fs"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
testfs "opendev.org/airship/airshipctl/testutil/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
testKubeconfigString = `apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: c29tZWNlcnQK
|
||||
server: https://10.23.25.101:6443
|
||||
name: child_cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: child_cluster
|
||||
user: child_user
|
||||
name: child
|
||||
current-context: dummy_cluster
|
||||
preferences: {}
|
||||
users:
|
||||
- name: child_user
|
||||
user:
|
||||
client-certificate-data: c29tZWNlcnQK
|
||||
client-key-data: c29tZWNlcnQK`
|
||||
testKubeconfigStringSecond = `apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: c29tZWNlcnQK
|
||||
server: https://10.23.25.101:6443
|
||||
name: parent_cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: parent_cluster
|
||||
user: parent_admin
|
||||
name: parent-context
|
||||
current-context: dummy_cluster
|
||||
preferences: {}
|
||||
users:
|
||||
- name: parent_admin
|
||||
user:
|
||||
client-certificate-data: c29tZWNlcnQK
|
||||
client-key-data: c29tZWNlcnQK`
|
||||
)
|
||||
|
||||
func TestBuilderClusterctl(t *testing.T) {
|
||||
childClusterID := "child"
|
||||
parentClusterID := "parent"
|
||||
parentParentClusterID := "parent-parent"
|
||||
// these are values in kubeconfig.cluster
|
||||
parentCluster := "parent_cluster"
|
||||
parentParentCluster := "parent_parent_cluster"
|
||||
childCluster := "child_cluster"
|
||||
parentUser := "parent_admin"
|
||||
parentParentUser := "parent_parent_admin"
|
||||
childUser := "child_user"
|
||||
testBundle, err := document.NewBundleByPath("testdata")
|
||||
require.NoError(t, err)
|
||||
kubeconfigPath := filepath.Join("testdata", "kubeconfig-12341234")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
errString string
|
||||
requestedClusterName string
|
||||
tempRoot string
|
||||
siteWide bool
|
||||
|
||||
expectedContexts, expectedClusters, expectedAuthInfos []string
|
||||
clusterMap clustermap.ClusterMap
|
||||
client corev1.CoreV1Interface
|
||||
fs fs.FileSystem
|
||||
}{
|
||||
{
|
||||
name: "success cluster-api not reachable",
|
||||
expectedContexts: []string{parentClusterID},
|
||||
expectedClusters: []string{parentParentCluster},
|
||||
expectedAuthInfos: []string{parentParentUser},
|
||||
siteWide: true,
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
childClusterID: {
|
||||
Parent: parentClusterID,
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeClusterAPI,
|
||||
},
|
||||
},
|
||||
},
|
||||
parentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeBundle,
|
||||
Bundle: v1alpha1.KubeconfigSourceBundle{
|
||||
Context: "parent_parent_context",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "success two clusters",
|
||||
expectedContexts: []string{parentClusterID, parentParentClusterID},
|
||||
expectedClusters: []string{"dummycluster_ephemeral", parentParentCluster},
|
||||
expectedAuthInfos: []string{"kubernetes-admin", parentParentUser},
|
||||
siteWide: true,
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
parentParentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeBundle,
|
||||
Bundle: v1alpha1.KubeconfigSourceBundle{
|
||||
Context: "parent_parent_context",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
parentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeFilesystem,
|
||||
FileSystem: v1alpha1.KubeconfigSourceFilesystem{
|
||||
Path: "testdata/kubeconfig",
|
||||
Context: "dummy_cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "success three clusters cluster-api",
|
||||
expectedContexts: []string{parentClusterID, childClusterID, parentParentClusterID},
|
||||
expectedClusters: []string{parentCluster, parentParentCluster, childCluster},
|
||||
expectedAuthInfos: []string{parentUser, parentParentUser, childUser},
|
||||
siteWide: true,
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
childClusterID: {
|
||||
Parent: parentClusterID,
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeClusterAPI,
|
||||
ClusterAPI: v1alpha1.KubeconfigSourceClusterAPI{
|
||||
NamespacedName: v1alpha1.NamespacedName{
|
||||
Name: childClusterID,
|
||||
Namespace: "target-infra",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
parentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeClusterAPI,
|
||||
ClusterAPI: v1alpha1.KubeconfigSourceClusterAPI{
|
||||
NamespacedName: v1alpha1.NamespacedName{
|
||||
Name: parentClusterID,
|
||||
Namespace: "target-infra",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Parent: parentParentClusterID,
|
||||
},
|
||||
parentParentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeBundle,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
tempRoot: "testdata",
|
||||
fs: testfs.MockFileSystem{
|
||||
MockRemoveAll: func() error { return nil },
|
||||
MockTempFile: func(s1, s2 string) (fs.File, error) {
|
||||
return testfs.TestFile{
|
||||
MockName: func() string { return kubeconfigPath },
|
||||
MockWrite: func([]byte) (int, error) { return 0, nil },
|
||||
MockClose: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
client: func() MockCoreV1Interface {
|
||||
ms := &SecretMockInterface{
|
||||
Mock: mock.Mock{},
|
||||
}
|
||||
ms.On("Get", parentClusterID+"-kubeconfig", metav1.GetOptions{}).
|
||||
Once().Return(&apiv1.Secret{Data: map[string][]byte{"value": []byte(testKubeconfigString)}}, nil)
|
||||
ms.On("Get", childClusterID+"-kubeconfig", metav1.GetOptions{}).
|
||||
Once().Return(&apiv1.Secret{Data: map[string][]byte{"value": []byte(testKubeconfigStringSecond)}}, nil)
|
||||
mc := MockCoreV1Interface{MockSecrets: func(s string) corev1.SecretInterface {
|
||||
return ms
|
||||
}}
|
||||
return mc
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "error requested cluster doesn't exist",
|
||||
errString: "is not defined in cluster map",
|
||||
requestedClusterName: "non-existent-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
parentParentClusterID: {
|
||||
Sources: []v1alpha1.KubeconfigSource{
|
||||
{
|
||||
Type: v1alpha1.KubeconfigSourceTypeBundle,
|
||||
Bundle: v1alpha1.KubeconfigSourceBundle{
|
||||
Context: "parent_parent_context",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kube := kubeconfig.NewBuilder().
|
||||
WithClusterMap(tt.clusterMap).
|
||||
WithClusterNames(tt.requestedClusterName).
|
||||
WithBundle(testBundle).
|
||||
WithTempRoot(tt.tempRoot).
|
||||
WithCoreV1Client(tt.client).
|
||||
WithFilesystem(tt.fs).
|
||||
SiteWide(tt.siteWide).
|
||||
Build()
|
||||
require.NotNil(t, kube)
|
||||
filePath, cleanup, err := kube.GetFile()
|
||||
// This is needed to avoid leftovers on test failures
|
||||
if cleanup != nil {
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
if tt.errString != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.errString)
|
||||
assert.Equal(t, "", filePath)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, "", filePath)
|
||||
assert.NotNil(t, cleanup)
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err := kube.Write(buf)
|
||||
require.NoError(t, err)
|
||||
compareResults(t, tt.expectedContexts, tt.expectedClusters, tt.expectedAuthInfos, buf.Bytes())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func compareResults(t *testing.T, contexts, clusters, authInfos []string, kubeconfBytes []byte) {
|
||||
t.Helper()
|
||||
resultKubeconf, err := clientcmd.Load(kubeconfBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, resultKubeconf.Contexts, len(contexts))
|
||||
for _, name := range contexts {
|
||||
assert.Contains(t, resultKubeconf.Contexts, name)
|
||||
}
|
||||
|
||||
assert.Len(t, resultKubeconf.AuthInfos, len(authInfos))
|
||||
for _, name := range authInfos {
|
||||
assert.Contains(t, resultKubeconf.AuthInfos, name)
|
||||
}
|
||||
|
||||
assert.Len(t, resultKubeconf.Clusters, len(clusters))
|
||||
for _, name := range clusters {
|
||||
assert.Contains(t, resultKubeconf.Clusters, name)
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
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 kubeconfig
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrAllSourcesFailed returned when kubeconfig path is not specified
|
||||
type ErrAllSourcesFailed struct {
|
||||
ClusterName string
|
||||
}
|
||||
|
||||
func (e *ErrAllSourcesFailed) Error() string {
|
||||
return fmt.Sprintf("all kubeconfig sources failed for cluster '%s'", e.ClusterName)
|
||||
}
|
||||
|
||||
// ErrKubeconfigMergeFailed is returned when builder doesn't know which context to merge
|
||||
type ErrKubeconfigMergeFailed struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *ErrKubeconfigMergeFailed) Error() string {
|
||||
return fmt.Sprintf("failed merging kubeconfig: %s", e.Message)
|
||||
}
|
||||
|
||||
// IsErrAllSourcesFailedErr returns true if error is of type ErrAllSourcesFailedErr
|
||||
func IsErrAllSourcesFailedErr(err error) bool {
|
||||
_, ok := err.(*ErrAllSourcesFailed)
|
||||
return ok
|
||||
}
|
||||
|
||||
// ErrUnknownKubeconfigSourceType returned type of kubeconfig source is unknown
|
||||
type ErrUnknownKubeconfigSourceType struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
func (e *ErrUnknownKubeconfigSourceType) Error() string {
|
||||
return fmt.Sprintf("unknown source type %s", e.Type)
|
||||
}
|
||||
|
||||
// ErrClusterNameEmpty returned when cluster name is not provided
|
||||
type ErrClusterNameEmpty struct {
|
||||
}
|
||||
|
||||
func (e ErrClusterNameEmpty) Error() string {
|
||||
return "cluster name is not defined"
|
||||
}
|
||||
|
||||
// ErrMalformedKubeconfig error returned if kubeconfig is empty
|
||||
type ErrMalformedKubeconfig struct {
|
||||
ClusterName string
|
||||
}
|
||||
|
||||
func (e ErrMalformedKubeconfig) Error() string {
|
||||
return fmt.Sprintf("retrieved kubeconfig for cluster '%s' is empty", e.ClusterName)
|
||||
}
|
|
@ -1,307 +0,0 @@
|
|||
/*
|
||||
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 kubeconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/fs"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// Prefix is a prefix that is added when writing temporary kubeconfig files
|
||||
Prefix = "kubeconfig-"
|
||||
defaultTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// Interface provides a uniform way to interact with kubeconfig file
|
||||
type Interface interface {
|
||||
// GetFile returns path to kubeconfig file and a function to remove it
|
||||
// if error is returned cleanup is not needed
|
||||
GetFile() (string, Cleanup, error)
|
||||
// Write will write kubeconfig to the provided writer
|
||||
Write(w io.Writer) error
|
||||
// WriteFile will write kubeconfig data to specified path
|
||||
// WriteOptions holds additional option when writing kubeconfig to file
|
||||
WriteFile(path string, options WriteOptions) error
|
||||
// WriteTempFile writes a file a temporary file, returns path to it, cleanup function and error
|
||||
// it is responsibility of the caller to use the cleanup function to make sure that there are no leftovers
|
||||
WriteTempFile(dumpRoot string) (string, Cleanup, error)
|
||||
}
|
||||
|
||||
var _ Interface = &kubeConfig{}
|
||||
|
||||
type kubeConfig struct {
|
||||
path string
|
||||
dumpRoot string
|
||||
savedByes []byte
|
||||
|
||||
fileSystem fs.FileSystem
|
||||
sourceFunc KubeSourceFunc
|
||||
}
|
||||
|
||||
// WriteOptions holds additional option while writing kubeconfig to the file
|
||||
type WriteOptions struct {
|
||||
Merge bool
|
||||
}
|
||||
|
||||
// NewKubeConfig serves as a constructor for kubeconfig Interface
|
||||
// first argument is a function that should return bytes with kubeconfig and error
|
||||
// see FromByte() FromAPIalphaV1() FromFile() functions or extend with your own
|
||||
// second argument are options that can be used to inject various supported options into it
|
||||
// see InjectTempRoot(), InjectFileSystem(), InjectFilePath() functions for more info
|
||||
func NewKubeConfig(source KubeSourceFunc, options ...Option) Interface {
|
||||
kf := &kubeConfig{}
|
||||
for _, o := range options {
|
||||
o(kf)
|
||||
}
|
||||
kf.sourceFunc = source
|
||||
if kf.fileSystem == nil {
|
||||
kf.fileSystem = fs.NewDocumentFs()
|
||||
}
|
||||
return kf
|
||||
}
|
||||
|
||||
// Option is a function that allows to modify kubeConfig object
|
||||
type Option func(*kubeConfig)
|
||||
|
||||
// KubeSourceFunc is a function which returns bytes array to construct new kubeConfig object
|
||||
type KubeSourceFunc func() ([]byte, error)
|
||||
|
||||
// Cleanup is a function which cleans up kubeconfig file from filesystem
|
||||
type Cleanup func()
|
||||
|
||||
// FromByte returns KubeSource type, uses plain bytes array as source to construct kubeconfig object
|
||||
func FromByte(b []byte) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
// FromAPIalphaV1 returns KubeSource type, uses API Config array as source to construct kubeconfig object
|
||||
func FromAPIalphaV1(apiObj *v1alpha1.KubeConfig) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
return yaml.Marshal(apiObj.Config)
|
||||
}
|
||||
}
|
||||
|
||||
// FromSecret returns KubeSource type, uses client interface to kubernetes cluster
|
||||
func FromSecret(c corev1.CoreV1Interface, o *v1alpha1.GetKubeconfigOptions) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
if o.ManagedClusterName == "" {
|
||||
return nil, ErrClusterNameEmpty{}
|
||||
}
|
||||
if o.ManagedClusterNamespace == "" {
|
||||
o.ManagedClusterNamespace = "default"
|
||||
}
|
||||
|
||||
data, exist, secretName := new([]byte), new(bool), fmt.Sprintf("%s-kubeconfig", o.ManagedClusterName)
|
||||
fn := func() (bool, error) {
|
||||
secret, err := c.Secrets(o.ManagedClusterNamespace).Get(context.Background(), secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Printf("get kubeconfig from secret failed, retrying, reason: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if *data, *exist = secret.Data["value"]; *exist && len(*data) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return true, ErrMalformedKubeconfig{ClusterName: o.ManagedClusterName}
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(o.Timeout)
|
||||
if err != nil || duration == 0 {
|
||||
duration = defaultTimeout
|
||||
}
|
||||
|
||||
if err = wait.PollImmediate(time.Second, duration, fn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return *data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// FromFile returns KubeSource type, uses path to kubeconfig on FS as source to construct kubeconfig object
|
||||
func FromFile(path string, fSys fs.FileSystem) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
expandedPath := util.ExpandTilde(path)
|
||||
if fSys == nil {
|
||||
fSys = fs.NewDocumentFs()
|
||||
}
|
||||
return fSys.ReadFile(expandedPath)
|
||||
}
|
||||
}
|
||||
|
||||
// FromBundle returns KubeSource type, uses path to document bundle to find kubeconfig
|
||||
func FromBundle(bundle document.Bundle) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
config := &v1alpha1.KubeConfig{}
|
||||
selector, err := document.NewSelector().ByObject(config, v1alpha1.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc, err := bundle.SelectOne(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := doc.ToAPIObject(config, v1alpha1.Scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return yaml.Marshal(config.Config)
|
||||
}
|
||||
}
|
||||
|
||||
// FromConfig returns KubeSource type, write passed config as bytes
|
||||
func FromConfig(cfg *api.Config) KubeSourceFunc {
|
||||
return func() ([]byte, error) {
|
||||
return clientcmd.Write(*cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// InjectFileSystem sets fileSystem to be used, mostly to be used for tests
|
||||
func InjectFileSystem(fSys fs.FileSystem) Option {
|
||||
return func(k *kubeConfig) {
|
||||
k.fileSystem = fSys
|
||||
}
|
||||
}
|
||||
|
||||
// InjectTempRoot sets root for temporary file system, if not set default OS temp dir will be used
|
||||
func InjectTempRoot(dumpRoot string) Option {
|
||||
return func(k *kubeConfig) {
|
||||
k.dumpRoot = dumpRoot
|
||||
}
|
||||
}
|
||||
|
||||
// InjectFilePath enables setting kubeconfig path, useful when you have kubeconfig
|
||||
// from the actual filesystem, if this option is used, please also make sure that
|
||||
// FromFile option is also used as a first argument in NewKubeConfig function
|
||||
func InjectFilePath(path string, fSys fs.FileSystem) Option {
|
||||
return func(k *kubeConfig) {
|
||||
k.path = path
|
||||
k.fileSystem = fSys
|
||||
}
|
||||
}
|
||||
|
||||
func (k *kubeConfig) WriteFile(path string, options WriteOptions) error {
|
||||
var data []byte
|
||||
var err error
|
||||
if options.Merge && path != "" {
|
||||
data, err = k.mergedBytes(path)
|
||||
} else {
|
||||
data, err = k.bytes()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return k.fileSystem.WriteFile(path, data)
|
||||
}
|
||||
|
||||
func (k *kubeConfig) Write(w io.Writer) (err error) {
|
||||
data, err := k.bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteTempFile implements kubeconfig Interface
|
||||
func (k *kubeConfig) WriteTempFile(root string) (string, Cleanup, error) {
|
||||
data, err := k.bytes()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
file, err := k.fileSystem.TempFile(root, Prefix)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write temporary file, error %v", err)
|
||||
return "", nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
fName := file.Name()
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
// delete the temp file that was created and return write error
|
||||
cleanup(fName, k.fileSystem)()
|
||||
return "", nil, err
|
||||
}
|
||||
return fName, cleanup(fName, k.fileSystem), nil
|
||||
}
|
||||
|
||||
func (k *kubeConfig) bytes() ([]byte, error) {
|
||||
var err error
|
||||
if len(k.savedByes) == 0 {
|
||||
k.savedByes, err = k.sourceFunc()
|
||||
}
|
||||
return k.savedByes, err
|
||||
}
|
||||
|
||||
// mergedBytes takes the file path and return byte data of the kubeconfig file to be written
|
||||
func (k *kubeConfig) mergedBytes(path string) ([]byte, error) {
|
||||
kFile, cleanup, err := k.WriteTempFile(k.dumpRoot)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
rules := clientcmd.ClientConfigLoadingRules{
|
||||
Precedence: []string{path, kFile},
|
||||
}
|
||||
mergedConfig, err := rules.Load()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return clientcmd.Write(*mergedConfig)
|
||||
}
|
||||
|
||||
// GetFile checks if path to kubeconfig is already set and returns it no cleanup is necessary,
|
||||
// and Cleanup() method will do nothing.
|
||||
// If path is not set kubeconfig will be written to temporary file system, returned path will
|
||||
// point to it and Cleanup() function will remove this file from the filesystem.
|
||||
func (k *kubeConfig) GetFile() (string, Cleanup, error) {
|
||||
if k.path != "" {
|
||||
return k.path, func() {}, nil
|
||||
}
|
||||
return k.WriteTempFile(k.dumpRoot)
|
||||
}
|
||||
|
||||
func cleanup(path string, fSys fs.FileSystem) Cleanup {
|
||||
if path == "" {
|
||||
return func() {}
|
||||
}
|
||||
return func() {
|
||||
if err := fSys.RemoveAll(path); err != nil {
|
||||
log.Fatalf("Failed to cleanup kubeconfig file %s, error: %v", path, err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,605 +0,0 @@
|
|||
/*
|
||||
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 kubeconfig_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
kustfs "sigs.k8s.io/kustomize/api/filesys"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/fs"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
testfs "opendev.org/airship/airshipctl/testutil/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
testValidKubeconfig = `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: ca-data
|
||||
server: https://10.0.1.7:6443
|
||||
name: kubernetes_target
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes_target
|
||||
user: kubernetes-admin
|
||||
name: kubernetes-admin@kubernetes
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: cert-data
|
||||
client-key-data: client-keydata
|
||||
`
|
||||
testValidKubeconfigTwo = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.0.1.7:6443
|
||||
name: kubernetes_target
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes_target
|
||||
user: kubernetes-admin
|
||||
name: kubernetes-admin@kubernetes
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user: {}
|
||||
`
|
||||
//testMergedValidKubeconfig tests to confirm whether two kubeconfig got merged or not
|
||||
testMergedValidKubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.23.25.101:6443
|
||||
name: dummycluster_ephemeral
|
||||
- cluster:
|
||||
server: https://10.0.1.7:6443
|
||||
name: kubernetes_target
|
||||
contexts:
|
||||
- context:
|
||||
cluster: dummycluster_ephemeral
|
||||
user: kubernetes-admin
|
||||
name: dummy_cluster
|
||||
- context:
|
||||
cluster: kubernetes_target
|
||||
user: kubernetes-admin
|
||||
name: kubernetes-admin@kubernetes
|
||||
current-context: dummy_cluster
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: dGVzdAo=
|
||||
client-key-data: dGVzdAo=
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
errTempFile = fmt.Errorf("tempFile Error")
|
||||
errSourceFunc = fmt.Errorf("source func error")
|
||||
errWriter = fmt.Errorf("writer error")
|
||||
testValidKubeconfigAPI = &v1alpha1.KubeConfig{
|
||||
Config: v1.Config{
|
||||
CurrentContext: "test",
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Name: "some-cluster",
|
||||
Cluster: v1.Cluster{
|
||||
CertificateAuthority: "ca",
|
||||
Server: "https://10.0.1.7:6443",
|
||||
},
|
||||
},
|
||||
},
|
||||
APIVersion: "v1",
|
||||
Contexts: []v1.NamedContext{
|
||||
{
|
||||
Name: "test",
|
||||
Context: v1.Context{
|
||||
Cluster: "some-cluster",
|
||||
AuthInfo: "some-user",
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
Name: "some-user",
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: "cert-data",
|
||||
ClientKey: "client-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestKubeconfigContent(t *testing.T) {
|
||||
expectedData := []byte(testValidKubeconfig)
|
||||
fSys := fs.NewDocumentFs()
|
||||
kubeconf := kubeconfig.NewKubeConfig(
|
||||
kubeconfig.FromByte(expectedData),
|
||||
kubeconfig.InjectFileSystem(fSys),
|
||||
kubeconfig.InjectTempRoot("."))
|
||||
path, clean, err := kubeconf.GetFile()
|
||||
require.NoError(t, err)
|
||||
defer clean()
|
||||
actualData, err := fSys.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedData, actualData)
|
||||
}
|
||||
|
||||
type MockCoreV1Interface struct {
|
||||
MockSecrets func(string) corev1.SecretInterface
|
||||
corev1.CoreV1Interface
|
||||
}
|
||||
|
||||
func (c MockCoreV1Interface) Secrets(n string) corev1.SecretInterface {
|
||||
return c.MockSecrets(n)
|
||||
}
|
||||
|
||||
var _ corev1.SecretInterface = &SecretMockInterface{}
|
||||
|
||||
type SecretMockInterface struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Create(_ context.Context, _ *apiv1.Secret,
|
||||
_ metav1.CreateOptions) (*apiv1.Secret, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Update(_ context.Context, _ *apiv1.Secret,
|
||||
_ metav1.UpdateOptions) (*apiv1.Secret, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Delete(_ context.Context, _ string, _ metav1.DeleteOptions) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) DeleteCollection(_ context.Context, _ metav1.DeleteOptions,
|
||||
_ metav1.ListOptions) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Apply(_ context.Context, _ *applycorev1.SecretApplyConfiguration,
|
||||
_ metav1.ApplyOptions) (*apiv1.Secret, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Get(_ context.Context, name string, options metav1.GetOptions) (*apiv1.Secret, error) {
|
||||
args := s.Called(name, options)
|
||||
expectedResult, ok := args.Get(0).(*apiv1.Secret)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("wrong input")
|
||||
}
|
||||
return expectedResult, args.Error(1)
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) List(_ context.Context, _ metav1.ListOptions) (*apiv1.SecretList, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Watch(_ context.Context, _ metav1.ListOptions) (watch.Interface, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *SecretMockInterface) Patch(_ context.Context, _ string, _ types.PatchType, _ []byte,
|
||||
_ metav1.PatchOptions, _ ...string) (*apiv1.Secret, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func TestFromSecret(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
options *v1alpha1.GetKubeconfigOptions
|
||||
getSecret *apiv1.Secret
|
||||
getErr error
|
||||
expectedData []byte
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "empty cluster name",
|
||||
options: &v1alpha1.GetKubeconfigOptions{},
|
||||
expectedErr: kubeconfig.ErrClusterNameEmpty{},
|
||||
},
|
||||
{
|
||||
name: "multiple retries and error",
|
||||
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster", Timeout: "1s"},
|
||||
getErr: errors.New("error"),
|
||||
expectedErr: wait.ErrWaitTimeout,
|
||||
},
|
||||
{
|
||||
name: "empty secret object",
|
||||
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
|
||||
getSecret: &apiv1.Secret{},
|
||||
expectedErr: kubeconfig.ErrMalformedKubeconfig{ClusterName: "cluster"},
|
||||
},
|
||||
{
|
||||
name: "empty data value",
|
||||
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
|
||||
getSecret: &apiv1.Secret{Data: map[string][]byte{"value": {}}},
|
||||
expectedErr: kubeconfig.ErrMalformedKubeconfig{ClusterName: "cluster"},
|
||||
},
|
||||
{
|
||||
name: "successfully get kubeconfig",
|
||||
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
|
||||
getSecret: &apiv1.Secret{Data: map[string][]byte{"value": []byte(testValidKubeconfig)}},
|
||||
expectedData: []byte(testValidKubeconfig),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sm := &SecretMockInterface{
|
||||
Mock: mock.Mock{},
|
||||
}
|
||||
sm.On("Get", tt.options.ManagedClusterName+"-kubeconfig", metav1.GetOptions{}).
|
||||
Return(tt.getSecret, tt.getErr)
|
||||
|
||||
coreV1Interface := MockCoreV1Interface{
|
||||
MockSecrets: func(s string) corev1.SecretInterface {
|
||||
return sm
|
||||
},
|
||||
}
|
||||
|
||||
kubeconf, err := kubeconfig.FromSecret(coreV1Interface, tt.options)()
|
||||
if tt.expectedErr != nil {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.expectedErr, err)
|
||||
require.Nil(t, kubeconf)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expectedData, kubeconf)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBundle(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rootPath string
|
||||
expectedContains string
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
name: "valid kubeconfig",
|
||||
rootPath: "testdata",
|
||||
shouldFail: false,
|
||||
expectedContains: "parent_parent_context",
|
||||
},
|
||||
{
|
||||
name: "wrong path",
|
||||
rootPath: "wrong/path",
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "kubeconfig not found",
|
||||
rootPath: "testdata_fail",
|
||||
shouldFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bundle, err := document.NewBundleByPath(tt.rootPath)
|
||||
if tt.shouldFail {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
kubeconf, err := kubeconfig.FromBundle(bundle)()
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(kubeconf), tt.expectedContains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewKubeConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
shouldPanic bool
|
||||
name string
|
||||
expectedPathContains string
|
||||
expectedErrorContains string
|
||||
src kubeconfig.KubeSourceFunc
|
||||
options []kubeconfig.Option
|
||||
}{
|
||||
{
|
||||
name: "write to temp file",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfig)),
|
||||
options: []kubeconfig.Option{
|
||||
kubeconfig.InjectFileSystem(
|
||||
testfs.MockFileSystem{
|
||||
MockTempFile: func(root, pattern string) (fs.File, error) {
|
||||
return testfs.TestFile{
|
||||
MockName: func() string { return "kubeconfig-142398" },
|
||||
MockWrite: func([]byte) (int, error) { return 0, nil },
|
||||
MockClose: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
MockRemoveAll: func() error { return nil },
|
||||
},
|
||||
),
|
||||
},
|
||||
expectedPathContains: "kubeconfig-142398",
|
||||
},
|
||||
{
|
||||
name: "cleanup with dump root",
|
||||
expectedPathContains: "kubeconfig-142398",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfig)),
|
||||
options: []kubeconfig.Option{
|
||||
kubeconfig.InjectTempRoot("/my-unique-root"),
|
||||
kubeconfig.InjectFileSystem(
|
||||
testfs.MockFileSystem{
|
||||
MockTempFile: func(root, _ string) (fs.File, error) {
|
||||
// check if root path is passed to the TempFile interface
|
||||
if root != "/my-unique-root" {
|
||||
return nil, errTempFile
|
||||
}
|
||||
return testfs.TestFile{
|
||||
MockName: func() string { return "kubeconfig-142398" },
|
||||
MockWrite: func([]byte) (int, error) { return 0, nil },
|
||||
MockClose: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
MockRemoveAll: func() error { return nil },
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from file, and fs option",
|
||||
src: kubeconfig.FromFile("/my/kubeconfig", fsWithFile(t, "/my/kubeconfig")),
|
||||
options: []kubeconfig.Option{
|
||||
kubeconfig.InjectFilePath("/my/kubeconfig", fsWithFile(t, "/my/kubeconfig")),
|
||||
},
|
||||
expectedPathContains: "/my/kubeconfig",
|
||||
},
|
||||
{
|
||||
name: "write to real fs",
|
||||
src: kubeconfig.FromAPIalphaV1(testValidKubeconfigAPI),
|
||||
expectedPathContains: "kubeconfig-",
|
||||
},
|
||||
{
|
||||
name: "from file, use SourceFile",
|
||||
src: kubeconfig.FromFile("/my/kubeconfig", fsWithFile(t, "/my/kubeconfig")),
|
||||
expectedPathContains: "kubeconfig-",
|
||||
},
|
||||
{
|
||||
name: "temp file error",
|
||||
src: kubeconfig.FromAPIalphaV1(testValidKubeconfigAPI),
|
||||
expectedErrorContains: errTempFile.Error(),
|
||||
options: []kubeconfig.Option{
|
||||
kubeconfig.InjectFileSystem(
|
||||
testfs.MockFileSystem{
|
||||
MockTempFile: func(string, string) (fs.File, error) {
|
||||
return nil, errTempFile
|
||||
},
|
||||
MockRemoveAll: func() error { return nil },
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "source func error",
|
||||
src: func() ([]byte, error) { return nil, errSourceFunc },
|
||||
expectedPathContains: "kubeconfig-",
|
||||
expectedErrorContains: errSourceFunc.Error(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kubeconf := kubeconfig.NewKubeConfig(tt.src, tt.options...)
|
||||
path, clean, err := kubeconf.GetFile()
|
||||
if tt.expectedErrorContains != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErrorContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
actualPath := path
|
||||
assert.Contains(t, actualPath, tt.expectedPathContains)
|
||||
clean()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeConfigWrite(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedContent string
|
||||
expectedErrorContains string
|
||||
|
||||
readWrite io.ReadWriter
|
||||
options []kubeconfig.Option
|
||||
src kubeconfig.KubeSourceFunc
|
||||
}{
|
||||
{
|
||||
name: "Basic write",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfig)),
|
||||
expectedContent: testValidKubeconfig,
|
||||
readWrite: bytes.NewBuffer([]byte{}),
|
||||
},
|
||||
{
|
||||
name: "Source error",
|
||||
src: func() ([]byte, error) { return nil, errSourceFunc },
|
||||
expectedErrorContains: errSourceFunc.Error(),
|
||||
},
|
||||
{
|
||||
name: "Writer error",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfig)),
|
||||
expectedErrorContains: errWriter.Error(),
|
||||
readWrite: fakeReaderWriter{writeErr: errWriter},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kubeconf := kubeconfig.NewKubeConfig(tt.src, tt.options...)
|
||||
err := kubeconf.Write(tt.readWrite)
|
||||
if tt.expectedErrorContains != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErrorContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedContent, read(t, tt.readWrite))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubeConfigWriteFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedContent string
|
||||
path string
|
||||
expectedErrorContains string
|
||||
merge bool
|
||||
|
||||
fs fs.FileSystem
|
||||
src kubeconfig.KubeSourceFunc
|
||||
}{
|
||||
{
|
||||
name: "Basic write file",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfig)),
|
||||
expectedContent: testValidKubeconfig,
|
||||
merge: false,
|
||||
fs: fsWithFile(t, "/test-path"),
|
||||
path: "/test-path",
|
||||
},
|
||||
{
|
||||
name: "Basic merge write file",
|
||||
src: kubeconfig.FromByte([]byte(testValidKubeconfigTwo)),
|
||||
expectedContent: testMergedValidKubeconfig,
|
||||
merge: true,
|
||||
fs: fs.NewDocumentFs(),
|
||||
path: "testdata/kubeconfig-test",
|
||||
},
|
||||
{
|
||||
name: "Source error",
|
||||
src: func() ([]byte, error) { return nil, errSourceFunc },
|
||||
expectedErrorContains: errSourceFunc.Error(),
|
||||
merge: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kubeconf := kubeconfig.NewKubeConfig(tt.src,
|
||||
kubeconfig.InjectTempRoot("testdata/"),
|
||||
kubeconfig.InjectFileSystem(tt.fs))
|
||||
options := kubeconfig.WriteOptions{
|
||||
Merge: tt.merge,
|
||||
}
|
||||
if tt.merge {
|
||||
_, clean, err := kubeconf.GetFile()
|
||||
require.NoError(t, err)
|
||||
defer clean()
|
||||
original, err := ioutil.ReadFile(tt.path)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err = ioutil.WriteFile(tt.path, original, 0600)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
}
|
||||
err := kubeconf.WriteFile(tt.path, options)
|
||||
if tt.expectedErrorContains != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.expectedErrorContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.YAMLEq(t, tt.expectedContent, readFile(t, tt.path, tt.fs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(t *testing.T, path string, fSys fs.FileSystem) string {
|
||||
b, err := fSys.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func read(t *testing.T, r io.Reader) string {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
require.NoError(t, err)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func fsWithFile(t *testing.T, path string) fs.FileSystem {
|
||||
memFs := kustfs.MakeFsInMemory()
|
||||
fSys := testfs.MockFileSystem{
|
||||
FileSystem: memFs,
|
||||
MockRemoveAll: func() error {
|
||||
return nil
|
||||
},
|
||||
MockTempFile: func(root, pattern string) (fs.File, error) {
|
||||
return testfs.TestFile{
|
||||
MockName: func() string { return "kubeconfig-142398" },
|
||||
MockWrite: func(b []byte) (int, error) { return 0, memFs.WriteFile("kubeconfig-142398", b) },
|
||||
MockClose: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
err := fSys.WriteFile(path, []byte(testValidKubeconfig))
|
||||
require.NoError(t, err)
|
||||
return fSys
|
||||
}
|
||||
|
||||
type fakeReaderWriter struct {
|
||||
readErr error
|
||||
writeErr error
|
||||
}
|
||||
|
||||
var _ io.Reader = fakeReaderWriter{}
|
||||
var _ io.Writer = fakeReaderWriter{}
|
||||
|
||||
func (f fakeReaderWriter) Read(_ []byte) (n int, err error) {
|
||||
return 0, f.readErr
|
||||
}
|
||||
|
||||
func (f fakeReaderWriter) Write(_ []byte) (n int, err error) {
|
||||
return 0, f.writeErr
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://10.23.25.101:6443
|
||||
name: dummycluster_ephemeral
|
||||
contexts:
|
||||
- context:
|
||||
cluster: dummycluster_ephemeral
|
||||
user: kubernetes-admin
|
||||
name: dummy_cluster
|
||||
current-context: dummy_cluster
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
|
@ -1,18 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.23.25.101:6443
|
||||
name: dummycluster_ephemeral
|
||||
contexts:
|
||||
- context:
|
||||
cluster: dummycluster_ephemeral
|
||||
user: kubernetes-admin
|
||||
name: dummy_cluster
|
||||
current-context: dummy_cluster
|
||||
preferences: {}
|
||||
users:
|
||||
- name: kubernetes-admin
|
||||
user:
|
||||
client-certificate-data: dGVzdAo=
|
||||
client-key-data: dGVzdAo=
|
|
@ -1,23 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: KubeConfig
|
||||
metadata:
|
||||
name: default
|
||||
config:
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://10.23.25.101:6443
|
||||
name: parent_parent_cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: parent_parent_cluster
|
||||
user: parent_parent_admin
|
||||
name: parent_parent_context
|
||||
preferences: {}
|
||||
users:
|
||||
- name: parent_parent_admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
|
@ -1,2 +0,0 @@
|
|||
resources:
|
||||
- kubeconfig.yaml
|
|
@ -1,43 +0,0 @@
|
|||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: c29tZWNlcnQK
|
||||
server: https://10.23.25.101:6443
|
||||
name: child_cluster
|
||||
- cluster:
|
||||
certificate-authority-data: c29tZWNlcnQK
|
||||
server: https://10.23.25.101:6443
|
||||
name: parent_cluster
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://10.23.25.101:6443
|
||||
name: parent_parent_cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: child_cluster
|
||||
user: child_user
|
||||
name: child
|
||||
- context:
|
||||
cluster: parent_cluster
|
||||
user: parent_admin
|
||||
name: parent-custom
|
||||
- context:
|
||||
cluster: parent_parent_cluster
|
||||
user: parent_parent_admin
|
||||
name: parent_parent_context
|
||||
current-context: dummy_cluster
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: child_user
|
||||
user:
|
||||
client-certificate-data: c29tZWNlcnQK
|
||||
client-key-data: c29tZWNlcnQK
|
||||
- name: parent_admin
|
||||
user:
|
||||
client-certificate-data: c29tZWNlcnQK
|
||||
client-key-data: c29tZWNlcnQK
|
||||
- name: parent_parent_admin
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
|
|
@ -1,2 +0,0 @@
|
|||
resources:
|
||||
- kubeconfig.yaml
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
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 utils
|
||||
|
||||
import (
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
// ClientOption is a function that allows to modify ConfigFlags object which is used to create Client
|
||||
type ClientOption func(*genericclioptions.ConfigFlags)
|
||||
|
||||
// SetTimeout sets Timeout option in ConfigFlags object
|
||||
func SetTimeout(timeout string) ClientOption {
|
||||
return func(co *genericclioptions.ConfigFlags) {
|
||||
*co.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// FactoryFromKubeConfig returns a factory with the
|
||||
// default Kubernetes resources for the given kube config path and context
|
||||
func FactoryFromKubeConfig(path, context string, opts ...ClientOption) cmdutil.Factory {
|
||||
kf := genericclioptions.NewConfigFlags(false)
|
||||
kf.KubeConfig = &path
|
||||
kf.Context = &context
|
||||
for _, o := range opts {
|
||||
o(kf)
|
||||
}
|
||||
|
||||
return cmdutil.NewFactory(cmdutil.NewMatchVersionFlags(kf))
|
||||
}
|
|
@ -27,7 +27,6 @@ import (
|
|||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors"
|
||||
|
@ -81,25 +80,10 @@ func (p *phase) Executor() (ifc.Executor, error) {
|
|||
return nil, executorerrors.ErrExecutorNotFound{GVK: refGVK}
|
||||
}
|
||||
|
||||
cMap, err := p.helper.ClusterMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kubeconf := kubeconfig.NewBuilder().
|
||||
WithBundle(p.helper.PhaseConfigBundle()).
|
||||
WithClusterMap(cMap).
|
||||
WithTempRoot(p.helper.WorkDir()).
|
||||
WithClusterNames(p.apiObj.ClusterName).
|
||||
SiteWide(p.apiObj.Config.SiteWideKubeconfig).
|
||||
Build()
|
||||
|
||||
return executorFactory(
|
||||
ifc.ExecutorConfig{
|
||||
ClusterMap: cMap,
|
||||
BundleFactory: document.BundleFactoryFromDocRoot(p.DocumentRoot),
|
||||
PhaseName: p.apiObj.Name,
|
||||
KubeConfig: kubeconf,
|
||||
ExecutorDocument: executorDoc,
|
||||
ClusterName: p.apiObj.ClusterName,
|
||||
PhaseConfigBundle: p.helper.PhaseConfigBundle(),
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
phaseerrors "opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
|
@ -39,8 +38,10 @@ const (
|
|||
|
||||
// GenericRunFlags generic options for run command
|
||||
type GenericRunFlags struct {
|
||||
DryRun bool
|
||||
Timeout time.Duration
|
||||
Kubeconfig string
|
||||
Context string
|
||||
DryRun bool
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// RunFlags options for phase run command
|
||||
|
@ -232,37 +233,6 @@ func (c *PlanRunCommand) RunE() error {
|
|||
return plan.Run(c.Options)
|
||||
}
|
||||
|
||||
// ClusterListCommand options for cluster list command
|
||||
type ClusterListCommand struct {
|
||||
Factory config.Factory
|
||||
Writer io.Writer
|
||||
Format string
|
||||
}
|
||||
|
||||
// RunE executes cluster list command
|
||||
func (c *ClusterListCommand) RunE() error {
|
||||
if c.Format != TableOutputFormat && c.Format != "name" {
|
||||
return phaseerrors.ErrInvalidOutputFormat{RequestedFormat: c.Format}
|
||||
}
|
||||
cfg, err := c.Factory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper, err := NewHelper(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterMap, err := helper.ClusterMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = clusterMap.Write(c.Writer, clustermap.WriteOptions{Format: c.Format})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateFlags options for phase validate command
|
||||
type ValidateFlags struct {
|
||||
PhaseID ifc.ID
|
||||
|
|
|
@ -499,80 +499,6 @@ func TestPlanRunCommand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestClusterListCommand_RunE(t *testing.T) {
|
||||
testErr := fmt.Errorf(testFactoryErr)
|
||||
testCases := []struct {
|
||||
name string
|
||||
factory config.Factory
|
||||
expectedErr string
|
||||
Format string
|
||||
}{
|
||||
{
|
||||
name: "Error config factory",
|
||||
factory: func() (*config.Config, error) {
|
||||
return nil, testErr
|
||||
},
|
||||
expectedErr: testFactoryErr,
|
||||
Format: "name",
|
||||
},
|
||||
{
|
||||
name: "Error new helper",
|
||||
factory: func() (*config.Config, error) {
|
||||
return &config.Config{
|
||||
CurrentContext: "does not exist",
|
||||
Contexts: make(map[string]*config.Context),
|
||||
}, nil
|
||||
},
|
||||
expectedErr: "missing configuration: context with name 'does not exist'",
|
||||
Format: "name",
|
||||
},
|
||||
{
|
||||
name: "No error",
|
||||
Format: "name",
|
||||
factory: func() (*config.Config, error) {
|
||||
conf := config.NewConfig()
|
||||
conf.Manifests = map[string]*config.Manifest{
|
||||
"manifest": {
|
||||
MetadataPath: "metadata.yaml",
|
||||
TargetPath: "testdata",
|
||||
PhaseRepositoryName: config.DefaultTestPhaseRepo,
|
||||
Repositories: map[string]*config.Repository{
|
||||
config.DefaultTestPhaseRepo: {
|
||||
URLString: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
conf.CurrentContext = defaultCurrentContext
|
||||
conf.Contexts = map[string]*config.Context{
|
||||
"context": {
|
||||
Manifest: "manifest",
|
||||
},
|
||||
}
|
||||
return conf, nil
|
||||
},
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tt := tc
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := phase.ClusterListCommand{
|
||||
Factory: tt.factory,
|
||||
Format: tt.Format,
|
||||
Writer: bytes.NewBuffer(nil),
|
||||
}
|
||||
err := cmd.RunE()
|
||||
if tt.expectedErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tt.expectedErr, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
@ -26,12 +26,10 @@ import (
|
|||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
airerrors "opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
phaseerrors "opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors/errors"
|
||||
|
@ -47,9 +45,7 @@ type ClusterctlExecutor struct {
|
|||
clusterName string
|
||||
targetPath string
|
||||
|
||||
clusterMap clustermap.ClusterMap
|
||||
options *airshipv1.Clusterctl
|
||||
kubecfg kubeconfig.Interface
|
||||
execObj *airshipv1.GenericContainer
|
||||
clientFunc container.ClientV1Alpha1FactoryFunc
|
||||
cctlOpts *airshipv1.ClusterctlOptions
|
||||
|
@ -95,8 +91,6 @@ func NewClusterctlExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
|
|||
clusterName: cfg.ClusterName,
|
||||
options: options,
|
||||
cctlOpts: cctlOpts,
|
||||
kubecfg: cfg.KubeConfig,
|
||||
clusterMap: cfg.ClusterMap,
|
||||
targetPath: cfg.TargetPath,
|
||||
execObj: apiObj,
|
||||
clientFunc: clientFunc,
|
||||
|
@ -159,6 +153,14 @@ func (c *ClusterctlExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
|
|||
c.cctlOpts.CmdOptions = append(c.cctlOpts.CmdOptions, "-v5")
|
||||
}
|
||||
|
||||
c.options.Kubeconfig = map[bool]string{true: opts.Kubeconfig,
|
||||
false: map[bool]string{true: KubeConfigFile,
|
||||
false: c.options.Kubeconfig}[c.options.Kubeconfig == ""]}[opts.Kubeconfig != ""]
|
||||
|
||||
c.options.Context = map[bool]string{true: opts.Context,
|
||||
false: map[bool]string{true: c.clusterName,
|
||||
false: c.options.Context}[c.options.Context == ""]}[opts.Context != ""]
|
||||
|
||||
cctlConfig := map[string]interface{}{
|
||||
"providers": c.options.Providers,
|
||||
"images": c.options.ImageMetas,
|
||||
|
@ -173,6 +175,13 @@ func (c *ClusterctlExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
|
|||
handleError(evtCh, err)
|
||||
}
|
||||
|
||||
c.execObj.Spec.StorageMounts = append(c.execObj.Spec.StorageMounts, airshipv1.StorageMount{
|
||||
MountType: "bind",
|
||||
Src: c.options.Kubeconfig,
|
||||
DstPath: KubeConfigMount,
|
||||
ReadWriteMode: false,
|
||||
})
|
||||
|
||||
switch c.options.Action {
|
||||
case airshipv1.Init:
|
||||
c.init(evtCh)
|
||||
|
@ -192,44 +201,16 @@ func (c *ClusterctlExecutor) run() error {
|
|||
return c.clientFunc("", &bytes.Buffer{}, os.Stdout, c.execObj, c.targetPath).Run()
|
||||
}
|
||||
|
||||
func (c *ClusterctlExecutor) getKubeconfig() (string, string, func(), error) {
|
||||
kubeConfigFile, cleanup, err := c.kubecfg.GetFile()
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
context, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
c.execObj.Spec.StorageMounts = append(c.execObj.Spec.StorageMounts, airshipv1.StorageMount{
|
||||
MountType: "bind",
|
||||
Src: kubeConfigFile,
|
||||
DstPath: kubeConfigFile,
|
||||
ReadWriteMode: false,
|
||||
})
|
||||
return kubeConfigFile, context, cleanup, nil
|
||||
}
|
||||
|
||||
func (c *ClusterctlExecutor) init(evtCh chan events.Event) {
|
||||
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlInitStart,
|
||||
Message: "starting clusterctl init executor",
|
||||
})
|
||||
|
||||
kubecfg, context, cleanup, err := c.getKubeconfig()
|
||||
if err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
c.cctlOpts.CmdOptions = append(c.cctlOpts.CmdOptions,
|
||||
"init",
|
||||
"--kubeconfig", kubecfg,
|
||||
"--kubeconfig-context", context,
|
||||
"--kubeconfig", KubeConfigMount,
|
||||
"--kubeconfig-context", c.options.Context,
|
||||
)
|
||||
|
||||
initMap := map[string]string{
|
||||
|
@ -244,7 +225,7 @@ func (c *ClusterctlExecutor) init(evtCh chan events.Event) {
|
|||
}
|
||||
}
|
||||
|
||||
if err = c.run(); err != nil {
|
||||
if err := c.run(); err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
|
@ -261,31 +242,14 @@ func (c *ClusterctlExecutor) move(dryRun bool, evtCh chan events.Event) {
|
|||
Operation: events.ClusterctlMoveStart,
|
||||
Message: "starting clusterctl move executor",
|
||||
})
|
||||
kubecfg, context, cleanup, err := c.getKubeconfig()
|
||||
if err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
fromCluster, err := c.clusterMap.ParentCluster(c.clusterName)
|
||||
if err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
fromContext, err := c.clusterMap.ClusterKubeconfigContext(fromCluster)
|
||||
if err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.cctlOpts.CmdOptions = append(
|
||||
c.cctlOpts.CmdOptions,
|
||||
"move",
|
||||
"--kubeconfig", kubecfg,
|
||||
"--kubeconfig-context", fromContext,
|
||||
"--to-kubeconfig", kubecfg,
|
||||
"--to-kubeconfig-context", context,
|
||||
"--kubeconfig", KubeConfigMount,
|
||||
"--kubeconfig-context", c.options.Context,
|
||||
"--to-kubeconfig", KubeConfigMount,
|
||||
"--to-kubeconfig-context", c.options.MoveOptions.TargetContext,
|
||||
"--namespace", c.options.MoveOptions.Namespace,
|
||||
)
|
||||
|
||||
|
@ -296,7 +260,7 @@ func (c *ClusterctlExecutor) move(dryRun bool, evtCh chan events.Event) {
|
|||
)
|
||||
}
|
||||
|
||||
if err = c.run(); err != nil {
|
||||
if err := c.run(); err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ package executors_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
@ -26,11 +25,9 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
|
@ -199,37 +196,6 @@ providers:
|
|||
}
|
||||
}
|
||||
|
||||
var _ clustermap.ClusterMap = &ClusterMapMockInterface{}
|
||||
|
||||
type ClusterMapMockInterface struct {
|
||||
MockClusterKubeconfigContext func(string) (string, error)
|
||||
MockParentCluster func(string) (string, error)
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) ValidateClusterMap() error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) ParentCluster(s string) (string, error) {
|
||||
return c.MockParentCluster(s)
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) AllClusters() []string {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) ClusterKubeconfigContext(s string) (string, error) {
|
||||
return c.MockClusterKubeconfigContext(s)
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) Sources(_ string) ([]v1alpha1.KubeconfigSource, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (c ClusterMapMockInterface) Write(_ io.Writer, _ clustermap.WriteOptions) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
var _ container.ClientV1Alpha1 = &MockClientFuncInterface{}
|
||||
|
||||
type MockClientFuncInterface struct {
|
||||
|
@ -241,15 +207,10 @@ func (c MockClientFuncInterface) Run() error {
|
|||
}
|
||||
|
||||
func TestClusterctlExecutorRun(t *testing.T) {
|
||||
errTmpFile := goerrors.New("TmpFile error")
|
||||
errCtx := goerrors.New("context error")
|
||||
errParent := goerrors.New("parent cluster error")
|
||||
testCases := []struct {
|
||||
name string
|
||||
cfgDoc document.Document
|
||||
kubecfg kubeconfig.Interface
|
||||
expectedEvt []events.Event
|
||||
clusterMap clustermap.ClusterMap
|
||||
clientFunc container.ClientV1Alpha1FactoryFunc
|
||||
}{
|
||||
{
|
||||
|
@ -258,96 +219,10 @@ func TestClusterctlExecutorRun(t *testing.T) {
|
|||
expectedEvt: []events.Event{
|
||||
wrapError(errors.ErrUnknownExecutorAction{Action: "someAction", ExecutorName: "clusterctl"}),
|
||||
},
|
||||
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
|
||||
},
|
||||
{
|
||||
name: "Failed get kubeconfig file - init",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", nil, errTmpFile
|
||||
}},
|
||||
expectedEvt: []events.Event{
|
||||
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlInitStart,
|
||||
}),
|
||||
wrapError(errTmpFile),
|
||||
},
|
||||
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
|
||||
},
|
||||
{
|
||||
name: "Failed get kubeconfig file - move",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", nil, errTmpFile
|
||||
}},
|
||||
expectedEvt: []events.Event{
|
||||
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlMoveStart,
|
||||
}),
|
||||
wrapError(errTmpFile),
|
||||
},
|
||||
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
|
||||
},
|
||||
{
|
||||
name: "Failed get kubeconfig context - init",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, nil
|
||||
}},
|
||||
expectedEvt: []events.Event{
|
||||
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlInitStart,
|
||||
}),
|
||||
wrapError(errCtx),
|
||||
},
|
||||
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
|
||||
return "", errCtx
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Failed get kubeconfig context - move",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, nil
|
||||
}},
|
||||
expectedEvt: []events.Event{
|
||||
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlMoveStart,
|
||||
}),
|
||||
wrapError(errCtx),
|
||||
},
|
||||
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
|
||||
return "", errCtx
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Failed get parent cluster",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, nil
|
||||
}},
|
||||
expectedEvt: []events.Event{
|
||||
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
|
||||
Operation: events.ClusterctlMoveStart,
|
||||
}),
|
||||
wrapError(errParent),
|
||||
},
|
||||
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
|
||||
return "ctx", nil
|
||||
},
|
||||
MockParentCluster: func(s string) (string, error) {
|
||||
return "", errParent
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Regular Run init",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, nil
|
||||
}},
|
||||
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
|
||||
return "cluster", nil
|
||||
}},
|
||||
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
|
||||
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
|
||||
return MockClientFuncInterface{MockRun: func() error {
|
||||
|
@ -366,15 +241,6 @@ func TestClusterctlExecutorRun(t *testing.T) {
|
|||
{
|
||||
name: "Regular Run move",
|
||||
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
|
||||
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, nil
|
||||
}},
|
||||
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
|
||||
return "cluster", nil
|
||||
},
|
||||
MockParentCluster: func(s string) (string, error) {
|
||||
return "parentCluster", nil
|
||||
}},
|
||||
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
|
||||
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
|
||||
return MockClientFuncInterface{MockRun: func() error {
|
||||
|
@ -399,8 +265,6 @@ func TestClusterctlExecutorRun(t *testing.T) {
|
|||
TargetPath: "testdata",
|
||||
PhaseConfigBundle: executorBundle(t, krmExecDoc),
|
||||
ExecutorDocument: tt.cfgDoc,
|
||||
KubeConfig: tt.kubecfg,
|
||||
ClusterMap: tt.clusterMap,
|
||||
ContainerFunc: tt.clientFunc,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -30,6 +30,9 @@ const (
|
|||
GenericContainer = "generic-container"
|
||||
Ephemeral = "ephemeral"
|
||||
BMHManager = "BaremetalManager"
|
||||
|
||||
KubeConfigFile = "~/.kube/config"
|
||||
KubeConfigMount = "/kubeconfig"
|
||||
)
|
||||
|
||||
// RegisterExecutor adds executor to phase executor registry
|
||||
|
|
|
@ -17,9 +17,6 @@ package executors_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -112,13 +109,3 @@ func wrapError(err error) events.Event {
|
|||
Error: err,
|
||||
})
|
||||
}
|
||||
|
||||
func testClusterMap(t *testing.T) clustermap.ClusterMap {
|
||||
doc, err := document.NewDocumentFromBytes([]byte(singleExecutorClusterMap))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, doc)
|
||||
apiObj := v1alpha1.DefaultClusterMap()
|
||||
err = doc.ToAPIObject(apiObj, v1alpha1.Scheme)
|
||||
require.NoError(t, err)
|
||||
return clustermap.NewClusterMap(apiObj)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
commonerrors "opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
|
@ -74,10 +73,9 @@ func NewContainerExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
|
|||
MountBasePath: cfg.TargetPath,
|
||||
ExecutorBundle: bundle,
|
||||
ExecutorDocument: cfg.ExecutorDocument,
|
||||
// TODO extend tests with proper client, make it interface
|
||||
ClientFunc: container.NewClientV1Alpha1,
|
||||
Container: apiObj,
|
||||
Options: cfg,
|
||||
ClientFunc: container.NewClientV1Alpha1,
|
||||
Container: apiObj,
|
||||
Options: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -91,12 +89,15 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
|
|||
})
|
||||
|
||||
if c.Options.ClusterName != "" {
|
||||
cleanup, err := c.SetKubeConfig()
|
||||
if opts.Kubeconfig == "" {
|
||||
opts.Kubeconfig = KubeConfigFile
|
||||
}
|
||||
|
||||
err := c.SetKubeConfig(opts.Kubeconfig, c.Options.ClusterName)
|
||||
if err != nil {
|
||||
handleError(evtCh, err)
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
input, err := bundleReader(c.ExecutorBundle)
|
||||
|
@ -138,24 +139,16 @@ func (c *ContainerExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
|
|||
}
|
||||
|
||||
// SetKubeConfig adds env variable and mounts kubeconfig to container
|
||||
func (c *ContainerExecutor) SetKubeConfig() (kubeconfig.Cleanup, error) {
|
||||
context, err := c.Options.ClusterMap.ClusterKubeconfigContext(c.Options.ClusterName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kubeConfigSrc, cleanup, err := c.Options.KubeConfig.GetFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (c *ContainerExecutor) SetKubeConfig(kubeconfig, context string) error {
|
||||
c.Container.Spec.StorageMounts = append(c.Container.Spec.StorageMounts, v1alpha1.StorageMount{
|
||||
MountType: "bind",
|
||||
Src: kubeConfigSrc,
|
||||
DstPath: v1alpha1.KubeConfigPath,
|
||||
Src: kubeconfig,
|
||||
DstPath: KubeConfigMount,
|
||||
})
|
||||
envs := []string{v1alpha1.KubeConfigEnv, v1alpha1.KubeConfigEnvKeyContext + "=" + context}
|
||||
c.Container.Spec.EnvVars = append(c.Container.Spec.EnvVars, envs...)
|
||||
|
||||
return cleanup, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// bundleReader sets input for function
|
||||
|
|
|
@ -15,8 +15,6 @@ package executors_test
|
|||
import (
|
||||
"bytes"
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -28,7 +26,6 @@ import (
|
|||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
|
@ -65,16 +62,6 @@ const (
|
|||
cmd: encrypt
|
||||
unencrypted-regex: '^(kind|apiVersion|group|metadata)$'`
|
||||
|
||||
singleExecutorClusterMap = `apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
testCluster: {}
|
||||
`
|
||||
|
||||
refConfig = `apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
|
@ -241,13 +228,7 @@ func TestGenericContainer(t *testing.T) {
|
|||
Container: tt.containerAPI,
|
||||
ClientFunc: tt.clientFunc,
|
||||
Options: ifc.ExecutorConfig{
|
||||
ClusterName: "testCluster",
|
||||
KubeConfig: fakeKubeConfig{
|
||||
getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "testPath", func() {}, nil
|
||||
},
|
||||
},
|
||||
ClusterMap: testClusterMap(t),
|
||||
ClusterName: "testCluster",
|
||||
PhaseConfigBundle: tt.phaseConfigBundle,
|
||||
},
|
||||
}
|
||||
|
@ -277,7 +258,6 @@ func TestGenericContainer(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSetKubeConfig(t *testing.T) {
|
||||
getFileErr := fmt.Errorf("failed to get file")
|
||||
testCases := []struct {
|
||||
name string
|
||||
opts ifc.ExecutorConfig
|
||||
|
@ -287,27 +267,8 @@ func TestSetKubeConfig(t *testing.T) {
|
|||
name: "Set valid kubeconfig",
|
||||
opts: ifc.ExecutorConfig{
|
||||
ClusterName: "testCluster",
|
||||
KubeConfig: fakeKubeConfig{
|
||||
getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "testPath", func() {}, nil
|
||||
},
|
||||
},
|
||||
ClusterMap: testClusterMap(t),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Failed to get kubeconfig file",
|
||||
opts: ifc.ExecutorConfig{
|
||||
ClusterName: "testCluster",
|
||||
KubeConfig: fakeKubeConfig{
|
||||
getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", func() {}, getFileErr
|
||||
},
|
||||
},
|
||||
ClusterMap: testClusterMap(t),
|
||||
},
|
||||
expectedErr: getFileErr,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -317,7 +278,7 @@ func TestSetKubeConfig(t *testing.T) {
|
|||
Options: tt.opts,
|
||||
Container: &v1alpha1.GenericContainer{},
|
||||
}
|
||||
_, err := e.SetKubeConfig()
|
||||
err := e.SetKubeConfig("", tt.opts.ClusterName)
|
||||
assert.Equal(t, tt.expectedErr, err)
|
||||
})
|
||||
}
|
||||
|
@ -372,14 +333,3 @@ metadata:
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeKubeConfig struct {
|
||||
getFile func() (string, kubeconfig.Cleanup, error)
|
||||
}
|
||||
|
||||
func (k fakeKubeConfig) GetFile() (string, kubeconfig.Cleanup, error) { return k.getFile() }
|
||||
func (k fakeKubeConfig) Write(_ io.Writer) error { return nil }
|
||||
func (k fakeKubeConfig) WriteFile(_ string, _ kubeconfig.WriteOptions) error { return nil }
|
||||
func (k fakeKubeConfig) WriteTempFile(_ string) (string, kubeconfig.Cleanup, error) {
|
||||
return k.getFile()
|
||||
}
|
||||
|
|
|
@ -22,12 +22,10 @@ import (
|
|||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
airerrors "opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/log"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/errors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
|
@ -43,9 +41,7 @@ type KubeApplierExecutor struct {
|
|||
targetPath string
|
||||
|
||||
apiObject *airshipv1.KubernetesApply
|
||||
clusterMap clustermap.ClusterMap
|
||||
clusterName string
|
||||
kubeconfig kubeconfig.Interface
|
||||
clientFunc container.ClientV1Alpha1FactoryFunc
|
||||
execObj *airshipv1.GenericContainer
|
||||
}
|
||||
|
@ -83,9 +79,7 @@ func NewKubeApplierExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
|
|||
BundleName: cfg.PhaseName,
|
||||
ExecutorDocument: cfg.ExecutorDocument,
|
||||
apiObject: apiObj,
|
||||
clusterMap: cfg.ClusterMap,
|
||||
clusterName: cfg.ClusterName,
|
||||
kubeconfig: cfg.KubeConfig,
|
||||
clientFunc: clientFunc,
|
||||
execObj: cObj,
|
||||
targetPath: cfg.TargetPath,
|
||||
|
@ -99,19 +93,18 @@ func (e *KubeApplierExecutor) Run(ch chan events.Event, runOpts ifc.RunOptions)
|
|||
e.apiObject.Config.Debug = log.DebugEnabled()
|
||||
e.apiObject.Config.PhaseName = e.BundleName
|
||||
|
||||
if e.apiObject.Config.Kubeconfig == "" {
|
||||
kcfg, ctx, cleanup, err := e.getKubeconfig()
|
||||
if err != nil {
|
||||
handleError(ch, err)
|
||||
return
|
||||
}
|
||||
defer cleanup()
|
||||
e.apiObject.Config.Kubeconfig, e.apiObject.Config.Context = kcfg, ctx
|
||||
}
|
||||
e.apiObject.Config.Kubeconfig = map[bool]string{true: runOpts.Kubeconfig,
|
||||
false: map[bool]string{true: KubeConfigFile,
|
||||
false: e.apiObject.Config.Kubeconfig}[e.apiObject.Config.Kubeconfig == ""]}[runOpts.Kubeconfig != ""]
|
||||
|
||||
e.apiObject.Config.Context = map[bool]string{true: runOpts.Context,
|
||||
false: map[bool]string{true: e.clusterName,
|
||||
false: e.apiObject.Config.Context}[e.apiObject.Config.Context == ""]}[runOpts.Context != ""]
|
||||
|
||||
e.execObj.Spec.StorageMounts = append(e.execObj.Spec.StorageMounts, airshipv1.StorageMount{
|
||||
MountType: "bind",
|
||||
Src: e.apiObject.Config.Kubeconfig,
|
||||
DstPath: e.apiObject.Config.Kubeconfig,
|
||||
DstPath: KubeConfigMount,
|
||||
ReadWriteMode: false,
|
||||
})
|
||||
log.Printf("using kubeconfig at '%s' and context '%s'", e.apiObject.Config.Kubeconfig, e.apiObject.Config.Context)
|
||||
|
@ -141,20 +134,6 @@ func (e *KubeApplierExecutor) Run(ch chan events.Event, runOpts ifc.RunOptions)
|
|||
}
|
||||
}
|
||||
|
||||
func (e *KubeApplierExecutor) getKubeconfig() (string, string, func(), error) {
|
||||
log.Debug("Getting kubeconfig context name from cluster map")
|
||||
ctx, err := e.clusterMap.ClusterKubeconfigContext(e.clusterName)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
log.Debug("Getting kubeconfig file information from kubeconfig provider")
|
||||
path, cleanup, err := e.kubeconfig.GetFile()
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
return path, ctx, cleanup, nil
|
||||
}
|
||||
|
||||
func (e *KubeApplierExecutor) prepareDocuments() (io.Reader, error) {
|
||||
log.Debug("Filtering out documents that shouldn't be applied to kubernetes from document bundle")
|
||||
filteredBundle, err := e.ExecutorBundle.SelectBundle(document.NewDeployToK8sSelector())
|
||||
|
|
|
@ -25,16 +25,12 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
"opendev.org/airship/airshipctl/pkg/fs"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/executors"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
testdoc "opendev.org/airship/airshipctl/testutil/document"
|
||||
testfs "opendev.org/airship/airshipctl/testutil/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -223,78 +219,30 @@ func TestKubeApplierExecutorRun(t *testing.T) {
|
|||
containsErr string
|
||||
clusterName string
|
||||
|
||||
kubeconf kubeconfig.Interface
|
||||
execDoc document.Document
|
||||
bundleFactory document.BundleFactoryFunc
|
||||
clusterMap clustermap.ClusterMap
|
||||
clientFunc container.ClientV1Alpha1FactoryFunc
|
||||
}{
|
||||
{
|
||||
name: "unable to get kubeconfig context",
|
||||
containsErr: "cluster 'foo' is not defined in cluster map",
|
||||
bundleFactory: testApplierBundleFactory(t, "", nil),
|
||||
kubeconf: testKubeconfig(`invalid kubeconfig`),
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "foo",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "unable to get kubeconfig file",
|
||||
containsErr: "failed to get kubeconfig",
|
||||
bundleFactory: testApplierBundleFactory(t, "", nil),
|
||||
kubeconf: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
|
||||
return "", nil, errors.New("failed to get kubeconfig")
|
||||
}},
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "ephemeral-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "unable to select bundle",
|
||||
containsErr: "error selecting bundle",
|
||||
bundleFactory: testApplierBundleFactorySelectBundleError(),
|
||||
kubeconf: testKubeconfig("kubeconfig"),
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "ephemeral-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "unable to write bundle",
|
||||
containsErr: "error writing bundle",
|
||||
bundleFactory: testApplierBundleFactoryWriteError(),
|
||||
kubeconf: testKubeconfig("kubeconfig"),
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "ephemeral-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "unsuccessful run",
|
||||
containsErr: "applier failure",
|
||||
bundleFactory: testApplierBundleFactoryNoError(),
|
||||
kubeconf: testKubeconfig("kubeconfig"),
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "ephemeral-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
|
||||
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
|
||||
return MockClientFuncInterface{MockRun: func() error {
|
||||
|
@ -306,14 +254,8 @@ func TestKubeApplierExecutorRun(t *testing.T) {
|
|||
name: "successful run",
|
||||
containsErr: "",
|
||||
bundleFactory: testApplierBundleFactoryNoError(),
|
||||
kubeconf: testKubeconfig("kubeconfig"),
|
||||
execDoc: executorDoc(t, ValidExecutorDoc),
|
||||
clusterName: "ephemeral-cluster",
|
||||
clusterMap: clustermap.NewClusterMap(&v1alpha1.ClusterMap{
|
||||
Map: map[string]*v1alpha1.Cluster{
|
||||
"ephemeral-cluster": {},
|
||||
},
|
||||
}),
|
||||
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
|
||||
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
|
||||
return MockClientFuncInterface{MockRun: func() error {
|
||||
|
@ -329,8 +271,6 @@ func TestKubeApplierExecutorRun(t *testing.T) {
|
|||
ifc.ExecutorConfig{
|
||||
ExecutorDocument: tt.execDoc,
|
||||
BundleFactory: tt.bundleFactory,
|
||||
KubeConfig: tt.kubeconf,
|
||||
ClusterMap: tt.clusterMap,
|
||||
ClusterName: tt.clusterName,
|
||||
PhaseConfigBundle: executorBundle(t, applierKRMDoc),
|
||||
ContainerFunc: tt.clientFunc,
|
||||
|
@ -369,23 +309,6 @@ func TestRender(t *testing.T) {
|
|||
assert.Equal(t, content, result)
|
||||
}
|
||||
|
||||
func testKubeconfig(stringData string) kubeconfig.Interface {
|
||||
return kubeconfig.NewKubeConfig(
|
||||
kubeconfig.FromByte([]byte(stringData)),
|
||||
kubeconfig.InjectFileSystem(
|
||||
testfs.MockFileSystem{
|
||||
MockTempFile: func(root, pattern string) (fs.File, error) {
|
||||
return testfs.TestFile{
|
||||
MockName: func() string { return "kubeconfig-142398" },
|
||||
MockWrite: func([]byte) (int, error) { return 0, nil },
|
||||
MockClose: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
MockRemoveAll: func() error { return nil },
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
func TestKubeApplierExecutor_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/document/metadata"
|
||||
|
@ -243,34 +242,6 @@ func (helper *Helper) ListPlans() ([]*v1alpha1.PhasePlan, error) {
|
|||
return plans, nil
|
||||
}
|
||||
|
||||
// ClusterMapAPIobj associated with the the manifest
|
||||
func (helper *Helper) ClusterMapAPIobj() (*v1alpha1.ClusterMap, error) {
|
||||
cMap := v1alpha1.DefaultClusterMap()
|
||||
selector, err := document.NewSelector().ByObject(cMap, v1alpha1.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc, err := helper.phaseConfigBundle.SelectOne(selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = doc.ToAPIObject(cMap, v1alpha1.Scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cMap, nil
|
||||
}
|
||||
|
||||
// ClusterMap associated with the the manifest
|
||||
func (helper *Helper) ClusterMap() (clustermap.ClusterMap, error) {
|
||||
cMap, err := helper.ClusterMapAPIobj()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clustermap.NewClusterMap(cMap), nil
|
||||
}
|
||||
|
||||
// ExecutorDoc returns executor document associated with phase
|
||||
func (helper *Helper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) {
|
||||
phaseObj, err := helper.Phase(phaseID)
|
||||
|
|
|
@ -331,128 +331,6 @@ func TestHelperListPlans(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHelperClusterMapAPI(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
errContains string
|
||||
helperErr bool
|
||||
expectedCMap *v1alpha1.ClusterMap
|
||||
config func(t *testing.T) *config.Config
|
||||
}{
|
||||
{
|
||||
name: "Success cluster map",
|
||||
expectedCMap: &airshipv1.ClusterMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "airshipit.org/v1alpha1",
|
||||
Kind: "ClusterMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "clusterctl-v1",
|
||||
},
|
||||
Map: map[string]*airshipv1.Cluster{
|
||||
"target": {
|
||||
Parent: "ephemeral",
|
||||
Sources: []airshipv1.KubeconfigSource{
|
||||
{
|
||||
Type: airshipv1.KubeconfigSourceTypeBundle,
|
||||
},
|
||||
},
|
||||
},
|
||||
"ephemeral": {
|
||||
Sources: []airshipv1.KubeconfigSource{
|
||||
{
|
||||
Type: airshipv1.KubeconfigSourceTypeBundle,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
config: testConfig,
|
||||
},
|
||||
{
|
||||
name: "Error bundle path doesn't exist",
|
||||
config: func(t *testing.T) *config.Config {
|
||||
conf := testConfig(t)
|
||||
conf.Manifests["dummy_manifest"].MetadataPath = brokenMetaPath
|
||||
return conf
|
||||
},
|
||||
errContains: "no such file or directory",
|
||||
helperErr: true,
|
||||
},
|
||||
{
|
||||
name: "Error no cluster map",
|
||||
config: func(t *testing.T) *config.Config {
|
||||
conf := testConfig(t)
|
||||
conf.Manifests["dummy_manifest"].MetadataPath = noPlanMetaPath
|
||||
return conf
|
||||
},
|
||||
errContains: "found no documents",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
helper, err := phase.NewHelper(tt.config(t))
|
||||
if tt.helperErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, helper)
|
||||
|
||||
actualCMap, actualErr := helper.ClusterMapAPIobj()
|
||||
if tt.errContains != "" {
|
||||
require.Error(t, actualErr)
|
||||
assert.Contains(t, actualErr.Error(), tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, actualErr)
|
||||
assert.Equal(t, tt.expectedCMap, actualCMap)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelperClusterMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
errContains string
|
||||
config func(t *testing.T) *config.Config
|
||||
}{
|
||||
{
|
||||
name: "Success phase list",
|
||||
config: testConfig,
|
||||
},
|
||||
{
|
||||
name: "Error no cluster map",
|
||||
config: func(t *testing.T) *config.Config {
|
||||
conf := testConfig(t)
|
||||
conf.Manifests["dummy_manifest"].MetadataPath = noPlanMetaPath
|
||||
return conf
|
||||
},
|
||||
errContains: "found no documents",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
helper, err := phase.NewHelper(tt.config(t))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, helper)
|
||||
|
||||
actualCMap, actualErr := helper.ClusterMap()
|
||||
if tt.errContains != "" {
|
||||
require.Error(t, actualErr)
|
||||
assert.Contains(t, actualErr.Error(), tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, actualErr)
|
||||
assert.NotNil(t, actualCMap)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelperExecutorDoc(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
|
@ -18,12 +18,10 @@ import (
|
|||
"io"
|
||||
"time"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/container"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/events"
|
||||
inventoryifc "opendev.org/airship/airshipctl/pkg/inventory/ifc"
|
||||
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
|
||||
)
|
||||
|
||||
// Executor interface should be implemented by each runner
|
||||
|
@ -39,8 +37,10 @@ type ExecutorStatus struct{}
|
|||
|
||||
// RunOptions holds options for run method
|
||||
type RunOptions struct {
|
||||
DryRun bool
|
||||
Timeout *time.Duration
|
||||
Kubeconfig string
|
||||
Context string
|
||||
DryRun bool
|
||||
Timeout *time.Duration
|
||||
}
|
||||
|
||||
// RenderOptions holds options for render method
|
||||
|
@ -58,9 +58,7 @@ type ExecutorConfig struct {
|
|||
SinkBasePath string
|
||||
TargetPath string
|
||||
|
||||
ClusterMap clustermap.ClusterMap
|
||||
ExecutorDocument document.Document
|
||||
KubeConfig kubeconfig.Interface
|
||||
BundleFactory document.BundleFactoryFunc
|
||||
PhaseConfigBundle document.Bundle
|
||||
Inventory inventoryifc.Inventory
|
||||
|
|
|
@ -16,7 +16,6 @@ package ifc
|
|||
|
||||
import (
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/inventory/ifc"
|
||||
)
|
||||
|
@ -31,8 +30,6 @@ type Helper interface {
|
|||
Plan(planID ID) (*v1alpha1.PhasePlan, error)
|
||||
ListPhases(o ListPhaseOptions) ([]*v1alpha1.Phase, error)
|
||||
ListPlans() ([]*v1alpha1.PhasePlan, error)
|
||||
ClusterMapAPIobj() (*v1alpha1.ClusterMap, error)
|
||||
ClusterMap() (clustermap.ClusterMap, error)
|
||||
ExecutorDoc(phaseID ID) (document.Document, error)
|
||||
PhaseBundleRoot() string
|
||||
Inventory() ifc.Inventory
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"io"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
)
|
||||
|
||||
// Phase provides a way to interact with a phase
|
||||
|
@ -67,5 +66,4 @@ type Client interface {
|
|||
PhaseByID(ID) (Phase, error)
|
||||
PlanByID(ID) (Plan, error)
|
||||
PhaseByAPIObj(*v1alpha1.Phase) (Phase, error)
|
||||
ClusterMap() (clustermap.ClusterMap, error)
|
||||
}
|
||||
|
|
|
@ -155,5 +155,4 @@ func TestRenderConfigBundle(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
// check that it contains phases and cluster map
|
||||
assert.Contains(t, buf.String(), "kind: Phase")
|
||||
assert.Contains(t, buf.String(), "kind: ClusterMap")
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
name: clusterctl-v1
|
||||
map:
|
||||
target:
|
||||
parent: ephemeral
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
||||
ephemeral:
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
|
@ -1,4 +1,3 @@
|
|||
resources:
|
||||
- kube_apply.yaml
|
||||
- kubernetes_apply.yaml
|
||||
- cluster_map.yaml
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
name: main-map
|
||||
map:
|
||||
target-cluster:
|
||||
parent: ephemeral
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
||||
ephemeral-cluster:
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
|
@ -1,5 +1,4 @@
|
|||
resources:
|
||||
- phases.yaml
|
||||
- executors.yaml
|
||||
- cluster-map.yaml
|
||||
- phaseplan.yaml
|
||||
- phaseplan.yaml
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
name: clusterctl-v1
|
||||
map:
|
||||
target:
|
||||
parent: ephemeral
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
||||
ephemeral:
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
|
@ -5,7 +5,6 @@ resources:
|
|||
- capi_init.yaml
|
||||
- clusterctl.yaml
|
||||
- kubernetes_apply.yaml
|
||||
- cluster_map.yaml
|
||||
- phase_no_docentrypoint.yaml
|
||||
- no_executor_phase.yaml
|
||||
- kubeapply_phase.yaml
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
name: clusterctl-v1
|
||||
map:
|
||||
target:
|
||||
parent: ephemeral
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
||||
ephemeral:
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
|
@ -6,7 +6,6 @@ resources:
|
|||
- capi_init.yaml
|
||||
- clusterctl.yaml
|
||||
- kubernetes_apply.yaml
|
||||
- cluster_map.yaml
|
||||
- phase_no_docentrypoint.yaml
|
||||
- no_executor_phase.yaml
|
||||
- kubeapply_phase.yaml
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: ClusterMap
|
||||
metadata:
|
||||
name: clusterctl-v1
|
||||
map:
|
||||
target:
|
||||
parent: ephemeral
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
||||
ephemeral:
|
||||
kubeconfigSources:
|
||||
- type: bundle
|
|
@ -1,5 +1,4 @@
|
|||
resources:
|
||||
- kube_apply.yaml
|
||||
- kubernetes_apply.yaml
|
||||
- cluster_map.yaml
|
||||
- validation_exec.yaml
|
||||
|
|
|
@ -68,7 +68,6 @@ func TestWriteOut(t *testing.T) {
|
|||
}
|
||||
|
||||
// Verify result contents
|
||||
// TODO (kkalynovskyi) make more reliable tests
|
||||
assert.Contains(t, b.String(), ob.Name)
|
||||
assert.Contains(t, b.String(), "airshiplabel: airshipit.org")
|
||||
assert.Regexp(t, regexp.MustCompile(`^---.*`), b.String())
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
|
||||
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
inventoryifc "opendev.org/airship/airshipctl/pkg/inventory/ifc"
|
||||
"opendev.org/airship/airshipctl/pkg/phase/ifc"
|
||||
|
@ -95,26 +94,6 @@ func (mh *MockHelper) ListPlans() ([]*v1alpha1.PhasePlan, error) {
|
|||
return val, args.Error(1)
|
||||
}
|
||||
|
||||
// ClusterMapAPIobj mock
|
||||
func (mh *MockHelper) ClusterMapAPIobj() (*v1alpha1.ClusterMap, error) {
|
||||
args := mh.Called()
|
||||
val, ok := args.Get(0).(*v1alpha1.ClusterMap)
|
||||
if !ok {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return val, args.Error(1)
|
||||
}
|
||||
|
||||
// ClusterMap mock
|
||||
func (mh *MockHelper) ClusterMap() (clustermap.ClusterMap, error) {
|
||||
args := mh.Called()
|
||||
val, ok := args.Get(0).(clustermap.ClusterMap)
|
||||
if !ok {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return val, args.Error(1)
|
||||
}
|
||||
|
||||
// ExecutorDoc mock
|
||||
func (mh *MockHelper) ExecutorDoc(phaseID ifc.ID) (document.Document, error) {
|
||||
args := mh.Called()
|
||||
|
|
|
@ -30,11 +30,10 @@ export WORKDIR="${AIRSHIP_CONFIG_MANIFEST_DIRECTORY}/${REPO_NAME}"
|
|||
if [[ -z "$EXTERNAL_KUBECONFIG" ]]; then
|
||||
# we want to take config from bundle - remove kubeconfig file so
|
||||
# airshipctl could regenerated it from kustomize
|
||||
[ -f "~/.airship/kubeconfig" ] && rm ~/.airship/kubeconfig
|
||||
[ -f "~/.airship/kubeconfig" ] && rm ~/.airship/kubeconfig && touch ~/.airship/kubeconfig
|
||||
# we need to use tmp file, because airshipctl uses it and fails
|
||||
# if we write directly
|
||||
airshipctl cluster get-kubeconfig > ~/.airship/tmp-kubeconfig
|
||||
mv ~/.airship/tmp-kubeconfig ~/.airship/kubeconfig
|
||||
airshipctl phase run secret-get
|
||||
fi
|
||||
|
||||
# Validate that we generated everything correctly
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue