<commit_before>// Copyright 2018 Google LLC. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package kms

import (
	"context"
	"log"

	"cloud.google.com/go/iam"
	cloudkms "cloud.google.com/go/kms/apiv1"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
	fieldmask "google.golang.org/genproto/protobuf/field_mask"
)

// [START kms_create_keyring]
// createKeyRing creates a new ring to store keys on KMS.
// example parentName: "projects/PROJECT_ID/locations/global/"
func createKeyRing(parentName, keyRingId string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.CreateKeyRingRequest{
		Parent:    parentName,
		KeyRingId: keyRingId,
	}
	// Call the API.
	result, err := client.CreateKeyRing(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Created key ring: %s", result)
	return nil
}

// [END kms_create_keyring]

// [START kms_create_cryptokey]
// createCryptoKey creates a new symmetric encrypt/decrypt key on KMS.
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func createCryptoKey(keyRingName, keyId string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Build the request.
	req := &kmspb.CreateCryptoKeyRequest{
		Parent:      keyRingName,
		CryptoKeyId: keyId,
		CryptoKey: &kmspb.CryptoKey{
			Purpose: kmspb.CryptoKey_ENCRYPT_DECRYPT,
			VersionTemplate: &kmspb.CryptoKeyVersionTemplate{
				Algorithm: kmspb.CryptoKeyVersion_GOOGLE_SYMMETRIC_ENCRYPTION,
			},
		},
	}
	// Call the API.
	result, err := client.CreateCryptoKey(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Created crypto key. %s", result)
	return nil
}

// [END kms_create_cryptokey]

// [START kms_disable_cryptokey_version]
// disableCryptoKeyVersion disables a specified key version on KMS.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func disableCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.UpdateCryptoKeyVersionRequest{
		CryptoKeyVersion: &kmspb.CryptoKeyVersion{
			Name:  keyVersionName,
			State: kmspb.CryptoKeyVersion_DISABLED,
		},
		UpdateMask: &fieldmask.FieldMask{
			Paths: []string{"state"},
		},
	}
	// Call the API.
	result, err := client.UpdateCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Disabled crypto key version: %s", result)
	return nil
}

// [END kms_disable_cryptokey_version]

// [START kms_enable_cryptokey_version]
// enableCryptoKeyVersion enables a previously disabled key version on KMS.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func enableCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.UpdateCryptoKeyVersionRequest{
		CryptoKeyVersion: &kmspb.CryptoKeyVersion{
			Name:  keyVersionName,
			State: kmspb.CryptoKeyVersion_ENABLED,
		},
		UpdateMask: &fieldmask.FieldMask{
			Paths: []string{"state"},
		},
	}
	// Call the API.
	result, err := client.UpdateCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Enabled crypto key version: %s", result)
	return nil
}

// [END kms_enable_cryptokey_version]

// [START kms_destroy_cryptokey_version]
// destroyCryptoKeyVersion marks a specified key version for deletion. The key can be restored if requested within 24 hours.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func destroyCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.DestroyCryptoKeyVersionRequest{
		Name: keyVersionName,
	}
	// Call the API.
	result, err := client.DestroyCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Destroyed crypto key version: %s", result)
	return nil
}

// [END kms_destroy_cryptokey_version]

// [START kms_restore_cryptokey_version]
// restoreCryptoKeyVersion attempts to recover a key that has been marked for destruction within the last 24 hours.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func restoreCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.RestoreCryptoKeyVersionRequest{
		Name: keyVersionName,
	}
	// Call the API.
	result, err := client.RestoreCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Restored crypto key version: %s", result)
	return nil
}

// [END kms_restore_cryptokey_version]

// [START kms_get_keyring_policy]
// getRingPolicy retrieves and prints the IAM policy associated with the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func getRingPolicy(keyRingName string) (*iam.Policy, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return nil, err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return nil, err
	}
	for _, role := range policy.Roles() {
		for _, member := range policy.Members(role) {
			log.Printf("Role: %s Member: %s\n", role, member)
		}
	}
	return policy, nil
}

// [END kms_get_keyring_policy]

// getCryptoKeyPolicy retrieves and prints the IAM policy associated with the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func getCryptoKeyPolicy(keyName string) (*iam.Policy, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Get the KeyRing.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return nil, err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return nil, err
	}
	for _, role := range policy.Roles() {
		for _, member := range policy.Members(role) {
			log.Printf("Role: %s Member: %s\n", role, member)
		}
	}
	return policy, nil
}

// [START kms_add_member_to_keyring_policy]
// addMemberRingPolicy adds a new member to a specified IAM role for the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func addMemberRingPolicy(keyRingName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Add Member.
	policy.Add(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Added member to keyring policy.")
	return nil
}

// [END kms_add_member_to_keyring_policy]

// removeMemberRingPolicy removes a specified member from an IAM role for the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func removeMemberRingPolicy(keyRingName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}

	// Remove Member.
	policy.Remove(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Removed member from keyring policy.")
	return nil
}

// [START kms_add_member_to_cryptokey_policy]
// addMemberCryptoKeyPolicy adds a new member to a specified IAM role for the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func addMemberCryptoKeyPolicy(keyName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the desired CryptoKey.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Add Member.
	policy.Add(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Added member to cryptokey policy.")
	return nil
}

// [END kms_add_member_to_cryptokey_policy]

// [START kms_remove_member_from_cryptokey_policy]
// removeMemberCryptoKeyPolicy removes a specified member from an IAM role for the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func removeMemberCryptoKeyPolicy(keyName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the desired CryptoKey.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Remove Member.
	policy.Remove(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Removed member from cryptokey policy.")
	return nil
}

// [END kms_remove_member_from_cryptokey_policy]

// [START kms_encrypt]
// encrypt will encrypt the input plaintext with the specified symmetric key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func encryptSymmetric(keyName string, plaintext []byte) ([]byte, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}

	// Build the request.
	req := &kmspb.EncryptRequest{
		Name:      keyName,
		Plaintext: plaintext,
	}
	// Call the API.
	resp, err := client.Encrypt(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp.Ciphertext, nil
}

// [END kms_encrypt]

// [START kms_decrypt]
// decrypt will decrypt the input ciphertext bytes using the specified symmetric key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func decryptSymmetric(keyName string, ciphertext []byte) ([]byte, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}

	// Build the request.
	req := &kmspb.DecryptRequest{
		Name:       keyName,
		Ciphertext: ciphertext,
	}
	// Call the API.
	resp, err := client.Decrypt(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp.Plaintext, nil
}

// [END kms_decrypt]
<commit_msg>kms: add 2 region tags (#727)<commit_after>// Copyright 2018 Google LLC. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package kms

import (
	"context"
	"log"

	"cloud.google.com/go/iam"
	cloudkms "cloud.google.com/go/kms/apiv1"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
	fieldmask "google.golang.org/genproto/protobuf/field_mask"
)

// [START kms_create_keyring]

// createKeyRing creates a new ring to store keys on KMS.
// example parentName: "projects/PROJECT_ID/locations/global/"
func createKeyRing(parentName, keyRingId string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.CreateKeyRingRequest{
		Parent:    parentName,
		KeyRingId: keyRingId,
	}
	// Call the API.
	result, err := client.CreateKeyRing(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Created key ring: %s", result)
	return nil
}

// [END kms_create_keyring]

// [START kms_create_cryptokey]

// createCryptoKey creates a new symmetric encrypt/decrypt key on KMS.
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func createCryptoKey(keyRingName, keyId string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Build the request.
	req := &kmspb.CreateCryptoKeyRequest{
		Parent:      keyRingName,
		CryptoKeyId: keyId,
		CryptoKey: &kmspb.CryptoKey{
			Purpose: kmspb.CryptoKey_ENCRYPT_DECRYPT,
			VersionTemplate: &kmspb.CryptoKeyVersionTemplate{
				Algorithm: kmspb.CryptoKeyVersion_GOOGLE_SYMMETRIC_ENCRYPTION,
			},
		},
	}
	// Call the API.
	result, err := client.CreateCryptoKey(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Created crypto key. %s", result)
	return nil
}

// [END kms_create_cryptokey]

// [START kms_disable_cryptokey_version]

// disableCryptoKeyVersion disables a specified key version on KMS.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func disableCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.UpdateCryptoKeyVersionRequest{
		CryptoKeyVersion: &kmspb.CryptoKeyVersion{
			Name:  keyVersionName,
			State: kmspb.CryptoKeyVersion_DISABLED,
		},
		UpdateMask: &fieldmask.FieldMask{
			Paths: []string{"state"},
		},
	}
	// Call the API.
	result, err := client.UpdateCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Disabled crypto key version: %s", result)
	return nil
}

// [END kms_disable_cryptokey_version]

// [START kms_enable_cryptokey_version]

// enableCryptoKeyVersion enables a previously disabled key version on KMS.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func enableCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.UpdateCryptoKeyVersionRequest{
		CryptoKeyVersion: &kmspb.CryptoKeyVersion{
			Name:  keyVersionName,
			State: kmspb.CryptoKeyVersion_ENABLED,
		},
		UpdateMask: &fieldmask.FieldMask{
			Paths: []string{"state"},
		},
	}
	// Call the API.
	result, err := client.UpdateCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Enabled crypto key version: %s", result)
	return nil
}

// [END kms_enable_cryptokey_version]

// [START kms_destroy_cryptokey_version]

// destroyCryptoKeyVersion marks a specified key version for deletion. The key can be restored if requested within 24 hours.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func destroyCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.DestroyCryptoKeyVersionRequest{
		Name: keyVersionName,
	}
	// Call the API.
	result, err := client.DestroyCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Destroyed crypto key version: %s", result)
	return nil
}

// [END kms_destroy_cryptokey_version]

// [START kms_restore_cryptokey_version]

// restoreCryptoKeyVersion attempts to recover a key that has been marked for destruction within the last 24 hours.
// example keyVersionName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func restoreCryptoKeyVersion(keyVersionName string) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}
	// Build the request.
	req := &kmspb.RestoreCryptoKeyVersionRequest{
		Name: keyVersionName,
	}
	// Call the API.
	result, err := client.RestoreCryptoKeyVersion(ctx, req)
	if err != nil {
		return err
	}
	log.Printf("Restored crypto key version: %s", result)
	return nil
}

// [END kms_restore_cryptokey_version]

// [START kms_get_keyring_policy]

// getRingPolicy retrieves and prints the IAM policy associated with the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func getRingPolicy(keyRingName string) (*iam.Policy, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return nil, err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return nil, err
	}
	for _, role := range policy.Roles() {
		for _, member := range policy.Members(role) {
			log.Printf("Role: %s Member: %s\n", role, member)
		}
	}
	return policy, nil
}

// [END kms_get_keyring_policy]

// [START kms_get_cryptokey_policy]

// getCryptoKeyPolicy retrieves and prints the IAM policy associated with the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func getCryptoKeyPolicy(keyName string) (*iam.Policy, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Get the KeyRing.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return nil, err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return nil, err
	}
	for _, role := range policy.Roles() {
		for _, member := range policy.Members(role) {
			log.Printf("Role: %s Member: %s\n", role, member)
		}
	}
	return policy, nil
}

// [END kms_get_cryptokey_policy]

// [START kms_add_member_to_keyring_policy]

// addMemberRingPolicy adds a new member to a specified IAM role for the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func addMemberRingPolicy(keyRingName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Add Member.
	policy.Add(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Added member to keyring policy.")
	return nil
}

// [END kms_add_member_to_keyring_policy]

// [START kms_remove_member_from_keyring_policy]

// removeMemberRingPolicy removes a specified member from an IAM role for the key ring
// example keyRingName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID"
func removeMemberRingPolicy(keyRingName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the KeyRing.
	keyRingObj, err := client.GetKeyRing(ctx, &kmspb.GetKeyRingRequest{Name: keyRingName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.KeyRingIAM(keyRingObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}

	// Remove Member.
	policy.Remove(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Removed member from keyring policy.")
	return nil
}

// [END kms_remove_member_from_keyring_policy]

// [START kms_add_member_to_cryptokey_policy]

// addMemberCryptoKeyPolicy adds a new member to a specified IAM role for the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func addMemberCryptoKeyPolicy(keyName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the desired CryptoKey.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Add Member.
	policy.Add(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Added member to cryptokey policy.")
	return nil
}

// [END kms_add_member_to_cryptokey_policy]

// [START kms_remove_member_from_cryptokey_policy]

// removeMemberCryptoKeyPolicy removes a specified member from an IAM role for the key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func removeMemberCryptoKeyPolicy(keyName, member string, role iam.RoleName) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Get the desired CryptoKey.
	keyObj, err := client.GetCryptoKey(ctx, &kmspb.GetCryptoKeyRequest{Name: keyName})
	if err != nil {
		return err
	}
	// Get IAM Policy.
	handle := client.CryptoKeyIAM(keyObj)
	policy, err := handle.Policy(ctx)
	if err != nil {
		return err
	}
	// Remove Member.
	policy.Remove(member, role)
	err = handle.SetPolicy(ctx, policy)
	if err != nil {
		return err
	}
	log.Print("Removed member from cryptokey policy.")
	return nil
}

// [END kms_remove_member_from_cryptokey_policy]

// [START kms_encrypt]

// encrypt will encrypt the input plaintext with the specified symmetric key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func encryptSymmetric(keyName string, plaintext []byte) ([]byte, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}

	// Build the request.
	req := &kmspb.EncryptRequest{
		Name:      keyName,
		Plaintext: plaintext,
	}
	// Call the API.
	resp, err := client.Encrypt(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp.Ciphertext, nil
}

// [END kms_encrypt]

// [START kms_decrypt]

// decrypt will decrypt the input ciphertext bytes using the specified symmetric key
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID"
func decryptSymmetric(keyName string, ciphertext []byte) ([]byte, error) {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}

	// Build the request.
	req := &kmspb.DecryptRequest{
		Name:       keyName,
		Ciphertext: ciphertext,
	}
	// Call the API.
	resp, err := client.Decrypt(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp.Plaintext, nil
}

// [END kms_decrypt]
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// Package eks implements 'kubetest' deployer interface.
// It uses 'aws-k8s-tester' and 'kubectl' binaries, rather than importing internal packages.
// All underlying implementation and external dependencies are compiled into one binary.
package eks

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"

	"github.com/aws/aws-k8s-tester/eksconfig"
	"github.com/aws/aws-k8s-tester/ekstester"

	"k8s.io/test-infra/kubetest/process"
	"k8s.io/test-infra/kubetest/util"
)

// deployer implements EKS deployer interface using "aws-k8s-tester" binary.
// Satisfies "k8s.io/test-infra/kubetest/main.go" 'deployer' and 'publisher" interfaces.
// Reference https://github.com/kubernetes/test-infra/blob/master/kubetest/main.go.
type deployer struct {
	stopc            chan struct{}
	cfg              *eksconfig.Config
	awsK8sTesterPath string
	kubectlPath      string
	ctrl             *process.Control
}

// NewDeployer creates a new EKS deployer.
func NewDeployer(timeout time.Duration, verbose bool) (ekstester.Deployer, error) {
	cfg := eksconfig.NewDefault()
	err := cfg.UpdateFromEnvs()
	if err != nil {
		return nil, err
	}
	var f *os.File
	f, err = ioutil.TempFile(os.TempDir(), "aws-k8s-tester")
	if err != nil {
		return nil, err
	}
	outputPath := f.Name()
	f.Close()
	cfg.ConfigPath, err = filepath.Abs(outputPath)
	if err != nil {
		return nil, err
	}
	if err = cfg.Sync(); err != nil {
		return nil, err
	}

	dp := &deployer{
		stopc: make(chan struct{}),
		cfg:   cfg,
		ctrl: process.NewControl(
			timeout,
			time.NewTimer(timeout),
			time.NewTimer(timeout),
			verbose,
		),
	}

	dp.awsK8sTesterPath, err = exec.LookPath("aws-k8s-tester")
	if err != nil {
		dp.awsK8sTesterPath = filepath.Join(os.TempDir(), "aws-k8s-tester")
		var f *os.File
		f, err = os.Create(dp.awsK8sTesterPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create %q (%v)", dp.awsK8sTesterPath, err)
		}
		defer f.Close()
		if err = httpRead(cfg.AWSK8sTesterDownloadURL, f); err != nil {
			return nil, err
		}
		if err = util.EnsureExecutable(dp.awsK8sTesterPath); err != nil {
			return nil, err
		}
	}

	dp.kubectlPath, err = exec.LookPath("kubectl")
	if err != nil {
		return nil, fmt.Errorf("cannot find 'kubectl' executable (%v)", err)
	}

	// TODO(gyuho): replace this kubernetes native Go client
	_, err = exec.LookPath("aws-iam-authenticator")
	if err != nil {
		bin := filepath.Join(os.TempDir(), "aws-iam-authenticator")
		var f *os.File
		f, err = os.Create(bin)
		if err != nil {
			return nil, fmt.Errorf("failed to create %q (%v)", bin, err)
		}
		defer f.Close()
		if err = httpRead(cfg.AWSIAMAuthenticatorDownloadURL, f); err != nil {
			return nil, err
		}
		if err = util.EnsureExecutable(bin); err != nil {
			return nil, err
		}
		if err = os.Rename(bin, "/usr/local/bin/aws-iam-authenticator"); err != nil {
			return nil, err
		}
	}

	return dp, nil
}

// Up creates a new EKS cluster.
func (dp *deployer) Up() (err error) {
	// "create cluster" command outputs cluster information
	// in the configuraion file (e.g. VPC ID, ALB DNS names, etc.)
	// this needs be reloaded for other deployer method calls
	createCmd := exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"create",
		"cluster",
	)
	errc := make(chan error)
	go func() {
		_, oerr := dp.ctrl.Output(createCmd)
		errc <- oerr
	}()
	select {
	case <-dp.stopc:
		fmt.Fprintln(os.Stderr, "received stop signal, interrupting 'create cluster' command...")
		ierr := createCmd.Process.Signal(syscall.SIGINT)
		err = fmt.Errorf("'create cluster' command interrupted (interrupt error %v)", ierr)
	case err = <-errc:
	}
	return err
}

// Down tears down the existing EKS cluster.
func (dp *deployer) Down() (err error) {
	defer func() {
		os.RemoveAll(dp.awsK8sTesterPath)
		os.RemoveAll("/usr/local/bin/aws-iam-authenticator")
	}()
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"delete",
		"cluster",
	))
	return err
}

// IsUp returns an error if the cluster is not up and running.
func (dp *deployer) IsUp() (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"check",
		"cluster",
	))
	if err != nil {
		return err
	}
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	if dp.cfg.ClusterState.Status != "ACTIVE" {
		return fmt.Errorf("cluster %q status is %q",
			dp.cfg.ClusterName,
			dp.cfg.ClusterState.Status,
		)
	}
	return nil
}

// TestSetup checks if EKS testing cluster has been set up or not.
func (dp *deployer) TestSetup() error {
	return dp.IsUp()
}

// GetClusterCreated returns EKS cluster creation time and error (if any).
func (dp *deployer) GetClusterCreated(v string) (time.Time, error) {
	err := dp.IsUp()
	if err != nil {
		return time.Time{}, err
	}
	return dp.cfg.ClusterState.Created, nil
}

func (dp *deployer) GetWorkerNodeLogs() (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "get-worker-node-logs",
	))
	return err
}

// DumpClusterLogs dumps all logs to artifact directory.
// Let default kubetest log dumper handle all artifact uploads.
// See https://github.com/kubernetes/test-infra/pull/9811/files#r225776067.
func (dp *deployer) DumpClusterLogs(artifactDir, _ string) (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "get-worker-node-logs",
	))
	if err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "dump-cluster-logs",
		artifactDir,
	))
	return err
}

// Stop stops ongoing operations.
// This is useful for local development.
// For example, one may run "Up" but have to cancel onging "Up"
// operation. Then, it can just send syscall.SIGINT to trigger "Stop".
func (dp *deployer) Stop() {
	close(dp.stopc)
}

// LoadConfig reloads configuration from disk to read the latest
// cluster configuration and its states.
func (dp *deployer) LoadConfig() (eksconfig.Config, error) {
	var err error
	dp.cfg, err = eksconfig.Load(dp.cfg.ConfigPath)
	return *dp.cfg, err
}

var httpTransport *http.Transport

func init() {
	httpTransport = new(http.Transport)
	httpTransport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
}

// curl -L [URL] | writer
func httpRead(u string, wr io.Writer) error {
	log.Printf("curl %s", u)
	cli := &http.Client{Transport: httpTransport}
	r, err := cli.Get(u)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode >= 400 {
		return fmt.Errorf("%v returned %d", u, r.StatusCode)
	}
	_, err = io.Copy(wr, r.Body)
	return err
}
<commit_msg>kubetest/eks: do not delete binaries on "Down" call<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// Package eks implements 'kubetest' deployer interface.
// It uses 'aws-k8s-tester' and 'kubectl' binaries, rather than importing internal packages.
// All underlying implementation and external dependencies are compiled into one binary.
package eks

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"

	"github.com/aws/aws-k8s-tester/eksconfig"
	"github.com/aws/aws-k8s-tester/ekstester"

	"k8s.io/test-infra/kubetest/process"
	"k8s.io/test-infra/kubetest/util"
)

// deployer implements EKS deployer interface using "aws-k8s-tester" binary.
// Satisfies "k8s.io/test-infra/kubetest/main.go" 'deployer' and 'publisher" interfaces.
// Reference https://github.com/kubernetes/test-infra/blob/master/kubetest/main.go.
type deployer struct {
	stopc            chan struct{}
	cfg              *eksconfig.Config
	awsK8sTesterPath string
	kubectlPath      string
	ctrl             *process.Control
}

// NewDeployer creates a new EKS deployer.
func NewDeployer(timeout time.Duration, verbose bool) (ekstester.Deployer, error) {
	cfg := eksconfig.NewDefault()
	err := cfg.UpdateFromEnvs()
	if err != nil {
		return nil, err
	}
	var f *os.File
	f, err = ioutil.TempFile(os.TempDir(), "aws-k8s-tester")
	if err != nil {
		return nil, err
	}
	outputPath := f.Name()
	f.Close()
	cfg.ConfigPath, err = filepath.Abs(outputPath)
	if err != nil {
		return nil, err
	}
	if err = cfg.Sync(); err != nil {
		return nil, err
	}

	dp := &deployer{
		stopc: make(chan struct{}),
		cfg:   cfg,
		ctrl: process.NewControl(
			timeout,
			time.NewTimer(timeout),
			time.NewTimer(timeout),
			verbose,
		),
	}

	dp.awsK8sTesterPath, err = exec.LookPath("aws-k8s-tester")
	if err != nil {
		dp.awsK8sTesterPath = filepath.Join(os.TempDir(), "aws-k8s-tester")
		var f *os.File
		f, err = os.Create(dp.awsK8sTesterPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create %q (%v)", dp.awsK8sTesterPath, err)
		}
		defer f.Close()
		if err = httpRead(cfg.AWSK8sTesterDownloadURL, f); err != nil {
			return nil, err
		}
		if err = util.EnsureExecutable(dp.awsK8sTesterPath); err != nil {
			return nil, err
		}
	}

	dp.kubectlPath, err = exec.LookPath("kubectl")
	if err != nil {
		return nil, fmt.Errorf("cannot find 'kubectl' executable (%v)", err)
	}

	// TODO(gyuho): replace this kubernetes native Go client
	_, err = exec.LookPath("aws-iam-authenticator")
	if err != nil {
		bin := filepath.Join(os.TempDir(), "aws-iam-authenticator")
		var f *os.File
		f, err = os.Create(bin)
		if err != nil {
			return nil, fmt.Errorf("failed to create %q (%v)", bin, err)
		}
		defer f.Close()
		if err = httpRead(cfg.AWSIAMAuthenticatorDownloadURL, f); err != nil {
			return nil, err
		}
		if err = util.EnsureExecutable(bin); err != nil {
			return nil, err
		}
		if err = os.Rename(bin, "/usr/local/bin/aws-iam-authenticator"); err != nil {
			return nil, err
		}
	}

	return dp, nil
}

// Up creates a new EKS cluster.
func (dp *deployer) Up() (err error) {
	// "create cluster" command outputs cluster information
	// in the configuraion file (e.g. VPC ID, ALB DNS names, etc.)
	// this needs be reloaded for other deployer method calls
	createCmd := exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"create",
		"cluster",
	)
	errc := make(chan error)
	go func() {
		_, oerr := dp.ctrl.Output(createCmd)
		errc <- oerr
	}()
	select {
	case <-dp.stopc:
		fmt.Fprintln(os.Stderr, "received stop signal, interrupting 'create cluster' command...")
		ierr := createCmd.Process.Signal(syscall.SIGINT)
		err = fmt.Errorf("'create cluster' command interrupted (interrupt error %v)", ierr)
	case err = <-errc:
	}
	return err
}

// Down tears down the existing EKS cluster.
func (dp *deployer) Down() (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"delete",
		"cluster",
	))
	return err
}

// IsUp returns an error if the cluster is not up and running.
func (dp *deployer) IsUp() (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"check",
		"cluster",
	))
	if err != nil {
		return err
	}
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	if dp.cfg.ClusterState.Status != "ACTIVE" {
		return fmt.Errorf("cluster %q status is %q",
			dp.cfg.ClusterName,
			dp.cfg.ClusterState.Status,
		)
	}
	return nil
}

// TestSetup checks if EKS testing cluster has been set up or not.
func (dp *deployer) TestSetup() error {
	return dp.IsUp()
}

// GetClusterCreated returns EKS cluster creation time and error (if any).
func (dp *deployer) GetClusterCreated(v string) (time.Time, error) {
	err := dp.IsUp()
	if err != nil {
		return time.Time{}, err
	}
	return dp.cfg.ClusterState.Created, nil
}

func (dp *deployer) GetWorkerNodeLogs() (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "get-worker-node-logs",
	))
	return err
}

// DumpClusterLogs dumps all logs to artifact directory.
// Let default kubetest log dumper handle all artifact uploads.
// See https://github.com/kubernetes/test-infra/pull/9811/files#r225776067.
func (dp *deployer) DumpClusterLogs(artifactDir, _ string) (err error) {
	// reload configuration from disk to read the latest configuration
	if _, err = dp.LoadConfig(); err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "get-worker-node-logs",
	))
	if err != nil {
		return err
	}
	_, err = dp.ctrl.Output(exec.Command(
		dp.awsK8sTesterPath,
		"eks",
		"--path="+dp.cfg.ConfigPath,
		"test", "dump-cluster-logs",
		artifactDir,
	))
	return err
}

// Stop stops ongoing operations.
// This is useful for local development.
// For example, one may run "Up" but have to cancel onging "Up"
// operation. Then, it can just send syscall.SIGINT to trigger "Stop".
func (dp *deployer) Stop() {
	close(dp.stopc)
}

// LoadConfig reloads configuration from disk to read the latest
// cluster configuration and its states.
func (dp *deployer) LoadConfig() (eksconfig.Config, error) {
	var err error
	dp.cfg, err = eksconfig.Load(dp.cfg.ConfigPath)
	return *dp.cfg, err
}

var httpTransport *http.Transport

func init() {
	httpTransport = new(http.Transport)
	httpTransport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
}

// curl -L [URL] | writer
func httpRead(u string, wr io.Writer) error {
	log.Printf("curl %s", u)
	cli := &http.Client{Transport: httpTransport}
	r, err := cli.Get(u)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode >= 400 {
		return fmt.Errorf("%v returned %d", u, r.StatusCode)
	}
	_, err = io.Copy(wr, r.Body)
	return err
}
<|endoftext|>


<commit_before>package labyrinth

import "github.com/hajimehoshi/ebiten"
import "time"

type wizard struct {
	image             *ebiten.Image
	imageShoot        *ebiten.Image
	shooting          bool
	TopLeft           *coord
	moveSpeed         int
	fireballs         []*fireball
	powerups          []*powerup
	fireCreator       *fireballCreator
	upKeys            []ebiten.Key
	downKeys          []ebiten.Key
	minPlayAreaHeight int
}

// newWizard returns an initialized wizard
func newWizard(i *ebiten.Image, is *ebiten.Image, speed int, fbc *fireballCreator, upkeys, downkeys []ebiten.Key, minPlayAreaHeight int) *wizard {
	c := &coord{
		x: 50,
		y: minPlayAreaHeight,
	}

	return &wizard{
		image:             i,
		imageShoot:        is,
		TopLeft:           c,
		moveSpeed:         speed,
		fireCreator:       fbc,
		upKeys:            upkeys,
		downKeys:          downkeys,
		minPlayAreaHeight: minPlayAreaHeight,
		powerups:          []*powerup{},
	}
}

func (w *wizard) Update(keys *KeyboardWrapper) error {
	movedUp := false
	for _, k := range w.upKeys {
		if keys.IsKeyPressed(k) && !movedUp {
			w.moveUp()
			movedUp = true
		}
	}

	movedDown := false
	for _, k := range w.downKeys {
		if keys.IsKeyPressed(k) && !movedDown {
			w.moveDown()
			movedDown = true
		}
	}

	if keys.IsKeyPressed(ebiten.KeySpace) {
		w.generateFireball()
	}

	w.updateFireballs()

	w.removePowerups()

	return nil
}

func (w *wizard) generateFireball() {
	if len(w.fireballs) == 0 {
		w.shooting = true
		go w.turnOffShooting(time.NewTimer(time.Millisecond * 100))
		// We cannot send a pointer to newFireball because then when we move
		// the fireball it will move the wizard! Comical, yes but not desired.

		if containsPowerupClass(w.powerups, branchFireballPowerup) {
			// branching fireballs are a bit different. we need three new fireballs,
			// one for up, center, and down.

			// center is just a standard one
			centerClass := w.getFireballClass(normalFireball, fastFireball, powerFireball, fastPowerFireball)
			centerF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), centerClass)
			w.fireballs = append(w.fireballs, centerF)

			// then up & down need their own special classes, so we can
			// use the right movement behavior
			upClass := w.getFireballClass(upFireball, fastUpFireball, powerUpFireball, fastPowerUpFireball)
			upF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), upClass)
			w.fireballs = append(w.fireballs, upF)

			downClass := w.getFireballClass(downFireball, fastDownFireball, powerDownFireball, fastPowerDownFireball)
			downF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), downClass)
			w.fireballs = append(w.fireballs, downF)

		} else {
			class := w.getFireballClass(normalFireball, fastFireball, powerFireball, fastPowerFireball)
			f := w.fireCreator.newFireball(*center(w.TopLeft, w.image), class)
			w.fireballs = append(w.fireballs, f)
		}
	}
}

// each parameter determines what should be returned when powerups of that type are found.
// this isn't necessarily expandible easily, but I don't see us having too many fireball types.
func (w *wizard) getFireballClass(normalClass fireballClass, fastClass fireballClass, powerClass fireballClass, fastPowerClass fireballClass) fireballClass {
	fast := containsPowerupClass(w.powerups, fastFireballPowerup)
	power := containsPowerupClass(w.powerups, powerFireballPowerup)

	if fast && power {
		return fastPowerClass
	} else if power {
		return powerClass
	} else if fast {
		return fastClass
	}
	return normalClass
}

func (w *wizard) updateFireballs() error {
	activeFireballs := []*fireball{}
	for _, f := range w.fireballs {
		if err := f.Update(); err != nil {
			return err
		}
		if f.active {
			activeFireballs = append(activeFireballs, f)
		}
	}

	// Only keep active fireballs in the array
	w.fireballs = activeFireballs

	return nil
}

func (w *wizard) removePowerups() error {
	activePowerups := []*powerup{}

	for _, p := range w.powerups {
		if p.active {
			activePowerups = append(activePowerups, p)
		}
	}

	w.powerups = activePowerups

	return nil
}

func (w *wizard) moveUp() {
	w.TopLeft.y -= getPoweredUpValue(w.moveSpeed, w.powerups, fastPlayerPowerup)
	if w.TopLeft.y <= w.minPlayAreaHeight {
		w.TopLeft.y = w.minPlayAreaHeight
	}
}

func (w *wizard) moveDown() {
	w.TopLeft.y += getPoweredUpValue(w.moveSpeed, w.powerups, fastPlayerPowerup)
	_, h := w.image.Size()
	if w.TopLeft.y+h >= ScreenHeight {
		w.TopLeft.y = ScreenHeight - h
	}
}

func (w *wizard) Draw(r *ebiten.Image) error {
	i := w.image
	if w.shooting {
		i = w.imageShoot
	}
	r.DrawImage(i, &ebiten.DrawImageOptions{
		ImageParts: w,
	})

	for _, f := range w.fireballs {
		if err := f.Draw(r); err != nil {
			return err
		}
	}

	return nil
}

func (w *wizard) Len() int {
	return 1
}

func (w *wizard) Dst(i int) (x0, y0, x1, y1 int) {
	return defaultDST(i, w.TopLeft, w.image)
}

func (w *wizard) Src(i int) (x0, y0, x1, y1 int) {
	width, height := w.image.Size()
	return 0, 0, width, height
}

func (w *wizard) activate(p *powerup) {
	w.powerups = append(w.powerups, p)
	p.Activate()
}

func (w *wizard) turnOffShooting(t *time.Timer) {
	<-t.C
	w.shooting = false
}
<commit_msg>as per art increase size of showing wizard shooting<commit_after>package labyrinth

import "github.com/hajimehoshi/ebiten"
import "time"

type wizard struct {
	image             *ebiten.Image
	imageShoot        *ebiten.Image
	shooting          bool
	TopLeft           *coord
	moveSpeed         int
	fireballs         []*fireball
	powerups          []*powerup
	fireCreator       *fireballCreator
	upKeys            []ebiten.Key
	downKeys          []ebiten.Key
	minPlayAreaHeight int
}

// newWizard returns an initialized wizard
func newWizard(i *ebiten.Image, is *ebiten.Image, speed int, fbc *fireballCreator, upkeys, downkeys []ebiten.Key, minPlayAreaHeight int) *wizard {
	c := &coord{
		x: 50,
		y: minPlayAreaHeight,
	}

	return &wizard{
		image:             i,
		imageShoot:        is,
		TopLeft:           c,
		moveSpeed:         speed,
		fireCreator:       fbc,
		upKeys:            upkeys,
		downKeys:          downkeys,
		minPlayAreaHeight: minPlayAreaHeight,
		powerups:          []*powerup{},
	}
}

func (w *wizard) Update(keys *KeyboardWrapper) error {
	movedUp := false
	for _, k := range w.upKeys {
		if keys.IsKeyPressed(k) && !movedUp {
			w.moveUp()
			movedUp = true
		}
	}

	movedDown := false
	for _, k := range w.downKeys {
		if keys.IsKeyPressed(k) && !movedDown {
			w.moveDown()
			movedDown = true
		}
	}

	if keys.IsKeyPressed(ebiten.KeySpace) {
		w.generateFireball()
	}

	w.updateFireballs()

	w.removePowerups()

	return nil
}

func (w *wizard) generateFireball() {
	if len(w.fireballs) == 0 {
		w.shooting = true
		go w.turnOffShooting(time.NewTimer(time.Millisecond * 300))
		// We cannot send a pointer to newFireball because then when we move
		// the fireball it will move the wizard! Comical, yes but not desired.

		if containsPowerupClass(w.powerups, branchFireballPowerup) {
			// branching fireballs are a bit different. we need three new fireballs,
			// one for up, center, and down.

			// center is just a standard one
			centerClass := w.getFireballClass(normalFireball, fastFireball, powerFireball, fastPowerFireball)
			centerF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), centerClass)
			w.fireballs = append(w.fireballs, centerF)

			// then up & down need their own special classes, so we can
			// use the right movement behavior
			upClass := w.getFireballClass(upFireball, fastUpFireball, powerUpFireball, fastPowerUpFireball)
			upF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), upClass)
			w.fireballs = append(w.fireballs, upF)

			downClass := w.getFireballClass(downFireball, fastDownFireball, powerDownFireball, fastPowerDownFireball)
			downF := w.fireCreator.newFireball(*center(w.TopLeft, w.image), downClass)
			w.fireballs = append(w.fireballs, downF)

		} else {
			class := w.getFireballClass(normalFireball, fastFireball, powerFireball, fastPowerFireball)
			f := w.fireCreator.newFireball(*center(w.TopLeft, w.image), class)
			w.fireballs = append(w.fireballs, f)
		}
	}
}

// each parameter determines what should be returned when powerups of that type are found.
// this isn't necessarily expandible easily, but I don't see us having too many fireball types.
func (w *wizard) getFireballClass(normalClass fireballClass, fastClass fireballClass, powerClass fireballClass, fastPowerClass fireballClass) fireballClass {
	fast := containsPowerupClass(w.powerups, fastFireballPowerup)
	power := containsPowerupClass(w.powerups, powerFireballPowerup)

	if fast && power {
		return fastPowerClass
	} else if power {
		return powerClass
	} else if fast {
		return fastClass
	}
	return normalClass
}

func (w *wizard) updateFireballs() error {
	activeFireballs := []*fireball{}
	for _, f := range w.fireballs {
		if err := f.Update(); err != nil {
			return err
		}
		if f.active {
			activeFireballs = append(activeFireballs, f)
		}
	}

	// Only keep active fireballs in the array
	w.fireballs = activeFireballs

	return nil
}

func (w *wizard) removePowerups() error {
	activePowerups := []*powerup{}

	for _, p := range w.powerups {
		if p.active {
			activePowerups = append(activePowerups, p)
		}
	}

	w.powerups = activePowerups

	return nil
}

func (w *wizard) moveUp() {
	w.TopLeft.y -= getPoweredUpValue(w.moveSpeed, w.powerups, fastPlayerPowerup)
	if w.TopLeft.y <= w.minPlayAreaHeight {
		w.TopLeft.y = w.minPlayAreaHeight
	}
}

func (w *wizard) moveDown() {
	w.TopLeft.y += getPoweredUpValue(w.moveSpeed, w.powerups, fastPlayerPowerup)
	_, h := w.image.Size()
	if w.TopLeft.y+h >= ScreenHeight {
		w.TopLeft.y = ScreenHeight - h
	}
}

func (w *wizard) Draw(r *ebiten.Image) error {
	i := w.image
	if w.shooting {
		i = w.imageShoot
	}
	r.DrawImage(i, &ebiten.DrawImageOptions{
		ImageParts: w,
	})

	for _, f := range w.fireballs {
		if err := f.Draw(r); err != nil {
			return err
		}
	}

	return nil
}

func (w *wizard) Len() int {
	return 1
}

func (w *wizard) Dst(i int) (x0, y0, x1, y1 int) {
	return defaultDST(i, w.TopLeft, w.image)
}

func (w *wizard) Src(i int) (x0, y0, x1, y1 int) {
	width, height := w.image.Size()
	return 0, 0, width, height
}

func (w *wizard) activate(p *powerup) {
	w.powerups = append(w.powerups, p)
	p.Activate()
}

func (w *wizard) turnOffShooting(t *time.Timer) {
	<-t.C
	w.shooting = false
}
<|endoftext|>


<commit_before>package storage

import (
	"errors"
	"io"
	"os"
	"path"

	"github.com/anacrolix/missinggo"

	"github.com/anacrolix/torrent/metainfo"
)

type pieceFileStorage struct {
	fs missinggo.FileStore
}

func NewPieceFileStorage(fs missinggo.FileStore) I {
	return &pieceFileStorage{
		fs: fs,
	}
}

type pieceFileTorrentStorage struct {
	s *pieceFileStorage
}

func (me *pieceFileStorage) OpenTorrent(info *metainfo.InfoEx) (Torrent, error) {
	return &pieceFileTorrentStorage{me}, nil
}

func (me *pieceFileTorrentStorage) Close() error {
	return nil
}

func (me *pieceFileTorrentStorage) Piece(p metainfo.Piece) Piece {
	return pieceFileTorrentStoragePiece{me, p, me.s.fs}
}

type pieceFileTorrentStoragePiece struct {
	ts *pieceFileTorrentStorage
	p  metainfo.Piece
	fs missinggo.FileStore
}

func (me pieceFileTorrentStoragePiece) completedPath() string {
	return path.Join("completed", me.p.Hash().HexString())
}

func (me pieceFileTorrentStoragePiece) incompletePath() string {
	return path.Join("incomplete", me.p.Hash().HexString())
}

func (me pieceFileTorrentStoragePiece) GetIsComplete() bool {
	fi, err := me.ts.s.fs.Stat(me.completedPath())
	return err == nil && fi.Size() == me.p.Length()
}

func (me pieceFileTorrentStoragePiece) MarkComplete() error {
	return me.fs.Rename(me.incompletePath(), me.completedPath())
}

func (me pieceFileTorrentStoragePiece) openFile() (f missinggo.File, err error) {
	f, err = me.fs.OpenFile(me.completedPath(), os.O_RDONLY)
	if err == nil {
		var fi os.FileInfo
		fi, err = f.Stat()
		if err == nil && fi.Size() == me.p.Length() {
			return
		}
		f.Close()
	} else if !os.IsNotExist(err) {
		return
	}
	f, err = me.fs.OpenFile(me.incompletePath(), os.O_RDONLY)
	if os.IsNotExist(err) {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (me pieceFileTorrentStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
	f, err := me.openFile()
	if err != nil {
		return
	}
	defer f.Close()
	missinggo.LimitLen(&b, me.p.Length()-off)
	n, err = f.ReadAt(b, off)
	off += int64(n)
	if off >= me.p.Length() {
		err = io.EOF
	} else if err == io.EOF {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (me pieceFileTorrentStoragePiece) WriteAt(b []byte, off int64) (n int, err error) {
	if me.GetIsComplete() {
		err = errors.New("piece completed")
		return
	}
	f, err := me.fs.OpenFile(me.incompletePath(), os.O_WRONLY|os.O_CREATE)
	if err != nil {
		return
	}
	defer f.Close()
	missinggo.LimitLen(&b, me.p.Length()-off)
	return f.WriteAt(b, off)
}
<commit_msg>piece-file storage: Use the receiver-local FileStore field<commit_after>package storage

import (
	"errors"
	"io"
	"os"
	"path"

	"github.com/anacrolix/missinggo"

	"github.com/anacrolix/torrent/metainfo"
)

type pieceFileStorage struct {
	fs missinggo.FileStore
}

func NewPieceFileStorage(fs missinggo.FileStore) I {
	return &pieceFileStorage{
		fs: fs,
	}
}

type pieceFileTorrentStorage struct {
	s *pieceFileStorage
}

func (me *pieceFileStorage) OpenTorrent(info *metainfo.InfoEx) (Torrent, error) {
	return &pieceFileTorrentStorage{me}, nil
}

func (me *pieceFileTorrentStorage) Close() error {
	return nil
}

func (me *pieceFileTorrentStorage) Piece(p metainfo.Piece) Piece {
	return pieceFileTorrentStoragePiece{me, p, me.s.fs}
}

type pieceFileTorrentStoragePiece struct {
	ts *pieceFileTorrentStorage
	p  metainfo.Piece
	fs missinggo.FileStore
}

func (me pieceFileTorrentStoragePiece) completedPath() string {
	return path.Join("completed", me.p.Hash().HexString())
}

func (me pieceFileTorrentStoragePiece) incompletePath() string {
	return path.Join("incomplete", me.p.Hash().HexString())
}

func (me pieceFileTorrentStoragePiece) GetIsComplete() bool {
	fi, err := me.fs.Stat(me.completedPath())
	return err == nil && fi.Size() == me.p.Length()
}

func (me pieceFileTorrentStoragePiece) MarkComplete() error {
	return me.fs.Rename(me.incompletePath(), me.completedPath())
}

func (me pieceFileTorrentStoragePiece) openFile() (f missinggo.File, err error) {
	f, err = me.fs.OpenFile(me.completedPath(), os.O_RDONLY)
	if err == nil {
		var fi os.FileInfo
		fi, err = f.Stat()
		if err == nil && fi.Size() == me.p.Length() {
			return
		}
		f.Close()
	} else if !os.IsNotExist(err) {
		return
	}
	f, err = me.fs.OpenFile(me.incompletePath(), os.O_RDONLY)
	if os.IsNotExist(err) {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (me pieceFileTorrentStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
	f, err := me.openFile()
	if err != nil {
		return
	}
	defer f.Close()
	missinggo.LimitLen(&b, me.p.Length()-off)
	n, err = f.ReadAt(b, off)
	off += int64(n)
	if off >= me.p.Length() {
		err = io.EOF
	} else if err == io.EOF {
		err = io.ErrUnexpectedEOF
	}
	return
}

func (me pieceFileTorrentStoragePiece) WriteAt(b []byte, off int64) (n int, err error) {
	if me.GetIsComplete() {
		err = errors.New("piece completed")
		return
	}
	f, err := me.fs.OpenFile(me.incompletePath(), os.O_WRONLY|os.O_CREATE)
	if err != nil {
		return
	}
	defer f.Close()
	missinggo.LimitLen(&b, me.p.Length()-off)
	return f.WriteAt(b, off)
}
<|endoftext|>


<commit_before>package aws

import (
	logger "log"
	"strings"

	"github.com/pchico83/i2kit/cli/providers/aws/cf"
	"github.com/pchico83/i2kit/cli/providers/aws/elb"
	"github.com/pchico83/i2kit/cli/schemas/environment"
	"github.com/pchico83/i2kit/cli/schemas/service"
)

//Deploy deploys a AWS Cloud Formation stack
func Deploy(s *service.Service, e *environment.Environment, log *logger.Logger) error {
	consumed := 0
	stackName := s.GetFullName(e, "-")
	config := e.Provider.GetConfig()
	stack, err := cf.Get(stackName, config)
	if err != nil {
		return err
	}
	if stack != nil && (*stack.StackStatus == "ROLLBACK_COMPLETE" || strings.HasSuffix(*stack.StackStatus, "_FAILED")) {
		if err = Destroy(s, e, log); err != nil {
			return err
		}
		log.Printf("Destroying previous stack '%s' in '%s' state...", stackName, *stack.StackStatus)
		stack = nil
	}

	var stackID string
	var template string
	if stack == nil {
		template, err = Translate(s, e, config)
		if err != nil {
			return err
		}
		log.Printf("Creating stack '%s'...", stackName)
		if stackID, err = cf.Create(stackName, template, config); err != nil {
			return err
		}
	} else {
		template, err = Translate(s, e, config)
		if err != nil {
			return err
		}
		stackID = *stack.StackId
		consumed = cf.NumEvents(stackID, config)
		log.Printf("Updating the stack '%s'...", stackName)
		var updated bool
		updated, err = cf.Update(stackID, template, config)
		if err != nil {
			return err
		}
		if !updated {
			log.Printf("No updates are to be performed.")
		}
	}
	stack, err = cf.Get(stackID, config)
	if err != nil {
		return err
	}
	startTime := new(int64)
	*startTime = 0
	if stack.LastUpdatedTime != nil {
		*startTime = stack.LastUpdatedTime.Unix() * 1000
	}
	if err = cf.Watch(stackID, consumed, s, e, startTime, config, log); err != nil {
		return err
	}
	elbName, _ := cf.GetOutput(stackID, "elbName", config)
	if elbName != "" {
		if err := elb.Wait(s, elbName, config, log); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Better handling CF states (#86)<commit_after>package aws

import (
	"fmt"
	logger "log"

	"github.com/pchico83/i2kit/cli/providers/aws/cf"
	"github.com/pchico83/i2kit/cli/providers/aws/elb"
	"github.com/pchico83/i2kit/cli/schemas/environment"
	"github.com/pchico83/i2kit/cli/schemas/service"
)

//Deploy deploys a AWS Cloud Formation stack
func Deploy(s *service.Service, e *environment.Environment, log *logger.Logger) error {
	consumed := 0
	stackName := s.GetFullName(e, "-")
	config := e.Provider.GetConfig()
	stack, err := cf.Get(stackName, config)
	if err != nil {
		return err
	}
	if stack != nil && (*stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "DELETE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" || *stack.StackStatus == "ROLLBACK_FAILED") {
		if err = Destroy(s, e, log); err != nil {
			return err
		}
		log.Printf("Destroying previous stack '%s' in '%s' state...", stackName, *stack.StackStatus)
		stack = nil
	}
	if stack != nil && *stack.StackStatus == "UPDATE_ROLLBACK_FAILED" {
		log.Printf("Stack %s is in 'UPDATE_ROLLBACK_FAILED' state. Fix the stack in the AWS console or destroy this service to unlock it.", stackName)
		return fmt.Errorf("'UPDATE_ROLLBACK_FAILED' state")
	}

	var stackID string
	var template string
	if stack == nil {
		template, err = Translate(s, e, config)
		if err != nil {
			return err
		}
		log.Printf("Creating stack '%s'...", stackName)
		if stackID, err = cf.Create(stackName, template, config); err != nil {
			return err
		}
	} else {
		template, err = Translate(s, e, config)
		if err != nil {
			return err
		}
		stackID = *stack.StackId
		consumed = cf.NumEvents(stackID, config)
		log.Printf("Updating the stack '%s'...", stackName)
		var updated bool
		updated, err = cf.Update(stackID, template, config)
		if err != nil {
			return err
		}
		if !updated {
			log.Printf("No updates are to be performed.")
		}
	}
	stack, err = cf.Get(stackID, config)
	if err != nil {
		return err
	}
	startTime := new(int64)
	*startTime = 0
	if stack.LastUpdatedTime != nil {
		*startTime = stack.LastUpdatedTime.Unix() * 1000
	}
	if err = cf.Watch(stackID, consumed, s, e, startTime, config, log); err != nil {
		return err
	}
	elbName, _ := cf.GetOutput(stackID, "elbName", config)
	if elbName != "" {
		if err := elb.Wait(s, elbName, config, log); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package openstack implements openstack API specific
// helper functions.
package openstack

import (
	"os"

	"github.com/romana/core/cli/util"
	log "github.com/romana/rlog"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
	"github.com/gophercloud/gophercloud/pagination"
)

var (
	identityClient *gophercloud.ServiceClient
)

func getIdentityClient() (*gophercloud.ServiceClient, error) {
	var err error
	if identityClient == nil {
		identityClient, err = initIdentityClient()
		if err != nil {
			log.Println("Error: ", err)
			return nil, err
		}
	}
	return identityClient, nil
}

// initIdentityClient initializes openstack api using
// gophercloud which handles auth tokens keeping api calls
// simpler. Currently it uses environment variables for
// authenticating with openstack identity.
func initIdentityClient() (*gophercloud.ServiceClient, error) {
	opts, err := openstack.AuthOptionsFromEnv()
	if err != nil {
		log.Println("Error fetching openstack env vars: ", err)
		return nil, err
	}
	if opts.DomainID == "" && opts.DomainName == "" {
		opts.DomainName = os.Getenv("OS_PROJECT_DOMAIN_NAME")
	}
	provider, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		log.Println("Error authenticating with openstack: ", err)
		return nil, err
	}
	client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{
		Region: os.Getenv("OS_REGION_NAME"),
	})
	if err != nil {
		log.Println("Error creating identity client:", err)
		return nil, err
	}
	return client, nil
}

// GetTenantName returns openstack tenant name corresponding
// to the UUID being used in romana tenants.
func GetTenantName(uuid string) (string, error) {
	c, err := getIdentityClient()
	if err != nil {
		log.Println("Error getting Identity Client: ", err)
		return "", err
	}
	//	result := projects.Get(c, uuid, projects.GetOpts{})
	result := projects.Get(c, uuid)
	project, err := result.Extract()
	if err != nil {
		log.Printf("Error looking up name for uuid %s: %s.\n", uuid, err)
		return "", err
	}

	return project.Name, nil
}

// TenantExists returns true/false depending on
// openstack tenant name or uuid exists.
func TenantExists(uuidOrName string) bool {
	name, err := GetTenantName(uuidOrName)
	// intentional nil error check
	if err == nil {
		log.Printf("TenantExists: UUID %s resolved to name %s", uuidOrName, name)
		return true
	}
	uuid, err := GetTenantUUID(uuidOrName)
	if err == nil {
		log.Printf("TenantExists: name %s resolved to uuid %s", uuidOrName, uuid)
		return true
	}
	log.Printf("%s not found as uuid or name", uuidOrName)
	return false
}

// GetTenantUUID returns openstack tenant UUID
// corresponding to the given tenantName.
func GetTenantUUID(tenantName string) (string, error) {
	var uuid string

	c, err := getIdentityClient()
	if err != nil {
		log.Println("Error getting Identity Client: ", err)
		return "", err
	}

	pager := projects.List(c, projects.ListOpts{Name: tenantName})
	pager.EachPage(
		func(page pagination.Page) (bool, error) {
			projectList, _ := projects.ExtractProjects(page)
			for _, project := range projectList {
				if project.Name == tenantName {
					uuid = project.ID
					// stop iterating and return tenant.Name
					return false, nil
				}
			}
			return true, nil
		},
	)

	if uuid == "" {
		log.Printf("Tenant (Name: %s) not found.\n", tenantName)
		return "", util.ErrTenantNotFound
	}

	return uuid, nil
}

// CreateTenant creates openstack specific tenant
// corresponding to the name given.
func CreateTenant(name string) error {
	return util.ErrUnimplementedFeature
}
<commit_msg>cli: fix spurious commit.<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package openstack implements openstack API specific
// helper functions.
package openstack

import (
	"os"

	"github.com/romana/core/cli/util"
	log "github.com/romana/rlog"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/projects"
	"github.com/gophercloud/gophercloud/pagination"
)

var (
	identityClient *gophercloud.ServiceClient
)

func getIdentityClient() (*gophercloud.ServiceClient, error) {
	var err error
	if identityClient == nil {
		identityClient, err = initIdentityClient()
		if err != nil {
			log.Println("Error: ", err)
			return nil, err
		}
	}
	return identityClient, nil
}

// initIdentityClient initializes openstack api using
// gophercloud which handles auth tokens keeping api calls
// simpler. Currently it uses environment variables for
// authenticating with openstack identity.
func initIdentityClient() (*gophercloud.ServiceClient, error) {
	opts, err := openstack.AuthOptionsFromEnv()
	if err != nil {
		log.Println("Error fetching openstack env vars: ", err)
		return nil, err
	}
	if opts.DomainID == "" && opts.DomainName == "" {
		opts.DomainName = os.Getenv("OS_PROJECT_DOMAIN_NAME")
	}
	provider, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		log.Println("Error authenticating with openstack: ", err)
		return nil, err
	}
	client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{
		Region: os.Getenv("OS_REGION_NAME"),
	})
	if err != nil {
		log.Println("Error creating identity client:", err)
		return nil, err
	}
	return client, nil
}

// GetTenantName returns openstack tenant name corresponding
// to the UUID being used in romana tenants.
func GetTenantName(uuid string) (string, error) {
	c, err := getIdentityClient()
	if err != nil {
		log.Println("Error getting Identity Client: ", err)
		return "", err
	}
	result := projects.Get(c, uuid, projects.GetOpts{})
	project, err := result.Extract()
	if err != nil {
		log.Printf("Error looking up name for uuid %s: %s.\n", uuid, err)
		return "", err
	}

	return project.Name, nil
}

// TenantExists returns true/false depending on
// openstack tenant name or uuid exists.
func TenantExists(uuidOrName string) bool {
	name, err := GetTenantName(uuidOrName)
	// intentional nil error check
	if err == nil {
		log.Printf("TenantExists: UUID %s resolved to name %s", uuidOrName, name)
		return true
	}
	uuid, err := GetTenantUUID(uuidOrName)
	if err == nil {
		log.Printf("TenantExists: name %s resolved to uuid %s", uuidOrName, uuid)
		return true
	}
	log.Printf("%s not found as uuid or name", uuidOrName)
	return false
}

// GetTenantUUID returns openstack tenant UUID
// corresponding to the given tenantName.
func GetTenantUUID(tenantName string) (string, error) {
	var uuid string

	c, err := getIdentityClient()
	if err != nil {
		log.Println("Error getting Identity Client: ", err)
		return "", err
	}

	pager := projects.List(c, projects.ListOpts{Name: tenantName})
	pager.EachPage(
		func(page pagination.Page) (bool, error) {
			projectList, _ := projects.ExtractProjects(page)
			for _, project := range projectList {
				if project.Name == tenantName {
					uuid = project.ID
					// stop iterating and return tenant.Name
					return false, nil
				}
			}
			return true, nil
		},
	)

	if uuid == "" {
		log.Printf("Tenant (Name: %s) not found.\n", tenantName)
		return "", util.ErrTenantNotFound
	}

	return uuid, nil
}

// CreateTenant creates openstack specific tenant
// corresponding to the name given.
func CreateTenant(name string) error {
	return util.ErrUnimplementedFeature
}
<|endoftext|>


<commit_before>package tmpfriend

import "testing"

func TestIsTmpFriendDir(t *testing.T) {
	valid := []string{"tmpfriend-1-", "tmpfriend-123-123", "/tmp/tmpfriend-123-3"}
	invalid := []string{"gobuild-123", "tmpfriend-123", "/tmp", "/", "/tmp/"}
	for _, d := range valid {
		if !IsTmpFriendDir(d) {
			t.Errorf("%v is a valid tmpfriend dir", d)
		}
	}
	for _, d := range invalid {
		if IsTmpFriendDir(d) {
			t.Errorf("%v is an invalid tmpfriend dir", d)
		}
	}
}
<commit_msg>Test RootTempDir<commit_after>package tmpfriend

import (
	"io/ioutil"
	"os"
	"testing"
)

func TestIsTmpFriendDir(t *testing.T) {
	valid := []string{"tmpfriend-1-", "tmpfriend-123-123", "/tmp/tmpfriend-123-3"}
	invalid := []string{"gobuild-123", "tmpfriend-123", "/tmp", "/", "/tmp/"}
	for _, d := range valid {
		if !IsTmpFriendDir(d) {
			t.Errorf("%v is a valid tmpfriend dir", d)
		}
	}
	for _, d := range invalid {
		if IsTmpFriendDir(d) {
			t.Errorf("%v is an invalid tmpfriend dir", d)
		}
	}
}

func TestRootTempDir(t *testing.T) {
	d, err := ioutil.TempDir("", "testroottempdir")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(d)

	old := os.Getenv("TMPDIR")
	cleanup, err := RootTempDir(d)
	if err != nil {
		t.Fatal(err)
	}

	testDir, err := ioutil.TempDir("", "testroottempsubdir")
	if err != nil {
		t.Fatal(err)
	}
	_, err = os.Stat(testDir)
	if err != nil {
		t.Fatal(err)
	}
	cleanup()
	_, err = os.Stat(testDir)
	if err == nil || !os.IsNotExist(err) {
		os.RemoveAll(testDir)
		t.Errorf("Cleanup did not delete sub temp dir %v", testDir)
	}
	if os.Getenv("TMPDIR") != old {
		t.Error("Failed to restore TMPDIR")
	}
}
<|endoftext|>


<commit_before>package serve

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	rice "github.com/GeertJohan/go.rice"
)

func TestServe(t *testing.T) {
	registerHandlers()
	ts := httptest.NewServer(http.DefaultServeMux)
	defer ts.Close()
	expected := make(map[string]int)
	for endpoint := range v1Endpoints {
		expected[v1APIPath(endpoint)] = http.StatusOK
	}

	err := rice.MustFindBox(staticDir).Walk("", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			expected["/"+path] = http.StatusOK
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}

	// Disabled endpoints should return '404 Not Found'
	expected[v1APIPath("sign")] = http.StatusNotFound
	expected[v1APIPath("authsign")] = http.StatusNotFound
	expected[v1APIPath("newcert")] = http.StatusNotFound
	expected[v1APIPath("info")] = http.StatusNotFound
	expected[v1APIPath("bundle")] = http.StatusNotFound
	expected[v1APIPath("ocspsign")] = http.StatusNotFound

	// Enabled endpoints should return '405 Method Not Allowed'
	expected[v1APIPath("init_ca")] = http.StatusMethodNotAllowed
	expected[v1APIPath("newkey")] = http.StatusMethodNotAllowed

	// POST-only endpoints should return '400 Bad Request'
	expected[v1APIPath("scan")] = http.StatusBadRequest

	// Non-existent endpoints should return '404 Not Found'
	expected["/bad_endpoint"] = http.StatusNotFound

	for endpoint, status := range expected {
		resp, err := http.Get(ts.URL + endpoint)
		if err != nil {
			t.Error(err)
		}
		if resp.StatusCode != status {
			t.Fatalf("%s: '%s' (expected '%s')", endpoint, resp.Status, http.StatusText(status))
		}
	}
}
<commit_msg>update serve test to reflect the fact bundle api by default is enabled<commit_after>package serve

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	rice "github.com/GeertJohan/go.rice"
)

func TestServe(t *testing.T) {
	registerHandlers()
	ts := httptest.NewServer(http.DefaultServeMux)
	defer ts.Close()
	expected := make(map[string]int)
	for endpoint := range v1Endpoints {
		expected[v1APIPath(endpoint)] = http.StatusOK
	}

	err := rice.MustFindBox(staticDir).Walk("", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() {
			expected["/"+path] = http.StatusOK
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}

	// Disabled endpoints should return '404 Not Found'
	expected[v1APIPath("sign")] = http.StatusNotFound
	expected[v1APIPath("authsign")] = http.StatusNotFound
	expected[v1APIPath("newcert")] = http.StatusNotFound
	expected[v1APIPath("info")] = http.StatusNotFound
	expected[v1APIPath("ocspsign")] = http.StatusNotFound

	// Enabled endpoints should return '405 Method Not Allowed'
	expected[v1APIPath("init_ca")] = http.StatusMethodNotAllowed
	expected[v1APIPath("newkey")] = http.StatusMethodNotAllowed
	expected[v1APIPath("bundle")] = http.StatusMethodNotAllowed

	// POST-only endpoints should return '400 Bad Request'
	expected[v1APIPath("scan")] = http.StatusBadRequest

	// Non-existent endpoints should return '404 Not Found'
	expected["/bad_endpoint"] = http.StatusNotFound

	for endpoint, status := range expected {
		resp, err := http.Get(ts.URL + endpoint)
		if err != nil {
			t.Error(err)
		}
		if resp.StatusCode != status {
			t.Fatalf("%s: '%s' (expected '%s')", endpoint, resp.Status, http.StatusText(status))
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Fix voice activated detection weighting<commit_after><|endoftext|>


<commit_before>package tools

// The ring scan process will constantly compare the rings servers have with
// the local rings and send out new rings as needed. By default, it will lazily
// scan servers, trying to hit each one about every ten minutes. But, if a
// fast-scan is triggered because a ring just changed locally, the ring scan
// process will go as quickly as it can to send out the new rings.
//
// In /etc/hummingbird/andrewd-server.conf:
// [ring-scan]
// initial_delay = 1            # seconds to wait between ring checks for the first pass
// pass_time_target = 600       # seconds to try to make subsequent passes take
// report_interval = 600        # seconds between progress reports
// fast_scan_concurrency = 25   # number of concurrent ring transfers allowed

// Example from recon:
// $ curl http://localhost:6010/recon/ringmd5
// {
//   "/etc/hummingbird/account.ring.gz": "50637e9e0eac3b5d085d21950b697f6b",
//   "/etc/hummingbird/container.ring.gz": "488602c6c8f0aa7c48c389524a8ec22b",
//   "/etc/hummingbird/object-1.ring.gz": "a16b7943bfe21cba5e0b42f811b67fba",
//   "/etc/hummingbird/object-2.ring.gz": "3529ce642ff99b724140f7af92a7f014",
//   "/etc/hummingbird/object.ring.gz": "94744009e53c8c4d1895b534285cd78c"
// }

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/troubling/hummingbird/common/ring"
	"go.uber.org/zap"
)

type ringScan struct {
	aa *AutoAdmin
	// delay between each ring check; adjusted each pass to try to make passes last passTimeTarget
	delay               time.Duration
	passTimeTarget      time.Duration
	reportInterval      time.Duration
	fastScanConcurrency int
	fastScan            bool
	prefix              string
	suffix              string
	ringMD5CacheLock    sync.Mutex
	ringMD5Cache        map[string]map[int]ring.RingMD5
}

func newRingScan(aa *AutoAdmin) *ringScan {
	rs := &ringScan{
		aa:                  aa,
		delay:               time.Duration(aa.serverconf.GetInt("ring-scan", "initial_delay", 1)) * time.Second,
		passTimeTarget:      time.Duration(aa.serverconf.GetInt("ring-scan", "pass_time_target", 600)) * time.Second,
		reportInterval:      time.Duration(aa.serverconf.GetInt("ring-scan", "report_interval", 600)) * time.Second,
		fastScanConcurrency: int(aa.serverconf.GetInt("ring-scan", "fast_scan_concurrency", 25)),
		ringMD5Cache:        map[string]map[int]ring.RingMD5{},
	}
	if rs.delay < 0 {
		rs.delay = time.Second
	}
	if rs.passTimeTarget < 0 {
		rs.passTimeTarget = time.Second
	}
	if rs.reportInterval < 0 {
		rs.reportInterval = time.Second
	}
	if rs.fastScanConcurrency < 1 {
		rs.fastScanConcurrency = 1
	}
	rs.fastScan = true
	rs.prefix, rs.suffix = getAffixes()
	rs.ringMD5Cache["account"] = map[int]ring.RingMD5{}
	rs.ringMD5Cache["container"] = map[int]ring.RingMD5{}
	rs.ringMD5Cache["object"] = map[int]ring.RingMD5{}
	return rs
}

func (rs *ringScan) runForever() {
	for {
		sleepFor := rs.runOnce()
		if sleepFor < 0 {
			break
		}
		select {
		case <-rs.aa.fastRingScan:
		CLEAR:
			for {
				select {
				case <-rs.aa.fastRingScan:
				default:
					break CLEAR
				}
			}
			rs.fastScan = true
			rs.runOnce()
			rs.fastScan = false
		case <-time.After(sleepFor):
		}
	}
}

func (rs *ringScan) runOnce() time.Duration {
	start := time.Now()
	logger := rs.aa.logger.With(zap.String("process", "ring scan"))
	if rs.fastScan {
		logger.Debug("starting fast scan")
		if err := rs.aa.db.startProcessPass("ring scan", "", 0); err != nil {
			logger.Error("startProcessPass", zap.Error(err))
		}
	} else {
		logger.Debug("starting pass")
		if err := rs.aa.db.startProcessPass("ring scan", "", 0); err != nil {
			logger.Error("startProcessPass", zap.Error(err))
		}
	}
	var delays int64
	var errors int64
	urls := rs.ringMD5URLs()
	cancel := make(chan struct{})
	progressDone := make(chan struct{})
	go func() {
		for {
			select {
			case <-cancel:
				close(progressDone)
				return
			case <-time.After(rs.reportInterval):
				d := atomic.LoadInt64(&delays)
				e := atomic.LoadInt64(&errors)
				eta := time.Duration(int64(time.Since(start)) / d * (int64(len(urls)) - d))
				logger.Debug("progress", zap.Int64("urls so far", d), zap.Int("total urls", len(urls)), zap.String("eta", eta.String()))
				if err := rs.aa.db.progressProcessPass("ring scan", "", 0, fmt.Sprintf("%d of %d urls, %d errors, eta %s", d, len(urls), e, eta)); err != nil {
					logger.Error("progressProcessPass", zap.Error(err))
				}
			}
		}
	}()
	reconURL := func(getURL string) {
		atomic.AddInt64(&delays, 1)
		getLogger := logger.With(zap.String("method", "GET"), zap.String("url", getURL))
		req, err := http.NewRequest("GET", getURL, nil)
		if err != nil {
			getLogger.Error("http.NewRequest", zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		resp, err := rs.aa.client.Do(req)
		if err != nil {
			getLogger.Error("Do", zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			getLogger.Error("Body", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		if resp.StatusCode/100 != 2 {
			getLogger.Error("StatusCode", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		ringMD5s := map[string]string{}
		if err := json.Unmarshal(body, &ringMD5s); err != nil {
			getLogger.Error("JSON", zap.String("JSON", string(body)), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		getLogger.Debug("response", zap.Any("ring md5s", ringMD5s))
		for ringPath, md5 := range ringMD5s {
			if !strings.HasSuffix(ringPath, ".ring.gz") {
				continue
			}
			parts := strings.SplitN(strings.SplitN(path.Base(ringPath), ".", 2)[0], "-", 2)
			typ := parts[0]
			policy := 0
			if len(parts) > 1 {
				policy, err = strconv.Atoi(parts[1])
				if err != nil {
					getLogger.Error("policy parsing error", zap.String("ring path", ringPath), zap.Error(err))
				}
			}
			getLogger.Debug("parsed", zap.String("type", typ), zap.Int("policy", policy), zap.String("md5", md5))
			ryng, err := rs.GetRingMD5(typ, policy)
			if err != nil {
				getLogger.Error("error getting ring", zap.String("type", typ), zap.Int("policy", policy), zap.String("md5", md5), zap.Error(err))
			}
			if md5 != ryng.MD5() {
				getLogger.Debug("pushing ring", zap.String("type", typ), zap.Int("policy", policy), zap.String("previous md5", md5), zap.String("new md5", ryng.MD5()))
				u, err := url.Parse(getURL)
				if err != nil {
					getLogger.Error("url.Parse", zap.String("url", getURL), zap.Error(err))
					atomic.AddInt64(&errors, 1)
					continue
				}
				putURL := fmt.Sprintf("%s://%s/ring%s", u.Scheme, u.Host, ringPath)
				putLogger := logger.With(zap.String("method", "PUT"), zap.String("url", putURL))
				fp, err := os.Open(ryng.DiskPath())
				if err != nil {
					getLogger.Error("os.Open", zap.String("disk path", ryng.DiskPath()), zap.Error(err))
					atomic.AddInt64(&errors, 1)
					continue
				}
				func() { // sub func so we can have the defer
					defer fp.Close()
					req, err := http.NewRequest("PUT", putURL, fp)
					if err != nil {
						putLogger.Error("http.NewRequest", zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
					req.Header.Set("Etag", ryng.MD5())
					resp, err := rs.aa.client.Do(req)
					if err != nil {
						putLogger.Error("Do", zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
					resp.Body.Close()
					if resp.StatusCode/100 != 2 {
						putLogger.Error("StatusCode", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
				}()
			}
		}
	}
	if rs.fastScan {
		var wg sync.WaitGroup
		fastScanURLs := make(chan string, rs.fastScanConcurrency)
		for i := 0; i < rs.fastScanConcurrency; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for {
					url := <-fastScanURLs
					if url == "" {
						return
					}
					reconURL(url)
				}
			}()
		}
		for _, url := range urls {
			fastScanURLs <- url
		}
		close(fastScanURLs)
		wg.Wait()
	} else {
	STANDARDSCAN:
		for _, url := range urls {
			select {
			case <-rs.aa.fastRingScan:
				rs.aa.fastRingScan <- struct{}{}
				break STANDARDSCAN
			case <-time.After(rs.delay):
				reconURL(url)
			}
		}
	}
	close(cancel)
	<-progressDone
	if !rs.fastScan {
		rs.delay = rs.passTimeTarget / time.Duration(delays)
	}
	sleepFor := time.Until(start.Add(rs.passTimeTarget))
	if sleepFor < 0 {
		sleepFor = 0
	}
	if rs.fastScan {
		logger.Debug("fast scan complete", zap.Int64("errors", errors))
	} else {
		logger.Debug("pass complete", zap.Int64("errors", errors), zap.String("next delay", rs.delay.String()), zap.String("sleep for", sleepFor.String()))
	}
	if err := rs.aa.db.progressProcessPass("ring scan", "", 0, fmt.Sprintf("%d of %d urls, %d errors", delays, len(urls), errors)); err != nil {
		logger.Error("progressProcessPass", zap.Error(err))
	}
	if err := rs.aa.db.completeProcessPass("ring scan", "", 0); err != nil {
		logger.Error("completeProcessPass", zap.Error(err))
	}
	return sleepFor
}

// ringMD5URLs returns a list of urls used to obtain the MD5s of the rings on
// that server, based on all the devices on all the servers in all the rings
// for the hummingbird configuration. Note that this distills down to one per
// IP destination. If there are account, container, and object servers running
// on the same IP, it is assumed they all share the same /etc/hummingbird or
// /etc/swift directory, and the recon/ringmd5 call will return the MD5s for
// all the ring files in that directory.
func (rs *ringScan) ringMD5URLs() []string {
	urlMap := map[string]string{}
	for _, typ := range []string{"account", "container", "object"} {
		if typ == "object" {
			for _, policy := range rs.aa.policies {
				ryng, _ := getRing("", typ, policy.Index)
				for _, dev := range ryng.AllDevices() {
					if !dev.Active() {
						continue
					}
					urlMap[dev.Ip] = fmt.Sprintf("%s://%s:%d/recon/ringmd5", dev.Scheme, dev.Ip, dev.Port)
				}
			}
		} else {
			ryng, _ := getRing("", typ, 0)
			for _, dev := range ryng.AllDevices() {
				if !dev.Active() {
					continue
				}
				urlMap[dev.Ip] = fmt.Sprintf("%s://%s:%d/recon/ringmd5", dev.Scheme, dev.Ip, dev.Port)
			}
		}
	}
	urls := make([]string, 0, len(urlMap))
	for _, url := range urlMap {
		urls = append(urls, url)
	}
	return urls
}

func (rs *ringScan) GetRingMD5(typ string, policy int) (ring.RingMD5, error) {
	rs.ringMD5CacheLock.Lock()
	defer rs.ringMD5CacheLock.Unlock()
	if ryng, ok := rs.ringMD5Cache[typ][policy]; ok {
		return ryng, ryng.Reload()
	}
	ryng, err := ring.GetRingMD5(typ, rs.prefix, rs.suffix, policy)
	if err != nil {
		return nil, err
	}
	rs.ringMD5Cache[typ][policy] = ryng
	return ryng, nil
}
<commit_msg>Only update delay on full scan with items<commit_after>package tools

// The ring scan process will constantly compare the rings servers have with
// the local rings and send out new rings as needed. By default, it will lazily
// scan servers, trying to hit each one about every ten minutes. But, if a
// fast-scan is triggered because a ring just changed locally, the ring scan
// process will go as quickly as it can to send out the new rings.
//
// In /etc/hummingbird/andrewd-server.conf:
// [ring-scan]
// initial_delay = 1            # seconds to wait between ring checks for the first pass
// pass_time_target = 600       # seconds to try to make subsequent passes take
// report_interval = 600        # seconds between progress reports
// fast_scan_concurrency = 25   # number of concurrent ring transfers allowed

// Example from recon:
// $ curl http://localhost:6010/recon/ringmd5
// {
//   "/etc/hummingbird/account.ring.gz": "50637e9e0eac3b5d085d21950b697f6b",
//   "/etc/hummingbird/container.ring.gz": "488602c6c8f0aa7c48c389524a8ec22b",
//   "/etc/hummingbird/object-1.ring.gz": "a16b7943bfe21cba5e0b42f811b67fba",
//   "/etc/hummingbird/object-2.ring.gz": "3529ce642ff99b724140f7af92a7f014",
//   "/etc/hummingbird/object.ring.gz": "94744009e53c8c4d1895b534285cd78c"
// }

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/troubling/hummingbird/common/ring"
	"go.uber.org/zap"
)

type ringScan struct {
	aa *AutoAdmin
	// delay between each ring check; adjusted each pass to try to make passes last passTimeTarget
	delay               time.Duration
	passTimeTarget      time.Duration
	reportInterval      time.Duration
	fastScanConcurrency int
	fastScan            bool
	prefix              string
	suffix              string
	ringMD5CacheLock    sync.Mutex
	ringMD5Cache        map[string]map[int]ring.RingMD5
}

func newRingScan(aa *AutoAdmin) *ringScan {
	rs := &ringScan{
		aa:                  aa,
		delay:               time.Duration(aa.serverconf.GetInt("ring-scan", "initial_delay", 1)) * time.Second,
		passTimeTarget:      time.Duration(aa.serverconf.GetInt("ring-scan", "pass_time_target", 600)) * time.Second,
		reportInterval:      time.Duration(aa.serverconf.GetInt("ring-scan", "report_interval", 600)) * time.Second,
		fastScanConcurrency: int(aa.serverconf.GetInt("ring-scan", "fast_scan_concurrency", 25)),
		ringMD5Cache:        map[string]map[int]ring.RingMD5{},
	}
	if rs.delay < 0 {
		rs.delay = time.Second
	}
	if rs.passTimeTarget < 0 {
		rs.passTimeTarget = time.Second
	}
	if rs.reportInterval < 0 {
		rs.reportInterval = time.Second
	}
	if rs.fastScanConcurrency < 1 {
		rs.fastScanConcurrency = 1
	}
	rs.fastScan = true
	rs.prefix, rs.suffix = getAffixes()
	rs.ringMD5Cache["account"] = map[int]ring.RingMD5{}
	rs.ringMD5Cache["container"] = map[int]ring.RingMD5{}
	rs.ringMD5Cache["object"] = map[int]ring.RingMD5{}
	return rs
}

func (rs *ringScan) runForever() {
	for {
		sleepFor := rs.runOnce()
		if sleepFor < 0 {
			break
		}
		select {
		case <-rs.aa.fastRingScan:
		CLEAR:
			for {
				select {
				case <-rs.aa.fastRingScan:
				default:
					break CLEAR
				}
			}
			rs.fastScan = true
			rs.runOnce()
			rs.fastScan = false
		case <-time.After(sleepFor):
		}
	}
}

func (rs *ringScan) runOnce() time.Duration {
	start := time.Now()
	logger := rs.aa.logger.With(zap.String("process", "ring scan"))
	if rs.fastScan {
		logger.Debug("starting fast scan")
		if err := rs.aa.db.startProcessPass("ring scan", "", 0); err != nil {
			logger.Error("startProcessPass", zap.Error(err))
		}
	} else {
		logger.Debug("starting pass")
		if err := rs.aa.db.startProcessPass("ring scan", "", 0); err != nil {
			logger.Error("startProcessPass", zap.Error(err))
		}
	}
	var delays int64
	var errors int64
	urls := rs.ringMD5URLs()
	cancel := make(chan struct{})
	progressDone := make(chan struct{})
	go func() {
		for {
			select {
			case <-cancel:
				close(progressDone)
				return
			case <-time.After(rs.reportInterval):
				d := atomic.LoadInt64(&delays)
				e := atomic.LoadInt64(&errors)
				eta := time.Duration(int64(time.Since(start)) / d * (int64(len(urls)) - d))
				logger.Debug("progress", zap.Int64("urls so far", d), zap.Int("total urls", len(urls)), zap.String("eta", eta.String()))
				if err := rs.aa.db.progressProcessPass("ring scan", "", 0, fmt.Sprintf("%d of %d urls, %d errors, eta %s", d, len(urls), e, eta)); err != nil {
					logger.Error("progressProcessPass", zap.Error(err))
				}
			}
		}
	}()
	reconURL := func(getURL string) {
		atomic.AddInt64(&delays, 1)
		getLogger := logger.With(zap.String("method", "GET"), zap.String("url", getURL))
		req, err := http.NewRequest("GET", getURL, nil)
		if err != nil {
			getLogger.Error("http.NewRequest", zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		resp, err := rs.aa.client.Do(req)
		if err != nil {
			getLogger.Error("Do", zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			getLogger.Error("Body", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		if resp.StatusCode/100 != 2 {
			getLogger.Error("StatusCode", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		ringMD5s := map[string]string{}
		if err := json.Unmarshal(body, &ringMD5s); err != nil {
			getLogger.Error("JSON", zap.String("JSON", string(body)), zap.Error(err))
			atomic.AddInt64(&errors, 1)
			return
		}
		getLogger.Debug("response", zap.Any("ring md5s", ringMD5s))
		for ringPath, md5 := range ringMD5s {
			if !strings.HasSuffix(ringPath, ".ring.gz") {
				continue
			}
			parts := strings.SplitN(strings.SplitN(path.Base(ringPath), ".", 2)[0], "-", 2)
			typ := parts[0]
			policy := 0
			if len(parts) > 1 {
				policy, err = strconv.Atoi(parts[1])
				if err != nil {
					getLogger.Error("policy parsing error", zap.String("ring path", ringPath), zap.Error(err))
				}
			}
			getLogger.Debug("parsed", zap.String("type", typ), zap.Int("policy", policy), zap.String("md5", md5))
			ryng, err := rs.GetRingMD5(typ, policy)
			if err != nil {
				getLogger.Error("error getting ring", zap.String("type", typ), zap.Int("policy", policy), zap.String("md5", md5), zap.Error(err))
			}
			if md5 != ryng.MD5() {
				getLogger.Debug("pushing ring", zap.String("type", typ), zap.Int("policy", policy), zap.String("previous md5", md5), zap.String("new md5", ryng.MD5()))
				u, err := url.Parse(getURL)
				if err != nil {
					getLogger.Error("url.Parse", zap.String("url", getURL), zap.Error(err))
					atomic.AddInt64(&errors, 1)
					continue
				}
				putURL := fmt.Sprintf("%s://%s/ring%s", u.Scheme, u.Host, ringPath)
				putLogger := logger.With(zap.String("method", "PUT"), zap.String("url", putURL))
				fp, err := os.Open(ryng.DiskPath())
				if err != nil {
					getLogger.Error("os.Open", zap.String("disk path", ryng.DiskPath()), zap.Error(err))
					atomic.AddInt64(&errors, 1)
					continue
				}
				func() { // sub func so we can have the defer
					defer fp.Close()
					req, err := http.NewRequest("PUT", putURL, fp)
					if err != nil {
						putLogger.Error("http.NewRequest", zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
					req.Header.Set("Etag", ryng.MD5())
					resp, err := rs.aa.client.Do(req)
					if err != nil {
						putLogger.Error("Do", zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
					resp.Body.Close()
					if resp.StatusCode/100 != 2 {
						putLogger.Error("StatusCode", zap.Int("StatusCode", resp.StatusCode), zap.Error(err))
						atomic.AddInt64(&errors, 1)
						return
					}
				}()
			}
		}
	}
	fullpass := true
	if rs.fastScan {
		var wg sync.WaitGroup
		fastScanURLs := make(chan string, rs.fastScanConcurrency)
		for i := 0; i < rs.fastScanConcurrency; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for {
					url := <-fastScanURLs
					if url == "" {
						return
					}
					reconURL(url)
				}
			}()
		}
		for _, url := range urls {
			fastScanURLs <- url
		}
		close(fastScanURLs)
		wg.Wait()
	} else {
	STANDARDSCAN:
		for _, url := range urls {
			select {
			case <-rs.aa.fastRingScan:
				fullpass = false
				rs.aa.fastRingScan <- struct{}{}
				break STANDARDSCAN
			case <-time.After(rs.delay):
				reconURL(url)
			}
		}
	}
	close(cancel)
	<-progressDone
	if !rs.fastScan && fullpass && delays > 0 {
		rs.delay = rs.passTimeTarget / time.Duration(delays)
	}
	sleepFor := time.Until(start.Add(rs.passTimeTarget))
	if sleepFor < 0 {
		sleepFor = 0
	}
	if rs.fastScan {
		logger.Debug("fast scan complete", zap.Int64("errors", errors))
	} else {
		logger.Debug("pass complete", zap.Int64("errors", errors), zap.String("next delay", rs.delay.String()), zap.String("sleep for", sleepFor.String()))
	}
	if err := rs.aa.db.progressProcessPass("ring scan", "", 0, fmt.Sprintf("%d of %d urls, %d errors", delays, len(urls), errors)); err != nil {
		logger.Error("progressProcessPass", zap.Error(err))
	}
	if err := rs.aa.db.completeProcessPass("ring scan", "", 0); err != nil {
		logger.Error("completeProcessPass", zap.Error(err))
	}
	return sleepFor
}

// ringMD5URLs returns a list of urls used to obtain the MD5s of the rings on
// that server, based on all the devices on all the servers in all the rings
// for the hummingbird configuration. Note that this distills down to one per
// IP destination. If there are account, container, and object servers running
// on the same IP, it is assumed they all share the same /etc/hummingbird or
// /etc/swift directory, and the recon/ringmd5 call will return the MD5s for
// all the ring files in that directory.
func (rs *ringScan) ringMD5URLs() []string {
	urlMap := map[string]string{}
	for _, typ := range []string{"account", "container", "object"} {
		if typ == "object" {
			for _, policy := range rs.aa.policies {
				ryng, _ := getRing("", typ, policy.Index)
				for _, dev := range ryng.AllDevices() {
					if !dev.Active() {
						continue
					}
					urlMap[dev.Ip] = fmt.Sprintf("%s://%s:%d/recon/ringmd5", dev.Scheme, dev.Ip, dev.Port)
				}
			}
		} else {
			ryng, _ := getRing("", typ, 0)
			for _, dev := range ryng.AllDevices() {
				if !dev.Active() {
					continue
				}
				urlMap[dev.Ip] = fmt.Sprintf("%s://%s:%d/recon/ringmd5", dev.Scheme, dev.Ip, dev.Port)
			}
		}
	}
	urls := make([]string, 0, len(urlMap))
	for _, url := range urlMap {
		urls = append(urls, url)
	}
	return urls
}

func (rs *ringScan) GetRingMD5(typ string, policy int) (ring.RingMD5, error) {
	rs.ringMD5CacheLock.Lock()
	defer rs.ringMD5CacheLock.Unlock()
	if ryng, ok := rs.ringMD5Cache[typ][policy]; ok {
		return ryng, ryng.Reload()
	}
	ryng, err := ring.GetRingMD5(typ, rs.prefix, rs.suffix, policy)
	if err != nil {
		return nil, err
	}
	rs.ringMD5Cache[typ][policy] = ryng
	return ryng, nil
}
<|endoftext|>