<commit_before>package authldap

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"net/url"
	"text/template"
	"time"

	ldap "gopkg.in/ldap.v2"

	"github.com/Sirupsen/logrus"
	"github.com/aakso/ssh-inscribe/pkg/auth"
	"github.com/pkg/errors"
)

const (
	ConnectModePlain int = iota
	ConnectModeLDAPS
	ConnectModeStartTLS

	UserBindDN        = "UserBindDN"
	UserSearchFilter  = "UserSearchFilter"
	GroupSearchFilter = "GroupSearchFilter"
	SubjectName       = "SubjectName"
	Principal         = "Principal"

	AuthLDAPUsertEntry = "authLDAPUserEntry"
)

type AuthLDAP struct {
	log         *logrus.Entry
	config      *Config
	url         *url.URL
	connectMode int

	tpls *template.Template
}

func (al *AuthLDAP) Authenticate(pctx *auth.AuthContext, creds *auth.Credentials) (*auth.AuthContext, bool) {
	var err error
	log := al.log.WithField("action", "authenticate")
	if v, ok := creds.Meta[auth.MetaAuditID]; ok {
		log = log.WithField(auth.MetaAuditID, v)
	}

	if creds == nil {
		return nil, false
	}

	newctx := &auth.AuthContext{
		Parent:          pctx,
		SubjectName:     creds.UserIdentifier,
		AuthMeta:        creds.Meta,
		Principals:      al.config.Principals,
		CriticalOptions: al.config.CriticalOptions,
		Extensions:      al.config.Extensions,
		Authenticator:   al.Name(),
	}
	if newctx.AuthMeta == nil {
		newctx.AuthMeta = make(map[string]interface{})
	}
	tplCtx := map[string]interface{}{
		"UserName": creds.UserIdentifier,
	}

	var conn *ldap.Conn
	host := fmt.Sprintf("%s:%s", al.url.Hostname(), al.url.Port())
	switch al.connectMode {
	case ConnectModeLDAPS:
		conn, err = ldap.DialTLS("tcp", host, &tls.Config{
			InsecureSkipVerify: al.config.Insecure,
			ServerName:         al.url.Hostname(),
		})
		if err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	case ConnectModePlain, ConnectModeStartTLS:
		conn, err = ldap.Dial("tcp", host)
		if err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	}
	defer conn.Close()
	conn.SetTimeout(time.Second * time.Duration(al.config.Timeout))
	if al.connectMode == ConnectModeStartTLS {
		if err := conn.StartTLS(&tls.Config{
			InsecureSkipVerify: al.config.Insecure,
			ServerName:         al.url.Hostname(),
		}); err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	}

	binddn := al.RenderTpl(UserBindDN, tplCtx)
	if err := conn.Bind(binddn, string(creds.Secret)); err != nil {
		log.WithError(err).Error("cannot bind")
		return nil, false
	}

	// Find user entry, require a single match
	filter := al.RenderTpl(UserSearchFilter, tplCtx)
	res, err := al.search(conn, al.config.UserSearchBase, filter, al.config.UserSearchGetAttributes)
	if err != nil {
		log.WithError(err).Error("search failure")
		return nil, false
	}
	if len(res.Entries) != 1 {
		log.WithField("matches", len(res.Entries)).Error("not a single match")
		return nil, false
	}
	user := entryToMap(res.Entries[0])
	tplCtx["User"] = user
	newctx.SubjectName = al.RenderTpl(SubjectName, tplCtx)
	newctx.AuthMeta[AuthLDAPUsertEntry] = user
	log.WithField("user", user["cn"]).Debug("user search ok")

	// Find groups
	if al.config.AddPrincipalsFromGroups {
		filter = al.RenderTpl(GroupSearchFilter, tplCtx)
		res, err = al.search(conn, al.config.GroupSearchBase, filter, al.config.GroupSearchGetAttributes)
		if err != nil {
			log.WithError(err).Error("search failure")
			return nil, false
		}
		for _, entry := range res.Entries {
			group := entryToMap(entry)
			log.WithField("group", group["cn"]).Debug("searched group")
			tplCtx["Group"] = group
			if principal := al.RenderTpl(Principal, tplCtx); principal != "" {
				newctx.Principals = append(newctx.Principals, principal)
			}
		}
	}

	return newctx, true
}

func (al *AuthLDAP) search(conn *ldap.Conn, base, filter string, attrs []string) (*ldap.SearchResult, error) {
	al.log.WithFields(logrus.Fields{
		"base":   base,
		"filter": filter,
		"attrs":  attrs,
	}).Debug("search")
	sr := ldap.NewSearchRequest(
		base,
		ldap.ScopeWholeSubtree,
		ldap.DerefInSearching,
		0, 0, false,
		filter,
		attrs,
		nil,
	)
	return conn.Search(sr)
}

func (al *AuthLDAP) RenderTpl(name string, data interface{}) string {
	buf := bytes.NewBuffer([]byte{})
	err := al.tpls.ExecuteTemplate(buf, name, data)
	if err != nil {
		al.log.WithError(err).Errorf("template render error: %s", name)
	}
	return buf.String()
}

func (al *AuthLDAP) Type() string {
	return Type
}

func (al *AuthLDAP) Name() string {
	return al.config.Name
}

func (al *AuthLDAP) Realm() string {
	return al.config.Realm
}

func (al *AuthLDAP) CredentialType() string {
	return auth.CredentialUserPassword
}

func New(conf *Config) (*AuthLDAP, error) {
	if conf == nil {
		conf = Defaults
	}

	var tplError error
	rootTpl := template.New("root")
	parseTpl := func(name, tpl string) {
		_, err := rootTpl.New(name).Parse(tpl)
		if err != nil {
			tplError = errors.Wrapf(err, "cannot parse %s", name)
		}
	}
	parseTpl(UserBindDN, conf.UserBindDN)
	parseTpl(UserSearchFilter, conf.UserSearchFilter)
	parseTpl(GroupSearchFilter, conf.GroupSearchFilter)
	parseTpl(SubjectName, conf.SubjectNameTemplate)
	parseTpl(Principal, conf.PrincipalTemplate)
	if tplError != nil {
		return nil, tplError
	}

	url, err := url.Parse(conf.ServerURL)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse ServerURL")
	}
	connectMode := ConnectModePlain
	switch url.Scheme {
	case "ldap":
		if url.Query().Get("startTLS") != "" {
			connectMode = ConnectModeStartTLS
		}
	case "ldaps":
		connectMode = ConnectModeLDAPS
	default:
		return nil, errors.Errorf("unsupported scheme: %s", url.Scheme)
	}
	if url.Port() == "" {
		return nil, errors.New("missing port for the ServerURL")
	}

	return &AuthLDAP{
		log:         Log.WithField("realm", conf.Realm),
		config:      conf,
		url:         url,
		connectMode: connectMode,
		tpls:        rootTpl,
	}, nil
}

type EntryMap map[string]interface{}

func (em EntryMap) DN() string {
	return em.Get("dn")
}

func (em EntryMap) Get(k string) string {
	switch v := em[k].(type) {
	case []string:
		if len(v) >= 1 {
			return v[0]
		}
	case string:
		return v
	}
	return ""
}

func entryToMap(entry *ldap.Entry) EntryMap {
	ret := make(map[string]interface{})
	ret["dn"] = []string{entry.DN}
	for _, v := range entry.Attributes {
		if len(v.Values) == 1 {
			ret[v.Name] = v.Values[0]
		} else {
			ret[v.Name] = v.Values
		}
	}
	return EntryMap(ret)
}
<commit_msg>set dial timeout for authldap<commit_after>package authldap

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"net/url"
	"text/template"
	"time"

	ldap "gopkg.in/ldap.v2"

	"github.com/Sirupsen/logrus"
	"github.com/aakso/ssh-inscribe/pkg/auth"
	"github.com/pkg/errors"
)

const (
	ConnectModePlain int = iota
	ConnectModeLDAPS
	ConnectModeStartTLS

	UserBindDN        = "UserBindDN"
	UserSearchFilter  = "UserSearchFilter"
	GroupSearchFilter = "GroupSearchFilter"
	SubjectName       = "SubjectName"
	Principal         = "Principal"

	AuthLDAPUsertEntry = "authLDAPUserEntry"
)

type AuthLDAP struct {
	log         *logrus.Entry
	config      *Config
	url         *url.URL
	connectMode int

	tpls *template.Template
}

func (al *AuthLDAP) Authenticate(pctx *auth.AuthContext, creds *auth.Credentials) (*auth.AuthContext, bool) {
	var err error
	log := al.log.WithField("action", "authenticate")
	if v, ok := creds.Meta[auth.MetaAuditID]; ok {
		log = log.WithField(auth.MetaAuditID, v)
	}

	if creds == nil {
		return nil, false
	}

	newctx := &auth.AuthContext{
		Parent:          pctx,
		SubjectName:     creds.UserIdentifier,
		AuthMeta:        creds.Meta,
		Principals:      al.config.Principals,
		CriticalOptions: al.config.CriticalOptions,
		Extensions:      al.config.Extensions,
		Authenticator:   al.Name(),
	}
	if newctx.AuthMeta == nil {
		newctx.AuthMeta = make(map[string]interface{})
	}
	tplCtx := map[string]interface{}{
		"UserName": creds.UserIdentifier,
	}

	// Set ldap package level dial timeout as it doesn't offer any other way
	ldap.DefaultTimeout = time.Second * time.Duration(al.config.Timeout)
	var conn *ldap.Conn
	host := fmt.Sprintf("%s:%s", al.url.Hostname(), al.url.Port())
	switch al.connectMode {
	case ConnectModeLDAPS:
		conn, err = ldap.DialTLS("tcp", host, &tls.Config{
			InsecureSkipVerify: al.config.Insecure,
			ServerName:         al.url.Hostname(),
		})
		if err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	case ConnectModePlain, ConnectModeStartTLS:
		conn, err = ldap.Dial("tcp", host)
		if err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	}
	defer conn.Close()
	conn.SetTimeout(time.Second * time.Duration(al.config.Timeout))
	if al.connectMode == ConnectModeStartTLS {
		if err := conn.StartTLS(&tls.Config{
			InsecureSkipVerify: al.config.Insecure,
			ServerName:         al.url.Hostname(),
		}); err != nil {
			log.WithError(err).Error("cannot connect to directory server")
			return nil, false
		}
	}

	binddn := al.RenderTpl(UserBindDN, tplCtx)
	if err := conn.Bind(binddn, string(creds.Secret)); err != nil {
		log.WithError(err).Error("cannot bind")
		return nil, false
	}

	// Find user entry, require a single match
	filter := al.RenderTpl(UserSearchFilter, tplCtx)
	res, err := al.search(conn, al.config.UserSearchBase, filter, al.config.UserSearchGetAttributes)
	if err != nil {
		log.WithError(err).Error("search failure")
		return nil, false
	}
	if len(res.Entries) != 1 {
		log.WithField("matches", len(res.Entries)).Error("not a single match")
		return nil, false
	}
	user := entryToMap(res.Entries[0])
	tplCtx["User"] = user
	newctx.SubjectName = al.RenderTpl(SubjectName, tplCtx)
	newctx.AuthMeta[AuthLDAPUsertEntry] = user
	log.WithField("user", user["cn"]).Debug("user search ok")

	// Find groups
	if al.config.AddPrincipalsFromGroups {
		filter = al.RenderTpl(GroupSearchFilter, tplCtx)
		res, err = al.search(conn, al.config.GroupSearchBase, filter, al.config.GroupSearchGetAttributes)
		if err != nil {
			log.WithError(err).Error("search failure")
			return nil, false
		}
		for _, entry := range res.Entries {
			group := entryToMap(entry)
			log.WithField("group", group["cn"]).Debug("searched group")
			tplCtx["Group"] = group
			if principal := al.RenderTpl(Principal, tplCtx); principal != "" {
				newctx.Principals = append(newctx.Principals, principal)
			}
		}
	}

	return newctx, true
}

func (al *AuthLDAP) search(conn *ldap.Conn, base, filter string, attrs []string) (*ldap.SearchResult, error) {
	al.log.WithFields(logrus.Fields{
		"base":   base,
		"filter": filter,
		"attrs":  attrs,
	}).Debug("search")
	sr := ldap.NewSearchRequest(
		base,
		ldap.ScopeWholeSubtree,
		ldap.DerefInSearching,
		0, 0, false,
		filter,
		attrs,
		nil,
	)
	return conn.Search(sr)
}

func (al *AuthLDAP) RenderTpl(name string, data interface{}) string {
	buf := bytes.NewBuffer([]byte{})
	err := al.tpls.ExecuteTemplate(buf, name, data)
	if err != nil {
		al.log.WithError(err).Errorf("template render error: %s", name)
	}
	return buf.String()
}

func (al *AuthLDAP) Type() string {
	return Type
}

func (al *AuthLDAP) Name() string {
	return al.config.Name
}

func (al *AuthLDAP) Realm() string {
	return al.config.Realm
}

func (al *AuthLDAP) CredentialType() string {
	return auth.CredentialUserPassword
}

func New(conf *Config) (*AuthLDAP, error) {
	if conf == nil {
		conf = Defaults
	}

	var tplError error
	rootTpl := template.New("root")
	parseTpl := func(name, tpl string) {
		_, err := rootTpl.New(name).Parse(tpl)
		if err != nil {
			tplError = errors.Wrapf(err, "cannot parse %s", name)
		}
	}
	parseTpl(UserBindDN, conf.UserBindDN)
	parseTpl(UserSearchFilter, conf.UserSearchFilter)
	parseTpl(GroupSearchFilter, conf.GroupSearchFilter)
	parseTpl(SubjectName, conf.SubjectNameTemplate)
	parseTpl(Principal, conf.PrincipalTemplate)
	if tplError != nil {
		return nil, tplError
	}

	url, err := url.Parse(conf.ServerURL)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse ServerURL")
	}
	connectMode := ConnectModePlain
	switch url.Scheme {
	case "ldap":
		if url.Query().Get("startTLS") != "" {
			connectMode = ConnectModeStartTLS
		}
	case "ldaps":
		connectMode = ConnectModeLDAPS
	default:
		return nil, errors.Errorf("unsupported scheme: %s", url.Scheme)
	}
	if url.Port() == "" {
		return nil, errors.New("missing port for the ServerURL")
	}

	return &AuthLDAP{
		log:         Log.WithField("realm", conf.Realm),
		config:      conf,
		url:         url,
		connectMode: connectMode,
		tpls:        rootTpl,
	}, nil
}

type EntryMap map[string]interface{}

func (em EntryMap) DN() string {
	return em.Get("dn")
}

func (em EntryMap) Get(k string) string {
	switch v := em[k].(type) {
	case []string:
		if len(v) >= 1 {
			return v[0]
		}
	case string:
		return v
	}
	return ""
}

func entryToMap(entry *ldap.Entry) EntryMap {
	ret := make(map[string]interface{})
	ret["dn"] = []string{entry.DN}
	for _, v := range entry.Attributes {
		if len(v.Values) == 1 {
			ret[v.Name] = v.Values[0]
		} else {
			ret[v.Name] = v.Values
		}
	}
	return EntryMap(ret)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 loadbalancers

import (
	"crypto/sha256"
	"fmt"
	"strings"

	"github.com/golang/glog"
	compute "google.golang.org/api/compute/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/ingress-gce/pkg/flags"
	"k8s.io/ingress-gce/pkg/utils"
)

const SslCertificateMissing = "SslCertificateMissing"

func (l *L7) checkSSLCert() error {
	if flags.F.Features.ManagedCertificates {
		// Handle annotation managed-certificates
		managedSslCerts, used, err := l.getManagedCertificates()
		if used {
			l.sslCerts = managedSslCerts
			return err
		}
	}

	// Handle annotation pre-shared-cert
	used, preSharedSslCerts, err := l.getPreSharedCertificates()
	if used {
		l.sslCerts = preSharedSslCerts
		return err
	}

	// Get updated value of certificate for comparison
	existingSecretsSslCerts, err := l.getIngressManagedSslCerts()
	if err != nil {
		return err
	}

	l.oldSSLCerts = existingSecretsSslCerts
	secretsSslCerts, err := l.createSslCertificates(existingSecretsSslCerts)
	l.sslCerts = secretsSslCerts
	return err
}

// createSslCertificates creates SslCertificates based on kubernetes secrets in Ingress configuration.
func (l *L7) createSslCertificates(existingCerts []*compute.SslCertificate) ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate

	existingCertsMap := getMapfromCertList(existingCerts)

	// mapping of currently configured certs
	visitedCertMap := make(map[string]string)
	var failedCerts []string

	for _, tlsCert := range l.runtimeInfo.TLS {
		ingCert := tlsCert.Cert
		ingKey := tlsCert.Key
		gcpCertName := l.namer.SSLCertName(l.Name, tlsCert.CertHash)

		if addedBy, exists := visitedCertMap[gcpCertName]; exists {
			glog.V(3).Infof("Secret %q has a certificate already used by %v", tlsCert.Name, addedBy)
			continue
		}

		// PrivateKey is write only, so compare certs alone. We're assuming that
		// no one will change just the key. We can remember the key and compare,
		// but a bug could end up leaking it, which feels worse.
		// If the cert contents have changed, its hash would be different, so would be the cert name. So it is enough
		// to check if this cert name exists in the map.
		if existingCertsMap != nil {
			if cert, ok := existingCertsMap[gcpCertName]; ok {
				glog.V(3).Infof("Secret %q already exists as certificate %q", tlsCert.Name, gcpCertName)
				visitedCertMap[gcpCertName] = fmt.Sprintf("certificate:%q", gcpCertName)
				result = append(result, cert)
				continue
			}
		}
		// Controller needs to create the certificate, no need to check if it exists and delete. If it did exist, it
		// would have been listed in the populateSSLCert function and matched in the check above.
		glog.V(2).Infof("Creating new sslCertificate %q for LB %q", gcpCertName, l.Name)
		cert, err := l.cloud.CreateSslCertificate(&compute.SslCertificate{
			Name:        gcpCertName,
			Certificate: ingCert,
			PrivateKey:  ingKey,
		})
		if err != nil {
			glog.Errorf("Failed to create new sslCertificate %q for %q - %v", gcpCertName, l.Name, err)
			failedCerts = append(failedCerts, gcpCertName+" Error:"+err.Error())
			continue
		}
		visitedCertMap[gcpCertName] = fmt.Sprintf("secret:%q", tlsCert.Name)
		result = append(result, cert)
	}

	// Save the old certs for cleanup after we update the target proxy.
	if len(failedCerts) > 0 {
		return result, fmt.Errorf("Cert creation failures - %s", strings.Join(failedCerts, ","))
	}
	return result, nil
}

// getSslCertificates fetches GCE SslCertificate resources by names.
func (l *L7) getSslCertificates(names []string) ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate
	var failedCerts []string
	for _, name := range names {
		// Ask GCE for the cert, checking for problems and existence.
		cert, err := l.cloud.GetSslCertificate(name)
		if err != nil {
			failedCerts = append(failedCerts, name+": "+err.Error())
			l.recorder.Eventf(l.runtimeInfo.Ingress, corev1.EventTypeNormal, SslCertificateMissing, err.Error())
			continue
		}
		if cert == nil {
			failedCerts = append(failedCerts, name+": unable to find existing SslCertificate")
			continue
		}

		glog.V(2).Infof("Using existing SslCertificate %v for %v", name, l.Name)
		result = append(result, cert)
	}
	if len(failedCerts) != 0 {
		return result, fmt.Errorf("Errors - %s", strings.Join(failedCerts, ","))
	}

	return result, nil
}

// getManagedCertificates fetches SslCertificates specified via managed-certificates annotation.
func (l *L7) getManagedCertificates() ([]*compute.SslCertificate, bool, error) {
	if l.runtimeInfo.ManagedCertificates == "" {
		return nil, false, nil
	}

	mcrtsNames := utils.SplitAnnotation(l.runtimeInfo.ManagedCertificates)
	req, err := labels.NewRequirement("metadata.name", selection.In, mcrtsNames)
	if err != nil {
		return nil, true, err
	}

	sel := labels.NewSelector()
	sel.Add(*req)
	mcrts, err := l.mcrt.ManagedCertificates(l.runtimeInfo.Ingress.Namespace).List(sel)
	if err != nil {
		return nil, true, err
	}

	var names []string
	for _, mcrt := range mcrts {
		if mcrt.Status.CertificateName != "" {
			names = append(names, mcrt.Status.CertificateName)
		}
	}

	sslCerts, err := l.getSslCertificates(names)
	if err != nil {
		return sslCerts, true, fmt.Errorf("managed-certificates errors: %s", err.Error())
	}

	return sslCerts, true, nil
}

// getPreSharedCertificates fetches SslCertificates specified via pre-shared-cert annotation.
func (l *L7) getPreSharedCertificates() (bool, []*compute.SslCertificate, error) {
	if l.runtimeInfo.TLSName == "" {
		return false, nil, nil
	}
	sslCerts, err := l.getSslCertificates(utils.SplitAnnotation(l.runtimeInfo.TLSName))

	if err != nil {
		return true, sslCerts, fmt.Errorf("pre-shared-cert errors: %s", err.Error())
	}

	return true, sslCerts, nil
}

func getMapfromCertList(certs []*compute.SslCertificate) map[string]*compute.SslCertificate {
	if len(certs) == 0 {
		return nil
	}
	certMap := make(map[string]*compute.SslCertificate)
	for _, cert := range certs {
		certMap[cert.Name] = cert
	}
	return certMap
}

// getIngressManagedSslCerts fetches SslCertificate resources created and managed by this load balancer
// instance. These SslCertificate resources were created based on kubernetes secrets in Ingress
// configuration.
func (l *L7) getIngressManagedSslCerts() ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate

	// Currently we list all certs available in gcloud and filter the ones managed by this loadbalancer instance. This is
	// to make sure we garbage collect any old certs that this instance might have lost track of due to crashes.
	// Can be a performance issue if there are too many global certs, default quota is only 10.
	certs, err := l.cloud.ListSslCertificates()
	if err != nil {
		return nil, err
	}
	for _, c := range certs {
		if l.namer.IsCertUsedForLB(l.Name, c.Name) {
			glog.V(4).Infof("Populating ssl cert %s for l7 %s", c.Name, l.Name)
			result = append(result, c)
		}
	}
	if len(result) == 0 {
		// Check for legacy cert since that follows a different naming convention
		glog.V(4).Infof("Looking for legacy ssl certs")
		expectedCertLinks, err := l.getSslCertLinkInUse()
		if err != nil {
			// Return nil if target proxy doesn't exist.
			return nil, utils.IgnoreHTTPNotFound(err)
		}
		for _, link := range expectedCertLinks {
			// Retrieve the certificate and ignore error if certificate wasn't found
			name, err := utils.KeyName(link)
			if err != nil {
				glog.Warningf("error parsing cert name: %v", err)
				continue
			}

			if !l.namer.IsLegacySSLCert(l.Name, name) {
				continue
			}
			cert, _ := l.cloud.GetSslCertificate(name)
			if cert != nil {
				glog.V(4).Infof("Populating legacy ssl cert %s for l7 %s", cert.Name, l.Name)
				result = append(result, cert)
			}
		}
	}
	return result, nil
}

func (l *L7) deleteOldSSLCerts() {
	if len(l.oldSSLCerts) == 0 {
		return
	}
	certsMap := getMapfromCertList(l.sslCerts)
	for _, cert := range l.oldSSLCerts {
		if !l.namer.IsCertUsedForLB(l.Name, cert.Name) && !l.namer.IsLegacySSLCert(l.Name, cert.Name) {
			// retain cert if it is managed by GCE(non-ingress)
			continue
		}
		if _, ok := certsMap[cert.Name]; ok {
			// cert found in current map
			continue
		}
		glog.V(3).Infof("Cleaning up old SSL Certificate %s", cert.Name)
		if certErr := utils.IgnoreHTTPNotFound(l.cloud.DeleteSslCertificate(cert.Name)); certErr != nil {
			glog.Errorf("Old cert delete failed - %v", certErr)
		}
	}
}

// Returns true if the input array of certs is identical to the certs in the L7 config.
// Returns false if there is any mismatch
func (l *L7) compareCerts(certLinks []string) bool {
	certsMap := getMapfromCertList(l.sslCerts)
	if len(certLinks) != len(certsMap) {
		glog.V(4).Infof("Loadbalancer has %d certs, target proxy has %d certs", len(certsMap), len(certLinks))
		return false
	}

	for _, link := range certLinks {
		certName, err := utils.KeyName(link)
		if err != nil {
			glog.Warningf("Cannot get cert name: %v", err)
			return false
		}

		if cert, ok := certsMap[certName]; !ok {
			glog.V(4).Infof("Cannot find cert with name %s in certsMap %+v", certName, certsMap)
			return false
		} else if ok && !utils.EqualResourceIDs(link, cert.SelfLink) {
			glog.V(4).Infof("Selflink compare failed for certs - %s in loadbalancer, %s in targetproxy", cert.SelfLink, link)
			return false
		}
	}
	return true
}

func GetCertHash(contents string) string {
	return fmt.Sprintf("%x", sha256.Sum256([]byte(contents)))[:16]
}

func toCertNames(certs []*compute.SslCertificate) (names []string) {
	for _, v := range certs {
		names = append(names, v.Name)
	}
	return names
}
<commit_msg>Fix wrong filtering of ManagedCertificate objects. The objects were in fact not filtered - because of a bug a catch-all selector was applied<commit_after>/*
Copyright 2018 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 loadbalancers

import (
	"crypto/sha256"
	"fmt"
	"strings"

	"github.com/golang/glog"
	compute "google.golang.org/api/compute/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/ingress-gce/pkg/flags"
	"k8s.io/ingress-gce/pkg/utils"
)

const SslCertificateMissing = "SslCertificateMissing"

func (l *L7) checkSSLCert() error {
	if flags.F.Features.ManagedCertificates {
		// Handle annotation managed-certificates
		managedSslCerts, used, err := l.getManagedCertificates()
		if used {
			l.sslCerts = managedSslCerts
			return err
		}
	}

	// Handle annotation pre-shared-cert
	used, preSharedSslCerts, err := l.getPreSharedCertificates()
	if used {
		l.sslCerts = preSharedSslCerts
		return err
	}

	// Get updated value of certificate for comparison
	existingSecretsSslCerts, err := l.getIngressManagedSslCerts()
	if err != nil {
		return err
	}

	l.oldSSLCerts = existingSecretsSslCerts
	secretsSslCerts, err := l.createSslCertificates(existingSecretsSslCerts)
	l.sslCerts = secretsSslCerts
	return err
}

// createSslCertificates creates SslCertificates based on kubernetes secrets in Ingress configuration.
func (l *L7) createSslCertificates(existingCerts []*compute.SslCertificate) ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate

	existingCertsMap := getMapfromCertList(existingCerts)

	// mapping of currently configured certs
	visitedCertMap := make(map[string]string)
	var failedCerts []string

	for _, tlsCert := range l.runtimeInfo.TLS {
		ingCert := tlsCert.Cert
		ingKey := tlsCert.Key
		gcpCertName := l.namer.SSLCertName(l.Name, tlsCert.CertHash)

		if addedBy, exists := visitedCertMap[gcpCertName]; exists {
			glog.V(3).Infof("Secret %q has a certificate already used by %v", tlsCert.Name, addedBy)
			continue
		}

		// PrivateKey is write only, so compare certs alone. We're assuming that
		// no one will change just the key. We can remember the key and compare,
		// but a bug could end up leaking it, which feels worse.
		// If the cert contents have changed, its hash would be different, so would be the cert name. So it is enough
		// to check if this cert name exists in the map.
		if existingCertsMap != nil {
			if cert, ok := existingCertsMap[gcpCertName]; ok {
				glog.V(3).Infof("Secret %q already exists as certificate %q", tlsCert.Name, gcpCertName)
				visitedCertMap[gcpCertName] = fmt.Sprintf("certificate:%q", gcpCertName)
				result = append(result, cert)
				continue
			}
		}
		// Controller needs to create the certificate, no need to check if it exists and delete. If it did exist, it
		// would have been listed in the populateSSLCert function and matched in the check above.
		glog.V(2).Infof("Creating new sslCertificate %q for LB %q", gcpCertName, l.Name)
		cert, err := l.cloud.CreateSslCertificate(&compute.SslCertificate{
			Name:        gcpCertName,
			Certificate: ingCert,
			PrivateKey:  ingKey,
		})
		if err != nil {
			glog.Errorf("Failed to create new sslCertificate %q for %q - %v", gcpCertName, l.Name, err)
			failedCerts = append(failedCerts, gcpCertName+" Error:"+err.Error())
			continue
		}
		visitedCertMap[gcpCertName] = fmt.Sprintf("secret:%q", tlsCert.Name)
		result = append(result, cert)
	}

	// Save the old certs for cleanup after we update the target proxy.
	if len(failedCerts) > 0 {
		return result, fmt.Errorf("Cert creation failures - %s", strings.Join(failedCerts, ","))
	}
	return result, nil
}

// getSslCertificates fetches GCE SslCertificate resources by names.
func (l *L7) getSslCertificates(names []string) ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate
	var failedCerts []string
	for _, name := range names {
		// Ask GCE for the cert, checking for problems and existence.
		cert, err := l.cloud.GetSslCertificate(name)
		if err != nil {
			failedCerts = append(failedCerts, name+": "+err.Error())
			l.recorder.Eventf(l.runtimeInfo.Ingress, corev1.EventTypeNormal, SslCertificateMissing, err.Error())
			continue
		}
		if cert == nil {
			failedCerts = append(failedCerts, name+": unable to find existing SslCertificate")
			continue
		}

		glog.V(2).Infof("Using existing SslCertificate %v for %v", name, l.Name)
		result = append(result, cert)
	}
	if len(failedCerts) != 0 {
		return result, fmt.Errorf("Errors - %s", strings.Join(failedCerts, ","))
	}

	return result, nil
}

// getManagedCertificates fetches SslCertificates specified via managed-certificates annotation.
func (l *L7) getManagedCertificates() ([]*compute.SslCertificate, bool, error) {
	if l.runtimeInfo.ManagedCertificates == "" {
		return nil, false, nil
	}

	mcrtsNames := utils.SplitAnnotation(l.runtimeInfo.ManagedCertificates)
	mcrts, err := l.mcrt.ManagedCertificates(l.runtimeInfo.Ingress.Namespace).List(labels.Everything())
	if err != nil {
		return nil, true, err
	}

	var sslCertsNames []string
	for _, mcrt := range mcrts {
		found := false
		for _, mcrtName := range mcrtsNames {
			if mcrtName == mcrt.Name {
				found = true
				break
			}
		}

		if !found {
			continue
		}

		if mcrt.Status.CertificateName != "" {
			sslCertsNames = append(sslCertsNames, mcrt.Status.CertificateName)
		}
	}

	sslCerts, err := l.getSslCertificates(sslCertsNames)
	if err != nil {
		return sslCerts, true, fmt.Errorf("managed-certificates errors: %s", err.Error())
	}

	return sslCerts, true, nil
}

// getPreSharedCertificates fetches SslCertificates specified via pre-shared-cert annotation.
func (l *L7) getPreSharedCertificates() (bool, []*compute.SslCertificate, error) {
	if l.runtimeInfo.TLSName == "" {
		return false, nil, nil
	}
	sslCerts, err := l.getSslCertificates(utils.SplitAnnotation(l.runtimeInfo.TLSName))

	if err != nil {
		return true, sslCerts, fmt.Errorf("pre-shared-cert errors: %s", err.Error())
	}

	return true, sslCerts, nil
}

func getMapfromCertList(certs []*compute.SslCertificate) map[string]*compute.SslCertificate {
	if len(certs) == 0 {
		return nil
	}
	certMap := make(map[string]*compute.SslCertificate)
	for _, cert := range certs {
		certMap[cert.Name] = cert
	}
	return certMap
}

// getIngressManagedSslCerts fetches SslCertificate resources created and managed by this load balancer
// instance. These SslCertificate resources were created based on kubernetes secrets in Ingress
// configuration.
func (l *L7) getIngressManagedSslCerts() ([]*compute.SslCertificate, error) {
	var result []*compute.SslCertificate

	// Currently we list all certs available in gcloud and filter the ones managed by this loadbalancer instance. This is
	// to make sure we garbage collect any old certs that this instance might have lost track of due to crashes.
	// Can be a performance issue if there are too many global certs, default quota is only 10.
	certs, err := l.cloud.ListSslCertificates()
	if err != nil {
		return nil, err
	}
	for _, c := range certs {
		if l.namer.IsCertUsedForLB(l.Name, c.Name) {
			glog.V(4).Infof("Populating ssl cert %s for l7 %s", c.Name, l.Name)
			result = append(result, c)
		}
	}
	if len(result) == 0 {
		// Check for legacy cert since that follows a different naming convention
		glog.V(4).Infof("Looking for legacy ssl certs")
		expectedCertLinks, err := l.getSslCertLinkInUse()
		if err != nil {
			// Return nil if target proxy doesn't exist.
			return nil, utils.IgnoreHTTPNotFound(err)
		}
		for _, link := range expectedCertLinks {
			// Retrieve the certificate and ignore error if certificate wasn't found
			name, err := utils.KeyName(link)
			if err != nil {
				glog.Warningf("error parsing cert name: %v", err)
				continue
			}

			if !l.namer.IsLegacySSLCert(l.Name, name) {
				continue
			}
			cert, _ := l.cloud.GetSslCertificate(name)
			if cert != nil {
				glog.V(4).Infof("Populating legacy ssl cert %s for l7 %s", cert.Name, l.Name)
				result = append(result, cert)
			}
		}
	}
	return result, nil
}

func (l *L7) deleteOldSSLCerts() {
	if len(l.oldSSLCerts) == 0 {
		return
	}
	certsMap := getMapfromCertList(l.sslCerts)
	for _, cert := range l.oldSSLCerts {
		if !l.namer.IsCertUsedForLB(l.Name, cert.Name) && !l.namer.IsLegacySSLCert(l.Name, cert.Name) {
			// retain cert if it is managed by GCE(non-ingress)
			continue
		}
		if _, ok := certsMap[cert.Name]; ok {
			// cert found in current map
			continue
		}
		glog.V(3).Infof("Cleaning up old SSL Certificate %s", cert.Name)
		if certErr := utils.IgnoreHTTPNotFound(l.cloud.DeleteSslCertificate(cert.Name)); certErr != nil {
			glog.Errorf("Old cert delete failed - %v", certErr)
		}
	}
}

// Returns true if the input array of certs is identical to the certs in the L7 config.
// Returns false if there is any mismatch
func (l *L7) compareCerts(certLinks []string) bool {
	certsMap := getMapfromCertList(l.sslCerts)
	if len(certLinks) != len(certsMap) {
		glog.V(4).Infof("Loadbalancer has %d certs, target proxy has %d certs", len(certsMap), len(certLinks))
		return false
	}

	for _, link := range certLinks {
		certName, err := utils.KeyName(link)
		if err != nil {
			glog.Warningf("Cannot get cert name: %v", err)
			return false
		}

		if cert, ok := certsMap[certName]; !ok {
			glog.V(4).Infof("Cannot find cert with name %s in certsMap %+v", certName, certsMap)
			return false
		} else if ok && !utils.EqualResourceIDs(link, cert.SelfLink) {
			glog.V(4).Infof("Selflink compare failed for certs - %s in loadbalancer, %s in targetproxy", cert.SelfLink, link)
			return false
		}
	}
	return true
}

func GetCertHash(contents string) string {
	return fmt.Sprintf("%x", sha256.Sum256([]byte(contents)))[:16]
}

func toCertNames(certs []*compute.SslCertificate) (names []string) {
	for _, v := range certs {
		names = append(names, v.Name)
	}
	return names
}
<|endoftext|>


<commit_before>package group

import (
	"errors"
	"fmt"
	"sort"
	"time"

	group_types "github.com/docker/infrakit/pkg/plugin/group/types"
	"github.com/docker/infrakit/pkg/spi/flavor"
	"github.com/docker/infrakit/pkg/spi/group"
	"github.com/docker/infrakit/pkg/spi/instance"
)

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func isSelf(inst instance.Description, settings groupSettings) bool {
	if settings.self != nil {
		if inst.LogicalID != nil && *inst.LogicalID == *settings.self {
			return true
		}
		if v, has := inst.Tags[instance.LogicalIDTag]; has {
			return string(*settings.self) == v
		}
	}
	return false
}

func doNotDestroySelf(inst instance.Description, settings groupSettings) bool {
	if !isSelf(inst, settings) {
		return false
	}
	if settings.options.PolicyLeaderSelfUpdate == nil {
		return false
	}

	return *settings.options.PolicyLeaderSelfUpdate == group_types.PolicyLeaderSelfUpdateNever
}

func desiredAndUndesiredInstances(
	instances []instance.Description, settings groupSettings) ([]instance.Description, []instance.Description) {

	desiredHash := settings.config.InstanceHash()
	desired := []instance.Description{}
	undesired := []instance.Description{}

	for _, inst := range instances {

		actualConfig, specified := inst.Tags[group.ConfigSHATag]
		if specified && actualConfig == desiredHash || doNotDestroySelf(inst, settings) {
			desired = append(desired, inst)
		} else {
			undesired = append(undesired, inst)
		}
	}

	return desired, undesired
}

type rollingupdate struct {
	desc         string
	scaled       Scaled
	updatingFrom groupSettings
	updatingTo   groupSettings
	stop         chan bool
}

func (r rollingupdate) Explain() string {
	return r.desc
}

func (r *rollingupdate) waitUntilQuiesced(pollInterval time.Duration, expectedNewInstances int) error {
	// Block until the expected number of instances in the desired state are ready.  Updates are unconcerned with
	// the health of instances in the undesired state.  This allows a user to dig out of a hole where the original
	// state of the group is bad, and instances are not reporting as healthy.

	ticker := time.NewTicker(pollInterval)
	for {
		select {
		case <-ticker.C:
			// Gather instances in the scaler with the desired state
			// Check:
			//   - that the scaler has the expected number of instances
			//   - instances with the desired config are healthy

			// TODO(wfarner): Get this information from the scaler to reduce redundant network calls.
			instances, err := labelAndList(r.scaled)
			if err != nil {
				return err
			}

			// The update is only concerned with instances being created in the course of the update.
			// The health of instances in any other state is irrelevant.  This behavior is important
			// especially if the previous state of the group is unhealthy and the update is attempting to
			// restore health.
			matching, _ := desiredAndUndesiredInstances(instances, r.updatingTo)

			// Now that we have isolated the instances with the new configuration, check if they are all
			// healthy.  We do not consider an update successful until the target number of instances are
			// confirmed healthy.
			// The following design choices are currently implemented:
			//
			//   - the update will continue indefinitely if one or more instances are in the
			//     flavor.UnknownHealth state.  Operators must stop the update and diagnose the cause.
			//
			//   - the update is stopped immediately if any instance enters the flavor.Unhealthy state.
			//
			//   - the update will proceed with other instances immediately when the currently-expected
			//     number of instances are observed in the flavor.Healthy state.
			//
			numHealthy := 0
			for _, inst := range matching {
				// TODO(wfarner): More careful thought is needed with respect to blocking and timeouts
				// here.  This might mean formalizing timeout behavior for different types of RPCs in
				// the group, and/or documenting the expectations for plugin implementations.
				switch r.scaled.Health(inst) {
				case flavor.Healthy:
					numHealthy++
				case flavor.Unhealthy:
					return fmt.Errorf("Instance %s is unhealthy", inst.ID)
				}
			}

			if numHealthy >= int(expectedNewInstances) {
				return nil
			}

			log.Info("Waiting for scaler to quiesce")

		case <-r.stop:
			ticker.Stop()
			return errors.New("Update halted by user")
		}
	}
}

// Run identifies instances not matching the desired state and destroys them one at a time until all instances in the
// group match the desired state, with the desired number of instances.
// TODO(wfarner): Make this routine more resilient to transient errors.
func (r *rollingupdate) Run(pollInterval time.Duration) error {

	instances, err := labelAndList(r.scaled)
	if err != nil {
		return err
	}

	desired, _ := desiredAndUndesiredInstances(instances, r.updatingTo)
	expectedNewInstances := len(desired)

	for {
		err := r.waitUntilQuiesced(
			pollInterval,
			minInt(expectedNewInstances, int(r.updatingTo.config.Allocation.Size)))
		if err != nil {
			return err
		}
		log.Info("Scaler has quiesced")

		instances, err := labelAndList(r.scaled)
		if err != nil {
			return err
		}

		_, undesiredInstances := desiredAndUndesiredInstances(instances, r.updatingTo)

		if len(undesiredInstances) == 0 {
			break
		}

		log.Info("Found undesired instances", "count", len(undesiredInstances))

		// Sort instances first to ensure predictable destroy order.
		sort.Sort(sortByID{list: undesiredInstances, settings: &r.updatingFrom})

		// TODO(wfarner): Make the 'batch size' configurable.
		r.scaled.Destroy(undesiredInstances[0], instance.RollingUpdate)

		expectedNewInstances++
	}

	return nil
}

func (r *rollingupdate) Stop() {
	close(r.stop)
}
<commit_msg>Pace quorum group rolling updates (#844)<commit_after>package group

import (
	"errors"
	"fmt"
	"sort"
	"time"

	group_types "github.com/docker/infrakit/pkg/plugin/group/types"
	"github.com/docker/infrakit/pkg/spi/flavor"
	"github.com/docker/infrakit/pkg/spi/group"
	"github.com/docker/infrakit/pkg/spi/instance"
)

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func isSelf(inst instance.Description, settings groupSettings) bool {
	if settings.self != nil {
		if inst.LogicalID != nil && *inst.LogicalID == *settings.self {
			return true
		}
		if v, has := inst.Tags[instance.LogicalIDTag]; has {
			return string(*settings.self) == v
		}
	}
	return false
}

func doNotDestroySelf(inst instance.Description, settings groupSettings) bool {
	if !isSelf(inst, settings) {
		return false
	}
	if settings.options.PolicyLeaderSelfUpdate == nil {
		return false
	}

	return *settings.options.PolicyLeaderSelfUpdate == group_types.PolicyLeaderSelfUpdateNever
}

func desiredAndUndesiredInstances(
	instances []instance.Description, settings groupSettings) ([]instance.Description, []instance.Description) {

	desiredHash := settings.config.InstanceHash()
	desired := []instance.Description{}
	undesired := []instance.Description{}

	for _, inst := range instances {

		actualConfig, specified := inst.Tags[group.ConfigSHATag]
		if specified && actualConfig == desiredHash || doNotDestroySelf(inst, settings) {
			desired = append(desired, inst)
		} else {
			undesired = append(undesired, inst)
		}
	}

	return desired, undesired
}

type rollingupdate struct {
	desc         string
	scaled       Scaled
	updatingFrom groupSettings
	updatingTo   groupSettings
	stop         chan bool
}

func (r rollingupdate) Explain() string {
	return r.desc
}

func (r *rollingupdate) waitUntilQuiesced(pollInterval time.Duration, expectedNewInstances int) error {
	// Block until the expected number of instances in the desired state are ready.  Updates are unconcerned with
	// the health of instances in the undesired state.  This allows a user to dig out of a hole where the original
	// state of the group is bad, and instances are not reporting as healthy.
	log.Info("waitUntilQuiesced", "expectedNewInstances", expectedNewInstances)
	// TODO: start processing right away instead of waiting for first tick
	ticker := time.NewTicker(pollInterval)
	for {
		select {
		case <-ticker.C:
			// Gather instances in the scaler with the desired state
			// Check:
			//   - that the scaler has the expected number of instances
			//   - instances with the desired config are healthy

			// TODO(wfarner): Get this information from the scaler to reduce redundant network calls.
			instances, err := labelAndList(r.scaled)
			if err != nil {
				return err
			}

			// The update is only concerned with instances being created in the course of the update.
			// The health of instances in any other state is irrelevant.  This behavior is important
			// especially if the previous state of the group is unhealthy and the update is attempting to
			// restore health.
			matching, _ := desiredAndUndesiredInstances(instances, r.updatingTo)
			log.Info("waitUntilQuiesced", "totalInstances", len(instances), "matchingInstances", len(matching))

			// Now that we have isolated the instances with the new configuration, check if they are all
			// healthy.  We do not consider an update successful until the target number of instances are
			// confirmed healthy.
			// The following design choices are currently implemented:
			//
			//   - the update will continue indefinitely if one or more instances are in the
			//     flavor.UnknownHealth state.  Operators must stop the update and diagnose the cause.
			//
			//   - the update is stopped immediately if any instance enters the flavor.Unhealthy state.
			//
			//   - the update will proceed with other instances immediately when the currently-expected
			//     number of instances are observed in the flavor.Healthy state.
			//
			numHealthy := 0
			for _, inst := range matching {
				// TODO(wfarner): More careful thought is needed with respect to blocking and timeouts
				// here.  This might mean formalizing timeout behavior for different types of RPCs in
				// the group, and/or documenting the expectations for plugin implementations.
				switch r.scaled.Health(inst) {
				case flavor.Healthy:
					log.Info("waitUntilQuiesced", "health", "heathy", "nodeID", inst.ID)
					numHealthy++
				case flavor.Unhealthy:
					log.Error("waitUntilQuiesced", "health", "unheathy", "nodeID", inst.ID)
					return fmt.Errorf("Instance %s is unhealthy", inst.ID)
				case flavor.Unknown:
					log.Info("waitUntilQuiesced", "health", "unknown", "nodeID", inst.ID)
				}
			}

			if numHealthy >= int(expectedNewInstances) {
				log.Info("waitUntilQuiesced",
					"msg", "Scaler has quiesced, terminating loop",
					"numHealthy", numHealthy,
					"expectedNewInstances", expectedNewInstances)
				return nil
			}
			log.Info("waitUntilQuiesced",
				"msg", "Waiting for scaler to quiesce",
				"numHealthy", numHealthy,
				"expectedNewInstances", expectedNewInstances)

		case <-r.stop:
			ticker.Stop()
			return errors.New("Update halted by user")
		}
	}
}

// Run identifies instances not matching the desired state and destroys them one at a time until all instances in the
// group match the desired state, with the desired number of instances.
// TODO(wfarner): Make this routine more resilient to transient errors.
func (r *rollingupdate) Run(pollInterval time.Duration) error {

	instances, err := labelAndList(r.scaled)
	if err != nil {
		return err
	}

	// First determine if any new instances should be created
	desired, _ := desiredAndUndesiredInstances(instances, r.updatingTo)
	expectedNewInstances := len(desired)
	log.Info("RollingUpdate-Run", "expectedNewInstances", expectedNewInstances)

	for {
		// Wait until any new nodes are healthy
		desiredSize := len(r.updatingTo.config.Allocation.LogicalIDs)
		if desiredSize == 0 {
			desiredSize = int(r.updatingTo.config.Allocation.Size)
		}
		err := r.waitUntilQuiesced(pollInterval, minInt(expectedNewInstances, desiredSize))
		if err != nil {
			return err
		}

		instances, err := labelAndList(r.scaled)
		if err != nil {
			return err
		}

		// Now check if we have instances that do not match the hash
		_, undesiredInstances := desiredAndUndesiredInstances(instances, r.updatingTo)
		log.Info("RollingUpdate-Run", "undesiredInstances", len(undesiredInstances))
		if len(undesiredInstances) == 0 {
			break
		}

		// Sort instances first to ensure predictable destroy order.
		sort.Sort(sortByID{list: undesiredInstances, settings: &r.updatingFrom})

		// TODO(wfarner): Make the 'batch size' configurable.
		r.scaled.Destroy(undesiredInstances[0], instance.RollingUpdate)

		// Increment new instance count to replace the node that was just destroyed
		expectedNewInstances++
	}

	return nil
}

func (r *rollingupdate) Stop() {
	close(r.stop)
}
<|endoftext|>


<commit_before>package ingress

import (
	"context"
	"kourier/pkg/config"
	"kourier/pkg/envoy"

	"knative.dev/serving/pkg/apis/networking"
	"knative.dev/serving/pkg/reconciler"

	"k8s.io/client-go/tools/cache"

	kubeclient "knative.dev/pkg/client/injection/kube/client"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	knativeclient "knative.dev/serving/pkg/client/injection/client"
	ingressinformer "knative.dev/serving/pkg/client/injection/informers/networking/v1alpha1/ingress"
)

const (
	controllerName = "KourierController"
	nodeID         = "3scale-kourier-gateway"
	gatewayPort    = 19001
	managementPort = 18000
)

func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	kubernetesClient := kubeclient.Get(ctx)
	knativeClient := knativeclient.Get(ctx)

	envoyXdsServer := envoy.NewEnvoyXdsServer(
		gatewayPort,
		managementPort,
		kubernetesClient,
		knativeClient,
	)
	go envoyXdsServer.RunManagementServer()
	go envoyXdsServer.RunGateway()

	logger := logging.FromContext(ctx)

	ingressInformer := ingressinformer.Get(ctx)
	endpointsInformer := endpointsinformer.Get(ctx)

	c := &Reconciler{
		IngressLister:   ingressInformer.Lister(),
		EndpointsLister: endpointsInformer.Lister(),
		EnvoyXDSServer:  envoyXdsServer,
		kubeClient:      kubernetesClient,
	}
	impl := controller.NewImpl(c, logger, controllerName)

	// Ingresses need to be filtered by ingress class, so Kourier does not
	// react to nor modify ingresses created by other gateways.
	ingressInformerHandler := cache.FilteringResourceEventHandler{
		FilterFunc: reconciler.AnnotationFilterFunc(
			networking.IngressClassAnnotationKey, config.KourierIngressClassName, false,
		),
		Handler: controller.HandleAll(impl.Enqueue),
	}
	ingressInformer.Informer().AddEventHandler(ingressInformerHandler)

	// In this first version, we are just refreshing the whole config for any
	// endpoint that we receive. So we should always enqueue the same key.
	enqueueFunc := func(obj interface{}) {
		impl.EnqueueKey(EndpointChange)
	}

	// We only want to react to endpoints that belong to a Knative serving and
	// are public.
	endpointsInformerHandler := cache.FilteringResourceEventHandler{
		FilterFunc: reconciler.LabelFilterFunc(
			networking.ServiceTypeKey, string(networking.ServiceTypePublic), false,
		),
		Handler: controller.HandleAll(enqueueFunc),
	}

	endpointsInformer.Informer().AddEventHandler(endpointsInformerHandler)

	// Force a first event to make sure we initialize a config. Otherwise, there
	// will be no config until a Knative service is deployed.
	// This is important because the gateway pods will not be marked as healthy
	// until they have been able to fetch a config.
	impl.EnqueueKey(FullResync)

	return impl
}
<commit_msg>Use NamespacedName var instead of string<commit_after>package ingress

import (
	"context"
	"kourier/pkg/config"
	"kourier/pkg/envoy"

	"k8s.io/apimachinery/pkg/types"

	"knative.dev/serving/pkg/apis/networking"
	"knative.dev/serving/pkg/reconciler"

	"k8s.io/client-go/tools/cache"

	kubeclient "knative.dev/pkg/client/injection/kube/client"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"

	knativeclient "knative.dev/serving/pkg/client/injection/client"
	ingressinformer "knative.dev/serving/pkg/client/injection/informers/networking/v1alpha1/ingress"
)

const (
	controllerName = "KourierController"
	nodeID         = "3scale-kourier-gateway"
	gatewayPort    = 19001
	managementPort = 18000
)

func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	kubernetesClient := kubeclient.Get(ctx)
	knativeClient := knativeclient.Get(ctx)

	envoyXdsServer := envoy.NewEnvoyXdsServer(
		gatewayPort,
		managementPort,
		kubernetesClient,
		knativeClient,
	)
	go envoyXdsServer.RunManagementServer()
	go envoyXdsServer.RunGateway()

	logger := logging.FromContext(ctx)

	ingressInformer := ingressinformer.Get(ctx)
	endpointsInformer := endpointsinformer.Get(ctx)

	c := &Reconciler{
		IngressLister:   ingressInformer.Lister(),
		EndpointsLister: endpointsInformer.Lister(),
		EnvoyXDSServer:  envoyXdsServer,
		kubeClient:      kubernetesClient,
	}
	impl := controller.NewImpl(c, logger, controllerName)

	// Ingresses need to be filtered by ingress class, so Kourier does not
	// react to nor modify ingresses created by other gateways.
	ingressInformerHandler := cache.FilteringResourceEventHandler{
		FilterFunc: reconciler.AnnotationFilterFunc(
			networking.IngressClassAnnotationKey, config.KourierIngressClassName, false,
		),
		Handler: controller.HandleAll(impl.Enqueue),
	}
	ingressInformer.Informer().AddEventHandler(ingressInformerHandler)

	// In this first version, we are just refreshing the whole config for any
	// endpoint that we receive. So we should always enqueue the same key.
	enqueueFunc := func(obj interface{}) {
		event := types.NamespacedName{
			Name: EndpointChange,
		}
		impl.EnqueueKey(event)
	}

	// We only want to react to endpoints that belong to a Knative serving and
	// are public.
	endpointsInformerHandler := cache.FilteringResourceEventHandler{
		FilterFunc: reconciler.LabelFilterFunc(
			networking.ServiceTypeKey, string(networking.ServiceTypePublic), false,
		),
		Handler: controller.HandleAll(enqueueFunc),
	}

	endpointsInformer.Informer().AddEventHandler(endpointsInformerHandler)

	// Force a first event to make sure we initialize a config. Otherwise, there
	// will be no config until a Knative service is deployed.
	// This is important because the gateway pods will not be marked as healthy
	// until they have been able to fetch a config.
	event := types.NamespacedName{
		Name: FullResync,
	}
	impl.EnqueueKey(event)

	return impl
}
<|endoftext|>


<commit_before>package podtask

import (
	"container/ring"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	log "github.com/golang/glog"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesosphere/kubernetes-mesos/pkg/scheduler/metrics"
)

/**
HACK(jdef): we're not using etcd but k8s has implemented namespace support and
we're going to try to honor that by namespacing pod keys. Hence, the following
funcs that were stolen from:
    https://github.com/GoogleCloudPlatform/kubernetes/blob/release-0.5/pkg/registry/etcd/etcd.go
**/

const (
	PodPath                  = "/pods"
	defaultFinishedTasksSize = 1024
)

type Registry interface {
	Register(*T, error) (*T, error)
	Unregister(*T)
	Get(taskId string) (task *T, currentState StateType)
	TaskForPod(podID string) (taskID string, ok bool)
	UpdateStatus(status *mesos.TaskStatus) (*T, StateType)
	// return a list of task ID's that match the given filter, or all task ID's if filter == nil
	List(func(*T) bool) []string
}

type inMemoryRegistry struct {
	rw            sync.RWMutex
	taskRegistry  map[string]*T
	tasksFinished *ring.Ring
	podToTask     map[string]string
}

func NewInMemoryRegistry() Registry {
	return &inMemoryRegistry{
		taskRegistry:  make(map[string]*T),
		tasksFinished: ring.New(defaultFinishedTasksSize),
		podToTask:     make(map[string]string),
	}
}

func (k *inMemoryRegistry) List(accepts func(t *T) bool) (taskids []string) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	for id, task := range k.taskRegistry {
		if accepts == nil || accepts(task) {
			taskids = append(taskids, id)
		}
	}
	return
}

func (k *inMemoryRegistry) TaskForPod(podID string) (taskID string, ok bool) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	// assume caller is holding scheduler lock
	taskID, ok = k.podToTask[podID]
	return
}

// registers a pod task unless the spec'd error is not nil
func (k *inMemoryRegistry) Register(task *T, err error) (*T, error) {
	if err == nil {
		k.rw.Lock()
		defer k.rw.Unlock()
		k.podToTask[task.podKey] = task.ID
		k.taskRegistry[task.ID] = task
	}
	return task, err
}

func (k *inMemoryRegistry) Unregister(task *T) {
	k.rw.Lock()
	defer k.rw.Unlock()
	delete(k.podToTask, task.podKey)
	delete(k.taskRegistry, task.ID)
}

func (k *inMemoryRegistry) Get(taskId string) (*T, StateType) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	return k._get(taskId)
}

// assume that the caller has already locked around access to task state
func (k *inMemoryRegistry) _get(taskId string) (*T, StateType) {
	if task, found := k.taskRegistry[taskId]; found {
		return task, task.State
	}
	return nil, StateUnknown
}

func (k *inMemoryRegistry) UpdateStatus(status *mesos.TaskStatus) (*T, StateType) {
	taskId := status.GetTaskId().GetValue()

	k.rw.Lock()
	defer k.rw.Unlock()
	task, state := k._get(taskId)

	switch status.GetState() {
	case mesos.TaskState_TASK_STAGING:
		k.handleTaskStaging(task, state, status)
	case mesos.TaskState_TASK_STARTING:
		k.handleTaskStarting(task, state, status)
	case mesos.TaskState_TASK_RUNNING:
		k.handleTaskRunning(task, state, status)
	case mesos.TaskState_TASK_FINISHED:
		k.handleTaskFinished(task, state, status)
	case mesos.TaskState_TASK_FAILED:
		k.handleTaskFailed(task, state, status)
	case mesos.TaskState_TASK_KILLED:
		k.handleTaskKilled(task, state, status)
	case mesos.TaskState_TASK_LOST:
		k.handleTaskLost(task, state, status)
	default:
		log.Warning("unhandled task status update: %+v", status)
	}
	return task, state
}

func (k *inMemoryRegistry) handleTaskStaging(task *T, state StateType, status *mesos.TaskStatus) {
	//TODO(jdef) validate that update source is SOURCE_MASTER, otherwise the system is doing something wonky
	log.Errorf("Not implemented: task staging")
}

func (k *inMemoryRegistry) handleTaskStarting(task *T, state StateType, status *mesos.TaskStatus) {
	// we expect to receive this when a launched task is finally "bound"
	// via the API server. however, there's nothing specific for us to do
	// here.
	switch state {
	case StatePending:
		task.UpdatedTime = time.Now()
		if !task.Has(Bound) {
			task.Set(Bound)
			task.bindTime = task.UpdatedTime
			timeToBind := task.bindTime.Sub(task.launchTime)
			metrics.BindLatency.Observe(metrics.InMicroseconds(timeToBind))
		}
	default:
		log.Warningf("Ignore status TASK_STARTING because the the task is not pending")
	}
}

func (k *inMemoryRegistry) handleTaskRunning(task *T, state StateType, status *mesos.TaskStatus) {
	switch state {
	case StatePending:
		task.UpdatedTime = time.Now()
		log.Infof("Received running status for pending task: %+v", status)
		fillRunningPodInfo(task, status)
		task.State = StateRunning
	case StateRunning:
		task.UpdatedTime = time.Now()
		log.V(2).Info("Ignore status TASK_RUNNING because the the task is already running")
	case StateFinished:
		log.Warningf("Ignore status TASK_RUNNING because the the task is already finished")
	default:
		log.Warningf("Ignore status TASK_RUNNING (%+v) because the the task is discarded", status.GetTaskId())
	}
}

func ParsePodStatusResult(taskStatus *mesos.TaskStatus) (result api.PodStatusResult, err error) {
	if taskStatus.Data != nil {
		err = json.Unmarshal(taskStatus.Data, &result)
	} else {
		err = fmt.Errorf("missing TaskStatus.Data")
	}
	return
}

func fillRunningPodInfo(task *T, taskStatus *mesos.TaskStatus) {
	if result, err := ParsePodStatusResult(taskStatus); err != nil {
		log.Errorf("invalid TaskStatus.Data for task '%v': %v", task.ID, err)
	} else {
		task.Pod.Status = result.Status
		log.Infof("received pod status for task %v: %+v", task.ID, task.Pod.Status)
	}
}

func (k *inMemoryRegistry) handleTaskFinished(task *T, state StateType, status *mesos.TaskStatus) {
	switch state {
	case StatePending:
		panic("Pending task finished, this couldn't happen")
	case StateRunning:
		log.V(2).Infof("received finished status for running task: %+v", status)
		delete(k.podToTask, task.podKey)
		task.State = StateFinished
		task.UpdatedTime = time.Now()
		k.tasksFinished = k.recordFinishedTask(task.ID)
	case StateFinished:
		log.Warningf("Ignore status TASK_FINISHED because the the task is already finished")
	default:
		log.Warningf("Ignore status TASK_FINISHED because the the task is not running")
	}
}

// record that a task has finished.
// older record are expunged one at a time once the historical ring buffer is saturated.
// assumes caller is holding state lock.
func (k *inMemoryRegistry) recordFinishedTask(taskId string) *ring.Ring {
	slot := k.tasksFinished.Next()
	if slot.Value != nil {
		// garbage collect older finished task from the registry
		gctaskId := slot.Value.(string)
		if gctask, found := k.taskRegistry[gctaskId]; found && gctask.State == StateFinished {
			delete(k.taskRegistry, gctaskId)
		}
	}
	slot.Value = taskId
	return slot
}

func (k *inMemoryRegistry) handleTaskFailed(task *T, state StateType, status *mesos.TaskStatus) {
	log.Errorf("task failed: %+v", status)
	switch state {
	case StatePending:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	case StateRunning:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}

func (k *inMemoryRegistry) handleTaskKilled(task *T, state StateType, status *mesos.TaskStatus) {
	defer func() {
		msg := fmt.Sprintf("task killed: %+v, task %+v", status, task)
		if task != nil && task.Has(Deleted) {
			// we were expecting this, nothing out of the ordinary
			log.V(2).Infoln(msg)
		} else {
			log.Errorln(msg)
		}
	}()
	switch state {
	case StatePending, StateRunning:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}

func (k *inMemoryRegistry) handleTaskLost(task *T, state StateType, status *mesos.TaskStatus) {
	log.Warningf("task lost: %+v", status)
	switch state {
	case StateRunning, StatePending:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}
<commit_msg>resolve TODO for staging tasks, check that source == MASTER<commit_after>package podtask

import (
	"container/ring"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	log "github.com/golang/glog"
	mesos "github.com/mesos/mesos-go/mesosproto"
	"github.com/mesosphere/kubernetes-mesos/pkg/scheduler/metrics"
)

/**
HACK(jdef): we're not using etcd but k8s has implemented namespace support and
we're going to try to honor that by namespacing pod keys. Hence, the following
funcs that were stolen from:
    https://github.com/GoogleCloudPlatform/kubernetes/blob/release-0.5/pkg/registry/etcd/etcd.go
**/

const (
	PodPath                  = "/pods"
	defaultFinishedTasksSize = 1024
)

type Registry interface {
	Register(*T, error) (*T, error)
	Unregister(*T)
	Get(taskId string) (task *T, currentState StateType)
	TaskForPod(podID string) (taskID string, ok bool)
	UpdateStatus(status *mesos.TaskStatus) (*T, StateType)
	// return a list of task ID's that match the given filter, or all task ID's if filter == nil
	List(func(*T) bool) []string
}

type inMemoryRegistry struct {
	rw            sync.RWMutex
	taskRegistry  map[string]*T
	tasksFinished *ring.Ring
	podToTask     map[string]string
}

func NewInMemoryRegistry() Registry {
	return &inMemoryRegistry{
		taskRegistry:  make(map[string]*T),
		tasksFinished: ring.New(defaultFinishedTasksSize),
		podToTask:     make(map[string]string),
	}
}

func (k *inMemoryRegistry) List(accepts func(t *T) bool) (taskids []string) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	for id, task := range k.taskRegistry {
		if accepts == nil || accepts(task) {
			taskids = append(taskids, id)
		}
	}
	return
}

func (k *inMemoryRegistry) TaskForPod(podID string) (taskID string, ok bool) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	// assume caller is holding scheduler lock
	taskID, ok = k.podToTask[podID]
	return
}

// registers a pod task unless the spec'd error is not nil
func (k *inMemoryRegistry) Register(task *T, err error) (*T, error) {
	if err == nil {
		k.rw.Lock()
		defer k.rw.Unlock()
		k.podToTask[task.podKey] = task.ID
		k.taskRegistry[task.ID] = task
	}
	return task, err
}

func (k *inMemoryRegistry) Unregister(task *T) {
	k.rw.Lock()
	defer k.rw.Unlock()
	delete(k.podToTask, task.podKey)
	delete(k.taskRegistry, task.ID)
}

func (k *inMemoryRegistry) Get(taskId string) (*T, StateType) {
	k.rw.RLock()
	defer k.rw.RUnlock()
	return k._get(taskId)
}

// assume that the caller has already locked around access to task state
func (k *inMemoryRegistry) _get(taskId string) (*T, StateType) {
	if task, found := k.taskRegistry[taskId]; found {
		return task, task.State
	}
	return nil, StateUnknown
}

func (k *inMemoryRegistry) UpdateStatus(status *mesos.TaskStatus) (*T, StateType) {
	taskId := status.GetTaskId().GetValue()

	k.rw.Lock()
	defer k.rw.Unlock()
	task, state := k._get(taskId)

	switch status.GetState() {
	case mesos.TaskState_TASK_STAGING:
		k.handleTaskStaging(task, state, status)
	case mesos.TaskState_TASK_STARTING:
		k.handleTaskStarting(task, state, status)
	case mesos.TaskState_TASK_RUNNING:
		k.handleTaskRunning(task, state, status)
	case mesos.TaskState_TASK_FINISHED:
		k.handleTaskFinished(task, state, status)
	case mesos.TaskState_TASK_FAILED:
		k.handleTaskFailed(task, state, status)
	case mesos.TaskState_TASK_KILLED:
		k.handleTaskKilled(task, state, status)
	case mesos.TaskState_TASK_LOST:
		k.handleTaskLost(task, state, status)
	default:
		log.Warning("unhandled task status update: %+v", status)
	}
	return task, state
}

func (k *inMemoryRegistry) handleTaskStaging(task *T, state StateType, status *mesos.TaskStatus) {
	if status.GetSource() != mesos.TaskStatus_SOURCE_MASTER {
		log.Errorf("received STAGING for task %v with unexpected source: %v",
			status.GetTaskId().GetValue(), status.GetSource())
	}
}

func (k *inMemoryRegistry) handleTaskStarting(task *T, state StateType, status *mesos.TaskStatus) {
	// we expect to receive this when a launched task is finally "bound"
	// via the API server. however, there's nothing specific for us to do
	// here.
	switch state {
	case StatePending:
		task.UpdatedTime = time.Now()
		if !task.Has(Bound) {
			task.Set(Bound)
			task.bindTime = task.UpdatedTime
			timeToBind := task.bindTime.Sub(task.launchTime)
			metrics.BindLatency.Observe(metrics.InMicroseconds(timeToBind))
		}
	default:
		log.Warningf("Ignore status TASK_STARTING because the the task is not pending")
	}
}

func (k *inMemoryRegistry) handleTaskRunning(task *T, state StateType, status *mesos.TaskStatus) {
	switch state {
	case StatePending:
		task.UpdatedTime = time.Now()
		log.Infof("Received running status for pending task: %+v", status)
		fillRunningPodInfo(task, status)
		task.State = StateRunning
	case StateRunning:
		task.UpdatedTime = time.Now()
		log.V(2).Info("Ignore status TASK_RUNNING because the the task is already running")
	case StateFinished:
		log.Warningf("Ignore status TASK_RUNNING because the the task is already finished")
	default:
		log.Warningf("Ignore status TASK_RUNNING (%+v) because the the task is discarded", status.GetTaskId())
	}
}

func ParsePodStatusResult(taskStatus *mesos.TaskStatus) (result api.PodStatusResult, err error) {
	if taskStatus.Data != nil {
		err = json.Unmarshal(taskStatus.Data, &result)
	} else {
		err = fmt.Errorf("missing TaskStatus.Data")
	}
	return
}

func fillRunningPodInfo(task *T, taskStatus *mesos.TaskStatus) {
	if result, err := ParsePodStatusResult(taskStatus); err != nil {
		log.Errorf("invalid TaskStatus.Data for task '%v': %v", task.ID, err)
	} else {
		task.Pod.Status = result.Status
		log.Infof("received pod status for task %v: %+v", task.ID, task.Pod.Status)
	}
}

func (k *inMemoryRegistry) handleTaskFinished(task *T, state StateType, status *mesos.TaskStatus) {
	switch state {
	case StatePending:
		panic("Pending task finished, this couldn't happen")
	case StateRunning:
		log.V(2).Infof("received finished status for running task: %+v", status)
		delete(k.podToTask, task.podKey)
		task.State = StateFinished
		task.UpdatedTime = time.Now()
		k.tasksFinished = k.recordFinishedTask(task.ID)
	case StateFinished:
		log.Warningf("Ignore status TASK_FINISHED because the the task is already finished")
	default:
		log.Warningf("Ignore status TASK_FINISHED because the the task is not running")
	}
}

// record that a task has finished.
// older record are expunged one at a time once the historical ring buffer is saturated.
// assumes caller is holding state lock.
func (k *inMemoryRegistry) recordFinishedTask(taskId string) *ring.Ring {
	slot := k.tasksFinished.Next()
	if slot.Value != nil {
		// garbage collect older finished task from the registry
		gctaskId := slot.Value.(string)
		if gctask, found := k.taskRegistry[gctaskId]; found && gctask.State == StateFinished {
			delete(k.taskRegistry, gctaskId)
		}
	}
	slot.Value = taskId
	return slot
}

func (k *inMemoryRegistry) handleTaskFailed(task *T, state StateType, status *mesos.TaskStatus) {
	log.Errorf("task failed: %+v", status)
	switch state {
	case StatePending:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	case StateRunning:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}

func (k *inMemoryRegistry) handleTaskKilled(task *T, state StateType, status *mesos.TaskStatus) {
	defer func() {
		msg := fmt.Sprintf("task killed: %+v, task %+v", status, task)
		if task != nil && task.Has(Deleted) {
			// we were expecting this, nothing out of the ordinary
			log.V(2).Infoln(msg)
		} else {
			log.Errorln(msg)
		}
	}()
	switch state {
	case StatePending, StateRunning:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}

func (k *inMemoryRegistry) handleTaskLost(task *T, state StateType, status *mesos.TaskStatus) {
	log.Warningf("task lost: %+v", status)
	switch state {
	case StateRunning, StatePending:
		delete(k.taskRegistry, task.ID)
		delete(k.podToTask, task.podKey)
	}
}
<|endoftext|>


<commit_before>package controller
<commit_msg>Add controller placeholder/notes<commit_after>package controller

//import (
//	. "github.com/Aptomi/aptomi/pkg/slinga/object"
//)

type RevisionController interface {
	/*
		router.GET("/api/v1/revision/:rev/policy", h.handleGetPolicy)               // get full policy from specific revision
		router.GET("/api/v1/revision/:rev/policy/key/:key", h.handleGetPolicy)      // get by key from specific revision
		router.GET("/api/v1/revision/:rev/policy/namespace/:ns", h.handleGetPolicy) // get policy for namespace from specific revision

		router.POST("/api/v1/revision", h.handleNewRevision)
	*/

	//GetRevision(generation Generation)
}

type RevisionControllerImpl struct {
}

func NewRevisionController() RevisionController {
	return &RevisionControllerImpl{}
}

/*

func (reg *Registry) LoadPolicy(gen Generation) (*PolicyNamespace, error) {
	policyObj, err := reg.store.GetNewestOne("system", PolicyNamespaceDataObject.Kind, "main")
	if err != nil {
		return nil, err
	}
	policyData, ok := policyObj.(*PolicyNamespaceData)
	if !ok {
		return nil, fmt.Errorf("Can't cast object from store to PolicyData: %v", policyObj)
	}

	policy := NewPolicyNamespace()

	keys := make([]Key, 0, len(policyData.Objects))
	for _, key := range policyData.Objects {
		keys = append(keys, key)
	}

	objects, err := reg.store.GetManyByKeys(keys)
	if err != nil {
		return nil, fmt.Errorf("Can't load objects for policy data %s: %s", policyData.GetKey(), err)
	}

	for _, obj := range objects {
		fmt.Println("Loaded object")
		fmt.Println(obj)
		policy.AddObject(obj)
	}

	return policy, nil
}

*/
<|endoftext|>


<commit_before>package libvirt

import (
  lv "github.com/libvirt/libvirt-go"
  "github.com/influxdata/telegraf"
  "github.com/influxdata/telegraf/plugins/inputs"
)

const sampleConfig = `
# specify a libvirt connection uri
uri = "qemu:///system"
`

type Libvirt struct {
  Uri string
}

func (l *Libvirt) SampleConfig() string {
  return sampleConfig
}

func (l *Libvirt) Description() string {
  return "Read domain infos from a libvirt deamon"
}

func (l *Libvirt) Gather(acc telegraf.Accumulator) error {
  connection, err := lv.NewConnectReadOnly(l.Uri)
  if err != nil {
    return err
  }
  defer connection.Close()

  domains, err := connection.ListAllDomains(lv.CONNECT_LIST_DOMAINS_ACTIVE)
  if err != nil {
    return err
  }

  for _, domain := range domains {
    domainInfo, err := domain.GetInfo()
    if err != nil {
      return err
    }

    uid, err := domain.GetUUIDString()
    if err != nil {
      return err
    }
    tags := map[string]string{"vm": uid, "cloud": "new"}
    acc.AddFields("vm.cpu_time", map[string]interface{}{"value": float64(domainInfo.CpuTime)} , tags)
    acc.AddFields("vm.max_mem", map[string]interface{}{"value": float64(domainInfo.MaxMem)}, tags)
    acc.AddFields("vm.memory", map[string]interface{}{"value": float64(domainInfo.Memory)}, tags)
    acc.AddFields("vm.nr_virt_cpu", map[string]interface{}{"value": float64(domainInfo.NrVirtCpu)}, tags)

    GatherInterfaces(*connection, domain, acc, tags)

    GatherDisks(*connection, domain, acc, tags)
    domain.Free()
  }

  return nil
}

func GatherInterfaces(c lv.Connect, d lv.Domain, acc telegraf.Accumulator , tags map[string]string) error {
  domStat, err := c.GetAllDomainStats(
    []*lv.Domain{&d},
    lv.DOMAIN_STATS_INTERFACE,
    lv.CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE,
  )
  if err != nil {
    return err
  }
  defer domStat[0].Domain.Free()
  for _, iface := range domStat[0].Net {
    tags["name"] = iface.Name
    acc.AddFields("vm.interface.rx_bytes", map[string]interface{}{"value": float64(iface.RxBytes)}, tags)
    acc.AddFields("vm.interface.rx_packets", map[string]interface{}{"value": float64(iface.RxPkts)}, tags)
    acc.AddFields("vm.interface.rx_errs", map[string]interface{}{"value": float64(iface.RxErrs)}, tags)
    acc.AddFields("vm.interface.rx_drop", map[string]interface{}{"value": float64(iface.RxDrop)}, tags)
    acc.AddFields("vm.interface.tx_bytes", map[string]interface{}{"value": float64(iface.TxBytes)}, tags)
    acc.AddFields("vm.interface.tx_packets", map[string]interface{}{"value": float64(iface.TxPkts)}, tags)
    acc.AddFields("vm.interface.tx_errs", map[string]interface{}{"value": float64(iface.TxErrs)}, tags)
    acc.AddFields("vm.interface.tx_drop", map[string]interface{}{"value": float64(iface.TxDrop)}, tags)
    delete(tags, "name")
  }
  return nil
}

func GatherDisks(c lv.Connect, d lv.Domain, acc telegraf.Accumulator , tags map[string]string) error {
  domStats, err := c.GetAllDomainStats(
    []*lv.Domain{&d},
    lv.DOMAIN_STATS_BLOCK,
    lv.CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE,
  )
  if err != nil {
    return err
  }
  defer domStats[0].Domain.Free()
  for _, disk := range domStats[0].Block {
    tags["name"] = disk.Name
    acc.AddFields("vm.disk.rd_req", map[string]interface{}{"value": float64(disk.RdReqs)}, tags)
    acc.AddFields("vm.disk.rd_bytes", map[string]interface{}{"value": float64(disk.RdBytes)}, tags)
    acc.AddFields("vm.disk.wr_req", map[string]interface{}{"value": float64(disk.WrReqs)}, tags)
    acc.AddFields("vm.disk.wr_bytes", map[string]interface{}{"value": float64(disk.WrBytes)}, tags)
    acc.AddFields("vm.disk.errs", map[string]interface{}{"value": float64(disk.Errors)}, tags)
    delete(tags, "name")
  }
  return nil
}

func init() {
  inputs.Add("libvirt", func() telegraf.Input {
    return &Libvirt{}
  })
}
<commit_msg>different measurements<commit_after>package libvirt

import (
  lv "github.com/libvirt/libvirt-go"
  "github.com/influxdata/telegraf"
  "github.com/influxdata/telegraf/plugins/inputs"
)

const sampleConfig = `
# specify a libvirt connection uri
uri = "qemu:///system"
`

type Libvirt struct {
  Uri string
}

func (l *Libvirt) SampleConfig() string {
  return sampleConfig
}

func (l *Libvirt) Description() string {
  return "Read domain infos from a libvirt deamon"
}

func (l *Libvirt) Gather(acc telegraf.Accumulator) error {
  connection, err := lv.NewConnectReadOnly(l.Uri)
  if err != nil {
    return err
  }
  defer connection.Close()

  domains, err := connection.ListAllDomains(lv.CONNECT_LIST_DOMAINS_ACTIVE)
  if err != nil {
    return err
  }

  for _, domain := range domains {
    domainInfo, err := domain.GetInfo()
    if err != nil {
      return err
    }

    uuid, err := domain.GetUUIDString()
    if err != nil {
      return err
    }
    fields := map[string]interface{}{
      "vm": uuid,
    }
    tags := make(map[string]string)
    fields["cpu_time"] = domainInfo.CpuTime
    fields["nr_virt_cpu"] = domainInfo.NrVirtCpu

    stats, err := domain.MemoryStats(10,0)
    if err != nil {
      return err
    }
    m := map[int32]string{
      int32(lv.DOMAIN_MEMORY_STAT_AVAILABLE): "mem_max",
      int32(lv.DOMAIN_MEMORY_STAT_USABLE): "mem_free",
    }
    for _, stat := range stats {
      if val, ok := m[stat.Tag]; ok {
        fields[val] = stat.Val
      }
    }
    acc.AddFields("vm.data", fields, tags)

    GatherInterfaces(*connection, domain, acc, uuid)

    GatherDisks(*connection, domain, acc, uuid)

    domain.Free()
  }

  return nil
}

func GatherInterfaces(c lv.Connect, d lv.Domain, acc telegraf.Accumulator, uuid string) error {
  domStat, err := c.GetAllDomainStats(
    []*lv.Domain{&d},
    lv.DOMAIN_STATS_INTERFACE,
    lv.CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE,
  )
  if err != nil {
    return err
  }
  defer domStat[0].Domain.Free()
  for _, iface := range domStat[0].Net {
    fields := map[string]interface{}{
      "vm"         : uuid,
      "rx_bytes"   : iface.RxBytes,
      "rx_packets" : iface.RxPkts,
      "rx_errs"    : iface.RxErrs,
      "rx_drop"    : iface.RxDrop,
      "tx_bytes"   : iface.TxBytes,
      "tx_packets" : iface.TxPkts,
      "tx_errs"    : iface.TxErrs,
      "tx_drop"    : iface.TxDrop,
    }
    acc.AddFields("vm.data", fields, map[string]string{"interface": iface.Name})
  }
  return nil
}

func GatherDisks(c lv.Connect, d lv.Domain, acc telegraf.Accumulator, uuid string) error {
  domStats, err := c.GetAllDomainStats(
    []*lv.Domain{&d},
    lv.DOMAIN_STATS_BLOCK,
    lv.CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE,
  )
  if err != nil {
    return err
  }
  defer domStats[0].Domain.Free()
  for _, disk := range domStats[0].Block {
    fields := map[string]interface{}{
      "vm"       : uuid,
      "rd_req"   : disk.RdReqs,
      "rd_bytes" : disk.RdBytes,
      "wr_req"   : disk.WrReqs,
      "wr_bytes" : disk.WrBytes,
      "errs"     : disk.Errors,
    }
    acc.AddFields("vm.data", fields, map[string]string{"disk": disk.Name})
  }
  return nil
}

func init() {
  inputs.Add("libvirt", func() telegraf.Input {
    return &Libvirt{}
  })
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "fmt"
    "math"
    "net"
    "net/http"
    "os"
    "strconv"
    "sync/atomic"
    "time"

    "amproxy/envparse"
    "amproxy/message"
)

type counters struct {
    Connections uint64 `json:"connections"`
    BadSkew uint64 `json:"badskew"`
    BadSig uint64 `json:"badsig"`
    BadKeyundef uint64 `json:"badkeyundef"`
    BadDecompose uint64 `json:"baddecompose"`
    BadMetric uint64 `json:"badmetric"`
    BadCarbonconn uint64 `json:"badcarbonconn"`
    BadCarbonwrite uint64 `json:"badcarbonwrite"`
    GoodMetric uint64 `json:"goodmetric"`
}

var cServerAddr *net.TCPAddr
var skew float64
var authMap map[string]Creds
var c counters

func handler(w http.ResponseWriter, r *http.Request) {
    b, err := json.Marshal(c)
    if err  == nil {
        fmt.Fprintf(w, "%s", b)
    }
}

func main() {
    var err error

    c.Connections = 0
    c.BadSkew = 0
    c.BadSig = 0
    c.BadKeyundef = 0
    c.BadDecompose = 0
    c.BadMetric = 0
    c.BadCarbonwrite = 0
    c.BadCarbonconn = 0
    c.GoodMetric = 0

    // Read config from the environment
    var bInterface = envparse.GetSettingStr("BIND_INTERFACE", "127.0.0.1")
    var bPort      = envparse.GetSettingInt("BIND_PORT", 2005)
    var cServer    = envparse.GetSettingStr("CARBON_SERVER", "localhost")
    var cPort      = envparse.GetSettingInt("CARBON_PORT", 2003)
    var authFile   = envparse.GetSettingStr("AUTH_FILE", "")
    skew           = float64(envparse.GetSettingInt("SKEW", 300))

    if(authFile == "") {
        println("No auth file passed")
        os.Exit(1)
    }

    authMap = loadUserConfigFile(authFile)

    cServerAddr, err = net.ResolveTCPAddr("tcp", cServer + ":" + strconv.Itoa(cPort))
    if err != nil {
        println("Unable to resolve carbon server: ", err.Error())
        os.Exit(1)
    }
    fmt.Printf("Carbon server: %s:%d\n", cServer, cPort)

    // Set up the metrics http server
    go func() {
        http.HandleFunc("/", handler)
        http.ListenAndServe(":8080", nil)
    }()

    // Listen for incoming connections.
    l, err := net.Listen("tcp", bInterface + ":" + strconv.Itoa(bPort))
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    // Close the listener when the application closes.
    defer l.Close()
    fmt.Println("Listening on " + bInterface + ":" + strconv.Itoa(bPort))

    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }
        // Handle connections in a new goroutine.
        atomic.AddUint64(&c.Connections, 1)
        go handleRequest(conn)
    }
}

func handleRequest(conn net.Conn) {
    defer conn.Close()

    fmt.Println("Connection from: ", conn.RemoteAddr())

    // connect to carbon server
    carbon_conn, err := net.DialTCP("tcp", nil, cServerAddr)
    if err != nil {
        atomic.AddUint64(&c.BadCarbonconn, 1)
        println("Connection to carbon server failed:", err.Error())
        return
    }
    defer carbon_conn.Close()

    var buf [1024]byte
    for {
        n, err := conn.Read(buf[0:])
        if err != nil {
            return
        }

        fmt.Println(string(buf[:n]))
        msg := new(message.Message)
        e := msg.Decompose(string(buf[:n]))
        if e != nil {
            atomic.AddUint64(&c.BadDecompose, 1)
            fmt.Printf("Error decomposing message %q - %s\n", string(buf[:n]), e.Error())
            return
        }

        creds, ok := authMap[msg.Public_key]

        if !ok {
            atomic.AddUint64(&c.BadKeyundef, 1)
            fmt.Printf("key not defined for %s\n", msg.Public_key)
            return
        }

        sig := msg.ComputeSignature(creds.SecretKey)

        if sig != msg.Signature {
            atomic.AddUint64(&c.BadSig, 1)
            fmt.Printf("Computed signature %s doesn't match provided signature %s\n", sig, msg.Signature)
            return
        }

        delta := math.Abs(float64(time.Now().Unix() - int64(msg.Timestamp)))
        if delta > skew {
            atomic.AddUint64(&c.BadSkew, 1)
            fmt.Printf("delta = %.0f, max skew set to %.0f\n", delta, skew)
            return
        }

        // validate the metric is on the approved list
        _, ok = creds.Metrics[msg.Name]
        if !ok {
            atomic.AddUint64(&c.BadMetric, 1)
            fmt.Printf("not an approved metric: %s", msg.Name)
            return
        }

        fmt.Println(msg.Public_key)
        fmt.Println(sig)

        _, err = carbon_conn.Write([]byte(msg.MetricStr() + "\n"))
        if err != nil {
            atomic.AddUint64(&c.BadCarbonwrite, 1)
            println("Write to carbon server failed:", err.Error())
            return
        }
        atomic.AddUint64(&c.GoodMetric, 1)

        // write the n bytes read
        _, err2 := conn.Write(buf[0:n])
        if err2 != nil {
            return
        }
    }
}
<commit_msg>set content type header<commit_after>package main

import (
    "encoding/json"
    "fmt"
    "math"
    "net"
    "net/http"
    "os"
    "strconv"
    "sync/atomic"
    "time"

    "amproxy/envparse"
    "amproxy/message"
)

type counters struct {
    Connections uint64 `json:"connections"`
    BadSkew uint64 `json:"badskew"`
    BadSig uint64 `json:"badsig"`
    BadKeyundef uint64 `json:"badkeyundef"`
    BadDecompose uint64 `json:"baddecompose"`
    BadMetric uint64 `json:"badmetric"`
    BadCarbonconn uint64 `json:"badcarbonconn"`
    BadCarbonwrite uint64 `json:"badcarbonwrite"`
    GoodMetric uint64 `json:"goodmetric"`
}

var cServerAddr *net.TCPAddr
var skew float64
var authMap map[string]Creds
var c counters

func handler(w http.ResponseWriter, r *http.Request) {
    b, err := json.Marshal(c)

    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(b)
}

func main() {
    var err error

    c.Connections = 0
    c.BadSkew = 0
    c.BadSig = 0
    c.BadKeyundef = 0
    c.BadDecompose = 0
    c.BadMetric = 0
    c.BadCarbonwrite = 0
    c.BadCarbonconn = 0
    c.GoodMetric = 0

    // Read config from the environment
    var bInterface = envparse.GetSettingStr("BIND_INTERFACE", "127.0.0.1")
    var bPort      = envparse.GetSettingInt("BIND_PORT", 2005)
    var cServer    = envparse.GetSettingStr("CARBON_SERVER", "localhost")
    var cPort      = envparse.GetSettingInt("CARBON_PORT", 2003)
    var authFile   = envparse.GetSettingStr("AUTH_FILE", "")
    skew           = float64(envparse.GetSettingInt("SKEW", 300))

    if(authFile == "") {
        println("No auth file passed")
        os.Exit(1)
    }

    authMap = loadUserConfigFile(authFile)

    cServerAddr, err = net.ResolveTCPAddr("tcp", cServer + ":" + strconv.Itoa(cPort))
    if err != nil {
        println("Unable to resolve carbon server: ", err.Error())
        os.Exit(1)
    }
    fmt.Printf("Carbon server: %s:%d\n", cServer, cPort)

    // Set up the metrics http server
    go func() {
        http.HandleFunc("/", handler)
        http.ListenAndServe(":8080", nil)
    }()

    // Listen for incoming connections.
    l, err := net.Listen("tcp", bInterface + ":" + strconv.Itoa(bPort))
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    // Close the listener when the application closes.
    defer l.Close()
    fmt.Println("Listening on " + bInterface + ":" + strconv.Itoa(bPort))

    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }
        // Handle connections in a new goroutine.
        atomic.AddUint64(&c.Connections, 1)
        go handleRequest(conn)
    }
}

func handleRequest(conn net.Conn) {
    defer conn.Close()

    fmt.Println("Connection from: ", conn.RemoteAddr())

    // connect to carbon server
    carbon_conn, err := net.DialTCP("tcp", nil, cServerAddr)
    if err != nil {
        atomic.AddUint64(&c.BadCarbonconn, 1)
        println("Connection to carbon server failed:", err.Error())
        return
    }
    defer carbon_conn.Close()

    var buf [1024]byte
    for {
        n, err := conn.Read(buf[0:])
        if err != nil {
            return
        }

        fmt.Println(string(buf[:n]))
        msg := new(message.Message)
        e := msg.Decompose(string(buf[:n]))
        if e != nil {
            atomic.AddUint64(&c.BadDecompose, 1)
            fmt.Printf("Error decomposing message %q - %s\n", string(buf[:n]), e.Error())
            return
        }

        creds, ok := authMap[msg.Public_key]

        if !ok {
            atomic.AddUint64(&c.BadKeyundef, 1)
            fmt.Printf("key not defined for %s\n", msg.Public_key)
            return
        }

        sig := msg.ComputeSignature(creds.SecretKey)

        if sig != msg.Signature {
            atomic.AddUint64(&c.BadSig, 1)
            fmt.Printf("Computed signature %s doesn't match provided signature %s\n", sig, msg.Signature)
            return
        }

        delta := math.Abs(float64(time.Now().Unix() - int64(msg.Timestamp)))
        if delta > skew {
            atomic.AddUint64(&c.BadSkew, 1)
            fmt.Printf("delta = %.0f, max skew set to %.0f\n", delta, skew)
            return
        }

        // validate the metric is on the approved list
        _, ok = creds.Metrics[msg.Name]
        if !ok {
            atomic.AddUint64(&c.BadMetric, 1)
            fmt.Printf("not an approved metric: %s", msg.Name)
            return
        }

        fmt.Println(msg.Public_key)
        fmt.Println(sig)

        _, err = carbon_conn.Write([]byte(msg.MetricStr() + "\n"))
        if err != nil {
            atomic.AddUint64(&c.BadCarbonwrite, 1)
            println("Write to carbon server failed:", err.Error())
            return
        }
        atomic.AddUint64(&c.GoodMetric, 1)

        // write the n bytes read
        _, err2 := conn.Write(buf[0:n])
        if err2 != nil {
            return
        }
    }
}
<|endoftext|>


<commit_before>package boshinit

import (
	"fmt"

	"github.com/enaml-ops/enaml"
	"github.com/enaml-ops/enaml/cloudproperties/aws"
	"github.com/enaml-ops/omg-cli/plugins/products/bosh-init/enaml-gen/aws_cpi"
)

const (
	awsCPIJobName     = "aws_cpi"
	awsCPIReleaseName = "bosh-aws-cpi"
)

type AWSInitConfig struct {
	AWSAvailabilityZone string
	AWSInstanceSize     string
	AWSSubnet           string
	AWSPEMFilePath      string
	AWSAccessKeyID      string
	AWSSecretKey        string
	AWSRegion           string
	AWSSecurityGroups   []string
	AWSKeyName          string
}

type AWSBosh struct {
	cfg      AWSInitConfig
	boshbase *BoshBase
}

func NewAWSIaaSProvider(cfg AWSInitConfig, boshBase *BoshBase) IAASManifestProvider {
	boshBase.CPIJobName = awsCPIJobName
	return &AWSBosh{
		cfg:      cfg,
		boshbase: boshBase,
	}
}

func GetAWSBoshBase() *BoshBase {
	return &BoshBase{
		NetworkCIDR:        "10.0.0.0/24",
		NetworkGateway:     "10.0.0.1",
		NetworkDNS:         []string{"10.0.0.2"},
		BoshReleaseURL:     "https://bosh.io/d/github.com/cloudfoundry/bosh?v=257.3",
		BoshReleaseSHA:     "e4442afcc64123e11f2b33cc2be799a0b59207d0",
		CPIReleaseURL:      "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=57",
		CPIReleaseSHA:      "cbc7ed758f4a41063e9aee881bfc164292664b84",
		GOAgentReleaseURL:  "https://bosh.io/d/stemcells/bosh-aws-xen-hvm-ubuntu-trusty-go_agent?v=3262.7",
		GOAgentSHA:         "bf44a5f81d29346af6a309199d2e012237dd222c",
		PrivateIP:          "10.0.0.6",
		NtpServers:         []string{"0.amazon.pool.ntp.org", "1.amazon.pool.ntp.org", "2.amazon.pool.ntp.org", "3.amazon.pool.ntp.org"},
		CPIJobName:         awsCPIJobName,
		PersistentDiskSize: 51200,
	}
}

func (s *AWSBosh) CreateDeploymentManifest() (*enaml.DeploymentManifest, error) {
	manifest := s.boshbase.CreateDeploymentManifest()
	manifest.AddRelease(s.CreateCPIRelease())
	if rp, err := s.CreateResourcePool(); err != nil {
		return nil, err
	} else {
		manifest.AddResourcePool(*rp)
	}

	manifest.AddDiskPool(s.CreateDiskPool())
	manifest.AddNetwork(s.CreateManualNetwork())
	manifest.AddNetwork(s.CreateVIPNetwork())
	boshJob := manifest.Jobs[0]
	boshJob.AddTemplate(s.CreateCPITemplate())
	n := s.CreateJobNetwork()
	if n != nil {
		boshJob.AddNetwork(*n)
	}
	for name, val := range s.CreateCPIJobProperties() {
		boshJob.AddProperty(name, val)
	}
	manifest.Jobs[0] = boshJob
	manifest.SetCloudProvider(s.CreateCloudProvider())
	return manifest, nil
}

func (s *AWSBosh) resourcePoolCloudProperties() interface{} {
	return awscloudproperties.ResourcePool{
		InstanceType: s.cfg.AWSInstanceSize,
		EphemeralDisk: awscloudproperties.EphemeralDisk{
			Size:     s.boshbase.PersistentDiskSize,
			DiskType: "gp2",
		},
		AvailabilityZone: s.cfg.AWSAvailabilityZone,
	}
}
func (s *AWSBosh) CreateResourcePool() (*enaml.ResourcePool, error) {
	return s.boshbase.CreateResourcePool(s.resourcePoolCloudProperties)
}

func (s *AWSBosh) CreateCPIRelease() enaml.Release {
	return enaml.Release{
		Name: awsCPIReleaseName,
		URL:  s.boshbase.CPIReleaseURL,
		SHA1: s.boshbase.CPIReleaseSHA,
	}
}

func (s *AWSBosh) CreateDiskPool() enaml.DiskPool {
	return enaml.DiskPool{
		Name:     "disks",
		DiskSize: s.boshbase.PersistentDiskSize,
		CloudProperties: awscloudproperties.RootDisk{
			DiskType: "gp2",
		},
	}
}

func (s *AWSBosh) CreateManualNetwork() (net enaml.ManualNetwork) {
	net = enaml.NewManualNetwork("private")
	net.AddSubnet(enaml.Subnet{
		Range:   s.boshbase.NetworkCIDR,
		Gateway: s.boshbase.NetworkGateway,
		DNS:     s.boshbase.NetworkDNS,
		Static: []string{
			s.boshbase.PrivateIP,
		},
		CloudProperties: awscloudproperties.Network{
			Subnet: s.cfg.AWSSubnet,
		},
	})
	return
}

func (s *AWSBosh) CreateJobNetwork() *enaml.Network {
	if s.boshbase.PublicIP != "" {
		return &enaml.Network{
			Name:      "public",
			StaticIPs: []string{s.boshbase.PublicIP},
		}
	}
	return &enaml.Network{
		Name: "public",
	}
}

func (s *AWSBosh) CreateVIPNetwork() (net enaml.VIPNetwork) {
	return enaml.NewVIPNetwork("public")
}

func (s *AWSBosh) CreateCPIJobProperties() map[string]interface{} {
	return map[string]interface{}{
		"aws": &aws_cpi.Aws{
			AccessKeyId:           s.cfg.AWSAccessKeyID,
			SecretAccessKey:       s.cfg.AWSSecretKey,
			DefaultKeyName:        s.cfg.AWSKeyName,
			DefaultSecurityGroups: s.cfg.AWSSecurityGroups,
			Region:                s.cfg.AWSRegion,
		},
		"agent": &aws_cpi.Agent{
			Mbus: fmt.Sprintf("nats://nats:%s@%s:4222", s.boshbase.NatsPassword, s.boshbase.PrivateIP),
		},
	}
}

func (s *AWSBosh) CreateCPITemplate() (template enaml.Template) {
	return enaml.Template{
		Name:    s.boshbase.CPIJobName,
		Release: awsCPIReleaseName,
	}
}

func (s *AWSBosh) CreateCloudProvider() (provider enaml.CloudProvider) {
	return enaml.CloudProvider{
		Template: enaml.Template{
			Name:    s.boshbase.CPIJobName,
			Release: awsCPIReleaseName,
		},
		MBus: fmt.Sprintf("https://mbus:%s@%s:6868", s.boshbase.MBusPassword, s.boshbase.GetRoutableIP()),
		SSHTunnel: enaml.SSHTunnel{
			Host:           s.boshbase.GetRoutableIP(),
			Port:           22,
			User:           "vcap",
			PrivateKeyPath: s.cfg.AWSPEMFilePath,
		},
		Properties: &aws_cpi.AwsCpiJob{
			Aws: &aws_cpi.Aws{
				AccessKeyId:           s.cfg.AWSAccessKeyID,
				SecretAccessKey:       s.cfg.AWSSecretKey,
				DefaultKeyName:        s.cfg.AWSKeyName,
				DefaultSecurityGroups: s.cfg.AWSSecurityGroups,
				Region:                s.cfg.AWSRegion,
			},
			Ntp: s.boshbase.NtpServers,
			Agent: &aws_cpi.Agent{
				Mbus: fmt.Sprintf("https://mbus:%s@0.0.0.0:6868", s.boshbase.MBusPassword),
			},
			Blobstore: &aws_cpi.Blobstore{
				Provider: "local",
				Path:     "/var/vcap/micro_bosh/data/cache",
			},
		},
	}
}
<commit_msg>[#134103585] bosh release, cpi and stemcell versions<commit_after>package boshinit

import (
	"fmt"

	"github.com/enaml-ops/enaml"
	"github.com/enaml-ops/enaml/cloudproperties/aws"
	"github.com/enaml-ops/omg-cli/plugins/products/bosh-init/enaml-gen/aws_cpi"
)

const (
	awsCPIJobName     = "aws_cpi"
	awsCPIReleaseName = "bosh-aws-cpi"
)

type AWSInitConfig struct {
	AWSAvailabilityZone string
	AWSInstanceSize     string
	AWSSubnet           string
	AWSPEMFilePath      string
	AWSAccessKeyID      string
	AWSSecretKey        string
	AWSRegion           string
	AWSSecurityGroups   []string
	AWSKeyName          string
}

type AWSBosh struct {
	cfg      AWSInitConfig
	boshbase *BoshBase
}

func NewAWSIaaSProvider(cfg AWSInitConfig, boshBase *BoshBase) IAASManifestProvider {
	boshBase.CPIJobName = awsCPIJobName
	return &AWSBosh{
		cfg:      cfg,
		boshbase: boshBase,
	}
}

func GetAWSBoshBase() *BoshBase {
	return &BoshBase{
		NetworkCIDR:        "10.0.0.0/24",
		NetworkGateway:     "10.0.0.1",
		NetworkDNS:         []string{"10.0.0.2"},
		BoshReleaseURL:     "https://bosh.io/d/github.com/cloudfoundry/bosh?v=260",
		BoshReleaseSHA:     "f8f086974d9769263078fb6cb7927655744dacbc",
		CPIReleaseURL:      "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=60",
		CPIReleaseSHA:      "8e40a9ff892204007889037f094a1b0d23777058",
		GOAgentReleaseURL:  "https://bosh.io/d/stemcells/bosh-aws-xen-hvm-ubuntu-trusty-go_agent?v=3263.10",
		GOAgentSHA:         "42ea4577caec4aec463bed951cfdffb935961270",
		PrivateIP:          "10.0.0.6",
		NtpServers:         []string{"0.amazon.pool.ntp.org", "1.amazon.pool.ntp.org", "2.amazon.pool.ntp.org", "3.amazon.pool.ntp.org"},
		CPIJobName:         awsCPIJobName,
		PersistentDiskSize: 51200,
	}
}

func (s *AWSBosh) CreateDeploymentManifest() (*enaml.DeploymentManifest, error) {
	manifest := s.boshbase.CreateDeploymentManifest()
	manifest.AddRelease(s.CreateCPIRelease())
	if rp, err := s.CreateResourcePool(); err != nil {
		return nil, err
	} else {
		manifest.AddResourcePool(*rp)
	}

	manifest.AddDiskPool(s.CreateDiskPool())
	manifest.AddNetwork(s.CreateManualNetwork())
	manifest.AddNetwork(s.CreateVIPNetwork())
	boshJob := manifest.Jobs[0]
	boshJob.AddTemplate(s.CreateCPITemplate())
	n := s.CreateJobNetwork()
	if n != nil {
		boshJob.AddNetwork(*n)
	}
	for name, val := range s.CreateCPIJobProperties() {
		boshJob.AddProperty(name, val)
	}
	manifest.Jobs[0] = boshJob
	manifest.SetCloudProvider(s.CreateCloudProvider())
	return manifest, nil
}

func (s *AWSBosh) resourcePoolCloudProperties() interface{} {
	return awscloudproperties.ResourcePool{
		InstanceType: s.cfg.AWSInstanceSize,
		EphemeralDisk: awscloudproperties.EphemeralDisk{
			Size:     s.boshbase.PersistentDiskSize,
			DiskType: "gp2",
		},
		AvailabilityZone: s.cfg.AWSAvailabilityZone,
	}
}
func (s *AWSBosh) CreateResourcePool() (*enaml.ResourcePool, error) {
	return s.boshbase.CreateResourcePool(s.resourcePoolCloudProperties)
}

func (s *AWSBosh) CreateCPIRelease() enaml.Release {
	return enaml.Release{
		Name: awsCPIReleaseName,
		URL:  s.boshbase.CPIReleaseURL,
		SHA1: s.boshbase.CPIReleaseSHA,
	}
}

func (s *AWSBosh) CreateDiskPool() enaml.DiskPool {
	return enaml.DiskPool{
		Name:     "disks",
		DiskSize: s.boshbase.PersistentDiskSize,
		CloudProperties: awscloudproperties.RootDisk{
			DiskType: "gp2",
		},
	}
}

func (s *AWSBosh) CreateManualNetwork() (net enaml.ManualNetwork) {
	net = enaml.NewManualNetwork("private")
	net.AddSubnet(enaml.Subnet{
		Range:   s.boshbase.NetworkCIDR,
		Gateway: s.boshbase.NetworkGateway,
		DNS:     s.boshbase.NetworkDNS,
		Static: []string{
			s.boshbase.PrivateIP,
		},
		CloudProperties: awscloudproperties.Network{
			Subnet: s.cfg.AWSSubnet,
		},
	})
	return
}

func (s *AWSBosh) CreateJobNetwork() *enaml.Network {
	if s.boshbase.PublicIP != "" {
		return &enaml.Network{
			Name:      "public",
			StaticIPs: []string{s.boshbase.PublicIP},
		}
	}
	return &enaml.Network{
		Name: "public",
	}
}

func (s *AWSBosh) CreateVIPNetwork() (net enaml.VIPNetwork) {
	return enaml.NewVIPNetwork("public")
}

func (s *AWSBosh) CreateCPIJobProperties() map[string]interface{} {
	return map[string]interface{}{
		"aws": &aws_cpi.Aws{
			AccessKeyId:           s.cfg.AWSAccessKeyID,
			SecretAccessKey:       s.cfg.AWSSecretKey,
			DefaultKeyName:        s.cfg.AWSKeyName,
			DefaultSecurityGroups: s.cfg.AWSSecurityGroups,
			Region:                s.cfg.AWSRegion,
		},
		"agent": &aws_cpi.Agent{
			Mbus: fmt.Sprintf("nats://nats:%s@%s:4222", s.boshbase.NatsPassword, s.boshbase.PrivateIP),
		},
	}
}

func (s *AWSBosh) CreateCPITemplate() (template enaml.Template) {
	return enaml.Template{
		Name:    s.boshbase.CPIJobName,
		Release: awsCPIReleaseName,
	}
}

func (s *AWSBosh) CreateCloudProvider() (provider enaml.CloudProvider) {
	return enaml.CloudProvider{
		Template: enaml.Template{
			Name:    s.boshbase.CPIJobName,
			Release: awsCPIReleaseName,
		},
		MBus: fmt.Sprintf("https://mbus:%s@%s:6868", s.boshbase.MBusPassword, s.boshbase.GetRoutableIP()),
		SSHTunnel: enaml.SSHTunnel{
			Host:           s.boshbase.GetRoutableIP(),
			Port:           22,
			User:           "vcap",
			PrivateKeyPath: s.cfg.AWSPEMFilePath,
		},
		Properties: &aws_cpi.AwsCpiJob{
			Aws: &aws_cpi.Aws{
				AccessKeyId:           s.cfg.AWSAccessKeyID,
				SecretAccessKey:       s.cfg.AWSSecretKey,
				DefaultKeyName:        s.cfg.AWSKeyName,
				DefaultSecurityGroups: s.cfg.AWSSecurityGroups,
				Region:                s.cfg.AWSRegion,
			},
			Ntp: s.boshbase.NtpServers,
			Agent: &aws_cpi.Agent{
				Mbus: fmt.Sprintf("https://mbus:%s@0.0.0.0:6868", s.boshbase.MBusPassword),
			},
			Blobstore: &aws_cpi.Blobstore{
				Provider: "local",
				Path:     "/var/vcap/micro_bosh/data/cache",
			},
		},
	}
}
<|endoftext|>