Make newcomer experience smooth

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-11-02 16:24:35 +01:00
parent 3066a1cdad
commit af7aebf8cf
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
9 changed files with 423 additions and 101 deletions

View File

@ -20,17 +20,22 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath"
"sort"
"strings" "strings"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"gopkg.in/ini.v1"
"github.com/docker/compose-cli/context/store" "github.com/docker/compose-cli/context/store"
"github.com/docker/compose-cli/errdefs" "github.com/docker/compose-cli/errdefs"
"github.com/docker/compose-cli/prompt" "github.com/docker/compose-cli/prompt"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
) )
func getEnvVars() ContextParams { func getEnvVars() ContextParams {
@ -57,66 +62,77 @@ func getEnvVars() ContextParams {
} }
type contextCreateAWSHelper struct { type contextCreateAWSHelper struct {
user prompt.UI user prompt.UI
availableRegions func(opts *ContextParams) ([]string, error)
} }
func newContextCreateHelper() contextCreateAWSHelper { func newContextCreateHelper() contextCreateAWSHelper {
return contextCreateAWSHelper{ return contextCreateAWSHelper{
user: prompt.User{}, user: prompt.User{},
availableRegions: listAvailableRegions,
} }
} }
func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) { func (h contextCreateAWSHelper) createContextData(_ context.Context, opts ContextParams) (interface{}, string, error) {
if opts.CredsFromEnv { if opts.CredsFromEnv {
// Explicit creation from ENV variables
ecsCtx, descr := h.createContext(&opts) ecsCtx, descr := h.createContext(&opts)
return ecsCtx, descr, nil return ecsCtx, descr, nil
} } else if opts.AccessKey != "" && opts.SecretKey != "" {
if opts.Profile != "" { // Explicit creation using keys
err := h.createProfileFromCredentials(&opts)
if err != nil {
return nil, "", err
}
} else if opts.Profile != "" {
// Excplicit creation by selecting a profile
// check profile exists // check profile exists
profilesList, err := getProfiles() profilesList, err := getProfiles()
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
if !contains(profilesList, opts.Profile) { if !contains(profilesList, opts.Profile) {
return nil, "", fmt.Errorf("profile %q not found", opts.Profile) return nil, "", errors.Wrapf(errdefs.ErrNotFound, "profile %q not found", opts.Profile)
} }
ecsCtx, descr := h.createContext(&opts) } else {
return ecsCtx, descr, nil // interactive
} var options []string
options := []string{ var actions []func(params *ContextParams) error
"An existing AWS profile",
"A new AWS profile",
"AWS environment variables",
}
selected, err := h.user.Select("Create a Docker context using:", options) if _, err := os.Stat(getAWSConfigFile()); err == nil {
if err != nil { // User has .aws/config file, so we can offer to select one of his profiles
if err == terminal.InterruptErr { options = append(options, "An existing AWS profile")
return nil, "", errdefs.ErrCanceled actions = append(actions, h.selectFromLocalProfile)
}
options = append(options, "AWS secret and token credentials")
actions = append(actions, h.createProfileFromCredentials)
options = append(options, "AWS environment variables")
actions = append(actions, func(params *ContextParams) error {
opts.CredsFromEnv = true
return nil
})
selected, err := h.user.Select("Create a Docker context using:", options)
if err != nil {
if err == terminal.InterruptErr {
return nil, "", errdefs.ErrCanceled
}
return nil, "", err
}
err = actions[selected](&opts)
if err != nil {
return nil, "", err
} }
return nil, "", err
} }
switch selected {
case 0:
err = h.selectFromLocalProfile(&opts)
case 1:
err = h.createProfileFromCredentials(&opts)
case 2:
opts.CredsFromEnv = true
}
if err != nil {
return nil, "", err
}
ecsCtx, descr := h.createContext(&opts) ecsCtx, descr := h.createContext(&opts)
return ecsCtx, descr, nil return ecsCtx, descr, nil
} }
func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, string) { func (h contextCreateAWSHelper) createContext(c *ContextParams) (interface{}, string) {
if c.Profile == "default" {
c.Profile = ""
}
var description string var description string
if c.CredsFromEnv { if c.CredsFromEnv {
@ -148,56 +164,42 @@ func (h contextCreateAWSHelper) selectFromLocalProfile(opts *ContextParams) erro
} }
func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams) error { func (h contextCreateAWSHelper) createProfileFromCredentials(opts *ContextParams) error {
accessKey, secretKey, err := h.askCredentials() if opts.AccessKey == "" || opts.SecretKey == "" {
if err != nil { fmt.Println("Retrieve or create AWS Access Key and Secret on https://console.aws.amazon.com/iam/home?#security_credential")
return err accessKey, secretKey, err := h.askCredentials()
} if err != nil {
opts.AccessKey = accessKey return err
opts.SecretKey = secretKey }
// we need a region set -- either read it from profile or prompt user opts.AccessKey = accessKey
// prompt for the region to use with this context opts.SecretKey = secretKey
opts.Region, err = h.chooseRegion(opts.Region, opts.Profile)
if err != nil {
return err
}
// save as a profile
if opts.Profile == "" {
opts.Profile = opts.Name
}
// check profile does not already exist
profilesList, err := getProfiles()
if err != nil {
return err
}
if contains(profilesList, opts.Profile) {
return fmt.Errorf("profile %q already exists", opts.Profile)
}
fmt.Printf("Saving to profile %q\n", opts.Profile)
// context name used as profile name
err = h.saveCredentials(opts.Name, opts.AccessKey, opts.SecretKey)
if err != nil {
return err
}
return h.saveRegion(opts.Name, opts.Region)
}
func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
p := credentials.SharedCredentialsProvider{Profile: profile}
_, err := p.Retrieve()
if err == nil {
return fmt.Errorf("credentials already exist")
} }
if err.(awserr.Error).Code() == "SharedCredsLoad" && err.(awserr.Error).Message() == "failed to load shared credentials file" { if opts.Region == "" {
_, err := os.Create(p.Filename) err := h.chooseRegion(opts)
if err != nil { if err != nil {
return err return err
} }
} }
credIni, err := ini.Load(p.Filename) // save as a profile
if opts.Profile == "" {
opts.Profile = "default"
}
// context name used as profile name
err := h.saveCredentials(opts.Profile, opts.AccessKey, opts.SecretKey)
if err != nil { if err != nil {
return err return err
} }
return h.saveRegion(opts.Profile, opts.Region)
}
func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID string, secretAccessKey string) error {
file := getAWSCredentialsFile()
err := os.MkdirAll(filepath.Dir(file), 0700)
if err != nil {
return err
}
credIni := ini.Empty()
section, err := credIni.NewSection(profile) section, err := credIni.NewSection(profile)
if err != nil { if err != nil {
return err return err
@ -210,7 +212,7 @@ func (h contextCreateAWSHelper) saveCredentials(profile string, accessKeyID stri
if err != nil { if err != nil {
return err return err
} }
return credIni.SaveTo(p.Filename) return credIni.SaveTo(file)
} }
func (h contextCreateAWSHelper) saveRegion(profile, region string) error { func (h contextCreateAWSHelper) saveRegion(profile, region string) error {
@ -218,7 +220,7 @@ func (h contextCreateAWSHelper) saveRegion(profile, region string) error {
return nil return nil
} }
// loads ~/.aws/config // loads ~/.aws/config
awsConfig := defaults.SharedConfigFilename() awsConfig := getAWSConfigFile()
configIni, err := ini.Load(awsConfig) configIni, err := ini.Load(awsConfig)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
@ -249,8 +251,8 @@ func getProfiles() ([]string, error) {
profiles := []string{} profiles := []string{}
// parse both .aws/credentials and .aws/config for profiles // parse both .aws/credentials and .aws/config for profiles
configFiles := map[string]bool{ configFiles := map[string]bool{
defaults.SharedCredentialsFilename(): false, getAWSCredentialsFile(): false,
defaults.SharedConfigFilename(): true, getAWSConfigFile(): true,
} }
for f, prefix := range configFiles { for f, prefix := range configFiles {
sections, err := loadIniFile(f, prefix) sections, err := loadIniFile(f, prefix)
@ -267,6 +269,10 @@ func getProfiles() ([]string, error) {
} }
} }
} }
sort.Slice(profiles, func(i, j int) bool {
return profiles[i] < profiles[j]
})
return profiles, nil return profiles, nil
} }
@ -316,32 +322,49 @@ func getRegion(profile string) (string, error) {
region := getProfileRegion(profile) region := getProfileRegion(profile)
if region == "" { if region == "" {
region = getProfileRegion("default") region = getProfileRegion("default")
if region == "" { }
return "us-east-1", nil if region == "" {
} // fallback to AWS default
return region, nil region = "us-east-1"
} }
return region, nil return region, nil
} }
func (h contextCreateAWSHelper) chooseRegion(region string, profile string) (string, error) { func (h contextCreateAWSHelper) chooseRegion(opts *ContextParams) error {
suggestion := region regions, err := h.availableRegions(opts)
if suggestion == "" { if err != nil {
region, err := getRegion(profile) return err
if err != nil {
return "", err
}
suggestion = region
} }
// promp user for region // promp user for region
region, err := h.user.Input("Region", suggestion) selected, err := h.user.Select("Region", regions)
if err != nil { if err != nil {
return "", err return err
} }
if region == "" { opts.Region = regions[selected]
return "", fmt.Errorf("region cannot be empty") return nil
}
func listAvailableRegions(opts *ContextParams) ([]string, error) {
// Setup SDK with credentials, will also validate those
session, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{
Credentials: credentials.NewStaticCredentials(opts.AccessKey, opts.SecretKey, ""),
Region: aws.String("us-east-1"),
},
})
if err != nil {
return nil, err
} }
return region, nil
desc, err := ec2.New(session).DescribeRegions(&ec2.DescribeRegionsInput{})
if err != nil {
return nil, err
}
var regions []string
for _, r := range desc.Regions {
regions = append(regions, aws.StringValue(r.RegionName))
}
return regions, nil
} }
func (h contextCreateAWSHelper) askCredentials() (string, string, error) { func (h contextCreateAWSHelper) askCredentials() (string, string, error) {
@ -384,3 +407,19 @@ func loadIniFile(path string, prefix bool) (map[string]ini.Section, error) {
} }
return profiles, nil return profiles, nil
} }
func getAWSConfigFile() string {
awsConfig, ok := os.LookupEnv("AWS_CONFIG_FILE")
if !ok {
awsConfig = defaults.SharedConfigFilename()
}
return awsConfig
}
func getAWSCredentialsFile() string {
awsConfig, ok := os.LookupEnv("AWS_SHARED_CREDENTIALS_FILE")
if !ok {
awsConfig = defaults.SharedCredentialsFilename()
}
return awsConfig
}

173
ecs/context_test.go Normal file
View File

@ -0,0 +1,173 @@
/*
Copyright 2020 Docker Compose CLI 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 ecs
import (
"context"
"os"
"testing"
"github.com/docker/compose-cli/context/store"
"github.com/docker/compose-cli/prompt"
"github.com/golang/mock/gomock"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
"gotest.tools/v3/golden"
)
func TestCreateContextDataFromEnv(t *testing.T) {
c := contextCreateAWSHelper{
user: nil,
}
data, desc, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
CredsFromEnv: true,
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).CredentialsFromEnv, true)
assert.Equal(t, desc, "credentials read from environment")
}
func TestCreateContextDataByKeys(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
c := contextCreateAWSHelper{
user: nil,
}
data, _, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
AccessKey: "ABCD",
SecretKey: "X&123",
Region: "eu-west-3",
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
s := golden.Get(t, dir.Join("config"))
golden.Assert(t, string(s), "context/by-keys/config.golden")
s = golden.Get(t, dir.Join("credentials"))
golden.Assert(t, string(s), "context/by-keys/credentials.golden")
}
func TestCreateContextDataFromProfile(t *testing.T) {
os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden") // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
c := contextCreateAWSHelper{
user: nil,
}
data, _, err := c.createContextData(context.TODO(), ContextParams{
Name: "test",
Profile: "foo",
})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "foo")
}
func TestCreateContextDataFromEnvInteractive(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).CredentialsFromEnv, true)
}
func TestCreateContextDataByKeysInteractive(t *testing.T) {
dir := fs.NewDir(t, "aws")
os.Setenv("AWS_CONFIG_FILE", dir.Join("config")) // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", dir.Join("credentials")) // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
availableRegions: func(opts *ContextParams) ([]string, error) {
return []string{"us-east-1", "eu-west-3"}, nil
},
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(0, nil)
ui.EXPECT().Input("AWS Access Key ID", gomock.Any()).Return("ABCD", nil)
ui.EXPECT().Password("Enter AWS Secret Access Key").Return("X&123", nil)
ui.EXPECT().Select("Region", []string{"us-east-1", "eu-west-3"}).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "default")
s := golden.Get(t, dir.Join("config"))
golden.Assert(t, string(s), "context/by-keys/config.golden")
s = golden.Get(t, dir.Join("credentials"))
golden.Assert(t, string(s), "context/by-keys/credentials.golden")
}
func TestCreateContextDataByProfileInteractive(t *testing.T) {
os.Setenv("AWS_CONFIG_FILE", "testdata/context/by-profile/config.golden") // nolint:errcheck
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "testdata/context/by-profile/credentials.golden") // nolint:errcheck
defer os.Unsetenv("AWS_CONFIG_FILE") // nolint:errcheck
defer os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE") // nolint:errcheck
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ui := prompt.NewMockUI(ctrl)
c := contextCreateAWSHelper{
user: ui,
}
ui.EXPECT().Select("Create a Docker context using:", gomock.Any()).Return(0, nil)
ui.EXPECT().Select("Select AWS Profile", []string{"default", "foo"}).Return(1, nil)
data, _, err := c.createContextData(context.TODO(), ContextParams{})
assert.NilError(t, err)
assert.Equal(t, data.(store.EcsContext).Profile, "foo")
}

View File

@ -0,0 +1,3 @@
[profile default]
region = eu-west-3

View File

@ -0,0 +1,4 @@
[default]
aws_access_key_id = ABCD
aws_secret_access_key = X&123

View File

@ -0,0 +1,3 @@
[profile foo]
region = eu-west-3

View File

@ -0,0 +1,4 @@
[foo]
aws_access_key_id = ABCD
aws_secret_access_key = X&123

1
go.sum
View File

@ -746,6 +746,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -20,6 +20,8 @@ import (
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
) )
//go:generate mockgen -destination=./prompt_mock.go -self_package "github.com/docker/compose-cli/prompt" -package=prompt . UI
// UI - prompt user input // UI - prompt user input
type UI interface { type UI interface {
Select(message string, options []string) (int, error) Select(message string, options []string) (int, error)

93
prompt/prompt_mock.go Normal file
View File

@ -0,0 +1,93 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/docker/compose-cli/prompt (interfaces: UI)
// Package prompt is a generated GoMock package.
package prompt
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockUI is a mock of UI interface
type MockUI struct {
ctrl *gomock.Controller
recorder *MockUIMockRecorder
}
// MockUIMockRecorder is the mock recorder for MockUI
type MockUIMockRecorder struct {
mock *MockUI
}
// NewMockUI creates a new mock instance
func NewMockUI(ctrl *gomock.Controller) *MockUI {
mock := &MockUI{ctrl: ctrl}
mock.recorder = &MockUIMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockUI) EXPECT() *MockUIMockRecorder {
return m.recorder
}
// Confirm mocks base method
func (m *MockUI) Confirm(arg0 string, arg1 bool) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Confirm", arg0, arg1)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Confirm indicates an expected call of Confirm
func (mr *MockUIMockRecorder) Confirm(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Confirm", reflect.TypeOf((*MockUI)(nil).Confirm), arg0, arg1)
}
// Input mocks base method
func (m *MockUI) Input(arg0, arg1 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Input", arg0, arg1)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Input indicates an expected call of Input
func (mr *MockUIMockRecorder) Input(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Input", reflect.TypeOf((*MockUI)(nil).Input), arg0, arg1)
}
// Password mocks base method
func (m *MockUI) Password(arg0 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Password", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Password indicates an expected call of Password
func (mr *MockUIMockRecorder) Password(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Password", reflect.TypeOf((*MockUI)(nil).Password), arg0)
}
// Select mocks base method
func (m *MockUI) Select(arg0 string, arg1 []string) (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Select", arg0, arg1)
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Select indicates an expected call of Select
func (mr *MockUIMockRecorder) Select(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockUI)(nil).Select), arg0, arg1)
}