<commit_before>// -*- tab-width: 4 -*-
package couch

import (
    "bytes"
    "fmt"
    "os"
    "json"
    "http"
    "net"
    "io/ioutil"
)

const (
    Id  = "_id"
    Rev = "_rev"
)

var (
    defaultHeaders = map[string]string{}
)

//
// Helper and utility functions (private)
//

// Converts given URL to string containing the body of the response.
func url_to_string(url string) string {
    if r, _, err := http.Get(url); err == nil {
        b, err := ioutil.ReadAll(r.Body)
        r.Body.Close()
        if err == nil {
            return string(b)
        }
    }
    return ""
}

// Marshal given interface to JSON string
func to_JSON(p interface{}) (result string, err os.Error) {
    err = nil
    result = ""
    if buf, err := json.Marshal(p); err == nil {
        result = string(buf)
    }
    return
}

// Unmarshal JSON string to given interface
func from_JSON(s string, p interface{}) (err os.Error) {
    err = json.Unmarshal([]byte(s), p)
    return
}

type IdAndRev struct {
    Id  string "_id"
    Rev string "_rev"
}

// Simply extract id and rev from a given JSON string (typically a document)
func extract_id_and_rev(json_str string) (string, string, os.Error) {
    id_rev := new(IdAndRev)
    if err := from_JSON(json_str, id_rev); err != nil {
        return "", "", err
    }
    return id_rev.Id, id_rev.Rev, nil
}

/*
 Sends a query to CouchDB and parses the response back.

 method: the name of the HTTP method (POST, PUT,...)
 url: the URL to interact with
 headers: additional headers to pass to the request
 in: body of the request
 out: a structure to fill in with the returned JSON document
*/
func (p Database) interact(method string, url string, headers map[string]string, in *[]byte, out interface{}) (int, os.Error) {

    fullHeaders := map[string]string{}
    for k, v := range headers {
        fullHeaders[k] = v
    }

    var bodyLength int
    if in == nil {
        bodyLength = 0
    } else {
        bodyLength = len(*in)
        fullHeaders["Content-Type"] = "application/json"
    }
    req := http.Request{
        Method:        method,
        ProtoMajor:    1,
        ProtoMinor:    1,
        Close:         true,
        ContentLength: int64(bodyLength),
        Header:        fullHeaders,
    }

    req.TransferEncoding = []string{"chunked"}
    req.URL, _ = http.ParseURL(url)
    if in != nil {
        req.Body = &buffer{bytes.NewBuffer(*in)}
    }

    // Make connection
    conn, err := net.Dial("tcp", "", fmt.Sprintf("%s:%s", p.Host, p.Port))
    if err != nil {
        return 0, err
    }
    http_conn := http.NewClientConn(conn, nil)
    defer http_conn.Close()
    if err := http_conn.Write(&req); err != nil {
        return 0, err
    }

    // Read response
    r, err := http_conn.Read()
    if err != nil {
        return 0, err
    }
    if r.StatusCode < 200 || r.StatusCode >= 300 {
        b := []byte{}
        r.Body.Read(b)
        fmt.Printf("%v\n", bytes.NewBuffer(b).String())
        return r.StatusCode, os.NewError("server said: " + r.Status)
    }

    decoder := json.NewDecoder(r.Body)
    if err = decoder.Decode(out); err != nil {
        return 0, err
    }
    r.Body.Close()

    return r.StatusCode, nil
}

func (p Database) create_database() os.Error {
    ir := response{}
    if _, err := p.interact("PUT", p.DBURL(), defaultHeaders, nil, &ir); err != nil {
        return err
    }
    if !ir.Ok {
        return os.NewError("CouchDB returned not-OK")
    }
    return nil
}

type buffer struct {
    b *bytes.Buffer
}

func (b *buffer) Read(out []byte) (int, os.Error) {
    return b.b.Read(out)
}

func (b *buffer) Close() os.Error { return nil }

//
// Database object + public methods
//

type Database struct {
    Host string
    Port string
    Name string
}

func (p Database) BaseURL() string {
    return fmt.Sprintf("http://%s:%s", p.Host, p.Port)
}

func (p Database) DBURL() string {
    return fmt.Sprintf("%s/%s", p.BaseURL(), p.Name)
}

// Test whether CouchDB is running (ignores Database.Name)
func (p Database) Running() bool {
    url := fmt.Sprintf("%s/%s", p.BaseURL(), "_all_dbs")
    s := url_to_string(url)
    if len(s) > 0 {
        return true
    }
    return false
}

type DatabaseInfo struct {
    Db_name string
    // other stuff too, ignore for now
}

// Test whether specified database exists in specified CouchDB instance
func (p Database) Exists() bool {
    di := new(DatabaseInfo)
    if err := from_JSON(url_to_string(p.DBURL()), di); err != nil {
        return false
    }
    if di.Db_name != p.Name {
        return false
    }
    return true
}

func NewDatabase(host, port, name string) (Database, os.Error) {
    db := Database{host, port, name}
    if !db.Running() {
        return db, os.NewError("CouchDB not running")
    }
    if !db.Exists() {
        if err := db.create_database(); err != nil {
            return db, err
        }
    }
    return db, nil
}

func cleanJson(d interface{}, id, rev *string) (json_buf []byte, err os.Error) {
    json_buf, err = json.Marshal(d)
    if err != nil {
        return
    }
    tmp := map[string]interface{}{}
    err = json.Unmarshal(json_buf, &tmp)
    if err != nil {
        return
    }
    if id == nil {
        tmp[Id] = nil, false
    } else {
        tmp[Id] = id
    }
    if rev == nil {
        tmp[Rev] = nil, false
    } else {
        tmp[Rev] = rev
    }
    json_buf, err = json.Marshal(tmp)
    return
}

type response struct {
    Ok     bool
    Id     string
    Rev    string
    Error  string
    Reason string
}

// Inserts document to CouchDB, returning id and rev on success.
func (p Database) Insert(d interface{}, id *string) (string, string, os.Error) {
    json_buf, err := cleanJson(d, id, nil)
    if err != nil {
        return "", "", err
    }
    ir := response{}
    if _, err = p.interact("POST", p.DBURL(), defaultHeaders, &json_buf, &ir); err != nil {
        return "", "", err
    }
    if !ir.Ok {
        return "", "", os.NewError(ir.Error + ": " + ir.Reason)
    }
    return ir.Id, ir.Rev, nil
}

// Edits the given document, which must specify both Id and Rev fields, and
// returns the new revision.
func (p Database) Edit(d interface{}, id, rev string) (string, os.Error) {
    if len(id) == 0 {
        return "", os.NewError("invalid id")
    }
    json_buf, err := cleanJson(d, &id, &rev)
    if err != nil {
        return "", err
    }
    url := p.DBURL() + "/" + http.URLEscape(id)
    ir := response{}
    if _, err = p.interact("PUT", url, defaultHeaders, &json_buf, &ir); err != nil {
        return "", err
    }
    return ir.Rev, nil
}

type RetrieveError struct {
    Error  string
    Reason string
}

// Unmarshals the document matching id to the given interface, returning rev.
func (p Database) Retrieve(id string, d interface{}) (string, os.Error) {
    if len(id) <= 0 {
        return "", os.NewError("no id specified")
    }
    json_str := url_to_string(fmt.Sprintf("%s/%s", p.DBURL(), id))
    retrieved_id, rev, err := extract_id_and_rev(json_str)
    if err != nil {
        return "", err
    }
    if retrieved_id != id {
        return "", os.NewError("invalid id specified")
    }
    return rev, from_JSON(json_str, d)
}

// Deletes document given by id and rev.
func (p Database) Delete(id, rev string) os.Error {
    headers := map[string]string{
        "If-Match": rev,
    }
    url := fmt.Sprintf("%s/%s", p.DBURL(), id)

    ir := response{}
    if _, err := p.interact("DELETE", url, headers, nil, &ir); err != nil {
        return err
    }
    if !ir.Ok {
        return os.NewError("CouchDB returned not-OK")
    }
    return nil
}

type Row struct {
    Id  string
    Key string
}

type KeyedViewResponse struct {
    Total_rows uint64
    Offset     uint64
    Rows       []Row
}

// Return array of document ids as returned by the given view/options combo.
// view should be eg. "_design/my_foo/_view/my_bar"
// options should be eg. { "limit": 10, "key": "baz" }
func (p Database) Query(view string, options map[string]interface{}) ([]string, os.Error) {
    if len(view) <= 0 {
        return make([]string, 0), os.NewError("empty view")
    }

    parameters := ""
    for k, v := range options {
        switch t := v.(type) {
        case string:
            parameters += fmt.Sprintf(`%s="%s"&`, k, http.URLEscape(t))
        case int:
            parameters += fmt.Sprintf(`%s=%d&`, k, t)
        case bool:
            parameters += fmt.Sprintf(`%s=%v&`, k, t)
        default:
            // TODO more types are supported
            panic(fmt.Sprintf("unsupported value-type %T in Query", t))
        }
    }
    full_url := fmt.Sprintf("%s/%s?%s", p.DBURL(), view, parameters)
    json_str := url_to_string(full_url)
    kvr := new(KeyedViewResponse)
    if err := from_JSON(json_str, kvr); err != nil {
        return make([]string, 0), err
    }

    ids := make([]string, len(kvr.Rows))
    for i, row := range kvr.Rows {
        ids[i] = row.Id
    }
    return ids, nil
}
<commit_msg>Minor formatting change to clean_JSON<commit_after>// -*- tab-width: 4 -*-
package couch

import (
    "bytes"
    "fmt"
    "os"
    "json"
    "http"
    "net"
    "io/ioutil"
)

const (
    Id  = "_id"
    Rev = "_rev"
)

var (
    defaultHeaders = map[string]string{}
)

//
// Helper and utility functions (private)
//

// Converts given URL to string containing the body of the response.
func url_to_string(url string) string {
    if r, _, err := http.Get(url); err == nil {
        b, err := ioutil.ReadAll(r.Body)
        r.Body.Close()
        if err == nil {
            return string(b)
        }
    }
    return ""
}

// Marshal given interface to JSON string
func to_JSON(p interface{}) (result string, err os.Error) {
    err = nil
    result = ""
    if buf, err := json.Marshal(p); err == nil {
        result = string(buf)
    }
    return
}

// Unmarshal JSON string to given interface
func from_JSON(s string, p interface{}) (err os.Error) {
    err = json.Unmarshal([]byte(s), p)
    return
}

type IdAndRev struct {
    Id  string "_id"
    Rev string "_rev"
}

// Simply extract id and rev from a given JSON string (typically a document)
func extract_id_and_rev(json_str string) (string, string, os.Error) {
    id_rev := new(IdAndRev)
    if err := from_JSON(json_str, id_rev); err != nil {
        return "", "", err
    }
    return id_rev.Id, id_rev.Rev, nil
}

/*
 Sends a query to CouchDB and parses the response back.

 method: the name of the HTTP method (POST, PUT,...)
 url: the URL to interact with
 headers: additional headers to pass to the request
 in: body of the request
 out: a structure to fill in with the returned JSON document
*/
func (p Database) interact(method string, url string, headers map[string]string, in *[]byte, out interface{}) (int, os.Error) {

    fullHeaders := map[string]string{}
    for k, v := range headers {
        fullHeaders[k] = v
    }

    var bodyLength int
    if in == nil {
        bodyLength = 0
    } else {
        bodyLength = len(*in)
        fullHeaders["Content-Type"] = "application/json"
    }
    req := http.Request{
        Method:        method,
        ProtoMajor:    1,
        ProtoMinor:    1,
        Close:         true,
        ContentLength: int64(bodyLength),
        Header:        fullHeaders,
    }

    req.TransferEncoding = []string{"chunked"}
    req.URL, _ = http.ParseURL(url)
    if in != nil {
        req.Body = &buffer{bytes.NewBuffer(*in)}
    }

    // Make connection
    conn, err := net.Dial("tcp", "", fmt.Sprintf("%s:%s", p.Host, p.Port))
    if err != nil {
        return 0, err
    }
    http_conn := http.NewClientConn(conn, nil)
    defer http_conn.Close()
    if err := http_conn.Write(&req); err != nil {
        return 0, err
    }

    // Read response
    r, err := http_conn.Read()
    if err != nil {
        return 0, err
    }
    if r.StatusCode < 200 || r.StatusCode >= 300 {
        b := []byte{}
        r.Body.Read(b)
        fmt.Printf("%v\n", bytes.NewBuffer(b).String())
        return r.StatusCode, os.NewError("server said: " + r.Status)
    }

    decoder := json.NewDecoder(r.Body)
    if err = decoder.Decode(out); err != nil {
        return 0, err
    }
    r.Body.Close()

    return r.StatusCode, nil
}

func (p Database) create_database() os.Error {
    ir := response{}
    if _, err := p.interact("PUT", p.DBURL(), defaultHeaders, nil, &ir); err != nil {
        return err
    }
    if !ir.Ok {
        return os.NewError("CouchDB returned not-OK")
    }
    return nil
}

type buffer struct {
    b *bytes.Buffer
}

func (b *buffer) Read(out []byte) (int, os.Error) {
    return b.b.Read(out)
}

func (b *buffer) Close() os.Error { return nil }

//
// Database object + public methods
//

type Database struct {
    Host string
    Port string
    Name string
}

func (p Database) BaseURL() string {
    return fmt.Sprintf("http://%s:%s", p.Host, p.Port)
}

func (p Database) DBURL() string {
    return fmt.Sprintf("%s/%s", p.BaseURL(), p.Name)
}

// Test whether CouchDB is running (ignores Database.Name)
func (p Database) Running() bool {
    url := fmt.Sprintf("%s/%s", p.BaseURL(), "_all_dbs")
    s := url_to_string(url)
    if len(s) > 0 {
        return true
    }
    return false
}

type DatabaseInfo struct {
    Db_name string
    // other stuff too, ignore for now
}

// Test whether specified database exists in specified CouchDB instance
func (p Database) Exists() bool {
    di := new(DatabaseInfo)
    if err := from_JSON(url_to_string(p.DBURL()), di); err != nil {
        return false
    }
    if di.Db_name != p.Name {
        return false
    }
    return true
}

func NewDatabase(host, port, name string) (Database, os.Error) {
    db := Database{host, port, name}
    if !db.Running() {
        return db, os.NewError("CouchDB not running")
    }
    if !db.Exists() {
        if err := db.create_database(); err != nil {
            return db, err
        }
    }
    return db, nil
}

func clean_JSON(d interface{}, id, rev *string) (json_buf []byte, err os.Error) {
    json_buf, err = json.Marshal(d)
    if err != nil {
        return
    }
    tmp := map[string]interface{}{}
    err = json.Unmarshal(json_buf, &tmp)
    if err != nil {
        return
    }
    if id == nil {
        tmp[Id] = nil, false
    } else {
        tmp[Id] = id
    }
    if rev == nil {
        tmp[Rev] = nil, false
    } else {
        tmp[Rev] = rev
    }
    json_buf, err = json.Marshal(tmp)
    return
}

type response struct {
    Ok     bool
    Id     string
    Rev    string
    Error  string
    Reason string
}

// Inserts document to CouchDB, returning id and rev on success.
func (p Database) Insert(d interface{}, id *string) (string, string, os.Error) {
    json_buf, err := clean_JSON(d, id, nil)
    if err != nil {
        return "", "", err
    }
    ir := response{}
    if _, err = p.interact("POST", p.DBURL(), defaultHeaders, &json_buf, &ir); err != nil {
        return "", "", err
    }
    if !ir.Ok {
        return "", "", os.NewError(ir.Error + ": " + ir.Reason)
    }
    return ir.Id, ir.Rev, nil
}

// Edits the given document, which must specify both Id and Rev fields, and
// returns the new revision.
func (p Database) Edit(d interface{}, id, rev string) (string, os.Error) {
    if len(id) == 0 {
        return "", os.NewError("invalid id")
    }
    json_buf, err := clean_JSON(d, &id, &rev)
    if err != nil {
        return "", err
    }
    url := p.DBURL() + "/" + http.URLEscape(id)
    ir := response{}
    if _, err = p.interact("PUT", url, defaultHeaders, &json_buf, &ir); err != nil {
        return "", err
    }
    return ir.Rev, nil
}

type RetrieveError struct {
    Error  string
    Reason string
}

// Unmarshals the document matching id to the given interface, returning rev.
func (p Database) Retrieve(id string, d interface{}) (string, os.Error) {
    if len(id) <= 0 {
        return "", os.NewError("no id specified")
    }
    json_str := url_to_string(fmt.Sprintf("%s/%s", p.DBURL(), id))
    retrieved_id, rev, err := extract_id_and_rev(json_str)
    if err != nil {
        return "", err
    }
    if retrieved_id != id {
        return "", os.NewError("invalid id specified")
    }
    return rev, from_JSON(json_str, d)
}

// Deletes document given by id and rev.
func (p Database) Delete(id, rev string) os.Error {
    headers := map[string]string{
        "If-Match": rev,
    }
    url := fmt.Sprintf("%s/%s", p.DBURL(), id)

    ir := response{}
    if _, err := p.interact("DELETE", url, headers, nil, &ir); err != nil {
        return err
    }
    if !ir.Ok {
        return os.NewError("CouchDB returned not-OK")
    }
    return nil
}

type Row struct {
    Id  string
    Key string
}

type KeyedViewResponse struct {
    Total_rows uint64
    Offset     uint64
    Rows       []Row
}

// Return array of document ids as returned by the given view/options combo.
// view should be eg. "_design/my_foo/_view/my_bar"
// options should be eg. { "limit": 10, "key": "baz" }
func (p Database) Query(view string, options map[string]interface{}) ([]string, os.Error) {
    if len(view) <= 0 {
        return make([]string, 0), os.NewError("empty view")
    }

    parameters := ""
    for k, v := range options {
        switch t := v.(type) {
        case string:
            parameters += fmt.Sprintf(`%s="%s"&`, k, http.URLEscape(t))
        case int:
            parameters += fmt.Sprintf(`%s=%d&`, k, t)
        case bool:
            parameters += fmt.Sprintf(`%s=%v&`, k, t)
        default:
            // TODO more types are supported
            panic(fmt.Sprintf("unsupported value-type %T in Query", t))
        }
    }
    full_url := fmt.Sprintf("%s/%s?%s", p.DBURL(), view, parameters)
    json_str := url_to_string(full_url)
    kvr := new(KeyedViewResponse)
    if err := from_JSON(json_str, kvr); err != nil {
        return make([]string, 0), err
    }

    ids := make([]string, len(kvr.Rows))
    for i, row := range kvr.Rows {
        ids[i] = row.Id
    }
    return ids, nil
}
<|endoftext|>


<commit_before>// (C) Copyright 2013, Jonas mg. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.

// Package crypt provides interface for password crypt functions and collects
// common constants.
package crypt

import (
	"errors"
	"strings"

	"github.com/GehirnInc/crypt/common"
)

var ErrKeyMismatch = errors.New("hashed value is not the hash of the given password")

// Crypter is the common interface implemented by all crypt functions.
type Crypter interface {
	// Generate performs the hashing algorithm, returning a full hash suitable
	// for storage and later password verification.
	//
	// If the salt is empty, a randomly-generated salt will be generated with a
	// length of SaltLenMax and number RoundsDefault of rounds.
	//
	// Any error only can be got when the salt argument is not empty.
	Generate(key, salt []byte) (string, error)

	// Verify compares a hashed key with its possible key equivalent.
	// Returns nil on success, or an error on failure; if the hashed key is
	// diffrent, the error is "ErrKeyMismatch".
	Verify(hashedKey string, key []byte) error

	// Cost returns the hashing cost (in rounds) used to create the given hashed
	// key.
	//
	// When, in the future, the hashing cost of a key needs to be increased in
	// order to adjust for greater computational power, this function allows one
	// to establish which keys need to be updated.
	//
	// The algorithms based in MD5-crypt use a fixed value of rounds.
	Cost(hashedKey string) (int, error)

	// SetSalt sets a different salt. It is used to easily create derivated
	// algorithms, i.e. "apr1_crypt" from "md5_crypt".
	SetSalt(salt common.Salt)
}

// Crypt identifies a crypt function that is implemented in another package.
type Crypt uint

const (
	APR1   Crypt = 1 + iota // import github.com/GehirnInc/crypt/apr1_crypt
	MD5                     // import github.com/GehirnInc/crypt/md5_crypt
	SHA256                  // import github.com/GehirnInc/crypt/sha256_crypt
	SHA512                  // import github.com/GehirnInc/crypt/sha512_crypt
	maxCrypt
)

var crypts = make([]func() Crypter, maxCrypt)

// New returns new Crypter making the Crypt c.
// New panics if the Crypt c is unavailable.
func (c Crypt) New() Crypter {
	if c > 0 && c < maxCrypt {
		f := crypts[c]
		if f != nil {
			return f()
		}
	}
	panic("crypt: requested crypt function is unavailable")
}

// Available reports whether the Crypt c is available.
func (c Crypt) Available() bool {
	return c > 0 && c < maxCrypt && crypts[c] != nil
}

var cryptPrefixes = make([]string, maxCrypt)

// RegisterCrypt registers a function that returns a new instance of the given
// crypt function. This is intended to be called from the init function in
// packages that implement crypt functions.
func RegisterCrypt(c Crypt, f func() Crypter, prefix string) {
	if c >= maxCrypt {
		panic("crypt: RegisterHash of unknown crypt function")
	}
	crypts[c] = f
	cryptPrefixes[c] = prefix
}

// New returns a new crypter.
func New(c Crypt) Crypter {
	return c.New()
}

// NewFromHash returns a new Crypter using the prefix in the given hashed key.
func NewFromHash(hashedKey string) Crypter {
	for i := range cryptPrefixes {
		if i == 0 {
			continue
		}
		prefix := cryptPrefixes[i]
		if strings.HasPrefix(hashedKey, prefix) {
			crypt := Crypt(uint(i))
			return crypt.New()
		}
	}

	panic("crypt: unknown crypt function")
}
<commit_msg>fix unintended panic in NewFromHash when crypt implementations are partially loaded<commit_after>// (C) Copyright 2013, Jonas mg. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.

// Package crypt provides interface for password crypt functions and collects
// common constants.
package crypt

import (
	"errors"
	"strings"

	"github.com/GehirnInc/crypt/common"
)

var ErrKeyMismatch = errors.New("hashed value is not the hash of the given password")

// Crypter is the common interface implemented by all crypt functions.
type Crypter interface {
	// Generate performs the hashing algorithm, returning a full hash suitable
	// for storage and later password verification.
	//
	// If the salt is empty, a randomly-generated salt will be generated with a
	// length of SaltLenMax and number RoundsDefault of rounds.
	//
	// Any error only can be got when the salt argument is not empty.
	Generate(key, salt []byte) (string, error)

	// Verify compares a hashed key with its possible key equivalent.
	// Returns nil on success, or an error on failure; if the hashed key is
	// diffrent, the error is "ErrKeyMismatch".
	Verify(hashedKey string, key []byte) error

	// Cost returns the hashing cost (in rounds) used to create the given hashed
	// key.
	//
	// When, in the future, the hashing cost of a key needs to be increased in
	// order to adjust for greater computational power, this function allows one
	// to establish which keys need to be updated.
	//
	// The algorithms based in MD5-crypt use a fixed value of rounds.
	Cost(hashedKey string) (int, error)

	// SetSalt sets a different salt. It is used to easily create derivated
	// algorithms, i.e. "apr1_crypt" from "md5_crypt".
	SetSalt(salt common.Salt)
}

// Crypt identifies a crypt function that is implemented in another package.
type Crypt uint

const (
	APR1   Crypt = 1 + iota // import github.com/GehirnInc/crypt/apr1_crypt
	MD5                     // import github.com/GehirnInc/crypt/md5_crypt
	SHA256                  // import github.com/GehirnInc/crypt/sha256_crypt
	SHA512                  // import github.com/GehirnInc/crypt/sha512_crypt
	maxCrypt
)

var crypts = make([]func() Crypter, maxCrypt)

// New returns new Crypter making the Crypt c.
// New panics if the Crypt c is unavailable.
func (c Crypt) New() Crypter {
	if c > 0 && c < maxCrypt {
		f := crypts[c]
		if f != nil {
			return f()
		}
	}
	panic("crypt: requested crypt function is unavailable")
}

// Available reports whether the Crypt c is available.
func (c Crypt) Available() bool {
	return c > 0 && c < maxCrypt && crypts[c] != nil
}

var cryptPrefixes = make([]string, maxCrypt)

// RegisterCrypt registers a function that returns a new instance of the given
// crypt function. This is intended to be called from the init function in
// packages that implement crypt functions.
func RegisterCrypt(c Crypt, f func() Crypter, prefix string) {
	if c >= maxCrypt {
		panic("crypt: RegisterHash of unknown crypt function")
	}
	crypts[c] = f
	cryptPrefixes[c] = prefix
}

// New returns a new crypter.
func New(c Crypt) Crypter {
	return c.New()
}

// NewFromHash returns a new Crypter using the prefix in the given hashed key.
func NewFromHash(hashedKey string) Crypter {
	for i := range cryptPrefixes {
		prefix := cryptPrefixes[i]
		if crypts[i] != nil && strings.HasPrefix(hashedKey, prefix) {
			crypt := Crypt(uint(i))
			return crypt.New()
		}
	}

	panic("crypt: unknown crypt function")
}
<|endoftext|>


<commit_before>package main

// TODO list
// refactor rabbitmq config options
// runqueue goroutine
// handle mysql disconnect
// handle rabbitmq disconnect

import (
	"code.google.com/p/gcfg"
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/streadway/amqp"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	debugging = flag.Bool("d", false, "turn on debug messages")
	cfg       struct {
		DB struct {
			Driver   string
			User     string
			Password string
			Host     string
			Port     string
			Db       string
			Table    string
		}
		RabbitMQ struct {
			Uri      string
			Exchange string
		}
		HTTP struct {
			Host string
			Port string
		}
	}

	db      *sql.DB
	broker  *amqp.Connection
	channel *amqp.Channel
	wakeUp  = make(chan int, 1)
)

type Job struct {
	routingKey string
	body       string
	interval   time.Duration
	nextRun    time.Time
}

func debug(args ...interface{}) {
	if *debugging {
		log.Println(args...)
	}
}

// hadleSchedule is the web server endpoint for path: /schedule
func handleSchedule(w http.ResponseWriter, r *http.Request) {
	routingKey, body, interval_s :=
		r.FormValue("routing_key"), r.FormValue("body"), r.FormValue("interval")
	debug("/schedule", routingKey, body)

	interval, err := strconv.ParseInt(interval_s, 10, 64)
	if err != nil {
		panic(err)
	}

	next_run := time.Now().UTC().Add(time.Duration(interval) * time.Second)
	_, err = db.Exec("INSERT INTO "+cfg.DB.Table+" "+
		"(routing_key, body, `interval`, next_run) "+
		"VALUES(?, ?, ?, ?) "+
		"ON DUPLICATE KEY UPDATE `interval`=?",
		routingKey, body, interval, next_run, interval)
	if err != nil {
		panic(err)
	}

	// Wake up the publisher.
	//
	// publisher() may be sleeping for the next job on the queue
	// at the time we schedule a new Job. Let it wake up so it can
	// re-fetch the new Job from the front of the queue.
	//
	// The code below is an idiom for non-blocking send to a channel.
	select {
	case wakeUp <- 1:
		debug("Sent wakeup signal")
	default:
		debug("Skipped wakeup signal")
	}
}

// handleCancel is the web server endpoint for path: /cancel
func handleCancel(w http.ResponseWriter, r *http.Request) {
	routingKey, body := r.FormValue("routing_key"), r.FormValue("body")
	debug("/cancel", routingKey, body)

	_, err := db.Exec("DELETE FROM "+cfg.DB.Table+" "+
		"WHERE routing_key=? AND body=?", routingKey, body)
	if err != nil {
		panic(err)
	}
}

// front returns the first job to be run in the queue.
func front() (*Job, error) {
	var interval uint
	j := Job{}
	row := db.QueryRow("SELECT routing_key, body, `interval`, next_run " +
		"FROM " + cfg.DB.Table + " " +
		"ORDER BY next_run ASC")
	err := row.Scan(&j.routingKey, &j.body, &interval, &j.nextRun)
	if err != nil {
		return nil, err
	}
	j.interval = time.Duration(interval) * time.Second
	return &j, nil
}

// Publish sends a message to exchange defined in the config and
// updates the Job's next run time on the database.
func (j *Job) Publish() error {
	debug("publish", *j)

	// Send a message to the broker
	err := channel.Publish(cfg.RabbitMQ.Exchange, j.routingKey, false, false, amqp.Publishing{
		Headers: amqp.Table{
			"interval":     j.interval,
			"published_at": time.Now().UTC(),
		},
		ContentType:     "application/octet-stream",
		ContentEncoding: "",
		Body:            []byte(j.body),
		DeliveryMode:    amqp.Persistent,
		Priority:        0,
	})
	if err != nil {
		return err
	}

	// Update next run time
	_, err = db.Exec("UPDATE "+cfg.DB.Table+" "+
		"SET next_run=? "+
		"WHERE routing_key=? AND body=?",
		j.CalculateNextRun(), j.routingKey, j.body)
	if err != nil {
		return err
	}
	return nil
}

// Remaining returns the duration until the job's next scheduled time.
func (j *Job) Remaining() time.Duration {
	return -time.Since(j.nextRun)
}

func (j *Job) CalculateNextRun() time.Time {
	return j.nextRun.Add(j.interval)
}

// publisher runs a loop that reads the next Job from the queue and publishes it.
func publisher() {
	publish := func(j *Job) {
		err := j.Publish()
		if err != nil {
			fmt.Println(err)
		}
	}

	for {
		job, err := front()
		if err != nil {
			if strings.Contains(err.Error(), "no rows in result set") {
				debug("No waiting jobs the queue")
				debug("Waiting wakeup signal")
				<-wakeUp
				debug("Got wakeup signal")
			} else {
				fmt.Println(err)
			}
			continue
		}
		remaining := job.Remaining()
		debug("Next job:", job, "Remaining:", remaining)

		now := time.Now().UTC()
		if job.nextRun.After(now) {
			// Wait until the next Job time or
			// the webserver's /schedule handler wakes us up
			debug("Sleeping for job:", remaining)
			select {
			case <-time.After(remaining):
				debug("Job sleep time finished")
				publish(job)
			case <-wakeUp:
				debug("Woke up by webserver")
				continue
			}
		} else {
			publish(job)
		}

	}
}

func main() {
	flag.Parse()

	// Read config
	err := gcfg.ReadFileInto(&cfg, "dalga.ini")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Read config: ", cfg)

	// Connect to database
	dsn := cfg.DB.User + ":" + cfg.DB.Password + "@" + "tcp(" + cfg.DB.Host + ":" + cfg.DB.Port + ")/" + cfg.DB.Db + "?parseTime=true"
	db, err = sql.Open(cfg.DB.Driver, dsn)
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to DB")

	// Connect to RabbitMQ
	broker, err = amqp.Dial(cfg.RabbitMQ.Uri)
	if err != nil {
		log.Fatal(err)
	}
	channel, err = broker.Channel()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to RabbitMQ")

	// Run publisher
	go publisher()

	// Start HTTP server
	addr := cfg.HTTP.Host + ":" + cfg.HTTP.Port
	http.HandleFunc("/schedule", handleSchedule)
	http.HandleFunc("/cancel", handleCancel)
	http.ListenAndServe(addr, nil)
}
<commit_msg>expire messages with ttl (interval)<commit_after>package main

// TODO list
// refactor rabbitmq config options
// runqueue goroutine
// handle mysql disconnect
// handle rabbitmq disconnect

import (
	"code.google.com/p/gcfg"
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/streadway/amqp"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	debugging = flag.Bool("d", false, "turn on debug messages")
	cfg       struct {
		DB struct {
			Driver   string
			User     string
			Password string
			Host     string
			Port     string
			Db       string
			Table    string
		}
		RabbitMQ struct {
			Uri      string
			Exchange string
		}
		HTTP struct {
			Host string
			Port string
		}
	}

	db      *sql.DB
	broker  *amqp.Connection
	channel *amqp.Channel
	wakeUp  = make(chan int, 1)
)

type Job struct {
	routingKey string
	body       string
	interval   time.Duration
	nextRun    time.Time
}

func debug(args ...interface{}) {
	if *debugging {
		log.Println(args...)
	}
}

// hadleSchedule is the web server endpoint for path: /schedule
func handleSchedule(w http.ResponseWriter, r *http.Request) {
	routingKey, body, interval_s :=
		r.FormValue("routing_key"), r.FormValue("body"), r.FormValue("interval")
	debug("/schedule", routingKey, body)

	interval, err := strconv.ParseInt(interval_s, 10, 64)
	if err != nil {
		panic(err)
	}

	next_run := time.Now().UTC().Add(time.Duration(interval) * time.Second)
	_, err = db.Exec("INSERT INTO "+cfg.DB.Table+" "+
		"(routing_key, body, `interval`, next_run) "+
		"VALUES(?, ?, ?, ?) "+
		"ON DUPLICATE KEY UPDATE `interval`=?",
		routingKey, body, interval, next_run, interval)
	if err != nil {
		panic(err)
	}

	// Wake up the publisher.
	//
	// publisher() may be sleeping for the next job on the queue
	// at the time we schedule a new Job. Let it wake up so it can
	// re-fetch the new Job from the front of the queue.
	//
	// The code below is an idiom for non-blocking send to a channel.
	select {
	case wakeUp <- 1:
		debug("Sent wakeup signal")
	default:
		debug("Skipped wakeup signal")
	}
}

// handleCancel is the web server endpoint for path: /cancel
func handleCancel(w http.ResponseWriter, r *http.Request) {
	routingKey, body := r.FormValue("routing_key"), r.FormValue("body")
	debug("/cancel", routingKey, body)

	_, err := db.Exec("DELETE FROM "+cfg.DB.Table+" "+
		"WHERE routing_key=? AND body=?", routingKey, body)
	if err != nil {
		panic(err)
	}
}

// front returns the first job to be run in the queue.
func front() (*Job, error) {
	var interval uint
	j := Job{}
	row := db.QueryRow("SELECT routing_key, body, `interval`, next_run " +
		"FROM " + cfg.DB.Table + " " +
		"ORDER BY next_run ASC")
	err := row.Scan(&j.routingKey, &j.body, &interval, &j.nextRun)
	if err != nil {
		return nil, err
	}
	j.interval = time.Duration(interval) * time.Second
	return &j, nil
}

// Publish sends a message to exchange defined in the config and
// updates the Job's next run time on the database.
func (j *Job) Publish() error {
	debug("publish", *j)

	// Send a message to the broker
	err := channel.Publish(cfg.RabbitMQ.Exchange, j.routingKey, false, false, amqp.Publishing{
		Headers: amqp.Table{
			"interval":     j.interval,
			"published_at": time.Now().UTC(),
		},
		ContentType:     "application/octet-stream",
		ContentEncoding: "",
		Body:            []byte(j.body),
		DeliveryMode:    amqp.Persistent,
		Priority:        0,
		Expiration:      strconv.FormatUint(uint64(j.interval.Seconds()), 10) + "000",
	})
	if err != nil {
		return err
	}

	// Update next run time
	_, err = db.Exec("UPDATE "+cfg.DB.Table+" "+
		"SET next_run=? "+
		"WHERE routing_key=? AND body=?",
		j.CalculateNextRun(), j.routingKey, j.body)
	if err != nil {
		return err
	}
	return nil
}

// Remaining returns the duration until the job's next scheduled time.
func (j *Job) Remaining() time.Duration {
	return -time.Since(j.nextRun)
}

func (j *Job) CalculateNextRun() time.Time {
	return j.nextRun.Add(j.interval)
}

// publisher runs a loop that reads the next Job from the queue and publishes it.
func publisher() {
	publish := func(j *Job) {
		err := j.Publish()
		if err != nil {
			fmt.Println(err)
		}
	}

	for {
		job, err := front()
		if err != nil {
			if strings.Contains(err.Error(), "no rows in result set") {
				debug("No waiting jobs the queue")
				debug("Waiting wakeup signal")
				<-wakeUp
				debug("Got wakeup signal")
			} else {
				fmt.Println(err)
			}
			continue
		}
		remaining := job.Remaining()
		debug("Next job:", job, "Remaining:", remaining)

		now := time.Now().UTC()
		if job.nextRun.After(now) {
			// Wait until the next Job time or
			// the webserver's /schedule handler wakes us up
			debug("Sleeping for job:", remaining)
			select {
			case <-time.After(remaining):
				debug("Job sleep time finished")
				publish(job)
			case <-wakeUp:
				debug("Woke up by webserver")
				continue
			}
		} else {
			publish(job)
		}

	}
}

func main() {
	flag.Parse()

	// Read config
	err := gcfg.ReadFileInto(&cfg, "dalga.ini")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Read config: ", cfg)

	// Connect to database
	dsn := cfg.DB.User + ":" + cfg.DB.Password + "@" + "tcp(" + cfg.DB.Host + ":" + cfg.DB.Port + ")/" + cfg.DB.Db + "?parseTime=true"
	db, err = sql.Open(cfg.DB.Driver, dsn)
	if err != nil {
		log.Fatal(err)
	}
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to DB")

	// Connect to RabbitMQ
	broker, err = amqp.Dial(cfg.RabbitMQ.Uri)
	if err != nil {
		log.Fatal(err)
	}
	channel, err = broker.Channel()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to RabbitMQ")

	// Run publisher
	go publisher()

	// Start HTTP server
	addr := cfg.HTTP.Host + ":" + cfg.HTTP.Port
	http.HandleFunc("/schedule", handleSchedule)
	http.HandleFunc("/cancel", handleCancel)
	http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The go-marathon Authors All rights reserved.

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

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

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

package marathon

// HealthCheck is the definition for an application health check
type HealthCheck struct {
	Command                *Command `json:"command,omitempty"`
	PortIndex              *int     `json:"portIndex,omitempty"`
	Port                   *int     `json:"port,omitempty"`
	Path                   *string  `json:"path,omitempty"`
	MaxConsecutiveFailures *int     `json:"maxConsecutiveFailures,omitempty"`
	Protocol               string   `json:"protocol,omitempty"`
	GracePeriodSeconds     int      `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        int      `json:"intervalSeconds,omitempty"`
	TimeoutSeconds         int      `json:"timeoutSeconds,omitempty"`
	IgnoreHTTP1xx          *bool    `json:"ignoreHttp1xx,omitempty"`
}

// HTTPHealthCheck describes an HTTP based health check
type HTTPHealthCheck struct {
	Endpoint string `json:"endpoint,omitempty"`
	Path     string `json:"path,omitempty"`
	Scheme   string `json:"scheme,omitempty"`
}

// TCPHealthCheck describes a TCP based health check
type TCPHealthCheck struct {
	Endpoint string `json:"endpoint,omitempty"`
}

// CommandHealthCheck describes a shell-based health check
type CommandHealthCheck struct {
	Command PodCommand `json:"command,omitempty"`
}

// PodHealthCheck describes how to determine a pod's health
type PodHealthCheck struct {
	HTTP                   *HTTPHealthCheck    `json:"http,omitempty"`
	TCP                    *TCPHealthCheck     `json:"tcp,omitempty"`
	Exec                   *CommandHealthCheck `json:"exec,omitempty"`
	GracePeriodSeconds     *int                `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        *int                `json:"intervalSeconds,omitempty"`
	MaxConsecutiveFailures *int                `json:"maxConsecutiveFailures,omitempty"`
	TimeoutSeconds         *int                `json:"timeoutSeconds,omitempty"`
	DelaySeconds           *int                `json:"delaySeconds,omitempty"`
}

// SetCommand sets the given command on the health check.
func (h *HealthCheck) SetCommand(c Command) *HealthCheck {
	h.Command = &c
	return h
}

// SetPortIndex sets the given port index on the health check.
func (h *HealthCheck) SetPortIndex(i int) *HealthCheck {
	h.PortIndex = &i
	return h
}

// SetPort sets the given port on the health check.
func (h *HealthCheck) SetPort(i int) *HealthCheck {
	h.Port = &i
	return h
}

// SetPath sets the given path on the health check.
func (h *HealthCheck) SetPath(p string) *HealthCheck {
	h.Path = &p
	return h
}

// SetMaxConsecutiveFailures sets the maximum consecutive failures on the health check.
func (h *HealthCheck) SetMaxConsecutiveFailures(i int) *HealthCheck {
	h.MaxConsecutiveFailures = &i
	return h
}

// SetIgnoreHTTP1xx sets ignore http 1xx on the health check.
func (h *HealthCheck) SetIgnoreHTTP1xx(ignore bool) *HealthCheck {
	h.IgnoreHTTP1xx = &ignore
	return h
}

// NewDefaultHealthCheck creates a default application health check
func NewDefaultHealthCheck() *HealthCheck {
	portIndex := 0
	path := ""
	maxConsecutiveFailures := 3

	return &HealthCheck{
		Protocol:               "HTTP",
		Path:                   &path,
		PortIndex:              &portIndex,
		MaxConsecutiveFailures: &maxConsecutiveFailures,
		GracePeriodSeconds:     30,
		IntervalSeconds:        10,
		TimeoutSeconds:         5,
	}
}

// HealthCheckResult is the health check result
type HealthCheckResult struct {
	Alive               bool   `json:"alive"`
	ConsecutiveFailures int    `json:"consecutiveFailures"`
	FirstSuccess        string `json:"firstSuccess"`
	LastFailure         string `json:"lastFailure"`
	LastFailureCause    string `json:"lastFailureCause"`
	LastSuccess         string `json:"lastSuccess"`
	TaskID              string `json:"taskId"`
}

// Command is the command health check type
type Command struct {
	Value string `json:"value"`
}
<commit_msg>Add builder methods for pod health checks (#346)<commit_after>/*
Copyright 2014 The go-marathon Authors All rights reserved.

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

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

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

package marathon

// HealthCheck is the definition for an application health check
type HealthCheck struct {
	Command                *Command `json:"command,omitempty"`
	PortIndex              *int     `json:"portIndex,omitempty"`
	Port                   *int     `json:"port,omitempty"`
	Path                   *string  `json:"path,omitempty"`
	MaxConsecutiveFailures *int     `json:"maxConsecutiveFailures,omitempty"`
	Protocol               string   `json:"protocol,omitempty"`
	GracePeriodSeconds     int      `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        int      `json:"intervalSeconds,omitempty"`
	TimeoutSeconds         int      `json:"timeoutSeconds,omitempty"`
	IgnoreHTTP1xx          *bool    `json:"ignoreHttp1xx,omitempty"`
}

// HTTPHealthCheck describes an HTTP based health check
type HTTPHealthCheck struct {
	Endpoint string `json:"endpoint,omitempty"`
	Path     string `json:"path,omitempty"`
	Scheme   string `json:"scheme,omitempty"`
}

// TCPHealthCheck describes a TCP based health check
type TCPHealthCheck struct {
	Endpoint string `json:"endpoint,omitempty"`
}

// CommandHealthCheck describes a shell-based health check
type CommandHealthCheck struct {
	Command PodCommand `json:"command,omitempty"`
}

// PodHealthCheck describes how to determine a pod's health
type PodHealthCheck struct {
	HTTP                   *HTTPHealthCheck    `json:"http,omitempty"`
	TCP                    *TCPHealthCheck     `json:"tcp,omitempty"`
	Exec                   *CommandHealthCheck `json:"exec,omitempty"`
	GracePeriodSeconds     *int                `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        *int                `json:"intervalSeconds,omitempty"`
	MaxConsecutiveFailures *int                `json:"maxConsecutiveFailures,omitempty"`
	TimeoutSeconds         *int                `json:"timeoutSeconds,omitempty"`
	DelaySeconds           *int                `json:"delaySeconds,omitempty"`
}

// NewPodHealthCheck creates an empty PodHealthCheck
func NewPodHealthCheck() *PodHealthCheck {
	return &PodHealthCheck{}
}

// NewHTTPHealthCheck creates an empty HTTPHealthCheck
func NewHTTPHealthCheck() *HTTPHealthCheck {
	return &HTTPHealthCheck{}
}

// NewTCPHealthCheck creates an empty TCPHealthCheck
func NewTCPHealthCheck() *TCPHealthCheck {
	return &TCPHealthCheck{}
}

// NewCommandHealthCheck creates an empty CommandHealthCheck
func NewCommandHealthCheck() *CommandHealthCheck {
	return &CommandHealthCheck{}
}

// SetCommand sets the given command on the health check.
func (h *HealthCheck) SetCommand(c Command) *HealthCheck {
	h.Command = &c
	return h
}

// SetPortIndex sets the given port index on the health check.
func (h *HealthCheck) SetPortIndex(i int) *HealthCheck {
	h.PortIndex = &i
	return h
}

// SetPort sets the given port on the health check.
func (h *HealthCheck) SetPort(i int) *HealthCheck {
	h.Port = &i
	return h
}

// SetPath sets the given path on the health check.
func (h *HealthCheck) SetPath(p string) *HealthCheck {
	h.Path = &p
	return h
}

// SetMaxConsecutiveFailures sets the maximum consecutive failures on the health check.
func (h *HealthCheck) SetMaxConsecutiveFailures(i int) *HealthCheck {
	h.MaxConsecutiveFailures = &i
	return h
}

// SetIgnoreHTTP1xx sets ignore http 1xx on the health check.
func (h *HealthCheck) SetIgnoreHTTP1xx(ignore bool) *HealthCheck {
	h.IgnoreHTTP1xx = &ignore
	return h
}

// NewDefaultHealthCheck creates a default application health check
func NewDefaultHealthCheck() *HealthCheck {
	portIndex := 0
	path := ""
	maxConsecutiveFailures := 3

	return &HealthCheck{
		Protocol:               "HTTP",
		Path:                   &path,
		PortIndex:              &portIndex,
		MaxConsecutiveFailures: &maxConsecutiveFailures,
		GracePeriodSeconds:     30,
		IntervalSeconds:        10,
		TimeoutSeconds:         5,
	}
}

// HealthCheckResult is the health check result
type HealthCheckResult struct {
	Alive               bool   `json:"alive"`
	ConsecutiveFailures int    `json:"consecutiveFailures"`
	FirstSuccess        string `json:"firstSuccess"`
	LastFailure         string `json:"lastFailure"`
	LastFailureCause    string `json:"lastFailureCause"`
	LastSuccess         string `json:"lastSuccess"`
	TaskID              string `json:"taskId"`
}

// Command is the command health check type
type Command struct {
	Value string `json:"value"`
}

// SetHTTPHealthCheck configures the pod's health check for an HTTP endpoint.
// Note this will erase any configured TCP/Exec health checks.
func (p *PodHealthCheck) SetHTTPHealthCheck(h *HTTPHealthCheck) *PodHealthCheck {
	p.HTTP = h
	p.TCP = nil
	p.Exec = nil
	return p
}

// SetTCPHealthCheck configures the pod's health check for a TCP endpoint.
// Note this will erase any configured HTTP/Exec health checks.
func (p *PodHealthCheck) SetTCPHealthCheck(t *TCPHealthCheck) *PodHealthCheck {
	p.TCP = t
	p.HTTP = nil
	p.Exec = nil
	return p
}

// SetExecHealthCheck configures the pod's health check for a command.
// Note this will erase any configured HTTP/TCP health checks.
func (p *PodHealthCheck) SetExecHealthCheck(e *CommandHealthCheck) *PodHealthCheck {
	p.Exec = e
	p.HTTP = nil
	p.TCP = nil
	return p
}

// SetGracePeriod sets the health check initial grace period, in seconds
func (p *PodHealthCheck) SetGracePeriod(gracePeriodSeconds int) *PodHealthCheck {
	p.GracePeriodSeconds = &gracePeriodSeconds
	return p
}

// SetInterval sets the health check polling interval, in seconds
func (p *PodHealthCheck) SetInterval(intervalSeconds int) *PodHealthCheck {
	p.IntervalSeconds = &intervalSeconds
	return p
}

// SetMaxConsecutiveFailures sets the maximum consecutive failures on the health check
func (p *PodHealthCheck) SetMaxConsecutiveFailures(maxFailures int) *PodHealthCheck {
	p.MaxConsecutiveFailures = &maxFailures
	return p
}

// SetTimeout sets the length of time the health check will await a result, in seconds
func (p *PodHealthCheck) SetTimeout(timeoutSeconds int) *PodHealthCheck {
	p.TimeoutSeconds = &timeoutSeconds
	return p
}

// SetDelay sets the length of time a pod will delay running health checks on initial launch, in seconds
func (p *PodHealthCheck) SetDelay(delaySeconds int) *PodHealthCheck {
	p.DelaySeconds = &delaySeconds
	return p
}

// SetEndpoint sets the name of the pod health check endpoint
func (h *HTTPHealthCheck) SetEndpoint(endpoint string) *HTTPHealthCheck {
	h.Endpoint = endpoint
	return h
}

// SetPath sets the HTTP path of the pod health check endpoint
func (h *HTTPHealthCheck) SetPath(path string) *HTTPHealthCheck {
	h.Path = path
	return h
}

// SetScheme sets the HTTP scheme of the pod health check endpoint
func (h *HTTPHealthCheck) SetScheme(scheme string) *HTTPHealthCheck {
	h.Scheme = scheme
	return h
}

// SetEndpoint sets the name of the pod health check endpoint
func (t *TCPHealthCheck) SetEndpoint(endpoint string) *TCPHealthCheck {
	t.Endpoint = endpoint
	return t
}

// SetCommand sets a CommandHealthCheck's underlying PodCommand
func (c *CommandHealthCheck) SetCommand(p PodCommand) *CommandHealthCheck {
	c.Command = p
	return c
}
<|endoftext|>


<commit_before>package riakpb

import (
	"bytes"
	"github.com/philhofer/riakpb/rpbc"
)

// Object is the interface that must
// be satisfied in order to store and retrieve
// an object from Riak.
type Object interface {
	// Satisfy this by
	// putting an Info{}
	// into your object.
	Info() *Info

	// Marshal should return the encoded
	// value of the object. It may use
	// the bytes passed to it as an argument
	// in order to reduce allocations, although
	// it should not count on that slice not being
	// nil.
	Marshal([]byte) ([]byte, error)

	// Unmarshal should unmarshal the object
	// from a []byte. It can safely use
	// zero-copy methods.
	Unmarshal([]byte) error
}

// Info contains information
// about a riak object. You can use
// it to satisfy the Object interface.
type Info struct {
	key    []byte          // key
	bucket []byte          // bucket
	links  []*rpbc.RpbLink // Links
	idxs   []*rpbc.RpbPair // Indexes
	meta   []*rpbc.RpbPair // Meta
	ctype  []byte          // Content-Type
	vclock []byte          // Vclock
	value  []byte          // value
}

func readHeader(o Object, ctnt *rpbc.RpbContent) {
	if o.Info() == nil {
		panic("nil Info")
	}
	o.Info().ctype = ctnt.GetContentType()
	o.Info().links = ctnt.GetLinks()
	o.Info().idxs = ctnt.GetIndexes()
	o.Info().meta = ctnt.GetUsermeta()
}

// read into 'o' from content
func readContent(o Object, ctnt *rpbc.RpbContent) error {
	// just in case
	if o.Info() == nil {
		panic("nil Info")
	}

	o.Info().ctype = ctnt.ContentType
	o.Info().links = ctnt.Links
	o.Info().idxs = ctnt.Indexes
	o.Info().meta = ctnt.Usermeta

	// read content
	o.Info().value = ctnt.Value // save reference
	return o.Unmarshal(ctnt.Value)
}

// write into content from 'o'
func writeContent(o Object, ctnt *rpbc.RpbContent) error {
	if o.Info() == nil {
		panic("nil Info")
	}

	var err error
	ctnt.Value, err = o.Marshal(nil)
	if err != nil {
		return err
	}
	ctnt.ContentType = o.Info().ctype
	ctnt.Links = o.Info().links
	ctnt.Usermeta = o.Info().meta
	ctnt.Indexes = o.Info().idxs
	return nil
}

func set(l *[]*rpbc.RpbPair, key, value []byte) {
	if l == nil || len(*l) == 0 {
		goto add
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			item.Key = key
			item.Value = value
			return
		}
	}
add:
	*l = append(*l, &rpbc.RpbPair{
		Key:   key,
		Value: value,
	})
	return
}

func get(l *[]*rpbc.RpbPair, key []byte) []byte {
	if l == nil || len(*l) == 0 {
		return nil
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			return item.Value
		}
	}
	return nil
}

func add(l *[]*rpbc.RpbPair, key, value []byte) bool {
	if l == nil || len(*l) == 0 {
		goto add
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			if bytes.Equal(value, item.Value) {
				return true
			}
			return false
		}
	}
add:
	*l = append(*l, &rpbc.RpbPair{
		Key:   key,
		Value: value,
	})
	return true
}

func del(l *[]*rpbc.RpbPair, key []byte) {
	if l == nil || len(*l) == 0 {
		return
	}
	nl := len(*l)
	for i, item := range *l {
		if bytes.Equal(key, item.Key) {
			(*l)[i], (*l)[nl-1], *l = (*l)[nl-1], nil, (*l)[:nl-1]
			return
		}
	}
}

// Key is the canonical riak key
func (in *Info) Key() string { return string(in.key) }

// Bucket is the canonical riak bucket
func (in *Info) Bucket() string { return string(in.bucket) }

// ContentType is the content-type
func (in *Info) ContentType() string { return string(in.ctype) }

func (in *Info) SetContentType(s string) { in.ctype = []byte(s) }

// Vclock is the vector clock value as a string
func (in *Info) Vclock() string { return string(in.vclock) }

// Add adds a key-value pair to an Indexes
// object, but returns false if a key already
// exists under that name and has a different value.
// Returns true if the index already has this exact key-value
// pair, or if the pair is written in with no conflicts.
func (in *Info) AddIndex(key string, value string) bool {
	return add(&in.idxs, []byte(key), []byte(value))
}

// Set sets a key-value pair in an Indexes object
func (in *Info) SetIndex(key string, value string) {
	set(&in.idxs, []byte(key), []byte(value))
}

// Get gets a key-value pair in an indexes object
func (in *Info) GetIndex(key string) (val string) {
	return string(get(&in.idxs, []byte(key)))
}

// Remove removes a key from an indexes object
func (in *Info) RemoveIndex(key string) {
	del(&in.idxs, []byte(key))
}

// AddMeta conditionally adds a key-value pair
// if it didn't exist already
func (in *Info) AddMeta(key string, value string) bool {
	return add(&in.meta, []byte(key), []byte(value))
}

// SetMeta sets a key-value pair
func (in *Info) SetMeta(key string, value string) {
	set(&in.meta, []byte(key), []byte(value))
}

// GetMeta gets a meta value
func (in *Info) GetMeta(key string) (val string) {
	return string(get(&in.meta, []byte(key)))
}

// RemoveMeta deletes the meta value
// at a key
func (in *Info) RemoveMeta(key string) {
	del(&in.meta, []byte(key))
}

// AddLink adds a link conditionally. It returns true
// if the value was already set to this bucket-key pair,
// or if no value existed at 'name'. It returns false otherwise.
func (in *Info) AddLink(name string, bucket string, key string) bool {
	nm := []byte(name)

	// don't duplicate
	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			return false
		}
	}

	in.links = append(in.links, &rpbc.RpbLink{
		Bucket: []byte(bucket),
		Key:    []byte(key),
		Tag:    nm,
	})
	return true
}

// SetLink sets a link
func (in *Info) SetLink(name string, bucket string, key string) {
	nm := []byte(name)
	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			link.Bucket = []byte(bucket)
			link.Key = []byte(key)
			return
		}
	}
	in.links = append(in.links, &rpbc.RpbLink{
		Bucket: []byte(bucket),
		Key:    []byte(key),
		Tag:    nm,
	})
	return
}

// RemoveLink removes a link (if it exists)
func (in *Info) RemoveLink(name string) {
	nm := []byte(name)
	nl := len(in.links)
	if nl == 0 {
		return
	}
	for i, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			// swap and don't preserve order
			in.links[i], in.links[nl-1], in.links = in.links[nl-1], nil, in.links[:nl-1]
		}
	}
}

// GetLink gets a link bucket-key pari
func (in *Info) GetLink(name string) (bucket string, key string) {
	nm := []byte(name)

	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			bucket = string(link.GetBucket())
			key = string(link.GetKey())
			return
		}
	}
	return
}
<commit_msg>all() for Indexes and UserMeta<commit_after>package riakpb

import (
	"bytes"
	"github.com/philhofer/riakpb/rpbc"
)

// Object is the interface that must
// be satisfied in order to store and retrieve
// an object from Riak.
type Object interface {
	// Satisfy this by
	// putting an Info{}
	// into your object.
	Info() *Info

	// Marshal should return the encoded
	// value of the object. It may use
	// the bytes passed to it as an argument
	// in order to reduce allocations, although
	// it should not count on that slice not being
	// nil.
	Marshal([]byte) ([]byte, error)

	// Unmarshal should unmarshal the object
	// from a []byte. It can safely use
	// zero-copy methods.
	Unmarshal([]byte) error
}

// Info contains information
// about a riak object. You can use
// it to satisfy the Object interface.
type Info struct {
	key    []byte          // key
	bucket []byte          // bucket
	links  []*rpbc.RpbLink // Links
	idxs   []*rpbc.RpbPair // Indexes
	meta   []*rpbc.RpbPair // Meta
	ctype  []byte          // Content-Type
	vclock []byte          // Vclock
	value  []byte          // value
}

func readHeader(o Object, ctnt *rpbc.RpbContent) {
	if o.Info() == nil {
		panic("nil Info")
	}
	o.Info().ctype = ctnt.GetContentType()
	o.Info().links = ctnt.GetLinks()
	o.Info().idxs = ctnt.GetIndexes()
	o.Info().meta = ctnt.GetUsermeta()
}

// read into 'o' from content
func readContent(o Object, ctnt *rpbc.RpbContent) error {
	// just in case
	if o.Info() == nil {
		panic("nil Info")
	}

	o.Info().ctype = ctnt.ContentType
	o.Info().links = ctnt.Links
	o.Info().idxs = ctnt.Indexes
	o.Info().meta = ctnt.Usermeta

	// read content
	o.Info().value = ctnt.Value // save reference
	return o.Unmarshal(ctnt.Value)
}

// write into content from 'o'
func writeContent(o Object, ctnt *rpbc.RpbContent) error {
	if o.Info() == nil {
		panic("nil Info")
	}

	var err error
	ctnt.Value, err = o.Marshal(nil)
	if err != nil {
		return err
	}
	ctnt.ContentType = o.Info().ctype
	ctnt.Links = o.Info().links
	ctnt.Usermeta = o.Info().meta
	ctnt.Indexes = o.Info().idxs
	return nil
}

func set(l *[]*rpbc.RpbPair, key, value []byte) {
	if l == nil || len(*l) == 0 {
		goto add
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			item.Key = key
			item.Value = value
			return
		}
	}
add:
	*l = append(*l, &rpbc.RpbPair{
		Key:   key,
		Value: value,
	})
	return
}

func get(l *[]*rpbc.RpbPair, key []byte) []byte {
	if l == nil || len(*l) == 0 {
		return nil
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			return item.Value
		}
	}
	return nil
}

func add(l *[]*rpbc.RpbPair, key, value []byte) bool {
	if l == nil || len(*l) == 0 {
		goto add
	}
	for _, item := range *l {
		if bytes.Equal(key, item.Key) {
			if bytes.Equal(value, item.Value) {
				return true
			}
			return false
		}
	}
add:
	*l = append(*l, &rpbc.RpbPair{
		Key:   key,
		Value: value,
	})
	return true
}

func del(l *[]*rpbc.RpbPair, key []byte) {
	if l == nil || len(*l) == 0 {
		return
	}
	nl := len(*l)
	for i, item := range *l {
		if bytes.Equal(key, item.Key) {
			(*l)[i], (*l)[nl-1], *l = (*l)[nl-1], nil, (*l)[:nl-1]
			return
		}
	}
}

func all(l *[]*rpbc.RpbPair) [][2]string {
	nl := len(*l)
	if nl == 0 {
		return nil
	}
	out := make([][2]string, nl)
	for i, item := range *l {
		out[i] = [2]string{string(item.Key), string(item.Value)}
	}
	return out
}

// Key is the canonical riak key
func (in *Info) Key() string { return string(in.key) }

// Bucket is the canonical riak bucket
func (in *Info) Bucket() string { return string(in.bucket) }

// ContentType is the content-type
func (in *Info) ContentType() string { return string(in.ctype) }

func (in *Info) SetContentType(s string) { in.ctype = []byte(s) }

// Vclock is the vector clock value as a string
func (in *Info) Vclock() string { return string(in.vclock) }

// Add adds a key-value pair to an Indexes
// object, but returns false if a key already
// exists under that name and has a different value.
// Returns true if the index already has this exact key-value
// pair, or if the pair is written in with no conflicts.
func (in *Info) AddIndex(key string, value string) bool {
	return add(&in.idxs, []byte(key), []byte(value))
}

// Set sets a key-value pair in an Indexes object
func (in *Info) SetIndex(key string, value string) {
	set(&in.idxs, []byte(key), []byte(value))
}

// Get gets a key-value pair in an indexes object
func (in *Info) GetIndex(key string) (val string) {
	return string(get(&in.idxs, []byte(key)))
}

// Remove removes a key from an indexes object
func (in *Info) RemoveIndex(key string) {
	del(&in.idxs, []byte(key))
}

// Indexes returns a list of all of the
// key-value pairs in this object. (Key first,
// then value.)
func (in *Info) Indexes() [][2]string {
	return all(&in.idxs)
}

// AddMeta conditionally adds a key-value pair
// if it didn't exist already
func (in *Info) AddMeta(key string, value string) bool {
	return add(&in.meta, []byte(key), []byte(value))
}

// SetMeta sets a key-value pair
func (in *Info) SetMeta(key string, value string) {
	set(&in.meta, []byte(key), []byte(value))
}

// GetMeta gets a meta value
func (in *Info) GetMeta(key string) (val string) {
	return string(get(&in.meta, []byte(key)))
}

// RemoveMeta deletes the meta value
// at a key
func (in *Info) RemoveMeta(key string) {
	del(&in.meta, []byte(key))
}

// Metas returns all of the metadata
// key-value pairs. (Key first, then value.)
func (in *Info) Metas() [][2]string {
	return all(&in.idxs)
}

// AddLink adds a link conditionally. It returns true
// if the value was already set to this bucket-key pair,
// or if no value existed at 'name'. It returns false otherwise.
func (in *Info) AddLink(name string, bucket string, key string) bool {
	nm := []byte(name)

	// don't duplicate
	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			return false
		}
	}

	in.links = append(in.links, &rpbc.RpbLink{
		Bucket: []byte(bucket),
		Key:    []byte(key),
		Tag:    nm,
	})
	return true
}

// SetLink sets a link
func (in *Info) SetLink(name string, bucket string, key string) {
	nm := []byte(name)
	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			link.Bucket = []byte(bucket)
			link.Key = []byte(key)
			return
		}
	}
	in.links = append(in.links, &rpbc.RpbLink{
		Bucket: []byte(bucket),
		Key:    []byte(key),
		Tag:    nm,
	})
	return
}

// RemoveLink removes a link (if it exists)
func (in *Info) RemoveLink(name string) {
	nm := []byte(name)
	nl := len(in.links)
	if nl == 0 {
		return
	}
	for i, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			// swap and don't preserve order
			in.links[i], in.links[nl-1], in.links = in.links[nl-1], nil, in.links[:nl-1]
		}
	}
}

// GetLink gets a link bucket-key pari
func (in *Info) GetLink(name string) (bucket string, key string) {
	nm := []byte(name)

	for _, link := range in.links {
		if bytes.Equal(nm, link.GetTag()) {
			bucket = string(link.GetBucket())
			key = string(link.GetKey())
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSpotInstanceRequest() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSpotInstanceRequestCreate,
		Read:   resourceAwsSpotInstanceRequestRead,
		Delete: resourceAwsSpotInstanceRequestDelete,
		Update: resourceAwsSpotInstanceRequestUpdate,

		Schema: func() map[string]*schema.Schema {
			// The Spot Instance Request Schema is based on the AWS Instance schema.
			s := resourceAwsInstance().Schema

			// Everything on a spot instance is ForceNew except tags
			for k, v := range s {
				if k == "tags" {
					continue
				}
				v.ForceNew = true
			}

			s["spot_price"] = &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			}
			s["spot_type"] = &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "persistent",
			}
			s["wait_for_fulfillment"] = &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			}
			s["spot_bid_status"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["spot_request_state"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["spot_instance_id"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["block_duration_minutes"] = &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			}

			return s
		}(),
	}
}

func resourceAwsSpotInstanceRequestCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	instanceOpts, err := buildAwsInstanceOpts(d, meta)
	if err != nil {
		return err
	}

	spotOpts := &ec2.RequestSpotInstancesInput{
		SpotPrice: aws.String(d.Get("spot_price").(string)),
		Type:      aws.String(d.Get("spot_type").(string)),

		// Though the AWS API supports creating spot instance requests for multiple
		// instances, for TF purposes we fix this to one instance per request.
		// Users can get equivalent behavior out of TF's "count" meta-parameter.
		InstanceCount: aws.Int64(1),

		LaunchSpecification: &ec2.RequestSpotLaunchSpecification{
			BlockDeviceMappings: instanceOpts.BlockDeviceMappings,
			EbsOptimized:        instanceOpts.EBSOptimized,
			Monitoring:          instanceOpts.Monitoring,
			IamInstanceProfile:  instanceOpts.IAMInstanceProfile,
			ImageId:             instanceOpts.ImageID,
			InstanceType:        instanceOpts.InstanceType,
			KeyName:             instanceOpts.KeyName,
			Placement:           instanceOpts.SpotPlacement,
			SecurityGroupIds:    instanceOpts.SecurityGroupIDs,
			SecurityGroups:      instanceOpts.SecurityGroups,
			SubnetId:            instanceOpts.SubnetID,
			UserData:            instanceOpts.UserData64,
		},
	}

	if v, ok := d.GetOk("block_duration_minutes"); ok {
		spotOpts.BlockDurationMinutes = aws.Int64(int64(v.(int)))
	}

	// If the instance is configured with a Network Interface (a subnet, has
	// public IP, etc), then the instanceOpts.SecurityGroupIds and SubnetId will
	// be nil
	if len(instanceOpts.NetworkInterfaces) > 0 {
		spotOpts.LaunchSpecification.SecurityGroupIds = instanceOpts.NetworkInterfaces[0].Groups
		spotOpts.LaunchSpecification.SubnetId = instanceOpts.NetworkInterfaces[0].SubnetId
	}

	// Make the spot instance request
	log.Printf("[DEBUG] Requesting spot bid opts: %s", spotOpts)

	var resp *ec2.RequestSpotInstancesOutput
	err = resource.Retry(15*time.Second, func() *resource.RetryError {
		var err error
		resp, err = conn.RequestSpotInstances(spotOpts)
		// IAM instance profiles can take ~10 seconds to propagate in AWS:
		// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
		if isAWSErr(err, "InvalidParameterValue", "Invalid IAM Instance Profile") {
			log.Printf("[DEBUG] Invalid IAM Instance Profile referenced, retrying...")
			return resource.RetryableError(err)
		}
		// IAM roles can also take time to propagate in AWS:
		if isAWSErr(err, "InvalidParameterValue", " has no associated IAM Roles") {
			log.Printf("[DEBUG] IAM Instance Profile appears to have no IAM roles, retrying...")
			return resource.RetryableError(err)
		}
		return resource.NonRetryableError(err)
	})

	if err != nil {
		return fmt.Errorf("Error requesting spot instances: %s", err)
	}
	if len(resp.SpotInstanceRequests) != 1 {
		return fmt.Errorf(
			"Expected response with length 1, got: %s", resp)
	}

	sir := *resp.SpotInstanceRequests[0]
	d.SetId(*sir.SpotInstanceRequestId)

	if d.Get("wait_for_fulfillment").(bool) {
		spotStateConf := &resource.StateChangeConf{
			// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html
			Pending:    []string{"start", "pending-evaluation", "pending-fulfillment"},
			Target:     []string{"fulfilled"},
			Refresh:    SpotInstanceStateRefreshFunc(conn, sir),
			Timeout:    10 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		log.Printf("[DEBUG] waiting for spot bid to resolve... this may take several minutes.")
		_, err = spotStateConf.WaitForState()

		if err != nil {
			return fmt.Errorf("Error while waiting for spot request (%s) to resolve: %s", sir, err)
		}
	}

	return resourceAwsSpotInstanceRequestUpdate(d, meta)
}

// Update spot state, etc
func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	req := &ec2.DescribeSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{aws.String(d.Id())},
	}
	resp, err := conn.DescribeSpotInstanceRequests(req)

	if err != nil {
		// If the spot request was not found, return nil so that we can show
		// that it is gone.
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSpotInstanceRequestID.NotFound" {
			d.SetId("")
			return nil
		}

		// Some other error, report it
		return err
	}

	// If nothing was found, then return no state
	if len(resp.SpotInstanceRequests) == 0 {
		d.SetId("")
		return nil
	}

	request := resp.SpotInstanceRequests[0]

	// if the request is cancelled, then it is gone
	if *request.State == "cancelled" {
		d.SetId("")
		return nil
	}

	d.Set("spot_bid_status", *request.Status.Code)
	// Instance ID is not set if the request is still pending
	if request.InstanceId != nil {
		d.Set("spot_instance_id", *request.InstanceId)
		// Read the instance data, setting up connection information
		if err := readInstance(d, meta); err != nil {
			return fmt.Errorf("[ERR] Error reading Spot Instance Data: %s", err)
		}
	}

	d.Set("spot_request_state", request.State)
	d.Set("block_duration_minutes", request.BlockDurationMinutes)
	d.Set("tags", tagsToMap(request.Tags))

	return nil
}

func readInstance(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
		InstanceIds: []*string{aws.String(d.Get("spot_instance_id").(string))},
	})
	if err != nil {
		// If the instance was not found, return nil so that we can show
		// that the instance is gone.
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
			return fmt.Errorf("no instance found")
		}

		// Some other error, report it
		return err
	}

	// If nothing was found, then return no state
	if len(resp.Reservations) == 0 {
		return fmt.Errorf("no instances found")
	}

	instance := resp.Reservations[0].Instances[0]

	// Set these fields for connection information
	if instance != nil {
		d.Set("public_dns", instance.PublicDnsName)
		d.Set("public_ip", instance.PublicIpAddress)
		d.Set("private_dns", instance.PrivateDnsName)
		d.Set("private_ip", instance.PrivateIpAddress)

		// set connection information
		if instance.PublicIpAddress != nil {
			d.SetConnInfo(map[string]string{
				"type": "ssh",
				"host": *instance.PublicIpAddress,
			})
		} else if instance.PrivateIpAddress != nil {
			d.SetConnInfo(map[string]string{
				"type": "ssh",
				"host": *instance.PrivateIpAddress,
			})
		}
		if err := readBlockDevices(d, instance, conn); err != nil {
			return err
		}
	}

	return nil
}

func resourceAwsSpotInstanceRequestUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	d.Partial(true)
	if err := setTags(conn, d); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	d.Partial(false)

	return resourceAwsSpotInstanceRequestRead(d, meta)
}

func resourceAwsSpotInstanceRequestDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	log.Printf("[INFO] Cancelling spot request: %s", d.Id())
	_, err := conn.CancelSpotInstanceRequests(&ec2.CancelSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{aws.String(d.Id())},
	})

	if err != nil {
		return fmt.Errorf("Error cancelling spot request (%s): %s", d.Id(), err)
	}

	if instanceId := d.Get("spot_instance_id").(string); instanceId != "" {
		log.Printf("[INFO] Terminating instance: %s", instanceId)
		if err := awsTerminateInstance(conn, instanceId); err != nil {
			return fmt.Errorf("Error terminating spot instance: %s", err)
		}
	}

	return nil
}

// SpotInstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an EC2 spot instance request
func SpotInstanceStateRefreshFunc(
	conn *ec2.EC2, sir ec2.SpotInstanceRequest) resource.StateRefreshFunc {

	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{
			SpotInstanceRequestIds: []*string{sir.SpotInstanceRequestId},
		})

		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSpotInstanceRequestID.NotFound" {
				// Set this to nil as if we didn't find anything.
				resp = nil
			} else {
				log.Printf("Error on StateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil || len(resp.SpotInstanceRequests) == 0 {
			// Sometimes AWS just has consistency issues and doesn't see
			// our request yet. Return an empty state.
			return nil, "", nil
		}

		req := resp.SpotInstanceRequests[0]
		return req, *req.Status.Code, nil
	}
}
<commit_msg>provider/aws: Update spot instance request to store new ipv6 (#12571)<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSpotInstanceRequest() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSpotInstanceRequestCreate,
		Read:   resourceAwsSpotInstanceRequestRead,
		Delete: resourceAwsSpotInstanceRequestDelete,
		Update: resourceAwsSpotInstanceRequestUpdate,

		Schema: func() map[string]*schema.Schema {
			// The Spot Instance Request Schema is based on the AWS Instance schema.
			s := resourceAwsInstance().Schema

			// Everything on a spot instance is ForceNew except tags
			for k, v := range s {
				if k == "tags" {
					continue
				}
				v.ForceNew = true
			}

			s["spot_price"] = &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			}
			s["spot_type"] = &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "persistent",
			}
			s["wait_for_fulfillment"] = &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			}
			s["spot_bid_status"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["spot_request_state"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["spot_instance_id"] = &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			}
			s["block_duration_minutes"] = &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				ForceNew: true,
			}

			return s
		}(),
	}
}

func resourceAwsSpotInstanceRequestCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	instanceOpts, err := buildAwsInstanceOpts(d, meta)
	if err != nil {
		return err
	}

	spotOpts := &ec2.RequestSpotInstancesInput{
		SpotPrice: aws.String(d.Get("spot_price").(string)),
		Type:      aws.String(d.Get("spot_type").(string)),

		// Though the AWS API supports creating spot instance requests for multiple
		// instances, for TF purposes we fix this to one instance per request.
		// Users can get equivalent behavior out of TF's "count" meta-parameter.
		InstanceCount: aws.Int64(1),

		LaunchSpecification: &ec2.RequestSpotLaunchSpecification{
			BlockDeviceMappings: instanceOpts.BlockDeviceMappings,
			EbsOptimized:        instanceOpts.EBSOptimized,
			Monitoring:          instanceOpts.Monitoring,
			IamInstanceProfile:  instanceOpts.IAMInstanceProfile,
			ImageId:             instanceOpts.ImageID,
			InstanceType:        instanceOpts.InstanceType,
			KeyName:             instanceOpts.KeyName,
			Placement:           instanceOpts.SpotPlacement,
			SecurityGroupIds:    instanceOpts.SecurityGroupIDs,
			SecurityGroups:      instanceOpts.SecurityGroups,
			SubnetId:            instanceOpts.SubnetID,
			UserData:            instanceOpts.UserData64,
		},
	}

	if v, ok := d.GetOk("block_duration_minutes"); ok {
		spotOpts.BlockDurationMinutes = aws.Int64(int64(v.(int)))
	}

	// If the instance is configured with a Network Interface (a subnet, has
	// public IP, etc), then the instanceOpts.SecurityGroupIds and SubnetId will
	// be nil
	if len(instanceOpts.NetworkInterfaces) > 0 {
		spotOpts.LaunchSpecification.SecurityGroupIds = instanceOpts.NetworkInterfaces[0].Groups
		spotOpts.LaunchSpecification.SubnetId = instanceOpts.NetworkInterfaces[0].SubnetId
	}

	// Make the spot instance request
	log.Printf("[DEBUG] Requesting spot bid opts: %s", spotOpts)

	var resp *ec2.RequestSpotInstancesOutput
	err = resource.Retry(15*time.Second, func() *resource.RetryError {
		var err error
		resp, err = conn.RequestSpotInstances(spotOpts)
		// IAM instance profiles can take ~10 seconds to propagate in AWS:
		// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
		if isAWSErr(err, "InvalidParameterValue", "Invalid IAM Instance Profile") {
			log.Printf("[DEBUG] Invalid IAM Instance Profile referenced, retrying...")
			return resource.RetryableError(err)
		}
		// IAM roles can also take time to propagate in AWS:
		if isAWSErr(err, "InvalidParameterValue", " has no associated IAM Roles") {
			log.Printf("[DEBUG] IAM Instance Profile appears to have no IAM roles, retrying...")
			return resource.RetryableError(err)
		}
		return resource.NonRetryableError(err)
	})

	if err != nil {
		return fmt.Errorf("Error requesting spot instances: %s", err)
	}
	if len(resp.SpotInstanceRequests) != 1 {
		return fmt.Errorf(
			"Expected response with length 1, got: %s", resp)
	}

	sir := *resp.SpotInstanceRequests[0]
	d.SetId(*sir.SpotInstanceRequestId)

	if d.Get("wait_for_fulfillment").(bool) {
		spotStateConf := &resource.StateChangeConf{
			// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html
			Pending:    []string{"start", "pending-evaluation", "pending-fulfillment"},
			Target:     []string{"fulfilled"},
			Refresh:    SpotInstanceStateRefreshFunc(conn, sir),
			Timeout:    10 * time.Minute,
			Delay:      10 * time.Second,
			MinTimeout: 3 * time.Second,
		}

		log.Printf("[DEBUG] waiting for spot bid to resolve... this may take several minutes.")
		_, err = spotStateConf.WaitForState()

		if err != nil {
			return fmt.Errorf("Error while waiting for spot request (%s) to resolve: %s", sir, err)
		}
	}

	return resourceAwsSpotInstanceRequestUpdate(d, meta)
}

// Update spot state, etc
func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	req := &ec2.DescribeSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{aws.String(d.Id())},
	}
	resp, err := conn.DescribeSpotInstanceRequests(req)

	if err != nil {
		// If the spot request was not found, return nil so that we can show
		// that it is gone.
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSpotInstanceRequestID.NotFound" {
			d.SetId("")
			return nil
		}

		// Some other error, report it
		return err
	}

	// If nothing was found, then return no state
	if len(resp.SpotInstanceRequests) == 0 {
		d.SetId("")
		return nil
	}

	request := resp.SpotInstanceRequests[0]

	// if the request is cancelled, then it is gone
	if *request.State == "cancelled" {
		d.SetId("")
		return nil
	}

	d.Set("spot_bid_status", *request.Status.Code)
	// Instance ID is not set if the request is still pending
	if request.InstanceId != nil {
		d.Set("spot_instance_id", *request.InstanceId)
		// Read the instance data, setting up connection information
		if err := readInstance(d, meta); err != nil {
			return fmt.Errorf("[ERR] Error reading Spot Instance Data: %s", err)
		}
	}

	d.Set("spot_request_state", request.State)
	d.Set("block_duration_minutes", request.BlockDurationMinutes)
	d.Set("tags", tagsToMap(request.Tags))

	return nil
}

func readInstance(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
		InstanceIds: []*string{aws.String(d.Get("spot_instance_id").(string))},
	})
	if err != nil {
		// If the instance was not found, return nil so that we can show
		// that the instance is gone.
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" {
			return fmt.Errorf("no instance found")
		}

		// Some other error, report it
		return err
	}

	// If nothing was found, then return no state
	if len(resp.Reservations) == 0 {
		return fmt.Errorf("no instances found")
	}

	instance := resp.Reservations[0].Instances[0]

	// Set these fields for connection information
	if instance != nil {
		d.Set("public_dns", instance.PublicDnsName)
		d.Set("public_ip", instance.PublicIpAddress)
		d.Set("private_dns", instance.PrivateDnsName)
		d.Set("private_ip", instance.PrivateIpAddress)

		// set connection information
		if instance.PublicIpAddress != nil {
			d.SetConnInfo(map[string]string{
				"type": "ssh",
				"host": *instance.PublicIpAddress,
			})
		} else if instance.PrivateIpAddress != nil {
			d.SetConnInfo(map[string]string{
				"type": "ssh",
				"host": *instance.PrivateIpAddress,
			})
		}
		if err := readBlockDevices(d, instance, conn); err != nil {
			return err
		}

		var ipv6Addresses []string
		if len(instance.NetworkInterfaces) > 0 {
			for _, ni := range instance.NetworkInterfaces {
				if *ni.Attachment.DeviceIndex == 0 {
					d.Set("subnet_id", ni.SubnetId)
					d.Set("network_interface_id", ni.NetworkInterfaceId)
					d.Set("associate_public_ip_address", ni.Association != nil)
					d.Set("ipv6_address_count", len(ni.Ipv6Addresses))

					for _, address := range ni.Ipv6Addresses {
						ipv6Addresses = append(ipv6Addresses, *address.Ipv6Address)
					}
				}
			}
		} else {
			d.Set("subnet_id", instance.SubnetId)
			d.Set("network_interface_id", "")
		}

		if err := d.Set("ipv6_addresses", ipv6Addresses); err != nil {
			log.Printf("[WARN] Error setting ipv6_addresses for AWS Spot Instance (%s): %s", d.Id(), err)
		}
	}

	return nil
}

func resourceAwsSpotInstanceRequestUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	d.Partial(true)
	if err := setTags(conn, d); err != nil {
		return err
	} else {
		d.SetPartial("tags")
	}

	d.Partial(false)

	return resourceAwsSpotInstanceRequestRead(d, meta)
}

func resourceAwsSpotInstanceRequestDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	log.Printf("[INFO] Cancelling spot request: %s", d.Id())
	_, err := conn.CancelSpotInstanceRequests(&ec2.CancelSpotInstanceRequestsInput{
		SpotInstanceRequestIds: []*string{aws.String(d.Id())},
	})

	if err != nil {
		return fmt.Errorf("Error cancelling spot request (%s): %s", d.Id(), err)
	}

	if instanceId := d.Get("spot_instance_id").(string); instanceId != "" {
		log.Printf("[INFO] Terminating instance: %s", instanceId)
		if err := awsTerminateInstance(conn, instanceId); err != nil {
			return fmt.Errorf("Error terminating spot instance: %s", err)
		}
	}

	return nil
}

// SpotInstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an EC2 spot instance request
func SpotInstanceStateRefreshFunc(
	conn *ec2.EC2, sir ec2.SpotInstanceRequest) resource.StateRefreshFunc {

	return func() (interface{}, string, error) {
		resp, err := conn.DescribeSpotInstanceRequests(&ec2.DescribeSpotInstanceRequestsInput{
			SpotInstanceRequestIds: []*string{sir.SpotInstanceRequestId},
		})

		if err != nil {
			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSpotInstanceRequestID.NotFound" {
				// Set this to nil as if we didn't find anything.
				resp = nil
			} else {
				log.Printf("Error on StateRefresh: %s", err)
				return nil, "", err
			}
		}

		if resp == nil || len(resp.SpotInstanceRequests) == 0 {
			// Sometimes AWS just has consistency issues and doesn't see
			// our request yet. Return an empty state.
			return nil, "", nil
		}

		req := resp.SpotInstanceRequests[0]
		return req, *req.Status.Code, nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"os"
)

func main() {
	var (
		clientOV        *ov.OVClient
		id              = "d4468f89-4442-4324-9c01-624c7382db2d"
		macAddress      = "94:57:A5:67:2C:BE"
		internalVlan    = "504"
		interconnectURI = "/rest/interconnects/aca6687f-1370-46cd-b832-7e3192dbddfd"
		externalVlan    = "504"
		tcId            = "1"
		//li_name     = "SYN03_LE-SYN03_LIG"
		//li_type   =   "logical-interconnectV5"
	)
	ovc := clientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		800)
	ovVer, _ := ovc.GetAPIVersion()
	fmt.Println(ovVer)

	fmt.Println("....  Logical Interconnects Collection .....")
	logicalInterconnectList, _ := ovc.GetLogicalInterconnects("", "0", "10")
	fmt.Println(logicalInterconnectList)

	fmt.Println("....  Logical Interconnect by Id.....")
	lig, _ := ovc.GetLogicalInterconnectById(id)
	fmt.Println(lig)

	fmt.Println("....  Logical Interconnect PortMonitor.....")
	portMonitor, _ := ovc.GetLogicalInterconnectPortMonitor(id)
	fmt.Println(portMonitor)

	fmt.Println("....  Logical Interconnect EthernetSettings.....")
	ethernetSettings, _ := ovc.GetLogicalInterconnectEthernetSettings(id)
	fmt.Println(ethernetSettings)

	fmt.Println("....  Logical Interconnect Firmware.....")
	firmware, _ := ovc.GetLogicalInterconnectFirmware(id)
	fmt.Println(firmware)

	fmt.Println("....  Logical Interconnect SNMPConfiguration.....")
	snmpconfig, _ := ovc.GetLogicalInterconnectSNMPConfiguration(id)
	fmt.Println(snmpconfig)

	fmt.Println("....  Logical Interconnect Forwarding Information.....")
	fi, _ := ovc.GetLogicalInterconnectForwardingInformation("", "", id)
	fmt.Println(fi)

	fmt.Println("....  Logical Interconnect Forwarding Information By Mac Address.....")
	fi_mac, _ := ovc.GetLogicalInterconnectForwardingInformationByMacAddress(macAddress, id)
	fmt.Println(fi_mac)

	fmt.Println("....  Logical Interconnect Forwarding Information By Internal Vlan.....")
	fi_intern_vlan, _ := ovc.GetLogicalInterconnectForwardingInformationByInternalVlan(internalVlan, id)
	fmt.Println(fi_intern_vlan)

	fmt.Println("....  Logical Interconnect Forwarding Information By Interconnect URI and ExternalVlan.....")
	fi_interconnect_external, _ := ovc.GetLogicalInterconnectForwardingInformationByInterconnectAndExternalVlan(interconnectURI, externalVlan, id)
	fmt.Println(fi_interconnect_external)

	fmt.Println("....  Logical Interconnect Internal VLAN IDs for the provisioned networks.....")
	fi_internal_vlan, _ := ovc.GetLogicalInternalVlans(id)
	fmt.Println(fi_internal_vlan)

	fmt.Println("....  Logical Interconnect QOS Configuration.....")
	fi_qos_config, _ := ovc.GetLogicalQosAggregatedConfiguration(id)
	fmt.Println(fi_qos_config)

	fmt.Println("....  Logical Interconnect Unassigned Ports for Port Monitor.....")
	port_monitor_ports := ovc.GetUnassignedPortsForPortMonitor(id)
	fmt.Println(port_monitor_ports)

	fmt.Println("....  Logical Interconnect Unassigned Uplink Ports for Port Monitor.....")
	uplink_port_monitor_ports, _ := ovc.GetUnassignedUplinkPortsForPortMonitor(id)
	fmt.Println(uplink_port_monitor_ports)

	fmt.Println("....  Logical Interconnect Telemetry Configuration.....")
	telemetry_config, _ := ovc.GetTelemetryConfigurations(id, "1")
	fmt.Println(telemetry_config)

	fmt.Println("....  Updating Logical Interconnect Consistent State.....")
	var liUris []utils.Nstring
	liUris = append(liUris, utils.NewNstring("/rest/logical-interconnects/d4468f89-4442-4324-9c01-624c7382db2d"))
	liCompliance := ov.LogicalInterconnectCompliance{Type: "li-compliance", LogicalInterconnectUris: liUris, Description: ""}
	err_compliance := ovc.UpdateLogicalInterconnectConsistentState(liCompliance)
	if err_compliance != nil {
		fmt.Println("Could not update ConsistentState of Logical Interconnect", err_compliance)
	}

	fmt.Println("....  Updating Logical Interconnect EthernetSetting.....")
	liEthernetSettings := ov.EthernetSettings{Type: "EthernetInterconnectSettingsV4", InterconnectType: "Ethernet", URI: utils.NewNstring("/rest/logical-interconnects/d4468f89-4442-4324-9c01-624c7382db2d/ethernetSettings"), ID: "d4468f89-4442-4324-9c01-624c7382db2d"}
	err_ethernet := ovc.UpdateLogicalInterconnectEthernetSettings(liEthernetSettings, id)
	if err_ethernet != nil {
		fmt.Println("Could not update Ethernet Settings of Logical Interconnect", err_ethernet)
	}

	fmt.Println("....  Updating Logical Interconnect Firmware.....")
	liFirmware := ov.Firmware{Command: "Update", EthernetActivationDelay: 5, EthernetActivationType: "Parallel", FcActivationDelay: 5, FcActivationType: "Parallel", Force: false, SppUri: utils.NewNstring("/rest/firmware-drivers/SPP_2018_06_20180709_for_HPE_Synergy_Z7550-96524")}
	err_firmware := ovc.UpdateLogicalInterconnectFirmware(liFirmware, id)
	if err_firmware != nil {
		fmt.Println("Could not update Firmware of Logical Interconnect", err_firmware)
	}

	fmt.Println("....  Updating Logical Interconnect InternalNetworks.....")
	var internalNetworks []utils.Nstring
	internalNetworks = append(internalNetworks, utils.NewNstring("/rest/ethernet-networks/a71b9c9e-b044-48ee-8e4e-26ced1a9a9ef"))
	err_networks := ovc.UpdateLogicalInterconnectInternalNetworks(internalNetworks, id)
	if err_networks != nil {
		fmt.Println("Could not update Internal Networks of Logical Interconnect", err_networks)
	}

	fmt.Println("....  Updating Logical Interconnect QOS Configuration.....")
	liActiveQosConfig := ov.ActiveQosConfig{Type: "QosConfiguration", Category: "qos-aggregated-configuration", ConfigType: "Passthrough"}
	liQosConfig := ov.QosConfiguration{Type: "qos-aggregated-configuration", Category: "qos-aggregated-configuration", ActiveQosConfig: liActiveQosConfig}

	err_qos := ovc.UpdateLogicalInterconnectQosConfigurations(liQosConfig, id)
	if err_qos != nil {
		fmt.Println("Could not update QOS Configuration of Logical Interconnect", err_qos)
	}

	fmt.Println("....  Updating Logical Interconnect SNMP Configuration.....")
	liSNMPConfig := ov.SnmpConfiguration{Type: "snmp-configuration", Category: "snmp-configuration", V3Enabled: true}

	err_snmp := ovc.UpdateLogicalInterconnectSNMPConfigurations(liSNMPConfig, id)
	if err_snmp != nil {
		fmt.Println("Could not update SNMP Configuration of Logical Interconnect", err_snmp)
	}

	fmt.Println("....  Updating Logical Interconnect Configuration.....")
	err_conf := ovc.UpdateLogicalInterconnectConfigurations(id)
	if err_conf != nil {
		fmt.Println("Could not update Configuration of Logical Interconnect", err_conf)
	}

	fmt.Println("....  Updating Logical Interconnect Port Monitor Configuration.....")
	liPMConfig := ov.PortMonitor{Type: "port-monitor", Category: "port-monitor", ETAG: "8a302a85-ec4d-4214-a3e0-10ef71d28769", Name: "name2095641007-1533682087640"}

	err_pm := ovc.UpdateLogicalInterconnectPortMonitor(liPMConfig, id)
	if err_pm != nil {
		fmt.Println("Could not update PortMonitor Configuration of Logical Interconnect", err_pm)
	}

	fmt.Println("....  Updating Logical Interconnect Telemetry  Configuration.....")
	liTMConfig := ov.TelemetryConfiguration{Type: "telemetry-configuration", EnableTelemetry: true, SampleInterval: 300, SampleCount: 12, Name: "name771327580-1533682118441"}

	err_tm := ovc.UpdateLogicalInterconnectTelemetryConfigurations(liTMConfig, id, tcId)
	if err_tm != nil {
		fmt.Println("Could not update PortMonitor Configuration of Logical Interconnect", err_tm)
	}

}
<commit_msg>addressing the review comments<commit_after>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"github.com/HewlettPackard/oneview-golang/utils"
	"os"
)

func main() {
	var (
		clientOV        *ov.OVClient
		id              = "d4468f89-4442-4324-9c01-624c7382db2d"
		macAddress      = "94:57:A5:67:2C:BE"
		internalVlan    = "504"
		interconnectURI = "/rest/interconnects/aca6687f-1370-46cd-b832-7e3192dbddfd"
		externalVlan    = "504"
		tcId            = "1"
	)
	ovc := clientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		800)

	fmt.Println("....  Logical Interconnects Collection .....")
	logicalInterconnectList, _ := ovc.GetLogicalInterconnects("", "0", "10")
	fmt.Println(logicalInterconnectList)

	fmt.Println("....  Logical Interconnect by Id.....")
	lig, _ := ovc.GetLogicalInterconnectById(id)
	fmt.Println(lig)

	fmt.Println("....  Logical Interconnect PortMonitor.....")
	portMonitor, _ := ovc.GetLogicalInterconnectPortMonitor(id)
	fmt.Println(portMonitor)

	fmt.Println("....  Logical Interconnect EthernetSettings.....")
	ethernetSettings, _ := ovc.GetLogicalInterconnectEthernetSettings(id)
	fmt.Println(ethernetSettings)

	fmt.Println("....  Logical Interconnect Firmware.....")
	firmware, _ := ovc.GetLogicalInterconnectFirmware(id)
	fmt.Println(firmware)

	fmt.Println("....  Logical Interconnect SNMPConfiguration.....")
	snmpconfig, _ := ovc.GetLogicalInterconnectSNMPConfiguration(id)
	fmt.Println(snmpconfig)

	fmt.Println("....  Logical Interconnect Forwarding Information.....")
	fi, _ := ovc.GetLogicalInterconnectForwardingInformation("", "", id)
	fmt.Println(fi)

	fmt.Println("....  Logical Interconnect Forwarding Information By Mac Address.....")
	fi_mac, _ := ovc.GetLogicalInterconnectForwardingInformationByMacAddress(macAddress, id)
	fmt.Println(fi_mac)

	fmt.Println("....  Logical Interconnect Forwarding Information By Internal Vlan.....")
	fi_intern_vlan, _ := ovc.GetLogicalInterconnectForwardingInformationByInternalVlan(internalVlan, id)
	fmt.Println(fi_intern_vlan)

	fmt.Println("....  Logical Interconnect Forwarding Information By Interconnect URI and ExternalVlan.....")
	fi_interconnect_external, _ := ovc.GetLogicalInterconnectForwardingInformationByInterconnectAndExternalVlan(interconnectURI, externalVlan, id)
	fmt.Println(fi_interconnect_external)

	fmt.Println("....  Logical Interconnect Internal VLAN IDs for the provisioned networks.....")
	fi_internal_vlan, _ := ovc.GetLogicalInternalVlans(id)
	fmt.Println(fi_internal_vlan)

	fmt.Println("....  Logical Interconnect QOS Configuration.....")
	fi_qos_config, _ := ovc.GetLogicalQosAggregatedConfiguration(id)
	fmt.Println(fi_qos_config)

	fmt.Println("....  Logical Interconnect Unassigned Ports for Port Monitor.....")
	port_monitor_ports := ovc.GetUnassignedPortsForPortMonitor(id)
	fmt.Println(port_monitor_ports)

	fmt.Println("....  Logical Interconnect Unassigned Uplink Ports for Port Monitor.....")
	uplink_port_monitor_ports, _ := ovc.GetUnassignedUplinkPortsForPortMonitor(id)
	fmt.Println(uplink_port_monitor_ports)

	fmt.Println("....  Logical Interconnect Telemetry Configuration.....")
	telemetry_config, _ := ovc.GetTelemetryConfigurations(id, "1")
	fmt.Println(telemetry_config)

	fmt.Println("....  Updating Logical Interconnect Consistent State.....")
	var liUris []utils.Nstring
	liUris = append(liUris, utils.NewNstring("/rest/logical-interconnects/d4468f89-4442-4324-9c01-624c7382db2d"))
	liCompliance := ov.LogicalInterconnectCompliance{Type: "li-compliance", LogicalInterconnectUris: liUris, Description: ""}
	err_compliance := ovc.UpdateLogicalInterconnectConsistentState(liCompliance)
	if err_compliance != nil {
		fmt.Println("Could not update ConsistentState of Logical Interconnect", err_compliance)
	}

	fmt.Println("....  Updating Logical Interconnect EthernetSetting.....")
	liEthernetSettings := ov.EthernetSettings{Type: "EthernetInterconnectSettingsV4", InterconnectType: "Ethernet", URI: utils.NewNstring("/rest/logical-interconnects/d4468f89-4442-4324-9c01-624c7382db2d/ethernetSettings"), ID: "d4468f89-4442-4324-9c01-624c7382db2d"}
	err_ethernet := ovc.UpdateLogicalInterconnectEthernetSettings(liEthernetSettings, id)
	if err_ethernet != nil {
		fmt.Println("Could not update Ethernet Settings of Logical Interconnect", err_ethernet)
	}

	fmt.Println("....  Updating Logical Interconnect Firmware.....")
	liFirmware := ov.Firmware{Command: "Update", EthernetActivationDelay: 5, EthernetActivationType: "Parallel", FcActivationDelay: 5, FcActivationType: "Parallel", Force: false, SppUri: utils.NewNstring("/rest/firmware-drivers/SPP_2018_06_20180709_for_HPE_Synergy_Z7550-96524")}
	err_firmware := ovc.UpdateLogicalInterconnectFirmware(liFirmware, id)
	if err_firmware != nil {
		fmt.Println("Could not update Firmware of Logical Interconnect", err_firmware)
	}

	fmt.Println("....  Updating Logical Interconnect InternalNetworks.....")
	var internalNetworks []utils.Nstring
	internalNetworks = append(internalNetworks, utils.NewNstring("/rest/ethernet-networks/a71b9c9e-b044-48ee-8e4e-26ced1a9a9ef"))
	err_networks := ovc.UpdateLogicalInterconnectInternalNetworks(internalNetworks, id)
	if err_networks != nil {
		fmt.Println("Could not update Internal Networks of Logical Interconnect", err_networks)
	}

	fmt.Println("....  Updating Logical Interconnect QOS Configuration.....")
	liActiveQosConfig := ov.ActiveQosConfig{Type: "QosConfiguration", Category: "qos-aggregated-configuration", ConfigType: "Passthrough"}
	liQosConfig := ov.QosConfiguration{Type: "qos-aggregated-configuration", Category: "qos-aggregated-configuration", ActiveQosConfig: liActiveQosConfig}

	err_qos := ovc.UpdateLogicalInterconnectQosConfigurations(liQosConfig, id)
	if err_qos != nil {
		fmt.Println("Could not update QOS Configuration of Logical Interconnect", err_qos)
	}

	fmt.Println("....  Updating Logical Interconnect SNMP Configuration.....")
	liSNMPConfig := ov.SnmpConfiguration{Type: "snmp-configuration", Category: "snmp-configuration", V3Enabled: true}

	err_snmp := ovc.UpdateLogicalInterconnectSNMPConfigurations(liSNMPConfig, id)
	if err_snmp != nil {
		fmt.Println("Could not update SNMP Configuration of Logical Interconnect", err_snmp)
	}

	fmt.Println("....  Updating Logical Interconnect Configuration.....")
	err_conf := ovc.UpdateLogicalInterconnectConfigurations(id)
	if err_conf != nil {
		fmt.Println("Could not update Configuration of Logical Interconnect", err_conf)
	}

	fmt.Println("....  Updating Logical Interconnect Port Monitor Configuration.....")
	liPMConfig := ov.PortMonitor{Type: "port-monitor", Category: "port-monitor", ETAG: "8a302a85-ec4d-4214-a3e0-10ef71d28769", Name: "name2095641007-1533682087640"}

	err_pm := ovc.UpdateLogicalInterconnectPortMonitor(liPMConfig, id)
	if err_pm != nil {
		fmt.Println("Could not update PortMonitor Configuration of Logical Interconnect", err_pm)
	}

	fmt.Println("....  Updating Logical Interconnect Telemetry  Configuration.....")
	liTMConfig := ov.TelemetryConfiguration{Type: "telemetry-configuration", EnableTelemetry: true, SampleInterval: 300, SampleCount: 12, Name: "name771327580-1533682118441"}

	err_tm := ovc.UpdateLogicalInterconnectTelemetryConfigurations(liTMConfig, id, tcId)
	if err_tm != nil {
		fmt.Println("Could not update PortMonitor Configuration of Logical Interconnect", err_tm)
	}

}
<|endoftext|>