Skip to content

⚠️ clusterctlv2 add implementation for clusterctl config providers #1864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions cmd/clusterctl/cmd/config_providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ limitations under the License.
package cmd

import (
"fmt"
"os"
"text/tabwriter"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client"
)

type configProvidersOptions struct {
Expand Down Expand Up @@ -80,6 +85,23 @@ func init() {
}

func runGetRepositories() error {
c, err := client.New(cfgFile)
if err != nil {
return err
}

repositoryList, err := c.GetProvidersConfig()
if err != nil {
return err
}

w := tabwriter.NewWriter(os.Stdout, 10, 4, 3, ' ', 0)
fmt.Fprintln(w, "NAME\tTYPE\tURL")
for _, r := range repositoryList {
fmt.Fprintf(w, "%s\t%s\t%s\n", r.Name(), r.Type(), r.URL())
}
w.Flush()

return nil
}

Expand Down
27 changes: 27 additions & 0 deletions cmd/clusterctl/pkg/client/alias.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// Alias creates local alias for types defined in the low-level library.
// By using local alias, ensures that the users of the API will be forced to import the clusterctl high-level library only.

// Provider defines a provider configuration.
type Provider config.Provider
74 changes: 74 additions & 0 deletions cmd/clusterctl/pkg/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

// Client is exposes the clusterctl high-level client library
type Client interface {
GetProvidersConfig() ([]Provider, error)
}

// clusterctlClient implements Client.
type clusterctlClient struct {
configClient config.Client
}

// ensure clusterctlClient implements Client.
var _ Client = &clusterctlClient{}

// NewOptions carries the options supported by New
type NewOptions struct {
injectConfig config.Client
}

// Option is a configuration option supplied to New
type Option func(*NewOptions)

// InjectConfig implements a New Option that allows to override the default configuration client used by clusterctl.
func InjectConfig(config config.Client) Option {
return func(c *NewOptions) {
c.injectConfig = config
}
}

// New returns a configClient.
func New(path string, options ...Option) (Client, error) {
return newClusterctlClient(path, options...)
}

func newClusterctlClient(path string, options ...Option) (*clusterctlClient, error) {
cfg := &NewOptions{}
for _, o := range options {
o(cfg)
}

configClient := cfg.injectConfig
if configClient == nil {
c, err := config.New(path)
if err != nil {
return nil, err
}
configClient = c
}

return &clusterctlClient{
configClient: configClient,
}, nil
}
109 changes: 109 additions & 0 deletions cmd/clusterctl/pkg/client/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"testing"

clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/internal/test"
)

// dummy test to document fakeClient usage
func TestNewFakeClient(t *testing.T) {
// create a fake config with a provider named P1 and a variable named var
repository1Config := config.NewProvider("p1", "url", clusterctlv1.CoreProviderType)

config1 := newFakeConfig().
WithVar("var", "value").
WithProvider(repository1Config)

// create a new fakeClient using the fake config
newFakeClient(config1)
}

type fakeClient struct {
configClient config.Client
internalclient *clusterctlClient
}

var _ Client = &fakeClient{}

func (f fakeClient) GetProvidersConfig() ([]Provider, error) {
return f.internalclient.GetProvidersConfig()
}

// newFakeClient return a fake implementation of the client for high-level clusterctl library, based on th given config.
func newFakeClient(configClient config.Client) *fakeClient {

fake := &fakeClient{}

fake.configClient = configClient
if fake.configClient == nil {
fake.configClient = newFakeConfig()
}

fake.internalclient, _ = newClusterctlClient("fake-config",
InjectConfig(fake.configClient),
)

return fake
}

// newFakeConfig return a fake implementation of the client for low-level config library.
// The implementation uses a FakeReader that stores configuration settings in a config map; you can use
// the WithVar or WithProvider methods to set the config map values.
func newFakeConfig() *fakeConfigClient {
fakeReader := test.NewFakeReader()

client, _ := config.New("fake-config", config.InjectReader(fakeReader))

return &fakeConfigClient{
fakeReader: fakeReader,
internalclient: client,
}
}

type fakeConfigClient struct {
fakeReader *test.FakeReader
internalclient config.Client
}

var _ config.Client = &fakeConfigClient{}

func (f fakeConfigClient) Providers() config.ProvidersClient {
return f.internalclient.Providers()
}

func (f fakeConfigClient) Variables() config.VariablesClient {
return f.internalclient.Variables()
}

func (f *fakeConfigClient) WithVar(key, value string) *fakeConfigClient {
f.fakeReader.WithVar(key, value)
return f
}

func (f *fakeConfigClient) WithProvider(provider config.Provider) *fakeConfigClient {
f.fakeReader.WithProvider(provider.Name(), provider.Type(), provider.URL())
return f
}

var (
bootstrapProviderConfig = config.NewProvider("bootstrap", "url", clusterctlv1.BootstrapProviderType)
)
32 changes: 32 additions & 0 deletions cmd/clusterctl/pkg/client/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

func (c *clusterctlClient) GetProvidersConfig() ([]Provider, error) {
r, err := c.configClient.Providers().List()
if err != nil {
return nil, err
}

// Provider is an alias for config.Provider; this makes the conversion
rr := make([]Provider, len(r))
for i, provider := range r {
rr[i] = provider
}

return rr, nil
}
87 changes: 87 additions & 0 deletions cmd/clusterctl/pkg/client/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package client

import (
"testing"

"sigs.k8s.io/cluster-api/cmd/clusterctl/pkg/client/config"
)

func Test_clusterctlClient_GetProvidersConfig(t *testing.T) {
type field struct {
client Client
}
tests := []struct {
name string
field field
wantProviders []string
wantErr bool
}{
{
name: "Returns default providers",
field: field{
client: newFakeClient(newFakeConfig()),
},
wantProviders: []string{
"aws",
config.ClusterAPIName,
"docker",
"kubeadm",
"vsphere",
},
wantErr: false,
},
{
name: "Returns default providers and custom providers if defined",
field: field{
client: newFakeClient(newFakeConfig().WithProvider(bootstrapProviderConfig)),
},
wantProviders: []string{
"aws",
bootstrapProviderConfig.Name(),
config.ClusterAPIName,
"docker",
"kubeadm",
"vsphere",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.field.client.GetProvidersConfig()
if (err != nil) != tt.wantErr {
t.Errorf("GetProvidersConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}

if len(got) != len(tt.wantProviders) {
t.Errorf("Init() got = %v items, want %v items", len(got), len(tt.wantProviders))
return
}

for i, g := range got {
w := tt.wantProviders[i]

if g.Name() != w {
t.Errorf("GetProvidersConfig(), Item[%d].Name() got = %v, want = %v ", i, g.Name(), w)
}
}
})
}
}
1 change: 1 addition & 0 deletions test/infrastructure/docker/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
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-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down