diff --git a/cmd/common/common.go b/cmd/common/common.go index b357b587..eef9fa19 100644 --- a/cmd/common/common.go +++ b/cmd/common/common.go @@ -7,6 +7,7 @@ import ( "github.com/Jason-ZW/autok3s/pkg/common" "github.com/Jason-ZW/autok3s/pkg/providers" + "github.com/Jason-ZW/autok3s/pkg/types" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -14,6 +15,17 @@ import ( "github.com/spf13/viper" ) +var ( + Provider = "" + P providers.Provider + + SSH = &types.SSH{ + SSHKey: "~/.ssh/id_rsa", + User: "root", + Port: "22", + } +) + func BindPFlags(cmd *cobra.Command, p providers.Provider) { name, err := cmd.Flags().GetString("provider") if err != nil { @@ -21,7 +33,7 @@ func BindPFlags(cmd *cobra.Command, p providers.Provider) { } cmd.Flags().Visit(func(f *pflag.Flag) { - if IsCredentialFlag(f.Name, p.GetCredentialFlags(cmd)) { + if IsCredentialFlag(f.Name, p.BindCredentialFlags()) { if err := viper.BindPFlag(fmt.Sprintf(common.BindPrefix, name, f.Name), f); err != nil { logrus.Fatalln(err) } diff --git a/cmd/create.go b/cmd/create.go index 3f869d01..260479e4 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/Jason-ZW/autok3s/cmd/common" "github.com/Jason-ZW/autok3s/pkg/providers" - "github.com/Jason-ZW/autok3s/pkg/types" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -16,22 +15,13 @@ var ( Short: "Create k3s cluster", Example: ` autok3s create --provider alibaba`, } - - provider = "" - p providers.Provider - - ssh = &types.SSH{ - SSHKeyPath: "~/.ssh/id_rsa", - User: "root", - Port: "22", - } ) func init() { - createCmd.Flags().StringVarP(&provider, "provider", "p", provider, "Provider is a module which provides an interface for managing cloud resources") - createCmd.Flags().StringVar(&ssh.User, "sshUser", ssh.User, "SSH user for host") - createCmd.Flags().StringVar(&ssh.Port, "sshPort", ssh.Port, "SSH port for host") - createCmd.Flags().StringVar(&ssh.SSHKeyPath, "sshKeyPath", ssh.SSHKeyPath, "SSH private key path") + createCmd.Flags().StringVarP(&common.Provider, "provider", "p", common.Provider, "Provider is a module which provides an interface for managing cloud resources") + createCmd.Flags().StringVar(&common.SSH.User, "user", common.SSH.User, "SSH user for host") + createCmd.Flags().StringVar(&common.SSH.Port, "ssh-port", common.SSH.Port, "SSH port for host") + createCmd.Flags().StringVar(&common.SSH.SSHKey, "ssh-key", common.SSH.SSHKey, "SSH private key path") } func CreateCommand() *cobra.Command { @@ -41,16 +31,16 @@ func CreateCommand() *cobra.Command { if reg, err := providers.Register(pStr); err != nil { logrus.Fatalln(err) } else { - p = reg + common.P = reg } - createCmd.Flags().AddFlagSet(p.GetCredentialFlags(createCmd)) - createCmd.Flags().AddFlagSet(p.GetCreateFlags(createCmd)) + createCmd.Flags().AddFlagSet(common.P.GetCredentialFlags(createCmd)) + createCmd.Flags().AddFlagSet(common.P.GetCreateFlags(createCmd)) } createCmd.Run = func(cmd *cobra.Command, args []string) { // must bind after dynamic provider flags loaded. - common.BindPFlags(cmd, p) + common.BindPFlags(cmd, common.P) // read options from config. if err := viper.ReadInConfig(); err != nil { @@ -62,7 +52,7 @@ func CreateCommand() *cobra.Command { logrus.Fatalln(err) } - if err := p.CreateK3sCluster(ssh); err != nil { + if err := common.P.CreateK3sCluster(common.SSH); err != nil { logrus.Fatalln(err) } } diff --git a/pkg/hosts/dialer.go b/pkg/hosts/dialer.go index 0938438a..7b2a5b55 100644 --- a/pkg/hosts/dialer.go +++ b/pkg/hosts/dialer.go @@ -31,11 +31,11 @@ type Host struct { } type dialer struct { - signer ssh.Signer - sshKeyString string - sshAddress string - username string - netConn string + signer ssh.Signer + sshKey string + sshAddress string + username string + netConn string } type DialersOptions struct { @@ -73,14 +73,13 @@ func newDialer(h *Host, kind string) (*dialer, error) { } d = &dialer{ - sshAddress: fmt.Sprintf("%s:%s", h.PublicIPAddress[0], h.Port), - username: h.User, - sshKeyString: h.SSHKey, + sshAddress: fmt.Sprintf("%s:%s", h.PublicIPAddress[0], h.Port), + username: h.User, } - if d.sshKeyString == "" { + if d.sshKey == "" { var err error - d.sshKeyString, err = utils.SSHPrivateKeyPath(h.SSHKeyPath) + d.sshKey, err = utils.SSHPrivateKeyPath(h.SSHKey) if err != nil { return nil, err } @@ -95,7 +94,7 @@ func newDialer(h *Host, kind string) (*dialer, error) { } func (d *dialer) getSSHTunnelConnection() (*ssh.Client, error) { - cfg, err := utils.GetSSHConfig(d.username, d.sshKeyString) + cfg, err := utils.GetSSHConfig(d.username, d.sshKey) if err != nil { return nil, err } diff --git a/pkg/providers/alibaba/alibaba.go b/pkg/providers/alibaba/alibaba.go index 017f6fe2..a993d283 100644 --- a/pkg/providers/alibaba/alibaba.go +++ b/pkg/providers/alibaba/alibaba.go @@ -22,8 +22,8 @@ import ( ) const ( - accessKeyID = "accessKeyID" - accessKeySecret = "accessKeySecret" + accessKeyID = "access-key" + accessKeySecret = "access-secret" imageID = "ubuntu_18_04_x64_20G_alibase_20200618.vhd" instanceType = "ecs.c6.large" internetMaxBandwidthOut = "50" @@ -60,8 +60,8 @@ func NewProvider() *Alibaba { Options: alibaba.Options{ DiskCategory: diskCategory, DiskSize: diskSize, - ImageID: imageID, - InstanceType: instanceType, + Image: imageID, + Type: instanceType, InternetMaxBandwidthOut: internetMaxBandwidthOut, }, Status: types.Status{ @@ -133,13 +133,13 @@ func (p *Alibaba) generateClientSDK() error { func (p *Alibaba) runInstances(num int, master bool) error { request := ecs.CreateRunInstancesRequest() request.Scheme = "https" - request.InstanceType = p.InstanceType - request.ImageId = p.ImageID - request.VSwitchId = p.VSwitchID - request.KeyPairName = p.KeyPairName + request.InstanceType = p.Type + request.ImageId = p.Image + request.VSwitchId = p.VSwitch + request.KeyPairName = p.KeyPair request.SystemDiskCategory = p.DiskCategory request.SystemDiskSize = p.DiskSize - request.SecurityGroupId = p.SecurityGroupID + request.SecurityGroupId = p.SecurityGroup outBandWidth, _ := strconv.Atoi(p.InternetMaxBandwidthOut) request.InternetMaxBandwidthOut = requests.NewInteger(outBandWidth) request.Amount = requests.NewInteger(num) @@ -242,7 +242,7 @@ func (p *Alibaba) assembleInstanceStatus(ssh *types.SSH) (*types.Cluster, error) v := value.(types.Node) v.Port = ssh.Port v.User = ssh.User - v.SSHKeyPath = ssh.SSHKeyPath + v.SSHKey = ssh.SSHKey if v.Master { p.Status.MasterNodes = append(p.Status.MasterNodes, v) } else { @@ -271,21 +271,27 @@ func (p *Alibaba) describeInstances() (*ecs.DescribeInstancesResponse, error) { return response, nil } -func (p *Alibaba) isClusterExist() bool { +func (p *Alibaba) isClusterExist() (bool, error) { request := ecs.CreateDescribeInstancesRequest() request.Scheme = "https" request.InstanceName = strings.ToLower(fmt.Sprintf(common.WildcardInstanceName, p.Name)) response, err := p.c.DescribeInstances(request) - if err == nil && len(response.Instances.Instance) == 0 { - return true + if err != nil || len(response.Instances.Instance) > 0 { + return false, err } - return false + return true, nil } func (p *Alibaba) preflight() error { - if !p.isClusterExist() { + exist, err := p.isClusterExist() + + if err != nil { + return err + } + + if !exist { return errors.New(fmt.Sprintf("[%s] calling preflight error: cluster name `%s` already exist\n", p.GetProviderName(), p.Name)) } diff --git a/pkg/providers/alibaba/flag.go b/pkg/providers/alibaba/flag.go index 3fa3a360..84942cc6 100644 --- a/pkg/providers/alibaba/flag.go +++ b/pkg/providers/alibaba/flag.go @@ -25,69 +25,69 @@ func (p *Alibaba) GetCreateFlags(cmd *cobra.Command) *pflag.FlagSet { P: &p.Region, V: p.Region, ShortHand: "r", - Usage: "Regions are physical locations (data centers) that spread all over the world to reduce the network latency", + Usage: "Physical locations (data centers) that spread all over the world to reduce the network latency", Required: true, }, { - Name: "keyPairName", - P: &p.KeyPairName, - V: p.KeyPairName, + Name: "key-pair", + P: &p.KeyPair, + V: p.KeyPair, ShortHand: "k", - Usage: "KeyPairName is used to connect to an instance", + Usage: "Used to connect to an instance", Required: true, }, { - Name: "imageID", - P: &p.ImageID, - V: p.ImageID, + Name: "image", + P: &p.Image, + V: p.Image, ShortHand: "i", - Usage: "ImageID is used to specify the image to be used by the instance", + Usage: "Used to specify the image to be used by the instance", Required: true, }, { - Name: "instanceType", - P: &p.InstanceType, - V: p.InstanceType, + Name: "type", + P: &p.Type, + V: p.Type, ShortHand: "t", - Usage: "InstanceType is used to specify the type to be used by the instance", + Usage: "Used to specify the type to be used by the instance", Required: true, }, { - Name: "vSwitchID", - P: &p.VSwitchID, - V: p.VSwitchID, + Name: "v-switch", + P: &p.VSwitch, + V: p.VSwitch, ShortHand: "v", - Usage: "VSwitchID is used to specify the vSwitch to be used by the instance", + Usage: "Used to specify the vSwitch to be used by the instance", Required: true, }, { - Name: "diskCategory", + Name: "disk-category", P: &p.DiskCategory, V: p.DiskCategory, - Usage: "diskCategory is used to specify the system disk category used by the instance", + Usage: "Used to specify the system disk category used by the instance", Required: true, }, { - Name: "diskSize", + Name: "disk-size", P: &p.DiskSize, V: p.DiskSize, - Usage: "diskSize is used to specify the system disk size used by the instance", + Usage: "Used to specify the system disk size used by the instance", Required: true, }, { - Name: "securityGroupID", - P: &p.SecurityGroupID, - V: p.SecurityGroupID, + Name: "security-group", + P: &p.SecurityGroup, + V: p.SecurityGroup, ShortHand: "s", - Usage: "securityGroupID is used to specify the security group used by the instance", + Usage: "Used to specify the security group used by the instance", Required: true, }, { - Name: "InternetMaxBandwidthOut", + Name: "internet-max-bandwidth-out", P: &p.InternetMaxBandwidthOut, V: p.InternetMaxBandwidthOut, ShortHand: "o", - Usage: "internetMaxBandwidthOut is used to specify the maximum out flow of the instance internet", + Usage: "Used to specify the maximum out flow of the instance internet", Required: true, }, { @@ -144,33 +144,30 @@ func (p *Alibaba) GetCredentialFlags(cmd *cobra.Command) *pflag.FlagSet { fs := []types.Flag{ { Name: accessKeyID, - P: &p.AccessKeyID, - V: p.AccessKeyID, - Usage: "User access key ID.", + P: &p.AccessKey, + V: p.AccessKey, + Usage: "User access key ID", Required: true, }, { Name: accessKeySecret, - P: &p.AccessKeySecret, - V: p.AccessKeySecret, - Usage: "User access key secret.", + P: &p.AccessSecret, + V: p.AccessSecret, + Usage: "User access key secret", Required: true, }, } - nfs := pflag.NewFlagSet("", pflag.ContinueOnError) - for _, f := range fs { if f.ShortHand == "" { if cmd.Flags().Lookup(f.Name) == nil { cmd.Flags().StringVar(f.P, f.Name, f.V, f.Usage) - nfs.StringVar(f.P, f.Name, f.V, f.Usage) } } else { if cmd.Flags().Lookup(f.Name) == nil { cmd.Flags().StringVarP(f.P, f.Name, f.ShortHand, f.V, f.Usage) - nfs.StringVarP(f.P, f.Name, f.ShortHand, f.V, f.Usage) } + } } @@ -190,5 +187,12 @@ func (p *Alibaba) GetCredentialFlags(cmd *cobra.Command) *pflag.FlagSet { return errors.New(fmt.Sprintf("required flags(s) \"%s\" not set\n", errFlags)) } + return cmd.Flags() +} + +func (p *Alibaba) BindCredentialFlags() *pflag.FlagSet { + nfs := pflag.NewFlagSet("", pflag.ContinueOnError) + nfs.StringVar(&p.AccessKey, accessKeyID, p.AccessKey, "User access key ID") + nfs.StringVar(&p.AccessSecret, accessKeySecret, p.AccessSecret, "User access key secret") return nfs } diff --git a/pkg/providers/provider.go b/pkg/providers/provider.go index 1926c215..36fa1a95 100644 --- a/pkg/providers/provider.go +++ b/pkg/providers/provider.go @@ -14,6 +14,8 @@ type Provider interface { GetProviderName() string GetCreateFlags(cmd *cobra.Command) *pflag.FlagSet GetCredentialFlags(cmd *cobra.Command) *pflag.FlagSet + // Use this method to bind Viper, although it is somewhat repetitive. + BindCredentialFlags() *pflag.FlagSet CreateK3sCluster(ssh *types.SSH) error } diff --git a/pkg/types/alibaba/alibaba.go b/pkg/types/alibaba/alibaba.go index 8ef6a9e5..a3733b02 100644 --- a/pkg/types/alibaba/alibaba.go +++ b/pkg/types/alibaba/alibaba.go @@ -6,15 +6,15 @@ var ( ) type Options struct { - AccessKeyID string `json:"accessKeyID,omitempty"` - AccessKeySecret string `json:"accessKeySecret,omitempty"` - DiskCategory string `json:"diskCategory,omitempty"` - DiskSize string `json:"diskSize,omitempty"` - ImageID string `json:"imageID,omitempty"` - InstanceType string `json:"instanceType,omitempty"` - KeyPairName string `json:"keyPairName,omitempty"` - Region string `json:"region,omitempty"` - VSwitchID string `json:"vSwitchID,omitempty"` - SecurityGroupID string `json:"securityGroupID,omitempty"` - InternetMaxBandwidthOut string `json:"internetMaxBandwidthOut,omitempty"` + AccessKey string `json:"access-key,omitempty" yaml:"access-key,omitempty"` + AccessSecret string `json:"access-secret,omitempty" yaml:"access-secret,omitempty"` + DiskCategory string `json:"disk-category,omitempty" yaml:"disk-category,omitempty"` + DiskSize string `json:"disk-size,omitempty" yaml:"disk-size,omitempty"` + Image string `json:"image,omitempty" yaml:"image,omitempty"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + KeyPair string `json:"key-pair,omitempty" yaml:"key-pair,omitempty"` + Region string `json:"region,omitempty" yaml:"region,omitempty"` + VSwitch string `json:"v-switch,omitempty" yaml:"v-switch,omitempty"` + SecurityGroup string `json:"security-group,omitempty" yaml:"security-group,omitempty"` + InternetMaxBandwidthOut string `json:"internet-max-bandwidth-out,omitempty" yaml:"internet-max-bandwidth-out,omitempty"` } diff --git a/pkg/types/autok3s.go b/pkg/types/autok3s.go index c6698e03..99e3a480 100644 --- a/pkg/types/autok3s.go +++ b/pkg/types/autok3s.go @@ -1,43 +1,42 @@ package types type AutoK3s struct { - Clusters []Cluster `json:"clusters"` + Clusters []Cluster `json:"clusters" yaml:"clusters"` } type Cluster struct { Metadata `json:",inline" mapstructure:",squash"` - Status `json:"status"` + Status `json:"status" yaml:"status"` } type Metadata struct { - Name string `json:"name"` - Provider string `json:"provider"` - Master string `json:"master"` - Worker string `json:"worker"` + Name string `json:"name" yaml:"name"` + Provider string `json:"provider" yaml:"provider"` + Master string `json:"master" yaml:"master"` + Worker string `json:"worker" yaml:"worker"` } type Status struct { - MasterNodes []Node `json:"masterNodes,omitempty"` - WorkerNodes []Node `json:"workerNodes,omitempty"` + MasterNodes []Node `json:"master-nodes,omitempty"` + WorkerNodes []Node `json:"worker-nodes,omitempty"` } type Node struct { SSH `json:",inline"` - Master bool `json:"master,omitempty"` - Port string `json:"port,omitempty"` - InstanceID string `json:"instanceID,omitempty"` - InstanceStatus string `json:"instanceStatus,omitempty"` - PublicIPAddress []string `json:"publicIPAddress,omitempty"` - InternalIPAddress []string `json:"internalIPAddress,omitempty"` + Master bool `json:"master,omitempty" yaml:"master,omitempty"` + Port string `json:"ssh-port,omitempty" yaml:"ssh-port,omitempty"` + InstanceID string `json:"instance-id,omitempty" yaml:"instance-id,omitempty"` + InstanceStatus string `json:"instance-status,omitempty" yaml:"instance-status,omitempty"` + PublicIPAddress []string `json:"public-ip-address,omitempty" yaml:"public-ip-address,omitempty"` + InternalIPAddress []string `json:"internal-ip-address,omitempty" yaml:"internal-ip-address,omitempty"` } type SSH struct { - Port string `json:"port,omitempty"` - User string `json:"user,omitempty"` - SSHKey string `json:"sshKey,omitempty"` - SSHKeyPath string `json:"sshKeyPath,omitempty"` + Port string `json:"ssh-port,omitempty" yaml:"ssh-port,omitempty"` + User string `json:"user,omitempty" yaml:"user,omitempty"` + SSHKey string `json:"ssh-key,omitempty" yaml:"ssh-key,omitempty"` } type Flag struct { diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 258cbc98..b19f185c 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "strings" "github.com/ghodss/yaml" ) @@ -23,7 +24,7 @@ func EnsureFileExist(path, file string) error { n := fmt.Sprintf("%s/%s", path, file) - err := os.MkdirAll(path, os.ModePerm) + err := os.MkdirAll(n[0:strings.LastIndex(n, "/")], os.ModePerm) if err != nil && !os.IsExist(err) { return err } diff --git a/pkg/utils/ssh.go b/pkg/utils/ssh.go index 7c6f8a8f..e70870e2 100644 --- a/pkg/utils/ssh.go +++ b/pkg/utils/ssh.go @@ -8,11 +8,11 @@ import ( "golang.org/x/crypto/ssh" ) -func SSHPrivateKeyPath(sshKeyPath string) (string, error) { - if sshKeyPath[:2] == "~/" { - sshKeyPath = filepath.Join(UserHome(), sshKeyPath[2:]) +func SSHPrivateKeyPath(sshKey string) (string, error) { + if sshKey[:2] == "~/" { + sshKey = filepath.Join(UserHome(), sshKey[2:]) } - buff, err := ioutil.ReadFile(sshKeyPath) + buff, err := ioutil.ReadFile(sshKey) if err != nil { return "", fmt.Errorf("error while reading SSH key file: %v", err) }