VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
This commit is contained in:
466
vendor/github.com/vmware/vic/cmd/vic-machine/common/certificate.go
generated
vendored
Normal file
466
vendor/github.com/vmware/vic/cmd/vic-machine/common/certificate.go
generated
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/certificate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// CertFactory has all input parameters for vic-machine certificate commands needed to create a certificate
|
||||
type CertFactory struct {
|
||||
Networks
|
||||
|
||||
CertPath string
|
||||
DisplayName string
|
||||
Scert string
|
||||
Skey string
|
||||
Ccert string
|
||||
Ckey string
|
||||
Cacert string
|
||||
Cakey string
|
||||
ClientCert *tls.Certificate
|
||||
ClientCAsArg cli.StringSlice `arg:"tls-ca"`
|
||||
ClientCAs []byte
|
||||
EnvFile string
|
||||
Cname string
|
||||
Org cli.StringSlice
|
||||
KeySize int
|
||||
NoTLS bool
|
||||
NoTLSverify bool
|
||||
KeyPEM []byte
|
||||
CertPEM []byte
|
||||
NoSaveToDisk bool
|
||||
}
|
||||
|
||||
func (c *CertFactory) CertFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "tls-server-key",
|
||||
Value: "",
|
||||
Usage: "Virtual Container Host private key file (server certificate)",
|
||||
Destination: &c.Skey,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-server-cert",
|
||||
Value: "",
|
||||
Usage: "Virtual Container Host x509 certificate file (server certificate)",
|
||||
Destination: &c.Scert,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-cname",
|
||||
Value: "",
|
||||
Usage: "Common Name to use in generated CA certificate when requiring client certificate authentication",
|
||||
Destination: &c.Cname,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-cert-path",
|
||||
Value: "",
|
||||
Usage: "The path to check for existing certificates and in which to save generated certificates. Defaults to './<vch name>/'",
|
||||
Destination: &c.CertPath,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-tlsverify, kv",
|
||||
Usage: "Disable authentication via client certificates - for more tls options see advanced help (-x)",
|
||||
Destination: &c.NoTLSverify,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "organization",
|
||||
Usage: "A list of identifiers to record in the generated certificates. Defaults to VCH name and IP/FQDN if not provided.",
|
||||
Value: &c.Org,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "certificate-key-size, ksz",
|
||||
Usage: "Size of key to use when generating certificates",
|
||||
Value: 2048,
|
||||
Destination: &c.KeySize,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "tls-ca, ca",
|
||||
Usage: "Specify a list of certificate authority files to use for client verification",
|
||||
Value: &c.ClientCAsArg,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CertFactory) ProcessCertificates(op trace.Operation, displayName string, force bool, debug int) error {
|
||||
// set up the locations for the certificates and env file
|
||||
if c.CertPath == "" {
|
||||
c.CertPath = displayName
|
||||
}
|
||||
c.EnvFile = fmt.Sprintf("%s/%s.env", c.CertPath, displayName)
|
||||
|
||||
// check for insecure case
|
||||
if c.NoTLS {
|
||||
op.Warn("Configuring without TLS - all communications will be insecure")
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.Scert != "" && c.Skey == "" {
|
||||
return cli.NewExitError("key and cert should be specified at the same time", 1)
|
||||
}
|
||||
if c.Scert == "" && c.Skey != "" {
|
||||
return cli.NewExitError("key and cert should be specified at the same time", 1)
|
||||
}
|
||||
|
||||
// if we've not got a specific CommonName but do have a static IP then go with that.
|
||||
if c.Cname == "" {
|
||||
if c.ClientNetworkIP != "" {
|
||||
c.Cname = c.ClientNetworkIP
|
||||
op.Infof("Using client-network-ip as cname where needed - use --tls-cname to override: %s", c.Cname)
|
||||
} else if c.PublicNetworkIP != "" && (c.PublicNetworkName == c.ClientNetworkName || c.ClientNetworkName == "") {
|
||||
c.Cname = c.PublicNetworkIP
|
||||
op.Infof("Using public-network-ip as cname where needed - use --tls-cname to override: %s", c.Cname)
|
||||
} else if c.ManagementNetworkIP != "" && (c.ManagementNetworkName == c.ClientNetworkName || (c.ClientNetworkName == "" && c.ManagementNetworkName == c.PublicNetworkName)) {
|
||||
c.Cname = c.ManagementNetworkIP
|
||||
op.Infof("Using management-network-ip as cname where needed - use --tls-cname to override: %s", c.Cname)
|
||||
}
|
||||
|
||||
if c.Cname != "" {
|
||||
// Strip network mask from IP address if set.
|
||||
// #nosec: Errors unhandled.
|
||||
if cnameIP, _, _ := net.ParseCIDR(c.Cname); cnameIP != nil {
|
||||
c.Cname = cnameIP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load what certificates we can
|
||||
cas, keypair, err := c.loadCertificates(op, debug)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to load certificates: %s", err)
|
||||
if !force {
|
||||
return err
|
||||
}
|
||||
|
||||
op.Warnf("Ignoring error loading certificates due to --force")
|
||||
cas = nil
|
||||
keypair = nil
|
||||
err = nil
|
||||
}
|
||||
|
||||
// we need to generate some part of the certificate configuration
|
||||
gcas, gkeypair, err := c.generateCertificates(op, keypair == nil, !c.NoTLSverify && len(cas) == 0)
|
||||
if err != nil {
|
||||
op.Error("cannot continue: unable to generate certificates")
|
||||
return err
|
||||
}
|
||||
|
||||
if keypair != nil {
|
||||
c.KeyPEM = keypair.KeyPEM
|
||||
c.CertPEM = keypair.CertPEM
|
||||
} else if gkeypair != nil {
|
||||
c.KeyPEM = gkeypair.KeyPEM
|
||||
c.CertPEM = gkeypair.CertPEM
|
||||
}
|
||||
|
||||
if len(cas) == 0 {
|
||||
cas = gcas
|
||||
}
|
||||
|
||||
if len(c.KeyPEM) == 0 {
|
||||
return errors.New("Failed to load or generate server certificates")
|
||||
}
|
||||
|
||||
if len(cas) == 0 && !c.NoTLSverify {
|
||||
return errors.New("Failed to load or generate certificate authority")
|
||||
}
|
||||
|
||||
// do we have key, cert, and --no-tlsverify
|
||||
if c.NoTLSverify || len(cas) == 0 {
|
||||
op.Warnf("Configuring without TLS verify - certificate-based authentication disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
c.ClientCAs = cas
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadCertificates returns the client CA pool and the keypair for server certificates on success
|
||||
func (c *CertFactory) loadCertificates(op trace.Operation, debug int) ([]byte, *certificate.KeyPair, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
// reads each of the files specified, assuming that they are PEM encoded certs,
|
||||
// and constructs a byte array suitable for passing to CertPool.AppendCertsFromPEM
|
||||
var certs []byte
|
||||
for _, f := range c.ClientCAsArg {
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
err = errors.Errorf("Failed to load authority from file %s: %s", f, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
certs = append(certs, b...)
|
||||
op.Infof("Loaded CA from %s", f)
|
||||
}
|
||||
|
||||
var keypair *certificate.KeyPair
|
||||
// default names
|
||||
skey := filepath.Join(c.CertPath, certificate.ServerKey)
|
||||
scert := filepath.Join(c.CertPath, certificate.ServerCert)
|
||||
ca := filepath.Join(c.CertPath, certificate.CACert)
|
||||
ckey := filepath.Join(c.CertPath, certificate.ClientKey)
|
||||
ccert := filepath.Join(c.CertPath, certificate.ClientCert)
|
||||
|
||||
// if specific files are supplied, use those
|
||||
explicit := false
|
||||
if c.Scert != "" && c.Skey != "" {
|
||||
explicit = true
|
||||
skey = c.Skey
|
||||
scert = c.Scert
|
||||
}
|
||||
|
||||
// load the server certificate
|
||||
keypair = certificate.NewKeyPair(scert, skey, nil, nil)
|
||||
if err := keypair.LoadCertificate(); err != nil {
|
||||
if explicit || !os.IsNotExist(err) {
|
||||
// if these files were explicit paths, or anything other than not found, fail
|
||||
op.Errorf("Failed to load certificate: %s", err)
|
||||
return certs, nil, err
|
||||
}
|
||||
|
||||
op.Debugf("Unable to locate existing server certificate in cert path")
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// check that any supplied cname matches the server cert CN
|
||||
cert, err := keypair.Certificate()
|
||||
if err != nil {
|
||||
op.Errorf("Failed to parse certificate: %s", err)
|
||||
return certs, nil, err
|
||||
}
|
||||
|
||||
if cert.Leaf == nil {
|
||||
op.Warnf("Failed to load x509 leaf: Unable to confirm server certificate cname matches provided cname %q. Continuing...", c.Cname)
|
||||
} else {
|
||||
// We just do a direct equality check here - trying to be clever is liable to lead to hard
|
||||
// to diagnose errors
|
||||
if cert.Leaf.Subject.CommonName != c.Cname {
|
||||
op.Errorf("Provided cname does not match that in existing server certificate: %s", cert.Leaf.Subject.CommonName)
|
||||
if debug > 2 {
|
||||
op.Debugf("Certificate does not match provided cname: %#+v", cert.Leaf)
|
||||
}
|
||||
return certs, nil, fmt.Errorf("cname option doesn't match existing server certificate in certificate path %s", c.CertPath)
|
||||
}
|
||||
}
|
||||
|
||||
op.Infof("Loaded server certificate %s", scert)
|
||||
c.Skey = skey
|
||||
c.Scert = scert
|
||||
|
||||
// only try for CA certificate if no-tlsverify has NOT been specified and we haven't already loaded an authority cert
|
||||
if !c.NoTLSverify && len(certs) == 0 {
|
||||
b, err := ioutil.ReadFile(ca)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
op.Debugf("Unable to locate existing CA in cert path")
|
||||
return certs, keypair, nil
|
||||
}
|
||||
|
||||
// if the CA exists but cannot be loaded then it's an error
|
||||
op.Errorf("Failed to load authority from certificate path %s: %s", c.CertPath, err)
|
||||
return certs, keypair, errors.New("failed to load certificate authority")
|
||||
}
|
||||
|
||||
c.Cacert = ca
|
||||
|
||||
op.Infof("Loaded CA with default name from certificate path %s", c.CertPath)
|
||||
certs = b
|
||||
|
||||
// load client certs - we ensure the client certs validate with the provided CA or ignore any we find
|
||||
cpair := certificate.NewKeyPair(ccert, ckey, nil, nil)
|
||||
if err := cpair.LoadCertificate(); err != nil {
|
||||
op.Warnf("Unable to load client certificate - validation of API endpoint will be best effort only: %s", err)
|
||||
}
|
||||
|
||||
clientCert, err := certificate.VerifyClientCert(certs, cpair)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case certificate.CertParseError, certificate.CreateCAPoolError:
|
||||
op.Debug(err)
|
||||
case certificate.CertVerifyError:
|
||||
op.Warnf("%s - continuing without client certificate", err)
|
||||
}
|
||||
|
||||
return certs, keypair, nil
|
||||
}
|
||||
|
||||
c.Ckey = ckey
|
||||
c.Ccert = ccert
|
||||
c.ClientCert = clientCert
|
||||
|
||||
op.Infof("Loaded client certificate with default name from certificate path %s", c.CertPath)
|
||||
}
|
||||
|
||||
return certs, keypair, nil
|
||||
}
|
||||
|
||||
func (c *CertFactory) generateCertificates(op trace.Operation, server bool, client bool) ([]byte, *certificate.KeyPair, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if !server && !client {
|
||||
op.Debug("Not generating server or client certs, nothing for generateCertificates to do")
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var certs []byte
|
||||
// generate the certs and keys with names conforming the default the docker client expects
|
||||
files, err := ioutil.ReadDir(c.CertPath)
|
||||
if len(files) > 0 {
|
||||
return nil, nil, fmt.Errorf("Specified directory to store certificates is not empty. Specify a new path in which to store generated certificates using --tls-cert-path or remove the contents of \"%s\" and run vic-machine again.", c.CertPath)
|
||||
}
|
||||
|
||||
if !c.NoSaveToDisk {
|
||||
err = os.MkdirAll(c.CertPath, 0700)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to make directory \"%s\" to hold certificates (set via --tls-cert-path)", c.CertPath)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.Skey = filepath.Join(c.CertPath, certificate.ServerKey)
|
||||
c.Scert = filepath.Join(c.CertPath, certificate.ServerCert)
|
||||
|
||||
c.Ckey = filepath.Join(c.CertPath, certificate.ClientKey)
|
||||
c.Ccert = filepath.Join(c.CertPath, certificate.ClientCert)
|
||||
|
||||
cakey := filepath.Join(c.CertPath, certificate.CAKey)
|
||||
c.Cacert = filepath.Join(c.CertPath, certificate.CACert)
|
||||
|
||||
if server && !client {
|
||||
op.Infof("Generating self-signed certificate/key pair - private key in %s", c.Skey)
|
||||
keypair := certificate.NewKeyPair(c.Scert, c.Skey, nil, nil)
|
||||
err := keypair.CreateSelfSigned(c.Cname, nil, c.KeySize)
|
||||
if err != nil {
|
||||
op.Errorf("Failed to generate self-signed certificate: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !c.NoSaveToDisk {
|
||||
if err = keypair.SaveCertificate(); err != nil {
|
||||
op.Errorf("Failed to save server certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return certs, keypair, nil
|
||||
}
|
||||
|
||||
// client auth path
|
||||
if c.Cname == "" {
|
||||
op.Error("Common Name must be provided when generating certificates for client authentication:")
|
||||
op.Info(" --tls-cname=<FQDN or static IP> # for the appliance VM")
|
||||
op.Info(" --tls-cname=<*.yourdomain.com> # if DNS has entries in that form for DHCP addresses (less secure)")
|
||||
op.Info(" --no-tlsverify # disables client authentication (anyone can connect to the VCH)")
|
||||
op.Info(" --no-tls # disables TLS entirely")
|
||||
op.Info("")
|
||||
|
||||
return certs, nil, errors.New("provide Common Name for server certificate")
|
||||
}
|
||||
|
||||
// for now re-use the display name as the organisation if unspecified
|
||||
if len(c.Org) == 0 {
|
||||
c.Org = []string{c.DisplayName}
|
||||
}
|
||||
if len(c.Org) == 1 && !strings.HasPrefix(c.Cname, "*") {
|
||||
// Add in the cname if it's not a wildcard
|
||||
c.Org = append(c.Org, c.Cname)
|
||||
}
|
||||
|
||||
// Certificate authority
|
||||
op.Infof("Generating CA certificate/key pair - private key in %s", cakey)
|
||||
cakp := certificate.NewKeyPair(c.Cacert, cakey, nil, nil)
|
||||
err = cakp.CreateRootCA(c.Cname, c.Org, c.KeySize)
|
||||
if err != nil {
|
||||
op.Errorf("Failed to generate CA: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !c.NoSaveToDisk {
|
||||
if err = cakp.SaveCertificate(); err != nil {
|
||||
op.Errorf("Failed to save CA certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Server certificates
|
||||
var skp *certificate.KeyPair
|
||||
if server {
|
||||
op.Infof("Generating server certificate/key pair - private key in %s", c.Skey)
|
||||
skp = certificate.NewKeyPair(c.Scert, c.Skey, nil, nil)
|
||||
err = skp.CreateServerCertificate(c.Cname, c.Org, c.KeySize, cakp)
|
||||
if err != nil {
|
||||
op.Errorf("Failed to generate server certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !c.NoSaveToDisk {
|
||||
if err = skp.SaveCertificate(); err != nil {
|
||||
op.Errorf("Failed to save server certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Client certificates
|
||||
if client {
|
||||
op.Infof("Generating client certificate/key pair - private key in %s", c.Ckey)
|
||||
ckp := certificate.NewKeyPair(c.Ccert, c.Ckey, nil, nil)
|
||||
err = ckp.CreateClientCertificate(c.Cname, c.Org, c.KeySize, cakp)
|
||||
if err != nil {
|
||||
op.Errorf("Failed to generate client certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !c.NoSaveToDisk {
|
||||
if err = ckp.SaveCertificate(); err != nil {
|
||||
op.Errorf("Failed to save client certificates: %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.ClientCert, err = ckp.Certificate()
|
||||
if err != nil {
|
||||
op.Warnf("Failed to stash client certificate for later application level validation: %s", err)
|
||||
}
|
||||
|
||||
// If openssl is present, try to generate a browser friendly pfx file (a bundle of the public certificate AND the private key)
|
||||
// The pfx file can be imported directly into keychains for client certificate authentication
|
||||
certPath := filepath.Clean(c.CertPath)
|
||||
args := strings.Split(fmt.Sprintf("pkcs12 -export -out %[1]s/cert.pfx -inkey %[1]s/key.pem -in %[1]s/cert.pem -certfile %[1]s/ca.pem -password pass:", certPath), " ")
|
||||
// #nosec: Subprocess launching with variable
|
||||
pfx := exec.Command("openssl", args...)
|
||||
out, err := pfx.CombinedOutput()
|
||||
if err != nil {
|
||||
op.Debug(out)
|
||||
op.Warnf("Failed to generate browser friendly PFX client certificate: %s", err)
|
||||
} else {
|
||||
op.Infof("Generated browser friendly PFX client certificate - certificate in %s/cert.pfx", certPath)
|
||||
}
|
||||
}
|
||||
|
||||
return cakp.CertPEM, skp, nil
|
||||
}
|
||||
60
vendor/github.com/vmware/vic/cmd/vic-machine/common/certificate_test.go
generated
vendored
Normal file
60
vendor/github.com/vmware/vic/cmd/vic-machine/common/certificate_test.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
cs = &CertFactory{}
|
||||
)
|
||||
|
||||
func TestGenKey(t *testing.T) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
os.Args = []string{"cmd", "create"}
|
||||
flag.Parse()
|
||||
cs.NoTLS = false
|
||||
cs.CertPath = "install-test"
|
||||
cs.Cname = "common name"
|
||||
cs.KeySize = 1024
|
||||
|
||||
op := trace.NewOperation(context.Background(), "TestGenKey")
|
||||
|
||||
ca, kp, err := cs.generateCertificates(op, true, true)
|
||||
defer os.RemoveAll(fmt.Sprintf("./%s", cs.CertPath))
|
||||
|
||||
assert.NoError(t, err, "Expected to cleanly generate certificates")
|
||||
assert.NotEmpty(t, ca, "Expected CA to contain data")
|
||||
assert.NotNil(t, kp, "Expected keypair to contain data")
|
||||
assert.NotEmpty(t, kp.CertPEM, "Expected certificate to contain data")
|
||||
assert.NotEmpty(t, kp.CertPEM, "Expected key to contain data")
|
||||
|
||||
ca, kp, err = cs.loadCertificates(op, 2)
|
||||
assert.NoError(t, err, "Expected to cleanly load certificates")
|
||||
assert.NotEmpty(t, ca, "Expected CA to contain data")
|
||||
assert.NotNil(t, kp, "Expected keypair to contain data")
|
||||
assert.NotEmpty(t, kp.CertPEM, "Expected certificate to contain data")
|
||||
assert.NotEmpty(t, kp.CertPEM, "Expected key to contain data")
|
||||
}
|
||||
317
vendor/github.com/vmware/vic/cmd/vic-machine/common/cnetworks.go
generated
vendored
Normal file
317
vendor/github.com/vmware/vic/cmd/vic-machine/common/cnetworks.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/lib/config/executor"
|
||||
"github.com/vmware/vic/pkg/ip"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// CNetworks holds user input from container network flags
|
||||
type CNetworks struct {
|
||||
ContainerNetworks cli.StringSlice `arg:"container-network"`
|
||||
ContainerNetworksGateway cli.StringSlice `arg:"container-network-gateway"`
|
||||
ContainerNetworksIPRanges cli.StringSlice `arg:"container-network-ip-range"`
|
||||
ContainerNetworksDNS cli.StringSlice `arg:"container-network-dns"`
|
||||
ContainerNetworksFirewall cli.StringSlice `arg:"container-network-firewall"`
|
||||
IsSet bool
|
||||
}
|
||||
|
||||
// ContainerNetworks holds container network data after processing
|
||||
type ContainerNetworks struct {
|
||||
MappedNetworks map[string]string `cmd:"parent" label:"key-value"`
|
||||
MappedNetworksGateways map[string]net.IPNet `cmd:"gateway" label:"key-value"`
|
||||
MappedNetworksIPRanges map[string][]ip.Range `cmd:"ip-range" label:"key-value"`
|
||||
MappedNetworksDNS map[string][]net.IP `cmd:"dns" label:"key-value"`
|
||||
MappedNetworksFirewalls map[string]executor.TrustLevel `cmd:"firewall" label:"key-value"`
|
||||
}
|
||||
|
||||
func (c *ContainerNetworks) IsSet() bool {
|
||||
return len(c.MappedNetworks) > 0 ||
|
||||
len(c.MappedNetworksGateways) > 0 ||
|
||||
len(c.MappedNetworksIPRanges) > 0 ||
|
||||
len(c.MappedNetworksDNS) > 0 ||
|
||||
len(c.MappedNetworksFirewalls) > 0
|
||||
}
|
||||
|
||||
func (c *CNetworks) CNetworkFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "container-network, cn",
|
||||
Value: &c.ContainerNetworks,
|
||||
Usage: "vSphere network list that containers can use directly with labels, e.g. vsphere-net:backend. Defaults to DCHP - see advanced help (-x).",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "container-network-gateway, cng",
|
||||
Value: &c.ContainerNetworksGateway,
|
||||
Usage: "Gateway for the container network's subnet in CONTAINER-NETWORK:SUBNET format, e.g. vsphere-net:172.16.0.1/16",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "container-network-ip-range, cnr",
|
||||
Value: &c.ContainerNetworksIPRanges,
|
||||
Usage: "IP range for the container network in CONTAINER-NETWORK:IP-RANGE format, e.g. vsphere-net:172.16.0.0/24, vsphere-net:172.16.0.10-172.16.0.20",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "container-network-dns, cnd",
|
||||
Value: &c.ContainerNetworksDNS,
|
||||
Usage: "DNS servers for the container network in CONTAINER-NETWORK:DNS format, e.g. vsphere-net:8.8.8.8. Ignored if no static IP assigned.",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "container-network-firewall, cnf",
|
||||
Value: &c.ContainerNetworksFirewall,
|
||||
Usage: "Container network trust level in CONTAINER-NETWORK:LEVEL format. Options: Closed, Outbound, Peers, Published, Open.",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func parseContainerNetworkGateways(cgs []string) (map[string]net.IPNet, error) {
|
||||
gws := make(map[string]net.IPNet)
|
||||
for _, cg := range cgs {
|
||||
m := &ipNetUnmarshaler{}
|
||||
vnet, err := parseVnetParam(cg, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := gws[vnet]; ok {
|
||||
return nil, fmt.Errorf("Duplicate gateway specified for container network %s", vnet)
|
||||
}
|
||||
|
||||
gws[vnet] = net.IPNet{IP: m.ip, Mask: m.ipnet.Mask}
|
||||
}
|
||||
|
||||
return gws, nil
|
||||
}
|
||||
|
||||
func parseContainerNetworkIPRanges(cps []string) (map[string][]ip.Range, error) {
|
||||
pools := make(map[string][]ip.Range)
|
||||
for _, cp := range cps {
|
||||
ipr := &ip.Range{}
|
||||
vnet, err := parseVnetParam(cp, ipr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pools[vnet] = append(pools[vnet], *ipr)
|
||||
}
|
||||
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func parseContainerNetworkDNS(cds []string) (map[string][]net.IP, error) {
|
||||
dns := make(map[string][]net.IP)
|
||||
for _, cd := range cds {
|
||||
var ip net.IP
|
||||
vnet, err := parseVnetParam(cd, &ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("DNS IP not specified for container network %s", vnet)
|
||||
}
|
||||
|
||||
dns[vnet] = append(dns[vnet], ip)
|
||||
}
|
||||
|
||||
return dns, nil
|
||||
}
|
||||
|
||||
func parseContainerNetworkFirewalls(cfs []string) (map[string]executor.TrustLevel, error) {
|
||||
firewalls := make(map[string]executor.TrustLevel)
|
||||
for _, cf := range cfs {
|
||||
vnet, value, err := splitVnetParam(cf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing container network parameter %s: %s", cf, err)
|
||||
}
|
||||
trust, err := executor.ParseTrustLevel(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
firewalls[vnet] = trust
|
||||
}
|
||||
return firewalls, nil
|
||||
}
|
||||
|
||||
func splitVnetParam(p string) (vnet string, value string, err error) {
|
||||
mapped := strings.Split(p, ":")
|
||||
if len(mapped) == 0 || len(mapped) > 2 {
|
||||
err = fmt.Errorf("Invalid value for parameter %s", p)
|
||||
return
|
||||
}
|
||||
|
||||
vnet = mapped[0]
|
||||
if vnet == "" {
|
||||
err = fmt.Errorf("Container network not specified in parameter %s", p)
|
||||
return
|
||||
}
|
||||
|
||||
// If the supplied vSphere network contains spaces then the user must supply a network alias. Guest info won't receive a name with spaces.
|
||||
if strings.Contains(vnet, " ") && (len(mapped) == 1 || (len(mapped) == 2 && len(mapped[1]) == 0)) {
|
||||
err = fmt.Errorf("A network alias must be supplied when network name %q contains spaces.", p)
|
||||
return
|
||||
}
|
||||
|
||||
if len(mapped) > 1 {
|
||||
// Make sure the alias does not contain spaces
|
||||
if strings.Contains(mapped[1], " ") {
|
||||
err = fmt.Errorf("The network alias supplied in %q cannot contain spaces.", p)
|
||||
return
|
||||
}
|
||||
value = mapped[1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseVnetParam(p string, m encoding.TextUnmarshaler) (vnet string, err error) {
|
||||
vnet, v, err := splitVnetParam(p)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing container network parameter %s: %s", p, err)
|
||||
}
|
||||
|
||||
if err = m.UnmarshalText([]byte(v)); err != nil {
|
||||
return "", fmt.Errorf("Error parsing container network parameter %s: %s", p, err)
|
||||
}
|
||||
|
||||
return vnet, nil
|
||||
}
|
||||
|
||||
type ipNetUnmarshaler struct {
|
||||
ipnet *net.IPNet
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
func (m *ipNetUnmarshaler) UnmarshalText(text []byte) error {
|
||||
s := string(text)
|
||||
ip, ipnet, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.ipnet = ipnet
|
||||
m.ip = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessContainerNetworks parses container network settings and returns a
|
||||
// struct containing all processed container network fields on success.
|
||||
func (c *CNetworks) ProcessContainerNetworks(op trace.Operation) (ContainerNetworks, error) {
|
||||
cNetworks := ContainerNetworks{
|
||||
MappedNetworks: make(map[string]string),
|
||||
MappedNetworksGateways: make(map[string]net.IPNet),
|
||||
MappedNetworksIPRanges: make(map[string][]ip.Range),
|
||||
MappedNetworksDNS: make(map[string][]net.IP),
|
||||
MappedNetworksFirewalls: make(map[string]executor.TrustLevel),
|
||||
}
|
||||
|
||||
if c.ContainerNetworks != nil || c.ContainerNetworksGateway != nil ||
|
||||
c.ContainerNetworksIPRanges != nil || c.ContainerNetworksDNS != nil {
|
||||
c.IsSet = true
|
||||
}
|
||||
|
||||
gws, err := parseContainerNetworkGateways([]string(c.ContainerNetworksGateway))
|
||||
if err != nil {
|
||||
return cNetworks, cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
|
||||
pools, err := parseContainerNetworkIPRanges([]string(c.ContainerNetworksIPRanges))
|
||||
if err != nil {
|
||||
return cNetworks, cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
|
||||
dns, err := parseContainerNetworkDNS([]string(c.ContainerNetworksDNS))
|
||||
if err != nil {
|
||||
return cNetworks, cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
|
||||
firewalls, err := parseContainerNetworkFirewalls([]string(c.ContainerNetworksFirewall))
|
||||
if err != nil {
|
||||
return cNetworks, cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
|
||||
// Parse container networks
|
||||
for _, cn := range c.ContainerNetworks {
|
||||
vnet, v, err := splitVnetParam(cn)
|
||||
if err != nil {
|
||||
return cNetworks, cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
|
||||
alias := vnet
|
||||
if v != "" {
|
||||
alias = v
|
||||
}
|
||||
|
||||
cNetworks.MappedNetworks[alias] = vnet
|
||||
cNetworks.MappedNetworksGateways[alias] = gws[vnet]
|
||||
cNetworks.MappedNetworksIPRanges[alias] = pools[vnet]
|
||||
cNetworks.MappedNetworksDNS[alias] = dns[vnet]
|
||||
cNetworks.MappedNetworksFirewalls[alias] = firewalls[vnet]
|
||||
|
||||
delete(gws, vnet)
|
||||
delete(pools, vnet)
|
||||
delete(dns, vnet)
|
||||
delete(firewalls, vnet)
|
||||
}
|
||||
|
||||
var hasError bool
|
||||
fmtMsg := "The following container network %s is set, but CONTAINER-NETWORK cannot be found. Please check the --container-network and %s settings"
|
||||
if len(gws) > 0 {
|
||||
op.Errorf(fmtMsg, "gateway", "--container-network-gateway")
|
||||
for key, value := range gws {
|
||||
mask, _ := value.Mask.Size()
|
||||
op.Errorf("\t%s:%s/%d, %q should be a vSphere network name", key, value.IP, mask, key)
|
||||
}
|
||||
hasError = true
|
||||
}
|
||||
if len(pools) > 0 {
|
||||
op.Errorf(fmtMsg, "ip range", "--container-network-ip-range")
|
||||
for key, value := range pools {
|
||||
op.Errorf("\t%s:%s, %q should be a vSphere network name", key, value, key)
|
||||
}
|
||||
hasError = true
|
||||
}
|
||||
if len(dns) > 0 {
|
||||
op.Errorf(fmtMsg, "dns", "--container-network-dns")
|
||||
for key, value := range dns {
|
||||
op.Errorf("\t%s:%s, %q should be a vSphere network name", key, value, key)
|
||||
}
|
||||
hasError = true
|
||||
}
|
||||
if len(firewalls) > 0 {
|
||||
op.Errorf(fmtMsg, "firewall", "--container-network-firewall")
|
||||
for key := range firewalls {
|
||||
op.Errorf("\t%q should be a vSphere network name", key)
|
||||
}
|
||||
hasError = true
|
||||
}
|
||||
if hasError {
|
||||
return cNetworks, cli.NewExitError("Inconsistent container network configuration.", 1)
|
||||
}
|
||||
|
||||
return cNetworks, nil
|
||||
}
|
||||
211
vendor/github.com/vmware/vic/cmd/vic-machine/common/cnetworks_test.go
generated
vendored
Normal file
211
vendor/github.com/vmware/vic/cmd/vic-machine/common/cnetworks_test.go
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/vmware/vic/pkg/ip"
|
||||
)
|
||||
|
||||
func TestParseContainerNetworkGateways(t *testing.T) {
|
||||
var tests = []struct {
|
||||
cgs []string
|
||||
gws map[string]net.IPNet
|
||||
err error
|
||||
}{
|
||||
{[]string{""}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:"}, nil, fmt.Errorf("")},
|
||||
{[]string{":"}, nil, fmt.Errorf("")},
|
||||
{[]string{":10.10.10.10/24"}, nil, fmt.Errorf("")},
|
||||
{[]string{":foo"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10.10.10.10/24", "foo:10.10.10.2/24"}, nil, fmt.Errorf("")},
|
||||
{
|
||||
[]string{"foo:10.10.10.10/24", "bar:10.10.9.1/16"},
|
||||
map[string]net.IPNet{
|
||||
"foo": {IP: net.ParseIP("10.10.10.10"), Mask: net.CIDRMask(24, 32)},
|
||||
"bar": {IP: net.ParseIP("10.10.9.1"), Mask: net.CIDRMask(16, 32)},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, te := range tests {
|
||||
gws, err := parseContainerNetworkGateways(te.cgs)
|
||||
if te.err != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("parseContainerNetworkGateways(%s) => (%v, nil) want (nil, err)", te.cgs, gws)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err != te.err ||
|
||||
gws == nil ||
|
||||
len(gws) != len(te.gws) {
|
||||
t.Fatalf("parseContainerNetworkGateways(%s) => (%v, %s) want (%v, nil)", te.cgs, gws, err, te.gws)
|
||||
}
|
||||
|
||||
for v, g := range gws {
|
||||
if g2, ok := te.gws[v]; !ok {
|
||||
t.Fatalf("parseContainerNetworkGateways(%s) => (%v, %s) want (%v, nil)", te.cgs, gws, err, te.gws)
|
||||
} else if !g2.IP.Equal(g.IP) || bytes.Compare(g2.Mask, g.Mask) != 0 {
|
||||
t.Fatalf("parseContainerNetworkGateways(%s) => (%v, %s) want (%v, nil)", te.cgs, gws, err, te.gws)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseContainerNetworkIPRanges(t *testing.T) {
|
||||
var tests = []struct {
|
||||
cps []string
|
||||
iprs map[string][]*ip.Range
|
||||
err error
|
||||
}{
|
||||
{[]string{""}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:"}, nil, fmt.Errorf("")},
|
||||
{[]string{":"}, nil, fmt.Errorf("")},
|
||||
{[]string{":10.10.10.10-24"}, nil, fmt.Errorf("")},
|
||||
{[]string{":foo"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10.10.10.10-9"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10.10.10.10-10.10.10.9"}, nil, fmt.Errorf("")},
|
||||
{
|
||||
[]string{"foo:10.10.10.10-24"},
|
||||
map[string][]*ip.Range{"foo": {ip.NewRange(net.ParseIP("10.10.10.10"), net.ParseIP("10.10.10.24"))}}, nil},
|
||||
{
|
||||
[]string{"foo:10.10.10.10-10.10.10.24"},
|
||||
map[string][]*ip.Range{"foo": {ip.NewRange(net.ParseIP("10.10.10.10"), net.ParseIP("10.10.10.24"))}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{"foo:10.10.10.10-10.10.10.24", "foo:10.10.10.100-10.10.10.105"},
|
||||
map[string][]*ip.Range{
|
||||
"foo": {
|
||||
ip.NewRange(net.ParseIP("10.10.10.10"), net.ParseIP("10.10.10.24")),
|
||||
ip.NewRange(net.ParseIP("10.10.10.100"), net.ParseIP("10.10.10.105")),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{"foo:10.10.10.10-10.10.10.24", "bar:10.10.9.1-10.10.9.10"},
|
||||
map[string][]*ip.Range{
|
||||
"foo": {ip.NewRange(net.ParseIP("10.10.10.10"), net.ParseIP("10.10.10.24"))},
|
||||
"bar": {ip.NewRange(net.ParseIP("10.10.9.1"), net.ParseIP("10.10.9.10"))},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, te := range tests {
|
||||
iprs, err := parseContainerNetworkIPRanges(te.cps)
|
||||
if te.err != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("parseContainerNetworkIPRanges(%s) => (%v, nil) want (nil, err)", te.cps, iprs)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err != te.err ||
|
||||
len(iprs) != len(te.iprs) {
|
||||
t.Fatalf("parseContainerNetworkIPRanges(%s) => (%v, %s) want (%v, %s)", te.cps, iprs, err, te.iprs, te.err)
|
||||
}
|
||||
|
||||
for v, ipr := range iprs {
|
||||
if ipr2, ok := te.iprs[v]; !ok {
|
||||
t.Fatalf("parseContainerNetworkIPRanges(%s) => (%v, %s) want (%v, %s)", te.cps, iprs, err, te.iprs, te.err)
|
||||
} else {
|
||||
for _, i := range ipr {
|
||||
found := false
|
||||
for _, i2 := range ipr2 {
|
||||
if i.Equal(i2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("parseContainerNetworkIPRanges(%s) => (%v, %s) want (%v, %s)", te.cps, iprs, err, te.iprs, te.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseContainerNetworkDNS(t *testing.T) {
|
||||
var tests = []struct {
|
||||
cds []string
|
||||
dns map[string][]net.IP
|
||||
err error
|
||||
}{
|
||||
{[]string{""}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:"}, nil, fmt.Errorf("")},
|
||||
{[]string{":"}, nil, fmt.Errorf("")},
|
||||
{[]string{":10.10.10.10"}, nil, fmt.Errorf("")},
|
||||
{[]string{":foo"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10"}, nil, fmt.Errorf("")},
|
||||
{[]string{"foo:10.10.10.109"}, map[string][]net.IP{"foo": {net.ParseIP("10.10.10.109")}}, nil},
|
||||
{[]string{"foo:10.10.10.109", "foo:10.10.10.110", "bar:10.10.9.109", "bar:10.10.9.110"},
|
||||
map[string][]net.IP{
|
||||
"foo": {net.ParseIP("10.10.10.109"), net.ParseIP("10.10.10.110")},
|
||||
"bar": {net.ParseIP("10.10.9.109"), net.ParseIP("10.10.9.110")},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, te := range tests {
|
||||
dns, err := parseContainerNetworkDNS(te.cds)
|
||||
if te.err != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("parseContainerNetworkDNS(%s) => (%v, nil) want (nil, err)", te.cds, dns)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err != te.err ||
|
||||
len(dns) != len(te.dns) {
|
||||
t.Fatalf("parseContainerNetworkDNS(%s) => (%v, %s) want (%v, %s)", te.cds, dns, err, te.dns, te.err)
|
||||
}
|
||||
|
||||
for v, d := range dns {
|
||||
if d2, ok := te.dns[v]; !ok {
|
||||
t.Fatalf("parseContainerNetworkDNS(%s) => (%v, %s) want (%v, %s)", te.cds, dns, err, te.dns, te.err)
|
||||
} else {
|
||||
for _, i := range d {
|
||||
found := false
|
||||
for _, i2 := range d2 {
|
||||
if i.Equal(i2) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("parseContainerNetworkDNS(%s) => (%v, %s) want (%v, %s)", te.cds, dns, err, te.dns, te.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
vendor/github.com/vmware/vic/cmd/vic-machine/common/compute.go
generated
vendored
Normal file
46
vendor/github.com/vmware/vic/cmd/vic-machine/common/compute.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import "gopkg.in/urfave/cli.v1"
|
||||
|
||||
type Compute struct {
|
||||
ComputeResourcePath string `cmd:"compute-resource"`
|
||||
DisplayName string `cmd:"name"`
|
||||
}
|
||||
|
||||
func (c *Compute) ComputeFlags() []cli.Flag {
|
||||
nameFlag := []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Value: "virtual-container-host",
|
||||
Usage: "The name of the Virtual Container Host",
|
||||
Destination: &c.DisplayName,
|
||||
},
|
||||
}
|
||||
|
||||
return append(nameFlag, c.ComputeFlagsNoName()...)
|
||||
}
|
||||
|
||||
func (c *Compute) ComputeFlagsNoName() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "compute-resource, r",
|
||||
Value: "",
|
||||
Usage: "Compute resource path, e.g. myCluster",
|
||||
Destination: &c.ComputeResourcePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
41
vendor/github.com/vmware/vic/cmd/vic-machine/common/container.go
generated
vendored
Normal file
41
vendor/github.com/vmware/vic/cmd/vic-machine/common/container.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
type ContainerConfig struct {
|
||||
// NameConvention
|
||||
ContainerNameConvention string `cmd:"container-name-convention"`
|
||||
}
|
||||
|
||||
func (c *ContainerConfig) ContainerFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
// container naming convention
|
||||
cli.StringFlag{
|
||||
Name: "container-name-convention, cnc",
|
||||
Value: "",
|
||||
Usage: "Provide a naming convention. Allows a token of '{name}' or '{id}', that will be replaced.",
|
||||
Destination: &c.ContainerNameConvention,
|
||||
Hidden: true,
|
||||
},
|
||||
// other container flags to to added"
|
||||
// default container memory
|
||||
// default container cpu
|
||||
// default container network
|
||||
}
|
||||
}
|
||||
36
vendor/github.com/vmware/vic/cmd/vic-machine/common/debug.go
generated
vendored
Normal file
36
vendor/github.com/vmware/vic/cmd/vic-machine/common/debug.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
)
|
||||
|
||||
type Debug struct {
|
||||
Debug *int `cmd:"debug"`
|
||||
}
|
||||
|
||||
func (d *Debug) DebugFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "debug, v",
|
||||
Value: flags.NewOptionalInt(&d.Debug),
|
||||
Usage: "[0(default),1...n], 0 is disabled, 1 is enabled, >= 1 may alter behaviour",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
66
vendor/github.com/vmware/vic/cmd/vic-machine/common/dns.go
generated
vendored
Normal file
66
vendor/github.com/vmware/vic/cmd/vic-machine/common/dns.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// general dns
|
||||
type DNS struct {
|
||||
DNS cli.StringSlice `arg:"dns-server"`
|
||||
IsSet bool
|
||||
}
|
||||
|
||||
func (d *DNS) DNSFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "dns-server",
|
||||
Value: &d.DNS,
|
||||
Usage: "DNS server for the client, public, and management networks. Defaults to 8.8.8.8 and 8.8.4.4 when VCH uses static IP",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// processDNSServers parses DNS servers used for client, public, mgmt networks
|
||||
func (d *DNS) ProcessDNSServers(op trace.Operation) ([]net.IP, error) {
|
||||
var parsedDNS []net.IP
|
||||
if len(d.DNS) > 0 {
|
||||
d.IsSet = true
|
||||
}
|
||||
|
||||
for _, n := range d.DNS {
|
||||
if n != "" {
|
||||
s := net.ParseIP(n)
|
||||
if s == nil {
|
||||
return nil, errors.Errorf("Invalid DNS server specified: %s", n)
|
||||
}
|
||||
parsedDNS = append(parsedDNS, s)
|
||||
}
|
||||
}
|
||||
|
||||
if len(parsedDNS) > 3 {
|
||||
op.Warn("Maximum of 3 DNS servers allowed. Additional servers specified will be ignored.")
|
||||
}
|
||||
|
||||
op.Debugf("VCH DNS servers: %s", parsedDNS)
|
||||
return parsedDNS, nil
|
||||
}
|
||||
35
vendor/github.com/vmware/vic/cmd/vic-machine/common/id.go
generated
vendored
Normal file
35
vendor/github.com/vmware/vic/cmd/vic-machine/common/id.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
type VCHID struct {
|
||||
// VCH id
|
||||
ID string
|
||||
}
|
||||
|
||||
func (i *VCHID) IDFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "The ID of the Virtual Container Host, e.g. vm-220",
|
||||
Destination: &i.ID,
|
||||
},
|
||||
}
|
||||
}
|
||||
196
vendor/github.com/vmware/vic/cmd/vic-machine/common/images.go
generated
vendored
Normal file
196
vendor/github.com/vmware/vic/cmd/vic-machine/common/images.go
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
)
|
||||
|
||||
const (
|
||||
ApplianceImageKey = "core"
|
||||
LinuxImageKey = "linux"
|
||||
ApplianceImageName = "appliance.iso"
|
||||
LinuxImageName = "bootstrap.iso"
|
||||
|
||||
// An ISO 9660 sector is normally 2 KiB long. Although the specification allows for alternative sector sizes, you will rarely find anything other than 2 KiB.
|
||||
ISO9660SectorSize = 2048
|
||||
ISOVolumeSector = 0x10
|
||||
PublisherOffset = 318
|
||||
)
|
||||
|
||||
var (
|
||||
images = map[string][]string{
|
||||
ApplianceImageKey: {ApplianceImageName},
|
||||
LinuxImageKey: {LinuxImageName},
|
||||
}
|
||||
)
|
||||
|
||||
type Images struct {
|
||||
ApplianceISO string
|
||||
BootstrapISO string
|
||||
OSType string
|
||||
}
|
||||
|
||||
func (i *Images) ImageFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "appliance-iso, ai",
|
||||
Value: "",
|
||||
Usage: "The appliance iso",
|
||||
Destination: &i.ApplianceISO,
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "bootstrap-iso, bi",
|
||||
Value: "",
|
||||
Usage: "The bootstrap iso",
|
||||
Destination: &i.BootstrapISO,
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Images) CheckImagesFiles(op trace.Operation, force bool) (map[string]string, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
i.OSType = "linux"
|
||||
// detect images files
|
||||
osImgs, ok := images[i.OSType]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Specified OS %q is not known to this installer", i.OSType)
|
||||
}
|
||||
|
||||
imgs := make(map[string]string)
|
||||
result := make(map[string]string)
|
||||
if i.ApplianceISO == "" {
|
||||
i.ApplianceISO = images[ApplianceImageKey][0]
|
||||
}
|
||||
imgs[ApplianceImageName] = i.ApplianceISO
|
||||
|
||||
if i.BootstrapISO == "" {
|
||||
i.BootstrapISO = osImgs[0]
|
||||
}
|
||||
imgs[LinuxImageName] = i.BootstrapISO
|
||||
|
||||
for name, img := range imgs {
|
||||
_, err := os.Stat(img)
|
||||
if os.IsNotExist(err) {
|
||||
var dir string
|
||||
dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
_, err = os.Stat(filepath.Join(dir, img))
|
||||
if err == nil {
|
||||
img = filepath.Join(dir, img)
|
||||
}
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
op.Warnf("\t\tUnable to locate %s in the current or installer directory.", img)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version, err := i.checkImageVersion(op, img, force)
|
||||
if err != nil {
|
||||
op.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
versionedName := fmt.Sprintf("%s-%s", version, name)
|
||||
result[versionedName] = img
|
||||
if name == ApplianceImageName {
|
||||
i.ApplianceISO = versionedName
|
||||
} else {
|
||||
i.BootstrapISO = versionedName
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetImageVersion will read iso file version from Primary Volume Descriptor, field "Publisher Identifier"
|
||||
func (i *Images) GetImageVersion(op trace.Operation, img string) (string, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
f, err := os.Open(img)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("failed to open iso file %q: %s", img, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// System area goes from sectors 0x00 to 0x0F. Volume descriptors can be
|
||||
// found starting at sector 0x10
|
||||
|
||||
_, err = f.Seek(int64(ISOVolumeSector*ISO9660SectorSize)+PublisherOffset, 0)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("failed to locate iso version section in file %q: %s", img, err)
|
||||
}
|
||||
publisherBytes := make([]byte, 128)
|
||||
size, err := f.Read(publisherBytes)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("failed to read iso version in file %q: %s", img, err)
|
||||
}
|
||||
if size == 0 {
|
||||
return "", errors.Errorf("version is not set in iso file %q", img)
|
||||
}
|
||||
|
||||
versions := strings.Fields(string(publisherBytes[:size]))
|
||||
if len(versions) > 0 {
|
||||
return versions[len(versions)-1], nil
|
||||
}
|
||||
return "version-unknown", nil
|
||||
}
|
||||
|
||||
func (i *Images) checkImageVersion(op trace.Operation, img string, force bool) (string, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
ver, err := i.GetImageVersion(op, img)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sv := i.getNoCommitHashVersion(op, ver)
|
||||
if sv == "" {
|
||||
op.Debugf("Version is not set in %q", img)
|
||||
ver = ""
|
||||
}
|
||||
|
||||
installerSV := i.getNoCommitHashVersion(op, version.GetBuild().ShortVersion())
|
||||
|
||||
// here compare version without last commit hash, to make developer life easier
|
||||
if !strings.EqualFold(installerSV, sv) {
|
||||
message := fmt.Sprintf("iso file %q version %q inconsistent with installer version %q", img, strings.ToLower(ver), version.GetBuild().ShortVersion())
|
||||
if !force {
|
||||
return "", errors.Errorf("%s. Specify --force to force create. ", message)
|
||||
}
|
||||
op.Warn(message)
|
||||
}
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
func (i *Images) getNoCommitHashVersion(op trace.Operation, version string) string {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
j := strings.LastIndex(version, "-")
|
||||
if j == -1 {
|
||||
return ""
|
||||
}
|
||||
return version[:j]
|
||||
}
|
||||
121
vendor/github.com/vmware/vic/cmd/vic-machine/common/images_test.go
generated
vendored
Normal file
121
vendor/github.com/vmware/vic/cmd/vic-machine/common/images_test.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
image = &Images{}
|
||||
)
|
||||
|
||||
func TestImageNotFound(t *testing.T) {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
tmpfile, err := ioutil.TempFile("", "appIso")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
op := trace.NewOperation(context.Background(), "TestImageNotFound")
|
||||
|
||||
image.ApplianceISO = tmpfile.Name()
|
||||
image.OSType = "linux"
|
||||
if _, err = image.CheckImagesFiles(op, false); err == nil {
|
||||
t.Errorf("Error is expected for boot iso file is not found.")
|
||||
}
|
||||
}
|
||||
|
||||
func writeImageVersion(fileName string, version string) error {
|
||||
f, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := f.Truncate(int64(0x10*2048) + 318); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.WriteAt([]byte(version), int64(0x10*2048)+318); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestImageChecks(t *testing.T) {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
tmpfile, err := ioutil.TempFile("", "bootIso")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
op := trace.NewOperation(context.Background(), "TestImageChecks")
|
||||
|
||||
_, err = os.Create("appliance.iso")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create default appliance iso file")
|
||||
}
|
||||
defer os.Remove("appliance.iso")
|
||||
|
||||
image.ApplianceISO = ""
|
||||
image.BootstrapISO = tmpfile.Name()
|
||||
image.OSType = "linux"
|
||||
var imageFiles map[string]string
|
||||
if _, err = image.CheckImagesFiles(op, false); err == nil {
|
||||
t.Errorf("Error is expected")
|
||||
}
|
||||
|
||||
if err = writeImageVersion("appliance.iso", "Inc. 0.1-000-abcd"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err = writeImageVersion(tmpfile.Name(), "Inc. 0.1-000-abcd"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
cliContext := &cli.Context{
|
||||
App: &cli.App{
|
||||
Version: "Inconsistent",
|
||||
},
|
||||
}
|
||||
if _, err = image.CheckImagesFiles(op, false); err == nil {
|
||||
t.Errorf("Error is expected")
|
||||
}
|
||||
|
||||
cliContext.App.Version = "0.1-000-abcd"
|
||||
if imageFiles, err = image.CheckImagesFiles(op, true); err != nil {
|
||||
t.Errorf("Error is returned: %s", err)
|
||||
}
|
||||
found := false
|
||||
for _, file := range imageFiles {
|
||||
if file == tmpfile.Name() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Image file list does not contain input, %s", imageFiles)
|
||||
}
|
||||
}
|
||||
59
vendor/github.com/vmware/vic/cmd/vic-machine/common/kubelet.go
generated
vendored
Normal file
59
vendor/github.com/vmware/vic/cmd/vic-machine/common/kubelet.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// Kubelet holds credentials for the VCH operations user
|
||||
type Kubelet struct {
|
||||
ServerAddress *string `cmd:"kubelet"`
|
||||
ConfigFile *string
|
||||
}
|
||||
|
||||
func (v *Kubelet) Flags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "k8s-server-address",
|
||||
Value: flags.NewOptionalString(&v.ServerAddress),
|
||||
Usage: "The Kubernetes Server URL, <hostname/ip>:<port>",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "k8s-config",
|
||||
Value: flags.NewOptionalString(&v.ConfigFile),
|
||||
Usage: "Kubernetes client config file",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Kubelet) ProcessKubelet(op trace.Operation, isCreateOp bool) error {
|
||||
if isCreateOp {
|
||||
if v.ServerAddress != nil && v.ConfigFile == nil {
|
||||
return errors.Errorf("A Kubernetes Config File must be specified when specifying a Kubernetes Server Address")
|
||||
}
|
||||
if v.ServerAddress == nil && v.ConfigFile != nil {
|
||||
return errors.Errorf("A Kubernetes Server Address must be specified when specifying a Kubernetes Config File")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
15
vendor/github.com/vmware/vic/cmd/vic-machine/common/kubelet_test.go
generated
vendored
Normal file
15
vendor/github.com/vmware/vic/cmd/vic-machine/common/kubelet_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
78
vendor/github.com/vmware/vic/cmd/vic-machine/common/limits.go
generated
vendored
Normal file
78
vendor/github.com/vmware/vic/cmd/vic-machine/common/limits.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
)
|
||||
|
||||
type ResourceLimits struct {
|
||||
VCHCPULimitsMHz *int `cmd:"cpu"`
|
||||
VCHCPUReservationsMHz *int `cmd:"cpu-reservation"`
|
||||
VCHCPUShares *types.SharesInfo `cmd:"cpu-shares"`
|
||||
|
||||
VCHMemoryLimitsMB *int `cmd:"memory"`
|
||||
VCHMemoryReservationsMB *int `cmd:"memory-reservation"`
|
||||
VCHMemoryShares *types.SharesInfo `cmd:"memory-shares"`
|
||||
|
||||
IsSet bool
|
||||
}
|
||||
|
||||
func (r *ResourceLimits) VCHMemoryLimitFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "memory, mem",
|
||||
Value: flags.NewOptionalInt(&r.VCHMemoryLimitsMB),
|
||||
Usage: "VCH resource pool memory limit in MB (unlimited=0)",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "memory-reservation, memr",
|
||||
Value: flags.NewOptionalInt(&r.VCHMemoryReservationsMB),
|
||||
Usage: "VCH resource pool memory reservation in MB",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "memory-shares, mems",
|
||||
Value: flags.NewSharesFlag(&r.VCHMemoryShares),
|
||||
Usage: "VCH resource pool memory shares in level or share number, e.g. high, normal, low, or 163840",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ResourceLimits) VCHCPULimitFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "cpu",
|
||||
Value: flags.NewOptionalInt(&r.VCHCPULimitsMHz),
|
||||
Usage: "VCH resource pool vCPUs limit in MHz (unlimited=0)",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "cpu-reservation, cpur",
|
||||
Value: flags.NewOptionalInt(&r.VCHCPUReservationsMHz),
|
||||
Usage: "VCH resource pool reservation in MHz",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "cpu-shares, cpus",
|
||||
Value: flags.NewSharesFlag(&r.VCHCPUShares),
|
||||
Usage: "VCH VCH resource pool vCPUs shares, in level or share number, e.g. high, normal, low, or 4000",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
29
vendor/github.com/vmware/vic/cmd/vic-machine/common/networks.go
generated
vendored
Normal file
29
vendor/github.com/vmware/vic/cmd/vic-machine/common/networks.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
type Networks struct {
|
||||
ClientNetworkName string
|
||||
ClientNetworkIP string
|
||||
ClientNetworkGateway string
|
||||
|
||||
PublicNetworkName string
|
||||
PublicNetworkIP string
|
||||
PublicNetworkGateway string
|
||||
|
||||
ManagementNetworkName string
|
||||
ManagementNetworkIP string
|
||||
ManagementNetworkGateway string
|
||||
}
|
||||
38
vendor/github.com/vmware/vic/cmd/vic-machine/common/operation.go
generated
vendored
Normal file
38
vendor/github.com/vmware/vic/cmd/vic-machine/common/operation.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
func NewOperation(clic *cli.Context, debug *int) trace.Operation {
|
||||
op := trace.NewOperation(context.Background(), clic.App.Name)
|
||||
op.Logger = logrus.New()
|
||||
op.Logger.Out = clic.App.Writer
|
||||
|
||||
if debug != nil && *debug > 0 {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
trace.Logger.Level = logrus.DebugLevel
|
||||
op.Logger.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
return op
|
||||
}
|
||||
112
vendor/github.com/vmware/vic/cmd/vic-machine/common/ops_credentials.go
generated
vendored
Normal file
112
vendor/github.com/vmware/vic/cmd/vic-machine/common/ops_credentials.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// OpsCredentials holds credentials for the VCH operations user
|
||||
type OpsCredentials struct {
|
||||
OpsUser *string `cmd:"ops-user"`
|
||||
OpsPassword *string
|
||||
GrantPerms *bool
|
||||
IsSet bool
|
||||
}
|
||||
|
||||
func (o *OpsCredentials) Flags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "ops-user",
|
||||
Value: flags.NewOptionalString(&o.OpsUser),
|
||||
Usage: "The user with which the VCH operates after creation. Defaults to the credential supplied with target",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "ops-password",
|
||||
Value: flags.NewOptionalString(&o.OpsPassword),
|
||||
Usage: "Password or token for the operations user. Defaults to the credential supplied with target",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "ops-grant-perms",
|
||||
Value: flags.NewOptionalBool(&o.GrantPerms),
|
||||
Usage: "Create roles and grant required permissions to the specified ops-use",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessOpsCredentials processes fields for the VCH operations user. When invoked
|
||||
// during a VCH create operation, adminUser and adminPassword must be supplied to
|
||||
// be used as ops credentials if they are not specified by the user. For a configure
|
||||
// operation, adminUser and adminPassword are not needed.
|
||||
func (o *OpsCredentials) ProcessOpsCredentials(op trace.Operation, isCreateOp bool, adminUser string, adminPassword *string) error {
|
||||
if o.OpsUser == nil && o.OpsPassword != nil {
|
||||
return errors.New("Password for operations user specified without user having been specified")
|
||||
}
|
||||
|
||||
if isCreateOp {
|
||||
if o.OpsUser == nil {
|
||||
// Check if there was a request to setup ops-user Roles and Permissions
|
||||
if o.GrantPerms != nil {
|
||||
// If true return error
|
||||
if *o.GrantPerms {
|
||||
return errors.Errorf("Invalid use of flag: --ops-grant-perms. Cannot setup Roles and Permissions for administrative user.")
|
||||
}
|
||||
// If false ignore
|
||||
o.GrantPerms = nil
|
||||
}
|
||||
op.Warn("Using administrative user for VCH operation - use --ops-user to improve security (see -x for advanced help)")
|
||||
o.OpsUser = &adminUser
|
||||
if adminPassword == nil {
|
||||
return errors.New("Unable to use nil password from administrative user for operations user")
|
||||
}
|
||||
o.OpsPassword = adminPassword
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if o.OpsUser != nil {
|
||||
o.IsSet = true
|
||||
}
|
||||
}
|
||||
|
||||
if o.OpsPassword != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prompt for the ops password only during a create operation or a configure
|
||||
// operation where the ops user is specified.
|
||||
if isCreateOp || o.IsSet {
|
||||
op.Infof("vSphere password for %s: ", *o.OpsUser)
|
||||
b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Failed to read password from stdin: %s", err)
|
||||
cli.NewExitError(message, 1)
|
||||
}
|
||||
sb := string(b)
|
||||
o.OpsPassword = &sb
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
109
vendor/github.com/vmware/vic/cmd/vic-machine/common/ops_credentials_test.go
generated
vendored
Normal file
109
vendor/github.com/vmware/vic/cmd/vic-machine/common/ops_credentials_test.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
func TestProcessOpsCredentials(t *testing.T) {
|
||||
createOps := &OpsCredentials{}
|
||||
isCreateOp := true
|
||||
adminUser := "admin"
|
||||
adminPassword := ""
|
||||
|
||||
op := trace.NewOperation(context.Background(), "TestProcessOpsCredentials")
|
||||
|
||||
// There should be an error if the admin password is not specified for a create operation.
|
||||
err := createOps.ProcessOpsCredentials(op, isCreateOp, adminUser, nil)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = createOps.ProcessOpsCredentials(op, isCreateOp, adminUser, &adminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *createOps.OpsUser, adminUser)
|
||||
assert.Equal(t, *createOps.OpsPassword, adminPassword)
|
||||
|
||||
opsUser := "op"
|
||||
opsPassword := "opPass"
|
||||
createOps.OpsUser = &opsUser
|
||||
createOps.OpsPassword = &opsPassword
|
||||
err = createOps.ProcessOpsCredentials(op, isCreateOp, adminUser, &adminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *createOps.OpsUser, opsUser)
|
||||
assert.Equal(t, *createOps.OpsPassword, opsPassword)
|
||||
|
||||
// Ensure that fields are set correctly for a configure operation.
|
||||
configureOps := &OpsCredentials{
|
||||
OpsUser: &opsUser,
|
||||
OpsPassword: &opsPassword,
|
||||
}
|
||||
isCreateOp = false
|
||||
err = configureOps.ProcessOpsCredentials(op, isCreateOp, "", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, configureOps.IsSet)
|
||||
assert.Equal(t, *createOps.OpsUser, opsUser)
|
||||
assert.Equal(t, *createOps.OpsPassword, opsPassword)
|
||||
|
||||
// There should be an error if the ops-password is specified without ops-user.
|
||||
configureOps.OpsUser = nil
|
||||
err = configureOps.ProcessOpsCredentials(op, isCreateOp, "", nil)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// Test correct grant permissions
|
||||
createOps = &OpsCredentials{}
|
||||
createOps.OpsUser = &opsUser
|
||||
createOps.OpsPassword = &opsPassword
|
||||
grantPerms := true
|
||||
createOps.GrantPerms = &grantPerms
|
||||
err = createOps.ProcessOpsCredentials(op, true, adminUser, &adminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *createOps.OpsUser, opsUser)
|
||||
assert.Equal(t, *createOps.OpsPassword, opsPassword)
|
||||
assert.True(t, *createOps.GrantPerms)
|
||||
|
||||
// Create Negative test: grantPerms is set to true but there is no ops-user,
|
||||
// grantPerms should be reset to false
|
||||
createOps = &OpsCredentials{}
|
||||
createOps.OpsUser = nil
|
||||
createOps.OpsPassword = nil
|
||||
grantPerms = true
|
||||
createOps.GrantPerms = &grantPerms
|
||||
err = createOps.ProcessOpsCredentials(op, true, adminUser, &adminPassword)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create Negative test: grantPerms is set to true but there is no ops-user,
|
||||
// grantPerms should be reset to false
|
||||
createOps = &OpsCredentials{}
|
||||
createOps.OpsUser = nil
|
||||
createOps.OpsPassword = nil
|
||||
grantPerms = false
|
||||
createOps.GrantPerms = &grantPerms
|
||||
err = createOps.ProcessOpsCredentials(op, true, adminUser, &adminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, createOps.GrantPerms)
|
||||
|
||||
// Configure test: grantPerms is set to true but there is no ops-user,
|
||||
// grantPerms should be true as the ops-user may come from the config
|
||||
grantPerms = true
|
||||
createOps.GrantPerms = &grantPerms
|
||||
err = createOps.ProcessOpsCredentials(op, false, adminUser, &adminPassword)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, *createOps.GrantPerms)
|
||||
}
|
||||
70
vendor/github.com/vmware/vic/cmd/vic-machine/common/proxy.go
generated
vendored
Normal file
70
vendor/github.com/vmware/vic/cmd/vic-machine/common/proxy.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
type Proxies struct {
|
||||
HTTPSProxy *string
|
||||
HTTPProxy *string
|
||||
IsSet bool
|
||||
}
|
||||
|
||||
func (p *Proxies) ProxyFlags(hidden bool) []cli.Flag {
|
||||
return []cli.Flag{
|
||||
// proxies
|
||||
cli.GenericFlag{
|
||||
Name: "https-proxy",
|
||||
Value: flags.NewOptionalString(&p.HTTPSProxy),
|
||||
Usage: "An HTTPS proxy for use when fetching images, in the form https://fqdn_or_ip:port",
|
||||
Hidden: hidden,
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "http-proxy",
|
||||
Value: flags.NewOptionalString(&p.HTTPProxy),
|
||||
Usage: "An HTTP proxy for use when fetching images, in the form http://fqdn_or_ip:port",
|
||||
Hidden: hidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxies) ProcessProxies() (hproxy, sproxy *url.URL, err error) {
|
||||
if p.HTTPProxy != nil || p.HTTPSProxy != nil {
|
||||
p.IsSet = true
|
||||
}
|
||||
if p.HTTPProxy != nil && *p.HTTPProxy != "" {
|
||||
hproxy, err = url.Parse(*p.HTTPProxy)
|
||||
if err != nil || hproxy.Host == "" || hproxy.Scheme != "http" {
|
||||
err = cli.NewExitError(fmt.Sprintf("Could not parse HTTP proxy - expected format http://fqnd_or_ip:port: %s", *p.HTTPProxy), 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if p.HTTPSProxy != nil && *p.HTTPSProxy != "" {
|
||||
sproxy, err = url.Parse(*p.HTTPSProxy)
|
||||
if err != nil || sproxy.Host == "" || sproxy.Scheme != "https" {
|
||||
err = cli.NewExitError(fmt.Sprintf("Could not parse HTTPS proxy - expected format https://fqnd_or_ip:port: %s", *p.HTTPSProxy), 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
82
vendor/github.com/vmware/vic/cmd/vic-machine/common/registries.go
generated
vendored
Normal file
82
vendor/github.com/vmware/vic/cmd/vic-machine/common/registries.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// Registries contains metadata used to create/configure registry CA data
|
||||
type Registries struct {
|
||||
RegistryCAsArg cli.StringSlice `arg:"registry-ca"`
|
||||
InsecureRegistriesArg cli.StringSlice `arg:"insecure-registry"`
|
||||
WhitelistRegistriesArg cli.StringSlice `arg:"whitelist-registry"`
|
||||
|
||||
RegistryCAs []byte
|
||||
|
||||
InsecureRegistries []string `cmd:"insecure-registry"`
|
||||
WhitelistRegistries []string `cmd:"whitelist-registry"`
|
||||
}
|
||||
|
||||
// Flags generates command line flags
|
||||
func (r *Registries) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "registry-ca, rc",
|
||||
Usage: "Specify a list of additional certificate authority files to use to verify secure registry servers",
|
||||
Value: &r.RegistryCAsArg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// LoadRegistryCAs loads additional CA certs for docker registry usage
|
||||
func (r *Registries) loadRegistryCAs(op trace.Operation) ([]byte, error) {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
var registryCerts []byte
|
||||
for _, f := range r.RegistryCAsArg {
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
err = errors.Errorf("Failed to load authority from file %s: %s", f, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registryCerts = append(registryCerts, b...)
|
||||
op.Infof("Loaded registry CA from %s", f)
|
||||
}
|
||||
|
||||
return registryCerts, nil
|
||||
}
|
||||
|
||||
func (r *Registries) ProcessRegistries(op trace.Operation) error {
|
||||
// load additional certificate authorities for use with registries
|
||||
if len(r.RegistryCAsArg) > 0 {
|
||||
registryCAs, err := r.loadRegistryCAs(op)
|
||||
if err != nil {
|
||||
return errors.Errorf("Unable to load CA certificates for registry logins: %s", err)
|
||||
}
|
||||
|
||||
r.RegistryCAs = registryCAs
|
||||
}
|
||||
|
||||
r.InsecureRegistries = r.InsecureRegistriesArg.Value()
|
||||
r.WhitelistRegistries = r.WhitelistRegistriesArg.Value()
|
||||
return nil
|
||||
}
|
||||
118
vendor/github.com/vmware/vic/cmd/vic-machine/common/target.go
generated
vendored
Normal file
118
vendor/github.com/vmware/vic/cmd/vic-machine/common/target.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/flags"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
URL *url.URL `cmd:"target"`
|
||||
|
||||
User string
|
||||
Password *string
|
||||
CloneTicket string
|
||||
Thumbprint string `cmd:"thumbprint"`
|
||||
}
|
||||
|
||||
func NewTarget() *Target {
|
||||
return &Target{}
|
||||
}
|
||||
|
||||
func (t *Target) TargetFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.GenericFlag{
|
||||
Name: "target, t",
|
||||
Value: flags.NewURLFlag(&t.URL),
|
||||
Usage: "REQUIRED. ESXi or vCenter connection URL, specifying a datacenter if multiple exist e.g. root:password@VC-FQDN/datacenter",
|
||||
EnvVar: "VIC_MACHINE_TARGET",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user, u",
|
||||
Value: "",
|
||||
Usage: "ESX or vCenter user",
|
||||
Destination: &t.User,
|
||||
EnvVar: "VIC_MACHINE_USER",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "password, p",
|
||||
Value: flags.NewOptionalString(&t.Password),
|
||||
Usage: "ESX or vCenter password",
|
||||
EnvVar: "VIC_MACHINE_PASSWORD",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "thumbprint",
|
||||
Value: "",
|
||||
Destination: &t.Thumbprint,
|
||||
Usage: "ESX or vCenter host certificate thumbprint",
|
||||
EnvVar: "VIC_MACHINE_THUMBPRINT",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// HasCredentials check that the credentials have been supplied by any of the permitted mechanisms
|
||||
func (t *Target) HasCredentials(op trace.Operation) error {
|
||||
if t.URL == nil {
|
||||
return cli.NewExitError("--target argument must be specified", 1)
|
||||
}
|
||||
|
||||
// assume if a vsphere session key exists, we want to use that instead of user/pass
|
||||
if t.CloneTicket != "" {
|
||||
t.URL.User = nil // necessary?
|
||||
return nil
|
||||
}
|
||||
|
||||
var urlUser string
|
||||
var urlPassword *string
|
||||
|
||||
if t.URL.User != nil {
|
||||
urlUser = t.URL.User.Username()
|
||||
if passwd, set := t.URL.User.Password(); set {
|
||||
urlPassword = &passwd
|
||||
}
|
||||
}
|
||||
if t.User == "" && urlUser == "" {
|
||||
return cli.NewExitError("vSphere user must be specified, either with --user or as part of --target", 1)
|
||||
} else if t.User == "" && urlUser != "" {
|
||||
t.User = urlUser
|
||||
}
|
||||
|
||||
//prompt for passwd if not specified
|
||||
if t.Password == nil && urlPassword == nil {
|
||||
op.Infof("vSphere password for %s: ", t.User)
|
||||
b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
message := fmt.Sprintf("Failed to read password from stdin: %s", err)
|
||||
cli.NewExitError(message, 1)
|
||||
}
|
||||
sb := string(b)
|
||||
t.Password = &sb
|
||||
} else if t.Password == nil && urlPassword != nil {
|
||||
t.Password = urlPassword
|
||||
}
|
||||
|
||||
// Used by vic-machine for Session login
|
||||
t.URL.User = url.UserPassword(t.User, *t.Password)
|
||||
|
||||
return nil
|
||||
}
|
||||
92
vendor/github.com/vmware/vic/cmd/vic-machine/common/target_test.go
generated
vendored
Normal file
92
vendor/github.com/vmware/vic/cmd/vic-machine/common/target_test.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
func TestFlags(t *testing.T) {
|
||||
target := NewTarget()
|
||||
flags := target.TargetFlags()
|
||||
|
||||
if len(flags) != 4 {
|
||||
t.Errorf("Wrong flag numbers")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
op := trace.NewOperation(context.Background(), "TestProcess")
|
||||
|
||||
passwd := "pass"
|
||||
url1, _ := soap.ParseURL("127.0.0.1")
|
||||
url2, _ := soap.ParseURL("root:@127.0.0.1")
|
||||
url3, _ := soap.ParseURL("line:password@127.0.0.1")
|
||||
url4, _ := soap.ParseURL("root:pass@127.0.0.1")
|
||||
result, _ := url.Parse("https://root:pass@127.0.0.1/sdk")
|
||||
passEmpty := ""
|
||||
result1, _ := url.Parse("https://root:@127.0.0.1/sdk")
|
||||
tests := []struct {
|
||||
URL *url.URL
|
||||
User string
|
||||
Password *string
|
||||
|
||||
err error
|
||||
result *url.URL
|
||||
}{
|
||||
{nil, "", nil, cli.NewExitError("--target argument must be specified", 1), nil},
|
||||
{nil, "root", nil, cli.NewExitError("--target argument must be specified", 1), nil},
|
||||
{nil, "root", &passwd, cli.NewExitError("--target argument must be specified", 1), nil},
|
||||
{url1, "root", &passwd, nil, result},
|
||||
{url4, "", nil, nil, result},
|
||||
{url3, "root", &passwd, nil, result},
|
||||
{url2, "", &passwd, nil, result},
|
||||
{url1, "root", &passEmpty, nil, result1},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
target := NewTarget()
|
||||
target.URL = test.URL
|
||||
target.User = test.User
|
||||
target.Password = test.Password
|
||||
if target.URL != nil {
|
||||
t.Logf("Before processing, url: %s", target.URL.String())
|
||||
}
|
||||
e := target.HasCredentials(op)
|
||||
if test.err != nil {
|
||||
if e == nil {
|
||||
t.Errorf("Empty error")
|
||||
}
|
||||
if e.Error() != test.err.Error() {
|
||||
t.Errorf("Unexpected error message: %s", e.Error())
|
||||
}
|
||||
} else if e != nil {
|
||||
t.Errorf("Unexpected error %s", e.Error())
|
||||
} else {
|
||||
if target.URL != test.URL {
|
||||
t.Errorf("unexpected result url: %s", target.URL.String())
|
||||
} else {
|
||||
t.Logf("result url: %s", target.URL.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
vendor/github.com/vmware/vic/cmd/vic-machine/common/utils.go
generated
vendored
Normal file
76
vendor/github.com/vmware/vic/cmd/vic-machine/common/utils.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// scheme string for nfs volume store targets
|
||||
NfsScheme = "nfs"
|
||||
|
||||
// scheme string for ds volume store targets
|
||||
DsScheme = "ds"
|
||||
|
||||
// scheme string for volume store targets without a scheme
|
||||
EmptyScheme = ""
|
||||
)
|
||||
|
||||
// https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2046088
|
||||
const unsuppCharsRegex = `%|&|\*|\$|#|@|!|\\|/|:|\?|"|<|>|;|'|\|`
|
||||
|
||||
// Same as unsuppCharsRegex but allows / and : for datastore paths
|
||||
const unsuppCharsDatastoreRegex = `%|&|\*|\$|#|@|!|\\|\?|"|<|>|;|'|\|`
|
||||
|
||||
var reUnsupp = regexp.MustCompile(unsuppCharsRegex)
|
||||
var reUnsuppDatastore = regexp.MustCompile(unsuppCharsDatastoreRegex)
|
||||
|
||||
func LogErrorIfAny(op trace.Operation, clic *cli.Context, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
op.Errorf("--------------------")
|
||||
op.Errorf("%s %s failed: %s\n", clic.App.Name, clic.Command.Name, errors.ErrorStack(err))
|
||||
return cli.NewExitError("", 1)
|
||||
}
|
||||
|
||||
// CheckUnsupportedChars returns an error if string contains special characters
|
||||
func CheckUnsupportedChars(s string) error {
|
||||
return checkUnsupportedChars(s, reUnsupp)
|
||||
}
|
||||
|
||||
// CheckUnsupportedCharsDatastore returns an error if a datastore string contains special characters
|
||||
func CheckUnsupportedCharsDatastore(s string) error {
|
||||
return checkUnsupportedChars(s, reUnsuppDatastore)
|
||||
}
|
||||
|
||||
func checkUnsupportedChars(s string, re *regexp.Regexp) error {
|
||||
st := []byte(s)
|
||||
var v []int
|
||||
// this is validation step for characters in a datastore URI
|
||||
if v = re.FindIndex(st); v == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unsupported character %q in %q", s[v[0]:v[1]], s)
|
||||
}
|
||||
121
vendor/github.com/vmware/vic/cmd/vic-machine/common/utils_test.go
generated
vendored
Normal file
121
vendor/github.com/vmware/vic/cmd/vic-machine/common/utils_test.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckUnsupportedchars(t *testing.T) {
|
||||
tests := []struct {
|
||||
S string
|
||||
valid bool
|
||||
}{
|
||||
{"anjunabeats", true},
|
||||
{"tony-1", true},
|
||||
{"paavo_1", true},
|
||||
{"jono(1)", true},
|
||||
{"oceanlab (1)", true},
|
||||
{"test%", false},
|
||||
{"test&", false},
|
||||
{"test*", false},
|
||||
{"test$", false},
|
||||
{"test#", false},
|
||||
{"test@", false},
|
||||
{"test!", false},
|
||||
{`test\`, false},
|
||||
{"test/", false}, // U+002F
|
||||
{"test\u002f", false}, // U+002F
|
||||
{`testЯ`, true}, // U+042F
|
||||
{"test\u042f", true}, // U+042F
|
||||
{`testį`, true}, // U+012F
|
||||
{"test\u012f", true}, // U+012F
|
||||
{"test:", false},
|
||||
{"test?", false},
|
||||
{`test"`, false},
|
||||
{"test<", false},
|
||||
{"test>", false},
|
||||
{"test;", false},
|
||||
{"test'", false},
|
||||
{"test|", false},
|
||||
{"test|", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := CheckUnsupportedChars(test.S)
|
||||
if err != nil {
|
||||
if test.valid {
|
||||
t.Errorf("got %q, expected pass for %q", err, test.S)
|
||||
}
|
||||
t.Logf("test case %q passed", test.S)
|
||||
continue
|
||||
}
|
||||
if test.valid {
|
||||
t.Logf("test case %q passed", test.S)
|
||||
continue
|
||||
}
|
||||
t.Errorf("got %q, expected error for %q", err, test.S)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckUnsupportedCharsDatastore(t *testing.T) {
|
||||
tests := []struct {
|
||||
S string
|
||||
valid bool
|
||||
}{
|
||||
{"anjunabeats", true},
|
||||
{"tony-1", true},
|
||||
{"paavo_1", true},
|
||||
{"jono(1)", true},
|
||||
{"oceanlab (1)", true},
|
||||
{"waawn/", true},
|
||||
{"tristate:", true},
|
||||
{"test%", false},
|
||||
{"test&", false},
|
||||
{"test*", false},
|
||||
{"test$", false},
|
||||
{"test#", false},
|
||||
{"test@", false},
|
||||
{"test!", false}, // U+0021
|
||||
{"test\u0021", false}, // U+0021
|
||||
{`testġ`, true}, // U+0121
|
||||
{"test\u0121", true}, // U+0121
|
||||
{`test\`, false},
|
||||
{"test?", false},
|
||||
{`test"`, false},
|
||||
{"test<", false},
|
||||
{"test>", false},
|
||||
{"test;", false},
|
||||
{"test'", false},
|
||||
{"test|", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := CheckUnsupportedCharsDatastore(test.S)
|
||||
|
||||
if err != nil {
|
||||
if test.valid {
|
||||
t.Errorf("got %q, expected pass for %q", err, test.S)
|
||||
}
|
||||
t.Logf("test case %q passed", test.S)
|
||||
continue
|
||||
}
|
||||
if test.valid {
|
||||
t.Logf("test case %q passed", test.S)
|
||||
continue
|
||||
}
|
||||
t.Errorf("got %q, expected error for %q", err, test.S)
|
||||
}
|
||||
}
|
||||
111
vendor/github.com/vmware/vic/cmd/vic-machine/common/volume_stores.go
generated
vendored
Normal file
111
vendor/github.com/vmware/vic/cmd/vic-machine/common/volume_stores.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
dsInputFormat = "<datastore url w/ path>:label"
|
||||
nfsInputFormat = "nfs://<host>/<url-path>?<mount option as query parameters>:<label>"
|
||||
)
|
||||
|
||||
type VolumeStores struct {
|
||||
VolumeStores cli.StringSlice `arg:"volume-store"`
|
||||
}
|
||||
|
||||
func (v *VolumeStores) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "volume-store, vs",
|
||||
Value: &v.VolumeStores,
|
||||
Usage: "Specify a list of location and label for volume store, nfs stores can have mount options specified as query parameters in the url target. \n\t Examples for a vsphere backed volume store are: \"datastore/path:label\" or \"datastore:label\" or \"ds://my-datastore-name:store-label\"\n\t Examples for nfs back volume stores are: \"nfs://127.0.0.1/path/to/share/point?uid=1234&gid=5678&proto=tcp:my-volume-store-label\" or \"nfs://my-store/path/to/share/point:my-label\"",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VolumeStores) ProcessVolumeStores() (map[string]*url.URL, error) {
|
||||
volumeLocations := make(map[string]*url.URL)
|
||||
|
||||
for _, arg := range v.VolumeStores {
|
||||
urlTarget, rawTarget, label, err := processVolumeStoreParam(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch urlTarget.Scheme {
|
||||
case NfsScheme:
|
||||
// nothing needs to be done here. parsing the url is enough for pre-validation checking of an nfs target.
|
||||
case EmptyScheme, DsScheme:
|
||||
// a datastore target is our default assumption
|
||||
urlTarget.Scheme = DsScheme
|
||||
if err := CheckUnsupportedCharsDatastore(rawTarget); err != nil {
|
||||
return nil, fmt.Errorf("--volume-store contains unsupported characters for datastore target: %s Allowed characters are alphanumeric, space and symbols - _ ( ) / : ,", err)
|
||||
}
|
||||
|
||||
if len(urlTarget.RawQuery) > 0 {
|
||||
return nil, fmt.Errorf("volume store input must be in format datastore/path:label or %s", nfsInputFormat)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("%s", "Please specify a datastore or nfs target. See -vs usage for examples.")
|
||||
}
|
||||
|
||||
volumeLocations[label] = urlTarget
|
||||
}
|
||||
|
||||
return volumeLocations, nil
|
||||
}
|
||||
|
||||
// processVolumeStoreParam will pull apart the raw input for -vs and return the parts for the actual store that are needed for validation
|
||||
func processVolumeStoreParam(rawVolumeStore string) (*url.URL, string, string, error) {
|
||||
errVolStoreFormat := fmt.Errorf("volume store input must be in format %s or %s", dsInputFormat, nfsInputFormat)
|
||||
splitMeta := strings.Split(rawVolumeStore, ":")
|
||||
if len(splitMeta) < 2 {
|
||||
return nil, "", "", errVolStoreFormat
|
||||
}
|
||||
|
||||
// divide out the label with the target
|
||||
lastIndex := len(splitMeta)
|
||||
label := splitMeta[lastIndex-1]
|
||||
rawTarget := strings.Join(splitMeta[0:lastIndex-1], ":")
|
||||
if label == "" || rawTarget == "" {
|
||||
return nil, "", "", errVolStoreFormat
|
||||
}
|
||||
|
||||
// This case will check if part of the url is assigned as the label (e.g. ds://No.label.target/some/path)
|
||||
if err := CheckUnsupportedChars(label); err != nil {
|
||||
return nil, "", "", errVolStoreFormat
|
||||
}
|
||||
|
||||
// raw target input should be in the form of a url
|
||||
stripRawTarget := rawTarget
|
||||
|
||||
if strings.HasPrefix(stripRawTarget, DsScheme+"://") {
|
||||
stripRawTarget = strings.Replace(rawTarget, DsScheme+"://", "", -1)
|
||||
}
|
||||
|
||||
urlTarget, err := url.Parse(stripRawTarget)
|
||||
if err != nil {
|
||||
return nil, "", "", fmt.Errorf("parsed url for option --volume-store could not be parsed as a url, valid inputs are datastore/path:label or %s. See -h for usage examples.", nfsInputFormat)
|
||||
}
|
||||
|
||||
return urlTarget, rawTarget, label, nil
|
||||
}
|
||||
59
vendor/github.com/vmware/vic/cmd/vic-machine/common/volume_stores_test.go
generated
vendored
Normal file
59
vendor/github.com/vmware/vic/cmd/vic-machine/common/volume_stores_test.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcessVolumeStoreParam(t *testing.T) {
|
||||
positiveTestCases := []string{
|
||||
"nfs://Shared.Volumes.Org/path/to/store:nfs-volumes",
|
||||
"ds://vsphere.target.here/:root-path",
|
||||
"no.scheme.target:/with/path:ds-store",
|
||||
"looooooooooooooooooooooooooooooong.hoooooooooooooooooooooooooooooooost/short/path:long-check",
|
||||
"nfs://0.0.0.0/ip/check:simple-target",
|
||||
"nfs://prod.shared.storage/vch_prod/volumes:test-label",
|
||||
"ds://0.0.0.0/ip/check?myArg=simple&complex=anotherArg:simple-target:test-label",
|
||||
}
|
||||
|
||||
negativeTestCases := []string{
|
||||
"ds://vsphere.rocks.com/no/label/here",
|
||||
"junk-text-%^()!@#:with-label",
|
||||
"junk-text-%^()!@#-no-label",
|
||||
":no-text",
|
||||
"no-label:",
|
||||
"no-label/with/path",
|
||||
}
|
||||
|
||||
for _, v := range positiveTestCases {
|
||||
target, rawString, label, err := processVolumeStoreParam(v)
|
||||
|
||||
assert.NotNil(t, target, v)
|
||||
assert.NotEqual(t, "", rawString, v)
|
||||
assert.NotEqual(t, "", label, v)
|
||||
assert.Nil(t, err, v)
|
||||
}
|
||||
|
||||
for _, v := range negativeTestCases {
|
||||
target, _, _, err := processVolumeStoreParam(v)
|
||||
|
||||
// here "" is possible for rawString and label so we check for err and nil target.
|
||||
assert.Nil(t, target, v)
|
||||
assert.NotNil(t, err, v)
|
||||
}
|
||||
}
|
||||
470
vendor/github.com/vmware/vic/cmd/vic-machine/configure/configure.go
generated
vendored
Normal file
470
vendor/github.com/vmware/vic/cmd/vic-machine/configure/configure.go
generated
vendored
Normal file
@@ -0,0 +1,470 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 configure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/config"
|
||||
"github.com/vmware/vic/lib/config/executor"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
// Configure has all input parameters for vic-machine configure command
|
||||
type Configure struct {
|
||||
*data.Data
|
||||
|
||||
proxies common.Proxies
|
||||
cNetworks common.CNetworks
|
||||
dns common.DNS
|
||||
volStores common.VolumeStores
|
||||
registries common.Registries
|
||||
|
||||
certificates common.CertFactory
|
||||
|
||||
upgrade bool
|
||||
executor *management.Dispatcher
|
||||
|
||||
Force bool
|
||||
}
|
||||
|
||||
func NewConfigure() *Configure {
|
||||
configure := &Configure{}
|
||||
configure.Data = data.NewData()
|
||||
|
||||
return configure
|
||||
}
|
||||
|
||||
// Flags return all cli flags for configure
|
||||
func (c *Configure) Flags() []cli.Flag {
|
||||
util := []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Force the configure operation",
|
||||
Destination: &c.Force,
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for configure",
|
||||
Destination: &c.Timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "reset-progress",
|
||||
Usage: "Reset the UpdateInProgress flag. Warning: Do not reset this flag if another upgrade/configure process is running",
|
||||
Destination: &c.ResetInProgressFlag,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "rollback",
|
||||
Usage: "Roll back VCH configuration to before the current upgrade/configure",
|
||||
Destination: &c.Rollback,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "upgrade",
|
||||
Usage: "Upgrade VCH to latest version together with configure",
|
||||
Destination: &c.upgrade,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
||||
dns := c.dns.DNSFlags(false)
|
||||
target := c.TargetFlags()
|
||||
ops := c.OpsCredentials.Flags(false)
|
||||
id := c.IDFlags()
|
||||
volume := c.volStores.Flags()
|
||||
compute := c.ComputeFlags()
|
||||
container := c.ContainerFlags()
|
||||
debug := c.DebugFlags(false)
|
||||
cNetwork := c.cNetworks.CNetworkFlags(false)
|
||||
proxies := c.proxies.ProxyFlags(false)
|
||||
memory := c.VCHMemoryLimitFlags(false)
|
||||
cpu := c.VCHCPULimitFlags(false)
|
||||
certificates := c.certificates.CertFlags()
|
||||
registries := c.registries.Flags()
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, ops, id, compute, container, volume, dns, cNetwork, memory, cpu, certificates, registries, proxies, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (c *Configure) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := c.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
if c.DNS, err = c.dns.ProcessDNSServers(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hproxy, sproxy, err := c.proxies.ProcessProxies()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.HTTPProxy = hproxy
|
||||
c.HTTPSProxy = sproxy
|
||||
c.ProxyIsSet = c.proxies.IsSet
|
||||
|
||||
c.ContainerNetworks, err = c.cNetworks.ProcessContainerNetworks(op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Pass empty admin credentials because they are needed only for a create
|
||||
// operation for use as ops credentials if ops credentials are not supplied.
|
||||
if err := c.OpsCredentials.ProcessOpsCredentials(op, false, "", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.VolumeLocations, err = c.volStores.ProcessVolumeStores()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.registries.ProcessRegistries(op); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Data.RegistryCAs = c.registries.RegistryCAs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyChangedConf takes the mostly-empty new config and copies it to the old one. NOTE: o gets installed on the VCH, not n
|
||||
// Currently we cannot automatically override old configuration with any difference in the new configuration, because some options are set during the VCH
|
||||
// Creation process, for example, image store path, volume store path, network slot id, etc. So we'll copy changes based on user input
|
||||
func (c *Configure) copyChangedConf(o *config.VirtualContainerHostConfigSpec, n *config.VirtualContainerHostConfigSpec) {
|
||||
//TODO: copy changed data
|
||||
personaSession := o.ExecutorConfig.Sessions[config.PersonaService]
|
||||
vicAdminSession := o.ExecutorConfig.Sessions[config.VicAdminService]
|
||||
if c.proxies.IsSet {
|
||||
hProxy := ""
|
||||
if c.HTTPProxy != nil {
|
||||
hProxy = c.HTTPProxy.String()
|
||||
}
|
||||
sProxy := ""
|
||||
if c.HTTPSProxy != nil {
|
||||
sProxy = c.HTTPSProxy.String()
|
||||
}
|
||||
updateSessionEnv(personaSession, config.GeneralHTTPProxy, hProxy)
|
||||
updateSessionEnv(personaSession, config.GeneralHTTPSProxy, sProxy)
|
||||
updateSessionEnv(vicAdminSession, config.VICAdminHTTPProxy, hProxy)
|
||||
updateSessionEnv(vicAdminSession, config.VICAdminHTTPSProxy, sProxy)
|
||||
}
|
||||
|
||||
if c.Debug.Debug != nil {
|
||||
o.SetDebug(n.Diagnostics.DebugLevel)
|
||||
}
|
||||
|
||||
if c.cNetworks.IsSet {
|
||||
o.ContainerNetworks = n.ContainerNetworks
|
||||
}
|
||||
|
||||
if c.Data.ContainerNameConvention != "" {
|
||||
o.ContainerNameConvention = c.Data.ContainerNameConvention
|
||||
}
|
||||
|
||||
// Copy the new volume store configuration directly since it has the merged
|
||||
// volume store configuration and its datastore URL fields have been populated
|
||||
// correctly by the storage validator. The old configuration has raw fields.
|
||||
o.VolumeLocations = n.VolumeLocations
|
||||
|
||||
if c.OpsCredentials.IsSet {
|
||||
o.Username = n.Username
|
||||
o.Token = n.Token
|
||||
}
|
||||
|
||||
// Copy the thumbprint directly since it has already been validated.
|
||||
o.TargetThumbprint = n.TargetThumbprint
|
||||
|
||||
if c.dns.IsSet {
|
||||
for k, v := range o.ExecutorConfig.Networks {
|
||||
v.Network.Nameservers = n.ExecutorConfig.Networks[k].Network.Nameservers
|
||||
var gw net.IPNet
|
||||
v.Network.Assigned.Gateway = gw
|
||||
v.Network.Assigned.Nameservers = nil
|
||||
}
|
||||
}
|
||||
|
||||
if n.HostCertificate != nil {
|
||||
o.HostCertificate = n.HostCertificate
|
||||
}
|
||||
|
||||
if n.CertificateAuthorities != nil {
|
||||
o.CertificateAuthorities = n.CertificateAuthorities
|
||||
}
|
||||
|
||||
if n.UserCertificates != nil {
|
||||
o.UserCertificates = n.UserCertificates
|
||||
}
|
||||
|
||||
if n.RegistryCertificateAuthorities != nil {
|
||||
o.RegistryCertificateAuthorities = n.RegistryCertificateAuthorities
|
||||
}
|
||||
}
|
||||
|
||||
func updateSessionEnv(sess *executor.SessionConfig, envName, envValue string) {
|
||||
envs := sess.Cmd.Env
|
||||
var newEnvs []string
|
||||
for _, env := range envs {
|
||||
if strings.HasPrefix(env, envName+"=") {
|
||||
continue
|
||||
}
|
||||
newEnvs = append(newEnvs, env)
|
||||
}
|
||||
if envValue != "" {
|
||||
newEnvs = append(newEnvs, fmt.Sprintf("%s=%s", envName, envValue))
|
||||
}
|
||||
sess.Cmd.Env = newEnvs
|
||||
}
|
||||
|
||||
func (c *Configure) processCertificates(op trace.Operation, client, public, management data.NetworkConfig) error {
|
||||
|
||||
if !c.certificates.NoTLSverify && (c.certificates.Skey == "" || c.certificates.Scert == "") {
|
||||
op.Info("No certificate regeneration requested. No new certificates provided. Certificates left unchanged.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.certificates.CertPath == "" {
|
||||
c.certificates.CertPath = c.DisplayName
|
||||
}
|
||||
|
||||
_, err := os.Lstat(c.certificates.CertPath)
|
||||
if err == nil || os.IsExist(err) {
|
||||
return fmt.Errorf("Specified or default certificate output location \"%s\" already exists. Specify a location that does not yet exist with --tls-cert-path to continue or do not specify --tls-noverify if, instead, you want to load certificates from %s", c.certificates.CertPath, c.certificates.CertPath)
|
||||
}
|
||||
|
||||
var debug int
|
||||
if c.Debug.Debug == nil {
|
||||
debug = 0
|
||||
} else {
|
||||
debug = *c.Debug.Debug
|
||||
}
|
||||
|
||||
c.certificates.Networks = common.Networks{
|
||||
ClientNetworkName: client.Name,
|
||||
ClientNetworkIP: client.IP.String(),
|
||||
PublicNetworkName: public.Name,
|
||||
PublicNetworkIP: public.IP.String(),
|
||||
ManagementNetworkName: management.Name,
|
||||
ManagementNetworkIP: management.IP.String(),
|
||||
}
|
||||
|
||||
if err := c.certificates.ProcessCertificates(op, c.DisplayName, c.Force, debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.KeyPEM = c.certificates.KeyPEM
|
||||
c.CertPEM = c.certificates.CertPEM
|
||||
c.ClientCAs = c.certificates.ClientCAs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Configure) Run(clic *cli.Context) (err error) {
|
||||
parentOp := common.NewOperation(clic, c.Debug.Debug)
|
||||
defer func(op trace.Operation) {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}(parentOp)
|
||||
op, cancel := trace.WithTimeout(&parentOp, c.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Configure timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
// process input parameters, this should reuse same code with create command, to make sure same options are provided
|
||||
if err = c.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
// TODO: add additional parameter processing, reuse same code with create command as well
|
||||
|
||||
if c.upgrade {
|
||||
// verify upgrade required parameters here
|
||||
}
|
||||
|
||||
op.Infof("### Configuring VCH ####")
|
||||
|
||||
validator, err := validate.NewValidator(op, c.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Configuring cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
defer validator.Session.Logout(parentOp) // parentOp is used here to ensure the logout occurs, even in the event of timeout
|
||||
|
||||
_, err = validator.ValidateTarget(op, c.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Configuring cannot continue - target validation failed: %s", err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, c.Force)
|
||||
|
||||
var vch *vm.VirtualMachine
|
||||
if c.Data.ID != "" {
|
||||
vch, err = executor.NewVCHFromID(c.Data.ID)
|
||||
} else {
|
||||
vch, err = executor.NewVCHFromComputePath(c.Data.ComputeResourcePath, c.Data.DisplayName, validator)
|
||||
}
|
||||
if err != nil {
|
||||
op.Errorf("Failed to get Virtual Container Host %s", c.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
op.Info("")
|
||||
op.Infof("VCH ID: %s", vch.Reference().String())
|
||||
|
||||
if c.ResetInProgressFlag {
|
||||
if err = vch.SetVCHUpdateStatus(op, false); err != nil {
|
||||
op.Error("Failed to reset UpdateInProgress flag")
|
||||
op.Error(err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
op.Info("Reset UpdateInProgress flag successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
var vchConfig *config.VirtualContainerHostConfigSpec
|
||||
if c.upgrade {
|
||||
vchConfig, err = executor.FetchAndMigrateVCHConfig(vch)
|
||||
} else {
|
||||
vchConfig, err = executor.GetVCHConfig(vch)
|
||||
}
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
installerVer := version.GetBuild().PluginVersion
|
||||
if vchConfig.ExecutorConfig.Version == nil {
|
||||
op.Error("Cannot configure VCH with version unavailable")
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
if vchConfig.ExecutorConfig.Version.PluginVersion < installerVer {
|
||||
op.Errorf("Cannot configure VCH with version %s, please upgrade first", vchConfig.ExecutorConfig.Version.ShortVersion())
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
// Convert guestinfo *VirtualContainerHost back to *Data, decrypt secret data
|
||||
oldData, err := validate.NewDataFromConfig(op, validator.Session.Finder, vchConfig)
|
||||
if err != nil {
|
||||
op.Error("Configuring cannot continue: configuration conversion failed")
|
||||
op.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = validate.SetDataFromVM(op, validator.Session.Finder, vch, oldData); err != nil {
|
||||
op.Error("Configuring cannot continue: querying configuration from VM failed")
|
||||
op.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// using new configuration override configuration query from guestinfo
|
||||
if err = oldData.CopyNonEmpty(c.Data); err != nil {
|
||||
op.Error("Configuring cannot continue: copying configuration failed")
|
||||
return err
|
||||
}
|
||||
c.Data = oldData
|
||||
|
||||
// in Create we process certificates as part of processParams but we need the old conf
|
||||
// to do this in the context of Configure so we need to call this method here instead
|
||||
if err = c.processCertificates(op, c.Data.ClientNetwork, c.Data.PublicNetwork, c.Data.ManagementNetwork); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// evaluate merged configuration
|
||||
newConfig, err := validator.Validate(op, c.Data)
|
||||
if err != nil {
|
||||
op.Error("Configuring cannot continue: configuration validation failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: copy changed configuration here. https://github.com/vmware/vic/issues/2911
|
||||
c.copyChangedConf(vchConfig, newConfig)
|
||||
|
||||
vConfig := validator.AddDeprecatedFields(op, vchConfig, c.Data)
|
||||
vConfig.Timeout = c.Timeout
|
||||
vConfig.VCHSizeIsSet = c.ResourceLimits.IsSet
|
||||
|
||||
updating, err := vch.VCHUpdateStatus(op)
|
||||
if err != nil {
|
||||
op.Error("Unable to determine if upgrade/configure is in progress")
|
||||
op.Error(err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
if updating {
|
||||
op.Error("Configure failed: another upgrade/configure operation is in progress")
|
||||
op.Error("If no other upgrade/configure process is running, use --reset-progress to reset the VCH upgrade/configure status")
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
if err = vch.SetVCHUpdateStatus(op, true); err != nil {
|
||||
op.Error("Failed to set UpdateInProgress flag to true")
|
||||
op.Error(err)
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = vch.SetVCHUpdateStatus(op, false); err != nil {
|
||||
op.Error("Failed to reset UpdateInProgress")
|
||||
op.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if !c.Data.Rollback {
|
||||
err = executor.Configure(vch, vchConfig, vConfig, true)
|
||||
} else {
|
||||
err = executor.Rollback(vch, vchConfig, vConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// configure failed
|
||||
executor.CollectDiagnosticLogs()
|
||||
return errors.New("configure failed")
|
||||
}
|
||||
|
||||
op.Info("Completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
576
vendor/github.com/vmware/vic/cmd/vic-machine/converter/converter.go
generated
vendored
Normal file
576
vendor/github.com/vmware/vic/cmd/vic-machine/converter/converter.go
generated
vendored
Normal file
@@ -0,0 +1,576 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 converter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/config/executor"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/pkg/ip"
|
||||
viclog "github.com/vmware/vic/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdTag = "cmd"
|
||||
labelTag = "label"
|
||||
parentTagValue = "parent"
|
||||
|
||||
optionSeparator = "-"
|
||||
|
||||
keyAfterValueLabel = "value-key"
|
||||
valueAfterKeyLabel = "key-value"
|
||||
)
|
||||
|
||||
var (
|
||||
kindConverters = make(map[reflect.Kind]converter)
|
||||
typeConverters = make(map[string]converter)
|
||||
labelHandlers = make(map[string]labelConverter)
|
||||
)
|
||||
|
||||
var log = &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
// We're using our own text formatter to skip the \n and \t escaping logrus
|
||||
// was doing on non TTY Out (we redirect to a file) descriptors.
|
||||
Formatter: viclog.NewTextFormatter(),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.InfoLevel,
|
||||
}
|
||||
|
||||
type converter func(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error
|
||||
type labelConverter func(dest map[string][]string, key string) error
|
||||
|
||||
func init() {
|
||||
kindConverters[reflect.Struct] = convertStruct
|
||||
kindConverters[reflect.Slice] = convertSlice
|
||||
kindConverters[reflect.Map] = convertMap
|
||||
kindConverters[reflect.String] = convertString
|
||||
kindConverters[reflect.Ptr] = convertPtr
|
||||
kindConverters[reflect.Int] = convertPrimitive
|
||||
kindConverters[reflect.Int8] = convertPrimitive
|
||||
kindConverters[reflect.Int16] = convertPrimitive
|
||||
kindConverters[reflect.Int32] = convertPrimitive
|
||||
kindConverters[reflect.Int64] = convertPrimitive
|
||||
kindConverters[reflect.Bool] = convertPrimitive
|
||||
kindConverters[reflect.Float32] = convertPrimitive
|
||||
kindConverters[reflect.Float64] = convertPrimitive
|
||||
|
||||
typeConverters["url.URL"] = convertURL
|
||||
typeConverters["net.IPNet"] = convertIPNet
|
||||
typeConverters["net.IP"] = convertIP
|
||||
typeConverters["ip.Range"] = convertIPRange
|
||||
typeConverters["data.NetworkConfig"] = convertNetwork
|
||||
typeConverters["common.ContainerNetworks"] = convertContainerNetworks
|
||||
typeConverters["executor.TrustLevel"] = convertTrustLevel
|
||||
typeConverters["types.SharesInfo"] = convertShares
|
||||
|
||||
labelHandlers[keyAfterValueLabel] = keyAfterValueLabelHandler
|
||||
labelHandlers[valueAfterKeyLabel] = valueAfterKeyLabelHandler
|
||||
}
|
||||
|
||||
func EnableLog() {
|
||||
log.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
func DisableLog() {
|
||||
log.Level = logrus.InfoLevel
|
||||
}
|
||||
|
||||
// DataToOption convert data.Data structure to vic-machine create command options based on tags defined in data.Data structure
|
||||
// Note: need to make sure the tags are consistent with command line option name
|
||||
func DataToOption(data *data.Data) (map[string][]string, error) {
|
||||
result := make(map[string][]string)
|
||||
|
||||
if data == nil {
|
||||
return result, nil
|
||||
}
|
||||
err := convert(reflect.ValueOf(data), "", "", result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func convert(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
t := src.Type().String()
|
||||
if converter, ok := typeConverters[t]; ok {
|
||||
return converter(src, prefix, tags, dest)
|
||||
|
||||
}
|
||||
if converter, ok := kindConverters[src.Kind()]; ok {
|
||||
return converter(src, prefix, tags, dest)
|
||||
}
|
||||
log.Debugf("Skipping unsupported field, interface: %#v, kind %s", src, src.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertPtr(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
if src.IsNil() {
|
||||
// no need to attempt anything
|
||||
return nil
|
||||
}
|
||||
return convert(src.Elem(), prefix, tags, dest)
|
||||
}
|
||||
|
||||
func convertStruct(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertStruct: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
// iterate through every field in the struct
|
||||
for i := 0; i < src.NumField(); i++ {
|
||||
field := src.Field(i)
|
||||
// get field key, and keep going even if the attribute key is empty, to make sure children attribute is not missing
|
||||
tags := src.Type().Field(i).Tag
|
||||
key := calculateKey(tags, prefix)
|
||||
if err := convert(field, key, tags, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Map {
|
||||
// label handler is invoked in map converter
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertSlice(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertSlice: prefix: %s, src: %s", prefix, src)
|
||||
|
||||
length := src.Len()
|
||||
if length == 0 {
|
||||
log.Debug("Skipping empty slice")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
if err := convert(src.Index(i), prefix, tags, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertMap(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertMap: prefix: %s, src: %s", prefix, src)
|
||||
|
||||
// iterate over keys and recurse
|
||||
mkeys := src.MapKeys()
|
||||
length := len(mkeys)
|
||||
if length == 0 {
|
||||
log.Debug("Skipping empty map")
|
||||
return nil
|
||||
}
|
||||
|
||||
handler, hasHandler := labelHandlers[tags.Get(labelTag)]
|
||||
// use tempMap to avoid duplicate processing
|
||||
for _, pkey := range src.MapKeys() {
|
||||
tempMap := make(map[string][]string)
|
||||
if pkey.Kind() != reflect.String {
|
||||
log.Errorf("Unsupported map key type interface: %s, kind %s", src, src.Kind())
|
||||
continue
|
||||
}
|
||||
if !hasHandler {
|
||||
if err := convert(src.MapIndex(pkey), prefix, tags, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := convert(src.MapIndex(pkey), prefix, tags, tempMap); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handler(tempMap, pkey.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range tempMap {
|
||||
addValues(dest, k, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// keyAfterValueLabelHandler will add the map key as label after the value,
|
||||
// e.g. change from datastore/path to datastore/path:default
|
||||
func keyAfterValueLabelHandler(dest map[string][]string, pkey string) error {
|
||||
log.Debugf("keyAfterValueLabelHandler: map key: %s, map: %#v", pkey, dest)
|
||||
|
||||
for _, values := range dest {
|
||||
for i := range values {
|
||||
values[i] = fmt.Sprintf("%s:%s", values[i], pkey)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// valueAfterKeyLabelHandler will add the map key as label before the value,
|
||||
// e.g. change from 10.10.10.0/24 to management:10.10.10.0/24
|
||||
func valueAfterKeyLabelHandler(dest map[string][]string, pkey string) error {
|
||||
log.Debugf("valueAfterKeyLabelHandler: map key: %s, map: %#v", pkey, dest)
|
||||
|
||||
for _, values := range dest {
|
||||
for i := range values {
|
||||
values[i] = fmt.Sprintf("%s:%s", pkey, values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateKey generate key as prefix-tag. if any one is empty, return the other
|
||||
func calculateKey(tags reflect.StructTag, prefix string) string {
|
||||
tag := tags.Get(cmdTag)
|
||||
if tag == "" {
|
||||
return prefix
|
||||
}
|
||||
if tag == parentTagValue && prefix == "" {
|
||||
return ""
|
||||
}
|
||||
if tag == parentTagValue && prefix != "" {
|
||||
// for this tag, use parent name only
|
||||
return prefix
|
||||
}
|
||||
if prefix == "" {
|
||||
return tag
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s", prefix, optionSeparator, tag)
|
||||
}
|
||||
|
||||
func convertURL(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertURL: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
u, ok := src.Interface().(url.URL)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not URL"))
|
||||
}
|
||||
v := u.String()
|
||||
if u.Scheme == "" {
|
||||
if u.Path == "" {
|
||||
v = u.Host
|
||||
} else if u.Host == "" {
|
||||
v = u.Path
|
||||
} else {
|
||||
v = fmt.Sprintf("%s/%s", u.Host, u.Path)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertIPNet(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertIPNet: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipNet, ok := src.Interface().(net.IPNet)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not IPNet"))
|
||||
}
|
||||
if ip.IsUnspecifiedSubnet(&ipNet) {
|
||||
return nil
|
||||
}
|
||||
v := ipNet.String()
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertIP(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertIP: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipAddr, ok := src.Interface().(net.IP)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not IP"))
|
||||
}
|
||||
if ip.IsUnspecifiedIP(ipAddr) {
|
||||
return nil
|
||||
}
|
||||
v := ipAddr.String()
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertTrustLevel(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertTrustLevel: prefix: %s, src: %s", prefix, src.String())
|
||||
trustLevel, ok := src.Interface().(executor.TrustLevel)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not TrustLevel"))
|
||||
}
|
||||
if trustLevel == executor.Unspecified {
|
||||
return nil
|
||||
}
|
||||
v := trustLevel.String()
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertIPRange(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertIPRange: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ipRange, ok := src.Interface().(ip.Range)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not ip range"))
|
||||
}
|
||||
v := ipRange.String()
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertString(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertString: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := src.String()
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertPrimitive(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertPrimitive: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := ""
|
||||
switch src.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if src.Int() == 0 {
|
||||
return nil
|
||||
}
|
||||
v = strconv.FormatInt(src.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if src.Uint() == 0 {
|
||||
return nil
|
||||
}
|
||||
v = strconv.FormatUint(src.Uint(), 10)
|
||||
case reflect.Bool:
|
||||
if !src.Bool() {
|
||||
return nil
|
||||
}
|
||||
v = strconv.FormatBool(src.Bool())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if src.Float() == 0 {
|
||||
return nil
|
||||
}
|
||||
v = strconv.FormatFloat(src.Float(), 'E', -1, 64)
|
||||
default:
|
||||
panic(fmt.Sprintf("%s is not supported type", src.Kind()))
|
||||
}
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertNetwork will merge destination and gateway to one option with format: 192.168.3.0/16,192.168.128.0/16:192.168.2.1
|
||||
// after that, convertStruct is called for left conversion
|
||||
func convertNetwork(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertNetwork: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
network, ok := src.Interface().(data.NetworkConfig)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not NetworkConfig"))
|
||||
}
|
||||
if !network.IsSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(network.Destinations) > 0 || !ip.Empty(network.Gateway) {
|
||||
destination := ""
|
||||
if len(network.Destinations) > 0 {
|
||||
for _, d := range network.Destinations {
|
||||
destination = fmt.Sprintf("%s,%s", destination, d.String())
|
||||
}
|
||||
destination = strings.TrimLeft(destination, ",")
|
||||
}
|
||||
gateway := network.Gateway.IP.String()
|
||||
tag := "cmd:\"gateway\""
|
||||
key := calculateKey(reflect.StructTag(tag), prefix)
|
||||
if destination != "" {
|
||||
addValue(dest, key, fmt.Sprintf("%s:%s", destination, gateway))
|
||||
} else {
|
||||
addValue(dest, key, gateway)
|
||||
}
|
||||
}
|
||||
return convertStruct(reflect.ValueOf(network), prefix, tags, dest)
|
||||
}
|
||||
|
||||
func convertShares(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertShares: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
shares, ok := src.Interface().(types.SharesInfo)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not SharesInfo"))
|
||||
}
|
||||
|
||||
v := ""
|
||||
switch shares.Level {
|
||||
case types.SharesLevelCustom:
|
||||
v = fmt.Sprintf("%v", shares.Shares)
|
||||
case types.SharesLevelNormal:
|
||||
return nil
|
||||
default:
|
||||
v = string(shares.Level)
|
||||
}
|
||||
|
||||
log.Debugf("%s=%s", prefix, v)
|
||||
|
||||
addValue(dest, prefix, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertContainerNetworks will switch the map keys in MappedNetworks using value, and replace all keys with the same value in other structure,
|
||||
// cause option is using vsphere network name as key label, but guestinfo is using alias as key for easy to use in portlayer
|
||||
// after that, convertStruct is called for left conversion
|
||||
func convertContainerNetworks(src reflect.Value, prefix string, tags reflect.StructTag, dest map[string][]string) error {
|
||||
log.Debugf("convertContainerNetworks: prefix: %s, src: %s", prefix, src.String())
|
||||
|
||||
if prefix == "" {
|
||||
return nil
|
||||
}
|
||||
if tags.Get(cmdTag) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
networks, ok := src.Interface().(common.ContainerNetworks)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(src.Type().String() + " is not ContainerNetworks"))
|
||||
}
|
||||
if !networks.IsSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
newMappedNetworks := make(map[string]string, len(networks.MappedNetworks))
|
||||
for k, v := range networks.MappedNetworks {
|
||||
if k == v {
|
||||
newMappedNetworks[v] = k
|
||||
continue
|
||||
}
|
||||
if dns, ok := networks.MappedNetworksDNS[k]; ok {
|
||||
networks.MappedNetworksDNS[v] = dns
|
||||
delete(networks.MappedNetworksDNS, k)
|
||||
}
|
||||
if gateways, ok := networks.MappedNetworksGateways[k]; ok {
|
||||
networks.MappedNetworksGateways[v] = gateways
|
||||
delete(networks.MappedNetworksGateways, k)
|
||||
}
|
||||
if ipRange, ok := networks.MappedNetworksIPRanges[k]; ok {
|
||||
networks.MappedNetworksIPRanges[v] = ipRange
|
||||
delete(networks.MappedNetworksIPRanges, k)
|
||||
}
|
||||
if firewall, ok := networks.MappedNetworksFirewalls[k]; ok {
|
||||
networks.MappedNetworksFirewalls[v] = firewall
|
||||
delete(networks.MappedNetworksFirewalls, k)
|
||||
}
|
||||
newMappedNetworks[v] = k
|
||||
}
|
||||
networks.MappedNetworks = newMappedNetworks
|
||||
return convertStruct(reflect.ValueOf(networks), prefix, tags, dest)
|
||||
}
|
||||
|
||||
// addValue will append value without duplicates
|
||||
func addValue(dest map[string][]string, key, value string) {
|
||||
slice, _ := dest[key]
|
||||
found := false
|
||||
for _, o := range slice {
|
||||
if o == value {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
slice = append(slice, value)
|
||||
}
|
||||
dest[key] = slice
|
||||
}
|
||||
|
||||
// addValues append new value to existing slice if missing
|
||||
// as this method is called every time the value is appended, the existing slice will be no duplicates
|
||||
func addValues(dest map[string][]string, key string, values []string) {
|
||||
for _, v := range values {
|
||||
addValue(dest, key, v)
|
||||
}
|
||||
}
|
||||
650
vendor/github.com/vmware/vic/cmd/vic-machine/converter/converter_test.go
generated
vendored
Normal file
650
vendor/github.com/vmware/vic/cmd/vic-machine/converter/converter_test.go
generated
vendored
Normal file
@@ -0,0 +1,650 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 converter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/vic/lib/config"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/vsphere/extraconfig"
|
||||
)
|
||||
|
||||
type testFinder struct {
|
||||
}
|
||||
|
||||
func (f *testFinder) ObjectReference(ctx context.Context, ref types.ManagedObjectReference) (object.Reference, error) {
|
||||
return testObject{
|
||||
Common: object.NewCommon(nil, ref),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type testObject struct {
|
||||
object.Common
|
||||
}
|
||||
|
||||
func (c testObject) Name() string {
|
||||
switch c.Common.Reference().String() {
|
||||
case "DistributedVirtualPortgroup:dvportgroup-357":
|
||||
return "management"
|
||||
case "DistributedVirtualPortgroup:dvportgroup-358":
|
||||
return "vm-network"
|
||||
case "DistributedVirtualPortgroup:dvportgroup-55":
|
||||
return "bridge"
|
||||
case "DistributedVirtualPortgroup:dvportgroup-56":
|
||||
return "external"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
// EnableLog()
|
||||
// trace.Logger.Level = log.DebugLevel
|
||||
|
||||
testInit(t)
|
||||
testConvertImageStore(t)
|
||||
testConvertVolumeStore(t)
|
||||
testConvertVCHName(t)
|
||||
testConvertTarget(t)
|
||||
testConvertOps(t)
|
||||
testConvertBaseImageSize(t)
|
||||
testConvertPublicNetwork(t)
|
||||
testConvertClientNetwork(t)
|
||||
testConvertMgmtNetwork(t)
|
||||
testGuestinfo(t)
|
||||
testConvertSharesInfo(t)
|
||||
}
|
||||
|
||||
func testInit(t *testing.T) {
|
||||
var ok bool
|
||||
_, ok = kindConverters[reflect.Struct]
|
||||
assert.True(t, ok, fmt.Sprintf("Struct converter is not found"))
|
||||
_, ok = kindConverters[reflect.Slice]
|
||||
assert.True(t, ok, fmt.Sprintf("Slice converter is not found"))
|
||||
_, ok = kindConverters[reflect.Map]
|
||||
assert.True(t, ok, fmt.Sprintf("Map converter is not found"))
|
||||
_, ok = kindConverters[reflect.String]
|
||||
assert.True(t, ok, fmt.Sprintf("String converter is not found"))
|
||||
_, ok = kindConverters[reflect.Ptr]
|
||||
assert.True(t, ok, fmt.Sprintf("Pointer converter is not found"))
|
||||
_, ok = kindConverters[reflect.Int]
|
||||
assert.True(t, ok, fmt.Sprintf("Int converter is not found"))
|
||||
_, ok = kindConverters[reflect.Int8]
|
||||
assert.True(t, ok, fmt.Sprintf("Int8 converter is not found"))
|
||||
_, ok = kindConverters[reflect.Int16]
|
||||
assert.True(t, ok, fmt.Sprintf("Int16 converter is not found"))
|
||||
_, ok = kindConverters[reflect.Int32]
|
||||
assert.True(t, ok, fmt.Sprintf("Int32 converter is not found"))
|
||||
_, ok = kindConverters[reflect.Int64]
|
||||
assert.True(t, ok, fmt.Sprintf("Int64 converter is not found"))
|
||||
_, ok = kindConverters[reflect.Bool]
|
||||
assert.True(t, ok, fmt.Sprintf("Bool converter is not found"))
|
||||
_, ok = kindConverters[reflect.Float32]
|
||||
assert.True(t, ok, fmt.Sprintf("Float32 converter is not found"))
|
||||
_, ok = kindConverters[reflect.Float64]
|
||||
assert.True(t, ok, fmt.Sprintf("Float64 converter is not found"))
|
||||
|
||||
_, ok = typeConverters["url.URL"]
|
||||
assert.True(t, ok, fmt.Sprintf("url.URL converter is not found"))
|
||||
_, ok = typeConverters["net.IPNet"]
|
||||
assert.True(t, ok, fmt.Sprintf("net.IPNet converter is not found"))
|
||||
_, ok = typeConverters["net.IP"]
|
||||
assert.True(t, ok, fmt.Sprintf("net.IP converter is not found"))
|
||||
_, ok = typeConverters["ip.Range"]
|
||||
assert.True(t, ok, fmt.Sprintf("ip.Range converter is not found"))
|
||||
_, ok = typeConverters["data.NetworkConfig"]
|
||||
assert.True(t, ok, fmt.Sprintf("data.NetworkConfig converter is not found"))
|
||||
_, ok = typeConverters["common.ContainerNetworks"]
|
||||
assert.True(t, ok, fmt.Sprintf("common.ContainerNetworks converter is not found"))
|
||||
|
||||
_, ok = labelHandlers[keyAfterValueLabel]
|
||||
assert.True(t, ok, fmt.Sprintf("value-key handler is not found"))
|
||||
_, ok = labelHandlers[valueAfterKeyLabel]
|
||||
assert.True(t, ok, fmt.Sprintf("key-value handler is not found"))
|
||||
}
|
||||
|
||||
func testConvertImageStore(t *testing.T) {
|
||||
data := data.NewData()
|
||||
data.ImageDatastorePath = "ds://vsan/path"
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 1, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "ds://vsan/path", options["image-store"][0], "not expected image-store option")
|
||||
}
|
||||
|
||||
func testConvertVolumeStore(t *testing.T) {
|
||||
tests := []struct {
|
||||
in map[string]*url.URL
|
||||
out []string
|
||||
}{
|
||||
{
|
||||
in: map[string]*url.URL{
|
||||
"default": {
|
||||
Scheme: "ds",
|
||||
Host: "vsan",
|
||||
Path: "path/volume",
|
||||
},
|
||||
|
||||
"noSchema": {
|
||||
Host: "vsan",
|
||||
Path: "path1",
|
||||
},
|
||||
},
|
||||
out: []string{
|
||||
"ds://vsan/path/volume:default",
|
||||
"vsan/path1:noSchema",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
data := data.NewData()
|
||||
data.VolumeLocations = test.in
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(options), "should not have other option generated")
|
||||
vols := options["volume-store"]
|
||||
assert.Equal(t, len(test.out), len(vols), "not expected length")
|
||||
for _, store := range test.out {
|
||||
found := false
|
||||
for _, actual := range vols {
|
||||
if actual == store {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, fmt.Sprintf("%s is not created", store))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testConvertVCHName(t *testing.T) {
|
||||
data := data.NewData()
|
||||
data.DisplayName = "vch1"
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 1, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "vch1", options["name"][0], "not expected name option")
|
||||
}
|
||||
|
||||
func testConvertTarget(t *testing.T) {
|
||||
data := data.NewData()
|
||||
data.Target.URL, _ = url.Parse("1.1.1.1")
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 1, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "1.1.1.1", options["target"][0], "not expected target option")
|
||||
}
|
||||
|
||||
func testConvertOps(t *testing.T) {
|
||||
data := data.NewData()
|
||||
adminUser := "admin"
|
||||
data.OpsCredentials.OpsUser = &adminUser
|
||||
pass := "password"
|
||||
data.OpsCredentials.OpsPassword = &pass
|
||||
data.Thumbprint = "uuidstring"
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(options), "should not have other option generated")
|
||||
assert.Equal(t, *data.OpsCredentials.OpsUser, options["ops-user"][0], "not expected ops-user option")
|
||||
assert.Equal(t, data.Thumbprint, options["thumbprint"][0], "not expected thumbprint option")
|
||||
}
|
||||
|
||||
func testConvertBaseImageSize(t *testing.T) {
|
||||
data := data.NewData()
|
||||
data.ScratchSize = "8GB"
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 1, len(options), "should not have other option generated")
|
||||
assert.Equal(t, data.ScratchSize, options["base-image-size"][0], "not expected base-image-size option")
|
||||
}
|
||||
|
||||
func testConvertBridgeNetwork(t *testing.T) {
|
||||
_, ipnet, _ := net.ParseCIDR("172.16.0.0/12")
|
||||
|
||||
data := data.NewData()
|
||||
data.BridgeIPRange = ipnet
|
||||
data.BridgeNetworkName = "portgroup1"
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 2, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "172.16.0.0/12", options["bridge-network-range"][0], "not expected bridge-network-range option")
|
||||
assert.Equal(t, data.BridgeNetworkName, options["bridge-network"][0], "not expected bridge-network option")
|
||||
}
|
||||
|
||||
func testConvertPublicNetwork(t *testing.T) {
|
||||
gwIP := net.ParseIP("172.16.0.1")
|
||||
_, destination, _ := net.ParseCIDR("172.16.0.0/12")
|
||||
ip, ipNet, _ := net.ParseCIDR("172.16.0.2/12")
|
||||
|
||||
data := data.NewData()
|
||||
data.PublicNetwork.Name = "public"
|
||||
data.PublicNetwork.Gateway.IP = gwIP
|
||||
data.PublicNetwork.Gateway.Mask = ipNet.Mask
|
||||
data.PublicNetwork.IP.IP = ip
|
||||
data.PublicNetwork.IP.Mask = ipNet.Mask
|
||||
data.PublicNetwork.Destinations = append(data.PublicNetwork.Destinations, *destination)
|
||||
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 3, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "172.16.0.0/12:172.16.0.1", options["public-network-gateway"][0], "not expected public-network-gateway option")
|
||||
assert.Equal(t, "172.16.0.2/12", options["public-network-ip"][0], "not expected public-network-ip option")
|
||||
}
|
||||
|
||||
func testConvertClientNetwork(t *testing.T) {
|
||||
gwIP := net.ParseIP("172.16.0.1")
|
||||
_, destination, _ := net.ParseCIDR("172.16.0.0/12")
|
||||
ip, ipNet, _ := net.ParseCIDR("172.16.0.2/12")
|
||||
|
||||
data := data.NewData()
|
||||
data.ClientNetwork.Name = "public"
|
||||
data.ClientNetwork.Gateway.IP = gwIP
|
||||
data.ClientNetwork.Gateway.Mask = ipNet.Mask
|
||||
data.ClientNetwork.IP.IP = ip
|
||||
data.ClientNetwork.IP.Mask = ipNet.Mask
|
||||
data.ClientNetwork.Destinations = append(data.ClientNetwork.Destinations, *destination)
|
||||
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 3, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "172.16.0.0/12:172.16.0.1", options["client-network-gateway"][0], "not expected client-network-gateway option")
|
||||
assert.Equal(t, "172.16.0.2/12", options["client-network-ip"][0], "not expected client-network-ip option")
|
||||
}
|
||||
|
||||
func testConvertMgmtNetwork(t *testing.T) {
|
||||
gwIP := net.ParseIP("172.16.0.1")
|
||||
_, destination, _ := net.ParseCIDR("172.16.0.0/12")
|
||||
ip, ipNet, _ := net.ParseCIDR("172.16.0.2/12")
|
||||
|
||||
data := data.NewData()
|
||||
data.ManagementNetwork.Name = "public"
|
||||
data.ManagementNetwork.Gateway.IP = gwIP
|
||||
data.ManagementNetwork.Gateway.Mask = ipNet.Mask
|
||||
data.ManagementNetwork.IP.IP = ip
|
||||
data.ManagementNetwork.IP.Mask = ipNet.Mask
|
||||
data.ManagementNetwork.Destinations = append(data.ManagementNetwork.Destinations, *destination)
|
||||
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 3, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "172.16.0.0/12:172.16.0.1", options["management-network-gateway"][0], "not expected management-network-gateway option")
|
||||
assert.Equal(t, "172.16.0.2/12", options["management-network-ip"][0], "not expected management-network-ip option")
|
||||
}
|
||||
|
||||
func testConvertContainerNetworks(t *testing.T) {
|
||||
gwIP := net.ParseIP("172.16.0.1")
|
||||
_, destination, _ := net.ParseCIDR("172.16.0.0/12")
|
||||
ip, ipNet, _ := net.ParseCIDR("172.16.0.2/12")
|
||||
|
||||
data := data.NewData()
|
||||
data.ManagementNetwork.Name = "public"
|
||||
data.ManagementNetwork.Gateway.IP = gwIP
|
||||
data.ManagementNetwork.Gateway.Mask = ipNet.Mask
|
||||
data.ManagementNetwork.IP.IP = ip
|
||||
data.ManagementNetwork.IP.Mask = ipNet.Mask
|
||||
data.ManagementNetwork.Destinations = append(data.ManagementNetwork.Destinations, *destination)
|
||||
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 3, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "172.16.0.0/12:172.16.0.1", options["management-network-gateway"][0], "not expected management-network-gateway option")
|
||||
assert.Equal(t, "172.16.0.2/12", options["management-network-ip"][0], "not expected management-network-ip option")
|
||||
}
|
||||
|
||||
func testConvertSharesInfo(t *testing.T) {
|
||||
data := data.NewData()
|
||||
cLimit, cReserve := 29300, 1024
|
||||
data.NumCPUs = 2
|
||||
data.VCHCPULimitsMHz = &cLimit
|
||||
data.VCHCPUReservationsMHz = &cReserve
|
||||
data.VCHCPUShares = &types.SharesInfo{
|
||||
Shares: 6000,
|
||||
Level: types.SharesLevelCustom,
|
||||
}
|
||||
mLimit, mReserve := 13144, 1024
|
||||
data.MemoryMB = 4096
|
||||
data.VCHMemoryLimitsMB = &mLimit
|
||||
data.VCHMemoryReservationsMB = &mReserve
|
||||
data.VCHMemoryShares = &types.SharesInfo{
|
||||
Shares: 163840,
|
||||
Level: types.SharesLevelNormal,
|
||||
}
|
||||
|
||||
options, err := DataToOption(data)
|
||||
assert.Empty(t, err)
|
||||
assert.Equal(t, 7, len(options), "should not have other option generated")
|
||||
assert.Equal(t, "2", options["endpoint-cpu"][0], "not expected endpoint-cpu option")
|
||||
assert.Equal(t, "4096", options["endpoint-memory"][0], "not expected endpoint-memory option")
|
||||
assert.Equal(t, "13144", options["memory"][0], "not expected memory option")
|
||||
assert.Equal(t, "1024", options["memory-reservation"][0], "not expected memory-reservation option")
|
||||
assert.Equal(t, "29300", options["cpu"][0], "not expected cpu option")
|
||||
assert.Equal(t, "1024", options["cpu-reservation"][0], "not expected cpu-reservation option")
|
||||
assert.Equal(t, "6000", options["cpu-shares"][0], "not expected cpu-shares option")
|
||||
}
|
||||
|
||||
func testGuestinfo(t *testing.T) {
|
||||
kv := map[string]string{
|
||||
"guestinfo.vice..init.sessions|docker-personality.detail.createtime": "0",
|
||||
"guestinfo.vice./init/sessions|docker-personality/Group": "",
|
||||
"init/networks|client/Common/notes": "",
|
||||
"guestinfo.vice./network/container_networks|vnet/gateway/IP": "AAAAAAAAAAAAAP//CgoKAQ==",
|
||||
"guestinfo.vice./init/sessions|port-layer/tty": "false",
|
||||
"guestinfo.vice..init.networks|bridge.network.type": "bridge",
|
||||
"vmotion.checkpointSVGAPrimarySize": "4194304",
|
||||
"guestinfo.vice./storage/image_stores|0/Host": "vsanDatastore",
|
||||
"guestinfo.vice./init/sessions|vicadmin/stopSignal": "",
|
||||
"guestinfo.vice./network/container_networks|vnet/Common/id": "DistributedVirtualPortgroup:dvportgroup-357",
|
||||
"guestinfo.vice..init.networks|client.network.assigned.dns|1": "CqYBAQ==",
|
||||
"guestinfo.vice..init.sessions|vicadmin.status": "0",
|
||||
"guestinfo.vice./init/networks|bridge/Common/id": "224",
|
||||
"guestinfo.vice./init/networks|bridge/network/Common/id": "DistributedVirtualPortgroup:dvportgroup-55",
|
||||
"guestinfo.vice./init/diagnostics/debug": "3",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/ForceQuery": "false",
|
||||
"guestinfo.vice./init/sessions|docker-personality/active": "true",
|
||||
"guestinfo.vice./container/ComputeResources": "0",
|
||||
"guestinfo.vice..init.networks|client.network.assigned.gateway.Mask": "///gAA==",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/Opaque": "",
|
||||
"guestinfo.vice..init.networks|management.assigned.IP": "CsCoOQ==",
|
||||
"guestinfo.vice./init/sessions|port-layer/common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/stopSignal": "",
|
||||
"init/networks|management/Common/notes": "",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/RawPath": "",
|
||||
"network/container_networks|bridge/Common/notes": "",
|
||||
"guestinfo.vice..network.container_networks|vmnet.type": "external",
|
||||
"guestinfo.vice..init.sessions|docker-personality.detail.stoptime": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/cmd/Dir": "/home/vicadmin",
|
||||
"guestinfo.vice./connect/keepalive": "0",
|
||||
"guestinfo.vice..init.networks|management.network.type": "",
|
||||
"guestinfo.vice./init/sessions|port-layer/common/name": "port-layer",
|
||||
"guestinfo.vice..init.networks|public.network.assigned.dns|1": "CqYBAQ==",
|
||||
"guestinfo.vice./init/sessions|docker-personality/common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/openstdin": "false",
|
||||
"guestinfo.vice./connect/target_thumbprint": "9F:F0:DF:BA:7F:E2:89:F0:98:E4:A6:D1:58:24:68:74:8A:9B:25:6F",
|
||||
"guestinfo.vice..init.networks|public.network.type": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Path": "/sbin/docker-engine-server",
|
||||
"guestinfo.vice..init.createtime": "0",
|
||||
"guestinfo.vice./init/sessions|port-layer/active": "true",
|
||||
"guestinfo.vice./network/container_networks|vnet/pools|0/last": "CgoK/w==",
|
||||
"init/sessions|docker-personality/common/notes": "",
|
||||
"guestinfo.vice..init.diagnostics.resurrections": "0",
|
||||
"guestinfo.vice..init.networks|public.network.assigned.gateway.Mask": "///gAA==",
|
||||
"guestinfo.vice./init/sessions|port-layer/cmd/Dir": "",
|
||||
"guestinfo.vice./network/container_networks|vnet/pools": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/tty": "false",
|
||||
"guestinfo.vice./storage/image_stores|0/RawPath": "",
|
||||
"guestinfo.vice./init/sessions|port-layer/attach": "false",
|
||||
"guestinfo.vice./container/ComputeResources|0/Type": "VirtualApp",
|
||||
"guestinfo.vice./storage/image_stores|0/Scheme": "ds",
|
||||
"guestinfo.vice..init.networks|public.assigned.Mask": "///gAA==",
|
||||
"guestinfo.vice./init/networks|bridge/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./storage/image_stores|0/Path": "91512359-242e-5a30-ba4a-0200360a2e79",
|
||||
"numa.autosize.vcpu.maxPerVirtualNode": "1",
|
||||
"guestinfo.vice./init/version/BuildDate": "2017/05/06@14:16:54",
|
||||
"guestinfo.vice./init/sessions|docker-personality/User": "",
|
||||
"guestinfo.vice..init.networks|client.assigned.Mask": "///gAA==",
|
||||
"guestinfo.vice./init/networks|management/network/Common/id": "DistributedVirtualPortgroup:dvportgroup-56",
|
||||
"guestinfo.vice./connect/target": "https://10.192.171.116",
|
||||
"guestinfo.vice./storage/image_stores|0/RawQuery": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/runblock": "false",
|
||||
"guestinfo.vice..init.networks|management.network.assigned.gateway.IP": "CsC//Q==",
|
||||
"init/networks|management/network/Common/notes": "",
|
||||
"guestinfo.vice./init/version/GitCommit": "65a1889",
|
||||
"guestinfo.vice./init/networks|public/Common/name": "public",
|
||||
"guestinfo.vice./init/version/Version": "v1.1.0-rc3",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/Scheme": "ds",
|
||||
"answer.msg.serial.file.open": "Append",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/Path": "[vsanDatastore] volumes/default",
|
||||
"guestinfo.vice..init.networks|management.network.assigned.dns": "1",
|
||||
"guestinfo.vice./init/networks|public/network/Common/id": "DistributedVirtualPortgroup:dvportgroup-56",
|
||||
"guestinfo.vice./storage/image_stores|0/ForceQuery": "false",
|
||||
"guestinfo.vice./init/imageid": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Env": "3",
|
||||
"guestinfo.vice./init/sessions|docker-personality/common/id": "docker-personality",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/Fragment": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/cmd/Env": "3",
|
||||
"guestinfo.vice./init/sessions|vicadmin/common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/User": "vicadmin",
|
||||
"guestinfo.vice..init.networks|public.network.assigned.dns": "1",
|
||||
"network/container_networks|vnet/Common/notes": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/common/id": "vicadmin",
|
||||
"guestinfo.vice..init.sessions|docker-personality.diagnostics.resurrections": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/cmd/Args~": "/sbin/vicadmin|--dc=vcqaDC|--pool=|--cluster=/vcqaDC/host/cls",
|
||||
"guestinfo.vice./init/networks|public/static": "false",
|
||||
"guestinfo.vice./init/networks|client/network/default": "false",
|
||||
"init/networks|public/network/Common/notes": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/restart": "true",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/Host": "vsanDatastore",
|
||||
"guestinfo.vice./init/networks|bridge/network/Common/name": "bridge",
|
||||
"guestinfo.vice..init.sessions|vicadmin.detail.stoptime": "0",
|
||||
"guestinfo.vice./container/ComputeResources|0/Value": "resgroup-v364",
|
||||
"guestinfo.vice./network/container_networks|vmnet/Common/name": "vmnet",
|
||||
"guestinfo.vice./network/container_networks": "bridge|vmnet|vnet",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Args": "2",
|
||||
"guestinfo.vice./init/networks|bridge/ip/IP": "AAAAAAAAAAAAAP//AAAAAA==",
|
||||
"guestinfo.vice./init/networks|bridge/network/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/RawQuery": "",
|
||||
"guestinfo.vice..init.networks|public.assigned.IP": "CsCoOQ==",
|
||||
"guestinfo.vice./init/networks|bridge/Common/name": "bridge",
|
||||
"guestinfo.vice./init/sessions|port-layer/openstdin": "false",
|
||||
"guestinfo.vice./create_bridge_network": "false",
|
||||
"guestinfo.vice./init/networks|management/Common/id": "192",
|
||||
"guestinfo.vice..init.networks|management.network.assigned.dns|0": "CqLMAQ==",
|
||||
"guestinfo.vice..init.networks|public.network.assigned.dns|0": "CqLMAQ==",
|
||||
"guestinfo.vice..init.sessions|docker-personality.started": "true",
|
||||
"init/networks|public/Common/notes": "",
|
||||
"guestinfo.vice..init.networks|management.network.assigned.dns|1": "CqYBAQ==",
|
||||
"guestinfo.vice./init/sessions|vicadmin/active": "true",
|
||||
"guestinfo.vice./init/networks": "bridge|client|management|public",
|
||||
"guestinfo.vice./init/networks|client/Common/id": "192",
|
||||
"guestinfo.vice./network/container_networks|vnet/Common/ExecutionEnvironment": "",
|
||||
"init/networks|client/network/Common/notes": "",
|
||||
"guestinfo.vice..init.sessions|port-layer.detail.stoptime": "0",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Args~": "/sbin/docker-engine-server|-port=2376|-port-layer-port=2377",
|
||||
"guestinfo.vice..init.sessions|vicadmin.runblock": "false",
|
||||
"guestinfo.vice./init/sessions|port-layer/Group": "",
|
||||
"guestinfo.vice./init/version/PluginVersion": "6",
|
||||
"guestinfo.vice./storage/scratch_size": "8000000",
|
||||
"guestinfo.vice./init/networks|client/Common/ExecutionEnvironment": "",
|
||||
"init/networks|bridge/network/Common/notes": "",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/Scheme": "ds",
|
||||
"guestinfo.vice..init.sessions|vicadmin.diagnostics.resurrections": "0",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/ForceQuery": "false",
|
||||
"guestinfo.vice./network/container_networks|vmnet/default": "false",
|
||||
"guestinfo.vice..init.sessions|vicadmin.detail.createtime": "0",
|
||||
"guestinfo.vice./storage/VolumeLocations": "default|vol1",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/Path": "[vsanDatastore] volumes/vol1",
|
||||
"guestinfo.vice./init/sessions|vicadmin/cmd/Args": "3",
|
||||
"guestinfo.vice..init.sessions|docker-personality.runblock": "false",
|
||||
"guestinfo.vice./init/version/BuildNumber": "10373",
|
||||
"guestinfo.vice./network/container_networks|vmnet/Common/id": "DistributedVirtualPortgroup:dvportgroup-358",
|
||||
"guestinfo.vice./init/networks|client/network/Common/id": "DistributedVirtualPortgroup:dvportgroup-56",
|
||||
"guestinfo.vice..init.sessions|docker-personality.status": "0",
|
||||
"guestinfo.vice..init.sessions|port-layer.started": "true",
|
||||
"guestinfo.vice./init/sessions|port-layer/runblock": "false",
|
||||
"guestinfo.vice./init/sessions|port-layer/diagnostics/debug": "0",
|
||||
"init/common/name": "test1",
|
||||
"guestinfo.vice..init.sessions|docker-personality.detail.starttime": "0",
|
||||
"guestinfo.vice./storage/VolumeLocations|vol1/RawQuery": "",
|
||||
"init/common/notes": "",
|
||||
"guestinfo.vice./network/container_networks|bridge/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice..init.networks|client.network.assigned.gateway.IP": "CsC//Q==",
|
||||
"guestinfo.vice..init.sessions|vicadmin.detail.starttime": "0",
|
||||
"guestinfo.vice./init/networks|client/Common/name": "client",
|
||||
"guestinfo.vice./init/networks|bridge/static": "true",
|
||||
"network/container_networks|vmnet/Common/notes": "",
|
||||
"guestinfo.vice./init/repo": "",
|
||||
"guestinfo.vice./storage/image_stores": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/runblock": "false",
|
||||
"guestinfo.vice./network/container_networks|vnet/gateway/Mask": "////AA==",
|
||||
"guestinfo.vice..init.networks|client.network.assigned.dns|0": "CqLMAQ==",
|
||||
"guestinfo.vice./init/networks|public/Common/id": "192",
|
||||
"guestinfo.vice./init/networks|public/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/networks|public/network/default": "true",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/Fragment": "",
|
||||
"guestinfo.vice./init/asymrouting": "false",
|
||||
"guestinfo.vice..init.sessions|vicadmin.started": "true",
|
||||
"guestinfo.vice./init/sessions|port-layer/User": "",
|
||||
"guestinfo.vice./init/networks|management/Common/name": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Dir": "",
|
||||
"guestinfo.vice./network/container_networks|vnet/Common/name": "vnet",
|
||||
"guestinfo.vice./network/container_networks|bridge/Common/id": "DistributedVirtualPortgroup:dvportgroup-55",
|
||||
"init/sessions|port-layer/common/notes": "",
|
||||
"guestinfo.vice./network/bridge-ip-range/IP": "rBAAAA==",
|
||||
"guestinfo.vice..init.networks|management.network.assigned.gateway.Mask": "///gAA==",
|
||||
"guestinfo.vice./init/sessions|vicadmin/common/name": "vicadmin",
|
||||
"guestinfo.vice./init/networks|management/network/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/networks|management/static": "false",
|
||||
"guestinfo.vice./init/common/id": "VirtualMachine:vm-365",
|
||||
"guestinfo.vice./network/bridge-ip-range/Mask": "//AAAA==",
|
||||
"guestinfo.vice./vic_machine_create_options": "13",
|
||||
"guestinfo.vice..init.sessions|port-layer.status": "0",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/Opaque": "",
|
||||
"guestinfo.vice./init/networks|client/static": "false",
|
||||
"guestinfo.vice./init/networks|management/network/Common/name": "management",
|
||||
"guestinfo.vice./init/sessions|docker-personality/openstdin": "false",
|
||||
"guestinfo.vice./init/sessions|port-layer/cmd/Args": "2",
|
||||
"guestinfo.vice..init.networks|client.assigned.IP": "CsCoOQ==",
|
||||
"guestinfo.vice./init/networks|public/network/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/version/State": "",
|
||||
"guestinfo.vice./network/container_networks|vmnet/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/attach": "false",
|
||||
"guestinfo.vice./storage/image_stores|0/Opaque": "",
|
||||
"guestinfo.vice..init.networks|client.network.assigned.dns": "1",
|
||||
"guestinfo.vice./init/sessions|port-layer/cmd/Env~": "VC_URL=https://10.192.171.116|DC_PATH=vcqaDC|CS_PATH=/vcqaDC/host/cls|POOL_PATH=|DS_PATH=vsanDatastore",
|
||||
"guestinfo.vice./network/container_networks|bridge/Common/name": "bridge",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/Host": "vsanDatastore",
|
||||
"guestinfo.vice./init/sessions|port-layer/cmd/Env": "4",
|
||||
"guestinfo.vice..init.sessions|port-layer.detail.starttime": "0",
|
||||
"guestinfo.vice./init/networks|management/Common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./init/sessions": "docker-personality|port-layer|vicadmin",
|
||||
"guestinfo.vice..init.sessions|port-layer.detail.createtime": "0",
|
||||
"guestinfo.vice./storage/image_stores|0/Fragment": "",
|
||||
"guestinfo.vice..init.networks|management.assigned.Mask": "///gAA==",
|
||||
"guestinfo.vice./network/container_networks|bridge/default": "false",
|
||||
"guestinfo.vice./container/ContainerNameConvention": "",
|
||||
"guestinfo.vice..init.networks|client.network.type": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/restart": "true",
|
||||
"guestinfo.vice./init/networks|public/network/Common/name": "public",
|
||||
"guestinfo.vice..init.sessions|port-layer.runblock": "false",
|
||||
"guestinfo.vice./init/sessions|port-layer/common/id": "port-layer",
|
||||
"guestinfo.vice./init/networks|client/network/Common/name": "client",
|
||||
"guestinfo.vice./network/container_networks|vnet/pools|0/first": "CgoKAA==",
|
||||
"guestinfo.vice..init.networks|public.network.assigned.gateway.IP": "CsC//Q==",
|
||||
"guestinfo.vice./init/layerid": "",
|
||||
"guestinfo.vice./init/sessions|docker-personality/common/name": "docker-personality",
|
||||
"guestinfo.vice./init/sessions|docker-personality/diagnostics/debug": "0",
|
||||
"guestinfo.vice./container/bootstrap_image_path": "[vsanDatastore] 91512359-242e-5a30-ba4a-0200360a2e79/V1.1.0-RC3-10373-65A1889-bootstrap.iso",
|
||||
"guestinfo.vice..network.container_networks|bridge.type": "bridge",
|
||||
"guestinfo.vice..network.container_networks|vnet.type": "external", "migrate.hostLogState": "none",
|
||||
"init/sessions|vicadmin/common/notes": "",
|
||||
"guestinfo.vice./init/sessions|vicadmin/diagnostics/debug": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/attach": "false",
|
||||
"guestinfo.vice./init/sessions|vicadmin/Group": "vicadmin",
|
||||
"guestinfo.vice./storage/VolumeLocations|default/RawPath": "",
|
||||
"guestinfo.vice./init/networks|bridge/network/default": "false",
|
||||
"guestinfo.vice./init/sessions|port-layer/stopSignal": "",
|
||||
"guestinfo.vice./connect/username": "administrator@vsphere.local",
|
||||
"guestinfo.vice..init.sessions|port-layer.diagnostics.resurrections": "0",
|
||||
"guestinfo.vice./init/common/ExecutionEnvironment": "",
|
||||
"guestinfo.vice./network/bridge_network": "bridge",
|
||||
"guestinfo.vice./registry/whitelist_registries": "1",
|
||||
"guestinfo.vice./registry/whitelist_registries~": "harbor.com:2345|insecure:2345",
|
||||
"guestinfo.vice./registry/insecure_registries~": "insecure:2345",
|
||||
"guestinfo.vice./registry/insecure_registries": "0",
|
||||
"guestinfo.vice./init/sessions|vicadmin/cmd/Env~": "PATH=/sbin:/bin|GOTRACEBACK=all|HTTP_PROXY=http://proxy.vmware.com:2318|HTTPS_PROXY=https://proxy.vmware.com:2318",
|
||||
"guestinfo.vice./init/sessions|docker-personality/cmd/Env~": "PATH=/sbin|GOTRACEBACK=all|HTTP_PROXY=http://proxy.vmware.com:2318|HTTPS_PROXY=https://proxy.vmware.com:2318",
|
||||
}
|
||||
|
||||
commands := map[string][]string{
|
||||
"target": {"https://10.192.171.116"},
|
||||
"thumbprint": {"9F:F0:DF:BA:7F:E2:89:F0:98:E4:A6:D1:58:24:68:74:8A:9B:25:6F"},
|
||||
"image-store": {"vsanDatastore"},
|
||||
"volume-store": {
|
||||
"vsanDatastore/volumes/default:default",
|
||||
"vsanDatastore/volumes/vol1:vol1",
|
||||
},
|
||||
"bridge-network": {"bridge"},
|
||||
"public-network": {"external"},
|
||||
"container-network": {"management:vnet",
|
||||
"vm-network:vmnet",
|
||||
},
|
||||
"container-network-gateway": {"management:10.10.10.1/24"},
|
||||
"container-network-ip-range": {"management:10.10.10.0/24"},
|
||||
|
||||
"debug": {"3"},
|
||||
"insecure-registry": {"insecure:2345"},
|
||||
"whitelist-registry": {"harbor.com:2345", "insecure:2345"},
|
||||
"http-proxy": {"http://proxy.vmware.com:2318"},
|
||||
"https-proxy": {"https://proxy.vmware.com:2318"},
|
||||
}
|
||||
conf := &config.VirtualContainerHostConfigSpec{}
|
||||
extraconfig.DecodeWithPrefix(extraconfig.MapSource(kv), conf, "")
|
||||
|
||||
ctx := context.Background()
|
||||
d, err := validate.NewDataFromConfig(ctx, &testFinder{}, conf)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
dest, err := DataToOption(d)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
}
|
||||
for k, v := range dest {
|
||||
if strings.Contains(k, "store") {
|
||||
for i := range v {
|
||||
v[i] = strings.TrimLeft(v[i], "ds://")
|
||||
}
|
||||
}
|
||||
c := commands[k]
|
||||
testArrays(t, c, v, k)
|
||||
}
|
||||
for k, v := range commands {
|
||||
if strings.Contains(k, "store") {
|
||||
for i := range v {
|
||||
v[i] = strings.TrimLeft(v[i], "ds://")
|
||||
}
|
||||
}
|
||||
c := dest[k]
|
||||
testArrays(t, v, c, k)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testArrays(t *testing.T, l, r []string, key string) {
|
||||
for i := range l {
|
||||
found := false
|
||||
for j := range r {
|
||||
if l[i] == r[j] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, fmt.Sprintf("option value %s mismatch: value %s in array %s is not found in array %s", key, l[i], l, r))
|
||||
}
|
||||
}
|
||||
0
vendor/github.com/vmware/vic/cmd/vic-machine/create/cert.pfx
generated
vendored
Normal file
0
vendor/github.com/vmware/vic/cmd/vic-machine/create/cert.pfx
generated
vendored
Normal file
789
vendor/github.com/vmware/vic/cmd/vic-machine/create/create.go
generated
vendored
Normal file
789
vendor/github.com/vmware/vic/cmd/vic-machine/create/create.go
generated
vendored
Normal file
@@ -0,0 +1,789 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 create
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/constants"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/lib/install/vchlog"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
// Max permitted length of Virtual Machine name
|
||||
MaxVirtualMachineNameLen = 80
|
||||
// Max permitted length of Virtual Switch name
|
||||
MaxDisplayNameLen = 31
|
||||
)
|
||||
|
||||
var EntireOptionHelpTemplate = `NAME:
|
||||
{{.HelpName}} - {{.Usage}}
|
||||
|
||||
USAGE:
|
||||
{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}
|
||||
|
||||
CATEGORY:
|
||||
{{.Category}}{{end}}{{if .Description}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{.Description}}{{end}}{{if .VisibleFlags}}
|
||||
|
||||
OPTIONS:
|
||||
{{range .Flags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
// Create has all input parameters for vic-machine create command
|
||||
type Create struct {
|
||||
common.Networks
|
||||
*data.Data
|
||||
Certs common.CertFactory
|
||||
containerNetworks common.CNetworks
|
||||
Registries common.Registries
|
||||
|
||||
volumeStores common.VolumeStores
|
||||
|
||||
Nameservers common.DNS
|
||||
|
||||
memoryReservLimits string
|
||||
cpuReservLimits string
|
||||
|
||||
advancedOptions bool
|
||||
BridgeIPRange string
|
||||
|
||||
Proxies common.Proxies
|
||||
|
||||
SyslogAddr string
|
||||
|
||||
executor *management.Dispatcher
|
||||
}
|
||||
|
||||
func NewCreate() *Create {
|
||||
create := &Create{}
|
||||
create.Data = data.NewData()
|
||||
|
||||
return create
|
||||
}
|
||||
|
||||
// SetFields iterates through the fields in the Create struct, searching for fields
|
||||
// tagged with the `arg` key. If the value of that tag matches the supplied `flag`
|
||||
// string, a nil check is performed. If the field is not nil, then the user supplied
|
||||
// this flag on the command line and we need to persist it.
|
||||
// This is a workaround for cli.Context.IsSet() returning false when
|
||||
// the short option for a cli.StringSlice is supplied instead of the long option.
|
||||
// See https://github.com/urfave/cli/issues/314
|
||||
func (c *Create) SetFields() map[string]struct{} {
|
||||
result := make(map[string]struct{})
|
||||
create := reflect.ValueOf(c).Elem()
|
||||
for i := 0; i < create.NumField(); i++ {
|
||||
t := create.Type().Field(i)
|
||||
if tag := t.Tag.Get("arg"); tag != "" {
|
||||
ss := create.Field(i)
|
||||
if !ss.IsNil() {
|
||||
result[tag] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Flags return all cli flags for create
|
||||
func (c *Create) Flags() []cli.Flag {
|
||||
create := []cli.Flag{
|
||||
// images
|
||||
cli.StringFlag{
|
||||
Name: "image-store, i",
|
||||
Value: "",
|
||||
Usage: "Image datastore path in format \"datastore/path\"",
|
||||
Destination: &c.ImageDatastorePath,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "base-image-size",
|
||||
Value: constants.DefaultBaseImageScratchSize,
|
||||
Usage: "Specify the size of the base image from which all other images are created e.g. 8GB/8000MB",
|
||||
Destination: &c.ScratchSize,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
||||
networks := []cli.Flag{
|
||||
// bridge
|
||||
cli.StringFlag{
|
||||
Name: "bridge-network, b",
|
||||
Value: "",
|
||||
Usage: "The bridge network port group name (private port group for containers). Defaults to the Virtual Container Host name",
|
||||
Destination: &c.BridgeNetworkName,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "bridge-network-range, bnr",
|
||||
Value: "172.16.0.0/12",
|
||||
Usage: "The IP range from which bridge networks are allocated",
|
||||
Destination: &c.BridgeIPRange,
|
||||
Hidden: true,
|
||||
},
|
||||
|
||||
// client
|
||||
cli.StringFlag{
|
||||
Name: "client-network, cln",
|
||||
Value: "",
|
||||
Usage: "The client network port group name (restricts DOCKER_API access to this network). Defaults to DCHP - see advanced help (-x)",
|
||||
Destination: &c.ClientNetworkName,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "client-network-gateway",
|
||||
Value: "",
|
||||
Usage: "Gateway for the VCH on the client network, including one or more routing destinations in a comma separated list, e.g. 10.1.0.0/16,10.2.0.0/16:10.0.0.1",
|
||||
Destination: &c.ClientNetworkGateway,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "client-network-ip",
|
||||
Value: "",
|
||||
Usage: "IP address with a network mask for the VCH on the client network, e.g. 10.0.0.2/24",
|
||||
Destination: &c.ClientNetworkIP,
|
||||
Hidden: true,
|
||||
},
|
||||
|
||||
// public
|
||||
cli.StringFlag{
|
||||
Name: "public-network, pn",
|
||||
Value: "VM Network",
|
||||
Usage: "The public network port group name (port forwarding and default route). Defaults to 'VM Network' and DHCP -- see advanced help (-x)",
|
||||
Destination: &c.PublicNetworkName,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "public-network-gateway",
|
||||
Value: "",
|
||||
Usage: "Gateway for the VCH on the public network, e.g. 10.0.0.1",
|
||||
Destination: &c.PublicNetworkGateway,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "public-network-ip",
|
||||
Value: "",
|
||||
Usage: "IP address with a network mask for the VCH on the public network, e.g. 10.0.1.2/24",
|
||||
Destination: &c.PublicNetworkIP,
|
||||
Hidden: true,
|
||||
},
|
||||
|
||||
// management
|
||||
cli.StringFlag{
|
||||
Name: "management-network, mn",
|
||||
Value: "",
|
||||
Usage: "The management network port group name (provides route to target hosting vSphere). Defaults to DCHP - see advanced help (-x)",
|
||||
Destination: &c.ManagementNetworkName,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "management-network-gateway",
|
||||
Value: "",
|
||||
Usage: "Gateway for the VCH on the management network, including one or more routing destinations in a comma separated list, e.g. 10.1.0.0/16,10.2.0.0/16:10.0.0.1",
|
||||
Destination: &c.ManagementNetworkGateway,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "management-network-ip",
|
||||
Value: "",
|
||||
Usage: "IP address with a network mask for the VCH on the management network, e.g. 10.0.2.2/24",
|
||||
Destination: &c.ManagementNetworkIP,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
var memory, cpu []cli.Flag
|
||||
memory = append(memory, c.VCHMemoryLimitFlags(true)...)
|
||||
memory = append(memory,
|
||||
cli.IntFlag{
|
||||
Name: "endpoint-memory",
|
||||
Value: constants.DefaultEndpointMemoryMB,
|
||||
Usage: "Memory for the VCH endpoint VM, in MB. Does not impact resources allocated per container.",
|
||||
Hidden: true,
|
||||
Destination: &c.MemoryMB,
|
||||
})
|
||||
cpu = append(cpu, c.VCHCPULimitFlags(true)...)
|
||||
cpu = append(cpu,
|
||||
cli.IntFlag{
|
||||
Name: "endpoint-cpu",
|
||||
Value: 1,
|
||||
Usage: "vCPUs for the VCH endpoint VM. Does not impact resources allocated per container.",
|
||||
Hidden: true,
|
||||
Destination: &c.NumCPUs,
|
||||
})
|
||||
|
||||
tls := c.Certs.CertFlags()
|
||||
|
||||
tls = append(tls, cli.BoolFlag{
|
||||
Name: "no-tls, k",
|
||||
Usage: "Disable TLS support completely",
|
||||
Destination: &c.Certs.NoTLS,
|
||||
Hidden: true,
|
||||
})
|
||||
|
||||
registries := c.Registries.Flags()
|
||||
registries = append(registries,
|
||||
cli.StringSliceFlag{
|
||||
Name: "insecure-registry, dir",
|
||||
Value: &c.Registries.InsecureRegistriesArg,
|
||||
Usage: "Specify a list of permitted insecure registry server addresses",
|
||||
})
|
||||
registries = append(registries,
|
||||
cli.StringSliceFlag{
|
||||
Name: "whitelist-registry, wr",
|
||||
Value: &c.Registries.WhitelistRegistriesArg,
|
||||
Usage: "Specify a list of permitted whitelist registry server addresses (insecure addresses still require the --insecure-registry option in addition)",
|
||||
})
|
||||
|
||||
syslog := []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "syslog-address",
|
||||
Value: "",
|
||||
Usage: "Address of the syslog server to send Virtual Container Host logs to. Must be in the format transport://host[:port], where transport is udp or tcp. port defaults to 514 if not specified",
|
||||
Destination: &c.SyslogAddr,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
||||
util := []cli.Flag{
|
||||
// miscellaneous
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Ignore error messages and proceed",
|
||||
Destination: &c.Force,
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for create",
|
||||
Destination: &c.Timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "asymmetric-routes, ar",
|
||||
Usage: "Set up the Virtual Container Host for asymmetric routing",
|
||||
Destination: &c.AsymmetricRouting,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
|
||||
help := []cli.Flag{
|
||||
// help options
|
||||
cli.BoolFlag{
|
||||
Name: "extended-help, x",
|
||||
Usage: "Show all options - this must be specified instead of --help",
|
||||
Destination: &c.advancedOptions,
|
||||
},
|
||||
}
|
||||
|
||||
target := c.TargetFlags()
|
||||
ops := c.OpsCredentials.Flags(true)
|
||||
compute := c.ComputeFlags()
|
||||
container := c.ContainerFlags()
|
||||
volume := c.volumeStores.Flags()
|
||||
iso := c.ImageFlags(true)
|
||||
cNetwork := c.containerNetworks.CNetworkFlags(true)
|
||||
dns := c.Nameservers.DNSFlags(true)
|
||||
proxies := c.Proxies.ProxyFlags(true)
|
||||
kubelet := c.Kubelet.Flags(true)
|
||||
debug := c.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, compute, ops, create, container, volume, dns, networks, cNetwork, memory, cpu, tls, registries, proxies, syslog, iso, util, kubelet, debug, help} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (c *Create) ProcessParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := c.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// prevent usage of special characters for certain user provided values
|
||||
if err := common.CheckUnsupportedChars(c.DisplayName); err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("--name contains unsupported characters: %s Allowed characters are alphanumeric, space and symbols - _ ( )", err), 1)
|
||||
}
|
||||
|
||||
if len(c.DisplayName) > MaxDisplayNameLen {
|
||||
return cli.NewExitError(fmt.Sprintf("Display name %s exceeds the permitted 31 characters limit. Please use a shorter -name parameter", c.DisplayName), 1)
|
||||
}
|
||||
|
||||
if c.BridgeNetworkName == "" {
|
||||
c.BridgeNetworkName = c.DisplayName
|
||||
}
|
||||
|
||||
// Pass admin credentials for use as ops credentials if ops credentials are not supplied.
|
||||
if err := c.OpsCredentials.ProcessOpsCredentials(op, true, c.Target.User, c.Target.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Kubelet.ProcessKubelet(op, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
c.ContainerNetworks, err = c.containerNetworks.ProcessContainerNetworks(op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.ProcessBridgeNetwork(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.ProcessNetwork(op, &c.Data.ClientNetwork, "client", c.ClientNetworkName,
|
||||
c.ClientNetworkIP, c.ClientNetworkGateway); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.ProcessNetwork(op, &c.Data.PublicNetwork, "public", c.PublicNetworkName,
|
||||
c.PublicNetworkIP, c.PublicNetworkGateway); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.ProcessNetwork(op, &c.Data.ManagementNetwork, "management", c.ManagementNetworkName,
|
||||
c.ManagementNetworkIP, c.ManagementNetworkGateway); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.DNS, err = c.Nameservers.ProcessDNSServers(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// must come after client network processing as it checks for static IP on that interface
|
||||
if err = c.processCertificates(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = common.CheckUnsupportedCharsDatastore(c.ImageDatastorePath); err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("--image-store contains unsupported characters: %s Allowed characters are alphanumeric, space and symbols - _ ( ) / :", err), 1)
|
||||
}
|
||||
|
||||
c.VolumeLocations, err = c.volumeStores.ProcessVolumeStores()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.Registries.ProcessRegistries(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.InsecureRegistries = c.Registries.InsecureRegistries
|
||||
c.WhitelistRegistries = c.Registries.WhitelistRegistries
|
||||
c.RegistryCAs = c.Registries.RegistryCAs
|
||||
|
||||
hproxy, sproxy, err := c.Proxies.ProcessProxies()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.HTTPProxy = hproxy
|
||||
c.HTTPSProxy = sproxy
|
||||
|
||||
if err = c.ProcessSyslog(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Create) processCertificates(op trace.Operation) error {
|
||||
|
||||
// debuglevel is a pointer now so we have to do this song and dance
|
||||
var debug int
|
||||
if c.Debug.Debug == nil {
|
||||
debug = 0
|
||||
} else {
|
||||
debug = *c.Debug.Debug
|
||||
}
|
||||
|
||||
c.Certs.Networks = c.Networks
|
||||
|
||||
if err := c.Certs.ProcessCertificates(op, c.DisplayName, c.Force, debug); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// copy a few things out of seed because ProcessCertificates has side effects
|
||||
c.KeyPEM = c.Certs.KeyPEM
|
||||
c.CertPEM = c.Certs.CertPEM
|
||||
c.ClientCAs = c.Certs.ClientCAs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Create) ProcessBridgeNetwork() error {
|
||||
// bridge network params
|
||||
var err error
|
||||
|
||||
_, c.Data.BridgeIPRange, err = net.ParseCIDR(c.BridgeIPRange)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Error parsing bridge network ip range: %s. Range must be in CIDR format, e.g., 172.16.0.0/12", err), 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseGatewaySpec(gw string) (cidrs []net.IPNet, gwIP net.IPNet, err error) {
|
||||
ss := strings.Split(gw, ":")
|
||||
if len(ss) > 2 {
|
||||
err = fmt.Errorf("gateway %s specified incorrectly", gw)
|
||||
return
|
||||
}
|
||||
|
||||
gwStr := ss[0]
|
||||
cidrsStr := ""
|
||||
if len(ss) > 1 {
|
||||
gwStr = ss[1]
|
||||
cidrsStr = ss[0]
|
||||
}
|
||||
|
||||
if gwIP.IP = net.ParseIP(gwStr); gwIP.IP == nil {
|
||||
err = fmt.Errorf("Provided gateway IP address is not valid: %s", gwStr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cidrsStr != "" {
|
||||
for _, c := range strings.Split(cidrsStr, ",") {
|
||||
var ipnet *net.IPNet
|
||||
_, ipnet, err = net.ParseCIDR(c)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid CIDR in gateway specification: %s", err)
|
||||
return
|
||||
}
|
||||
cidrs = append(cidrs, *ipnet)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ProcessNetwork parses network args if present
|
||||
func (c *Create) ProcessNetwork(op trace.Operation, network *data.NetworkConfig, netName, pgName, staticIP, gateway string) error {
|
||||
var err error
|
||||
|
||||
network.Name = pgName
|
||||
|
||||
if gateway != "" && staticIP == "" {
|
||||
return fmt.Errorf("Gateway provided without static IP for %s network", netName)
|
||||
}
|
||||
|
||||
defer func(net *data.NetworkConfig) {
|
||||
if err == nil {
|
||||
op.Debugf("%s network: IP %s gateway %s dest: %s", netName, net.IP, net.Gateway.IP, net.Destinations)
|
||||
}
|
||||
}(network)
|
||||
|
||||
var ipNet *net.IPNet
|
||||
if staticIP != "" {
|
||||
var ipAddr net.IP
|
||||
ipAddr, ipNet, err = net.ParseCIDR(staticIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse the provided %s network IP address %s: %s", netName, staticIP, err)
|
||||
}
|
||||
|
||||
network.IP.IP = ipAddr
|
||||
network.IP.Mask = ipNet.Mask
|
||||
}
|
||||
|
||||
if gateway != "" {
|
||||
network.Destinations, network.Gateway, err = parseGatewaySpec(gateway)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid %s network gateway: %s", netName, err)
|
||||
}
|
||||
|
||||
if !network.IP.Contains(network.Gateway.IP) {
|
||||
return fmt.Errorf("%s gateway with IP %s is not reachable from %s", netName, network.Gateway.IP, ipNet.String())
|
||||
}
|
||||
|
||||
// TODO(vburenin): this seems ugly, and it actually is. The reason is that a gateway required to specify
|
||||
// a network mask for it, which is just not how network configuration should be done. Network mask has to
|
||||
// be provided separately or with the IP address. It is hard to change all dependencies to keep mask
|
||||
// with IP address, so it will be stored with network gateway as it was previously.
|
||||
network.Gateway.Mask = network.IP.Mask
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Create) ProcessSyslog() error {
|
||||
if len(c.SyslogAddr) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(c.SyslogAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SyslogConfig.Addr = u
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Create) logArguments(op trace.Operation, cliContext *cli.Context) []string {
|
||||
args := []string{}
|
||||
sf := c.SetFields() // StringSlice options set by the user
|
||||
for _, f := range cliContext.FlagNames() {
|
||||
_, ok := sf[f]
|
||||
if !cliContext.IsSet(f) && !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// avoid logging sensitive data
|
||||
if f == "user" || f == "password" || f == "ops-password" {
|
||||
op.Debugf("--%s=<censored>", f)
|
||||
continue
|
||||
}
|
||||
|
||||
if f == "tls-server-cert" || f == "tls-cert-path" || f == "tls-server-key" || f == "registry-ca" || f == "tls-ca" {
|
||||
continue
|
||||
}
|
||||
|
||||
if f == "target" {
|
||||
url, err := url.Parse(cliContext.String(f))
|
||||
if err != nil {
|
||||
op.Debugf("Unable to re-parse target url for logging")
|
||||
continue
|
||||
}
|
||||
url.User = nil
|
||||
flag := fmt.Sprintf("--target=%s", url.String())
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
|
||||
i := cliContext.Int(f)
|
||||
if i != 0 {
|
||||
flag := fmt.Sprintf("--%s=%d", f, i)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
d := cliContext.Duration(f)
|
||||
if d != 0 {
|
||||
flag := fmt.Sprintf("--%s=%s", f, d.String())
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
x := cliContext.Float64(f)
|
||||
if x != 0 {
|
||||
flag := fmt.Sprintf("--%s=%f", f, x)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
|
||||
// check for StringSlice before String as the cli String checker
|
||||
// will mistake a StringSlice for a String and jackaroo the formatting
|
||||
match := func() (result bool) {
|
||||
result = false
|
||||
defer func() { recover() }()
|
||||
ss := cliContext.StringSlice(f)
|
||||
if ss != nil {
|
||||
for _, o := range ss {
|
||||
flag := fmt.Sprintf("--%s=%s", f, o)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
}
|
||||
}
|
||||
return ss != nil
|
||||
}()
|
||||
if match {
|
||||
continue
|
||||
}
|
||||
|
||||
s := cliContext.String(f)
|
||||
if s != "" {
|
||||
flag := fmt.Sprintf("--%s=%s", f, s)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
|
||||
b := cliContext.Bool(f)
|
||||
bT := cliContext.BoolT(f)
|
||||
if b && !bT {
|
||||
flag := fmt.Sprintf("--%s=%t", f, true)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
continue
|
||||
}
|
||||
|
||||
match = func() (result bool) {
|
||||
result = false
|
||||
defer func() { recover() }()
|
||||
is := cliContext.IntSlice(f)
|
||||
if is != nil {
|
||||
flag := fmt.Sprintf("--%s=%#v", f, is)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
}
|
||||
return is != nil
|
||||
}()
|
||||
if match {
|
||||
continue
|
||||
}
|
||||
|
||||
// generic last because it matches everything
|
||||
g := cliContext.Generic(f)
|
||||
if g != nil {
|
||||
flag := fmt.Sprintf("--%s=%#v", f, g)
|
||||
op.Debug(flag)
|
||||
args = append(args, flag)
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func (c *Create) Run(clic *cli.Context) (err error) {
|
||||
|
||||
if c.advancedOptions {
|
||||
cli.HelpPrinter(clic.App.Writer, EntireOptionHelpTemplate, clic.Command)
|
||||
return nil
|
||||
}
|
||||
|
||||
// create the logger for streaming VCH log messages
|
||||
datastoreLog := vchlog.New()
|
||||
defer func(old io.Writer) {
|
||||
trace.Logger.Out = old
|
||||
datastoreLog.Close()
|
||||
}(trace.Logger.Out)
|
||||
trace.Logger.Out = io.MultiWriter(trace.Logger.Out, datastoreLog.GetPipe())
|
||||
go datastoreLog.Run()
|
||||
|
||||
// These operations will be executed without timeout
|
||||
op := common.NewOperation(clic, c.Debug.Debug)
|
||||
op.Infof("### Installing VCH ####")
|
||||
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
|
||||
if err = c.ProcessParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := c.logArguments(op, clic)
|
||||
|
||||
var images map[string]string
|
||||
if images, err = c.CheckImagesFiles(op, c.Force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
validator, err := validate.NewValidator(op, c.Data)
|
||||
if err != nil {
|
||||
op.Error("Create cannot continue: failed to create validator")
|
||||
return err
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
vchConfig, err := validator.Validate(op, c.Data)
|
||||
if err != nil {
|
||||
op.Error("Create cannot continue: configuration validation failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// persist cli args used to create the VCH
|
||||
vchConfig.VicMachineCreateOptions = args
|
||||
|
||||
vConfig := validator.AddDeprecatedFields(op, vchConfig, c.Data)
|
||||
vConfig.ImageFiles = images
|
||||
vConfig.ApplianceISO = path.Base(c.ApplianceISO)
|
||||
vConfig.BootstrapISO = path.Base(c.BootstrapISO)
|
||||
|
||||
vConfig.HTTPProxy = c.HTTPProxy
|
||||
vConfig.HTTPSProxy = c.HTTPSProxy
|
||||
|
||||
vConfig.Timeout = c.Data.Timeout
|
||||
|
||||
// separate initial validation from dispatch of creation task
|
||||
op.Info("")
|
||||
|
||||
executor := management.NewDispatcher(op, validator.Session, vchConfig, c.Force)
|
||||
if err = executor.CreateVCH(vchConfig, vConfig, datastoreLog); err != nil {
|
||||
executor.CollectDiagnosticLogs()
|
||||
op.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform the remaining work using a context with a timeout to ensure the user does not wait forever
|
||||
op, cancel := trace.WithTimeout(&op, c.Timeout, "Create")
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Creating VCH exceeded time limit of %s. Please increase the timeout using --timeout to accommodate for a busy vSphere target", c.Timeout)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = executor.CheckServiceReady(op, vchConfig, c.Certs.ClientCert); err != nil {
|
||||
executor.CollectDiagnosticLogs()
|
||||
cmd, _ := executor.GetDockerAPICommand(vchConfig, c.Certs.Ckey, c.Certs.Ccert, c.Certs.Cacert, c.Certs.CertPath)
|
||||
op.Info("\tAPI may be slow to start - try to connect to API after a few minutes:")
|
||||
if cmd != "" {
|
||||
op.Infof("\t\tRun command: %s", cmd)
|
||||
} else {
|
||||
op.Infof("\t\tRun %s inspect to find API connection command and run the command if ip address is ready", clic.App.Name)
|
||||
}
|
||||
op.Info("\t\tIf command succeeds, VCH is started. If command fails, VCH failed to install - see documentation for troubleshooting.")
|
||||
return err
|
||||
}
|
||||
|
||||
op.Info("Initialization of appliance successful")
|
||||
|
||||
// We must check for the volume stores that are present after the portlayer presents.
|
||||
|
||||
executor.ShowVCH(vchConfig, c.Certs.Ckey, c.Certs.Ccert, c.Certs.Cacert, c.Certs.EnvFile, c.Certs.CertPath)
|
||||
op.Info("Installer completed successfully")
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(3 * time.Second):
|
||||
op.Infof("Waiting for log upload to complete") // tell the user if the wait causes noticeable delay
|
||||
case <-op.Done():
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// wait on the logger to finish streaming
|
||||
datastoreLog.Close()
|
||||
datastoreLog.Wait(op)
|
||||
|
||||
return nil
|
||||
}
|
||||
95
vendor/github.com/vmware/vic/cmd/vic-machine/create/create_test.go
generated
vendored
Normal file
95
vendor/github.com/vmware/vic/cmd/vic-machine/create/create_test.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2016-2018 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 create
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseGatewaySpec(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
dest []string
|
||||
gw string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
in: "10.10.10.10",
|
||||
gw: "10.10.10.10",
|
||||
},
|
||||
{
|
||||
in: "10.12.0.0/16:10.10.10.10",
|
||||
dest: []string{"10.12.0.0/16"},
|
||||
gw: "10.10.10.10",
|
||||
},
|
||||
{
|
||||
in: "10.13.0.0/16,10.12.0.0/16:10.10.10.10",
|
||||
dest: []string{"10.13.0.0/16", "10.12.0.0/16"},
|
||||
gw: "10.10.10.10",
|
||||
},
|
||||
}
|
||||
|
||||
for _, te := range tests {
|
||||
dest, gw, err := parseGatewaySpec(te.in)
|
||||
if te.err != nil {
|
||||
assert.EqualError(t, err, te.err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.NotNil(t, gw)
|
||||
assert.Equal(t, te.gw, gw.IP.String())
|
||||
|
||||
assert.Equal(t, len(te.dest), len(dest))
|
||||
for _, d := range te.dest {
|
||||
found := false
|
||||
for _, d2 := range dest {
|
||||
if d2.String() == d {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlags(t *testing.T) {
|
||||
c := NewCreate()
|
||||
flags := c.Flags()
|
||||
numberOfFlags := 62
|
||||
assert.Equal(t, numberOfFlags, len(flags), "Missing flags during Create.")
|
||||
}
|
||||
|
||||
func TestProcessBridgeNetwork(t *testing.T) {
|
||||
c := NewCreate()
|
||||
|
||||
c.BridgeIPRange = "172.16.0.0.0"
|
||||
improperBridgeIPRange := c.ProcessBridgeNetwork()
|
||||
assert.NotNil(t, improperBridgeIPRange)
|
||||
|
||||
c.BridgeIPRange = "172.16.0.0/12"
|
||||
properBridgeIPRange := c.ProcessBridgeNetwork()
|
||||
assert.Nil(t, properBridgeIPRange, "Bridge Network IP Range can't be parsed. Range must be in CIDR format, e.g., 172.16.0.0/12.")
|
||||
}
|
||||
|
||||
func TestSetFields(t *testing.T) {
|
||||
c := NewCreate()
|
||||
option := c.SetFields()
|
||||
assert.NotNil(t, option)
|
||||
}
|
||||
205
vendor/github.com/vmware/vic/cmd/vic-machine/debug/debug.go
generated
vendored
Normal file
205
vendor/github.com/vmware/vic/cmd/vic-machine/debug/debug.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
// Debug has all input parameters for vic-machine Debug command
|
||||
type Debug struct {
|
||||
*data.Data
|
||||
|
||||
executor *management.Dispatcher
|
||||
|
||||
enableSSH bool
|
||||
password string
|
||||
authorizedKey string
|
||||
}
|
||||
|
||||
func NewDebug() *Debug {
|
||||
d := &Debug{}
|
||||
d.Data = data.NewData()
|
||||
return d
|
||||
}
|
||||
|
||||
// Flags return all cli flags for Debug
|
||||
func (d *Debug) Flags() []cli.Flag {
|
||||
preFlags := append(d.TargetFlags(), d.IDFlags()...)
|
||||
preFlags = append(preFlags, d.ComputeFlags()...)
|
||||
|
||||
ssh := []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "enable-ssh, ssh",
|
||||
Usage: "Enable SSH server within appliance VM",
|
||||
Destination: &d.enableSSH,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "authorized-key, key",
|
||||
Value: "",
|
||||
Usage: "File with public key to place as /root/.ssh/authorized_keys",
|
||||
Destination: &d.authorizedKey,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "rootpw, pw",
|
||||
Value: "",
|
||||
Usage: "Password to set for root user (non-persistent over reboots)",
|
||||
Destination: &d.password,
|
||||
},
|
||||
}
|
||||
|
||||
util := []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for operation to complete",
|
||||
Destination: &d.Timeout,
|
||||
},
|
||||
}
|
||||
|
||||
target := d.TargetFlags()
|
||||
id := d.IDFlags()
|
||||
compute := d.ComputeFlags()
|
||||
debug := d.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, id, compute, ssh, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (d *Debug) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := d.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debug) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, d.Debug.Debug)
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithTimeout(&op, d.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Debug timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = d.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
op.Info("### Configuring VCH for debug ####")
|
||||
|
||||
validator, err := validate.NewValidator(op, d.Data)
|
||||
|
||||
if err != nil {
|
||||
op.Errorf("Debug cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
_, err = validator.ValidateTarget(op, d.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Debug cannot continue - target validation failed: %s", err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, d.Force)
|
||||
|
||||
var vch *vm.VirtualMachine
|
||||
if d.Data.ID != "" {
|
||||
vch, err = executor.NewVCHFromID(d.Data.ID)
|
||||
} else {
|
||||
vch, err = executor.NewVCHFromComputePath(d.Data.ComputeResourcePath, d.Data.DisplayName, validator)
|
||||
}
|
||||
if err != nil {
|
||||
op.Errorf("Failed to get Virtual Container Host %s", d.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
|
||||
op.Infof("")
|
||||
op.Infof("VCH ID: %s", vch.Reference().String())
|
||||
|
||||
vchConfig, err := executor.GetVCHConfig(vch)
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
|
||||
installerVer := version.GetBuild()
|
||||
|
||||
op.Info("")
|
||||
op.Infof("Installer version: %s", installerVer.ShortVersion())
|
||||
op.Infof("VCH version: %s", vchConfig.Version.ShortVersion())
|
||||
|
||||
// load the key file if set
|
||||
var key []byte
|
||||
if d.authorizedKey != "" {
|
||||
key, err = ioutil.ReadFile(d.authorizedKey)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to read public key from %s: %s", d.authorizedKey, err)
|
||||
return errors.New("unable to load public key")
|
||||
}
|
||||
}
|
||||
|
||||
if err = executor.DebugVCH(vch, vchConfig, d.password, string(key)); err != nil {
|
||||
executor.CollectDiagnosticLogs()
|
||||
op.Errorf("%s", err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
|
||||
// display the VCH endpoints again for convenience
|
||||
if err = executor.InspectVCH(vch, vchConfig, ""); err != nil {
|
||||
executor.CollectDiagnosticLogs()
|
||||
op.Errorf("%s", err)
|
||||
return errors.New("debug failed")
|
||||
}
|
||||
|
||||
op.Infof("Completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
172
vendor/github.com/vmware/vic/cmd/vic-machine/delete/delete.go
generated
vendored
Normal file
172
vendor/github.com/vmware/vic/cmd/vic-machine/delete/delete.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 delete
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
// Delete has all input parameters for vic-machine delete command
|
||||
type Uninstall struct {
|
||||
*data.Data
|
||||
|
||||
executor *management.Dispatcher
|
||||
}
|
||||
|
||||
func NewUninstall() *Uninstall {
|
||||
d := &Uninstall{}
|
||||
d.Data = data.NewData()
|
||||
return d
|
||||
}
|
||||
|
||||
// Flags return all cli flags for delete
|
||||
func (d *Uninstall) Flags() []cli.Flag {
|
||||
util := []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Force the deletion",
|
||||
Destination: &d.Force,
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for delete",
|
||||
Destination: &d.Timeout,
|
||||
},
|
||||
}
|
||||
|
||||
target := d.TargetFlags()
|
||||
id := d.IDFlags()
|
||||
compute := d.ComputeFlags()
|
||||
debug := d.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, id, compute, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (d *Uninstall) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := d.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Uninstall) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, d.Debug.Debug)
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithTimeout(&op, d.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Delete timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = d.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
op.Infof("### Removing VCH ####")
|
||||
|
||||
validator, err := validate.NewValidator(op, d.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Delete cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
_, err = validator.ValidateTarget(op, d.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Delete cannot continue - target validation failed: %s", err)
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, d.Force)
|
||||
|
||||
var vch *vm.VirtualMachine
|
||||
if d.Data.ID != "" {
|
||||
vch, err = executor.NewVCHFromID(d.Data.ID)
|
||||
} else {
|
||||
vch, err = executor.NewVCHFromComputePath(d.Data.ComputeResourcePath, d.Data.DisplayName, validator)
|
||||
}
|
||||
if err != nil {
|
||||
op.Errorf("Failed to get Virtual Container Host %s", d.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
|
||||
op.Infof("")
|
||||
op.Infof("VCH ID: %s", vch.Reference().String())
|
||||
|
||||
vchConfig, err := executor.GetNoSecretVCHConfig(vch)
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
|
||||
// compare vch version and vic-machine version
|
||||
installerBuild := version.GetBuild()
|
||||
if vchConfig.Version == nil || !installerBuild.Equal(vchConfig.Version) {
|
||||
if !d.Data.Force {
|
||||
op.Errorf("VCH version %q is different than installer version %s. Upgrade VCH before deleting or specify --force to force delete", vchConfig.Version.ShortVersion(), installerBuild.ShortVersion())
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
|
||||
op.Warnf("VCH version %q is different than installer version %s. Force delete will attempt to remove everything related to the installed VCH", vchConfig.Version.ShortVersion(), installerBuild.ShortVersion())
|
||||
}
|
||||
|
||||
if err = executor.DeleteVCH(vchConfig, nil, nil); err != nil {
|
||||
executor.CollectDiagnosticLogs()
|
||||
op.Errorf("%s", err)
|
||||
return errors.New("delete failed")
|
||||
}
|
||||
|
||||
op.Info("----------")
|
||||
op.Info("If firewall changes were made for VIC during install, they were not reverted during delete")
|
||||
op.Info("To modify firewall rules see vic-machine update firewall --help")
|
||||
op.Info("----------")
|
||||
op.Info("Completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
342
vendor/github.com/vmware/vic/cmd/vic-machine/inspect/inspect.go
generated
vendored
Normal file
342
vendor/github.com/vmware/vic/cmd/vic-machine/inspect/inspect.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 inspect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/cmd/vic-machine/converter"
|
||||
"github.com/vmware/vic/cmd/vic-machine/create"
|
||||
"github.com/vmware/vic/lib/config"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
// Inspect has all input parameters for vic-machine inspect command
|
||||
type Inspect struct {
|
||||
*data.Data
|
||||
|
||||
CertPath string
|
||||
|
||||
executor *management.Dispatcher
|
||||
|
||||
Format string
|
||||
}
|
||||
|
||||
type state struct {
|
||||
i *Inspect
|
||||
op trace.Operation
|
||||
validator *validate.Validator
|
||||
vchConfig *config.VirtualContainerHostConfigSpec
|
||||
vch *vm.VirtualMachine
|
||||
executor *management.Dispatcher
|
||||
}
|
||||
|
||||
type command func(state) error
|
||||
|
||||
func NewInspect() *Inspect {
|
||||
d := &Inspect{}
|
||||
d.Data = data.NewData()
|
||||
return d
|
||||
}
|
||||
|
||||
// Flags returns all cli flags for inspect
|
||||
func (i *Inspect) Flags() []cli.Flag {
|
||||
util := []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for inspect",
|
||||
Destination: &i.Timeout,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "tls-cert-path",
|
||||
Value: "",
|
||||
Usage: "The path to check for existing certificates. Defaults to './<vch name>/'",
|
||||
Destination: &i.CertPath,
|
||||
},
|
||||
}
|
||||
|
||||
target := i.TargetFlags()
|
||||
id := i.IDFlags()
|
||||
compute := i.ComputeFlags()
|
||||
debug := i.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, id, compute, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (i *Inspect) ConfigFlags() []cli.Flag {
|
||||
config := cli.StringFlag{
|
||||
Name: "format",
|
||||
Value: "verbose",
|
||||
Usage: "Determine the format of configuration output. Supported formats: raw, verbose",
|
||||
Destination: &i.Format,
|
||||
}
|
||||
flags := []cli.Flag{config}
|
||||
flags = append(flags, i.Flags()...)
|
||||
return flags
|
||||
}
|
||||
|
||||
func (i *Inspect) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := i.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Inspect) run(clic *cli.Context, op trace.Operation, cmd command) (err error) {
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithTimeout(&op, i.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Inspect timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = i.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
op.Infof("### Inspecting VCH ####")
|
||||
|
||||
validator, err := validate.NewValidator(op, i.Data)
|
||||
|
||||
if err != nil {
|
||||
op.Errorf("Inspect cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
_, err = validator.ValidateTarget(op, i.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Inspect cannot continue - target validation failed: %s", err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, i.Force)
|
||||
|
||||
var vch *vm.VirtualMachine
|
||||
if i.Data.ID != "" {
|
||||
vch, err = executor.NewVCHFromID(i.Data.ID)
|
||||
} else {
|
||||
vch, err = executor.NewVCHFromComputePath(i.Data.ComputeResourcePath, i.Data.DisplayName, validator)
|
||||
}
|
||||
if err != nil {
|
||||
op.Errorf("Failed to get Virtual Container Host %s", i.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
|
||||
vchConfig, err := executor.GetNoSecretVCHConfig(vch)
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
|
||||
return cmd(state{i, op, validator, vchConfig, vch, executor})
|
||||
}
|
||||
|
||||
func (i *Inspect) RunConfig(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, i.Debug.Debug)
|
||||
|
||||
if i.Format == "raw" {
|
||||
op.Logger.Level = logrus.ErrorLevel
|
||||
op.Logger.Out = os.Stderr
|
||||
} else if i.Format != "verbose" {
|
||||
op.Warnf("Invalid configuration output format '%s'. Valid options are raw, verbose.", i.Format)
|
||||
op.Warn("Using verbose configuration format")
|
||||
i.Format = "verbose"
|
||||
}
|
||||
|
||||
return i.run(clic, op, func(s state) error {
|
||||
err = i.showConfiguration(s.op, s.validator.Session.Finder, s.vchConfig, s.vch)
|
||||
if err != nil {
|
||||
op.Error("Failed to print Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (i *Inspect) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, i.Debug.Debug)
|
||||
|
||||
return i.run(clic, op, func(s state) error {
|
||||
op.Infof("")
|
||||
op.Infof("VCH ID: %s", s.vch.Reference().String())
|
||||
|
||||
installerVer := version.GetBuild()
|
||||
|
||||
op.Info("")
|
||||
op.Infof("Installer version: %s", installerVer.ShortVersion())
|
||||
op.Infof("VCH version: %s", s.vchConfig.Version.ShortVersion())
|
||||
op.Info("")
|
||||
op.Info("VCH upgrade status:")
|
||||
i.upgradeStatusMessage(s.op, s.vch, installerVer, s.vchConfig.Version)
|
||||
|
||||
if err = s.executor.InspectVCH(s.vch, s.vchConfig, i.CertPath); err != nil {
|
||||
s.executor.CollectDiagnosticLogs()
|
||||
op.Errorf("%s", err)
|
||||
return errors.New("inspect failed")
|
||||
}
|
||||
|
||||
op.Infof("Completed successfully")
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func retrieveMapOptions(op trace.Operation, finder validate.Finder,
|
||||
conf *config.VirtualContainerHostConfigSpec, vm *vm.VirtualMachine) (map[string][]string, error) {
|
||||
data, err := validate.NewDataFromConfig(op, finder, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = validate.SetDataFromVM(op, finder, vm, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return converter.DataToOption(data)
|
||||
}
|
||||
|
||||
func (i Inspect) showConfiguration(op trace.Operation, finder validate.Finder, conf *config.VirtualContainerHostConfigSpec, vm *vm.VirtualMachine) error {
|
||||
mapOptions, err := retrieveMapOptions(op, finder, conf, vm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options := i.sortedOutput(mapOptions)
|
||||
if i.Format == "raw" {
|
||||
strOptions := strings.Join(options, " ")
|
||||
fmt.Println(strOptions)
|
||||
} else if i.Format == "verbose" {
|
||||
strOptions := strings.Join(options, "\n\t")
|
||||
op.Info("")
|
||||
op.Infof("Target VCH created with the following options: \n\n\t%s\n", strOptions)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Inspect) sortedOutput(mapOptions map[string][]string) (output []string) {
|
||||
create := create.NewCreate()
|
||||
cFlags := create.Flags()
|
||||
for _, f := range cFlags {
|
||||
key := f.GetName()
|
||||
// change multiple option name to long name: e.g. from target,t => target
|
||||
s := strings.Split(key, ",")
|
||||
if len(s) > 1 {
|
||||
key = s[0]
|
||||
}
|
||||
|
||||
values, ok := mapOptions[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
defaultValue := ""
|
||||
switch t := f.(type) {
|
||||
case cli.StringFlag:
|
||||
defaultValue = t.Value
|
||||
case cli.IntFlag:
|
||||
defaultValue = strconv.Itoa(t.Value)
|
||||
}
|
||||
for _, val := range values {
|
||||
if val == defaultValue {
|
||||
// do not print command option if it's same to default
|
||||
continue
|
||||
}
|
||||
output = append(output, fmt.Sprintf("--%s=%s", key, val))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// upgradeStatusMessage generates a user facing status string about upgrade progress and status
|
||||
func (i *Inspect) upgradeStatusMessage(op trace.Operation, vch *vm.VirtualMachine, installerVer *version.Build, vchVer *version.Build) {
|
||||
if sameVer := installerVer.Equal(vchVer); sameVer {
|
||||
op.Info("Installer has same version as VCH")
|
||||
op.Info("No upgrade available with this installer version")
|
||||
return
|
||||
}
|
||||
|
||||
upgrading, err := vch.VCHUpdateStatus(op)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to determine if upgrade/configure is in progress: %s", err)
|
||||
return
|
||||
}
|
||||
if upgrading {
|
||||
op.Info("Upgrade/configure in progress")
|
||||
return
|
||||
}
|
||||
|
||||
canUpgrade, err := installerVer.IsNewer(vchVer)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to determine if upgrade is available: %s", err)
|
||||
return
|
||||
}
|
||||
if canUpgrade {
|
||||
op.Info("Upgrade available")
|
||||
return
|
||||
}
|
||||
|
||||
oldInstaller, err := installerVer.IsOlder(vchVer)
|
||||
if err != nil {
|
||||
op.Errorf("Unable to determine if upgrade is available: %s", err)
|
||||
return
|
||||
}
|
||||
if oldInstaller {
|
||||
op.Info("Installer has older version than VCH")
|
||||
op.Info("No upgrade available with this installer version")
|
||||
return
|
||||
}
|
||||
|
||||
// can't get here
|
||||
op.Warn("Invalid upgrade status")
|
||||
return
|
||||
}
|
||||
219
vendor/github.com/vmware/vic/cmd/vic-machine/list/list.go
generated
vendored
Normal file
219
vendor/github.com/vmware/vic/cmd/vic-machine/list/list.go
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright 2016 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 list
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
type items struct {
|
||||
ID string
|
||||
Path string
|
||||
Name string
|
||||
Version string
|
||||
UpgradeStatus string
|
||||
}
|
||||
|
||||
// templ is parsed by text/template package
|
||||
const templ = `{{range .}}
|
||||
{{.ID}} {{.Path}} {{.Name}} {{.Version}} {{.UpgradeStatus}}{{end}}
|
||||
`
|
||||
|
||||
// List has all input parameters for vic-machine ls command
|
||||
type List struct {
|
||||
*data.Data
|
||||
|
||||
executor *management.Dispatcher
|
||||
}
|
||||
|
||||
func NewList() *List {
|
||||
d := &List{}
|
||||
d.Data = data.NewData()
|
||||
return d
|
||||
}
|
||||
|
||||
// Flags return all cli flags for ls
|
||||
func (l *List) Flags() []cli.Flag {
|
||||
util := []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for list",
|
||||
Destination: &l.Timeout,
|
||||
},
|
||||
}
|
||||
|
||||
target := l.TargetFlags()
|
||||
// TODO: why not allow name as a filter, like most list operations
|
||||
compute := l.ComputeFlagsNoName()
|
||||
debug := l.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, compute, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (l *List) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := l.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *List) prettyPrint(op trace.Operation, cli *cli.Context, vchs []*vm.VirtualMachine, executor *management.Dispatcher) {
|
||||
data := []items{
|
||||
{"ID", "PATH", "NAME", "VERSION", "UPGRADE STATUS"},
|
||||
}
|
||||
installerVer := version.GetBuild()
|
||||
for _, vch := range vchs {
|
||||
|
||||
vchConfig, err := executor.GetNoSecretVCHConfig(vch)
|
||||
var version string
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
version = "unknown"
|
||||
} else {
|
||||
version = vchConfig.Version.ShortVersion()
|
||||
}
|
||||
|
||||
parentPath := path.Dir(path.Dir(vch.InventoryPath))
|
||||
name := path.Base(vch.InventoryPath)
|
||||
upgradeStatus := l.upgradeStatusMessage(op, vch, installerVer, vchConfig.Version)
|
||||
data = append(data,
|
||||
items{vch.Reference().Value, parentPath, name, version, upgradeStatus})
|
||||
}
|
||||
t := template.New("vic-machine ls")
|
||||
// #nosec: Errors unhandled.
|
||||
t, _ = t.Parse(templ)
|
||||
w := tabwriter.NewWriter(cli.App.Writer, 8, 8, 8, ' ', 0)
|
||||
if err := t.Execute(w, data); err != nil {
|
||||
op.Fatal(err)
|
||||
}
|
||||
// #nosec: Errors unhandled.
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func (l *List) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, l.Debug.Debug)
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithTimeout(&op, l.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("List timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = l.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
op.Infof("### Listing VCHs ####")
|
||||
|
||||
var validator *validate.Validator
|
||||
if validator, err = validate.NewValidator(op, l.Data); err != nil {
|
||||
op.Errorf("List cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("list failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
// If dc is not set, and multiple datacenter is available, vic-machine ls will list VCHs under all datacenters.
|
||||
validator.AllowEmptyDC()
|
||||
|
||||
_, err = validator.ValidateTarget(op, l.Data)
|
||||
if err != nil {
|
||||
op.Errorf("List cannot continue - target validation failed: %s", err)
|
||||
return errors.New("list failed")
|
||||
}
|
||||
_, err = validator.ValidateCompute(op, l.Data, false)
|
||||
if err != nil {
|
||||
op.Errorf("List cannot continue - compute resource validation failed: %s", err)
|
||||
return errors.New("list failed")
|
||||
}
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, false)
|
||||
vchs, err := executor.SearchVCHs(validator.ClusterPath)
|
||||
if err != nil {
|
||||
op.Errorf("List cannot continue - failed to search VCHs in %s: %s", validator.ResourcePoolPath, err)
|
||||
}
|
||||
l.prettyPrint(op, clic, vchs, executor)
|
||||
return nil
|
||||
}
|
||||
|
||||
// upgradeStatusMessage generates a user facing status string about upgrade progress and status
|
||||
func (l *List) upgradeStatusMessage(op trace.Operation, vch *vm.VirtualMachine, installerVer *version.Build, vchVer *version.Build) string {
|
||||
if sameVer := installerVer.Equal(vchVer); sameVer {
|
||||
return "Up to date"
|
||||
}
|
||||
|
||||
upgrading, err := vch.VCHUpdateStatus(op)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Unknown: %s", err)
|
||||
}
|
||||
if upgrading {
|
||||
return "Upgrade in progress"
|
||||
}
|
||||
|
||||
canUpgrade, err := installerVer.IsNewer(vchVer)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Unknown: %s", err)
|
||||
}
|
||||
if canUpgrade {
|
||||
return fmt.Sprintf("Upgradeable to %s", installerVer.ShortVersion())
|
||||
}
|
||||
|
||||
oldInstaller, err := installerVer.IsOlder(vchVer)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Unknown: %s", err)
|
||||
}
|
||||
if oldInstaller {
|
||||
return fmt.Sprintf("VCH has newer version")
|
||||
}
|
||||
|
||||
// can't get here
|
||||
return "Invalid upgrade status"
|
||||
}
|
||||
178
vendor/github.com/vmware/vic/cmd/vic-machine/main.go
generated
vendored
Normal file
178
vendor/github.com/vmware/vic/cmd/vic-machine/main.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
runtime "runtime/debug"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/configure"
|
||||
"github.com/vmware/vic/cmd/vic-machine/create"
|
||||
"github.com/vmware/vic/cmd/vic-machine/debug"
|
||||
uninstall "github.com/vmware/vic/cmd/vic-machine/delete"
|
||||
"github.com/vmware/vic/cmd/vic-machine/inspect"
|
||||
"github.com/vmware/vic/cmd/vic-machine/list"
|
||||
"github.com/vmware/vic/cmd/vic-machine/update"
|
||||
"github.com/vmware/vic/cmd/vic-machine/upgrade"
|
||||
viclog "github.com/vmware/vic/pkg/log"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/version"
|
||||
)
|
||||
|
||||
const (
|
||||
LogFile = "vic-machine.log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = filepath.Base(os.Args[0])
|
||||
app.Usage = "Create and manage Virtual Container Hosts"
|
||||
app.EnableBashCompletion = true
|
||||
|
||||
create := create.NewCreate()
|
||||
uninstall := uninstall.NewUninstall()
|
||||
inspect := inspect.NewInspect()
|
||||
list := list.NewList()
|
||||
upgrade := upgrade.NewUpgrade()
|
||||
debug := debug.NewDebug()
|
||||
updateFw := update.NewUpdateFw()
|
||||
configure := configure.NewConfigure()
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "create",
|
||||
Usage: "Deploy VCH",
|
||||
Action: create.Run,
|
||||
Flags: create.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "configure",
|
||||
Usage: "Update VCH configuration",
|
||||
Action: configure.Run,
|
||||
Flags: configure.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Usage: "Delete VCH and associated resources",
|
||||
Action: uninstall.Run,
|
||||
Flags: uninstall.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "ls",
|
||||
Usage: "List VCHs",
|
||||
Action: list.Run,
|
||||
Flags: list.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "inspect",
|
||||
Usage: "Inspect VCH",
|
||||
Action: inspect.Run,
|
||||
Flags: inspect.Flags(),
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "Show VCH configuration options",
|
||||
Action: inspect.RunConfig,
|
||||
Flags: inspect.ConfigFlags(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "upgrade",
|
||||
Usage: "Upgrade VCH to latest version",
|
||||
Action: upgrade.Run,
|
||||
Flags: upgrade.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Usage: "Show VIC version information",
|
||||
Action: showVersion,
|
||||
},
|
||||
{
|
||||
Name: "debug",
|
||||
Usage: "Debug VCH",
|
||||
Action: debug.Run,
|
||||
Flags: debug.Flags(),
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Usage: "Modify configuration",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "firewall",
|
||||
Usage: "Modify host firewall",
|
||||
Action: updateFw.Run,
|
||||
Flags: updateFw.Flags(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.Version = version.GetBuild().ShortVersion()
|
||||
|
||||
logs := []io.Writer{app.Writer}
|
||||
// Open log file
|
||||
// #nosec: Expect file permissions to be 0600 or less
|
||||
f, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
// #nosec: Errors unhandled.
|
||||
fmt.Fprintf(os.Stderr, "Error opening logfile %s: %v\n", LogFile, err)
|
||||
} else {
|
||||
defer f.Close()
|
||||
logs = append(logs, f)
|
||||
}
|
||||
|
||||
// Initiliaze logger with default TextFormatter
|
||||
log.SetFormatter(viclog.NewTextFormatter())
|
||||
// SetOutput to io.MultiWriter so that we can log to stdout and a file
|
||||
log.SetOutput(io.MultiWriter(logs...))
|
||||
// Set up the global trace logger to write to the file
|
||||
trace.Logger.Out = f
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Output instructions to check log file for details to the user, but not the log file
|
||||
logger := log.New()
|
||||
logger.Out = app.Writer
|
||||
logger.Errorf("--------------------")
|
||||
logger.Errorf("%s failed, please check log file %s for details", app.Name, LogFile)
|
||||
|
||||
// Output the stack to the log file, but not the user
|
||||
// #nosec: Errors unhandled.
|
||||
fmt.Fprintf(f, "%s", runtime.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
// When writing log files (vic-machine.log and the datastore log), always log at debug.
|
||||
defer func(old log.Level) {
|
||||
trace.Logger.Level = old
|
||||
}(trace.Logger.Level)
|
||||
trace.Logger.Level = log.DebugLevel
|
||||
|
||||
// #nosec: Errors unhandled.
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func showVersion(cli *cli.Context) error {
|
||||
// #nosec: Errors unhandled.
|
||||
fmt.Fprintf(cli.App.Writer, "%v version %v\n", cli.App.Name, cli.App.Version)
|
||||
return nil
|
||||
}
|
||||
185
vendor/github.com/vmware/vic/cmd/vic-machine/update/firewall.go
generated
vendored
Normal file
185
vendor/github.com/vmware/vic/cmd/vic-machine/update/firewall.go
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 update
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
)
|
||||
|
||||
// UpdateFw has all input parameters for vic-machine update firewall command
|
||||
type UpdateFw struct {
|
||||
*data.Data
|
||||
|
||||
executor *management.Dispatcher
|
||||
|
||||
enableFw bool
|
||||
disableFw bool
|
||||
}
|
||||
|
||||
func NewUpdateFw() *UpdateFw {
|
||||
i := &UpdateFw{}
|
||||
i.Data = data.NewData()
|
||||
return i
|
||||
}
|
||||
|
||||
// Flags return all cli flags for update firewall
|
||||
func (i *UpdateFw) Flags() []cli.Flag {
|
||||
update := []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for update firewall",
|
||||
Destination: &i.Timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "allow",
|
||||
Usage: "Enable a firewall rule on target host(s) to allow VIC communication",
|
||||
Destination: &i.enableFw,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "deny",
|
||||
Usage: "Disable the firewall rule on target host(s) that allows VIC communication",
|
||||
Destination: &i.disableFw,
|
||||
},
|
||||
}
|
||||
|
||||
target := i.TargetFlags()
|
||||
compute := i.ComputeFlagsNoName()
|
||||
debug := i.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, compute, update, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (i *UpdateFw) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := i.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.enableFw && i.disableFw {
|
||||
return errors.New("Only one of --allow and --deny can be set")
|
||||
}
|
||||
|
||||
if !i.enableFw && !i.disableFw {
|
||||
return errors.New("No command selected")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *UpdateFw) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, i.Debug.Debug)
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithTimeout(&op, i.Timeout, clic.App.Name)
|
||||
defer cancel()
|
||||
defer func() {
|
||||
if op.Err() != nil && op.Err() == context.DeadlineExceeded {
|
||||
//context deadline exceeded, replace returned error message
|
||||
err = errors.Errorf("Update timed out: use --timeout to add more time")
|
||||
}
|
||||
}()
|
||||
|
||||
if err = i.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
op.Infof("### Updating Firewall ####")
|
||||
|
||||
var validator *validate.Validator
|
||||
if validator, err = validate.NewValidator(op, i.Data); err != nil {
|
||||
op.Errorf("Update cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("update firewall failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
_, err = validator.ValidateTarget(op, i.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Update cannot continue - target validation failed: %s", err)
|
||||
return errors.New("update firewall failed")
|
||||
}
|
||||
_, err = validator.ValidateCompute(op, i.Data, true)
|
||||
if err != nil {
|
||||
op.Errorf("Update cannot continue - compute resource validation failed: %s", err)
|
||||
return errors.New("update firewall failed")
|
||||
}
|
||||
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, false)
|
||||
|
||||
if i.enableFw {
|
||||
op.Info("")
|
||||
op.Warn("### WARNING ###")
|
||||
op.Warn("\tThis command modifies the host firewall on the target machine or cluster")
|
||||
op.Warnf("\tThe ruleset %q will be enabled", management.RulesetID)
|
||||
op.Warn("\tThis allows all outbound TCP traffic from the target")
|
||||
op.Warn("\tTo undo this modification use --deny")
|
||||
op.Info("")
|
||||
|
||||
err := executor.EnableFirewallRuleset()
|
||||
if err != nil {
|
||||
op.Errorf("Failed to enable VIC firewall rule: %s", err)
|
||||
return errors.New("failed to enable firewall rule")
|
||||
}
|
||||
}
|
||||
|
||||
if i.disableFw {
|
||||
op.Info("")
|
||||
op.Warn("### WARNING ###")
|
||||
op.Warn("\tThis command modifies the host firewall on the target machine or cluster")
|
||||
op.Warnf("\tThe ruleset %q will be disabled", management.RulesetID)
|
||||
op.Warn("\tThis disables the ruleset that allows all outbound TCP traffic from the target")
|
||||
op.Warn("\tVIC Engine will not function unless 2377/tcp outbound is allowed")
|
||||
op.Warn("\tTo undo this modification use --allow")
|
||||
op.Info("")
|
||||
|
||||
err := executor.DisableFirewallRuleset()
|
||||
if err != nil {
|
||||
op.Errorf("Failed to disable VIC firewall rule: %s", err)
|
||||
return errors.New("failed to disable firewall rule")
|
||||
}
|
||||
}
|
||||
op.Info("")
|
||||
|
||||
if i.enableFw || i.disableFw {
|
||||
op.Infof("Firewall changes complete")
|
||||
}
|
||||
|
||||
op.Infof("Command completed successfully")
|
||||
return nil
|
||||
}
|
||||
228
vendor/github.com/vmware/vic/cmd/vic-machine/upgrade/upgrade.go
generated
vendored
Normal file
228
vendor/github.com/vmware/vic/cmd/vic-machine/upgrade/upgrade.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
|
||||
//
|
||||
// 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 upgrade
|
||||
|
||||
import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
||||
"github.com/vmware/vic/cmd/vic-machine/common"
|
||||
"github.com/vmware/vic/lib/install/data"
|
||||
"github.com/vmware/vic/lib/install/management"
|
||||
"github.com/vmware/vic/lib/install/validate"
|
||||
"github.com/vmware/vic/pkg/errors"
|
||||
"github.com/vmware/vic/pkg/trace"
|
||||
"github.com/vmware/vic/pkg/vsphere/vm"
|
||||
)
|
||||
|
||||
// Upgrade has all input parameters for vic-machine upgrade command
|
||||
type Upgrade struct {
|
||||
*data.Data
|
||||
|
||||
executor *management.Dispatcher
|
||||
}
|
||||
|
||||
func NewUpgrade() *Upgrade {
|
||||
upgrade := &Upgrade{}
|
||||
upgrade.Data = data.NewData()
|
||||
|
||||
return upgrade
|
||||
}
|
||||
|
||||
// Flags return all cli flags for upgrade
|
||||
func (u *Upgrade) Flags() []cli.Flag {
|
||||
util := []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "force, f",
|
||||
Usage: "Force the upgrade (ignores version checks)",
|
||||
Destination: &u.Force,
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 3 * time.Minute,
|
||||
Usage: "Time to wait for upgrade",
|
||||
Destination: &u.Timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "rollback",
|
||||
Usage: "Roll back VCH version to before the current upgrade",
|
||||
Destination: &u.Rollback,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "reset-progress",
|
||||
Usage: "Reset the UpdateInProgress flag. Warning: Do not reset this flag if another upgrade/configure process is running",
|
||||
Destination: &u.ResetInProgressFlag,
|
||||
},
|
||||
}
|
||||
|
||||
target := u.TargetFlags()
|
||||
id := u.IDFlags()
|
||||
compute := u.ComputeFlags()
|
||||
iso := u.ImageFlags(false)
|
||||
debug := u.DebugFlags(true)
|
||||
|
||||
// flag arrays are declared, now combined
|
||||
var flags []cli.Flag
|
||||
for _, f := range [][]cli.Flag{target, id, compute, iso, util, debug} {
|
||||
flags = append(flags, f...)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (u *Upgrade) processParams(op trace.Operation) error {
|
||||
defer trace.End(trace.Begin("", op))
|
||||
|
||||
if err := u.HasCredentials(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Upgrade) Run(clic *cli.Context) (err error) {
|
||||
op := common.NewOperation(clic, u.Debug.Debug)
|
||||
defer func() {
|
||||
// urfave/cli will print out exit in error handling, so no more information in main method can be printed out.
|
||||
err = common.LogErrorIfAny(op, clic, err)
|
||||
}()
|
||||
op, cancel := trace.WithCancel(&op, clic.App.Name)
|
||||
defer cancel()
|
||||
|
||||
if err = u.processParams(op); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(clic.Args()) > 0 {
|
||||
op.Errorf("Unknown argument: %s", clic.Args()[0])
|
||||
return errors.New("invalid CLI arguments")
|
||||
}
|
||||
|
||||
var images map[string]string
|
||||
if images, err = u.CheckImagesFiles(op, u.Force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
op.Infof("### Upgrading VCH ####")
|
||||
|
||||
validator, err := validate.NewValidator(op, u.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Upgrade cannot continue - failed to create validator: %s", err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
defer validator.Session.Logout(op)
|
||||
|
||||
_, err = validator.ValidateTarget(op, u.Data)
|
||||
if err != nil {
|
||||
op.Errorf("Upgrade cannot continue - target validation failed: %s", err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
executor := management.NewDispatcher(validator.Context, validator.Session, nil, u.Force)
|
||||
|
||||
var vch *vm.VirtualMachine
|
||||
if u.Data.ID != "" {
|
||||
vch, err = executor.NewVCHFromID(u.Data.ID)
|
||||
} else {
|
||||
vch, err = executor.NewVCHFromComputePath(u.Data.ComputeResourcePath, u.Data.DisplayName, validator)
|
||||
}
|
||||
if err != nil {
|
||||
op.Errorf("Failed to get Virtual Container Host %s", u.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
op.Infof("")
|
||||
op.Infof("VCH ID: %s", vch.Reference().String())
|
||||
|
||||
if u.ResetInProgressFlag {
|
||||
if err = vch.SetVCHUpdateStatus(op, false); err != nil {
|
||||
op.Error("Failed to reset UpdateInProgress flag")
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
op.Infof("Reset UpdateInProgress flag successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
upgrading, err := vch.VCHUpdateStatus(op)
|
||||
if err != nil {
|
||||
op.Error("Unable to determine if upgrade/configure is in progress")
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
if upgrading {
|
||||
op.Error("Upgrade failed: another upgrade/configure operation is in progress")
|
||||
op.Error("If no other upgrade/configure process is running, use --reset-progress to reset the VCH upgrade/configure status")
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
if err = vch.SetVCHUpdateStatus(op, true); err != nil {
|
||||
op.Error("Failed to set UpdateInProgress flag to true")
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = vch.SetVCHUpdateStatus(op, false); err != nil {
|
||||
op.Error("Failed to reset UpdateInProgress")
|
||||
op.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
vchConfig, err := executor.FetchAndMigrateVCHConfig(vch)
|
||||
if err != nil {
|
||||
op.Error("Failed to get Virtual Container Host configuration")
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
vConfig := validator.AddDeprecatedFields(op, vchConfig, u.Data)
|
||||
vConfig.ImageFiles = images
|
||||
vConfig.ApplianceISO = path.Base(u.ApplianceISO)
|
||||
vConfig.BootstrapISO = path.Base(u.BootstrapISO)
|
||||
vConfig.Timeout = u.Timeout
|
||||
|
||||
// only care about versions if we're not doing a manual rollback
|
||||
if !u.Data.Rollback {
|
||||
if err := validator.AssertVersion(op, vchConfig); err != nil {
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
}
|
||||
|
||||
if vchConfig, err = validator.ValidateMigratedConfig(op, vchConfig); err != nil {
|
||||
op.Errorf("Failed to migrate Virtual Container Host configuration %s", u.DisplayName)
|
||||
op.Error(err)
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
if !u.Data.Rollback {
|
||||
err = executor.Configure(vch, vchConfig, vConfig, false)
|
||||
} else {
|
||||
err = executor.Rollback(vch, vchConfig, vConfig)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// upgrade failed
|
||||
executor.CollectDiagnosticLogs()
|
||||
return errors.New("upgrade failed")
|
||||
}
|
||||
|
||||
op.Infof("Completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user