<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 registry

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"time"

	"github.com/jacobsa/aws/sdb"
	"github.com/jacobsa/comeback/crypto"
	"github.com/jacobsa/gcloud/gcs"
)

// Create a registry that stores data in the supplied GCS bucket, deriving a
// crypto key from the supplied password and ensuring that the bucket may not
// in the future be used with any other key and has not in the past, either.
// Return a crypter configured to use the key.
func NewGCSRegistry(
	bucket gcs.Bucket,
	cryptoPassword string,
	deriver crypto.KeyDeriver) (r Registry, crypter crypto.Crypter, err error) {
	return newGCSRegistry(
		bucket,
		cryptoPassword,
		deriver,
		crypto.NewCrypter,
		rand.Reader)
}

const (
	gcsJobKeyPrefix      = "jobs/"
	gcsMetadataKey_Name  = "job_name"
	gcsMetadataKey_Score = "hex_score"
)

// A registry that stores job records in a GCS bucket. Object names are of the
// form
//
//     <gcsJobKeyPrefix><time>
//
// where <time> is a time.Time with UTC location formatted according to
// time.RFC3339Nano. Additional information is stored as object metadata fields
// keyed by the constants above. Metadata fields are used in preference to
// object content so that they are accessible on a ListObjects request.
//
// The bucket additionally contains a "marker" object (named by the constant
// markerItemName) with metadata keys specifying a salt and a ciphertext for
// some random plaintext, generated ant written the first time the bucket is
// used. This marker allows us to verify that the user-provided crypto password
// is correct by deriving a key using the password and the salt and making sure
// that the ciphertext can be decrypted using that key.
type gcsRegistry struct {
	bucket gcs.Bucket
}

// Like NewGCSRegistry, but with more injected.
func newGCSRegistry(
	bucket gcs.Bucket,
	cryptoPassword string,
	deriver crypto.KeyDeriver,
	createCrypter func(key []byte) (crypto.Crypter, error),
	cryptoRandSrc io.Reader) (r Registry, crypter crypto.Crypter, err error) {
}

func (r *gcsRegistry) RecordBackup(j CompletedJob) (err error) {
	err = fmt.Errorf("gcsRegistry.RecordBackup is not implemented.")
	return
}

func (r *gcsRegistry) ListRecentBackups() (jobs []CompletedJob, err error) {
	err = fmt.Errorf("gcsRegistry.ListRecentBackups is not implemented.")
	return
}

func (r *gcsRegistry) FindBackup(
	startTime time.Time) (job CompletedJob, err error) {
	err = fmt.Errorf("gcsRegistry.FindBackup is not implemented.")
	return
}

func verifyCompatibleAndSetUpCrypter(
	markerAttrs []sdb.Attribute,
	cryptoPassword string,
	deriver crypto.KeyDeriver,
	createCrypter func(key []byte) (crypto.Crypter, error),
) (crypter crypto.Crypter, err error) {
	// Look through the attributes for what we need.
	var ciphertext []byte
	var salt []byte

	for _, attr := range markerAttrs {
		var dest *[]byte
		switch attr.Name {
		case encryptedDataMarker:
			dest = &ciphertext
		case passwordSaltMarker:
			dest = &salt
		default:
			continue
		}

		// The data is base64-encoded.
		if *dest, err = base64.StdEncoding.DecodeString(attr.Value); err != nil {
			err = fmt.Errorf("Decoding %s (%s): %v", attr.Name, attr.Value, err)
			return
		}
	}

	// Did we get both ciphertext and salt?
	if ciphertext == nil {
		err = fmt.Errorf("Missing encrypted data marker.")
		return
	}

	if salt == nil {
		err = fmt.Errorf("Missing password salt marker.")
		return
	}

	// Derive a key and create a crypter.
	cryptoKey := deriver.DeriveKey(cryptoPassword, salt)
	if crypter, err = createCrypter(cryptoKey); err != nil {
		err = fmt.Errorf("createCrypter: %v", err)
		return
	}

	// Attempt to decrypt the ciphertext.
	if _, err = crypter.Decrypt(ciphertext); err != nil {
		// Special case: Did the crypter signal that the key was wrong?
		if _, ok := err.(*crypto.NotAuthenticError); ok {
			err = fmt.Errorf("The supplied password is incorrect.")
			return
		}

		// Generic error.
		err = fmt.Errorf("Decrypt: %v", err)
		return
	}

	return
}
<commit_msg>Fixed up verifyCompatibleAndSetUpCrypter.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 registry

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"time"

	"github.com/jacobsa/comeback/crypto"
	"github.com/jacobsa/gcloud/gcs"
)

// Create a registry that stores data in the supplied GCS bucket, deriving a
// crypto key from the supplied password and ensuring that the bucket may not
// in the future be used with any other key and has not in the past, either.
// Return a crypter configured to use the key.
func NewGCSRegistry(
	bucket gcs.Bucket,
	cryptoPassword string,
	deriver crypto.KeyDeriver) (r Registry, crypter crypto.Crypter, err error) {
	return newGCSRegistry(
		bucket,
		cryptoPassword,
		deriver,
		crypto.NewCrypter,
		rand.Reader)
}

const (
	gcsJobKeyPrefix      = "jobs/"
	gcsMetadataKey_Name  = "job_name"
	gcsMetadataKey_Score = "hex_score"
)

// A registry that stores job records in a GCS bucket. Object names are of the
// form
//
//     <gcsJobKeyPrefix><time>
//
// where <time> is a time.Time with UTC location formatted according to
// time.RFC3339Nano. Additional information is stored as object metadata fields
// keyed by the constants above. Metadata fields are used in preference to
// object content so that they are accessible on a ListObjects request.
//
// The bucket additionally contains a "marker" object (named by the constant
// markerItemName) with metadata keys specifying a salt and a ciphertext for
// some random plaintext, generated ant written the first time the bucket is
// used. This marker allows us to verify that the user-provided crypto password
// is correct by deriving a key using the password and the salt and making sure
// that the ciphertext can be decrypted using that key.
type gcsRegistry struct {
	bucket gcs.Bucket
}

// Like NewGCSRegistry, but with more injected.
func newGCSRegistry(
	bucket gcs.Bucket,
	cryptoPassword string,
	deriver crypto.KeyDeriver,
	createCrypter func(key []byte) (crypto.Crypter, error),
	cryptoRandSrc io.Reader) (r Registry, crypter crypto.Crypter, err error)

func (r *gcsRegistry) RecordBackup(j CompletedJob) (err error) {
	err = fmt.Errorf("gcsRegistry.RecordBackup is not implemented.")
	return
}

func (r *gcsRegistry) ListRecentBackups() (jobs []CompletedJob, err error) {
	err = fmt.Errorf("gcsRegistry.ListRecentBackups is not implemented.")
	return
}

func (r *gcsRegistry) FindBackup(
	startTime time.Time) (job CompletedJob, err error) {
	err = fmt.Errorf("gcsRegistry.FindBackup is not implemented.")
	return
}

func verifyCompatibleAndSetUpCrypter(
	ciphertextBase64 string,
	passwordSaltBase64 string,
	cryptoPassword string,
	deriver crypto.KeyDeriver,
	createCrypter func(key []byte) (crypto.Crypter, error)) (
	crypter crypto.Crypter,
	err error) {
	// Base64-decode.
	ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
	if err != nil {
		err = fmt.Errorf("Decoding ciphertext: %v", err)
		return
	}

	passwordSalt, err := base64.StdEncoding.DecodeString(passwordSaltBase64)
	if err != nil {
		err = fmt.Errorf("Decoding password salt: %v", err)
		return
	}

	// Derive a key and create a crypter.
	cryptoKey := deriver.DeriveKey(cryptoPassword, passwordSalt)
	if crypter, err = createCrypter(cryptoKey); err != nil {
		err = fmt.Errorf("createCrypter: %v", err)
		return
	}

	// Attempt to decrypt the ciphertext.
	if _, err = crypter.Decrypt(ciphertext); err != nil {
		// Special case: Did the crypter signal that the key was wrong?
		if _, ok := err.(*crypto.NotAuthenticError); ok {
			err = fmt.Errorf("The supplied password is incorrect.")
			return
		}

		// Generic error.
		err = fmt.Errorf("Decrypt: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 transport

import (
	"net/http"
	"reflect"
	"strings"
	"testing"
)

type testRoundTripper struct {
	Request  *http.Request
	Response *http.Response
	Err      error
}

func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	rt.Request = req
	return rt.Response, rt.Err
}

func TestBearerAuthRoundTripper(t *testing.T) {
	rt := &testRoundTripper{}
	req := &http.Request{}
	NewBearerAuthRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request == req {
		t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("Authorization") != "Bearer test" {
		t.Errorf("unexpected authorization header: %#v", rt.Request)
	}
}

func TestBasicAuthRoundTripper(t *testing.T) {
	for n, tc := range map[string]struct {
		user string
		pass string
	}{
		"basic":   {user: "user", pass: "pass"},
		"no pass": {user: "user"},
	} {
		rt := &testRoundTripper{}
		req := &http.Request{}
		NewBasicAuthRoundTripper(tc.user, tc.pass, rt).RoundTrip(req)
		if rt.Request == nil {
			t.Fatalf("%s: unexpected nil request: %v", n, rt)
		}
		if rt.Request == req {
			t.Fatalf("%s: round tripper should have copied request object: %#v", n, rt.Request)
		}
		if user, pass, found := rt.Request.BasicAuth(); !found || user != tc.user || pass != tc.pass {
			t.Errorf("%s: unexpected authorization header: %#v", n, rt.Request)
		}
	}
}

func TestUserAgentRoundTripper(t *testing.T) {
	rt := &testRoundTripper{}
	req := &http.Request{
		Header: make(http.Header),
	}
	req.Header.Set("User-Agent", "other")
	NewUserAgentRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request != req {
		t.Fatalf("round tripper should not have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("User-Agent") != "other" {
		t.Errorf("unexpected user agent header: %#v", rt.Request)
	}

	req = &http.Request{}
	NewUserAgentRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request == req {
		t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("User-Agent") != "test" {
		t.Errorf("unexpected user agent header: %#v", rt.Request)
	}
}

func TestImpersonationRoundTripper(t *testing.T) {
	tcs := []struct {
		name                string
		impersonationConfig ImpersonationConfig
		expected            map[string][]string
	}{
		{
			name: "all",
			impersonationConfig: ImpersonationConfig{
				UserName: "user",
				Groups:   []string{"one", "two"},
				Extra: map[string][]string{
					"first":  {"A", "a"},
					"second": {"B", "b"},
				},
			},
			expected: map[string][]string{
				ImpersonateUserHeader:                       {"user"},
				ImpersonateGroupHeader:                      {"one", "two"},
				ImpersonateUserExtraHeaderPrefix + "First":  {"A", "a"},
				ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
			},
		},
	}

	for _, tc := range tcs {
		rt := &testRoundTripper{}
		req := &http.Request{
			Header: make(http.Header),
		}
		NewImpersonatingRoundTripper(tc.impersonationConfig, rt).RoundTrip(req)

		for k, v := range rt.Request.Header {
			expected, ok := tc.expected[k]
			if !ok {
				t.Errorf("%v missing %v=%v", tc.name, k, v)
				continue
			}
			if !reflect.DeepEqual(expected, v) {
				t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
			}
		}
		for k, v := range tc.expected {
			expected, ok := rt.Request.Header[k]
			if !ok {
				t.Errorf("%v missing %v=%v", tc.name, k, v)
				continue
			}
			if !reflect.DeepEqual(expected, v) {
				t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
			}
		}
	}
}

func TestAuthProxyRoundTripper(t *testing.T) {
	for n, tc := range map[string]struct {
		username string
		groups   []string
		extra    map[string][]string
	}{
		"allfields": {
			username: "user",
			groups:   []string{"groupA", "groupB"},
			extra: map[string][]string{
				"one": {"alpha", "bravo"},
				"two": {"charlie", "delta"},
			},
		},
	} {
		rt := &testRoundTripper{}
		req := &http.Request{}
		NewAuthProxyRoundTripper(tc.username, tc.groups, tc.extra, rt).RoundTrip(req)
		if rt.Request == nil {
			t.Errorf("%s: unexpected nil request: %v", n, rt)
			continue
		}
		if rt.Request == req {
			t.Errorf("%s: round tripper should have copied request object: %#v", n, rt.Request)
			continue
		}

		actualUsernames, ok := rt.Request.Header["X-Remote-User"]
		if !ok {
			t.Errorf("%s missing value", n)
			continue
		}
		if e, a := []string{tc.username}, actualUsernames; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}
		actualGroups, ok := rt.Request.Header["X-Remote-Group"]
		if !ok {
			t.Errorf("%s missing value", n)
			continue
		}
		if e, a := tc.groups, actualGroups; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}

		actualExtra := map[string][]string{}
		for key, values := range rt.Request.Header {
			if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
				extraKey := strings.ToLower(key[len("X-Remote-Extra-"):])
				actualExtra[extraKey] = append(actualExtra[key], values...)
			}
		}
		if e, a := tc.extra, actualExtra; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}
	}
}
<commit_msg>c-go/transport: Add test for CacheRoundTripper<commit_after>/*
Copyright 2014 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 transport

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"testing"
)

type testRoundTripper struct {
	Request  *http.Request
	Response *http.Response
	Err      error
}

func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	rt.Request = req
	return rt.Response, rt.Err
}

func TestBearerAuthRoundTripper(t *testing.T) {
	rt := &testRoundTripper{}
	req := &http.Request{}
	NewBearerAuthRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request == req {
		t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("Authorization") != "Bearer test" {
		t.Errorf("unexpected authorization header: %#v", rt.Request)
	}
}

func TestBasicAuthRoundTripper(t *testing.T) {
	for n, tc := range map[string]struct {
		user string
		pass string
	}{
		"basic":   {user: "user", pass: "pass"},
		"no pass": {user: "user"},
	} {
		rt := &testRoundTripper{}
		req := &http.Request{}
		NewBasicAuthRoundTripper(tc.user, tc.pass, rt).RoundTrip(req)
		if rt.Request == nil {
			t.Fatalf("%s: unexpected nil request: %v", n, rt)
		}
		if rt.Request == req {
			t.Fatalf("%s: round tripper should have copied request object: %#v", n, rt.Request)
		}
		if user, pass, found := rt.Request.BasicAuth(); !found || user != tc.user || pass != tc.pass {
			t.Errorf("%s: unexpected authorization header: %#v", n, rt.Request)
		}
	}
}

func TestUserAgentRoundTripper(t *testing.T) {
	rt := &testRoundTripper{}
	req := &http.Request{
		Header: make(http.Header),
	}
	req.Header.Set("User-Agent", "other")
	NewUserAgentRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request != req {
		t.Fatalf("round tripper should not have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("User-Agent") != "other" {
		t.Errorf("unexpected user agent header: %#v", rt.Request)
	}

	req = &http.Request{}
	NewUserAgentRoundTripper("test", rt).RoundTrip(req)
	if rt.Request == nil {
		t.Fatalf("unexpected nil request: %v", rt)
	}
	if rt.Request == req {
		t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
	}
	if rt.Request.Header.Get("User-Agent") != "test" {
		t.Errorf("unexpected user agent header: %#v", rt.Request)
	}
}

func TestImpersonationRoundTripper(t *testing.T) {
	tcs := []struct {
		name                string
		impersonationConfig ImpersonationConfig
		expected            map[string][]string
	}{
		{
			name: "all",
			impersonationConfig: ImpersonationConfig{
				UserName: "user",
				Groups:   []string{"one", "two"},
				Extra: map[string][]string{
					"first":  {"A", "a"},
					"second": {"B", "b"},
				},
			},
			expected: map[string][]string{
				ImpersonateUserHeader:                       {"user"},
				ImpersonateGroupHeader:                      {"one", "two"},
				ImpersonateUserExtraHeaderPrefix + "First":  {"A", "a"},
				ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
			},
		},
	}

	for _, tc := range tcs {
		rt := &testRoundTripper{}
		req := &http.Request{
			Header: make(http.Header),
		}
		NewImpersonatingRoundTripper(tc.impersonationConfig, rt).RoundTrip(req)

		for k, v := range rt.Request.Header {
			expected, ok := tc.expected[k]
			if !ok {
				t.Errorf("%v missing %v=%v", tc.name, k, v)
				continue
			}
			if !reflect.DeepEqual(expected, v) {
				t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
			}
		}
		for k, v := range tc.expected {
			expected, ok := rt.Request.Header[k]
			if !ok {
				t.Errorf("%v missing %v=%v", tc.name, k, v)
				continue
			}
			if !reflect.DeepEqual(expected, v) {
				t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
			}
		}
	}
}

func TestAuthProxyRoundTripper(t *testing.T) {
	for n, tc := range map[string]struct {
		username string
		groups   []string
		extra    map[string][]string
	}{
		"allfields": {
			username: "user",
			groups:   []string{"groupA", "groupB"},
			extra: map[string][]string{
				"one": {"alpha", "bravo"},
				"two": {"charlie", "delta"},
			},
		},
	} {
		rt := &testRoundTripper{}
		req := &http.Request{}
		NewAuthProxyRoundTripper(tc.username, tc.groups, tc.extra, rt).RoundTrip(req)
		if rt.Request == nil {
			t.Errorf("%s: unexpected nil request: %v", n, rt)
			continue
		}
		if rt.Request == req {
			t.Errorf("%s: round tripper should have copied request object: %#v", n, rt.Request)
			continue
		}

		actualUsernames, ok := rt.Request.Header["X-Remote-User"]
		if !ok {
			t.Errorf("%s missing value", n)
			continue
		}
		if e, a := []string{tc.username}, actualUsernames; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}
		actualGroups, ok := rt.Request.Header["X-Remote-Group"]
		if !ok {
			t.Errorf("%s missing value", n)
			continue
		}
		if e, a := tc.groups, actualGroups; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}

		actualExtra := map[string][]string{}
		for key, values := range rt.Request.Header {
			if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
				extraKey := strings.ToLower(key[len("X-Remote-Extra-"):])
				actualExtra[extraKey] = append(actualExtra[key], values...)
			}
		}
		if e, a := tc.extra, actualExtra; !reflect.DeepEqual(e, a) {
			t.Errorf("%s expected %v, got %v", n, e, a)
			continue
		}
	}
}

func TestCacheRoundTripper(t *testing.T) {
	rt := &testRoundTripper{}
	cacheDir, err := ioutil.TempDir("", "cache-rt")
	defer os.RemoveAll(cacheDir)
	if err != nil {
		t.Fatal(err)
	}
	cache := NewCacheRoundTripper(cacheDir, rt)

	// First call, caches the response
	req := &http.Request{
		Method: http.MethodGet,
		URL:    &url.URL{Host: "localhost"},
	}
	rt.Response = &http.Response{
		Header:     http.Header{"ETag": []string{`"123456"`}},
		Body:       ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
		StatusCode: http.StatusOK,
	}
	resp, err := cache.RoundTrip(req)
	if err != nil {
		t.Fatal(err)
	}
	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(content) != "Content" {
		t.Errorf(`Expected Body to be "Content", got %q`, string(content))
	}

	// Second call, returns cached response
	req = &http.Request{
		Method: http.MethodGet,
		URL:    &url.URL{Host: "localhost"},
	}
	rt.Response = &http.Response{
		StatusCode: http.StatusNotModified,
		Body:       ioutil.NopCloser(bytes.NewReader([]byte("Other Content"))),
	}

	resp, err = cache.RoundTrip(req)
	if err != nil {
		t.Fatal(err)
	}

	// Read body and make sure we have the initial content
	content, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		t.Fatal(err)
	}
	if string(content) != "Content" {
		t.Errorf("Invalid content read from cache %q", string(content))
	}
}
<|endoftext|>


<commit_before>package releases

import (
	// Stdlib
	"bufio"
	"fmt"
	"sort"

	// Internal
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/version"
)

// ListNewTrunkCommits returns the list of commits that are new since the last release.
func ListNewTrunkCommits() ([]*git.Commit, error) {
	// Get git config.
	config, err := git.LoadConfig()
	if err != nil {
		return nil, err
	}
	trunkBranch := config.TrunkBranchName()

	// Get sorted release tags.
	tags, err := ListTags()
	if err != nil {
		return nil, err
	}

	// In case there are no tags, take the whole trunk branch.
	if len(tags) == 0 {
		return git.ShowCommitRange(trunkBranch)
	}

	// Return the list of relevant commits.
	lastTag := tags[len(tags)-1]
	return git.ShowCommitRange(fmt.Sprintf("%v..%v", lastTag, trunkBranch))
}

// ListStoryIdsToBeAssigned lists the story IDs that are associated with
// the commits that modified trunk since the last release, i.e. with the commits
// as returned by ListNewTrunkCommits.
//
// Only the story IDs matching the issue tracker that is passed in are returned.
func ListStoryIdsToBeAssigned(tracker common.IssueTracker) ([]string, error) {
	// Get the commits that modified trunk.
	task := "Get the commits that modified trunk"
	commits, err := ListNewTrunkCommits()
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Collect the story IDs.
	idSet := make(map[string]struct{}, len(commits))
	for _, commit := range commits {
		// Skip empty tags.
		if commit.StoryIdTag == "" {
			continue
		}

		// Parse the tag to get the story ID.
		storyId, err := tracker.StoryTagToReadableStoryId(commit.StoryIdTag)
		if err != nil {
			continue
		}

		// Add the ID to the set.
		idSet[storyId] = struct{}{}
	}

	// Convert the set to a list.
	idList := make([]string, 0, len(idSet))
	for id := range idSet {
		idList = append(idList, id)
	}

	// Return the final list of story IDs.
	return idList, nil
}

// ListTags returns the list of all release tags, sorted by the versions they represent.
func ListTags() (tags []string, err error) {
	var task = "Get release tags"

	// Get all release tags.
	stdout, err := git.RunCommand("tag", "--list", "v*.*.*")
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Parse the output to get sortable versions.
	var vers []*version.Version
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			continue
		}
		line = line[1:] // strip "v"
		ver, _ := version.Parse(line)
		vers = append(vers, ver)
	}
	if err := scanner.Err(); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Sort the versions.
	sort.Sort(version.Versions(vers))

	// Convert versions back to tag names and return.
	tgs := make([]string, 0, len(vers))
	for _, ver := range vers {
		tgs = append(tgs, "v"+ver.String())
	}
	return tgs, nil
}
<commit_msg>releases: Fix ListNewTrunkCommits<commit_after>package releases

import (
	// Stdlib
	"bufio"
	"fmt"
	"sort"

	// Internal
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/git"
	"github.com/salsaflow/salsaflow/modules/common"
	"github.com/salsaflow/salsaflow/version"
)

// ListNewTrunkCommits returns the list of commits that are new since the last release.
func ListNewTrunkCommits() ([]*git.Commit, error) {
	// Get git config.
	config, err := git.LoadConfig()
	if err != nil {
		return nil, err
	}
	var (
		remoteName    = config.RemoteName()
		trunkBranch   = config.TrunkBranchName()
		stagingBranch = config.StagingBranchName()
	)

	// By default, use the staging branch as the --not part.
	// In other words, list commits that are on trunk,
	// but which are not reachable from the staging branch.
	// In case the staging branch doesn't exist, take the whole trunk.
	// That probably means that no release has ever been started,
	// so the staging branch has not been created yet.
	startingReference := stagingBranch
	err = git.CheckOrCreateTrackingBranch(stagingBranch, remoteName)
	if err != nil {
		if _, ok := err.(*git.ErrRefNotFound); ok {
			startingReference = trunkBranch
		}
		return nil, err
	}

	// Return the list of relevant commits.
	return git.ShowCommitRange(fmt.Sprintf("%v..%v", startingReference, trunkBranch))
}

// ListStoryIdsToBeAssigned lists the story IDs that are associated with
// the commits that modified trunk since the last release, i.e. with the commits
// as returned by ListNewTrunkCommits.
//
// Only the story IDs matching the issue tracker that is passed in are returned.
func ListStoryIdsToBeAssigned(tracker common.IssueTracker) ([]string, error) {
	// Get the commits that modified trunk.
	task := "Get the commits that modified trunk"
	commits, err := ListNewTrunkCommits()
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Collect the story IDs.
	idSet := make(map[string]struct{}, len(commits))
	for _, commit := range commits {
		// Skip empty tags.
		if commit.StoryIdTag == "" {
			continue
		}

		// Parse the tag to get the story ID.
		storyId, err := tracker.StoryTagToReadableStoryId(commit.StoryIdTag)
		if err != nil {
			continue
		}

		// Add the ID to the set.
		idSet[storyId] = struct{}{}
	}

	// Convert the set to a list.
	idList := make([]string, 0, len(idSet))
	for id := range idSet {
		idList = append(idList, id)
	}

	// Return the final list of story IDs.
	return idList, nil
}

// ListTags returns the list of all release tags, sorted by the versions they represent.
func ListTags() (tags []string, err error) {
	var task = "Get release tags"

	// Get all release tags.
	stdout, err := git.RunCommand("tag", "--list", "v*.*.*")
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Parse the output to get sortable versions.
	var vers []*version.Version
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			continue
		}
		line = line[1:] // strip "v"
		ver, _ := version.Parse(line)
		vers = append(vers, ver)
	}
	if err := scanner.Err(); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Sort the versions.
	sort.Sort(version.Versions(vers))

	// Convert versions back to tag names and return.
	tgs := make([]string, 0, len(vers))
	for _, ver := range vers {
		tgs = append(tgs, "v"+ver.String())
	}
	return tgs, nil
}
<|endoftext|>


<commit_before>package repeater

import (
	lg "github.com/advantageous/go-logback/logging"
	m "github.com/advantageous/metricsd/metric"
	"github.com/advantageous/metricsd/util"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"strings"
	"time"
)

type AwsCloudMetricRepeater struct {
	logger lg.Logger
	conn   *cloudwatch.CloudWatch
	config *m.Config
}

func (cw AwsCloudMetricRepeater) ProcessMetrics(metrics []m.Metric) error {

	timestamp := aws.Time(time.Now())

	createDatum := func(name string, provider string) *cloudwatch.MetricDatum {

		dimensions := make([]*cloudwatch.Dimension, 0, 3)

		instanceIdDim := &cloudwatch.Dimension{
			Name:  aws.String("instanceId"),
			Value: aws.String(cw.config.EC2InstanceId),
		}
		dimensions = append(dimensions, instanceIdDim)

		if cw.config.IpAddress != "" {
			ipDim := &cloudwatch.Dimension{
				Name:  aws.String("ip"),
				Value: aws.String(cw.config.IpAddress),
			}
			dimensions = append(dimensions, ipDim)
		}

		if cw.config.Env != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("Environment"),
				Value: aws.String(cw.config.Env),
			}
			dimensions = append(dimensions, dim)
		}

		if cw.config.EC2InstanceNameTag != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("instanceName"),
				Value: aws.String(cw.config.EC2InstanceNameTag),
			}
			dimensions = append(dimensions, dim)
		}

		if cw.config.ServerRole != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("serverRole"),
				Value: aws.String(cw.config.ServerRole),
			}
			dimensions = append(dimensions, dim)
		}

		if provider != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("Provider"),
				Value: aws.String(provider),
			}
			dimensions = append(dimensions, dim)
		}
		return &cloudwatch.MetricDatum{
			MetricName: aws.String(name),
			Timestamp:  timestamp,
			Dimensions: dimensions,
		}
	}

	data := []*cloudwatch.MetricDatum{}

	var err error

	for index, d := range metrics {

		if cw.config.Debug {
			cw.logger.Printf("%s %d %d", d.GetName(), d.GetType(), d.GetValue())
		}

		switch d.GetType() {
		case m.COUNT:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			if strings.HasSuffix(d.GetName(), "Per") {
				datum.Unit = aws.String(cloudwatch.StandardUnitCount)
			} else {
				datum.Unit = aws.String(cloudwatch.StandardUnitPercent)
			}
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)
		case m.LEVEL:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			if strings.HasSuffix(d.GetName(), "Per") {
				datum.Unit = aws.String(cloudwatch.StandardUnitKilobytes)
			} else {
				datum.Unit = aws.String(cloudwatch.StandardUnitPercent)
			}
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)
		case m.TIMING:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			datum.Unit = aws.String(cloudwatch.StandardUnitMilliseconds)
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)

		}

		if index%20 == 0 && index != 0 {
			data = []*cloudwatch.MetricDatum{}

			if len(data) > 0 {
				request := &cloudwatch.PutMetricDataInput{
					Namespace:  aws.String(cw.config.NameSpace),
					MetricData: data,
				}
				_, err = cw.conn.PutMetricData(request)
				if err != nil {
					cw.logger.PrintError("Error writing metrics", err)
					cw.logger.Error("Error writing metrics", err, index)
				} else {
					if cw.config.Debug {
						cw.logger.Info("SENT..........................")
					}
				}
			}

		}

	}

	if len(data) > 0 {
		request := &cloudwatch.PutMetricDataInput{
			Namespace:  aws.String(cw.config.NameSpace),
			MetricData: data,
		}
		_, err = cw.conn.PutMetricData(request)

	}
	return err
}

func NewAwsCloudMetricRepeater(config *m.Config) AwsCloudMetricRepeater {
	session := util.NewAWSSession(config)
	logger := lg.NewSimpleLogger("log-repeater")
	return AwsCloudMetricRepeater{logger, cloudwatch.New(session), config}
}
<commit_msg>fix percentage vs. kilos<commit_after>package repeater

import (
	lg "github.com/advantageous/go-logback/logging"
	m "github.com/advantageous/metricsd/metric"
	"github.com/advantageous/metricsd/util"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"strings"
	"time"
)

type AwsCloudMetricRepeater struct {
	logger lg.Logger
	conn   *cloudwatch.CloudWatch
	config *m.Config
}

func (cw AwsCloudMetricRepeater) ProcessMetrics(metrics []m.Metric) error {

	timestamp := aws.Time(time.Now())

	createDatum := func(name string, provider string) *cloudwatch.MetricDatum {

		dimensions := make([]*cloudwatch.Dimension, 0, 3)

		instanceIdDim := &cloudwatch.Dimension{
			Name:  aws.String("instanceId"),
			Value: aws.String(cw.config.EC2InstanceId),
		}
		dimensions = append(dimensions, instanceIdDim)

		if cw.config.IpAddress != "" {
			ipDim := &cloudwatch.Dimension{
				Name:  aws.String("ip"),
				Value: aws.String(cw.config.IpAddress),
			}
			dimensions = append(dimensions, ipDim)
		}

		if cw.config.Env != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("Environment"),
				Value: aws.String(cw.config.Env),
			}
			dimensions = append(dimensions, dim)
		}

		if cw.config.EC2InstanceNameTag != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("instanceName"),
				Value: aws.String(cw.config.EC2InstanceNameTag),
			}
			dimensions = append(dimensions, dim)
		}

		if cw.config.ServerRole != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("serverRole"),
				Value: aws.String(cw.config.ServerRole),
			}
			dimensions = append(dimensions, dim)
		}

		if provider != "" {
			dim := &cloudwatch.Dimension{
				Name:  aws.String("Provider"),
				Value: aws.String(provider),
			}
			dimensions = append(dimensions, dim)
		}
		return &cloudwatch.MetricDatum{
			MetricName: aws.String(name),
			Timestamp:  timestamp,
			Dimensions: dimensions,
		}
	}

	data := []*cloudwatch.MetricDatum{}

	var err error

	for index, d := range metrics {

		if cw.config.Debug {
			cw.logger.Printf("%s %d %d", d.GetName(), d.GetType(), d.GetValue())
		}

		switch d.GetType() {
		case m.COUNT:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			if !strings.HasSuffix(d.GetName(), "Per") {
				datum.Unit = aws.String(cloudwatch.StandardUnitCount)
			} else {
				datum.Unit = aws.String(cloudwatch.StandardUnitPercent)
			}
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)
		case m.LEVEL:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			if !strings.HasSuffix(d.GetName(), "Per") {
				datum.Unit = aws.String(cloudwatch.StandardUnitKilobytes)
			} else {
				datum.Unit = aws.String(cloudwatch.StandardUnitPercent)
			}
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)
		case m.TIMING:
			value := float64(d.GetValue())
			datum := createDatum(d.GetName(), d.GetProvider())
			datum.Unit = aws.String(cloudwatch.StandardUnitMilliseconds)
			datum.Value = aws.Float64(float64(value))
			data = append(data, datum)

		}

		if index%20 == 0 && index != 0 {
			data = []*cloudwatch.MetricDatum{}

			if len(data) > 0 {
				request := &cloudwatch.PutMetricDataInput{
					Namespace:  aws.String(cw.config.NameSpace),
					MetricData: data,
				}
				_, err = cw.conn.PutMetricData(request)
				if err != nil {
					cw.logger.PrintError("Error writing metrics", err)
					cw.logger.Error("Error writing metrics", err, index)
				} else {
					if cw.config.Debug {
						cw.logger.Info("SENT..........................")
					}
				}
			}

		}

	}

	if len(data) > 0 {
		request := &cloudwatch.PutMetricDataInput{
			Namespace:  aws.String(cw.config.NameSpace),
			MetricData: data,
		}
		_, err = cw.conn.PutMetricData(request)

	}
	return err
}

func NewAwsCloudMetricRepeater(config *m.Config) AwsCloudMetricRepeater {
	session := util.NewAWSSession(config)
	logger := lg.NewSimpleLogger("log-repeater")
	return AwsCloudMetricRepeater{logger, cloudwatch.New(session), config}
}
<|endoftext|>


<commit_before>package sql

import (
	"bufio"
	"bytes"
	"io"
	"strings"
)

// stack represents a stack.
type stack struct {
	c []rune
}

// newStack returns an instance of a stack.
func newStack() *stack {
	return &stack{
		c: make([]rune, 0),
	}
}

// push pushes a rune onto the stack.
func (s *stack) push(r rune) {
	s.c = append(s.c, r)
}

// pop pops a rune off the stack.
func (s *stack) pop() rune {
	if len(s.c) == 0 {
		return rune(0)
	}
	c := s.c[len(s.c)-1]
	s.c = s.c[:len(s.c)-1]
	return c
}

// peek returns what is on the stack, without changing the stack.
func (s *stack) peek() rune {
	if len(s.c) == 0 {
		return rune(0)
	}
	c := s.c[len(s.c)-1]
	return c
}

// empty returns whether the stack is empty.
func (s *stack) empty() bool {
	return len(s.c) == 0
}

// Scanner represents a SQL statement scanner.
type Scanner struct {
	r *bufio.Reader
	c *stack
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r: bufio.NewReader(r),
		c: newStack(),
	}
}

// read reads the next rune from the bufferred reader.
// Returns the rune(0) if an error occurs (or io.EOF is returned).
func (s *Scanner) read() rune {
	ch, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return ch
}

// Scan returns the next SQL statement.
func (s *Scanner) Scan() (string, error) {
	var buf bytes.Buffer
	seekSemi := true

	for {
		ch := s.read()

		if ch == eof {
			return "", io.EOF
		}

		// Store the character.
		_, _ = buf.WriteRune(ch)

		if ch == '\'' || ch == '"' {
			if s.c.empty() {
				s.c.push(ch)
				seekSemi = false
			} else if s.c.peek() != ch {
				s.c.push(ch)
				seekSemi = false
			} else {
				s.c.pop()
				if s.c.empty() {
					seekSemi = true
				}
			}
		} else if ch == ';' && seekSemi {
			break
		}
	}

	return strings.TrimRight(buf.String(), "\n;"), nil
}

// eof represents a marker rune for the end of the reader.
var eof = rune(0)
<commit_msg>Trim leading and trailing whitespace<commit_after>package sql

import (
	"bufio"
	"bytes"
	"io"
	"strings"
)

// stack represents a stack.
type stack struct {
	c []rune
}

// newStack returns an instance of a stack.
func newStack() *stack {
	return &stack{
		c: make([]rune, 0),
	}
}

// push pushes a rune onto the stack.
func (s *stack) push(r rune) {
	s.c = append(s.c, r)
}

// pop pops a rune off the stack.
func (s *stack) pop() rune {
	if len(s.c) == 0 {
		return rune(0)
	}
	c := s.c[len(s.c)-1]
	s.c = s.c[:len(s.c)-1]
	return c
}

// peek returns what is on the stack, without changing the stack.
func (s *stack) peek() rune {
	if len(s.c) == 0 {
		return rune(0)
	}
	c := s.c[len(s.c)-1]
	return c
}

// empty returns whether the stack is empty.
func (s *stack) empty() bool {
	return len(s.c) == 0
}

// Scanner represents a SQL statement scanner.
type Scanner struct {
	r *bufio.Reader
	c *stack
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r: bufio.NewReader(r),
		c: newStack(),
	}
}

// read reads the next rune from the bufferred reader.
// Returns the rune(0) if an error occurs (or io.EOF is returned).
func (s *Scanner) read() rune {
	ch, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return ch
}

// Scan returns the next SQL statement.
func (s *Scanner) Scan() (string, error) {
	var buf bytes.Buffer
	seekSemi := true

	for {
		ch := s.read()

		if ch == eof {
			return "", io.EOF
		}

		// Store the character.
		_, _ = buf.WriteRune(ch)

		if ch == '\'' || ch == '"' {
			if s.c.empty() {
				s.c.push(ch)
				seekSemi = false
			} else if s.c.peek() != ch {
				s.c.push(ch)
				seekSemi = false
			} else {
				s.c.pop()
				if s.c.empty() {
					seekSemi = true
				}
			}
		} else if ch == ';' && seekSemi {
			break
		}
	}

	return strings.Trim(strings.TrimRight(buf.String(), ";"), "\n"), nil
}

// eof represents a marker rune for the end of the reader.
var eof = rune(0)
<|endoftext|>


<commit_before>package font

import (
	"C"
	"errors"
	"fmt"
	. "github.com/losinggeneration/hge-go/helpers/sprite"
	hge "github.com/losinggeneration/hge-go/hge"
	. "github.com/losinggeneration/hge-go/legacy"
	"strconv"
	"strings"
)

const (
	TEXT_LEFT     = 0
	TEXT_RIGHT    = 1
	TEXT_CENTER   = 2
	TEXT_HORZMASK = 0x03

	TEXT_TOP      = 0
	TEXT_BOTTOM   = 4
	TEXT_MIDDLE   = 8
	TEXT_VERTMASK = 0x0C
)

const (
	fntHEADERTAG = "[HGEFONT]"
	fntBITMAPTAG = "Bitmap"
	fntCHARTAG   = "Char"
)

/*
 * * HGE Font class
 */
type Font struct {
	hge *HGE

	texture    hge.Texture
	letters    [256]*Sprite
	pre        [256]float64
	post       [256]float64
	height     float64
	scale      float64
	proportion float64
	rot        float64
	tracking   float64
	spacing    float64

	color hge.Dword
	z     float64
	blend int
}

func getLines(file string) []string {
	lines := strings.FieldsFunc(file, func(r rune) bool {
		if r == '\n' || r == '\r' {
			return true
		}
		return false
	})

	for i, line := range lines {
		lines[i] = strings.TrimSpace(line)
	}

	return lines
}

func tokenizeLine(line string) (string, string, error) {
	if i := strings.Index(line, "="); i != -1 {
		return strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:]), nil
	}

	if len(strings.TrimSpace(line)) == 0 {
		return "", "", nil
	}

	return "", "", errors.New("Unable to tokenize line")
}

func tokenizeChar(value string) (chr byte, x, y, w, h, a, c float64) {
	z := strings.Split(value, ",")
	if len(z[0]) == 3 {
		chr = z[0][1]
	} else if len(z[0]) == 1 {
		chr = ','
	}

	x1, _ := strconv.ParseFloat(z[1], 32)
	x = x1
	y1, _ := strconv.ParseFloat(z[2], 32)
	y = y1
	w1, _ := strconv.ParseFloat(z[3], 32)
	w = w1
	h1, _ := strconv.ParseFloat(z[4], 32)
	h = h1
	a1, _ := strconv.ParseFloat(z[5], 32)
	a = a1
	c1, _ := strconv.ParseFloat(z[6], 32)
	c = c1

	return
}

func NewFont(filename string, arg ...interface{}) *Font {
	mipmap := false

	if len(arg) == 1 {
		if m, ok := arg[0].(bool); ok {
			mipmap = m
		}
	}

	f := new(Font)

	f.hge = Create(hge.VERSION)

	f.scale, f.proportion = 1.0, 1.0
	f.spacing = 1.0

	f.z = 0.5
	f.blend = hge.BLEND_COLORMUL | hge.BLEND_ALPHABLEND | hge.BLEND_NOZWRITE
	f.color = 0xFFFFFFFF

	desc := f.hge.ResourceLoadString(filename)

	if desc == nil {
		return nil
	}

	lines := getLines(string(*desc))

	if len(lines) == 0 || lines[0] != fntHEADERTAG {
		f.hge.System_Log("Font %s has incorrect format.", filename)
		return nil
	}

	// parse the font description
	for _, line := range lines {
		option, value, err := tokenizeLine(line)

		if err != nil || len(line) == 0 || len(option) == 0 || len(value) == 0 {
			f.hge.System_Log("Unreadable line in font file:", filename)
			continue
		}

		if option == fntBITMAPTAG {
			f.texture = f.hge.Texture_Load(value, 0, mipmap)
		} else if option == fntCHARTAG {
			chr, x, y, w, h, a, c := tokenizeChar(value)

			sprt := NewSprite(f.texture, x, y, w, h)

			f.letters[chr] = &sprt
			f.pre[chr] = a
			f.post[chr] = c
			f.height = h
		}
	}

	return f
}

func (f *Font) Render(x, y float64, align int, str string) {
	fx := x

	align &= TEXT_HORZMASK
	if align == TEXT_RIGHT {
		fx -= f.GetStringWidth(str, false)
	}
	if align == TEXT_CENTER {
		fx -= f.GetStringWidth(str, false) / 2.0
	}

	for i, chr := range str {
		if chr == '\n' {
			y += f.height * f.scale * f.spacing
			fx = x

			if align == TEXT_RIGHT {
				fx -= f.GetStringWidth(string(str[i+1]), false)
			}
			if align == TEXT_CENTER {
				fx -= f.GetStringWidth(string(str[i+1]), false) / 2.0
			}
		} else {
			j := chr
			if f.letters[j] == nil {
				j = '?'
			}
			if f.letters[j] != nil {
				fx += f.pre[j] * f.scale * f.proportion
				f.letters[j].RenderEx(fx, y, f.rot, f.scale*f.proportion, f.scale)
				fx += (f.letters[j].GetWidth() + f.post[j] + f.tracking) * f.scale * f.proportion
			}
		}
	}
}

func (f *Font) Printf(x, y float64, align int, format string, arg ...interface{}) {
	f.Render(x, y, align, fmt.Sprintf(format, arg...))
}

func (f *Font) Printfb(x, y, w, h float64, align int, format string, arg ...interface{}) {
}

func (f *Font) SetColor(color hge.Dword) {
	f.color = color

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetColor(color)
		}
	}
}

func (f *Font) SetZ(z float64) {
	f.z = z

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetZ(z)
		}
	}
}

func (f *Font) SetBlendMode(blend int) {
	f.blend = blend

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetBlendMode(blend)
		}
	}
}

func (f *Font) SetScale(scale float64) {
	f.scale = scale
}

func (f *Font) SetProportion(prop float64) {
	f.proportion = prop
}

func (f *Font) SetRotation(rot float64) {
	f.rot = rot
}

func (f *Font) SetTracking(tracking float64) {
	f.tracking = tracking
}

func (f *Font) SetSpacing(spacing float64) {
	f.spacing = spacing
}

func (f Font) GetColor() hge.Dword {
	return f.color
}

func (f Font) GetZ() float64 {
	return f.z
}

func (f Font) GetBlendMode() int {
	return f.blend
}

func (f Font) GetScale() float64 {
	return f.scale
}

func (f Font) GetProportion() float64 {
	return f.proportion
}

func (f Font) GetRotation() float64 {
	return f.rot
}

func (f Font) GetTracking() float64 {
	return f.tracking
}

func (f Font) GetSpacing() float64 {
	return f.spacing
}

func (f Font) GetSprite(chr byte) *Sprite {
	return f.letters[chr]
}

func (f Font) GetPreWidth(chr byte) float64 {
	return f.pre[chr]
}

func (f Font) GetPostWidth(chr byte) float64 {
	return f.post[chr]
}

func (f Font) GetHeight() float64 {
	return f.height
}

func (f Font) GetStringWidth(str string, arg ...interface{}) float64 {
	multiline := true
	w := 0.0

	if len(arg) == 1 {
		if m, ok := arg[0].(bool); ok {
			multiline = m
		}
	}

	for _, chr := range str {
		linew := 0.0

		if chr != '\n' {
			i := chr

			if f.letters[i] == nil {
				i = '?'
			}
			if f.letters[i] != nil {
				linew += f.letters[i].GetWidth() + f.pre[i] + f.post[i] + f.tracking
			}
		}

		if !multiline {
			return linew * f.scale * f.proportion
		}

		if linew > w {
			w = linew
		}

		for chr == '\n' || chr == '\r' {
			continue
		}
	}

	return w * f.scale * f.proportion
}
<commit_msg>Add an log for when the font file is empty<commit_after>package font

import (
	"C"
	"errors"
	"fmt"
	. "github.com/losinggeneration/hge-go/helpers/sprite"
	hge "github.com/losinggeneration/hge-go/hge"
	. "github.com/losinggeneration/hge-go/legacy"
	"strconv"
	"strings"
)

const (
	TEXT_LEFT     = 0
	TEXT_RIGHT    = 1
	TEXT_CENTER   = 2
	TEXT_HORZMASK = 0x03

	TEXT_TOP      = 0
	TEXT_BOTTOM   = 4
	TEXT_MIDDLE   = 8
	TEXT_VERTMASK = 0x0C
)

const (
	fntHEADERTAG = "[HGEFONT]"
	fntBITMAPTAG = "Bitmap"
	fntCHARTAG   = "Char"
)

/*
 * * HGE Font class
 */
type Font struct {
	hge *HGE

	texture    hge.Texture
	letters    [256]*Sprite
	pre        [256]float64
	post       [256]float64
	height     float64
	scale      float64
	proportion float64
	rot        float64
	tracking   float64
	spacing    float64

	color hge.Dword
	z     float64
	blend int
}

func getLines(file string) []string {
	lines := strings.FieldsFunc(file, func(r rune) bool {
		if r == '\n' || r == '\r' {
			return true
		}
		return false
	})

	for i, line := range lines {
		lines[i] = strings.TrimSpace(line)
	}

	return lines
}

func tokenizeLine(line string) (string, string, error) {
	if i := strings.Index(line, "="); i != -1 {
		return strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:]), nil
	}

	if len(strings.TrimSpace(line)) == 0 {
		return "", "", nil
	}

	return "", "", errors.New("Unable to tokenize line")
}

func tokenizeChar(value string) (chr byte, x, y, w, h, a, c float64) {
	z := strings.Split(value, ",")
	if len(z[0]) == 3 {
		chr = z[0][1]
	} else if len(z[0]) == 1 {
		chr = ','
	}

	x1, _ := strconv.ParseFloat(z[1], 32)
	x = x1
	y1, _ := strconv.ParseFloat(z[2], 32)
	y = y1
	w1, _ := strconv.ParseFloat(z[3], 32)
	w = w1
	h1, _ := strconv.ParseFloat(z[4], 32)
	h = h1
	a1, _ := strconv.ParseFloat(z[5], 32)
	a = a1
	c1, _ := strconv.ParseFloat(z[6], 32)
	c = c1

	return
}

func NewFont(filename string, arg ...interface{}) *Font {
	mipmap := false

	if len(arg) == 1 {
		if m, ok := arg[0].(bool); ok {
			mipmap = m
		}
	}

	f := new(Font)

	f.hge = Create(hge.VERSION)

	f.scale, f.proportion = 1.0, 1.0
	f.spacing = 1.0

	f.z = 0.5
	f.blend = hge.BLEND_COLORMUL | hge.BLEND_ALPHABLEND | hge.BLEND_NOZWRITE
	f.color = 0xFFFFFFFF

	desc := f.hge.ResourceLoadString(filename)

	if desc == nil {
		f.hge.System_Log("Font %s seems to be empty.", filename)
		return nil
	}

	lines := getLines(*desc)

	if len(lines) == 0 || lines[0] != fntHEADERTAG {
		f.hge.System_Log("Font %s has incorrect format.", filename)
		return nil
	}

	// parse the font description
	for _, line := range lines {
		option, value, err := tokenizeLine(line)

		if err != nil || len(line) == 0 || len(option) == 0 || len(value) == 0 {
			f.hge.System_Log("Unreadable line in font file:", filename)
			continue
		}

		if option == fntBITMAPTAG {
			f.texture = f.hge.Texture_Load(value, 0, mipmap)
		} else if option == fntCHARTAG {
			chr, x, y, w, h, a, c := tokenizeChar(value)

			sprt := NewSprite(f.texture, x, y, w, h)

			f.letters[chr] = &sprt
			f.pre[chr] = a
			f.post[chr] = c
			f.height = h
		}
	}

	return f
}

func (f *Font) Render(x, y float64, align int, str string) {
	fx := x

	align &= TEXT_HORZMASK
	if align == TEXT_RIGHT {
		fx -= f.GetStringWidth(str, false)
	}
	if align == TEXT_CENTER {
		fx -= f.GetStringWidth(str, false) / 2.0
	}

	for i, chr := range str {
		if chr == '\n' {
			y += f.height * f.scale * f.spacing
			fx = x

			if align == TEXT_RIGHT {
				fx -= f.GetStringWidth(string(str[i+1]), false)
			}
			if align == TEXT_CENTER {
				fx -= f.GetStringWidth(string(str[i+1]), false) / 2.0
			}
		} else {
			j := chr
			if f.letters[j] == nil {
				j = '?'
			}
			if f.letters[j] != nil {
				fx += f.pre[j] * f.scale * f.proportion
				f.letters[j].RenderEx(fx, y, f.rot, f.scale*f.proportion, f.scale)
				fx += (f.letters[j].GetWidth() + f.post[j] + f.tracking) * f.scale * f.proportion
			}
		}
	}
}

func (f *Font) Printf(x, y float64, align int, format string, arg ...interface{}) {
	f.Render(x, y, align, fmt.Sprintf(format, arg...))
}

func (f *Font) Printfb(x, y, w, h float64, align int, format string, arg ...interface{}) {
}

func (f *Font) SetColor(color hge.Dword) {
	f.color = color

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetColor(color)
		}
	}
}

func (f *Font) SetZ(z float64) {
	f.z = z

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetZ(z)
		}
	}
}

func (f *Font) SetBlendMode(blend int) {
	f.blend = blend

	for i := 0; i < 256; i++ {
		if f.letters[i] != nil {
			f.letters[i].SetBlendMode(blend)
		}
	}
}

func (f *Font) SetScale(scale float64) {
	f.scale = scale
}

func (f *Font) SetProportion(prop float64) {
	f.proportion = prop
}

func (f *Font) SetRotation(rot float64) {
	f.rot = rot
}

func (f *Font) SetTracking(tracking float64) {
	f.tracking = tracking
}

func (f *Font) SetSpacing(spacing float64) {
	f.spacing = spacing
}

func (f Font) GetColor() hge.Dword {
	return f.color
}

func (f Font) GetZ() float64 {
	return f.z
}

func (f Font) GetBlendMode() int {
	return f.blend
}

func (f Font) GetScale() float64 {
	return f.scale
}

func (f Font) GetProportion() float64 {
	return f.proportion
}

func (f Font) GetRotation() float64 {
	return f.rot
}

func (f Font) GetTracking() float64 {
	return f.tracking
}

func (f Font) GetSpacing() float64 {
	return f.spacing
}

func (f Font) GetSprite(chr byte) *Sprite {
	return f.letters[chr]
}

func (f Font) GetPreWidth(chr byte) float64 {
	return f.pre[chr]
}

func (f Font) GetPostWidth(chr byte) float64 {
	return f.post[chr]
}

func (f Font) GetHeight() float64 {
	return f.height
}

func (f Font) GetStringWidth(str string, arg ...interface{}) float64 {
	multiline := true
	w := 0.0

	if len(arg) == 1 {
		if m, ok := arg[0].(bool); ok {
			multiline = m
		}
	}

	for _, chr := range str {
		linew := 0.0

		if chr != '\n' {
			i := chr

			if f.letters[i] == nil {
				i = '?'
			}
			if f.letters[i] != nil {
				linew += f.letters[i].GetWidth() + f.pre[i] + f.post[i] + f.tracking
			}
		}

		if !multiline {
			return linew * f.scale * f.proportion
		}

		if linew > w {
			w = linew
		}

		for chr == '\n' || chr == '\r' {
			continue
		}
	}

	return w * f.scale * f.proportion
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"net/http"
	"sort"
	"time"

	"github.com/vespa-engine/vespa/client/go/util"
)

func listSampleApps() ([]string, error) {
	return listSampleAppsAt("https://api.github.com/repos/vespa-engine/sample-apps/contents/")
}

func listSampleAppsAt(url string) ([]string, error) {
	rfs, err := getRepositoryFiles(url)
	if err != nil {
		return nil, err
	}
	var apps []string
	for _, rf := range rfs {
		isApp, follow := isApp(rf)
		if isApp {
			apps = append(apps, rf.Path)
		} else if follow {
			apps2, err := listSampleAppsAt(rf.URL)
			if err != nil {
				return nil, err
			}
			apps = append(apps, apps2...)
		}
	}
	sort.Strings(apps)
	return apps, nil
}

func getRepositoryFiles(url string) ([]repositoryFile, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	response, err := util.HttpDo(req, time.Minute, "GitHub")
	var files []repositoryFile
	dec := json.NewDecoder(response.Body)
	if err := dec.Decode(&files); err != nil {
		return nil, err
	}
	return files, nil
}

func isApp(rf repositoryFile) (ok bool, follow bool) {
	if rf.Type != "dir" {
		return false, false
	}
	if rf.Path == "" {
		return false, false
	}
	if rf.Path[0] == '_' || rf.Path[0] == '.' {
		return false, false
	}
	// These are just heuristics and must be updated if we add more directories that are not applications, or that
	// contain multiple applications inside
	switch rf.Name {
	case "test", "bin", "src":
		return false, false
	}
	switch rf.Path {
	case "news", "operations", "vespa-cloud":
		return false, true
	}
	return true, false
}

type repositoryFile struct {
	Path    string `json:"path"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	URL     string `json:"url"`
	HtmlURL string `json:"html_url"`
}
<commit_msg>Close response<commit_after>package cmd

import (
	"encoding/json"
	"net/http"
	"sort"
	"time"

	"github.com/vespa-engine/vespa/client/go/util"
)

func listSampleApps() ([]string, error) {
	return listSampleAppsAt("https://api.github.com/repos/vespa-engine/sample-apps/contents/")
}

func listSampleAppsAt(url string) ([]string, error) {
	rfs, err := getRepositoryFiles(url)
	if err != nil {
		return nil, err
	}
	var apps []string
	for _, rf := range rfs {
		isApp, follow := isApp(rf)
		if isApp {
			apps = append(apps, rf.Path)
		} else if follow {
			apps2, err := listSampleAppsAt(rf.URL)
			if err != nil {
				return nil, err
			}
			apps = append(apps, apps2...)
		}
	}
	sort.Strings(apps)
	return apps, nil
}

func getRepositoryFiles(url string) ([]repositoryFile, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	response, err := util.HttpDo(req, time.Minute, "GitHub")
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	var files []repositoryFile
	dec := json.NewDecoder(response.Body)
	if err := dec.Decode(&files); err != nil {
		return nil, err
	}
	return files, nil
}

func isApp(rf repositoryFile) (ok bool, follow bool) {
	if rf.Type != "dir" {
		return false, false
	}
	if rf.Path == "" {
		return false, false
	}
	if rf.Path[0] == '_' || rf.Path[0] == '.' {
		return false, false
	}
	// These are just heuristics and must be updated if we add more directories that are not applications, or that
	// contain multiple applications inside
	switch rf.Name {
	case "test", "bin", "src":
		return false, false
	}
	switch rf.Path {
	case "news", "operations", "vespa-cloud":
		return false, true
	}
	return true, false
}

type repositoryFile struct {
	Path    string `json:"path"`
	Name    string `json:"name"`
	Type    string `json:"type"`
	URL     string `json:"url"`
	HtmlURL string `json:"html_url"`
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
)

// JSONReport - The true output of a deeply disturbed,
// incurably depressed and manically suicidal program named ski.
type JSONReport struct {
	Meta    Opts            `json:"meta,omitempty"`
	Planets []PlanetWrapper `json:"planets"`
}

// PlanetWrapper ...
type PlanetWrapper struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	User       string `json:"user"`
	Host       string `json:"host"`
	PlanetType string `json:"planet_type"`
	DbID       string `json:"db_id"`
	Valid      bool   `json:"valid"`
	Output     string `json:"output"`
	Index      int    `json:"index"`
	Errored    bool   `json:"errored"`
}

func decode(jsonObject string) ([][]string, error) {
	var jsonBlob = []byte(jsonObject)
	var toReturn = make([][]string, 0)
	err := json.Unmarshal(jsonBlob, &toReturn)
	if err != nil {
		log.Errorln(err)
		return make([][]string, 0), err
	}
	return toReturn, nil
}

func writeResultAsJSON(planets []Planet, opts *Opts, writer io.Writer) {
	allInOne := JSONReport{}
	allInOne.Planets = make([]PlanetWrapper, len(planets))
	allInOne.Meta = *opts
	for i, planet := range planets {
		wrapper := PlanetWrapper{
			ID:         planet.id,
			Name:       planet.name,
			User:       planet.user,
			Host:       planet.host,
			PlanetType: planet.planetType,
			DbID:       planet.dbID,
			Valid:      planet.valid,
			Output:     planet.outputStruct.output,
			Index:      i,
			Errored:    planet.outputStruct.errored,
		}
		allInOne.Planets[i] = wrapper
	}

	json, err := json.MarshalIndent(allInOne, "", "    ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error marshaling the output as json %s\n", err.Error())
		fmt.Fprintf(os.Stderr, "Non json %v\n", allInOne)
		os.Exit(1)
	}

	fmt.Fprintf(writer, "%s\n", json)
}

func createJSONReport(options map[string]string, planets []Planet, opts *Opts) {
	basename := strings.Split(options["job_name"], ".")[0]
	home := options["orbit_home"]
	reports := options["output"]
	if len(basename) == 0 || len(home) == 0 || len(reports) == 0 {
		log.Fatalf("Could not create json output for the job %s", opts.String())
	}

	folders := path.Join(home, reports, basename)
	err := os.MkdirAll(folders, 0744)

	if err != nil {
		log.Fatalf("Could not create json output for the job %s", opts.String())
	}

	removeOldOutput(folders, opts.MaxToKeep)
	now := time.Now()
	format := "%d-%02d-%02dT%02d_%02d_%02d"
	stamp := fmt.Sprintf(format, now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second())
	fileToWrite := strings.Join([]string{stamp, "json"}, ".")
	toCreate := path.Join(folders, fileToWrite)

	if writer, err := os.Create(toCreate); err == nil {
		defer writer.Close()
		writeResultAsJSON(planets, opts, writer)
		return
	}
	log.Fatalf("Could not create json output for the job %s", basename)
}

func removeOldOutput(dir string, maxToKeep int) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Fatalf("Could not read files in folder %s", dir)
	}

	total := len(files)

	if total > maxToKeep {
		diff := total - maxToKeep
		toDelete := files[:diff]

		for _, file := range toDelete {
			name := file.Name()
			abs := path.Join(dir, name)
			log.Infoln("removing old output " + abs)
			if err := os.Remove(abs); err != nil {
				fmt.Println("removing old output " + abs + " failed")
				log.Errorln("removing old output " + abs + " failed")
			}
		}
	}
}

// creates a task from a json file
func createATaskFromJobFile(jsonFile string) (opts Opts) {
	job := Opts{}
	wcopy := jsonFile // assumption abs path
	tokens := strings.Split(jsonFile, string(os.PathSeparator))

	if len(tokens) == 1 {
		// relative path given, read from jobs folder
		wcopy = path.Join(os.Getenv("ORBIT_HOME"), "jobs", jsonFile)
	}
	var err error
	var bytes []byte
	if bytes, err = ioutil.ReadFile(wcopy); err != nil {
		errorMessage := fmt.Sprintf("%s : %s", err.Error(), jsonFile)
		fmt.Fprint(os.Stderr, errorMessage)
		log.Fatal(errorMessage)
	}

	if json.Unmarshal(bytes, &job); err != nil {
		errorMessage := fmt.Sprintf("%s : %s", err.Error(), jsonFile)
		fmt.Fprint(os.Stderr, errorMessage)
		log.Fatal(errorMessage)
	}

	log.Debugf("Read a task from %s:", jsonFile)
	log.Debugf("Unmarshalled %v", job)
	return job
}
<commit_msg>Ticket #44: removed log msg to stdout<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
)

// JSONReport - The true output of a deeply disturbed,
// incurably depressed and manically suicidal program named ski.
type JSONReport struct {
	Meta    Opts            `json:"meta,omitempty"`
	Planets []PlanetWrapper `json:"planets"`
}

// PlanetWrapper ...
type PlanetWrapper struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	User       string `json:"user"`
	Host       string `json:"host"`
	PlanetType string `json:"planet_type"`
	DbID       string `json:"db_id"`
	Valid      bool   `json:"valid"`
	Output     string `json:"output"`
	Index      int    `json:"index"`
	Errored    bool   `json:"errored"`
}

func decode(jsonObject string) ([][]string, error) {
	var jsonBlob = []byte(jsonObject)
	var toReturn = make([][]string, 0)
	err := json.Unmarshal(jsonBlob, &toReturn)
	if err != nil {
		log.Errorln(err)
		return make([][]string, 0), err
	}
	return toReturn, nil
}

func writeResultAsJSON(planets []Planet, opts *Opts, writer io.Writer) {
	allInOne := JSONReport{}
	allInOne.Planets = make([]PlanetWrapper, len(planets))
	allInOne.Meta = *opts
	for i, planet := range planets {
		wrapper := PlanetWrapper{
			ID:         planet.id,
			Name:       planet.name,
			User:       planet.user,
			Host:       planet.host,
			PlanetType: planet.planetType,
			DbID:       planet.dbID,
			Valid:      planet.valid,
			Output:     planet.outputStruct.output,
			Index:      i,
			Errored:    planet.outputStruct.errored,
		}
		allInOne.Planets[i] = wrapper
	}

	json, err := json.MarshalIndent(allInOne, "", "    ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error marshaling the output as json %s\n", err.Error())
		fmt.Fprintf(os.Stderr, "Non json %v\n", allInOne)
		os.Exit(1)
	}

	fmt.Fprintf(writer, "%s\n", json)
}

func createJSONReport(options map[string]string, planets []Planet, opts *Opts) {
	basename := strings.Split(options["job_name"], ".")[0]
	home := options["orbit_home"]
	reports := options["output"]
	if len(basename) == 0 || len(home) == 0 || len(reports) == 0 {
		log.Fatalf("Could not create json output for the job %s", opts.String())
	}

	folders := path.Join(home, reports, basename)
	err := os.MkdirAll(folders, 0744)

	if err != nil {
		log.Fatalf("Could not create json output for the job %s", opts.String())
	}

	removeOldOutput(folders, opts.MaxToKeep)
	now := time.Now()
	format := "%d-%02d-%02dT%02d_%02d_%02d"
	stamp := fmt.Sprintf(format, now.Year(), now.Month(), now.Day(),
		now.Hour(), now.Minute(), now.Second())
	fileToWrite := strings.Join([]string{stamp, "json"}, ".")
	toCreate := path.Join(folders, fileToWrite)

	if writer, err := os.Create(toCreate); err == nil {
		defer writer.Close()
		writeResultAsJSON(planets, opts, writer)
		return
	}
	log.Fatalf("Could not create json output for the job %s", basename)
}

func removeOldOutput(dir string, maxToKeep int) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Fatalf("Could not read files in folder %s", dir)
	}

	total := len(files)

	if total > maxToKeep {
		diff := total - maxToKeep
		toDelete := files[:diff]

		for _, file := range toDelete {
			name := file.Name()
			abs := path.Join(dir, name)
			log.Infoln("removing old output " + abs)
			if err := os.Remove(abs); err != nil {
				log.Errorln("removing old output " + abs + " failed")
			}
		}
	}
}

// creates a task from a json file
func createATaskFromJobFile(jsonFile string) (opts Opts) {
	job := Opts{}
	wcopy := jsonFile // assumption abs path
	tokens := strings.Split(jsonFile, string(os.PathSeparator))

	if len(tokens) == 1 {
		// relative path given, read from jobs folder
		wcopy = path.Join(os.Getenv("ORBIT_HOME"), "jobs", jsonFile)
	}
	var err error
	var bytes []byte
	if bytes, err = ioutil.ReadFile(wcopy); err != nil {
		errorMessage := fmt.Sprintf("%s : %s", err.Error(), jsonFile)
		fmt.Fprint(os.Stderr, errorMessage)
		log.Fatal(errorMessage)
	}

	if json.Unmarshal(bytes, &job); err != nil {
		errorMessage := fmt.Sprintf("%s : %s", err.Error(), jsonFile)
		fmt.Fprint(os.Stderr, errorMessage)
		log.Fatal(errorMessage)
	}

	log.Debugf("Read a task from %s:", jsonFile)
	log.Debugf("Unmarshalled %v", job)
	return job
}
<|endoftext|>


<commit_before>package datatrade

import (
	"errors"
	"fmt"
	"math/rand"
	"net/url"
	"path/filepath"

	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/zfs"
	"github.com/pborman/uuid"
)

// DatasetImportArgs are arguments for configuring an imported dataset.
type DatasetImportArgs struct {
	NFS        bool   `json:"nfs"`
	Quota      uint64 `json:"quota"`
	ReadOnly   bool   `json:"readOnly"`
	Redundancy uint64 `json:"redundancy"`
}

// DatasetImportResult is the result of a dataset import.
type DatasetImportResult struct {
	Dataset clusterconf.Dataset `json:"dataset"`
	NodeID  string              `json:"nodeID"`
}

// DatasetImport imports a dataset into the cluster and tracks it in the
// cluster configuration.
func (p *Provider) DatasetImport(req *acomm.Request) (interface{}, *url.URL, error) {
	var args DatasetImportArgs
	if err := req.UnmarshalArgs(&args); err != nil {
		return nil, nil, err
	}
	if args.Redundancy == 0 {
		return nil, nil, errors.New("missing arg: redundancy")
	}

	if req.StreamURL == nil {
		return nil, nil, errors.New("missing request streamURL")
	}

	dataset := clusterconf.Dataset{
		ID:         uuid.New(),
		NFS:        args.ReadOnly,
		Quota:      args.Quota,
		ReadOnly:   args.ReadOnly,
		Redundancy: args.Redundancy,
	}

	node, err := p.datasetImportNode()
	if err != nil {
		return nil, nil, err
	}

	if err := p.datasetImport(node.ID, dataset.ID, req.StreamURL); err != nil {
		return nil, nil, err
	}

	return DatasetImportResult{Dataset: dataset, NodeID: node.ID}, nil, p.datasetConfig(dataset)

}

func (p *Provider) datasetImportNode() (*clusterconf.Node, error) {
	opts := acomm.RequestOptions{
		Task: "list-nodes",
	}
	resp, err := p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	if err != nil {
		return nil, err
	}
	var result clusterconf.ListNodesResult
	if err := resp.UnmarshalResult(&result); err != nil {
		return nil, err
	}
	if len(result.Nodes) == 0 {
		return nil, errors.New("no nodes found")
	}
	node := result.Nodes[rand.Intn(len(result.Nodes))]
	return &node, nil
}

func (p *Provider) datasetImport(nodeID, datasetID string, streamURL *url.URL) error {
	taskURL, err := url.ParseRequestURI(fmt.Sprintf("http://%s:%d", nodeID, p.config.NodeCoordinatorPort()))
	if err != nil {
		return err
	}
	opts := acomm.RequestOptions{
		Task:      "zfs-receive",
		TaskURL:   taskURL,
		StreamURL: streamURL,
		Args: zfs.CommonArgs{
			Name: filepath.Join(p.config.DatasetDir(), datasetID),
		},
	}
	_, err = p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	return err
}

func (p *Provider) datasetConfig(dataset clusterconf.Dataset) error {
	opts := acomm.RequestOptions{
		Task: "update-dataset",
		Args: clusterconf.DatasetPayload{Dataset: &dataset},
	}
	_, err := p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	return err
}
<commit_msg>datatrade - Create an {id}@{id} snapshot of a RO dataset after import<commit_after>package datatrade

import (
	"errors"
	"fmt"
	"math/rand"
	"net/url"
	"path/filepath"

	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/zfs"
	"github.com/pborman/uuid"
)

// DatasetImportArgs are arguments for configuring an imported dataset.
type DatasetImportArgs struct {
	NFS        bool   `json:"nfs"`
	Quota      uint64 `json:"quota"`
	ReadOnly   bool   `json:"readOnly"`
	Redundancy uint64 `json:"redundancy"`
}

// DatasetImportResult is the result of a dataset import.
type DatasetImportResult struct {
	Dataset clusterconf.Dataset `json:"dataset"`
	NodeID  string              `json:"nodeID"`
}

// DatasetImport imports a dataset into the cluster and tracks it in the
// cluster configuration.
func (p *Provider) DatasetImport(req *acomm.Request) (interface{}, *url.URL, error) {
	var args DatasetImportArgs
	if err := req.UnmarshalArgs(&args); err != nil {
		return nil, nil, err
	}
	if args.Redundancy == 0 {
		return nil, nil, errors.New("missing arg: redundancy")
	}

	if req.StreamURL == nil {
		return nil, nil, errors.New("missing request streamURL")
	}

	dataset := clusterconf.Dataset{
		ID:         uuid.New(),
		NFS:        args.ReadOnly,
		Quota:      args.Quota,
		ReadOnly:   args.ReadOnly,
		Redundancy: args.Redundancy,
	}

	node, err := p.datasetImportNode()
	if err != nil {
		return nil, nil, err
	}

	if err := p.datasetImport(node.ID, dataset.ID, req.StreamURL); err != nil {
		return nil, nil, err
	}

	if args.ReadOnly {
		if err := p.datasetSnapshot(node.ID, dataset.ID); err != nil {
			return nil, nil, err
		}
	}

	return DatasetImportResult{Dataset: dataset, NodeID: node.ID}, nil, p.datasetConfig(dataset)

}

func (p *Provider) datasetImportNode() (*clusterconf.Node, error) {
	opts := acomm.RequestOptions{
		Task: "list-nodes",
	}
	resp, err := p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	if err != nil {
		return nil, err
	}
	var result clusterconf.ListNodesResult
	if err := resp.UnmarshalResult(&result); err != nil {
		return nil, err
	}
	if len(result.Nodes) == 0 {
		return nil, errors.New("no nodes found")
	}
	node := result.Nodes[rand.Intn(len(result.Nodes))]
	return &node, nil
}

func (p *Provider) datasetImport(nodeID, datasetID string, streamURL *url.URL) error {
	taskURL, err := url.ParseRequestURI(fmt.Sprintf("http://%s:%d", nodeID, p.config.NodeCoordinatorPort()))
	if err != nil {
		return err
	}
	opts := acomm.RequestOptions{
		Task:      "zfs-receive",
		TaskURL:   taskURL,
		StreamURL: streamURL,
		Args: zfs.CommonArgs{
			Name: filepath.Join(p.config.DatasetDir(), datasetID),
		},
	}
	_, err = p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	return err
}

func (p *Provider) datasetSnapshot(nodeID, datasetID string) error {
	taskURL, err := url.ParseRequestURI(fmt.Sprintf("http://%s:%d", nodeID, p.config.NodeCoordinatorPort()))
	if err != nil {
		return err
	}
	opts := acomm.RequestOptions{
		Task:    "zfs-snapshot",
		TaskURL: taskURL,
		Args: zfs.SnapshotArgs{
			Name:      filepath.Join(p.config.DatasetDir(), datasetID),
			SnapName:  datasetID,
			Recursive: false,
		},
	}
	_, err = p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	return err
}

func (p *Provider) datasetConfig(dataset clusterconf.Dataset) error {
	opts := acomm.RequestOptions{
		Task: "update-dataset",
		Args: clusterconf.DatasetPayload{Dataset: &dataset},
	}
	_, err := p.tracker.SyncRequest(p.config.CoordinatorURL(), opts, p.config.RequestTimeout())
	return err
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) { gocheck.TestingT(t) }

type S struct {
	collName      string
	imageCollName string
	conn          *db.Storage
	gitHost       string
	repoNamespace string
}

var _ = gocheck.Suite(&S{})

func (s *S) SetUpSuite(c *gocheck.C) {
	s.collName = "docker_unit"
	s.imageCollName = "docker_image"
	s.gitHost = "my.gandalf.com"
	s.repoNamespace = "tsuru"
	config.Set("git:host", s.gitHost)
	config.Set("docker:repository-namespace", s.repoNamespace)
	config.Set("docker:binary", "docker")
	config.Set("docker:router", "fake")
	config.Set("docker:collection", s.collName)
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "juju_provision_tests_s")
	config.Set("docker:deploy-cmd", "/var/lib/tsuru/deploy")
	config.Set("docker:run-cmd:bin", "/usr/local/bin/circusd")
	config.Set("docker:run-cmd:args", "/etc/circus/circus.ini")
	config.Set("docker:run-cmd:port", "8888")
	var err error
	s.conn, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
}

func (s *S) TearDownSuite(c *gocheck.C) {
	s.conn.Collection(s.collName).Database.DropDatabase()
}
<commit_msg>provision/docker/suite_test: pre instantiating some commonly used confs<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"github.com/globocom/config"
	"github.com/globocom/tsuru/db"
	"launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) { gocheck.TestingT(t) }

type S struct {
	collName      string
	imageCollName string
	conn          *db.Storage
	gitHost       string
	repoNamespace string
	deployCmd     string
	runBin        string
	runArgs       string
	port          string
}

var _ = gocheck.Suite(&S{})

func (s *S) SetUpSuite(c *gocheck.C) {
	s.collName = "docker_unit"
	s.imageCollName = "docker_image"
	s.gitHost = "my.gandalf.com"
	s.repoNamespace = "tsuru"
	config.Set("git:host", s.gitHost)
	config.Set("docker:repository-namespace", s.repoNamespace)
	config.Set("docker:binary", "docker")
	config.Set("docker:router", "fake")
	config.Set("docker:collection", s.collName)
	config.Set("database:url", "127.0.0.1:27017")
	config.Set("database:name", "juju_provision_tests_s")
	config.Set("docker:deploy-cmd", "/var/lib/tsuru/deploy")
	config.Set("docker:run-cmd:bin", "/usr/local/bin/circusd")
	config.Set("docker:run-cmd:args", "/etc/circus/circus.ini")
	config.Set("docker:run-cmd:port", "8888")
	s.deployCmd = "/var/lib/tsuru/deploy"
	s.runBin = "/usr/local/bin/circusd"
	s.runArgs = "/etc/circus/circus.ini"
	s.port = "8888"
	var err error
	s.conn, err = db.Conn()
	c.Assert(err, gocheck.IsNil)
}

func (s *S) TearDownSuite(c *gocheck.C) {
	s.conn.Collection(s.collName).Database.DropDatabase()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"text/template"

	"github.com/kyokomi/gogen"
	"github.com/ttacon/chalk"
)

var (
	// command line flags
	out  string // output file
	file string // input file (or directory)
	pkg  string // output package name

)

var (
	baseTemplateText = `
// Sample sample code
func ({{.Varname}} *{{.Value.Struct.Name}}) Sample() {
	{{range .Value.Struct.Fields}}
	fmt.Println({{.FieldElem.Varname}})
	{{end}}
}
`
	sampleTemplate = template.Must(template.New("base").Parse(baseTemplateText))
)

func init() {
	flag.StringVar(&out, "o", "", "output file")
	flag.StringVar(&file, "file", os.Getenv("GOFILE"), "input file")
	flag.StringVar(&pkg, "pkg", os.Getenv("GOPACKAGE"), "output package")
}

func main() {
	flag.Parse()

	if file == "" {
		fmt.Println(chalk.Red.Color("No file to parse."))
		os.Exit(1)
	}

	g := gogen.NewGenerator(file, out, pkg, "fmt")

	err := g.DoAllTemplate(sampleTemplate)

	if err != nil {
		fmt.Println(chalk.Red.Color(err.Error()))
		os.Exit(1)
	}
}
<commit_msg>removed: exmaple code import<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"text/template"

	"github.com/kyokomi/gogen"
)

var (
	// command line flags
	out  string // output file
	file string // input file (or directory)
	pkg  string // output package name

)

var (
	baseTemplateText = `
// Sample sample code
func ({{.Varname}} *{{.Value.Struct.Name}}) Sample() {
	{{range .Value.Struct.Fields}}
	fmt.Println({{.FieldElem.Varname}})
	{{end}}
}
`
	sampleTemplate = template.Must(template.New("base").Parse(baseTemplateText))
)

func init() {
	flag.StringVar(&out, "o", "", "output file")
	flag.StringVar(&file, "file", os.Getenv("GOFILE"), "input file")
	flag.StringVar(&pkg, "pkg", os.Getenv("GOPACKAGE"), "output package")
}

func main() {
	flag.Parse()

	if file == "" {
		fmt.Println("No file to parse.")
		os.Exit(1)
	}

	g := gogen.NewGenerator(file, out, pkg, "fmt")

	err := g.DoAllTemplate(sampleTemplate)

	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/route53resolver"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/waiter"
)

func resourceAwsRoute53ResolverQueryLogConfig() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRoute53ResolverQueryLogConfigCreate,
		Read:   resourceAwsRoute53ResolverQueryLogConfigRead,
		Update: resourceAwsRoute53ResolverQueryLogConfigUpdate,
		Delete: resourceAwsRoute53ResolverQueryLogConfigDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"destination_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},

			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateRoute53ResolverName,
			},

			"owner_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"share_status": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

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

	input := &route53resolver.CreateResolverQueryLogConfigInput{
		CreatorRequestId: aws.String(resource.PrefixedUniqueId("tf-r53-resolver-query-log-config-")),
		DestinationArn:   aws.String(d.Get("destination_arn").(string)),
		Name:             aws.String(d.Get("name").(string)),
	}
	if v, ok := d.GetOk("tags"); ok && len(v.(map[string]interface{})) > 0 {
		input.Tags = keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().Route53resolverTags()
	}

	log.Printf("[DEBUG] Creating Route53 Resolver Query Log Config: %s", input)
	output, err := conn.CreateResolverQueryLogConfig(input)

	if err != nil {
		return fmt.Errorf("error creating Route53 Resolver Query Log Config: %w", err)
	}

	d.SetId(aws.StringValue(output.ResolverQueryLogConfig.Id))

	_, err = waiter.QueryLogConfigCreated(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error waiting for Route53 Resolver Query Log Config (%s) to become available: %w", d.Id(), err)
	}

	return resourceAwsRoute53ResolverQueryLogConfigRead(d, meta)
}

func resourceAwsRoute53ResolverQueryLogConfigRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53resolverconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	queryLogConfig, err := finder.ResolverQueryLogConfigByID(conn, d.Id())

	if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] Route53 Resolver Query Log Config (%s), removing from state", d.Id())
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading Route53 Resolver Query Log Config (%s): %w", d.Id(), err)
	}

	if queryLogConfig == nil {
		log.Printf("[WARN] Route53 Resolver Query Log Config (%s), removing from state", d.Id())
		d.SetId("")
		return nil
	}

	arn := aws.StringValue(queryLogConfig.Arn)
	d.Set("arn", arn)
	d.Set("destination_arn", queryLogConfig.DestinationArn)
	d.Set("name", queryLogConfig.Name)
	d.Set("owner_id", queryLogConfig.OwnerId)
	d.Set("share_status", queryLogConfig.ShareStatus)

	tags, err := keyvaluetags.Route53resolverListTags(conn, arn)
	if err != nil {
		return fmt.Errorf("error listing tags for Route53 Resolver Query Log Config (%s): %w", arn, err)
	}

	if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %w", err)
	}

	return nil
}

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

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")
		if err := keyvaluetags.Route53resolverUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
			return fmt.Errorf("error updating Route53 Resolver Query Log Config (%s) tags: %s", d.Get("arn").(string), err)
		}
	}

	return resourceAwsRoute53ResolverQueryLogConfigRead(d, meta)
}

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

	log.Printf("[DEBUG] Deleting Route53 Resolver Query Log Config (%s)", d.Id())
	_, err := conn.DeleteResolverQueryLogConfig(&route53resolver.DeleteResolverQueryLogConfigInput{
		ResolverQueryLogConfigId: aws.String(d.Id()),
	})
	if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") {
		return nil
	}
	if err != nil {
		return fmt.Errorf("error deleting Route53 Resolver Query Log Config (%s): %w", d.Id(), err)
	}

	_, err = waiter.QueryLogConfigDeleted(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error waiting for Route53 Resolver Query Log Config (%s) to be deleted: %w", d.Id(), err)
	}

	return nil
}
<commit_msg>Tweak log message when resource not found.<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/route53resolver"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/waiter"
)

func resourceAwsRoute53ResolverQueryLogConfig() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRoute53ResolverQueryLogConfigCreate,
		Read:   resourceAwsRoute53ResolverQueryLogConfigRead,
		Update: resourceAwsRoute53ResolverQueryLogConfigUpdate,
		Delete: resourceAwsRoute53ResolverQueryLogConfigDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"destination_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},

			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateRoute53ResolverName,
			},

			"owner_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"share_status": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"tags": tagsSchema(),
		},
	}
}

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

	input := &route53resolver.CreateResolverQueryLogConfigInput{
		CreatorRequestId: aws.String(resource.PrefixedUniqueId("tf-r53-resolver-query-log-config-")),
		DestinationArn:   aws.String(d.Get("destination_arn").(string)),
		Name:             aws.String(d.Get("name").(string)),
	}
	if v, ok := d.GetOk("tags"); ok && len(v.(map[string]interface{})) > 0 {
		input.Tags = keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().Route53resolverTags()
	}

	log.Printf("[DEBUG] Creating Route53 Resolver Query Log Config: %s", input)
	output, err := conn.CreateResolverQueryLogConfig(input)

	if err != nil {
		return fmt.Errorf("error creating Route53 Resolver Query Log Config: %w", err)
	}

	d.SetId(aws.StringValue(output.ResolverQueryLogConfig.Id))

	_, err = waiter.QueryLogConfigCreated(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error waiting for Route53 Resolver Query Log Config (%s) to become available: %w", d.Id(), err)
	}

	return resourceAwsRoute53ResolverQueryLogConfigRead(d, meta)
}

func resourceAwsRoute53ResolverQueryLogConfigRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).route53resolverconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	queryLogConfig, err := finder.ResolverQueryLogConfigByID(conn, d.Id())

	if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] Route53 Resolver Query Log Config (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading Route53 Resolver Query Log Config (%s): %w", d.Id(), err)
	}

	if queryLogConfig == nil {
		log.Printf("[WARN] Route53 Resolver Query Log Config (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	arn := aws.StringValue(queryLogConfig.Arn)
	d.Set("arn", arn)
	d.Set("destination_arn", queryLogConfig.DestinationArn)
	d.Set("name", queryLogConfig.Name)
	d.Set("owner_id", queryLogConfig.OwnerId)
	d.Set("share_status", queryLogConfig.ShareStatus)

	tags, err := keyvaluetags.Route53resolverListTags(conn, arn)
	if err != nil {
		return fmt.Errorf("error listing tags for Route53 Resolver Query Log Config (%s): %w", arn, err)
	}

	if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %w", err)
	}

	return nil
}

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

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")
		if err := keyvaluetags.Route53resolverUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
			return fmt.Errorf("error updating Route53 Resolver Query Log Config (%s) tags: %s", d.Get("arn").(string), err)
		}
	}

	return resourceAwsRoute53ResolverQueryLogConfigRead(d, meta)
}

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

	log.Printf("[DEBUG] Deleting Route53 Resolver Query Log Config (%s)", d.Id())
	_, err := conn.DeleteResolverQueryLogConfig(&route53resolver.DeleteResolverQueryLogConfigInput{
		ResolverQueryLogConfigId: aws.String(d.Id()),
	})

	if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("error deleting Route53 Resolver Query Log Config (%s): %w", d.Id(), err)
	}

	_, err = waiter.QueryLogConfigDeleted(conn, d.Id())

	if err != nil {
		return fmt.Errorf("error waiting for Route53 Resolver Query Log Config (%s) to be deleted: %w", d.Id(), err)
	}

	return nil
}
<|endoftext|>