<commit_before>package fblib

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/novatrixtech/go-fbmessenger/fbmodelsend"
)

/*
GetUserData - Get Facebook User's data.
It can be obtained after she starts a conversation with Bot
*/
func GetUserData(senderID string) (*fbmodelsend.User, error) {

	url := fmt.Sprintf("https://graph.facebook.com/v2.8/%s?fields=first_name,last_name,profile_pic,locale,timezone,gender&access_token=%s",
		senderID,
		fbAccessToken)
	fmt.Println("[GetUserData] URL: " + url)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	respBody := string(data)
	fmt.Println("[GetUserData] Response: " + respBody)

	fbUser := new(fbmodelsend.User)

	if err := json.Unmarshal(data, fbUser); err != nil {
		fmt.Println("[GetUserData] Erro no unmarshall " + err.Error())
		return nil, err
	}

	fmt.Println(fbUser.Gender)
	return fbUser, nil
}
<commit_msg>removed unnecessary log<commit_after>package fblib

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/novatrixtech/go-fbmessenger/fbmodelsend"
)

/*
GetUserData - Get Facebook User's data.
It can be obtained after she starts a conversation with Bot
*/
func GetUserData(senderID string) (*fbmodelsend.User, error) {

	url := fmt.Sprintf("https://graph.facebook.com/v2.8/%s?fields=first_name,last_name,profile_pic,locale,timezone,gender&access_token=%s",
		senderID,
		fbAccessToken)
	fmt.Println("[GetUserData] URL: " + url)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	respBody := string(data)
	fmt.Println("[GetUserData] Response: " + respBody)

	fbUser := new(fbmodelsend.User)

	if err := json.Unmarshal(data, fbUser); err != nil {
		fmt.Println("[GetUserData] Erro no unmarshall " + err.Error())
		return nil, err
	}

	return fbUser, nil
}
<|endoftext|>


<commit_before>package pass

import (
  "bytes"
  "compress/flate"
  "compress/gzip"
  "crypto/aes"
  "crypto/cipher"
  "crypto/hmac"
  "crypto/rand"
  "crypto/sha512"
  "encoding/binary"
  "fmt"
  "io/ioutil"

  "code.google.com/p/go.crypto/scrypt"
)

// IMPLEMENTATION NOTES:
// - BUILD LOTS OF TESTS, especially around database loading/saving/durability
// - encrypt PII separately from the main database
// - decrypt once to get all non-PII with encrypted PII, then decrypt each
//   individual PII (passwords, custom fields, etc.) as needed. this should
//   prevent more than one PII from existing in memory at a time.
// - use simpler internal encryption so decryption of in-memory stuff is fast
// - use a "tags" format instead of folders (array of strings).
// - user fields are at same level as internal fields
// - internal fields can't be deleted (for simplicity's sake, allow this?)
// - allow easy integration with autotype-like tools via user scripts (plugins)
// - use a sub-command style command line API
// - allow the user to do _everything_ through the command line
// - include an HTTPS-only interface that uses the same mechanism as command
//   line API. this will decrease the maintenance burden by making the HTTP
//   interface a consumer of said API, and increase dogfooding of the same.
// - include a password generator that's independent of entry creation
// - allow several password customization options (see
//   http://passwordsgenerator.net), arbitrary lengths, and the ability to
//   generate several passwords at once.
// - include history for all modifications

// TODO:
// - store scrypt paramters with the encrypted blob for future-proofing
// - make scrypt use at least 128Mb of memory by default, lots of CPU

// the current version of the encrypted format as a byte array
const Version uint32 = 0

// how large in bytes our version number is, in bytes. a uint32 should ALWAYS be
// 4 bytes, so we just hard-code this here.
const VersionSize = 4

// the size of the signature appended to signed data
const SignatureSize = sha512.Size

// the size of the random salt in bytes we use during password hashing
const SaltSize = 32

// the size of key to use for encryption. using 32 bytes (256 bits) selects
// AES-256 encryption (see: http://golang.org/pkg/crypto/aes/#NewCipher).
const EncryptionKeySize = 32

// we want our HMAC keys to the same size as the blocksize (see:
// http://stackoverflow.com/a/12207647 and
// http://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Definition_.28from_RFC_2104.29).
const HMACKeySize = sha512.BlockSize

// the parameters to use when hashing the master password. we shoot for a memory
// requirement of 128Mb (128 * N * r bytes).
const HashN uint32 = 1 << 16 // 2^16
const HashR uint32 = 16
const HashP uint32 = 2

// how large in bytes each hash parameter is, in bytes
const HashParamSize = 4

// the minimum size of encrypted content, since it must include a version, the
// password salt, the hashing parameters, an initialization vector, and a
// signature - at a minimum!
const minEncryptedLength = (VersionSize + SaltSize + (3 * HashParamSize) +
    aes.BlockSize + SignatureSize)

// compress some data using the GZip algorithm and return it
func compress(data []byte) ([]byte, error) {
  compressed := new(bytes.Buffer)
  writer, err := gzip.NewWriterLevel(compressed, flate.BestCompression)
  if err != nil { return nil, err }

  // compress our data
  writer.Write(data)
  writer.Close()

  return compressed.Bytes(), nil
}

// decompress some data compressed by the GZip algorithm
func decompress(data []byte) ([]byte, error) {
  b := bytes.NewBuffer(data)
  reader, err := gzip.NewReader(b)
  if err != nil { return nil, err }

  // decompress our data
  result, err := ioutil.ReadAll(reader)
  if err != nil { return nil, err }
  reader.Close()

  return result, nil
}

// get the signature of the given data as a byte array using SHA-512. the
// resulting byte array will have a length of SignatureSize.
func sign(data, key []byte) ([]byte, error) {
  // we want the key to be no shorter than the hash algorithm's block size,
  // otherwise it will be zero-padded. longer keys are hashed to obtain a key of
  // the same size as the block size, so there's really no benefit in using a
  // key size that's not equal to the block size of the hash algorithm. it
  // doesn't hurt, however, so we let that case alone.
  if len(key) < HMACKeySize {
    err := fmt.Errorf("Key size is too small (should be %d bytes)",
        HMACKeySize)
    return nil, err
  }

  mac := hmac.New(sha512.New, key)
  mac.Write(data)

  // compute and return the signature
  return mac.Sum(nil), nil
}

// return whether the given signature verifies the given data
func verify(data, suppliedSignature, key []byte) error {
  // make sure the signature is the correct size
  if len(suppliedSignature) != SignatureSize {
    err := fmt.Errorf("Signature must be %d bytes long (got %d)",
        SignatureSize, len(suppliedSignature))
    return err
  }

  // sign the data ourself
  computedSignature, err := sign(data, key)
  if err != nil { return err }

  // signal an error if the computed signature doesn't match the given one.
  // notice that we securely compare the signatures to avoid timing attacks!
  if !hmac.Equal(suppliedSignature, computedSignature) {
    err := fmt.Errorf(
        "Signatures do not match:\n  supplied: %v\n  computed: %v)",
        suppliedSignature, computedSignature)
    return err
  }

  // return no error since the data authenticated correctly
  return nil
}

// encode the given version number as an array of bytes, then return the array
// and whether there was an error.
func uint32ToBytes(version uint32) ([]byte, error) {
  buf := new(bytes.Buffer)
  if err := binary.Write(buf, binary.BigEndian, version); err != nil {
    return nil, err
  }
  return buf.Bytes(), nil
}

// read a version number from an array of bytes and return the version number
// along with an error, if any.
func bytesToUint32(versionBytes []byte) (uint32, error) {
  // make sure we got enough bytes to parse a version out of them
  if len(versionBytes) < VersionSize {
    return 0, fmt.Errorf(
        "Not enough bytes to contain a version (minimum: %d)", VersionSize)
  }

  // read the version from our bytes and return it
  buf := bytes.NewBuffer(versionBytes)
  var version uint32
  if err := binary.Read(buf, binary.BigEndian, &version); err != nil {
    return 0, err
  }

  return version, nil
}

// given a password string and a salt, return two byte arrays. the first should
// be used for encryption, the second for HMAC.
func hashPassword(password string, salt []byte, N, r, p uint32) ([]byte, []byte, error) {
  // ensure that all the encryption paramters meet minimum requirements
  if (N <= 1) {
    return nil, nil, fmt.Errorf("N must be larger than one")
  } else if (r <= 0) {
    return nil, nil, fmt.Errorf("r must be larger than zero")
  } else if (p <= 0) {
    return nil, nil, fmt.Errorf("p must be larger than zero")
  }

  // NOTE: scrypt memory usage is approximately 128 * `N` * `r` bytes. since `p`
  // has little effect on memory usage, it can be used to tune the running time
  // of the algorithm.

  // generate enough bytes for both the encryption and HMAC keys. additionally,
  // since scrypt is checking the sizes of the paramter values for us, we don't
  // need to do it ourselves (see:
  // http://code.google.com/p/go/source/browse/scrypt/scrypt.go?repo=crypto).
  hash, err := scrypt.Key([]byte(password), salt, int(N), int(r), int(p),
      EncryptionKeySize + HMACKeySize)
  if err != nil { return nil, nil, err }

  // return the keys according to our convention (encryption, then hmac)
  encryptionKey := hash[:EncryptionKeySize]
  hmacKey := hash[EncryptionKeySize:]
  return encryptionKey, hmacKey, nil
}

// encrypt some data using the given password and default scrypt params, then
// return the result.
func Encrypt(plaintext []byte, password string) ([]byte, error) {
  // use the default params to encrypt this text
  return EncryptWithHashParams(plaintext, password, HashN, HashR, HashP)
}

// encrypt some data using the given password and scrypt params, then return the
// result.
func EncryptWithHashParams(plaintext []byte, password string, N, r, p uint32) ([]byte, error) {
  // NOTE: no plaintext padding is needed since we're using CFB mode (see:
  // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Padding).

  // first, compress the plaintext to obfuscate its contents and reduce its size
  compressedPlaintext, err := compress(plaintext);
  if err != nil { return nil, err }

  // make a blob that conforms to our defined structure
  blob := NewBlob(
    "version", VersionSize,
    "N", HashParamSize,
    "r", HashParamSize,
    "p", HashParamSize,
    "salt", SaltSize,
    "iv", aes.BlockSize,
    "data", len(compressedPlaintext),
    "signature", SignatureSize,
  )

  // get the slices we'll be working with
  version := blob.Get("version")
  salt := blob.Get("salt")
  BlobN := blob.Get("N")
  BlobR := blob.Get("r")
  BlobP := blob.Get("p")
  iv := blob.Get("iv")
  ciphertext := blob.Get("data")
  signature := blob.Get("signature")

  // serialize and store the current version
  versionBytes, err := uint32ToBytes(Version)
  if err != nil { return nil, err }
  copy(version, versionBytes)

  // randomize the salt and the initialization vector
  if _, err := rand.Read(salt); err != nil { return nil, err }
  if _, err := rand.Read(iv); err != nil { return nil, err }

  // serialize and store the hash paramters
  nBytes, err := uint32ToBytes(N)
  if err != nil { return nil, err }
  copy(BlobN, nBytes)

  rBytes, err := uint32ToBytes(r)
  if err != nil { return nil, err }
  copy(BlobR, rBytes)

  pBytes, err := uint32ToBytes(p)
  if err != nil { return nil, err }
  copy(BlobP, pBytes)

  // hash the password into the necessary keys using the salt
  encryptionKey, hmacKey, err := hashPassword(password, salt, N, r, p)
  if err != nil { return nil, err }

  // encrypt the compressed plaintext
  block, err := aes.NewCipher(encryptionKey)
  if err != nil { return nil, err }

  // use CFB mode to encrypt the data, so we don't have to pad
  stream := cipher.NewCFBEncrypter(block, iv)
  stream.XORKeyStream(ciphertext, compressedPlaintext)

  // sign our data (everything _but_ the signature space)
  content := blob.To("data")
  signatureData, err := sign(content, hmacKey)
  if err != nil { return nil, err }

  // store the signature
  copy(signature, signatureData)

  return blob.Bytes(), nil
}

// decrypt some data using the given password
func Decrypt(data []byte, password string) ([]byte, error) {
  // make sure our data is of at least the minimum length
  if len(data) < minEncryptedLength {
    err := fmt.Errorf("Data is too short to be valid (min length: %d)",
        minEncryptedLength)
    return nil, err
  }

  // make a blob that conforms to our defined structure
  blob := NewBlob(
    "version", VersionSize,
    "N", HashParamSize,
    "r", HashParamSize,
    "p", HashParamSize,
    "salt", SaltSize,
    "iv", aes.BlockSize,

    // the ciphertext is everything in the blob _except_ the other fields
    "data", len(data) - (
      VersionSize +
      SaltSize +
      (3 * HashParamSize) +
      aes.BlockSize +
      SignatureSize),

    "signature", SignatureSize,

    // initalize the blob with the encrypted data
    data,
  )

  // make sure we can decrypt this version
  version, err := bytesToUint32(blob.Get("version"))
  if err != nil { return nil, err }

  // we'll never be able to handle newer versions!
  if version > Version {
    return nil, fmt.Errorf("Latest supported version is %d (got: %d)",
        Version, version)
  }

  // decrypt using a version of the algorithm that matches the given blob
  if version < Version {
    // TODO: add support for older versions once they exist
    panic("No older versions shoud exist at this time!")
  }

  // read the the parts we need from the unverified data
  salt := blob.Get("salt")
  iv := blob.Get("iv")
  ciphertext := blob.Get("data")
  signature := blob.Get("signature")

  // read the hash paramters we need to hash the password
  N, err := bytesToUint32(blob.Get("N"))
  if err != nil { return nil, err }
  r, err := bytesToUint32(blob.Get("r"))
  if err != nil { return nil, err }
  p, err := bytesToUint32(blob.Get("p"))
  if err != nil { return nil, err }

  // hash the password with the supplied salt and paramters to get the keys
  encryptionKey, hmacKey, err := hashPassword(password, salt, N, r, p)
  if err != nil { return nil, err }

  // verify the integrity of the blob (including the version)
  err = verify(blob.To("data"), signature, hmacKey)
  if err != nil { return nil, err }

  // decrypt the ciphertext
  block, err := aes.NewCipher(encryptionKey)
  if err != nil { return nil, err }

  // decrypt directly into the original slice to save creating a new array
  compressedPlaintext := ciphertext[:]
  stream := cipher.NewCFBDecrypter(block, iv)
  stream.XORKeyStream(compressedPlaintext, ciphertext)

  // decompress the compressed plaintext
  plaintext, err := decompress(compressedPlaintext)
  if err != nil { return nil, err }

  return plaintext, nil
}
<commit_msg>Rename uppercased local variables<commit_after>package pass

import (
  "bytes"
  "compress/flate"
  "compress/gzip"
  "crypto/aes"
  "crypto/cipher"
  "crypto/hmac"
  "crypto/rand"
  "crypto/sha512"
  "encoding/binary"
  "fmt"
  "io/ioutil"

  "code.google.com/p/go.crypto/scrypt"
)

// IMPLEMENTATION NOTES:
// - BUILD LOTS OF TESTS, especially around database loading/saving/durability
// - encrypt PII separately from the main database
// - decrypt once to get all non-PII with encrypted PII, then decrypt each
//   individual PII (passwords, custom fields, etc.) as needed. this should
//   prevent more than one PII from existing in memory at a time.
// - use simpler internal encryption so decryption of in-memory stuff is fast
// - use a "tags" format instead of folders (array of strings).
// - user fields are at same level as internal fields
// - internal fields can't be deleted (for simplicity's sake, allow this?)
// - allow easy integration with autotype-like tools via user scripts (plugins)
// - use a sub-command style command line API
// - allow the user to do _everything_ through the command line
// - include an HTTPS-only interface that uses the same mechanism as command
//   line API. this will decrease the maintenance burden by making the HTTP
//   interface a consumer of said API, and increase dogfooding of the same.
// - include a password generator that's independent of entry creation
// - allow several password customization options (see
//   http://passwordsgenerator.net), arbitrary lengths, and the ability to
//   generate several passwords at once.
// - include history for all modifications

// TODO:
// - store scrypt paramters with the encrypted blob for future-proofing
// - make scrypt use at least 128Mb of memory by default, lots of CPU

// the current version of the encrypted format as a byte array
const Version uint32 = 0

// how large in bytes our version number is, in bytes. a uint32 should ALWAYS be
// 4 bytes, so we just hard-code this here.
const VersionSize = 4

// the size of the signature appended to signed data
const SignatureSize = sha512.Size

// the size of the random salt in bytes we use during password hashing
const SaltSize = 32

// the size of key to use for encryption. using 32 bytes (256 bits) selects
// AES-256 encryption (see: http://golang.org/pkg/crypto/aes/#NewCipher).
const EncryptionKeySize = 32

// we want our HMAC keys to the same size as the blocksize (see:
// http://stackoverflow.com/a/12207647 and
// http://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Definition_.28from_RFC_2104.29).
const HMACKeySize = sha512.BlockSize

// the parameters to use when hashing the master password. we shoot for a memory
// requirement of 128Mb (128 * N * r bytes).
const HashN uint32 = 1 << 16 // 2^16
const HashR uint32 = 16
const HashP uint32 = 2

// how large in bytes each hash parameter is, in bytes
const HashParamSize = 4

// the minimum size of encrypted content, since it must include a version, the
// password salt, the hashing parameters, an initialization vector, and a
// signature - at a minimum!
const minEncryptedLength = (VersionSize + SaltSize + (3 * HashParamSize) +
    aes.BlockSize + SignatureSize)

// compress some data using the GZip algorithm and return it
func compress(data []byte) ([]byte, error) {
  compressed := new(bytes.Buffer)
  writer, err := gzip.NewWriterLevel(compressed, flate.BestCompression)
  if err != nil { return nil, err }

  // compress our data
  writer.Write(data)
  writer.Close()

  return compressed.Bytes(), nil
}

// decompress some data compressed by the GZip algorithm
func decompress(data []byte) ([]byte, error) {
  b := bytes.NewBuffer(data)
  reader, err := gzip.NewReader(b)
  if err != nil { return nil, err }

  // decompress our data
  result, err := ioutil.ReadAll(reader)
  if err != nil { return nil, err }
  reader.Close()

  return result, nil
}

// get the signature of the given data as a byte array using SHA-512. the
// resulting byte array will have a length of SignatureSize.
func sign(data, key []byte) ([]byte, error) {
  // we want the key to be no shorter than the hash algorithm's block size,
  // otherwise it will be zero-padded. longer keys are hashed to obtain a key of
  // the same size as the block size, so there's really no benefit in using a
  // key size that's not equal to the block size of the hash algorithm. it
  // doesn't hurt, however, so we let that case alone.
  if len(key) < HMACKeySize {
    err := fmt.Errorf("Key size is too small (should be %d bytes)",
        HMACKeySize)
    return nil, err
  }

  mac := hmac.New(sha512.New, key)
  mac.Write(data)

  // compute and return the signature
  return mac.Sum(nil), nil
}

// return whether the given signature verifies the given data
func verify(data, suppliedSignature, key []byte) error {
  // make sure the signature is the correct size
  if len(suppliedSignature) != SignatureSize {
    err := fmt.Errorf("Signature must be %d bytes long (got %d)",
        SignatureSize, len(suppliedSignature))
    return err
  }

  // sign the data ourself
  computedSignature, err := sign(data, key)
  if err != nil { return err }

  // signal an error if the computed signature doesn't match the given one.
  // notice that we securely compare the signatures to avoid timing attacks!
  if !hmac.Equal(suppliedSignature, computedSignature) {
    err := fmt.Errorf(
        "Signatures do not match:\n  supplied: %v\n  computed: %v)",
        suppliedSignature, computedSignature)
    return err
  }

  // return no error since the data authenticated correctly
  return nil
}

// encode the given version number as an array of bytes, then return the array
// and whether there was an error.
func uint32ToBytes(version uint32) ([]byte, error) {
  buf := new(bytes.Buffer)
  if err := binary.Write(buf, binary.BigEndian, version); err != nil {
    return nil, err
  }
  return buf.Bytes(), nil
}

// read a version number from an array of bytes and return the version number
// along with an error, if any.
func bytesToUint32(versionBytes []byte) (uint32, error) {
  // make sure we got enough bytes to parse a version out of them
  if len(versionBytes) < VersionSize {
    return 0, fmt.Errorf(
        "Not enough bytes to contain a version (minimum: %d)", VersionSize)
  }

  // read the version from our bytes and return it
  buf := bytes.NewBuffer(versionBytes)
  var version uint32
  if err := binary.Read(buf, binary.BigEndian, &version); err != nil {
    return 0, err
  }

  return version, nil
}

// given a password string and a salt, return two byte arrays. the first should
// be used for encryption, the second for HMAC.
func hashPassword(password string, salt []byte, N, r, p uint32) ([]byte, []byte, error) {
  // ensure that all the encryption paramters meet minimum requirements
  if (N <= 1) {
    return nil, nil, fmt.Errorf("N must be larger than one")
  } else if (r <= 0) {
    return nil, nil, fmt.Errorf("r must be larger than zero")
  } else if (p <= 0) {
    return nil, nil, fmt.Errorf("p must be larger than zero")
  }

  // NOTE: scrypt memory usage is approximately 128 * `N` * `r` bytes. since `p`
  // has little effect on memory usage, it can be used to tune the running time
  // of the algorithm.

  // generate enough bytes for both the encryption and HMAC keys. additionally,
  // since scrypt is checking the sizes of the paramter values for us, we don't
  // need to do it ourselves (see:
  // http://code.google.com/p/go/source/browse/scrypt/scrypt.go?repo=crypto).
  hash, err := scrypt.Key([]byte(password), salt, int(N), int(r), int(p),
      EncryptionKeySize + HMACKeySize)
  if err != nil { return nil, nil, err }

  // return the keys according to our convention (encryption, then hmac)
  encryptionKey := hash[:EncryptionKeySize]
  hmacKey := hash[EncryptionKeySize:]
  return encryptionKey, hmacKey, nil
}

// encrypt some data using the given password and default scrypt params, then
// return the result.
func Encrypt(plaintext []byte, password string) ([]byte, error) {
  // use the default params to encrypt this text
  return EncryptWithHashParams(plaintext, password, HashN, HashR, HashP)
}

// encrypt some data using the given password and scrypt params, then return the
// result.
func EncryptWithHashParams(plaintext []byte, password string, N, r, p uint32) ([]byte, error) {
  // NOTE: no plaintext padding is needed since we're using CFB mode (see:
  // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Padding).

  // first, compress the plaintext to obfuscate its contents and reduce its size
  compressedPlaintext, err := compress(plaintext);
  if err != nil { return nil, err }

  // make a blob that conforms to our defined structure
  blob := NewBlob(
    "version", VersionSize,
    "N", HashParamSize,
    "r", HashParamSize,
    "p", HashParamSize,
    "salt", SaltSize,
    "iv", aes.BlockSize,
    "data", len(compressedPlaintext),
    "signature", SignatureSize,
  )

  // get the slices we'll be working with
  version := blob.Get("version")
  salt := blob.Get("salt")
  blobN := blob.Get("N")
  blobR := blob.Get("r")
  blobP := blob.Get("p")
  iv := blob.Get("iv")
  ciphertext := blob.Get("data")
  signature := blob.Get("signature")

  // serialize and store the current version
  versionBytes, err := uint32ToBytes(Version)
  if err != nil { return nil, err }
  copy(version, versionBytes)

  // randomize the salt and the initialization vector
  if _, err := rand.Read(salt); err != nil { return nil, err }
  if _, err := rand.Read(iv); err != nil { return nil, err }

  // serialize and store the hash paramters
  nBytes, err := uint32ToBytes(N)
  if err != nil { return nil, err }
  copy(blobN, nBytes)

  rBytes, err := uint32ToBytes(r)
  if err != nil { return nil, err }
  copy(blobR, rBytes)

  pBytes, err := uint32ToBytes(p)
  if err != nil { return nil, err }
  copy(blobP, pBytes)

  // hash the password into the necessary keys using the salt
  encryptionKey, hmacKey, err := hashPassword(password, salt, N, r, p)
  if err != nil { return nil, err }

  // encrypt the compressed plaintext
  block, err := aes.NewCipher(encryptionKey)
  if err != nil { return nil, err }

  // use CFB mode to encrypt the data, so we don't have to pad
  stream := cipher.NewCFBEncrypter(block, iv)
  stream.XORKeyStream(ciphertext, compressedPlaintext)

  // sign our data (everything _but_ the signature space)
  content := blob.To("data")
  signatureData, err := sign(content, hmacKey)
  if err != nil { return nil, err }

  // store the signature
  copy(signature, signatureData)

  return blob.Bytes(), nil
}

// decrypt some data using the given password
func Decrypt(data []byte, password string) ([]byte, error) {
  // make sure our data is of at least the minimum length
  if len(data) < minEncryptedLength {
    err := fmt.Errorf("Data is too short to be valid (min length: %d)",
        minEncryptedLength)
    return nil, err
  }

  // make a blob that conforms to our defined structure
  blob := NewBlob(
    "version", VersionSize,
    "N", HashParamSize,
    "r", HashParamSize,
    "p", HashParamSize,
    "salt", SaltSize,
    "iv", aes.BlockSize,

    // the ciphertext is everything in the blob _except_ the other fields
    "data", len(data) - (
      VersionSize +
      SaltSize +
      (3 * HashParamSize) +
      aes.BlockSize +
      SignatureSize),

    "signature", SignatureSize,

    // initalize the blob with the encrypted data
    data,
  )

  // make sure we can decrypt this version
  version, err := bytesToUint32(blob.Get("version"))
  if err != nil { return nil, err }

  // we'll never be able to handle newer versions!
  if version > Version {
    return nil, fmt.Errorf("Latest supported version is %d (got: %d)",
        Version, version)
  }

  // decrypt using a version of the algorithm that matches the given blob
  if version < Version {
    // TODO: add support for older versions once they exist
    panic("No older versions shoud exist at this time!")
  }

  // read the the parts we need from the unverified data
  salt := blob.Get("salt")
  iv := blob.Get("iv")
  ciphertext := blob.Get("data")
  signature := blob.Get("signature")

  // read the hash paramters we need to hash the password
  N, err := bytesToUint32(blob.Get("N"))
  if err != nil { return nil, err }
  r, err := bytesToUint32(blob.Get("r"))
  if err != nil { return nil, err }
  p, err := bytesToUint32(blob.Get("p"))
  if err != nil { return nil, err }

  // hash the password with the supplied salt and paramters to get the keys
  encryptionKey, hmacKey, err := hashPassword(password, salt, N, r, p)
  if err != nil { return nil, err }

  // verify the integrity of the blob (including the version)
  err = verify(blob.To("data"), signature, hmacKey)
  if err != nil { return nil, err }

  // decrypt the ciphertext
  block, err := aes.NewCipher(encryptionKey)
  if err != nil { return nil, err }

  // decrypt directly into the original slice to save creating a new array
  compressedPlaintext := ciphertext[:]
  stream := cipher.NewCFBDecrypter(block, iv)
  stream.XORKeyStream(compressedPlaintext, ciphertext)

  // decompress the compressed plaintext
  plaintext, err := decompress(compressedPlaintext)
  if err != nil { return nil, err }

  return plaintext, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/droundy/goopt"
	"github.com/tesujimath/rktrunner"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
)

type optionsT struct {
	exec          *string
	interactive   *bool
	verbose       *bool
	noImagePrefix *bool
}

type runT struct {
	options optionsT
	image   string
	cmd     string
	cmdArgs []string
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "rkt-run: %s\n", fmt.Sprintf(format, args))
	os.Exit(1)
}

func autoPrefix(image string, c *rktrunner.ConfigT) string {
	for key, val := range c.AutoImagePrefix {
		if strings.HasPrefix(image, key) {
			return strings.Replace(image, key, val, 1)
		}
	}
	return image
}

func parseArgs(c *rktrunner.ConfigT) (r runT, err error) {
	r.options.exec = goopt.String([]string{"-e", "--exec"}, "", "command to run instead of image default")
	r.options.interactive = goopt.Flag([]string{"-i", "--interactive"}, []string{}, "run image interactively", "")
	r.options.verbose = goopt.Flag([]string{"-v", "--verbose"}, []string{}, "show full rkt run command", "")
	r.options.noImagePrefix = goopt.Flag([]string{"-n", "--no-image-prefix"}, []string{}, "disable auto image prefix", "")
	goopt.RequireOrder = true
	goopt.Author = "Simon Guest <simon.guest@tesujimath.org>"
	goopt.Description = func() string {
		return `Run rkt containers with user mapping, and volume mounting
as defined by the system administrator.

$ rkt-run <options> <image> [<command> [<command-args>]]
`
	}
	goopt.Summary = "Enable unprivileged users to run containers using rkt"
	goopt.Suite = "rktrunner"
	goopt.Parse(nil)
	args := goopt.Args

	// image
	if len(args) == 0 || args[0] == "" {
		err = fmt.Errorf("missing image")
		return
	}
	if args[0][0] == '-' {
		err = fmt.Errorf("image cannot start with -")
		return
	}
	if !*r.options.noImagePrefix {
		r.image = autoPrefix(args[0], c)
	} else {
		r.image = args[0]
	}

	// command
	if *r.options.exec != "" {
		if (*r.options.exec)[0] == '-' {
			err = fmt.Errorf("command cannot start with -")
			return
		}
		r.cmd = *r.options.exec
	} else if *r.options.interactive && c.DefaultInteractiveCmd != "" {
		r.cmd = c.DefaultInteractiveCmd
	}

	// args, check for ---
	for _, arg := range args[1:] {
		if arg == "---" {
			err = fmt.Errorf("%s invalid", arg)
			return
		}
	}
	if len(args) > 1 {
		r.cmdArgs = args[1:]
	}

	return
}

func formatVolumes(f *rktrunner.FragmentsT) []string {
	var s []string
	for key, vol := range f.Volume {
		if vol.Volume != "" {
			s = append(s, "--volume", fmt.Sprintf("%s,%s", key, vol.Volume))
		}
	}
	return s
}

func formatMounts(f *rktrunner.FragmentsT) []string {
	var s []string
	for key, vol := range f.Volume {
		if vol.Mount != "" {
			s = append(s, "--mount", fmt.Sprintf("volume=%s,%s", key, vol.Mount))
		}
	}
	return s
}

func printEnviron(environ map[string]string) {
	// get keys in order
	var keys []string
	for key := range environ {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		fmt.Printf("%s=%s ", key, environ[key])
	}
}

// augmentEnviron overrides and/or augments the base environment with the extra
func augmentEnviron(base []string, extra map[string]string) []string {
	environ := make(map[string]string)
	for _, keyval := range base {
		i := strings.IndexRune(keyval, '=')
		if i != -1 {
			key := keyval[:i]
			val := keyval[i+1:]
			environ[key] = val
		}
	}
	for key, val := range extra {
		environ[key] = val
	}
	var result []string
	for key, val := range environ {
		result = append(result, fmt.Sprintf("%s=%s", key, val))
	}
	return result
}

func execute(c *rktrunner.ConfigT, f *rktrunner.FragmentsT, r *runT) error {
	//rkt --insecure-options=image run --set-env=HOME=/home/guestsi --volume home,kind=host,source=/home/guestsi --volume data,kind=host,source=/data docker://quay.io/biocontainers/blast:2.6.0--boost1.61_0 --mount volume=home,target=/home/guestsi --mount volume=data,target=/hostdata --user=511 --group=511 --exec ~/scripts/myblast -- /hostdata/myfile
	args := make([]string, 1)
	args[0] = filepath.Base(c.Rkt)
	args = append(args, f.Options[rktrunner.GeneralOptions]...)
	args = append(args, "run")
	if *r.options.interactive {
		args = append(args, "--interactive")
	}
	args = append(args, f.Options[rktrunner.RunOptions]...)
	args = append(args, formatVolumes(f)...)
	args = append(args, r.image)
	args = append(args, formatMounts(f)...)
	args = append(args, f.Options[rktrunner.ImageOptions]...)
	if r.cmd != "" {
		args = append(args, "--exec", r.cmd)
	}
	if len(r.cmdArgs) > 0 {
		args = append(args, "--")
		args = append(args, r.cmdArgs...)
	}
	if *r.options.verbose {
		printEnviron(f.Environment)
		fmt.Printf("%s %s\n", c.Rkt, strings.Join(args[1:], " "))
	}

	environ := augmentEnviron(os.Environ(), f.Environment)
	return syscall.Exec(c.Rkt, args, environ)
}

func main() {
	c, err := rktrunner.GetConfig()
	if err != nil {
		die("configuration error: %v", err)
	}

	r, err := parseArgs(c)
	if err != nil {
		die("bad usage: %v", err)
	}

	u, err := user.Current()
	if err != nil {
		die("failed to get current user: %v", err)
	}

	f, err := rktrunner.GetFragments(c, u)
	if err != nil {
		die("configuration error: %v", err)
	}

	// set real uid same as effective
	err = syscall.Setreuid(syscall.Geteuid(), syscall.Geteuid())
	if err != nil {
		die("failed to set real uid: %v", err)
	}

	err = execute(c, f, &r)
	if err != nil {
		die("failed: %v", err)
	}
}
<commit_msg>Fixed bug in usage description.<commit_after>package main

import (
	"fmt"
	"github.com/droundy/goopt"
	"github.com/tesujimath/rktrunner"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
)

type optionsT struct {
	exec          *string
	interactive   *bool
	verbose       *bool
	noImagePrefix *bool
}

type runT struct {
	options optionsT
	image   string
	cmd     string
	cmdArgs []string
}

func die(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "rkt-run: %s\n", fmt.Sprintf(format, args))
	os.Exit(1)
}

func autoPrefix(image string, c *rktrunner.ConfigT) string {
	for key, val := range c.AutoImagePrefix {
		if strings.HasPrefix(image, key) {
			return strings.Replace(image, key, val, 1)
		}
	}
	return image
}

func parseArgs(c *rktrunner.ConfigT) (r runT, err error) {
	r.options.exec = goopt.String([]string{"-e", "--exec"}, "", "command to run instead of image default")
	r.options.interactive = goopt.Flag([]string{"-i", "--interactive"}, []string{}, "run image interactively", "")
	r.options.verbose = goopt.Flag([]string{"-v", "--verbose"}, []string{}, "show full rkt run command", "")
	r.options.noImagePrefix = goopt.Flag([]string{"-n", "--no-image-prefix"}, []string{}, "disable auto image prefix", "")
	goopt.RequireOrder = true
	goopt.Author = "Simon Guest <simon.guest@tesujimath.org>"
	goopt.Description = func() string {
		return `Run rkt containers with user mapping, and volume mounting
as defined by the system administrator.

$ rkt-run <options> <image> [<args>]
`
	}
	goopt.Summary = "Enable unprivileged users to run containers using rkt"
	goopt.Suite = "rktrunner"
	goopt.Parse(nil)
	args := goopt.Args

	// image
	if len(args) == 0 || args[0] == "" {
		err = fmt.Errorf("missing image")
		return
	}
	if args[0][0] == '-' {
		err = fmt.Errorf("image cannot start with -")
		return
	}
	if !*r.options.noImagePrefix {
		r.image = autoPrefix(args[0], c)
	} else {
		r.image = args[0]
	}

	// command
	if *r.options.exec != "" {
		if (*r.options.exec)[0] == '-' {
			err = fmt.Errorf("command cannot start with -")
			return
		}
		r.cmd = *r.options.exec
	} else if *r.options.interactive && c.DefaultInteractiveCmd != "" {
		r.cmd = c.DefaultInteractiveCmd
	}

	// args, check for ---
	for _, arg := range args[1:] {
		if arg == "---" {
			err = fmt.Errorf("%s invalid", arg)
			return
		}
	}
	if len(args) > 1 {
		r.cmdArgs = args[1:]
	}

	return
}

func formatVolumes(f *rktrunner.FragmentsT) []string {
	var s []string
	for key, vol := range f.Volume {
		if vol.Volume != "" {
			s = append(s, "--volume", fmt.Sprintf("%s,%s", key, vol.Volume))
		}
	}
	return s
}

func formatMounts(f *rktrunner.FragmentsT) []string {
	var s []string
	for key, vol := range f.Volume {
		if vol.Mount != "" {
			s = append(s, "--mount", fmt.Sprintf("volume=%s,%s", key, vol.Mount))
		}
	}
	return s
}

func printEnviron(environ map[string]string) {
	// get keys in order
	var keys []string
	for key := range environ {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		fmt.Printf("%s=%s ", key, environ[key])
	}
}

// augmentEnviron overrides and/or augments the base environment with the extra
func augmentEnviron(base []string, extra map[string]string) []string {
	environ := make(map[string]string)
	for _, keyval := range base {
		i := strings.IndexRune(keyval, '=')
		if i != -1 {
			key := keyval[:i]
			val := keyval[i+1:]
			environ[key] = val
		}
	}
	for key, val := range extra {
		environ[key] = val
	}
	var result []string
	for key, val := range environ {
		result = append(result, fmt.Sprintf("%s=%s", key, val))
	}
	return result
}

func execute(c *rktrunner.ConfigT, f *rktrunner.FragmentsT, r *runT) error {
	//rkt --insecure-options=image run --set-env=HOME=/home/guestsi --volume home,kind=host,source=/home/guestsi --volume data,kind=host,source=/data docker://quay.io/biocontainers/blast:2.6.0--boost1.61_0 --mount volume=home,target=/home/guestsi --mount volume=data,target=/hostdata --user=511 --group=511 --exec ~/scripts/myblast -- /hostdata/myfile
	args := make([]string, 1)
	args[0] = filepath.Base(c.Rkt)
	args = append(args, f.Options[rktrunner.GeneralOptions]...)
	args = append(args, "run")
	if *r.options.interactive {
		args = append(args, "--interactive")
	}
	args = append(args, f.Options[rktrunner.RunOptions]...)
	args = append(args, formatVolumes(f)...)
	args = append(args, r.image)
	args = append(args, formatMounts(f)...)
	args = append(args, f.Options[rktrunner.ImageOptions]...)
	if r.cmd != "" {
		args = append(args, "--exec", r.cmd)
	}
	if len(r.cmdArgs) > 0 {
		args = append(args, "--")
		args = append(args, r.cmdArgs...)
	}
	if *r.options.verbose {
		printEnviron(f.Environment)
		fmt.Printf("%s %s\n", c.Rkt, strings.Join(args[1:], " "))
	}

	environ := augmentEnviron(os.Environ(), f.Environment)
	return syscall.Exec(c.Rkt, args, environ)
}

func main() {
	c, err := rktrunner.GetConfig()
	if err != nil {
		die("configuration error: %v", err)
	}

	r, err := parseArgs(c)
	if err != nil {
		die("bad usage: %v", err)
	}

	u, err := user.Current()
	if err != nil {
		die("failed to get current user: %v", err)
	}

	f, err := rktrunner.GetFragments(c, u)
	if err != nil {
		die("configuration error: %v", err)
	}

	// set real uid same as effective
	err = syscall.Setreuid(syscall.Geteuid(), syscall.Geteuid())
	if err != nil {
		die("failed to set real uid: %v", err)
	}

	err = execute(c, f, &r)
	if err != nil {
		die("failed: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/dmathieu/sabayon/certs"
	"github.com/dmathieu/sabayon/heroku"
	"github.com/joho/godotenv"
)

func main() {
	godotenv.Load()

	var force bool
	flag.BoolVar(&force, "force", false, "Force updating the certificate even if it's not about to expire")
	flag.Parse()

	var domains = strings.Split(os.Getenv("ACME_DOMAIN"), ",")
	// trim potential whitespace
	for i, domain := range domains {
		domains[i] = strings.TrimSpace(domain)
	}
	var email = os.Getenv("ACME_EMAIL")
	var token = os.Getenv("HEROKU_TOKEN")
	var appName = os.Getenv("ACME_APP_NAME")
	wait, _ := strconv.Atoi(os.Getenv("RESTART_WAIT_TIME"))
	if wait == 0 {
		wait = 20
	}

	herokuClient := heroku.NewClient(nil, token)
	certificates, err := herokuClient.GetSSLCertificates(appName)
	if err != nil {
		log.Fatal(err)
	}

	if len(certificates) > 1 {
		log.Fatalf("Found %d certificate. Can only update one. Nothing done.", len(certificates))
	}

	if len(certificates) != 0 && !force {
		certExpiration, err := time.Parse(time.RFC3339, certificates[0].SslCert.ExpiresAt)
		if err != nil {
			log.Fatal(err)
		}
		now := time.Now()
		m := now.AddDate(0, +1, 0)

		if certExpiration.After(m) {
			log.Printf("cert.ignore_update expires_at=\"%s\" renew_at=\"%s\"", certExpiration, m)
			return
		}
	}

	log.Printf("cert.create email='%s' domains='%s'", email, domains)

	ce := certs.NewCert(email, domains)
	go ce.Create()

	for {
		select {
		case r := <-ce.ErrChan:
			log.Printf("%s", r)
			return
		case r := <-ce.ChallengeChan:
			log.Printf("cert.validate")
			var index int
			for i, v := range domains {
				if v == r.Domain {
					index = i + 1
				}
			}

			err := herokuClient.SetConfigVars(appName, index, r.KeyAuth, r.Token)
			if err != nil {
				log.Fatal(err)
			}

			// Wait for a few seconds so the app can restart
			time.Sleep(time.Duration(wait) * time.Second)

			ce.ComChan <- "validate"
		case r := <-ce.CertChan:
			log.Printf("cert.created")

			if len(certificates) == 0 {
				err = herokuClient.SetSSLCertificate(appName, r.Certificate, r.PrivateKey)
				if err != nil {
					log.Fatal(err)
				}

				log.Printf("cert.added")
			} else {
				err = herokuClient.UpdateSSLCertificate(appName, certificates[0].Name, r.Certificate, r.PrivateKey)
				if err != nil {
					log.Fatal(err)
				}

				log.Printf("cert.updated")
			}

			return
		}
	}
}
<commit_msg>Log real renew date on each run<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/dmathieu/sabayon/certs"
	"github.com/dmathieu/sabayon/heroku"
	"github.com/joho/godotenv"
)

func main() {
	godotenv.Load()

	var force bool
	flag.BoolVar(&force, "force", false, "Force updating the certificate even if it's not about to expire")
	flag.Parse()

	var domains = strings.Split(os.Getenv("ACME_DOMAIN"), ",")
	// trim potential whitespace
	for i, domain := range domains {
		domains[i] = strings.TrimSpace(domain)
	}
	var email = os.Getenv("ACME_EMAIL")
	var token = os.Getenv("HEROKU_TOKEN")
	var appName = os.Getenv("ACME_APP_NAME")
	wait, _ := strconv.Atoi(os.Getenv("RESTART_WAIT_TIME"))
	if wait == 0 {
		wait = 20
	}

	herokuClient := heroku.NewClient(nil, token)
	certificates, err := herokuClient.GetSSLCertificates(appName)
	if err != nil {
		log.Fatal(err)
	}

	if len(certificates) > 1 {
		log.Fatalf("Found %d certificate. Can only update one. Nothing done.", len(certificates))
	}

	if len(certificates) != 0 && !force {
		certExpiration, err := time.Parse(time.RFC3339, certificates[0].SslCert.ExpiresAt)
		if err != nil {
			log.Fatal(err)
		}
		now := time.Now()
		renew := certExpiration.AddDate(0, -1, 0)

		if now.Before(renew) {
			log.Printf("cert.ignore_update expires_at=\"%s\" renew_at=\"%s\"", certExpiration, renew)
			return
		}
	}

	log.Printf("cert.create email='%s' domains='%s'", email, domains)

	ce := certs.NewCert(email, domains)
	go ce.Create()

	for {
		select {
		case r := <-ce.ErrChan:
			log.Printf("%s", r)
			return
		case r := <-ce.ChallengeChan:
			log.Printf("cert.validate")
			var index int
			for i, v := range domains {
				if v == r.Domain {
					index = i + 1
				}
			}

			err := herokuClient.SetConfigVars(appName, index, r.KeyAuth, r.Token)
			if err != nil {
				log.Fatal(err)
			}

			// Wait for a few seconds so the app can restart
			time.Sleep(time.Duration(wait) * time.Second)

			ce.ComChan <- "validate"
		case r := <-ce.CertChan:
			log.Printf("cert.created")

			if len(certificates) == 0 {
				err = herokuClient.SetSSLCertificate(appName, r.Certificate, r.PrivateKey)
				if err != nil {
					log.Fatal(err)
				}

				log.Printf("cert.added")
			} else {
				err = herokuClient.UpdateSSLCertificate(appName, certificates[0].Name, r.Certificate, r.PrivateKey)
				if err != nil {
					log.Fatal(err)
				}

				log.Printf("cert.updated")
			}

			return
		}
	}
}
<|endoftext|>


<commit_before>// Command testbot provides a web server for running Chain integeration tests.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/kr/s3"

	"chain/env"
)

var (
	listen      = env.String("LISTEN", ":8080")
	githubToken = os.Getenv("GITHUB_TOKEN")
	slackURL    = os.Getenv("SLACK_WEBHOOK_URL")
	sourcedir   = os.Getenv("CHAIN")
	keys        = s3.Keys{
		AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
		SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
	}
	mu sync.Mutex
)

type pullRequest struct {
	Action string
	PR     struct {
		Head struct {
			Ref string
			Sha string
		}
	} `json:"pull_request"`
}

func main() {
	log.SetFlags(log.Lshortfile)
	http.HandleFunc("/pr", prHandler)
	http.HandleFunc("/commit", commitHandler)
	http.HandleFunc("/health", func(http.ResponseWriter, *http.Request) {})
	env.Parse()
	log.Println("listening on", *listen)
	log.Fatal(http.ListenAndServe(*listen, nil))
}

// handles updates to pull requests
func prHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	req := pullRequest{}
	err := decoder.Decode(&req)
	if err != nil {
		body, err := json.Marshal(map[string]string{"text": fmt.Sprintln("parsing request:", err)})
		if err != nil {
			panic(err)
		}
		postToSlack(body)
		return
	}

	log.Println("pr ref:", req.PR.Head.Ref)
	log.Println("pr action:", req.Action)
	if req.Action == "opened" || req.Action == "synchronize" {
		go func() {
			mu.Lock()
			defer mu.Unlock()
			var out bytes.Buffer
			defer uploadToS3(req.PR.Head.Sha, &out)
			defer catch(&out)
			runIn(sourcedir, &out, exec.Command("git", "fetch", "origin"), req)
			runIn(sourcedir, &out, exec.Command("git", "clean", "-xdf"), req)
			runIn(sourcedir, &out, exec.Command("git", "checkout", req.PR.Head.Ref, "--"), req)
			runIn(sourcedir, &out, exec.Command("git", "reset", "--hard", "origin/"+req.PR.Head.Ref), req)
			runIn(sourcedir, &out, exec.Command("sh", "docker/testbot/tests.sh"), req)
			postToGithub(req.PR.Head.Sha, map[string]string{
				"state":       "success",
				"description": "Integration tests passed",
				"context":     "chain/testbot",
			})
		}()
	}
}

// handles commits to the "main" branch
func commitHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var req struct {
		After, Ref string
	}
	err := decoder.Decode(&req)
	if err != nil {
		body, err := json.Marshal(map[string]string{"text": fmt.Sprintln("parsing request:", err)})
		if err != nil {
			panic(err)
		}
		postToSlack(body)
		return
	}

	log.Println("ref pushed:", req.Ref)
	if req.Ref == "refs/heads/main" {
		go func() {
			mu.Lock()
			defer mu.Unlock()
			defer catch(new(bytes.Buffer))
			select {
			case <-startBenchcore(req.After):
			case <-time.After(2 * time.Minute):
				postToSlackText("starting benchmark timed out for " + req.After)
			}
		}()
	}
}

// ready unblocks when benchcore is no longer
// reading from the filesystem.
func startBenchcore(commit string) (ready <-chan struct{}) {
	ch := make(chan struct{})
	go func() {
		var errbuf bytes.Buffer
		c := exec.Command("benchcore", "IouSettlement.java")
		c.Dir = filepath.Join(sourcedir, "perf")
		c.Stderr = io.MultiWriter(&errbuf, &signalWriter{
			target: []byte("READY, done with local filesystem"),
			done:   ch,
		})
		stats, err := c.Output()
		// TODO(kr): save log in s3
		if err != nil {
			postToSlackText("benchcore error: " + err.Error())
			return
		}
		// TODO(kr): save stats in s3 (not just slack)
		var x struct {
			Elapsed float64 `json:"elapsed_ms"`
			Txs     float64
		}
		err = json.Unmarshal(stats, &x)
		if err != nil {
			postToSlackText("benchcore error: " + err.Error())
			return
		}
		x.Elapsed = x.Elapsed / 1000 // ms -> s
		postToSlackText(fmt.Sprintf("throughput for %s: %.2f tx/s", commit, x.Txs/x.Elapsed))
	}()
	return ch
}

type signalWriter struct {
	target []byte
	done   chan<- struct{}

	buf []byte // lazily initialized to 2x len(target)
	w   int    // bytes written to 2nd half of buf
}

func (w *signalWriter) Write(p []byte) (int, error) {
	if w.done == nil {
		return len(p), nil
	}
	if w.buf == nil {
		w.buf = make([]byte, 2*len(w.target))
	}

	t := len(w.target)
	bufa := w.buf[:t]
	bufb := w.buf[t:]

	for r := 0; r < len(p); {
		n := copy(bufb[w.w:], p[r:])
		w.w += n
		r += n
		if w.w == t {
			copy(bufa, bufb)
			w.w = 0
		}
		if bytes.Contains(w.buf[:t+w.w], w.target) {
			close(w.done)
			w.done = nil
			break
		}
	}
	return len(p), nil
}

func runIn(dir string, w io.Writer, c *exec.Cmd, req pullRequest) {
	c.Dir = dir
	c.Env = os.Environ()
	c.Stdout = w
	c.Stderr = w
	if err := c.Run(); err != nil {
		log.Printf("error: command run: %s\n", strings.Join(c.Args, " "))
		postToGithub(req.PR.Head.Sha, map[string]string{
			"state":       "failure",
			"description": "Integration tests failed",
			"target_url":  "https://s3.amazonaws.com/chain-qa/testbot/" + req.PR.Head.Sha,
			"context":     "chain/testbot",
		})
		panic(req)
	}
}

func uploadToS3(filename string, logfile io.Reader) {
	log.Println("uploading results to s3")
	req, err := http.NewRequest("PUT", "https://chain-qa.s3.amazonaws.com/testbot/"+filename, logfile)
	if err != nil {
		log.Println("sending request:", err)
	}
	req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	req.Header.Set("X-Amz-Acl", "public-read")
	req.Header.Set("Content-Type", "text/plain")
	req.Header.Set("Content-Disposition", "inline")
	s3.Sign(req, keys)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	log.Printf("response from aws: %s", resp.Status)
}

func postToGithub(sha string, requestBody map[string]string) {
	log.Println("sending results to github")
	b, err := json.Marshal(requestBody)
	if err != nil {
		panic(err)
	}
	req, err := http.NewRequest("POST", "https://api.github.com/repos/chain/chain/statuses/"+sha, bytes.NewReader(b))
	if err != nil {
		log.Println("sending request:", err)
	}
	req.Header.Add("Authorization", "token "+githubToken)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Println("sending request:", err)
	}
	defer resp.Body.Close()
	log.Printf("response from github: %s", resp.Status)
}

func postToSlackText(s string) {
	b, err := json.Marshal(map[string]string{"text": s})
	if err != nil {
		panic(err)
	}
	postToSlack(b)
}

func postToSlack(b []byte) {
	log.Println("sending results to slack")
	resp, err := http.Post(slackURL, "application/json", bytes.NewReader(b))
	if err != nil {
		log.Println("sending request:", err)
	}
	defer resp.Body.Close()
	log.Printf("response from slack: %s", resp.Status)
}

func catch(w io.Writer) {
	if err := recover(); err != nil {
		switch err := err.(type) {
		case pullRequest:
			fmt.Fprintln(w, err)
		default:
			panic(err)
		}
	}
}
<commit_msg>cmd/testbot: fetch pull request instead of branch ref<commit_after>// Command testbot provides a web server for running Chain integeration tests.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/kr/s3"

	"chain/env"
)

var (
	listen      = env.String("LISTEN", ":8080")
	githubToken = os.Getenv("GITHUB_TOKEN")
	slackURL    = os.Getenv("SLACK_WEBHOOK_URL")
	sourcedir   = os.Getenv("CHAIN")
	keys        = s3.Keys{
		AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
		SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
	}
	mu sync.Mutex
)

type pullRequest struct {
	Action string
	PR     struct {
		Number string
		Head   struct {
			Ref string
			Sha string
		}
		StatusesURL string `json:"statuses_url"`
	} `json:"pull_request"`
}

func main() {
	log.SetFlags(log.Lshortfile)
	http.HandleFunc("/pr", prHandler)
	http.HandleFunc("/commit", commitHandler)
	http.HandleFunc("/health", func(http.ResponseWriter, *http.Request) {})
	env.Parse()
	log.Println("listening on", *listen)
	log.Fatal(http.ListenAndServe(*listen, nil))
}

// handles updates to pull requests
func prHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	req := pullRequest{}
	err := decoder.Decode(&req)
	if err != nil {
		body, err := json.Marshal(map[string]string{"text": fmt.Sprintln("parsing request:", err)})
		if err != nil {
			panic(err)
		}
		postToSlack(body)
		return
	}

	log.Println("pr sha:", req.PR.Head.Sha)
	log.Println("pr action:", req.Action)
	if req.Action == "opened" || req.Action == "synchronize" {
		go func() {
			mu.Lock()
			defer mu.Unlock()
			var out bytes.Buffer
			sha := req.PR.Head.Sha
			defer uploadToS3(sha, &out)
			defer catch(&out)
			runIn(sourcedir, &out, exec.Command("git", "fetch", "origin", "pull/"+req.PR.Number+"/head"), req)
			runIn(sourcedir, &out, exec.Command("git", "clean", "-xdf"), req)
			runIn(sourcedir, &out, exec.Command("git", "checkout", sha, "--"), req)
			runIn(sourcedir, &out, exec.Command("sh", "docker/testbot/tests.sh"), req)
			postToGithub(req.PR.StatusesURL, map[string]string{
				"state":       "success",
				"description": "Integration tests passed",
				"context":     "chain/testbot",
			})
		}()
	}
}

// handles commits to the "main" branch
func commitHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var req struct {
		After, Ref string
	}
	err := decoder.Decode(&req)
	if err != nil {
		body, err := json.Marshal(map[string]string{"text": fmt.Sprintln("parsing request:", err)})
		if err != nil {
			panic(err)
		}
		postToSlack(body)
		return
	}

	log.Println("ref pushed:", req.Ref)
	if req.Ref == "refs/heads/main" {
		go func() {
			mu.Lock()
			defer mu.Unlock()
			defer catch(new(bytes.Buffer))
			select {
			case <-startBenchcore(req.After):
			case <-time.After(2 * time.Minute):
				postToSlackText("starting benchmark timed out for " + req.After)
			}
		}()
	}
}

// ready unblocks when benchcore is no longer
// reading from the filesystem.
func startBenchcore(commit string) (ready <-chan struct{}) {
	ch := make(chan struct{})
	go func() {
		var errbuf bytes.Buffer
		c := exec.Command("benchcore", "IouSettlement.java")
		c.Dir = filepath.Join(sourcedir, "perf")
		c.Stderr = io.MultiWriter(&errbuf, &signalWriter{
			target: []byte("READY, done with local filesystem"),
			done:   ch,
		})
		stats, err := c.Output()
		// TODO(kr): save log in s3
		if err != nil {
			postToSlackText("benchcore error: " + err.Error())
			return
		}
		// TODO(kr): save stats in s3 (not just slack)
		var x struct {
			Elapsed float64 `json:"elapsed_ms"`
			Txs     float64
		}
		err = json.Unmarshal(stats, &x)
		if err != nil {
			postToSlackText("benchcore error: " + err.Error())
			return
		}
		x.Elapsed = x.Elapsed / 1000 // ms -> s
		postToSlackText(fmt.Sprintf("throughput for %s: %.2f tx/s", commit, x.Txs/x.Elapsed))
	}()
	return ch
}

type signalWriter struct {
	target []byte
	done   chan<- struct{}

	buf []byte // lazily initialized to 2x len(target)
	w   int    // bytes written to 2nd half of buf
}

func (w *signalWriter) Write(p []byte) (int, error) {
	if w.done == nil {
		return len(p), nil
	}
	if w.buf == nil {
		w.buf = make([]byte, 2*len(w.target))
	}

	t := len(w.target)
	bufa := w.buf[:t]
	bufb := w.buf[t:]

	for r := 0; r < len(p); {
		n := copy(bufb[w.w:], p[r:])
		w.w += n
		r += n
		if w.w == t {
			copy(bufa, bufb)
			w.w = 0
		}
		if bytes.Contains(w.buf[:t+w.w], w.target) {
			close(w.done)
			w.done = nil
			break
		}
	}
	return len(p), nil
}

func runIn(dir string, w io.Writer, c *exec.Cmd, req pullRequest) {
	c.Dir = dir
	c.Env = os.Environ()
	c.Stdout = w
	c.Stderr = w
	if err := c.Run(); err != nil {
		log.Printf("error: command run: %s\n", strings.Join(c.Args, " "))
		postToGithub(req.PR.StatusesURL, map[string]string{
			"state":       "failure",
			"description": "Integration tests failed",
			"target_url":  "https://s3.amazonaws.com/chain-qa/testbot/" + req.PR.Head.Sha,
			"context":     "chain/testbot",
		})
		panic(req)
	}
}

func uploadToS3(filename string, logfile io.Reader) {
	log.Println("uploading results to s3")
	req, err := http.NewRequest("PUT", "https://chain-qa.s3.amazonaws.com/testbot/"+filename, logfile)
	if err != nil {
		log.Println("sending request:", err)
	}
	req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	req.Header.Set("X-Amz-Acl", "public-read")
	req.Header.Set("Content-Type", "text/plain")
	req.Header.Set("Content-Disposition", "inline")
	s3.Sign(req, keys)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	log.Printf("response from aws: %s", resp.Status)
}

func postToGithub(url string, requestBody map[string]string) {
	log.Println("sending results to github")
	b, err := json.Marshal(requestBody)
	if err != nil {
		panic(err)
	}
	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		log.Println("sending request:", err)
	}
	req.Header.Add("Authorization", "token "+githubToken)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Println("sending request:", err)
	}
	defer resp.Body.Close()
	log.Printf("response from github: %s", resp.Status)
}

func postToSlackText(s string) {
	b, err := json.Marshal(map[string]string{"text": s})
	if err != nil {
		panic(err)
	}
	postToSlack(b)
}

func postToSlack(b []byte) {
	log.Println("sending results to slack")
	resp, err := http.Post(slackURL, "application/json", bytes.NewReader(b))
	if err != nil {
		log.Println("sending request:", err)
	}
	defer resp.Body.Close()
	log.Printf("response from slack: %s", resp.Status)
}

func catch(w io.Writer) {
	if err := recover(); err != nil {
		switch err := err.(type) {
		case pullRequest:
			fmt.Fprintln(w, err)
		default:
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton Authors

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

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

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

package main

import (
	"context"
	"log"
	"net/http"
	"os"

	defaultconfig "github.com/tektoncd/pipeline/pkg/apis/config"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
	"github.com/tektoncd/pipeline/pkg/contexts"
	"github.com/tektoncd/pipeline/pkg/system"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/injection"
	"knative.dev/pkg/injection/sharedmain"
	pkgleaderelection "knative.dev/pkg/leaderelection"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/webhook"
	"knative.dev/pkg/webhook/certificates"
	"knative.dev/pkg/webhook/configmaps"
	"knative.dev/pkg/webhook/resourcesemantics"
	"knative.dev/pkg/webhook/resourcesemantics/conversion"
	"knative.dev/pkg/webhook/resourcesemantics/defaulting"
	"knative.dev/pkg/webhook/resourcesemantics/validation"
)

var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{
	// v1alpha1
	v1alpha1.SchemeGroupVersion.WithKind("Pipeline"):         &v1alpha1.Pipeline{},
	v1alpha1.SchemeGroupVersion.WithKind("Task"):             &v1alpha1.Task{},
	v1alpha1.SchemeGroupVersion.WithKind("ClusterTask"):      &v1alpha1.ClusterTask{},
	v1alpha1.SchemeGroupVersion.WithKind("TaskRun"):          &v1alpha1.TaskRun{},
	v1alpha1.SchemeGroupVersion.WithKind("PipelineRun"):      &v1alpha1.PipelineRun{},
	v1alpha1.SchemeGroupVersion.WithKind("Condition"):        &v1alpha1.Condition{},
	v1alpha1.SchemeGroupVersion.WithKind("PipelineResource"): &v1alpha1.PipelineResource{},
	v1alpha1.SchemeGroupVersion.WithKind("Run"):              &v1alpha1.Run{},
	// v1beta1
	v1beta1.SchemeGroupVersion.WithKind("Pipeline"):    &v1beta1.Pipeline{},
	v1beta1.SchemeGroupVersion.WithKind("Task"):        &v1beta1.Task{},
	v1beta1.SchemeGroupVersion.WithKind("ClusterTask"): &v1beta1.ClusterTask{},
	v1beta1.SchemeGroupVersion.WithKind("TaskRun"):     &v1beta1.TaskRun{},
	v1beta1.SchemeGroupVersion.WithKind("PipelineRun"): &v1beta1.PipelineRun{},
}

func newDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// Decorate contexts with the current state of the config.
	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
	store.WatchConfigs(cmw)

	return defaulting.NewAdmissionController(ctx,

		// Name of the resource webhook.
		"webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/defaulting",

		// The resources to validate and default.
		types,

		// A function that infuses the context passed to Validate/SetDefaults with custom metadata.
		func(ctx context.Context) context.Context {
			return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx))
		},

		// Whether to disallow unknown fields.
		true,
	)
}

func newValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// Decorate contexts with the current state of the config.
	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
	store.WatchConfigs(cmw)
	return validation.NewAdmissionController(ctx,

		// Name of the resource webhook.
		"validation.webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/resource-validation",

		// The resources to validate and default.
		types,

		// A function that infuses the context passed to Validate/SetDefaults with custom metadata.
		func(ctx context.Context) context.Context {
			return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx))
		},

		// Whether to disallow unknown fields.
		true,
	)
}

func newConfigValidationController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	return configmaps.NewAdmissionController(ctx,

		// Name of the configmap webhook.
		"config.webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/config-validation",

		// The configmaps to validate.
		configmap.Constructors{
			logging.ConfigMapName():               logging.NewConfigFromConfigMap,
			defaultconfig.GetDefaultsConfigName(): defaultconfig.NewDefaultsFromConfigMap,
			pkgleaderelection.ConfigMapName():     pkgleaderelection.NewConfigFromConfigMap,
		},
	)
}

func newConversionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// nolint: golint
	var (
		v1alpha1GroupVersion = v1alpha1.SchemeGroupVersion.Version
		v1beta1GroupVersion  = v1beta1.SchemeGroupVersion.Version
	)

	return conversion.NewConversionController(ctx,
		// The path on which to serve the webhook
		"/resource-conversion",

		// Specify the types of custom resource definitions that should be converted
		map[schema.GroupKind]conversion.GroupKindConversion{
			v1beta1.Kind("Task"): {
				DefinitionName: pipeline.TaskResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.Task{},
					v1beta1GroupVersion:  &v1beta1.Task{},
				},
			},
			v1beta1.Kind("ClusterTask"): {
				DefinitionName: pipeline.ClusterTaskResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.ClusterTask{},
					v1beta1GroupVersion:  &v1beta1.ClusterTask{},
				},
			},
			v1beta1.Kind("TaskRun"): {
				DefinitionName: pipeline.TaskRunResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.TaskRun{},
					v1beta1GroupVersion:  &v1beta1.TaskRun{},
				},
			},
			v1beta1.Kind("Pipeline"): {
				DefinitionName: pipeline.PipelineResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.Pipeline{},
					v1beta1GroupVersion:  &v1beta1.Pipeline{},
				},
			},
			v1beta1.Kind("PipelineRun"): {
				DefinitionName: pipeline.PipelineRunResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.PipelineRun{},
					v1beta1GroupVersion:  &v1beta1.PipelineRun{},
				},
			},
		},

		// A function that infuses the context passed to ConvertTo/ConvertFrom/SetDefaults with custom metadata
		func(ctx context.Context) context.Context {
			return ctx
		},
	)
}

func main() {
	serviceName := os.Getenv("WEBHOOK_SERVICE_NAME")
	if serviceName == "" {
		serviceName = "tekton-pipelines-webhook"
	}

	secretName := os.Getenv("WEBHOOK_SECRET_NAME")
	if secretName == "" {
		secretName = "webhook-certs" // #nosec
	}

	// Scope informers to the webhook's namespace instead of cluster-wide
	ctx := injection.WithNamespaceScope(signals.NewContext(), system.GetNamespace())

	// Set up a signal context with our webhook options
	ctx = webhook.WithOptions(ctx, webhook.Options{
		ServiceName: serviceName,
		Port:        8443,
		SecretName:  secretName,
	})

	mux := http.NewServeMux()

	mux.HandleFunc("/", handler)
	mux.HandleFunc("/health", handler)
	mux.HandleFunc("/readiness", handler)

	port := os.Getenv("PROBES_PORT")
	if port == "" {
		port = "8080"
	}

	go func() {
		// start the web server on port and accept requests
		log.Printf("Readiness and health check server listening on port %s", port)
		log.Fatal(http.ListenAndServe(":"+port, mux))
	}()

	sharedmain.WebhookMainWithConfig(ctx, "webhook",
		sharedmain.ParseAndGetConfigOrDie(),
		certificates.NewController,
		newDefaultingAdmissionController,
		newValidationAdmissionController,
		newConfigValidationController,
		newConversionController,
	)
}

func handler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}
<commit_msg>Change the webhook name to pipeline-webhook<commit_after>/*
Copyright 2019 The Tekton Authors

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

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

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

package main

import (
	"context"
	"log"
	"net/http"
	"os"

	defaultconfig "github.com/tektoncd/pipeline/pkg/apis/config"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
	"github.com/tektoncd/pipeline/pkg/contexts"
	"github.com/tektoncd/pipeline/pkg/system"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/injection"
	"knative.dev/pkg/injection/sharedmain"
	pkgleaderelection "knative.dev/pkg/leaderelection"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/webhook"
	"knative.dev/pkg/webhook/certificates"
	"knative.dev/pkg/webhook/configmaps"
	"knative.dev/pkg/webhook/resourcesemantics"
	"knative.dev/pkg/webhook/resourcesemantics/conversion"
	"knative.dev/pkg/webhook/resourcesemantics/defaulting"
	"knative.dev/pkg/webhook/resourcesemantics/validation"
)

var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{
	// v1alpha1
	v1alpha1.SchemeGroupVersion.WithKind("Pipeline"):         &v1alpha1.Pipeline{},
	v1alpha1.SchemeGroupVersion.WithKind("Task"):             &v1alpha1.Task{},
	v1alpha1.SchemeGroupVersion.WithKind("ClusterTask"):      &v1alpha1.ClusterTask{},
	v1alpha1.SchemeGroupVersion.WithKind("TaskRun"):          &v1alpha1.TaskRun{},
	v1alpha1.SchemeGroupVersion.WithKind("PipelineRun"):      &v1alpha1.PipelineRun{},
	v1alpha1.SchemeGroupVersion.WithKind("Condition"):        &v1alpha1.Condition{},
	v1alpha1.SchemeGroupVersion.WithKind("PipelineResource"): &v1alpha1.PipelineResource{},
	v1alpha1.SchemeGroupVersion.WithKind("Run"):              &v1alpha1.Run{},
	// v1beta1
	v1beta1.SchemeGroupVersion.WithKind("Pipeline"):    &v1beta1.Pipeline{},
	v1beta1.SchemeGroupVersion.WithKind("Task"):        &v1beta1.Task{},
	v1beta1.SchemeGroupVersion.WithKind("ClusterTask"): &v1beta1.ClusterTask{},
	v1beta1.SchemeGroupVersion.WithKind("TaskRun"):     &v1beta1.TaskRun{},
	v1beta1.SchemeGroupVersion.WithKind("PipelineRun"): &v1beta1.PipelineRun{},
}

func newDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// Decorate contexts with the current state of the config.
	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
	store.WatchConfigs(cmw)

	return defaulting.NewAdmissionController(ctx,

		// Name of the resource webhook.
		"webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/defaulting",

		// The resources to validate and default.
		types,

		// A function that infuses the context passed to Validate/SetDefaults with custom metadata.
		func(ctx context.Context) context.Context {
			return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx))
		},

		// Whether to disallow unknown fields.
		true,
	)
}

func newValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// Decorate contexts with the current state of the config.
	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
	store.WatchConfigs(cmw)
	return validation.NewAdmissionController(ctx,

		// Name of the resource webhook.
		"validation.webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/resource-validation",

		// The resources to validate and default.
		types,

		// A function that infuses the context passed to Validate/SetDefaults with custom metadata.
		func(ctx context.Context) context.Context {
			return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx))
		},

		// Whether to disallow unknown fields.
		true,
	)
}

func newConfigValidationController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	return configmaps.NewAdmissionController(ctx,

		// Name of the configmap webhook.
		"config.webhook.pipeline.tekton.dev",

		// The path on which to serve the webhook.
		"/config-validation",

		// The configmaps to validate.
		configmap.Constructors{
			logging.ConfigMapName():               logging.NewConfigFromConfigMap,
			defaultconfig.GetDefaultsConfigName(): defaultconfig.NewDefaultsFromConfigMap,
			pkgleaderelection.ConfigMapName():     pkgleaderelection.NewConfigFromConfigMap,
		},
	)
}

func newConversionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
	// nolint: golint
	var (
		v1alpha1GroupVersion = v1alpha1.SchemeGroupVersion.Version
		v1beta1GroupVersion  = v1beta1.SchemeGroupVersion.Version
	)

	return conversion.NewConversionController(ctx,
		// The path on which to serve the webhook
		"/resource-conversion",

		// Specify the types of custom resource definitions that should be converted
		map[schema.GroupKind]conversion.GroupKindConversion{
			v1beta1.Kind("Task"): {
				DefinitionName: pipeline.TaskResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.Task{},
					v1beta1GroupVersion:  &v1beta1.Task{},
				},
			},
			v1beta1.Kind("ClusterTask"): {
				DefinitionName: pipeline.ClusterTaskResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.ClusterTask{},
					v1beta1GroupVersion:  &v1beta1.ClusterTask{},
				},
			},
			v1beta1.Kind("TaskRun"): {
				DefinitionName: pipeline.TaskRunResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.TaskRun{},
					v1beta1GroupVersion:  &v1beta1.TaskRun{},
				},
			},
			v1beta1.Kind("Pipeline"): {
				DefinitionName: pipeline.PipelineResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.Pipeline{},
					v1beta1GroupVersion:  &v1beta1.Pipeline{},
				},
			},
			v1beta1.Kind("PipelineRun"): {
				DefinitionName: pipeline.PipelineRunResource.String(),
				HubVersion:     v1alpha1GroupVersion,
				Zygotes: map[string]conversion.ConvertibleObject{
					v1alpha1GroupVersion: &v1alpha1.PipelineRun{},
					v1beta1GroupVersion:  &v1beta1.PipelineRun{},
				},
			},
		},

		// A function that infuses the context passed to ConvertTo/ConvertFrom/SetDefaults with custom metadata
		func(ctx context.Context) context.Context {
			return ctx
		},
	)
}

func main() {
	serviceName := os.Getenv("WEBHOOK_SERVICE_NAME")
	if serviceName == "" {
		serviceName = "tekton-pipelines-webhook"
	}

	secretName := os.Getenv("WEBHOOK_SECRET_NAME")
	if secretName == "" {
		secretName = "webhook-certs" // #nosec
	}

	// Scope informers to the webhook's namespace instead of cluster-wide
	ctx := injection.WithNamespaceScope(signals.NewContext(), system.GetNamespace())

	// Set up a signal context with our webhook options
	ctx = webhook.WithOptions(ctx, webhook.Options{
		ServiceName: serviceName,
		Port:        8443,
		SecretName:  secretName,
	})

	mux := http.NewServeMux()

	mux.HandleFunc("/", handler)
	mux.HandleFunc("/health", handler)
	mux.HandleFunc("/readiness", handler)

	port := os.Getenv("PROBES_PORT")
	if port == "" {
		port = "8080"
	}

	go func() {
		// start the web server on port and accept requests
		log.Printf("Readiness and health check server listening on port %s", port)
		log.Fatal(http.ListenAndServe(":"+port, mux))
	}()

	// NOTE(afrittoli) - we should have the name "webhook-pipeline"
	// configurable. Once the change is done on knative/pkg side
	// knative/eventing#4530 we can inherit it from it
	sharedmain.WebhookMainWithConfig(ctx, "webhook-pipeline",
		sharedmain.ParseAndGetConfigOrDie(),
		certificates.NewController,
		newDefaultingAdmissionController,
		newValidationAdmissionController,
		newConfigValidationController,
		newConversionController,
	)
}

func handler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package appbits

import (
	"archive/zip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	"io"
	"io/ioutil"

	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/gofileutils/fileutils"
	"github.com/glyn/bloblets/blobstore"
	"github.com/glyn/bloblets/servutil"

	"os"
)

func AppHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("AppHandler entered")
	r.ParseMultipartForm(100 * 1024 * 1024)
	mpForm := r.MultipartForm

	res := mpForm.Value["resources"]
	presentFiles := []resources.AppFileResource{}
	err := json.Unmarshal([]byte(res[0]), &presentFiles)
	if err != nil {
		servutil.Fail(w, "demarshalling resources failed: %s", err)
		return
	}
	log.Println("AppHandler demarshalled resources")

	application := mpForm.File["application"][0]
	zipFile, err := application.Open()
	if err != nil {
		servutil.Fail(w, "failed to open application zip: %s", err)
		return
	}
	cl := application.Header.Get("Content-Length")
	if cl == "" {
		servutil.Fail(w, "Content-Length header not supplied for application zip: %s", err)
		return
	}
	zipLen, err := strconv.ParseInt(cl, 10, 64)
	if err != nil {
		servutil.Fail(w, "Invalid Content-Length %s: %s", cl, err)
		return
	}

	zr, err := zip.NewReader(zipFile, zipLen)

	for _, f := range zr.File {
		t, err := ioutil.TempFile("", "tempFile")
		if err != nil {
			servutil.Fail(w, "Failed to create temporary file: %s", err)
			return
		}

		of, err := f.Open()
		if err != nil {
			servutil.Fail(w, "Failed to open application file: %s", err)
			return
		}

		io.Copy(t, of)
		of.Close()
		fi, err := t.Stat()
		if err != nil {
			servutil.Fail(w, "Failed to stat application file: %s", err)
			return
		}
		t.Close()
		fn := t.Name()
		if !fi.IsDir() && fi.Size() > 65535 {
			sha := sha(fn)
			log.Printf("AppHandler adding file %s to the blob store: %s\n", f.Name, sha)
			blobstore.Add(sha, fn)
		}
		os.RemoveAll(fn)
	}
	log.Println("AppHandler exiting")
}

func sha(path string) string {
	hash := sha1.New()
	err := fileutils.CopyPathToWriter(path, hash)
	if err != nil {
		panic("Failed to compute sha1")
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}
<commit_msg>Recompose application in server<commit_after>package appbits

import (
	"archive/zip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"path/filepath"
	"strconv"

	"io"
	"io/ioutil"

	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/gofileutils/fileutils"
	"github.com/glyn/bloblets/blobstore"
	"github.com/glyn/bloblets/servutil"

	"os"
)

func AppHandler(w http.ResponseWriter, r *http.Request) {
	log.Println("AppHandler entered")
	appDir, err := ioutil.TempDir("", "recomposed-application")
	if err != nil {
		servutil.Fail(w, "creating recomposed application directory failed: %s", err)
		return
	}
	defer os.RemoveAll(appDir)

	r.ParseMultipartForm(100 * 1024 * 1024)
	mpForm := r.MultipartForm

	// Unzip the uploaded portion of the application.
	application := mpForm.File["application"][0]
	zipFile, err := application.Open()
	if err != nil {
		servutil.Fail(w, "failed to open application zip: %s", err)
		return
	}
	cl := application.Header.Get("Content-Length")
	if cl == "" {
		servutil.Fail(w, "Content-Length header not supplied for application zip: %s", err)
		return
	}
	zipLen, err := strconv.ParseInt(cl, 10, 64)
	if err != nil {
		servutil.Fail(w, "Invalid Content-Length %s: %s", cl, err)
		return
	}

	zr, err := zip.NewReader(zipFile, zipLen)

	for _, f := range zr.File {
		destPath := filepath.Join(appDir, f.Name)
		destDir, _ := filepath.Split(destPath)
		err := os.MkdirAll(destDir[:len(destDir)-1], 0755)
		if err != nil {
			servutil.Fail(w, "Failed to create destination directory: %s", err)
			return
		}

		if f.FileInfo().IsDir() {
			continue
		}

		dest, err := os.OpenFile(destPath, os.O_CREATE|os.O_RDWR, 0755)
		if err != nil {
			servutil.Fail(w, "Failed to create destination file: %s", err)
			return
		}

		of, err := f.Open()
		if err != nil {
			servutil.Fail(w, "Failed to open application file: %s", err)
			return
		}

		io.Copy(dest, of)
		of.Close()
		fi, err := dest.Stat()
		if err != nil {
			servutil.Fail(w, "Failed to stat application file: %s", err)
			return
		}
		dest.Close()
		fn := dest.Name()
		if !fi.IsDir() && fi.Size() > 65535 {
			sha := sha(fn)
			log.Printf("AppHandler adding file %s to the blob store: %s\n", f.Name, sha)
			blobstore.Add(sha, fn)
		}
	}
	log.Println("AppHandler unzipped uploaded portion of application")

	// Add blobs to the application
	res := mpForm.Value["resources"]
	presentFiles := []resources.AppFileResource{}
	err = json.Unmarshal([]byte(res[0]), &presentFiles)
	if err != nil {
		servutil.Fail(w, "demarshalling resources failed: %s", err)
		return
	}
	for _, pf := range presentFiles {
		dest := filepath.Join(appDir, pf.Path)
		blobstore.Get(pf.Sha1, dest)
	}

	log.Println("AppHandler exiting")
}

func sha(path string) string {
	hash := sha1.New()
	err := fileutils.CopyPathToWriter(path, hash)
	if err != nil {
		panic("Failed to compute sha1")
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"log"
	"math"
	"math/rand"
	"time"

	"github.com/ianremmler/setgo"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	cardAspRat = 1.4
)

var (
	set   *setgo.SetGo
	field []setgo.Card

	candidate = map[int]struct{}{}

	program  gl.Program
	position gl.Attrib
	color    gl.Uniform
	shading  gl.Uniform
	mvMat    gl.Uniform

	cardShape = shape{verts: cardVerts}
	cardColor = []float32{1, 1, 1, 1}
)

var colors = [][]float32{
	{1.0, 0.0, 0.0, 1.0},
	{0.0, 0.75, 0.0, 1.0},
	{0.0, 0.0, 1.0, 1.0},
}

var shapes = []shape{
	{verts: squareVerts},
	{verts: triVerts},
	{verts: hexVerts},
}

type shape struct {
	verts []float32
	buf   gl.Buffer
}

type state int

const (
	normal state = iota
	selected
	invalid
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	rand.Seed(time.Now().UnixNano())
	set = setgo.NewStd()
	set.Deal()

	var err error
	program, err = glutil.CreateProgram(vertShader, fragShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.LineWidth(4)

	cardShape.buf = gl.CreateBuffer()
	vertBytes := f32.Bytes(binary.LittleEndian, cardShape.verts...)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	for i := range shapes {
		shapes[i].buf = gl.CreateBuffer()
		vertBytes = f32.Bytes(binary.LittleEndian, shapes[i].verts...)
		gl.BindBuffer(gl.ARRAY_BUFFER, shapes[i].buf)
		gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	}

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	shading = gl.GetUniformLocation(program, "shading")
	mvMat = gl.GetUniformLocation(program, "mvMat")

	field = set.Field()
}

func stop() {
	gl.DeleteProgram(program)
	gl.DeleteBuffer(cardShape.buf)
	for i := range shapes {
		gl.DeleteBuffer(shapes[i].buf)
	}
}

func touch(evt event.Touch) {
	if evt.Type != event.TouchEnd {
		return
	}

	w, h, fw, fh := viewDims()
	dw, dh := float32(geom.Width), float32(geom.Height)
	tx, ty := float32(evt.Loc.X), float32(evt.Loc.Y)

	rows, cols := 3, len(field)/3
	c := int(float32(cols) * (tx/dw*w/fw - 0.5*(w-fw)/fw))
	r := int(float32(rows) * (ty/dh*h/fh - 0.5*(h-fh)/fh))

	idx := -1
	if r >= 0 && r < rows && c >= 0 && c < cols {
		idx = 3*c + (2 - r)
	}

	if idx >= 0 {
		updateCandidate(idx)
	}
}

func updateCandidate(idx int) {
	if _, ok := candidate[idx]; ok {
		delete(candidate, idx)
	} else if len(candidate) < 3 {
		candidate[idx] = struct{}{}
	}
	if len(candidate) < 3 {
		return
	}
	check := []int{}
	for idx := range candidate {
		check = append(check, idx)
	}
	if !set.IsSet(check) {
		return
	}
	// still here... we got a set!
	candidate = map[int]struct{}{}
	set.Remove(check)
	set.Deal()
	if set.NumSets() == 0 {
		set.Shuffle()
		set.Deal()
	}
	field = set.Field()
}

func drawCard(mat *f32.Mat4, card *setgo.Card, st state) {
	num, clr, shp, fil := card.Attr[0], card.Attr[1], card.Attr[2], card.Attr[3]

	mvMat.WriteMat4(mat)

	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform1i(shading, 2)
	gl.Uniform4fv(color, cardColor)
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)

	gl.BindBuffer(gl.ARRAY_BUFFER, shapes[shp].buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform4fv(color, colors[clr])

	for i := 0; i <= num; i++ {
		shapeMat := *mat
		offset := float32(i+1) / (float32(num) + 2)
		shapeMat.Translate(&shapeMat, 0.5, offset*cardAspRat, 0)
		shapeMat.Scale(&shapeMat, 0.1, 0.1, 0)
		mvMat.WriteMat4(&shapeMat)

		gl.Uniform1i(shading, fil)
		gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(shapes[shp].verts)/3)
		gl.Uniform1i(shading, 2)
		gl.DrawArrays(gl.LINE_LOOP, 0, len(shapes[shp].verts)/3)
	}
	gl.DisableVertexAttribArray(position)

	if st == normal {
		return
	}

	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	switch st {
	case selected:
		gl.Uniform4f(color, 0, 1, 1, 0.25)
	case invalid:
		gl.Uniform4f(color, 1, 0, 0, 0.25)
	}
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)
}

func viewDims() (float32, float32, float32, float32) {
	cols := len(field) / 3
	fieldAspRat := float32(cols) / (3 * cardAspRat)
	dispAspRat := float32(geom.Width / geom.Height)

	fieldWidth, fieldHeight := float32(cols), float32(3*cardAspRat)
	width, height := fieldWidth, fieldHeight
	if dispAspRat > fieldAspRat {
		width = fieldHeight * dispAspRat
	} else {
		height = fieldWidth / dispAspRat
	}
	return width, height, fieldWidth, fieldHeight
}

func viewMat(w, h, fw, fh float32) f32.Mat4 {
	mat := f32.Mat4{}
	mat.Identity()
	mat.Scale(&mat, 1.0/(0.5*w), 1.0/(0.5*h), 1)
	return mat
}

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	w, h, fw, fh := viewDims()
	mat := viewMat(w, h, fw, fh)
	for i := range field {
		x, y := float32(i/3), cardAspRat*float32(i%3)
		cardMat := mat
		cardMat.Translate(&cardMat, x-0.5*fw, y-0.5*fh, 0)
		// shrink just a bit to separate cards
		cardMat.Translate(&cardMat, 0.5, 0.5*cardAspRat, 0)
		cardMat.Scale(&cardMat, 1.0-0.02*cardAspRat, 1.0-0.02, 1)
		cardMat.Translate(&cardMat, -0.5, -0.5*cardAspRat, 0)

		st := normal
		if _, ok := candidate[i]; ok {
			if len(candidate) < 3 {
				st = selected
			} else {
				st = invalid
			}
		}
		drawCard(&cardMat, &field[i], st)
	}
}

var cardVerts = []float32{
	0, 0, 0,
	1, 0, 0,
	1, cardAspRat, 0,
	0, cardAspRat, 0,
}

var sin60 = float32(math.Sqrt(3) / 2)

var squareVerts = []float32{
	-sin60, -sin60, 0,
	sin60, -sin60, 0,
	sin60, sin60, 0,
	-sin60, sin60, 0,
}

var triVerts = []float32{
	-sin60, -sin60, 0,
	0, sin60, 0,
	sin60, -sin60, 0,
}

var hexVerts = []float32{
	-0.5, -sin60, 0,
	-1, 0, 0,
	-0.5, sin60, 0,
	0.5, sin60, 0,
	1, 0, 0,
	0.5, -sin60, 0,
}

const vertShader = `#version 100
uniform mat4 mvMat;

attribute vec4 position;
void main() {
	gl_Position = mvMat * position;
}`

const fragShader = `#version 100
precision mediump float;

uniform vec4 color;
uniform int shading;

void main() {
	if (shading == 0) {
		discard;
	}
	if (shading == 1) {
		if (mod((gl_FragCoord.x + gl_FragCoord.y) / 8.0, 2.0) < 1.0) {
			discard;
		}
	}
	gl_FragColor = color;
}`
<commit_msg>Fixes.<commit_after>package main

import (
	"encoding/binary"
	"log"
	"math"
	"math/rand"
	"time"

	"github.com/ianremmler/setgo"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	cardAspRat = 1.4
)

var (
	set   *setgo.SetGo
	field []setgo.Card

	candidate = map[int]struct{}{}

	program  gl.Program
	position gl.Attrib
	color    gl.Uniform
	shading  gl.Uniform
	mvMat    gl.Uniform

	cardShape = shape{verts: cardVerts}
	cardColor = []float32{1, 1, 1, 1}
)

var colors = [][]float32{
	{1.0, 0.0, 0.0, 1.0},
	{0.0, 0.75, 0.0, 1.0},
	{0.0, 0.0, 1.0, 1.0},
}

var shapes = []shape{
	{verts: squareVerts},
	{verts: triVerts},
	{verts: hexVerts},
}

type shape struct {
	verts []float32
	buf   gl.Buffer
}

type state int

const (
	normal state = iota
	selected
	invalid
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	rand.Seed(time.Now().UnixNano())
	set = setgo.NewStd()

	set.Shuffle()
	set.Deal()
	field = set.Field()

	var err error
	program, err = glutil.CreateProgram(vertShader, fragShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.LineWidth(4)

	cardShape.buf = gl.CreateBuffer()
	vertBytes := f32.Bytes(binary.LittleEndian, cardShape.verts...)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	for i := range shapes {
		shapes[i].buf = gl.CreateBuffer()
		vertBytes = f32.Bytes(binary.LittleEndian, shapes[i].verts...)
		gl.BindBuffer(gl.ARRAY_BUFFER, shapes[i].buf)
		gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	}

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	shading = gl.GetUniformLocation(program, "shading")
	mvMat = gl.GetUniformLocation(program, "mvMat")
}

func stop() {
	gl.DeleteProgram(program)
	gl.DeleteBuffer(cardShape.buf)
	for i := range shapes {
		gl.DeleteBuffer(shapes[i].buf)
	}
}

func touch(evt event.Touch) {
	if evt.Type != event.TouchEnd {
		return
	}

	w, h, fw, fh := viewDims()
	dw, dh := float32(geom.Width), float32(geom.Height)
	tx, ty := float32(evt.Loc.X), float32(evt.Loc.Y)

	rows, cols := 3, len(field)/3
	c := int(float32(cols) * (tx/dw*w/fw - 0.5*(w-fw)/fw))
	r := int(float32(rows) * (ty/dh*h/fh - 0.5*(h-fh)/fh))

	idx := -1
	if r >= 0 && r < rows && c >= 0 && c < cols {
		idx = 3*c + (2 - r)
	}

	if idx >= 0 && idx < len(field) && !field[idx].Blank {
		updateCandidate(idx)
	}
}

func updateCandidate(idx int) {
	if _, ok := candidate[idx]; ok {
		delete(candidate, idx)
	} else if len(candidate) < 3 {
		candidate[idx] = struct{}{}
	}
	if len(candidate) < 3 {
		return
	}
	check := []int{}
	for idx := range candidate {
		check = append(check, idx)
	}
	if !set.IsSet(check) {
		return
	}
	// still here... we got a set!
	candidate = map[int]struct{}{}
	set.Remove(check)
	set.Deal()
	if set.NumSets() == 0 {
		// we won!  play again...
		set.Shuffle()
		set.Deal()
	}
	field = set.Field()
}

func drawCard(mat *f32.Mat4, card *setgo.Card, st state) {
	num, clr, shp, fil := card.Attr[0], card.Attr[1], card.Attr[2], card.Attr[3]

	mvMat.WriteMat4(mat)

	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform1i(shading, 2)
	gl.Uniform4fv(color, cardColor)
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)

	gl.BindBuffer(gl.ARRAY_BUFFER, shapes[shp].buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform4fv(color, colors[clr])

	for i := 0; i <= num; i++ {
		shapeMat := *mat
		offset := float32(i+1) / (float32(num) + 2)
		shapeMat.Translate(&shapeMat, 0.5, offset*cardAspRat, 0)
		shapeMat.Scale(&shapeMat, 0.1, 0.1, 0)
		mvMat.WriteMat4(&shapeMat)

		gl.Uniform1i(shading, fil)
		gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(shapes[shp].verts)/3)
		gl.Uniform1i(shading, 2)
		gl.DrawArrays(gl.LINE_LOOP, 0, len(shapes[shp].verts)/3)
	}
	gl.DisableVertexAttribArray(position)

	if st == normal {
		return
	}

	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	switch st {
	case selected:
		gl.Uniform4f(color, 0, 1, 1, 0.25)
	case invalid:
		gl.Uniform4f(color, 1, 0, 0, 0.25)
	}
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)
}

func viewDims() (float32, float32, float32, float32) {
	cols := len(field) / 3
	fieldAspRat := float32(cols) / (3 * cardAspRat)
	dispAspRat := float32(geom.Width / geom.Height)

	fieldWidth, fieldHeight := float32(cols), float32(3*cardAspRat)
	width, height := fieldWidth, fieldHeight
	if dispAspRat > fieldAspRat {
		width = fieldHeight * dispAspRat
	} else {
		height = fieldWidth / dispAspRat
	}
	return width, height, fieldWidth, fieldHeight
}

func viewMat(w, h, fw, fh float32) f32.Mat4 {
	mat := f32.Mat4{}
	mat.Identity()
	mat.Scale(&mat, 1.0/(0.5*w), 1.0/(0.5*h), 1)
	return mat
}

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	w, h, fw, fh := viewDims()
	mat := viewMat(w, h, fw, fh)
	for i := range field {
		if field[i].Blank {
			continue
		}
		x, y := float32(i/3), cardAspRat*float32(i%3)
		cardMat := mat
		cardMat.Translate(&cardMat, x-0.5*fw, y-0.5*fh, 0)
		// shrink just a bit to separate cards
		cardMat.Translate(&cardMat, 0.5, 0.5*cardAspRat, 0)
		cardMat.Scale(&cardMat, 1.0-0.02*cardAspRat, 1.0-0.02, 1)
		cardMat.Translate(&cardMat, -0.5, -0.5*cardAspRat, 0)

		st := normal
		if _, ok := candidate[i]; ok {
			if len(candidate) < 3 {
				st = selected
			} else {
				st = invalid
			}
		}
		drawCard(&cardMat, &field[i], st)
	}
}

var cardVerts = []float32{
	0, 0, 0,
	1, 0, 0,
	1, cardAspRat, 0,
	0, cardAspRat, 0,
}

var sin60 = float32(math.Sqrt(3) / 2)

var squareVerts = []float32{
	-sin60, -sin60, 0,
	sin60, -sin60, 0,
	sin60, sin60, 0,
	-sin60, sin60, 0,
}

var triVerts = []float32{
	-sin60, -sin60, 0,
	0, sin60, 0,
	sin60, -sin60, 0,
}

var hexVerts = []float32{
	-0.5, -sin60, 0,
	-1, 0, 0,
	-0.5, sin60, 0,
	0.5, sin60, 0,
	1, 0, 0,
	0.5, -sin60, 0,
}

const vertShader = `#version 100
uniform mat4 mvMat;

attribute vec4 position;
void main() {
	gl_Position = mvMat * position;
}`

const fragShader = `#version 100
precision mediump float;

uniform vec4 color;
uniform int shading;

void main() {
	if (shading == 0) {
		discard;
	}
	if (shading == 1) {
		if (mod((gl_FragCoord.x + gl_FragCoord.y) / 8.0, 2.0) < 1.0) {
			discard;
		}
	}
	gl_FragColor = color;
}`
<|endoftext|>


<commit_before>package fcgi

import (
	"bufio"
	"net"
	"net/http"
)

type child struct {
	conn       net.Conn
	recordChan chan *request
	r          *bufio.Reader
}

func startChildHandleLoop(conn net.Conn, handler http.Handler, fcgis *fcgiServer) error {
	c := getChild()
	c.conn = conn
	c.r = bufio.NewReader(conn)

	go c.childHandleProcessor()
	go c.outboundProcessor()

	//TODO
	return nil
}

func (this *child) release() {
	this.conn.Close()
}

func (this *child) reset() {
	//TODO
}

func (this *child) childHandleProcessor() {
	r := this.r
	defer this.reset()
	defer this.release()
	for {
		header := requestHeader{}
		close, err := header.read(r)
		if close {
			break
		}
		if err != nil {
			logError("read header error.", err)
			break
		}
		req := request{
			Header: header,
		}
		err = this.packetDispatching(req)
		if err != nil {
			logError("packet dispatching error. exit inbound loop.", err)
			this.release()
			readToEOF(r)
			break
		}
	}
}

func (this *child) outboundProcessor() {
	//TODO
}

func (this *child) packetDispatching(req request) error {
	//TODO
	if req.Header.getRequestId() == 0 {
		t := req.Header.Type
		ok := checkIsManagingRequest(t)
		if !ok {
			logError("requestId 0 with non-managing request")
		}
		//TODO we don't support managing request now, send unknow type packet
	}
	return nil
}
<commit_msg>add different error types for processing loop<commit_after>package fcgi

import (
	"bufio"
	"net"
	"net/http"
)

type child struct {
	conn       net.Conn
	recordChan chan *request
	r          *bufio.Reader
}

func startChildHandleLoop(conn net.Conn, handler http.Handler, fcgis *fcgiServer) error {
	c := getChild()
	c.conn = conn
	c.r = bufio.NewReader(conn)

	go c.childHandleProcessor()
	go c.outboundProcessor()

	//TODO
	return nil
}

func (this *child) release() {
	this.conn.Close()
}

func (this *child) reset() {
	//TODO
}

func (this *child) childHandleProcessor() {
	r := this.r
	defer this.reset()
	defer this.release()
	for {
		header := requestHeader{}
		close, err := header.read(r)
		if close {
			break
		}
		if err != nil {
			logError("read header error.", err)
			break
		}
		req := request{
			Header: header,
		}
		bizErr, ioErr := this.packetDispatching(req)
		if bizErr != nil {
			logError("packet dispatching occurs biz error. exit inbound loop.", err)
			this.release()
			readToEOF(r)
			break
		} else if ioErr != nil {
			logError("packet dispatching occurs io error. exit inbound loop.", err)
			break
		}
	}
}

func (this *child) outboundProcessor() {
	//TODO
}

func (this *child) packetDispatching(req request) (error, error) {
	//TODO
	if req.Header.getRequestId() == 0 {
		t := req.Header.Type
		ok := checkIsManagingRequest(t)
		if !ok {
			logError("requestId 0 with non-managing request")
		}
		//TODO we don't support managing request now, send unknow type packet
	}
	return nil, nil
}
<|endoftext|>