<commit_before>/*
Copyright 2019 The Knative Authors

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

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

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

package utils

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"

	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
)

const (
	BrokerConfigMapKey    = "bootstrapServers"
	KafkaChannelSeparator = "."

	// DefaultNumPartitions defines the default number of partitions
	DefaultNumPartitions = 1

	// DefaultReplicationFactor defines the default number of replications
	DefaultReplicationFactor = 1

	knativeKafkaTopicPrefix = "knative-messaging-kafka"

	DefaultMaxIdleConns        = 1000
	DefaultMaxIdleConnsPerHost = 100
)

var (
	firstKafkaConfigMapCall = true
)

type KafkaConfig struct {
	Brokers             []string
	MaxIdleConns        int
	MaxIdleConnsPerHost int
}

// GetKafkaConfig returns the details of the Kafka cluster.
func GetKafkaConfig(configMap map[string]string) (*KafkaConfig, error) {
	if len(configMap) == 0 {
		return nil, fmt.Errorf("missing configuration")
	}

	config := &KafkaConfig{}

	if brokers, ok := configMap[BrokerConfigMapKey]; ok {
		bootstrapServers := strings.Split(brokers, ",")
		for _, s := range bootstrapServers {
			if len(s) == 0 {
				return nil, fmt.Errorf("empty %s value in configuration", BrokerConfigMapKey)
			}
		}
		config.Brokers = bootstrapServers
	} else {
		return nil, fmt.Errorf("missing key %s in configuration", BrokerConfigMapKey)
	}

	if maxConns, ok := configMap["maxIdleConns"]; ok {
		mc, err := strconv.Atoi(maxConns)
		if err != nil {
			config.MaxIdleConns = DefaultMaxIdleConns
		}
		config.MaxIdleConns = mc
	} else {
		config.MaxIdleConns = DefaultMaxIdleConns
	}
	if maxConnsPerHost, ok := configMap["maxIdleConnsPerHost"]; ok {
		mcph, err := strconv.Atoi(maxConnsPerHost)
		if err != nil {
			config.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
		}
		config.MaxIdleConnsPerHost = mcph

	} else {
		config.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return config, nil
}

func TopicName(separator, namespace, name string) string {
	topic := []string{knativeKafkaTopicPrefix, namespace, name}
	return strings.Join(topic, separator)
}

// We skip the first call into KafkaConfigMapObserver because it is not an indication
// of change of the watched ConfigMap but the map's initial state. See the comment for
// knative.dev/pkg/configmap/watcher.Start()
func KafkaConfigMapObserver(logger *zap.SugaredLogger) func(configMap *corev1.ConfigMap) {
	return func(kafkaConfigMap *corev1.ConfigMap) {
		if firstKafkaConfigMapCall {
			firstKafkaConfigMapCall = false
		} else {
			logger.Info("Kafka broker configuration updated, restarting")
			syscall.Kill(syscall.Getpid(), syscall.SIGINT)
		}
	}
}
<commit_msg>Removed unused code (#1234)<commit_after>/*
Copyright 2019 The Knative Authors

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

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

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

package utils

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	BrokerConfigMapKey    = "bootstrapServers"
	KafkaChannelSeparator = "."

	// DefaultNumPartitions defines the default number of partitions
	DefaultNumPartitions = 1

	// DefaultReplicationFactor defines the default number of replications
	DefaultReplicationFactor = 1

	knativeKafkaTopicPrefix = "knative-messaging-kafka"

	DefaultMaxIdleConns        = 1000
	DefaultMaxIdleConnsPerHost = 100
)

var (
	firstKafkaConfigMapCall = true
)

type KafkaConfig struct {
	Brokers             []string
	MaxIdleConns        int
	MaxIdleConnsPerHost int
}

// GetKafkaConfig returns the details of the Kafka cluster.
func GetKafkaConfig(configMap map[string]string) (*KafkaConfig, error) {
	if len(configMap) == 0 {
		return nil, fmt.Errorf("missing configuration")
	}

	config := &KafkaConfig{}

	if brokers, ok := configMap[BrokerConfigMapKey]; ok {
		bootstrapServers := strings.Split(brokers, ",")
		for _, s := range bootstrapServers {
			if len(s) == 0 {
				return nil, fmt.Errorf("empty %s value in configuration", BrokerConfigMapKey)
			}
		}
		config.Brokers = bootstrapServers
	} else {
		return nil, fmt.Errorf("missing key %s in configuration", BrokerConfigMapKey)
	}

	if maxConns, ok := configMap["maxIdleConns"]; ok {
		mc, err := strconv.Atoi(maxConns)
		if err != nil {
			config.MaxIdleConns = DefaultMaxIdleConns
		}
		config.MaxIdleConns = mc
	} else {
		config.MaxIdleConns = DefaultMaxIdleConns
	}
	if maxConnsPerHost, ok := configMap["maxIdleConnsPerHost"]; ok {
		mcph, err := strconv.Atoi(maxConnsPerHost)
		if err != nil {
			config.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
		}
		config.MaxIdleConnsPerHost = mcph

	} else {
		config.MaxIdleConnsPerHost = DefaultMaxIdleConnsPerHost
	}

	return config, nil
}

func TopicName(separator, namespace, name string) string {
	topic := []string{knativeKafkaTopicPrefix, namespace, name}
	return strings.Join(topic, separator)
}
<|endoftext|>


<commit_before>package transit

import (
	"context"
	"crypto/elliptic"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"strconv"
	"time"

	"golang.org/x/crypto/ed25519"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/helper/keysutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func (b *backend) pathListKeys() *framework.Path {
	return &framework.Path{
		Pattern: "keys/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathKeysList,
		},

		HelpSynopsis:    pathPolicyHelpSyn,
		HelpDescription: pathPolicyHelpDesc,
	}
}

func (b *backend) pathKeys() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of the key",
			},

			"type": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "aes256-gcm96",
				Description: `
The type of key to create. Currently, "aes256-gcm96" (symmetric), "ecdsa-p256"
(asymmetric), 'ed25519' (asymmetric), 'rsa-2048' (asymmetric), 'rsa-4096'
(asymmetric) are supported.  Defaults to "aes256-gcm96".
`,
			},

			"derived": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables key derivation mode. This
allows for per-transaction unique
keys for encryption operations.`,
			},

			"convergent_encryption": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Whether to support convergent encryption.
This is only supported when using a key with
key derivation enabled and will require all
requests to carry both a context and 96-bit
(12-byte) nonce. The given nonce will be used
in place of a randomly generated nonce. As a
result, when the same context and nonce are
supplied, the same ciphertext is generated. It
is *very important* when using this mode that
you ensure that all nonces are unique for a
given context. Failing to do so will severely
impact the ciphertext's security.`,
			},

			"exportable": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables keys to be exportable.
This allows for all the valid keys
in the key ring to be exported.`,
			},

			"allow_plaintext_backup": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables taking a backup of the named
key in plaintext format. Once set,
this cannot be disabled.`,
			},

			"context": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Base64 encoded context for key derivation.
When reading a key with key derivation enabled,
if the key type supports public keys, this will
return the public key for the given context.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathPolicyWrite,
			logical.DeleteOperation: b.pathPolicyDelete,
			logical.ReadOperation:   b.pathPolicyRead,
		},

		HelpSynopsis:    pathPolicyHelpSyn,
		HelpDescription: pathPolicyHelpDesc,
	}
}

func (b *backend) pathKeysList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List(ctx, "policy/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	derived := d.Get("derived").(bool)
	convergent := d.Get("convergent_encryption").(bool)
	keyType := d.Get("type").(string)
	exportable := d.Get("exportable").(bool)
	allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)

	if !derived && convergent {
		return logical.ErrorResponse("convergent encryption requires derivation to be enabled"), nil
	}

	polReq := keysutil.PolicyRequest{
		Storage:              req.Storage,
		Name:                 name,
		Derived:              derived,
		Convergent:           convergent,
		Exportable:           exportable,
		AllowPlaintextBackup: allowPlaintextBackup,
	}
	switch keyType {
	case "aes256-gcm96":
		polReq.KeyType = keysutil.KeyType_AES256_GCM96
	case "ecdsa-p256":
		polReq.KeyType = keysutil.KeyType_ECDSA_P256
	case "ed25519":
		polReq.KeyType = keysutil.KeyType_ED25519
	case "rsa-2048":
		polReq.KeyType = keysutil.KeyType_RSA2048
	case "rsa-4096":
		polReq.KeyType = keysutil.KeyType_RSA4096
	default:
		return logical.ErrorResponse(fmt.Sprintf("unknown key type %v", keyType)), logical.ErrInvalidRequest
	}

	p, lock, upserted, err := b.lm.GetPolicyUpsert(ctx, polReq)
	if lock != nil {
		defer lock.RUnlock()
	}
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, fmt.Errorf("error generating key: returned policy was nil")
	}

	resp := &logical.Response{}
	if !upserted {
		resp.AddWarning(fmt.Sprintf("key %s already existed", name))
	}

	return nil, nil
}

// Built-in helper type for returning asymmetric keys
type asymKey struct {
	Name         string    `json:"name" structs:"name" mapstructure:"name"`
	PublicKey    string    `json:"public_key" structs:"public_key" mapstructure:"public_key"`
	CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"creation_time"`
}

func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)

	p, lock, err := b.lm.GetPolicyShared(ctx, req.Storage, name)
	if lock != nil {
		defer lock.RUnlock()
	}
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, nil
	}

	// Return the response
	resp := &logical.Response{
		Data: map[string]interface{}{
			"name":                   p.Name,
			"type":                   p.Type.String(),
			"derived":                p.Derived,
			"deletion_allowed":       p.DeletionAllowed,
			"min_decryption_version": p.MinDecryptionVersion,
			"min_encryption_version": p.MinEncryptionVersion,
			"latest_version":         p.LatestVersion,
			"exportable":             p.Exportable,
			"allow_plaintext_backup": p.AllowPlaintextBackup,
			"supports_encryption":    p.Type.EncryptionSupported(),
			"supports_decryption":    p.Type.DecryptionSupported(),
			"supports_signing":       p.Type.SigningSupported(),
			"supports_derivation":    p.Type.DerivationSupported(),
			"backup_info":            p.BackupInfo,
			"restore_info":           p.RestoreInfo,
		},
	}

	if p.Derived {
		switch p.KDF {
		case keysutil.Kdf_hmac_sha256_counter:
			resp.Data["kdf"] = "hmac-sha256-counter"
			resp.Data["kdf_mode"] = "hmac-sha256-counter"
		case keysutil.Kdf_hkdf_sha256:
			resp.Data["kdf"] = "hkdf_sha256"
		}
		resp.Data["convergent_encryption"] = p.ConvergentEncryption
		if p.ConvergentEncryption {
			resp.Data["convergent_encryption_version"] = p.ConvergentVersion
		}
	}

	contextRaw := d.Get("context").(string)
	var context []byte
	if len(contextRaw) != 0 {
		context, err = base64.StdEncoding.DecodeString(contextRaw)
		if err != nil {
			return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
		}
	}

	switch p.Type {
	case keysutil.KeyType_AES256_GCM96:
		retKeys := map[string]int64{}
		for k, v := range p.Keys {
			retKeys[k] = v.DeprecatedCreationTime
		}
		resp.Data["keys"] = retKeys

	case keysutil.KeyType_ECDSA_P256, keysutil.KeyType_ED25519, keysutil.KeyType_RSA2048, keysutil.KeyType_RSA4096:
		retKeys := map[string]map[string]interface{}{}
		for k, v := range p.Keys {
			key := asymKey{
				PublicKey:    v.FormattedPublicKey,
				CreationTime: v.CreationTime,
			}
			if key.CreationTime.IsZero() {
				key.CreationTime = time.Unix(v.DeprecatedCreationTime, 0)
			}

			switch p.Type {
			case keysutil.KeyType_ECDSA_P256:
				key.Name = elliptic.P256().Params().Name
			case keysutil.KeyType_ED25519:
				if p.Derived {
					if len(context) == 0 {
						key.PublicKey = ""
					} else {
						ver, err := strconv.Atoi(k)
						if err != nil {
							return nil, fmt.Errorf("invalid version %q: %v", k, err)
						}
						derived, err := p.DeriveKey(context, ver)
						if err != nil {
							return nil, fmt.Errorf("failed to derive key to return public component")
						}
						pubKey := ed25519.PrivateKey(derived).Public().(ed25519.PublicKey)
						key.PublicKey = base64.StdEncoding.EncodeToString(pubKey)
					}
				}
				key.Name = "ed25519"
			case keysutil.KeyType_RSA2048, keysutil.KeyType_RSA4096:
				key.Name = "rsa-2048"
				if p.Type == keysutil.KeyType_RSA4096 {
					key.Name = "rsa-4096"
				}

				// Encode the RSA public key in PEM format to return over the
				// API
				derBytes, err := x509.MarshalPKIXPublicKey(v.RSAKey.Public())
				if err != nil {
					return nil, fmt.Errorf("error marshaling RSA public key: %v", err)
				}
				pemBlock := &pem.Block{
					Type:  "PUBLIC KEY",
					Bytes: derBytes,
				}
				pemBytes := pem.EncodeToMemory(pemBlock)
				if pemBytes == nil || len(pemBytes) == 0 {
					return nil, fmt.Errorf("failed to PEM-encode RSA public key")
				}
				key.PublicKey = string(pemBytes)
			}

			retKeys[k] = structs.New(key).Map()
		}
		resp.Data["keys"] = retKeys
	}

	return resp, nil
}

func (b *backend) pathPolicyDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)

	// Delete does its own locking
	err := b.lm.DeletePolicy(ctx, req.Storage, name)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("error deleting policy %s: %s", name, err)), err
	}

	return nil, nil
}

const pathPolicyHelpSyn = `Managed named encryption keys`

const pathPolicyHelpDesc = `
This path is used to manage the named keys that are available.
Doing a write with no value against a new named key will create
it using a randomly generated key.
`
<commit_msg>Fix auditing for transit keys with backup/restore info (#3919)<commit_after>package transit

import (
	"context"
	"crypto/elliptic"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"strconv"
	"time"

	"golang.org/x/crypto/ed25519"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/helper/keysutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func (b *backend) pathListKeys() *framework.Path {
	return &framework.Path{
		Pattern: "keys/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathKeysList,
		},

		HelpSynopsis:    pathPolicyHelpSyn,
		HelpDescription: pathPolicyHelpDesc,
	}
}

func (b *backend) pathKeys() *framework.Path {
	return &framework.Path{
		Pattern: "keys/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of the key",
			},

			"type": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "aes256-gcm96",
				Description: `
The type of key to create. Currently, "aes256-gcm96" (symmetric), "ecdsa-p256"
(asymmetric), 'ed25519' (asymmetric), 'rsa-2048' (asymmetric), 'rsa-4096'
(asymmetric) are supported.  Defaults to "aes256-gcm96".
`,
			},

			"derived": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables key derivation mode. This
allows for per-transaction unique
keys for encryption operations.`,
			},

			"convergent_encryption": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Whether to support convergent encryption.
This is only supported when using a key with
key derivation enabled and will require all
requests to carry both a context and 96-bit
(12-byte) nonce. The given nonce will be used
in place of a randomly generated nonce. As a
result, when the same context and nonce are
supplied, the same ciphertext is generated. It
is *very important* when using this mode that
you ensure that all nonces are unique for a
given context. Failing to do so will severely
impact the ciphertext's security.`,
			},

			"exportable": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables keys to be exportable.
This allows for all the valid keys
in the key ring to be exported.`,
			},

			"allow_plaintext_backup": &framework.FieldSchema{
				Type: framework.TypeBool,
				Description: `Enables taking a backup of the named
key in plaintext format. Once set,
this cannot be disabled.`,
			},

			"context": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Base64 encoded context for key derivation.
When reading a key with key derivation enabled,
if the key type supports public keys, this will
return the public key for the given context.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathPolicyWrite,
			logical.DeleteOperation: b.pathPolicyDelete,
			logical.ReadOperation:   b.pathPolicyRead,
		},

		HelpSynopsis:    pathPolicyHelpSyn,
		HelpDescription: pathPolicyHelpDesc,
	}
}

func (b *backend) pathKeysList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List(ctx, "policy/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathPolicyWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)
	derived := d.Get("derived").(bool)
	convergent := d.Get("convergent_encryption").(bool)
	keyType := d.Get("type").(string)
	exportable := d.Get("exportable").(bool)
	allowPlaintextBackup := d.Get("allow_plaintext_backup").(bool)

	if !derived && convergent {
		return logical.ErrorResponse("convergent encryption requires derivation to be enabled"), nil
	}

	polReq := keysutil.PolicyRequest{
		Storage:              req.Storage,
		Name:                 name,
		Derived:              derived,
		Convergent:           convergent,
		Exportable:           exportable,
		AllowPlaintextBackup: allowPlaintextBackup,
	}
	switch keyType {
	case "aes256-gcm96":
		polReq.KeyType = keysutil.KeyType_AES256_GCM96
	case "ecdsa-p256":
		polReq.KeyType = keysutil.KeyType_ECDSA_P256
	case "ed25519":
		polReq.KeyType = keysutil.KeyType_ED25519
	case "rsa-2048":
		polReq.KeyType = keysutil.KeyType_RSA2048
	case "rsa-4096":
		polReq.KeyType = keysutil.KeyType_RSA4096
	default:
		return logical.ErrorResponse(fmt.Sprintf("unknown key type %v", keyType)), logical.ErrInvalidRequest
	}

	p, lock, upserted, err := b.lm.GetPolicyUpsert(ctx, polReq)
	if lock != nil {
		defer lock.RUnlock()
	}
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, fmt.Errorf("error generating key: returned policy was nil")
	}

	resp := &logical.Response{}
	if !upserted {
		resp.AddWarning(fmt.Sprintf("key %s already existed", name))
	}

	return nil, nil
}

// Built-in helper type for returning asymmetric keys
type asymKey struct {
	Name         string    `json:"name" structs:"name" mapstructure:"name"`
	PublicKey    string    `json:"public_key" structs:"public_key" mapstructure:"public_key"`
	CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"creation_time"`
}

func (b *backend) pathPolicyRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)

	p, lock, err := b.lm.GetPolicyShared(ctx, req.Storage, name)
	if lock != nil {
		defer lock.RUnlock()
	}
	if err != nil {
		return nil, err
	}
	if p == nil {
		return nil, nil
	}

	// Return the response
	resp := &logical.Response{
		Data: map[string]interface{}{
			"name":                   p.Name,
			"type":                   p.Type.String(),
			"derived":                p.Derived,
			"deletion_allowed":       p.DeletionAllowed,
			"min_decryption_version": p.MinDecryptionVersion,
			"min_encryption_version": p.MinEncryptionVersion,
			"latest_version":         p.LatestVersion,
			"exportable":             p.Exportable,
			"allow_plaintext_backup": p.AllowPlaintextBackup,
			"supports_encryption":    p.Type.EncryptionSupported(),
			"supports_decryption":    p.Type.DecryptionSupported(),
			"supports_signing":       p.Type.SigningSupported(),
			"supports_derivation":    p.Type.DerivationSupported(),
		},
	}

	if p.BackupInfo != nil {
		resp.Data["backup_info"] = map[string]interface{}{
			"time":    p.BackupInfo.Time,
			"version": p.BackupInfo.Version,
		}
	}
	if p.RestoreInfo != nil {
		resp.Data["restore_info"] = map[string]interface{}{
			"time":    p.RestoreInfo.Time,
			"version": p.RestoreInfo.Version,
		}
	}

	if p.Derived {
		switch p.KDF {
		case keysutil.Kdf_hmac_sha256_counter:
			resp.Data["kdf"] = "hmac-sha256-counter"
			resp.Data["kdf_mode"] = "hmac-sha256-counter"
		case keysutil.Kdf_hkdf_sha256:
			resp.Data["kdf"] = "hkdf_sha256"
		}
		resp.Data["convergent_encryption"] = p.ConvergentEncryption
		if p.ConvergentEncryption {
			resp.Data["convergent_encryption_version"] = p.ConvergentVersion
		}
	}

	contextRaw := d.Get("context").(string)
	var context []byte
	if len(contextRaw) != 0 {
		context, err = base64.StdEncoding.DecodeString(contextRaw)
		if err != nil {
			return logical.ErrorResponse("failed to base64-decode context"), logical.ErrInvalidRequest
		}
	}

	switch p.Type {
	case keysutil.KeyType_AES256_GCM96:
		retKeys := map[string]int64{}
		for k, v := range p.Keys {
			retKeys[k] = v.DeprecatedCreationTime
		}
		resp.Data["keys"] = retKeys

	case keysutil.KeyType_ECDSA_P256, keysutil.KeyType_ED25519, keysutil.KeyType_RSA2048, keysutil.KeyType_RSA4096:
		retKeys := map[string]map[string]interface{}{}
		for k, v := range p.Keys {
			key := asymKey{
				PublicKey:    v.FormattedPublicKey,
				CreationTime: v.CreationTime,
			}
			if key.CreationTime.IsZero() {
				key.CreationTime = time.Unix(v.DeprecatedCreationTime, 0)
			}

			switch p.Type {
			case keysutil.KeyType_ECDSA_P256:
				key.Name = elliptic.P256().Params().Name
			case keysutil.KeyType_ED25519:
				if p.Derived {
					if len(context) == 0 {
						key.PublicKey = ""
					} else {
						ver, err := strconv.Atoi(k)
						if err != nil {
							return nil, fmt.Errorf("invalid version %q: %v", k, err)
						}
						derived, err := p.DeriveKey(context, ver)
						if err != nil {
							return nil, fmt.Errorf("failed to derive key to return public component")
						}
						pubKey := ed25519.PrivateKey(derived).Public().(ed25519.PublicKey)
						key.PublicKey = base64.StdEncoding.EncodeToString(pubKey)
					}
				}
				key.Name = "ed25519"
			case keysutil.KeyType_RSA2048, keysutil.KeyType_RSA4096:
				key.Name = "rsa-2048"
				if p.Type == keysutil.KeyType_RSA4096 {
					key.Name = "rsa-4096"
				}

				// Encode the RSA public key in PEM format to return over the
				// API
				derBytes, err := x509.MarshalPKIXPublicKey(v.RSAKey.Public())
				if err != nil {
					return nil, fmt.Errorf("error marshaling RSA public key: %v", err)
				}
				pemBlock := &pem.Block{
					Type:  "PUBLIC KEY",
					Bytes: derBytes,
				}
				pemBytes := pem.EncodeToMemory(pemBlock)
				if pemBytes == nil || len(pemBytes) == 0 {
					return nil, fmt.Errorf("failed to PEM-encode RSA public key")
				}
				key.PublicKey = string(pemBytes)
			}

			retKeys[k] = structs.New(key).Map()
		}
		resp.Data["keys"] = retKeys
	}

	return resp, nil
}

func (b *backend) pathPolicyDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	name := d.Get("name").(string)

	// Delete does its own locking
	err := b.lm.DeletePolicy(ctx, req.Storage, name)
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("error deleting policy %s: %s", name, err)), err
	}

	return nil, nil
}

const pathPolicyHelpSyn = `Managed named encryption keys`

const pathPolicyHelpDesc = `
This path is used to manage the named keys that are available.
Doing a write with no value against a new named key will create
it using a randomly generated key.
`
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 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 routing

import (
	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
)

func RegisterRoutes(e *gin.Engine) {
	e.GET("", func(c *gin.Context) {
		c.String(200, "pufferd is running")
	})
	e.GET("/", func(c *gin.Context) {
		c.String(200, "pufferd is running")
	})
	e.GET("/templates", GetTemplates)
	e.GET("_shutdown", httphandlers.OAuth2Handler, Shutdown)
}

func Shutdown(c *gin.Context) {
	if !hasScope(c, "node.stop") {
		c.AbortWithStatus(401)
		return
	}

	for _, element := range programs.GetAll() {
		running := element.IsRunning()
		if running {
			logging.Info("Stopping program " + element.Id())
			element.Stop()
		}
	}
	manners.Close()
}

func GetTemplates(c *gin.Context) {
	c.JSON(200, programs.GetPlugins())
}

func hasScope(gin *gin.Context, scope string) bool {
	scopes, _ := gin.Get("scopes")
	return utils.ContainsValue(scopes.([]string), scope)
}
<commit_msg>Gin considers "" and / to be same.... but does not treat them the same<commit_after>/*
 Copyright 2016 Padduck, LLC

 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 routing

import (
	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
)

func RegisterRoutes(e *gin.Engine) {
	e.GET("", func(c *gin.Context) {
		c.String(200, "pufferd is running")
	})
	e.GET("/templates", GetTemplates)
	e.GET("_shutdown", httphandlers.OAuth2Handler, Shutdown)
}

func Shutdown(c *gin.Context) {
	if !hasScope(c, "node.stop") {
		c.AbortWithStatus(401)
		return
	}

	for _, element := range programs.GetAll() {
		running := element.IsRunning()
		if running {
			logging.Info("Stopping program " + element.Id())
			element.Stop()
		}
	}
	manners.Close()
}

func GetTemplates(c *gin.Context) {
	c.JSON(200, programs.GetPlugins())
}

func hasScope(gin *gin.Context, scope string) bool {
	scopes, _ := gin.Get("scopes")
	return utils.ContainsValue(scopes.([]string), scope)
}
<|endoftext|>


<commit_before>package db

import "github.com/pkg/errors"

func (vdb *TemporaryEmail) LoadByUserIDAndConfirmationKey(tx *Tx, userID, confirmationKey string) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`SELECT `)
	stmt.WriteString(TemporaryEmailStdSelectColumns)
	stmt.WriteString(` FROM `)
	stmt.WriteString(TemporaryEmailTable)
	stmt.WriteString(` WHERE user_id = ? AND confirmation_key = ?`)

	row := tx.QueryRow(stmt.String(), userID, confirmationKey)
	if err := vdb.Scan(row); err != nil {
		return errors.Wrap(err, "failed to execute query")
	}

	return nil
}

func (vdb *TemporaryEmail) Upsert(tx *Tx) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`INSERT INTO `)
	stmt.WriteString(TemporaryEmailTable)
	stmt.WriteString(` (user_id, confirmation_key, email, expires_on) VALUES (?, ?, ?, ?) `)
	stmt.WriteString(` ON DUPLICATE KEY UPDATE confirmation_key = VALUES(confirmation_key), expires_on = VALUES(expires_on)`)

	row := tx.QueryRow(stmt.String(), vdb.UserID, vdb.ConfirmationKey, vdb.Email, vdb.ExpiresOn)
	if err := vdb.Scan(row); err != nil {
		return errors.Wrap(err, "failed to execute query")
	}

	return nil
}
<commit_msg>oops, this was an insert operation<commit_after>package db

import "github.com/pkg/errors"

func (vdb *TemporaryEmail) LoadByUserIDAndConfirmationKey(tx *Tx, userID, confirmationKey string) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`SELECT `)
	stmt.WriteString(TemporaryEmailStdSelectColumns)
	stmt.WriteString(` FROM `)
	stmt.WriteString(TemporaryEmailTable)
	stmt.WriteString(` WHERE user_id = ? AND confirmation_key = ?`)

	row := tx.QueryRow(stmt.String(), userID, confirmationKey)
	if err := vdb.Scan(row); err != nil {
		return errors.Wrap(err, "failed to execute query")
	}

	return nil
}

func (vdb *TemporaryEmail) Upsert(tx *Tx) error {
	stmt := getStmtBuf()
	defer releaseStmtBuf(stmt)

	stmt.WriteString(`INSERT INTO `)
	stmt.WriteString(TemporaryEmailTable)
	stmt.WriteString(` (user_id, confirmation_key, email, expires_on) VALUES (?, ?, ?, ?) `)
	stmt.WriteString(` ON DUPLICATE KEY UPDATE confirmation_key = VALUES(confirmation_key), expires_on = VALUES(expires_on)`)

	result, err := tx.Exec(stmt.String(), vdb.UserID, vdb.ConfirmationKey, vdb.Email, vdb.ExpiresOn)
	if err != nil {
		return err
	}

	lii, err := result.LastInsertId()
	if err != nil {
		return err
	}

	vdb.OID = lii
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The NorthShore Authors 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 blueprint

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/boltdb/bolt"

	"github.com/Mirantis/northshore/store"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/go-connections/nat"
	"github.com/satori/go.uuid"
	"github.com/spf13/viper"

	"golang.org/x/net/context"
)

// Stage represents a Blueprint Stage
type Stage struct {
	//Docker image for bootstrap stage
	Image       string `json:"image"`
	Description string `json:"description"`
	//Ports for exposing to host
	Ports []map[string]string `json:"ports"`
	//Environment variables
	Variables map[string]string `json:"variables"`
	//Provisioner type (docker/...)
	Provisioner string `json:"provisioner"`
	// State is current Blueprint status
	State StageState `json:"state"`
}

// Blueprint represents a Blueprint
type Blueprint struct {
	//API version for processing blueprint
	Version string `json:"version"`
	//Type of blueprint (pipeline/application)
	Type   string           `json:"type"`
	Name   string           `json:"name"`
	Stages map[string]Stage `json:"stages"`
	// State is current Blueprint status
	State State     `json:"state"`
	ID    uuid.UUID `json:"id"`
}

// State represents a state of the Blueprint
type State string

// StageState represents a state of the Stage
type StageState string

const (
	// StateNew is default state of the Blueprint
	StateNew State = "new"
	// StateProvision is the Blueprint status while provisioning
	StateProvision State = "provision"
	// StateActive is the Blueprint status when all Stages are up and ready
	StateActive State = "active"
	// StateInactive is the Blueprint status when some Stage is down
	StateInactive State = "inactive"
)

const (
	// StageStateNew is default state of the Stage
	StageStateNew StageState = "new"
	// StageStateCreated indicates that container is created
	StageStateCreated StageState = "created"
	// StageStateRunning indicates that container is running
	StageStateRunning StageState = "running"
	// StageStatePaused indicates that container is paused
	StageStatePaused StageState = "paused"
	// StageStateStopped indicates that container is stopped
	StageStateStopped StageState = "stopped"
	// StageStateDeleted indicates that container is deleted
	StageStateDeleted StageState = "deleted"
)

const (
	// DBBucketWatcher defines boltdb bucket for Watcher
	DBBucketWatcher = "Northshore"

	// DBKeyWatcher defines boltdb key for Watcher
	DBKeyWatcher = "containers"

	// DBBucket defines boltdb bucket for blueprints
	DBBucket = "blueprints"
)

// GetID implements `api2go.MarshalIdentifier` interface
func (bp Blueprint) GetID() string {
	return bp.ID.String()
}

// SetID implements `api2go.UnmarshalIdentifier` interface
func (bp *Blueprint) SetID(id string) (err error) {
	bp.ID, err = uuid.FromString(id)
	return
}

// Run creates and starts Docker containers
func (bp *Blueprint) Run() {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	ids := []string{}

	for name, stage := range bp.Stages {
		bindings := make(map[nat.Port][]nat.PortBinding)
		for _, ports := range stage.Ports {
			port, _ := nat.NewPort("tcp", ports["fromPort"])
			bindings[port] = []nat.PortBinding{nat.PortBinding{HostIP: "0.0.0.0", HostPort: ports["toPort"]}}
		}

		hostConfig := container.HostConfig{
			PortBindings: bindings,
		}

		config := container.Config{
			Image: bp.Stages[name].Image,
		}
		log.Printf("%s -> Config was built.", name)

		r, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, nil, name)
		if err != nil && strings.Contains(err.Error(), "No such image") {
			log.Println(err)
			log.Println("Start pulling process...")
			rc, e := cli.ImagePull(context.Background(), config.Image, types.ImagePullOptions{})
			if e != nil {
				log.Println(e)
			}
			//TODO: add pretty print of pulling process
			_, re := ioutil.ReadAll(rc)
			if re != nil {
				log.Println(re)
			}
			rc.Close()
			r, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, nil, name)
		}
		if err != nil {
			log.Println(err)
			continue
		}
		log.Printf("%s -> Container was created.", name)
		ids = append(ids, r.ID)

		err = cli.ContainerStart(
			context.Background(),
			r.ID,
			types.ContainerStartOptions{})
		if err != nil {
			log.Println(err)
			continue
		}
		log.Printf("%s -> Container was started.", name)
		log.Printf("%s -> Container ID  %s", name, r.ID)
		log.Printf("%s -> Warnings: %s", name, r.Warnings)
	}
	if len(ids) > 0 {
		//Update list of containers in DB
		//TODO add ability to add one container
		res := strings.Join(ids[:], ",")
		store.Save([]byte(DBBucketWatcher), []byte(DBKeyWatcher), res)
	}
}

// Delete deletes blueprint with containers
func (bp *Blueprint) Delete() error {
	// TODO: stop and remove containers
	log.Debugln("#blueprint,#DeleteBlueprint")

	return store.Delete([]byte(DBBucket), []byte(bp.ID.String()))
}

// Save stores blueprint in db
func (bp *Blueprint) Save() error {
	bp.updateStates()
	zerouuid := uuid.UUID{}
	if bp.ID == zerouuid {
		bp.ID = uuid.NewV4()
	}

	return store.Save([]byte(DBBucket), []byte(bp.ID.String()), bp)
}

func (bp *Blueprint) updateStates() {
	/* Set stage as StageStateNew if unknown */
	for i, s := range bp.Stages {
		switch s.State {
		case
			StageStateNew,
			StageStateCreated,
			StageStateRunning,
			StageStatePaused,
			StageStateStopped,
			StageStateDeleted:
			break
		default:
			s.State = StageStateNew
			bp.Stages[i] = s
		}
	}

	bpState := StateNew

	for _, s := range bp.Stages {
		if s.State == StageStateRunning {
			bpState = StateActive
			break
		}
	}
LookProvision:
	for _, s := range bp.Stages {
		switch s.State {
		case
			StageStateNew:
			if bpState == StateActive {
				bpState = StateProvision
				break LookProvision
			}
		case
			StageStateCreated:
			bpState = StateProvision
			break LookProvision
		}
	}
LookInactive:
	for _, s := range bp.Stages {
		switch s.State {
		case
			StageStateDeleted,
			StageStatePaused,
			StageStateStopped:
			bpState = StateInactive
			break LookInactive
		}
	}

	bp.State = bpState
}

// ParseFile parses and validates the incoming data
func ParseFile(path string) (bp Blueprint, err error) {
	v := viper.New()
	v.SetConfigFile(path)
	err = v.ReadInConfig()
	if err != nil {
		return bp, fmt.Errorf("Config not found. %s", err)
	}

	err = v.Unmarshal(&bp)
	if err != nil {
		return bp, fmt.Errorf("Unable to decode into struct, %v", err)
	}

	return bp, nil
}

// ParseBytes parses and validates the incoming data
func ParseBytes(b []byte) (bp Blueprint, err error) {
	v := viper.New()
	v.SetConfigType("yaml")
	err = v.ReadConfig(bytes.NewBuffer(b))
	if err != nil {
		return bp, fmt.Errorf("Config not found. %s", err)
	}

	err = v.Unmarshal(&bp)
	if err != nil {
		return bp, fmt.Errorf("Unable to decode into struct, %v", err)
	}

	return bp, nil
}

// LoadAll loads stored blueprints
func LoadAll() (items []Blueprint, err error) {
	db := store.OpenDBBucket([]byte(DBBucket))
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DBBucket))
		items = make([]Blueprint, 0, b.Stats().KeyN)

		log.Debugln("#blueprint,#LoadAll", b.Stats().KeyN, len(items))

		b.ForEach(func(k, v []byte) error {
			item := new(Blueprint)
			if err = json.Unmarshal(v, item); err != nil {
				log.Errorln("#DB,#LoadBucket", err)
				return err
			}
			items = append(items, *item)

			log.Debugln("#blueprint,#LoadAll,##", string(k), len(items))
			return nil
		})

		return nil
	})

	return items, err
}

// Blueprints represents Storable collection
type Blueprints []Blueprint

// Bucket implements Storable interface
func (items *Blueprints) Bucket() []byte {
	return []byte(DBBucket)
}

// Next implements Storable interface
func (items *Blueprints) Next() interface{} {
	item := new(Blueprint)
	*items = append(*items, *item)
	l := len(*items)
	return &(*items)[l-1]
}

// Prepare implements Storable interface
func (items *Blueprints) Prepare(len int) {
	*items = make([]Blueprint, 0, len)
}
<commit_msg>Add (*Blueprint)UnmarshalJSON to check the typed unmarshaling<commit_after>// Copyright 2016 The NorthShore Authors 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 blueprint

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/boltdb/bolt"

	"github.com/Mirantis/northshore/store"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"github.com/docker/go-connections/nat"
	"github.com/satori/go.uuid"
	"github.com/spf13/viper"

	"golang.org/x/net/context"
)

// Stage represents a Blueprint Stage
type Stage struct {
	//Docker image for bootstrap stage
	Image       string `json:"image"`
	Description string `json:"description"`
	//Ports for exposing to host
	Ports []map[string]string `json:"ports"`
	//Environment variables
	Variables map[string]string `json:"variables"`
	//Provisioner type (docker/...)
	Provisioner string `json:"provisioner"`
	// State is current Blueprint status
	State StageState `json:"state"`
}

// Blueprint represents a Blueprint
type Blueprint struct {
	//API version for processing blueprint
	Version string `json:"version"`
	//Type of blueprint (pipeline/application)
	Type   string           `json:"type"`
	Name   string           `json:"name"`
	Stages map[string]Stage `json:"stages"`
	// State is current Blueprint status
	State State     `json:"state"`
	ID    uuid.UUID `json:"id"`
}

// State represents a state of the Blueprint
type State string

// StageState represents a state of the Stage
type StageState string

const (
	// StateNew is default state of the Blueprint
	StateNew State = "new"
	// StateProvision is the Blueprint status while provisioning
	StateProvision State = "provision"
	// StateActive is the Blueprint status when all Stages are up and ready
	StateActive State = "active"
	// StateInactive is the Blueprint status when some Stage is down
	StateInactive State = "inactive"
)

const (
	// StageStateNew is default state of the Stage
	StageStateNew StageState = "new"
	// StageStateCreated indicates that container is created
	StageStateCreated StageState = "created"
	// StageStateRunning indicates that container is running
	StageStateRunning StageState = "running"
	// StageStatePaused indicates that container is paused
	StageStatePaused StageState = "paused"
	// StageStateStopped indicates that container is stopped
	StageStateStopped StageState = "stopped"
	// StageStateDeleted indicates that container is deleted
	StageStateDeleted StageState = "deleted"
)

const (
	// DBBucketWatcher defines boltdb bucket for Watcher
	DBBucketWatcher = "Northshore"

	// DBKeyWatcher defines boltdb key for Watcher
	DBKeyWatcher = "containers"

	// DBBucket defines boltdb bucket for blueprints
	DBBucket = "blueprints"
)

// GetID implements `api2go.MarshalIdentifier` interface
func (bp Blueprint) GetID() string {
	return bp.ID.String()
}

// SetID implements `api2go.UnmarshalIdentifier` interface
func (bp *Blueprint) SetID(id string) (err error) {
	bp.ID, err = uuid.FromString(id)
	return
}

// Run creates and starts Docker containers
func (bp *Blueprint) Run() {
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	ids := []string{}

	for name, stage := range bp.Stages {
		bindings := make(map[nat.Port][]nat.PortBinding)
		for _, ports := range stage.Ports {
			port, _ := nat.NewPort("tcp", ports["fromPort"])
			bindings[port] = []nat.PortBinding{nat.PortBinding{HostIP: "0.0.0.0", HostPort: ports["toPort"]}}
		}

		hostConfig := container.HostConfig{
			PortBindings: bindings,
		}

		config := container.Config{
			Image: bp.Stages[name].Image,
		}
		log.Printf("%s -> Config was built.", name)

		r, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, nil, name)
		if err != nil && strings.Contains(err.Error(), "No such image") {
			log.Println(err)
			log.Println("Start pulling process...")
			rc, e := cli.ImagePull(context.Background(), config.Image, types.ImagePullOptions{})
			if e != nil {
				log.Println(e)
			}
			//TODO: add pretty print of pulling process
			_, re := ioutil.ReadAll(rc)
			if re != nil {
				log.Println(re)
			}
			rc.Close()
			r, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, nil, name)
		}
		if err != nil {
			log.Println(err)
			continue
		}
		log.Printf("%s -> Container was created.", name)
		ids = append(ids, r.ID)

		err = cli.ContainerStart(
			context.Background(),
			r.ID,
			types.ContainerStartOptions{})
		if err != nil {
			log.Println(err)
			continue
		}
		log.Printf("%s -> Container was started.", name)
		log.Printf("%s -> Container ID  %s", name, r.ID)
		log.Printf("%s -> Warnings: %s", name, r.Warnings)
	}
	if len(ids) > 0 {
		//Update list of containers in DB
		//TODO add ability to add one container
		res := strings.Join(ids[:], ",")
		store.Save([]byte(DBBucketWatcher), []byte(DBKeyWatcher), res)
	}
}

// Delete deletes blueprint with containers
func (bp *Blueprint) Delete() error {
	// TODO: stop and remove containers
	log.Debugln("#blueprint,#DeleteBlueprint")

	return store.Delete([]byte(DBBucket), []byte(bp.ID.String()))
}

// Save stores blueprint in db
func (bp *Blueprint) Save() error {
	bp.updateStates()
	zerouuid := uuid.UUID{}
	if bp.ID == zerouuid {
		bp.ID = uuid.NewV4()
	}

	return store.Save([]byte(DBBucket), []byte(bp.ID.String()), bp)
}

func (bp *Blueprint) updateStates() {
	/* Set stage as StageStateNew if unknown */
	for i, s := range bp.Stages {
		switch s.State {
		case
			StageStateNew,
			StageStateCreated,
			StageStateRunning,
			StageStatePaused,
			StageStateStopped,
			StageStateDeleted:
			break
		default:
			s.State = StageStateNew
			bp.Stages[i] = s
		}
	}

	bpState := StateNew

	for _, s := range bp.Stages {
		if s.State == StageStateRunning {
			bpState = StateActive
			break
		}
	}
LookProvision:
	for _, s := range bp.Stages {
		switch s.State {
		case
			StageStateNew:
			if bpState == StateActive {
				bpState = StateProvision
				break LookProvision
			}
		case
			StageStateCreated:
			bpState = StateProvision
			break LookProvision
		}
	}
LookInactive:
	for _, s := range bp.Stages {
		switch s.State {
		case
			StageStateDeleted,
			StageStatePaused,
			StageStateStopped:
			bpState = StateInactive
			break LookInactive
		}
	}

	bp.State = bpState
}

// ParseFile parses and validates the incoming data
func ParseFile(path string) (bp Blueprint, err error) {
	v := viper.New()
	v.SetConfigFile(path)
	err = v.ReadInConfig()
	if err != nil {
		return bp, fmt.Errorf("Config not found. %s", err)
	}

	err = v.Unmarshal(&bp)
	if err != nil {
		return bp, fmt.Errorf("Unable to decode into struct, %v", err)
	}

	return bp, nil
}

// ParseBytes parses and validates the incoming data
func ParseBytes(b []byte) (bp Blueprint, err error) {
	v := viper.New()
	v.SetConfigType("yaml")
	err = v.ReadConfig(bytes.NewBuffer(b))
	if err != nil {
		return bp, fmt.Errorf("Config not found. %s", err)
	}

	err = v.Unmarshal(&bp)
	if err != nil {
		return bp, fmt.Errorf("Unable to decode into struct, %v", err)
	}

	return bp, nil
}

// LoadAll loads stored blueprints
func LoadAll() (items []Blueprint, err error) {
	db := store.OpenDBBucket([]byte(DBBucket))
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(DBBucket))
		items = make([]Blueprint, 0, b.Stats().KeyN)

		log.Debugln("#blueprint,#LoadAll", b.Stats().KeyN, len(items))

		b.ForEach(func(k, v []byte) error {
			item := new(Blueprint)
			if err = json.Unmarshal(v, item); err != nil {
				log.Errorln("#DB,#LoadBucket", err)
				return err
			}
			items = append(items, *item)

			log.Debugln("#blueprint,#LoadAll,##", string(k), len(items))
			return nil
		})

		return nil
	})

	return items, err
}

// Blueprints represents Storable collection
type Blueprints []Blueprint

// Bucket implements Storable interface
func (items *Blueprints) Bucket() []byte {
	return []byte(DBBucket)
}

// Next implements Storable interface
func (items *Blueprints) Next() interface{} {
	item := new(Blueprint)
	*items = append(*items, *item)
	l := len(*items)
	return &(*items)[l-1]
}

// Prepare implements Storable interface
func (items *Blueprints) Prepare(len int) {
	*items = make([]Blueprint, 0, len)
}

// Used to avoid recursion in UnmarshalJSON below
// Note at http://attilaolah.eu/2013/11/29/json-decoding-in-go/
type blueprint Blueprint

// UnmarshalJSON implements Unmarshaller interface
func (bp *Blueprint) UnmarshalJSON(b []byte) (err error) {
	log.Debugln("#Blueprint,#UnmarshalJSON")
	// return json.Unmarshal(b, bp) // stack owerflow

	buf := blueprint{}
	if err = json.Unmarshal(b, &buf); err == nil {
		*bp = Blueprint(buf)
	}
	return
}
<|endoftext|>


<commit_before>package proxy

import (
	"fmt"
	"io"
	"net"
	"net/http"

	"github.com/yryz/httpproxy/config"

	ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
	log "github.com/sirupsen/logrus"
)

type ProxyServer struct {
	ssServer string
	ssCipher *ss.Cipher
}

func NewProxyServer() *http.Server {
	p := &ProxyServer{
		ssServer: config.Conf.SsServer,
	}

	var err error
	p.ssCipher, err = ss.NewCipher(config.Conf.SsCipher, config.Conf.SsPassword)
	if err != nil {
		panic("init cipher error: " + err.Error())
	}

	return &http.Server{
		Addr:    config.Conf.Listen,
		Handler: p,
	}
}

func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
			log.Debugf("panic: %v\n", err)
		}
	}()

	if r.Method == "CONNECT" {
		p.HandleConnect(w, r)
	} else {
		p.HandleHttp(w, r)
	}
}

// 处理HTTPS、HTTP2代理请求
func (p *ProxyServer) HandleConnect(w http.ResponseWriter, r *http.Request) {
	log.Infof("%s %s", r.Method, r.Host)

	hj, _ := w.(http.Hijacker)
	conn, _, err := hj.Hijack()
	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	ssConn, err := ss.Dial(r.URL.Host, p.ssServer, p.ssCipher.Copy())
	if err != nil {
		log.Error("ss dial: ", err)
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))

	go ss.PipeThenClose(conn, ssConn, nil)
	ss.PipeThenClose(ssConn, conn, nil)
}

// 处理HTTP代理请求
func (p *ProxyServer) HandleHttp(w http.ResponseWriter, r *http.Request) {
	log.Infof("%s %s", r.Method, r.URL)

	// ss proxy
	tr := http.Transport{
		Dial: func(network, addr string) (net.Conn, error) {
			log.Infof("dial ss %v/%v", addr, network)
			return ss.Dial(addr, p.ssServer, p.ssCipher.Copy())
		},
	}

	// transport
	resp, err := tr.RoundTrip(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error("request error: ", err)
		return
	}
	defer resp.Body.Close()

	// copy headers
	for k, values := range resp.Header {
		for _, v := range values {
			w.Header().Add(k, v)
		}
	}

	// copy body
	n, err := io.Copy(w, resp.Body)
	if err != nil && err != io.EOF {
		log.Errorf("copy response body error: %v", err)
	}

	log.Infof("copied %v bytes from %v.", n, r.Host)
}
<commit_msg>修复HTTP 301 跳转HTTPS问题<commit_after>package proxy

import (
	"fmt"
	"io"
	"net"
	"net/http"

	"github.com/yryz/httpproxy/config"

	ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
	log "github.com/sirupsen/logrus"
)

type ProxyServer struct {
	ssServer string
	ssCipher *ss.Cipher
}

func NewProxyServer() *http.Server {
	p := &ProxyServer{
		ssServer: config.Conf.SsServer,
	}

	var err error
	p.ssCipher, err = ss.NewCipher(config.Conf.SsCipher, config.Conf.SsPassword)
	if err != nil {
		panic("init cipher error: " + err.Error())
	}

	return &http.Server{
		Addr:    config.Conf.Listen,
		Handler: p,
	}
}

func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
			log.Debugf("panic: %v\n", err)
		}
	}()

	if r.Method == "CONNECT" {
		p.HandleConnect(w, r)
	} else {
		p.HandleHttp(w, r)
	}
}

// 处理HTTPS、HTTP2代理请求
func (p *ProxyServer) HandleConnect(w http.ResponseWriter, r *http.Request) {
	log.Infof("%s %s", r.Method, r.Host)

	hj, _ := w.(http.Hijacker)
	conn, _, err := hj.Hijack()
	if err != nil {
		log.Error(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	ssConn, err := ss.Dial(r.URL.Host, p.ssServer, p.ssCipher.Copy())
	if err != nil {
		log.Error("ss dial: ", err)
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))

	go ss.PipeThenClose(conn, ssConn, nil)
	ss.PipeThenClose(ssConn, conn, nil)
}

// 处理HTTP代理请求
func (p *ProxyServer) HandleHttp(w http.ResponseWriter, r *http.Request) {
	log.Infof("%s %s", r.Method, r.URL)

	// ss proxy
	tr := http.Transport{
		Dial: func(network, addr string) (net.Conn, error) {
			log.Infof("dial ss %v/%v", addr, network)
			return ss.Dial(addr, p.ssServer, p.ssCipher.Copy())
		},
	}

	// transport
	resp, err := tr.RoundTrip(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Error("request error: ", err)
		return
	}
	defer resp.Body.Close()

	// copy headers
	for k, values := range resp.Header {
		for _, v := range values {
			w.Header().Add(k, v)
		}
	}
	w.WriteHeader(resp.StatusCode)

	// copy body
	n, err := io.Copy(w, resp.Body)
	if err != nil && err != io.EOF {
		log.Errorf("copy response body error: %v", err)
	}

	log.Infof("copied %v bytes from %v.", n, r.Host)
}
<|endoftext|>


<commit_before>package app_config

import (
	"encoding/json"
	"errors"
	"github.com/watermint/toolbox/essentials/log/es_log"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	ConfigFileName = "config.json"
)

var (
	ErrorValueNotFound = errors.New("value not found")
)

type Config interface {
	Get(key string) (v interface{}, err error)
	Put(key string, v interface{}) (err error)
	List() (settings map[string]interface{}, err error)
}

func NewConfig(path string) Config {
	return &configImpl{
		path: path,
	}
}

type configImpl struct {
	path string
}

func (z *configImpl) load() (values map[string]interface{}, err error) {
	values = make(map[string]interface{})
	l := es_log.Default()
	p := filepath.Join(z.path, ConfigFileName)

	_, err = os.Lstat(p)
	if err != nil {
		l.Debug("No file information; skip loading", es_log.Error(err))
		return values, nil
	}

	l.Debug("load config", es_log.String("path", p))
	b, err := ioutil.ReadFile(p)
	if err != nil {
		l.Debug("Unable to read config", es_log.Error(err))
		return
	}
	if err := json.Unmarshal(b, &values); err != nil {
		l.Debug("unable to unmarshal", es_log.Error(err))
		return values, err
	}
	return
}

func (z *configImpl) save(key string, v interface{}) (err error) {
	l := es_log.Default()
	p := filepath.Join(z.path, ConfigFileName)
	l.Debug("load config", es_log.String("path", p))
	values, err := z.load()
	if err != nil {
		return err
	}
	values[key] = v

	b, err := json.Marshal(values)
	if err != nil {
		l.Debug("Unable to marshal", es_log.Error(err))
		return err
	}
	if err := ioutil.WriteFile(p, b, 0644); err != nil {
		l.Debug("Unable to write config", es_log.Error(err))
		return err
	}
	return nil
}

func (z *configImpl) Get(key string) (v interface{}, err error) {
	if values, err := z.load(); err != nil {
		return nil, err
	} else if v, ok := values[key]; ok {
		return v, nil
	} else {
		return nil, ErrorValueNotFound
	}
}

func (z *configImpl) Put(key string, v interface{}) (err error) {
	return z.save(key, v)
}

func (z *configImpl) List() (settings map[string]interface{}, err error) {
	return z.load()
}
<commit_msg>#360 : always load/save on get/put<commit_after>package app_config

import (
	"encoding/json"
	"errors"
	"github.com/watermint/toolbox/essentials/log/es_log"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	ConfigFileName = "config.json"
)

var (
	ErrorValueNotFound = errors.New("value not found")
)

type Config interface {
	Get(key string) (v interface{}, err error)
	Put(key string, v interface{}) (err error)
	List() (settings map[string]interface{}, err error)
}

func NewConfig(path string) Config {
	return &configImpl{
		path: path,
	}
}

type configImpl struct {
	path string
}

func (z configImpl) load() (values map[string]interface{}, err error) {
	values = make(map[string]interface{})
	l := es_log.Default()
	p := filepath.Join(z.path, ConfigFileName)

	_, err = os.Lstat(p)
	if err != nil {
		l.Debug("No file information; skip loading", es_log.Error(err))
		return values, nil
	}

	l.Debug("load config", es_log.String("path", p))
	b, err := ioutil.ReadFile(p)
	if err != nil {
		l.Debug("Unable to read config", es_log.Error(err))
		return
	}
	if err := json.Unmarshal(b, &values); err != nil {
		l.Debug("unable to unmarshal", es_log.Error(err))
		return values, err
	}
	return
}

func (z configImpl) save(key string, v interface{}) (err error) {
	l := es_log.Default()
	p := filepath.Join(z.path, ConfigFileName)
	l.Debug("load config", es_log.String("path", p))
	values, err := z.load()
	if err != nil {
		return err
	}
	values[key] = v

	b, err := json.Marshal(values)
	if err != nil {
		l.Debug("Unable to marshal", es_log.Error(err))
		return err
	}
	if err := ioutil.WriteFile(p, b, 0644); err != nil {
		l.Debug("Unable to write config", es_log.Error(err))
		return err
	}
	return nil
}

func (z configImpl) Get(key string) (v interface{}, err error) {
	if values, err := z.load(); err != nil {
		return nil, err
	} else if v, ok := values[key]; ok {
		return v, nil
	} else {
		return nil, ErrorValueNotFound
	}
}

func (z configImpl) Put(key string, v interface{}) (err error) {
	return z.save(key, v)
}

func (z configImpl) List() (settings map[string]interface{}, err error) {
	return z.load()
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Original source: github.com/micro/go-micro/v3/selector/random/random.go

package random

import (
	"math/rand"

	"github.com/micro/micro/v3/internal/selector"
)

type random struct{}

func (r *random) Select(routes []string, opts ...selector.SelectOption) (selector.Next, error) {
	// we can't select from an empty pool of routes
	if len(routes) == 0 {
		return nil, selector.ErrNoneAvailable
	}

	// return the next func
	return func() string {
		// if there is only one route provided we'll select it
		if len(routes) == 1 {
			return routes[0]
		}

		// select a random route from the slice
		return routes[rand.Intn(len(routes)-1)]
	}, nil
}

func (r *random) Record(addr string, err error) error {
	return nil
}

func (r *random) Reset() error {
	return nil
}

func (r *random) String() string {
	return "random"
}

// NewSelector returns a random selector
func NewSelector(opts ...selector.Option) selector.Selector {
	return new(random)
}
<commit_msg>fix random selector to choose from full range of nodes (#1613)<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Original source: github.com/micro/go-micro/v3/selector/random/random.go

package random

import (
	"math/rand"

	"github.com/micro/micro/v3/internal/selector"
)

type random struct{}

func (r *random) Select(routes []string, opts ...selector.SelectOption) (selector.Next, error) {
	// we can't select from an empty pool of routes
	if len(routes) == 0 {
		return nil, selector.ErrNoneAvailable
	}

	// return the next func
	return func() string {
		// if there is only one route provided we'll select it
		if len(routes) == 1 {
			return routes[0]
		}

		// select a random route from the slice
		return routes[rand.Intn(len(routes))]
	}, nil
}

func (r *random) Record(addr string, err error) error {
	return nil
}

func (r *random) Reset() error {
	return nil
}

func (r *random) String() string {
	return "random"
}

// NewSelector returns a random selector
func NewSelector(opts ...selector.Option) selector.Selector {
	return new(random)
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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 media

import (
	"testing"
	"time"

	"barista.run/bar"
	dbusWatcher "barista.run/base/watchers/dbus"
	"barista.run/outputs"
	testBar "barista.run/testing/bar"
	"barista.run/timing"

	"github.com/godbus/dbus"
	"github.com/stretchr/testify/require"
)

func init() {
	busType = dbusWatcher.Test
}

type methodCall struct {
	name string
	arg  interface{}
}

func TestMedia(t *testing.T) {
	testBar.New(t)
	bus := dbusWatcher.SetupTestBus()
	srv := bus.RegisterService("org.mpris.MediaPlayer2.testplayer")
	obj := srv.Object("/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player")
	obj.SetProperties(map[string]interface{}{
		"Position":       180 * 1000 * 1000,
		"PlaybackStatus": "Playing",
		"Rate":           1.0,
		"Metadata": map[string]dbus.Variant{
			"xesam:title":  dbus.MakeVariant("Title"),
			"xesam:artist": dbus.MakeVariant([]string{"Artist1", "Artist2"}),
		},
	}, dbusWatcher.SignalTypeNone)
	calls := make(chan methodCall, 10)

	pl := New("testplayer")
	testBar.Run(pl)
	testBar.NextOutput("on start").AssertText([]string{"3m0s: Title"})

	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on props change").AssertText([]string{"Title"})

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title": dbus.MakeVariant("foo"),
	}, dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on title change").AssertText([]string{"foo"})

	srv1 := bus.RegisterService()
	obj = srv1.Object("/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player")
	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeNone)
	obj.OnElse(func(method string, args ...interface{}) ([]interface{}, error) {
		c := methodCall{name: method}
		if len(args) > 0 {
			c.arg = args[0]
		}
		calls <- c
		return nil, nil
	})
	testBar.AssertNoOutput("On unrelated service changes")

	srv.Unregister()
	testBar.NextOutput("on service shutdown").AssertEmpty()

	srv1.AddName("org.mpris.MediaPlayer2.testplayer")
	testBar.NextOutput("on service move").AssertText([]string{""},
		"Does not show stale title")

	obj.SetProperties(map[string]interface{}{
		"PlaybackStatus": "Paused",
		"Shuffle":        true,
		"Rate":           1.0,
		"Metadata": map[string]dbus.Variant{
			"xesam:title":   dbus.MakeVariant("Song"),
			"mpris:trackid": dbus.MakeVariant("a"),
			"mpris:ArtURL":  dbus.MakeVariant("file:///tmp/art.webp"),
		},
	}, dbusWatcher.SignalTypeInvalidated)
	out := testBar.NextOutput("on properties change")
	out.AssertText([]string{"Song"}, "Still paused")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.PlayPause", nil},
		<-calls, "On left click")

	obj.SetProperty("PlaybackStatus", "Playing", dbusWatcher.SignalTypeChanged)
	out = testBar.NextOutput("on playstate change")
	out.AssertText([]string{"0s: Song"})

	out.At(0).Click(bar.Event{Button: bar.ScrollLeft})
	out.At(0).Click(bar.Event{Button: bar.ScrollRight})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Seek", int64(-1000 * 1000)},
		<-calls, "On scroll left")
	select {
	case <-calls:
		require.Fail(t, "Unexpected method call",
			"Rate limiter should not allow second call")
	case <-time.After(50 * time.Millisecond):
	}

	obj.Emit("Seeked", 99*1000*1000)
	out = testBar.NextOutput("on seek")
	out.AssertText([]string{"1m39s: Song"})

	out.At(0).Click(bar.Event{Button: bar.ButtonBack})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Previous", nil},
		<-calls, "On back click")

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title":   dbus.MakeVariant("Title"),
		"xesam:artist":  dbus.MakeVariant([]string{"Artist1", "Artist2"}),
		"xesam:album":   dbus.MakeVariant("Album"),
		"mpris:trackid": dbus.MakeVariant("2"),
	}, dbusWatcher.SignalTypeInvalidated)
	testBar.NextOutput("on metadata update").AssertText([]string{"0s: Title"})

	var lastInfo Info
	pl.RepeatingOutput(func(i Info) bar.Output {
		lastInfo = i
		return outputs.Textf("[%s, %v] %s - %s",
			i.PlaybackStatus, i.TruncatedPosition("k"), i.Title, i.Artist)
	})
	out = testBar.NextOutput("on output format change")
	out.AssertText([]string{"[Playing, 0s] Title - Artist1"})

	out.At(0).Click(bar.Event{Button: bar.ButtonForward})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Next", nil},
		<-calls, "On click with custom output")

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title":       dbus.MakeVariant("Song"),
		"mpris:trackid":     dbus.MakeVariant("3"),
		"xesam:albumArtist": dbus.MakeVariant([]string{"Person1", "Person2"}),
		"mpris:length":      dbus.MakeVariant(180 * 1000 * 1000),
	}, dbusWatcher.SignalTypeInvalidated)
	out = testBar.NextOutput("on metadata update")
	out.AssertText([]string{"[Playing, 0s] Song - "})

	timing.AdvanceBy(time.Second)
	out = testBar.NextOutput("on time passing")
	out.AssertText([]string{"[Playing, 1s] Song - "})

	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeChanged)
	out = testBar.NextOutput("on pause")
	out.AssertText([]string{"[Paused, 1s] Song - "})

	timing.AdvanceBy(time.Second)
	testBar.AssertNoOutput("on time passing, but paused")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Seek", int64(1000 * 1000)},
		<-calls, "On scroll with custom output")

	obj.Emit("Seeked", 2*1000*1000)
	testBar.NextOutput("on seek").AssertText([]string{"[Paused, 2s] Song - "})

	obj.SetProperty("PlaybackStatus", "Stopped", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on stop").AssertText([]string{"[Stopped, 0s] Song - "})

	obj.SetProperty("PlaybackStatus", "Playing", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on play").AssertText([]string{"[Playing, 0s] Song - "})

	lastInfo.Stop()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Stop", nil},
		<-calls, "Info.Stop()")
	lastInfo.Play()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Play", nil},
		<-calls, "Info.Stop()")
	lastInfo.Pause()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Pause", nil},
		<-calls, "Info.Stop()")

	require.True(t, lastInfo.Connected(), "Playing is Connected()")
	require.True(t, lastInfo.Playing(), "Playing == Playing()")
	require.False(t, lastInfo.Paused(), "Playing != Paused()")
	require.False(t, lastInfo.Stopped(), "Playing != Stopped()")
}

func TestDbusLongAndFloats(t *testing.T) {
	for _, tc := range []struct {
		val      interface{}
		asDouble float64
		asLong   int64
	}{
		{1.1, 1.1, 1},
		{2, 2.0, 2},
		{float32(3.3), 3.3, 3},
		{dbus.MakeVariant(4.4), 4.4, 4},
		{uint(5), 5.0, 5},
		{int64(6), 6.0, 6},
		{int32(7), 7.0, 7},
		{uint8(8), 8.0, 8},
		{dbus.MakeVariant(uint(9)), 9.0, 9},
		{dbus.MakeVariant(int64(10)), 10.0, 10},
		{dbus.MakeVariant(int32(11)), 11.0, 11},
		{dbus.MakeVariant(uint8(12)), 12.0, 12},
		{"foo", 0.0, 0},
		{dbus.MakeVariant("baz"), 0.0, 0},
		{"13.34", 0.0, 0},
		{dbus.MakeVariant("14.45"), 0.0, 0},
		{dbus.MakeVariant([]float64{15.8}), 0.0, 0},
	} {
		require.InDelta(t, tc.asDouble, getDouble(tc.val), 0.001,
			"getDouble(%v) == %v", tc.val, tc.asDouble)
		require.Equal(t, tc.asLong, getLong(tc.val),
			"getLong(%v) == %v", tc.val, tc.asLong)
	}
}
<commit_msg>Allow -count > 1 for modules/media test<commit_after>// Copyright 2018 Google Inc.
//
// 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 media

import (
	"testing"
	"time"

	"barista.run/bar"
	dbusWatcher "barista.run/base/watchers/dbus"
	"barista.run/outputs"
	testBar "barista.run/testing/bar"
	"barista.run/timing"
	"golang.org/x/time/rate"

	"github.com/godbus/dbus"
	"github.com/stretchr/testify/require"
)

func init() {
	busType = dbusWatcher.Test
}

type methodCall struct {
	name string
	arg  interface{}
}

func TestMedia(t *testing.T) {
	// To allow -count >1 to work.
	seekLimiter = rate.NewLimiter(rate.Every(50*time.Millisecond), 1)

	testBar.New(t)
	bus := dbusWatcher.SetupTestBus()
	srv := bus.RegisterService("org.mpris.MediaPlayer2.testplayer")
	obj := srv.Object("/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player")
	obj.SetProperties(map[string]interface{}{
		"Position":       180 * 1000 * 1000,
		"PlaybackStatus": "Playing",
		"Rate":           1.0,
		"Metadata": map[string]dbus.Variant{
			"xesam:title":  dbus.MakeVariant("Title"),
			"xesam:artist": dbus.MakeVariant([]string{"Artist1", "Artist2"}),
		},
	}, dbusWatcher.SignalTypeNone)
	calls := make(chan methodCall, 10)

	pl := New("testplayer")
	testBar.Run(pl)
	testBar.NextOutput("on start").AssertText([]string{"3m0s: Title"})

	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on props change").AssertText([]string{"Title"})

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title": dbus.MakeVariant("foo"),
	}, dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on title change").AssertText([]string{"foo"})

	srv1 := bus.RegisterService()
	obj = srv1.Object("/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player")
	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeNone)
	obj.OnElse(func(method string, args ...interface{}) ([]interface{}, error) {
		c := methodCall{name: method}
		if len(args) > 0 {
			c.arg = args[0]
		}
		calls <- c
		return nil, nil
	})
	testBar.AssertNoOutput("On unrelated service changes")

	srv.Unregister()
	testBar.NextOutput("on service shutdown").AssertEmpty()

	srv1.AddName("org.mpris.MediaPlayer2.testplayer")
	testBar.NextOutput("on service move").AssertText([]string{""},
		"Does not show stale title")

	obj.SetProperties(map[string]interface{}{
		"PlaybackStatus": "Paused",
		"Shuffle":        true,
		"Rate":           1.0,
		"Metadata": map[string]dbus.Variant{
			"xesam:title":   dbus.MakeVariant("Song"),
			"mpris:trackid": dbus.MakeVariant("a"),
			"mpris:ArtURL":  dbus.MakeVariant("file:///tmp/art.webp"),
		},
	}, dbusWatcher.SignalTypeInvalidated)
	out := testBar.NextOutput("on properties change")
	out.AssertText([]string{"Song"}, "Still paused")

	out.At(0).Click(bar.Event{Button: bar.ButtonLeft})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.PlayPause", nil},
		<-calls, "On left click")

	obj.SetProperty("PlaybackStatus", "Playing", dbusWatcher.SignalTypeChanged)
	out = testBar.NextOutput("on playstate change")
	out.AssertText([]string{"0s: Song"})

	out.At(0).Click(bar.Event{Button: bar.ScrollLeft})
	out.At(0).Click(bar.Event{Button: bar.ScrollRight})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Seek", int64(-1000 * 1000)},
		<-calls, "On scroll left")
	select {
	case <-calls:
		require.Fail(t, "Unexpected method call",
			"Rate limiter should not allow second call")
	case <-time.After(50 * time.Millisecond):
	}

	obj.Emit("Seeked", 99*1000*1000)
	out = testBar.NextOutput("on seek")
	out.AssertText([]string{"1m39s: Song"})

	out.At(0).Click(bar.Event{Button: bar.ButtonBack})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Previous", nil},
		<-calls, "On back click")

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title":   dbus.MakeVariant("Title"),
		"xesam:artist":  dbus.MakeVariant([]string{"Artist1", "Artist2"}),
		"xesam:album":   dbus.MakeVariant("Album"),
		"mpris:trackid": dbus.MakeVariant("2"),
	}, dbusWatcher.SignalTypeInvalidated)
	testBar.NextOutput("on metadata update").AssertText([]string{"0s: Title"})

	var lastInfo Info
	pl.RepeatingOutput(func(i Info) bar.Output {
		lastInfo = i
		return outputs.Textf("[%s, %v] %s - %s",
			i.PlaybackStatus, i.TruncatedPosition("k"), i.Title, i.Artist)
	})
	out = testBar.NextOutput("on output format change")
	out.AssertText([]string{"[Playing, 0s] Title - Artist1"})

	out.At(0).Click(bar.Event{Button: bar.ButtonForward})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Next", nil},
		<-calls, "On click with custom output")

	obj.SetProperty("Metadata", map[string]dbus.Variant{
		"xesam:title":       dbus.MakeVariant("Song"),
		"mpris:trackid":     dbus.MakeVariant("3"),
		"xesam:albumArtist": dbus.MakeVariant([]string{"Person1", "Person2"}),
		"mpris:length":      dbus.MakeVariant(180 * 1000 * 1000),
	}, dbusWatcher.SignalTypeInvalidated)
	out = testBar.NextOutput("on metadata update")
	out.AssertText([]string{"[Playing, 0s] Song - "})

	timing.AdvanceBy(time.Second)
	out = testBar.NextOutput("on time passing")
	out.AssertText([]string{"[Playing, 1s] Song - "})

	obj.SetProperty("PlaybackStatus", "Paused", dbusWatcher.SignalTypeChanged)
	out = testBar.NextOutput("on pause")
	out.AssertText([]string{"[Paused, 1s] Song - "})

	timing.AdvanceBy(time.Second)
	testBar.AssertNoOutput("on time passing, but paused")

	out.At(0).Click(bar.Event{Button: bar.ScrollDown})
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Seek", int64(1000 * 1000)},
		<-calls, "On scroll with custom output")

	obj.Emit("Seeked", 2*1000*1000)
	testBar.NextOutput("on seek").AssertText([]string{"[Paused, 2s] Song - "})

	obj.SetProperty("PlaybackStatus", "Stopped", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on stop").AssertText([]string{"[Stopped, 0s] Song - "})

	obj.SetProperty("PlaybackStatus", "Playing", dbusWatcher.SignalTypeChanged)
	testBar.NextOutput("on play").AssertText([]string{"[Playing, 0s] Song - "})

	lastInfo.Stop()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Stop", nil},
		<-calls, "Info.Stop()")
	lastInfo.Play()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Play", nil},
		<-calls, "Info.Stop()")
	lastInfo.Pause()
	require.Equal(t,
		methodCall{"org.mpris.MediaPlayer2.Player.Pause", nil},
		<-calls, "Info.Stop()")

	require.True(t, lastInfo.Connected(), "Playing is Connected()")
	require.True(t, lastInfo.Playing(), "Playing == Playing()")
	require.False(t, lastInfo.Paused(), "Playing != Paused()")
	require.False(t, lastInfo.Stopped(), "Playing != Stopped()")
}

func TestDbusLongAndFloats(t *testing.T) {
	for _, tc := range []struct {
		val      interface{}
		asDouble float64
		asLong   int64
	}{
		{1.1, 1.1, 1},
		{2, 2.0, 2},
		{float32(3.3), 3.3, 3},
		{dbus.MakeVariant(4.4), 4.4, 4},
		{uint(5), 5.0, 5},
		{int64(6), 6.0, 6},
		{int32(7), 7.0, 7},
		{uint8(8), 8.0, 8},
		{dbus.MakeVariant(uint(9)), 9.0, 9},
		{dbus.MakeVariant(int64(10)), 10.0, 10},
		{dbus.MakeVariant(int32(11)), 11.0, 11},
		{dbus.MakeVariant(uint8(12)), 12.0, 12},
		{"foo", 0.0, 0},
		{dbus.MakeVariant("baz"), 0.0, 0},
		{"13.34", 0.0, 0},
		{dbus.MakeVariant("14.45"), 0.0, 0},
		{dbus.MakeVariant([]float64{15.8}), 0.0, 0},
	} {
		require.InDelta(t, tc.asDouble, getDouble(tc.val), 0.001,
			"getDouble(%v) == %v", tc.val, tc.asDouble)
		require.Equal(t, tc.asLong, getLong(tc.val),
			"getLong(%v) == %v", tc.val, tc.asLong)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/validation"
	"k8s.io/kops/util/pkg/tables"
	k8sapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)

type ValidateClusterOptions struct {
	// No options yet
}

func NewCmdValidateCluster(f *util.Factory, out io.Writer) *cobra.Command {
	options := &ValidateClusterOptions{}

	cmd := &cobra.Command{
		Use: "cluster",
		//Aliases: []string{"cluster"},
		Short: "Validate cluster",
		Long:  `Validate a kubernetes cluster`,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunValidateCluster(f, cmd, args, os.Stdout, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	return cmd
}

func RunValidateCluster(f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, options *ValidateClusterOptions) error {
	err := rootCommand.ProcessArgs(args)
	if err != nil {
		return err
	}

	cluster, err := rootCommand.Cluster()
	if err != nil {
		return err
	}

	clientSet, err := f.Clientset()
	if err != nil {
		return err
	}

	list, err := clientSet.InstanceGroups(cluster.ObjectMeta.Name).List(k8sapi.ListOptions{})
	if err != nil {
		return fmt.Errorf("cannot get InstanceGroups for %q: %v", cluster.ObjectMeta.Name, err)
	}

	fmt.Fprintf(out, "Validating cluster %v\n\n", cluster.ObjectMeta.Name)

	var instanceGroups []api.InstanceGroup
	for _, ig := range list.Items {
		instanceGroups = append(instanceGroups, ig)
		glog.V(2).Infof("instance group: %#v\n\n", ig.Spec)
	}

	if len(instanceGroups) == 0 {
		return fmt.Errorf("no InstanceGroup objects found\n")
	}

	// TODO: Refactor into util.Factory
	contextName := cluster.ObjectMeta.Name
	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		clientcmd.NewDefaultClientConfigLoadingRules(),
		&clientcmd.ConfigOverrides{CurrentContext: contextName}).ClientConfig()
	if err != nil {
		return fmt.Errorf("Cannot load kubecfg settings for %q: %v\n", contextName, err)
	}

	k8sClient, err := k8s_clientset.NewForConfig(config)
	if err != nil {
		return fmt.Errorf("Cannot build kube api client for %q: %v\n", contextName, err)
	}

	validationCluster, validationFailed := validation.ValidateCluster(cluster.ObjectMeta.Name, list, k8sClient)

	if validationCluster == nil || validationCluster.NodeList == nil || validationCluster.NodeList.Items == nil {
		// validationFailed error is already formatted
		return validationFailed
	}

	t := &tables.Table{}
	t.AddColumn("NAME", func(c api.InstanceGroup) string {
		return c.ObjectMeta.Name
	})
	t.AddColumn("ROLE", func(c api.InstanceGroup) string {
		return string(c.Spec.Role)
	})
	t.AddColumn("MACHINETYPE", func(c api.InstanceGroup) string {
		return c.Spec.MachineType
	})
	t.AddColumn("SUBNETS", func(c api.InstanceGroup) string {
		return strings.Join(c.Spec.Subnets, ",")
	})
	t.AddColumn("MIN", func(c api.InstanceGroup) string {
		return int32PointerToString(c.Spec.MinSize)
	})
	t.AddColumn("MAX", func(c api.InstanceGroup) string {
		return int32PointerToString(c.Spec.MaxSize)
	})

	fmt.Fprintln(out, "INSTANCE GROUPS")
	err = t.Render(instanceGroups, out, "NAME", "ROLE", "MACHINETYPE", "MIN", "MAX", "SUBNETS")

	if err != nil {
		return fmt.Errorf("cannot render nodes for %q: %v", cluster.ObjectMeta.Name, err)
	}

	t = &tables.Table{}

	t.AddColumn("NAME", func(n v1.Node) string {
		return n.Name
	})

	t.AddColumn("READY", func(n v1.Node) v1.ConditionStatus {
		return validation.GetNodeConditionStatus(&n)
	})

	t.AddColumn("ROLE", func(n v1.Node) string {
		// TODO: Maybe print the instance group role instead?
		// TODO: Maybe include the instance group name?
		role := "node"
		if val, ok := n.ObjectMeta.Labels[api.RoleLabelName]; ok {
			role = val
		}
		return role
	})

	fmt.Fprintln(out, "\nNODE STATUS")
	err = t.Render(validationCluster.NodeList.Items, out, "NAME", "ROLE", "READY")

	if err != nil {
		return fmt.Errorf("cannot render nodes for %q: %v", cluster.ObjectMeta.Name, err)
	}

	if validationFailed == nil {
		fmt.Fprintf(out, "\nYour cluster %s is ready\n", cluster.ObjectMeta.Name)
		return nil
	} else {
		// do we need to print which instance group is not ready?
		// nodes are going to be a pain
		fmt.Fprint(out, "\nValidation Failed\n")
		fmt.Fprintf(out, "Ready Master(s) %d out of %d.\n", len(validationCluster.MastersNotReadyArray), validationCluster.MastersCount)
		fmt.Fprintf(out, "Ready Node(s) %d out of %d.\n", len(validationCluster.NodesNotReadyArray), validationCluster.NodesCount)
		return fmt.Errorf("Your cluster %s is NOT ready.\n", cluster.ObjectMeta.Name)
	}
}
<commit_msg>Correct variables name<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"k8s.io/kops/cmd/kops/util"
	api "k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/validation"
	"k8s.io/kops/util/pkg/tables"
	k8sapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)

type ValidateClusterOptions struct {
	// No options yet
}

func NewCmdValidateCluster(f *util.Factory, out io.Writer) *cobra.Command {
	options := &ValidateClusterOptions{}

	cmd := &cobra.Command{
		Use: "cluster",
		//Aliases: []string{"cluster"},
		Short: "Validate cluster",
		Long:  `Validate a kubernetes cluster`,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunValidateCluster(f, cmd, args, os.Stdout, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	return cmd
}

func RunValidateCluster(f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, options *ValidateClusterOptions) error {
	err := rootCommand.ProcessArgs(args)
	if err != nil {
		return err
	}

	cluster, err := rootCommand.Cluster()
	if err != nil {
		return err
	}

	clientSet, err := f.Clientset()
	if err != nil {
		return err
	}

	list, err := clientSet.InstanceGroups(cluster.ObjectMeta.Name).List(k8sapi.ListOptions{})
	if err != nil {
		return fmt.Errorf("cannot get InstanceGroups for %q: %v", cluster.ObjectMeta.Name, err)
	}

	fmt.Fprintf(out, "Validating cluster %v\n\n", cluster.ObjectMeta.Name)

	var instanceGroups []api.InstanceGroup
	for _, ig := range list.Items {
		instanceGroups = append(instanceGroups, ig)
		glog.V(2).Infof("instance group: %#v\n\n", ig.Spec)
	}

	if len(instanceGroups) == 0 {
		return fmt.Errorf("no InstanceGroup objects found\n")
	}

	// TODO: Refactor into util.Factory
	contextName := cluster.ObjectMeta.Name
	config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		clientcmd.NewDefaultClientConfigLoadingRules(),
		&clientcmd.ConfigOverrides{CurrentContext: contextName}).ClientConfig()
	if err != nil {
		return fmt.Errorf("Cannot load kubecfg settings for %q: %v\n", contextName, err)
	}

	k8sClient, err := k8s_clientset.NewForConfig(config)
	if err != nil {
		return fmt.Errorf("Cannot build kube api client for %q: %v\n", contextName, err)
	}

	validationCluster, validationFailed := validation.ValidateCluster(cluster.ObjectMeta.Name, list, k8sClient)

	if validationCluster == nil || validationCluster.NodeList == nil || validationCluster.NodeList.Items == nil {
		// validationFailed error is already formatted
		return validationFailed
	}

	t := &tables.Table{}
	t.AddColumn("NAME", func(c api.InstanceGroup) string {
		return c.ObjectMeta.Name
	})
	t.AddColumn("ROLE", func(c api.InstanceGroup) string {
		return string(c.Spec.Role)
	})
	t.AddColumn("MACHINETYPE", func(c api.InstanceGroup) string {
		return c.Spec.MachineType
	})
	t.AddColumn("SUBNETS", func(c api.InstanceGroup) string {
		return strings.Join(c.Spec.Subnets, ",")
	})
	t.AddColumn("MIN", func(c api.InstanceGroup) string {
		return int32PointerToString(c.Spec.MinSize)
	})
	t.AddColumn("MAX", func(c api.InstanceGroup) string {
		return int32PointerToString(c.Spec.MaxSize)
	})

	fmt.Fprintln(out, "INSTANCE GROUPS")
	err = t.Render(instanceGroups, out, "NAME", "ROLE", "MACHINETYPE", "MIN", "MAX", "SUBNETS")

	if err != nil {
		return fmt.Errorf("cannot render nodes for %q: %v", cluster.ObjectMeta.Name, err)
	}

	t = &tables.Table{}

	t.AddColumn("NAME", func(n v1.Node) string {
		return n.Name
	})

	t.AddColumn("READY", func(n v1.Node) v1.ConditionStatus {
		return validation.GetNodeConditionStatus(&n)
	})

	t.AddColumn("ROLE", func(n v1.Node) string {
		// TODO: Maybe print the instance group role instead?
		// TODO: Maybe include the instance group name?
		role := "node"
		if val, ok := n.ObjectMeta.Labels[api.RoleLabelName]; ok {
			role = val
		}
		return role
	})

	fmt.Fprintln(out, "\nNODE STATUS")
	err = t.Render(validationCluster.NodeList.Items, out, "NAME", "ROLE", "READY")

	if err != nil {
		return fmt.Errorf("cannot render nodes for %q: %v", cluster.ObjectMeta.Name, err)
	}

	if validationFailed == nil {
		fmt.Fprintf(out, "\nYour cluster %s is ready\n", cluster.ObjectMeta.Name)
		return nil
	} else {
		// do we need to print which instance group is not ready?
		// nodes are going to be a pain
		fmt.Fprint(out, "\nValidation Failed\n")
		fmt.Fprintf(out, "Ready Master(s) %d out of %d.\n", len(validationCluster.MastersReadyArray), validationCluster.MastersCount)
		fmt.Fprintf(out, "Ready Node(s) %d out of %d.\n", len(validationCluster.NodesReadyArray), validationCluster.NodesCount)
		return fmt.Errorf("Your cluster %s is NOT ready.\n", cluster.ObjectMeta.Name)
	}
}
<|endoftext|>