<commit_before>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"time"

	. "gopkg.in/check.v1"

	"github.com/hoffie/larasync/repository"
)

type RepoListCreateTests struct {
	server         *Server
	rm             *repository.Manager
	req            *http.Request
	repos          string
	repositoryName string
	pubKey         []byte
}

var _ = Suite(&RepoListCreateTests{})

func (t *RepoListCreateTests) requestWithBytes(c *C, body []byte) *http.Request {
	var httpBody io.Reader
	if body == nil {
		httpBody = nil
	} else {
		httpBody = bytes.NewReader(body)
	}
	return t.requestWithReader(c, httpBody)
}

func (t *RepoListCreateTests) requestWithReader(c *C, httpBody io.Reader) *http.Request {
	req, err := http.NewRequest(
		"PUT",
		fmt.Sprintf(
			"http://example.org/repositories/%s",
			t.repositoryName,
		),
		httpBody)
	c.Assert(err, IsNil)
	if httpBody != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	return req
}

func (t *RepoListCreateTests) SetUpTest(c *C) {
	t.repos = c.MkDir()
	t.repositoryName = "test"
	rm, err := repository.NewManager(t.repos)
	c.Assert(err, IsNil)
	t.server = New(adminPubkey, time.Minute, rm)
	c.Assert(rm.Exists(t.repositoryName), Equals, false)
	t.rm = rm
	t.req = t.requestWithBytes(c, nil)
}

func (t *RepoListCreateTests) SetUpSuite(c *C) {
	t.pubKey = make([]byte, PubkeySize)
}

func (t *RepoListCreateTests) TearDownTest(c *C) {
	os.RemoveAll(t.repos)
}

func (t *RepoListCreateTests) getResponse(req *http.Request) *httptest.ResponseRecorder {
	rw := httptest.NewRecorder()
	t.server.router.ServeHTTP(rw, req)
	return rw
}

func (t *RepoListCreateTests) addPubKey(c *C) {
	repository, err := json.Marshal(JSONRepository{
		PubKey: t.pubKey,
	})
	c.Assert(err, IsNil)
	t.req = t.requestWithBytes(c, repository)
}

func (t *RepoListCreateTests) TestRepoCreateUnauthorized(c *C) {
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusUnauthorized)
}

func (t *RepoListCreateTests) TestRepoCreateAdmin(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusCreated)
}

func (t *RepoListCreateTests) TestRepoCreateContentType(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	resp := t.getResponse(t.req)

	contentType := resp.Header().Get("Content-Type")
	c.Assert(
		strings.HasPrefix(
			contentType,
			"application/json"),
		Equals,
		true)
}

func (t *RepoListCreateTests) TestRepoCreateMangled(c *C) {
	SignWithPassphrase(t.req, adminSecret)
	t.req.Header.Set("Mangled", "Yes")
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusUnauthorized)
}

func (t *RepoListCreateTests) TestRepositoryCreate(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	t.getResponse(t.req)
	c.Assert(
		t.rm.Exists(t.repositoryName),
		Equals,
		true,
	)
}

func (t *RepoListCreateTests) TestWrongPubKeySize(c *C) {
	t.pubKey = make([]byte, 5)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusBadRequest,
	)
}

func (t *RepoListCreateTests) TestRepoAlreadyExists(c *C) {
	t.rm.Create(t.repositoryName, t.pubKey)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusConflict,
	)
}

func (t *RepoListCreateTests) TestMangledJson(c *C) {
	jsonBytes := bytes.NewBufferString("{'hello':'world'}").Bytes()
	t.req = t.requestWithBytes(c, jsonBytes)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusBadRequest,
	)
}

func (t *RepoListCreateTests) TestRepositoryError(c *C) {
	os.RemoveAll(t.repos)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusInternalServerError,
	)
}
<commit_msg>go fmt<commit_after>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"time"

	. "gopkg.in/check.v1"

	"github.com/hoffie/larasync/repository"
)

type RepoListCreateTests struct {
	server         *Server
	rm             *repository.Manager
	req            *http.Request
	repos          string
	repositoryName string
	pubKey         []byte
}

var _ = Suite(&RepoListCreateTests{})

func (t *RepoListCreateTests) requestWithBytes(c *C, body []byte) *http.Request {
	var httpBody io.Reader
	if body == nil {
		httpBody = nil
	} else {
		httpBody = bytes.NewReader(body)
	}
	return t.requestWithReader(c, httpBody)
}

func (t *RepoListCreateTests) requestWithReader(c *C, httpBody io.Reader) *http.Request {
	req, err := http.NewRequest(
		"PUT",
		fmt.Sprintf(
			"http://example.org/repositories/%s",
			t.repositoryName,
		),
		httpBody)
	c.Assert(err, IsNil)
	if httpBody != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	return req
}

func (t *RepoListCreateTests) SetUpTest(c *C) {
	t.repos = c.MkDir()
	t.repositoryName = "test"
	rm, err := repository.NewManager(t.repos)
	c.Assert(err, IsNil)
	t.server = New(adminPubkey, time.Minute, rm)
	c.Assert(rm.Exists(t.repositoryName), Equals, false)
	t.rm = rm
	t.req = t.requestWithBytes(c, nil)
}

func (t *RepoListCreateTests) SetUpSuite(c *C) {
	t.pubKey = make([]byte, PubkeySize)
}

func (t *RepoListCreateTests) TearDownTest(c *C) {
	os.RemoveAll(t.repos)
}

func (t *RepoListCreateTests) getResponse(req *http.Request) *httptest.ResponseRecorder {
	rw := httptest.NewRecorder()
	t.server.router.ServeHTTP(rw, req)
	return rw
}

func (t *RepoListCreateTests) addPubKey(c *C) {
	repository, err := json.Marshal(JSONRepository{
		PubKey: t.pubKey,
	})
	c.Assert(err, IsNil)
	t.req = t.requestWithBytes(c, repository)
}

func (t *RepoListCreateTests) TestRepoCreateUnauthorized(c *C) {
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusUnauthorized)
}

func (t *RepoListCreateTests) TestRepoCreateAdmin(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusCreated)
}

func (t *RepoListCreateTests) TestRepoCreateContentType(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	resp := t.getResponse(t.req)

	contentType := resp.Header().Get("Content-Type")
	c.Assert(
		strings.HasPrefix(
			contentType,
			"application/json"),
		Equals,
		true)
}

func (t *RepoListCreateTests) TestRepoCreateMangled(c *C) {
	SignWithPassphrase(t.req, adminSecret)
	t.req.Header.Set("Mangled", "Yes")
	resp := t.getResponse(t.req)
	c.Assert(resp.Code, Equals, http.StatusUnauthorized)
}

func (t *RepoListCreateTests) TestRepositoryCreate(c *C) {
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	t.getResponse(t.req)
	c.Assert(
		t.rm.Exists(t.repositoryName),
		Equals,
		true,
	)
}

func (t *RepoListCreateTests) TestWrongPubKeySize(c *C) {
	t.pubKey = make([]byte, 5)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusBadRequest,
	)
}

func (t *RepoListCreateTests) TestRepoAlreadyExists(c *C) {
	t.rm.Create(t.repositoryName, t.pubKey)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusConflict,
	)
}

func (t *RepoListCreateTests) TestMangledJson(c *C) {
	jsonBytes := bytes.NewBufferString("{'hello':'world'}").Bytes()
	t.req = t.requestWithBytes(c, jsonBytes)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusBadRequest,
	)
}

func (t *RepoListCreateTests) TestRepositoryError(c *C) {
	os.RemoveAll(t.repos)
	t.addPubKey(c)
	SignWithPassphrase(t.req, adminSecret)
	c.Assert(
		t.getResponse(t.req).Code,
		Equals,
		http.StatusInternalServerError,
	)
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2016 Adobe Systems Incorporated. All rights reserved.
 *  This file is licensed to you 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 REPRESENTATIONS
 *  OF ANY KIND, either express or implied. See the License for the specific language
 *  governing permissions and limitations under the License.
 */
package provision

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os/exec"

	"github.com/adobe-platform/porter/aws_session"
	"github.com/adobe-platform/porter/cfn"
	"github.com/adobe-platform/porter/conf"
	"github.com/adobe-platform/porter/constants"
	dockerutil "github.com/adobe-platform/porter/docker/util"
	"github.com/adobe-platform/porter/secrets"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

func (recv *stackCreator) getSecrets() (containerSecrets map[string]string, success bool) {
	recv.log.Debug("getSecrets() BEGIN")
	defer recv.log.Debug("getSecrets() END")

	containerSecrets = make(map[string]string)

	for _, container := range recv.region.Containers {

		var envFile string
		if container.SrcEnvFile.ExecName != "" {

			envFile, success = recv.getExecSecrets(container)
		} else if container.SrcEnvFile.S3Bucket != "" && container.SrcEnvFile.S3Key != "" {

			envFile, success = recv.getS3Secrets(container)
		} else {
			continue
		}

		if !success {
			return
		}

		envFile = dockerutil.CleanEnvFile(envFile)

		containerSecrets[container.OriginalName] = envFile
	}

	for containerName := range containerSecrets {
		recv.log.Debug(fmt.Sprintf("Container [%s] has secrets", containerName))
	}

	success = true
	return
}

func (recv *stackCreator) getExecSecrets(container *conf.Container) (containerSecrets string, success bool) {

	var stdoutBuf bytes.Buffer
	var stderrBuf bytes.Buffer

	log := recv.log.New("ContainerName", container.OriginalName)
	log.Info("Getting secrets from exec")

	cmd := exec.Command(container.SrcEnvFile.ExecName, container.SrcEnvFile.ExecArgs...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf
	err := cmd.Run()
	if err != nil {
		log.Error("exec.Command", "Error", err, "Stderr", stderrBuf.String())
		return
	}

	containerSecrets = stdoutBuf.String()
	success = true
	return
}

func (recv *stackCreator) getS3Secrets(container *conf.Container) (containerSecrets string, success bool) {
	s3DstClient := s3.New(recv.roleSession)
	log := recv.log.New("ContainerName", container.OriginalName)

	roleArn, err := recv.environment.GetRoleARN(recv.region.Name)
	if err != nil {
		log.Crit("GetRoleARN", "Error", err)
		return
	}

	log.Info("Getting secrets from S3")

	var s3SrcClient *s3.S3

	if container.SrcEnvFile.S3Region == "" {
		s3SrcClient = s3DstClient
	} else {
		srcSession := aws_session.STS(container.SrcEnvFile.S3Region, roleArn, 0)
		s3SrcClient = s3.New(srcSession)
	}

	getObjectInput := &s3.GetObjectInput{
		Bucket: aws.String(container.SrcEnvFile.S3Bucket),
		Key:    aws.String(container.SrcEnvFile.S3Key),
	}

	getObjectOutput, err := s3SrcClient.GetObject(getObjectInput)
	if err != nil {
		log.Crit("GetObject",
			"Error", err,
			"Container", container.Name,
			"SrcEnvFile.S3Bucket", container.SrcEnvFile.S3Bucket,
			"SrcEnvFile.S3Key", container.SrcEnvFile.S3Key,
		)
		return
	}
	defer getObjectOutput.Body.Close()

	getObjectBytes, err := ioutil.ReadAll(getObjectOutput.Body)
	if err != nil {
		log.Crit("ioutil.ReadAll",
			"Error", err,
			"Container", container.Name,
			"SrcEnvFile.S3Bucket", container.SrcEnvFile.S3Bucket,
			"SrcEnvFile.S3Key", container.SrcEnvFile.S3Key,
		)
		return
	}

	containerSecrets = string(getObjectBytes)
	success = true
	return
}

func (recv *stackCreator) uploadSecrets() (success bool) {
	recv.log.Debug("uploadSecrets() BEGIN")
	defer recv.log.Debug("uploadSecrets() END")

	s3DstClient := s3.New(recv.roleSession)

	containerToSecrets, getSecretsSuccess := recv.getSecrets()
	if !getSecretsSuccess {
		return
	}

	containerNameToDstKey := make(map[string]string)

	symmetricKey, err := secrets.GenerateKey()
	if err != nil {
		recv.log.Crit("secrets.GenerateKey", "Error", err)
		return
	}
	recv.secretsKey = hex.EncodeToString(symmetricKey)

	for _, container := range recv.region.Containers {

		log := recv.log.New("ContainerName", container.OriginalName)

		containerSecretString, exists := containerToSecrets[container.OriginalName]
		if !exists {
			log.Warn("Secrets config exists but not for this a container in this region")
			continue
		}

		if len(containerSecretString) == 0 {
			log.Warn("After cleaning the env file it's empty")
			continue
		}

		containerSecrets, err := secrets.Encrypt([]byte(containerSecretString), symmetricKey)
		if err != nil {
			log.Crit("Secrets encryption failed", "Error", err)
			return
		}

		checksumArray := md5.Sum(containerSecrets)
		checksum := hex.EncodeToString(checksumArray[:])

		dstEnvFileS3Key := fmt.Sprintf("%s/%s.env-file", recv.s3KeyRoot(), checksum)
		log.Info("Set destination env file", "S3Key", dstEnvFileS3Key)

		putObjectInput := &s3.PutObjectInput{
			Bucket: aws.String(container.DstEnvFile.S3Bucket),
			Key:    aws.String(dstEnvFileS3Key),
			Body:   bytes.NewReader(containerSecrets),
		}

		if container.DstEnvFile.KMSARN != nil {
			putObjectInput.SSEKMSKeyId = container.DstEnvFile.KMSARN
			putObjectInput.ServerSideEncryption = aws.String("aws:kms")
		}

		_, err = s3DstClient.PutObject(putObjectInput)
		if err != nil {
			log.Crit("PutObject",
				"Error", err,
				"DstEnvFile.S3Bucket", container.DstEnvFile.S3Bucket,
				"DstEnvFile.KMSARN", container.DstEnvFile.KMSARN,
			)
			return
		}

		containerNameToDstKey[container.Name] = dstEnvFileS3Key
	}

	fnMeta := func(recv *stackCreator, template *cfn.Template, resource map[string]interface{}) bool {
		var (
			metadata map[string]interface{}
			ok       bool
		)

		if metadata, ok = resource["Metadata"].(map[string]interface{}); !ok {
			metadata = make(map[string]interface{})
			resource["Metadata"] = metadata
		}

		metadata[constants.MetadataAsEnvFiles] = containerNameToDstKey
		return true
	}

	// The problem is tagging. porterd already needs the EC2 instance to be
	// tagged with constants.PorterWaitConditionHandleLogicalIdTag so it can get
	// that resource handle and call the wait condition on behalf of a service.
	//
	// The next problem is where to put this metadata.
	//
	// While it doesn't make a ton of sense to put it on the
	// WaitConditionHandle, it makes less sense to tag the EC2 instance (which
	// has a tag limit) with the logical resource id of some other resource
	// where we might put this metadata
	var (
		fns []MapResource
		ok  bool
	)
	if fns, ok = recv.templateTransforms[cfn.CloudFormation_WaitConditionHandle]; !ok {
		fns = make([]MapResource, 0)
	}

	fns = append(fns, fnMeta)
	recv.templateTransforms[cfn.CloudFormation_WaitConditionHandle] = fns

	success = true
	return
}
<commit_msg>fix null pointer panic<commit_after>/*
 *  Copyright 2016 Adobe Systems Incorporated. All rights reserved.
 *  This file is licensed to you 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 REPRESENTATIONS
 *  OF ANY KIND, either express or implied. See the License for the specific language
 *  governing permissions and limitations under the License.
 */
package provision

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os/exec"

	"github.com/adobe-platform/porter/aws_session"
	"github.com/adobe-platform/porter/cfn"
	"github.com/adobe-platform/porter/conf"
	"github.com/adobe-platform/porter/constants"
	dockerutil "github.com/adobe-platform/porter/docker/util"
	"github.com/adobe-platform/porter/secrets"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

func (recv *stackCreator) getSecrets() (containerSecrets map[string]string, success bool) {
	recv.log.Debug("getSecrets() BEGIN")
	defer recv.log.Debug("getSecrets() END")

	containerSecrets = make(map[string]string)

	for _, container := range recv.region.Containers {

		if container.SrcEnvFile == nil {
			continue
		}

		var envFile string
		if container.SrcEnvFile.ExecName != "" {

			envFile, success = recv.getExecSecrets(container)
		} else if container.SrcEnvFile.S3Bucket != "" && container.SrcEnvFile.S3Key != "" {

			envFile, success = recv.getS3Secrets(container)
		} else {
			continue
		}

		if !success {
			return
		}

		envFile = dockerutil.CleanEnvFile(envFile)

		containerSecrets[container.OriginalName] = envFile
	}

	for containerName := range containerSecrets {
		recv.log.Debug(fmt.Sprintf("Container [%s] has secrets", containerName))
	}

	success = true
	return
}

func (recv *stackCreator) getExecSecrets(container *conf.Container) (containerSecrets string, success bool) {

	var stdoutBuf bytes.Buffer
	var stderrBuf bytes.Buffer

	log := recv.log.New("ContainerName", container.OriginalName)
	log.Info("Getting secrets from exec")

	cmd := exec.Command(container.SrcEnvFile.ExecName, container.SrcEnvFile.ExecArgs...)
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf
	err := cmd.Run()
	if err != nil {
		log.Error("exec.Command", "Error", err, "Stderr", stderrBuf.String())
		return
	}

	containerSecrets = stdoutBuf.String()
	success = true
	return
}

func (recv *stackCreator) getS3Secrets(container *conf.Container) (containerSecrets string, success bool) {
	s3DstClient := s3.New(recv.roleSession)
	log := recv.log.New("ContainerName", container.OriginalName)

	roleArn, err := recv.environment.GetRoleARN(recv.region.Name)
	if err != nil {
		log.Crit("GetRoleARN", "Error", err)
		return
	}

	log.Info("Getting secrets from S3")

	var s3SrcClient *s3.S3

	if container.SrcEnvFile.S3Region == "" {
		s3SrcClient = s3DstClient
	} else {
		srcSession := aws_session.STS(container.SrcEnvFile.S3Region, roleArn, 0)
		s3SrcClient = s3.New(srcSession)
	}

	getObjectInput := &s3.GetObjectInput{
		Bucket: aws.String(container.SrcEnvFile.S3Bucket),
		Key:    aws.String(container.SrcEnvFile.S3Key),
	}

	getObjectOutput, err := s3SrcClient.GetObject(getObjectInput)
	if err != nil {
		log.Crit("GetObject",
			"Error", err,
			"Container", container.Name,
			"SrcEnvFile.S3Bucket", container.SrcEnvFile.S3Bucket,
			"SrcEnvFile.S3Key", container.SrcEnvFile.S3Key,
		)
		return
	}
	defer getObjectOutput.Body.Close()

	getObjectBytes, err := ioutil.ReadAll(getObjectOutput.Body)
	if err != nil {
		log.Crit("ioutil.ReadAll",
			"Error", err,
			"Container", container.Name,
			"SrcEnvFile.S3Bucket", container.SrcEnvFile.S3Bucket,
			"SrcEnvFile.S3Key", container.SrcEnvFile.S3Key,
		)
		return
	}

	containerSecrets = string(getObjectBytes)
	success = true
	return
}

func (recv *stackCreator) uploadSecrets() (success bool) {
	recv.log.Debug("uploadSecrets() BEGIN")
	defer recv.log.Debug("uploadSecrets() END")

	s3DstClient := s3.New(recv.roleSession)

	containerToSecrets, getSecretsSuccess := recv.getSecrets()
	if !getSecretsSuccess {
		return
	}

	containerNameToDstKey := make(map[string]string)

	symmetricKey, err := secrets.GenerateKey()
	if err != nil {
		recv.log.Crit("secrets.GenerateKey", "Error", err)
		return
	}
	recv.secretsKey = hex.EncodeToString(symmetricKey)

	for _, container := range recv.region.Containers {

		log := recv.log.New("ContainerName", container.OriginalName)

		containerSecretString, exists := containerToSecrets[container.OriginalName]
		if !exists {
			log.Warn("Secrets config exists but not for this a container in this region")
			continue
		}

		if len(containerSecretString) == 0 {
			log.Warn("After cleaning the env file it's empty")
			continue
		}

		containerSecrets, err := secrets.Encrypt([]byte(containerSecretString), symmetricKey)
		if err != nil {
			log.Crit("Secrets encryption failed", "Error", err)
			return
		}

		checksumArray := md5.Sum(containerSecrets)
		checksum := hex.EncodeToString(checksumArray[:])

		dstEnvFileS3Key := fmt.Sprintf("%s/%s.env-file", recv.s3KeyRoot(), checksum)
		log.Info("Set destination env file", "S3Key", dstEnvFileS3Key)

		putObjectInput := &s3.PutObjectInput{
			Bucket: aws.String(container.DstEnvFile.S3Bucket),
			Key:    aws.String(dstEnvFileS3Key),
			Body:   bytes.NewReader(containerSecrets),
		}

		if container.DstEnvFile.KMSARN != nil {
			putObjectInput.SSEKMSKeyId = container.DstEnvFile.KMSARN
			putObjectInput.ServerSideEncryption = aws.String("aws:kms")
		}

		_, err = s3DstClient.PutObject(putObjectInput)
		if err != nil {
			log.Crit("PutObject",
				"Error", err,
				"DstEnvFile.S3Bucket", container.DstEnvFile.S3Bucket,
				"DstEnvFile.KMSARN", container.DstEnvFile.KMSARN,
			)
			return
		}

		containerNameToDstKey[container.Name] = dstEnvFileS3Key
	}

	fnMeta := func(recv *stackCreator, template *cfn.Template, resource map[string]interface{}) bool {
		var (
			metadata map[string]interface{}
			ok       bool
		)

		if metadata, ok = resource["Metadata"].(map[string]interface{}); !ok {
			metadata = make(map[string]interface{})
			resource["Metadata"] = metadata
		}

		metadata[constants.MetadataAsEnvFiles] = containerNameToDstKey
		return true
	}

	// The problem is tagging. porterd already needs the EC2 instance to be
	// tagged with constants.PorterWaitConditionHandleLogicalIdTag so it can get
	// that resource handle and call the wait condition on behalf of a service.
	//
	// The next problem is where to put this metadata.
	//
	// While it doesn't make a ton of sense to put it on the
	// WaitConditionHandle, it makes less sense to tag the EC2 instance (which
	// has a tag limit) with the logical resource id of some other resource
	// where we might put this metadata
	var (
		fns []MapResource
		ok  bool
	)
	if fns, ok = recv.templateTransforms[cfn.CloudFormation_WaitConditionHandle]; !ok {
		fns = make([]MapResource, 0)
	}

	fns = append(fns, fnMeta)
	recv.templateTransforms[cfn.CloudFormation_WaitConditionHandle] = fns

	success = true
	return
}
<|endoftext|>


<commit_before>// Package vmess contains the implementation of VMess protocol and transportation.
//
// VMess contains both inbound and outbound connections. VMess inbound is usually used on servers
// together with 'freedom' to talk to final destination, while VMess outbound is usually used on
// clients with 'socks' for proxying.
package vmess

//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess

import (
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/signal"
)

const (
	updateInterval   = 10 * time.Second
	cacheDurationSec = 120
)

type user struct {
	user    *protocol.User
	account *InternalAccount
	lastSec protocol.Timestamp
}

type TimedUserValidator struct {
	sync.RWMutex
	users    []*user
	userHash map[[16]byte]indexTimePair
	hasher   protocol.IDHash
	baseTime protocol.Timestamp
	task     *signal.PeriodicTask
}

type indexTimePair struct {
	user    *user
	timeInc uint32
}

func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator {
	tuv := &TimedUserValidator{
		users:    make([]*user, 0, 16),
		userHash: make(map[[16]byte]indexTimePair, 1024),
		hasher:   hasher,
		baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*3),
	}
	tuv.task = &signal.PeriodicTask{
		Interval: updateInterval,
		Execute: func() error {
			tuv.updateUserHash()
			return nil
		},
	}
	tuv.task.Start()
	return tuv
}

func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) {
	var hashValue [16]byte
	genHashForID := func(id *protocol.ID) {
		idHash := v.hasher(id.Bytes())
		lastSec := user.lastSec
		if lastSec < nowSec-cacheDurationSec*2 {
			lastSec = nowSec - cacheDurationSec*2
		}
		for ts := lastSec; ts <= nowSec; ts++ {
			common.Must2(idHash.Write(ts.Bytes(nil)))
			idHash.Sum(hashValue[:0])
			idHash.Reset()

			v.userHash[hashValue] = indexTimePair{
				user:    user,
				timeInc: uint32(ts - v.baseTime),
			}
		}
	}

	genHashForID(user.account.ID)
	for _, id := range user.account.AlterIDs {
		genHashForID(id)
	}
	user.lastSec = nowSec
}

func (v *TimedUserValidator) removeExpiredHashes(expire uint32) {
	for key, pair := range v.userHash {
		if pair.timeInc < expire {
			delete(v.userHash, key)
		}
	}
}

func (v *TimedUserValidator) updateUserHash() {
	now := time.Now()
	nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
	v.Lock()
	defer v.Unlock()

	for _, user := range v.users {
		v.generateNewHashes(nowSec, user)
	}

	expire := protocol.Timestamp(now.Unix() - cacheDurationSec*3)
	if expire > v.baseTime {
		v.removeExpiredHashes(uint32(expire - v.baseTime))
	}
}

func (v *TimedUserValidator) Add(u *protocol.User) error {
	v.Lock()
	defer v.Unlock()

	rawAccount, err := u.GetTypedAccount()
	if err != nil {
		return err
	}
	account := rawAccount.(*InternalAccount)

	nowSec := time.Now().Unix()

	uu := &user{
		user:    u,
		account: account,
		lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
	}
	v.users = append(v.users, uu)
	v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), uu)

	return nil
}

func (v *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Timestamp, bool) {
	defer v.RUnlock()
	v.RLock()

	var fixedSizeHash [16]byte
	copy(fixedSizeHash[:], userHash)
	pair, found := v.userHash[fixedSizeHash]
	if found {
		return pair.user.user, protocol.Timestamp(pair.timeInc) + v.baseTime, true
	}
	return nil, 0, false
}

func (v *TimedUserValidator) Remove(email string) bool {
	v.Lock()
	defer v.Unlock()

	email = strings.ToLower(email)
	idx := -1
	for i, u := range v.users {
		if strings.ToLower(u.user.Email) == email {
			idx = i
			break
		}
	}
	if idx == -1 {
		return false
	}
	ulen := len(v.users)
	if idx < ulen {
		v.users[idx] = v.users[ulen-1]
		v.users[ulen-1] = nil
		v.users = v.users[:ulen-1]
	}
	return true
}

// Close implements common.Closable.
func (v *TimedUserValidator) Close() error {
	return v.task.Close()
}
<commit_msg>remove unnecessary id cache<commit_after>// Package vmess contains the implementation of VMess protocol and transportation.
//
// VMess contains both inbound and outbound connections. VMess inbound is usually used on servers
// together with 'freedom' to talk to final destination, while VMess outbound is usually used on
// clients with 'socks' for proxying.
package vmess

//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg vmess -path Proxy,VMess

import (
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/protocol"
	"v2ray.com/core/common/signal"
)

const (
	updateInterval   = 10 * time.Second
	cacheDurationSec = 120
)

type user struct {
	user    *protocol.User
	account *InternalAccount
	lastSec protocol.Timestamp
}

type TimedUserValidator struct {
	sync.RWMutex
	users    []*user
	userHash map[[16]byte]indexTimePair
	hasher   protocol.IDHash
	baseTime protocol.Timestamp
	task     *signal.PeriodicTask
}

type indexTimePair struct {
	user    *user
	timeInc uint32
}

func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator {
	tuv := &TimedUserValidator{
		users:    make([]*user, 0, 16),
		userHash: make(map[[16]byte]indexTimePair, 1024),
		hasher:   hasher,
		baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*2),
	}
	tuv.task = &signal.PeriodicTask{
		Interval: updateInterval,
		Execute: func() error {
			tuv.updateUserHash()
			return nil
		},
	}
	tuv.task.Start()
	return tuv
}

func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) {
	var hashValue [16]byte
	genHashForID := func(id *protocol.ID) {
		idHash := v.hasher(id.Bytes())
		lastSec := user.lastSec
		if lastSec < nowSec-cacheDurationSec*2 {
			lastSec = nowSec - cacheDurationSec*2
		}
		for ts := lastSec; ts <= nowSec; ts++ {
			common.Must2(idHash.Write(ts.Bytes(nil)))
			idHash.Sum(hashValue[:0])
			idHash.Reset()

			v.userHash[hashValue] = indexTimePair{
				user:    user,
				timeInc: uint32(ts - v.baseTime),
			}
		}
	}

	genHashForID(user.account.ID)
	for _, id := range user.account.AlterIDs {
		genHashForID(id)
	}
	user.lastSec = nowSec
}

func (v *TimedUserValidator) removeExpiredHashes(expire uint32) {
	for key, pair := range v.userHash {
		if pair.timeInc < expire {
			delete(v.userHash, key)
		}
	}
}

func (v *TimedUserValidator) updateUserHash() {
	now := time.Now()
	nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
	v.Lock()
	defer v.Unlock()

	for _, user := range v.users {
		v.generateNewHashes(nowSec, user)
	}

	expire := protocol.Timestamp(now.Unix() - cacheDurationSec)
	if expire > v.baseTime {
		v.removeExpiredHashes(uint32(expire - v.baseTime))
	}
}

func (v *TimedUserValidator) Add(u *protocol.User) error {
	v.Lock()
	defer v.Unlock()

	rawAccount, err := u.GetTypedAccount()
	if err != nil {
		return err
	}
	account := rawAccount.(*InternalAccount)

	nowSec := time.Now().Unix()

	uu := &user{
		user:    u,
		account: account,
		lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
	}
	v.users = append(v.users, uu)
	v.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), uu)

	return nil
}

func (v *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Timestamp, bool) {
	defer v.RUnlock()
	v.RLock()

	var fixedSizeHash [16]byte
	copy(fixedSizeHash[:], userHash)
	pair, found := v.userHash[fixedSizeHash]
	if found {
		return pair.user.user, protocol.Timestamp(pair.timeInc) + v.baseTime, true
	}
	return nil, 0, false
}

func (v *TimedUserValidator) Remove(email string) bool {
	v.Lock()
	defer v.Unlock()

	email = strings.ToLower(email)
	idx := -1
	for i, u := range v.users {
		if strings.ToLower(u.user.Email) == email {
			idx = i
			break
		}
	}
	if idx == -1 {
		return false
	}
	ulen := len(v.users)
	if idx < ulen {
		v.users[idx] = v.users[ulen-1]
		v.users[ulen-1] = nil
		v.users = v.users[:ulen-1]
	}
	return true
}

// Close implements common.Closable.
func (v *TimedUserValidator) Close() error {
	return v.task.Close()
}
<|endoftext|>


<commit_before>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 docker

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"io/ioutil"
	"os"
	"path"
	"time"
)

var LogOutput bool

type Client struct {
	URL    string
	client *docker.Client
}

func New(url string) *Client {
	dockerClient, err := docker.NewClient("unix:///var/run/docker.sock")
	if err != nil {
		panic(err)
	}
	return &Client{URL: url, client: dockerClient}
}

func (c *Client) PullImage(repository string) bool {
	pullOpts := docker.PullImageOptions{
		Repository:   c.URL + "/" + repository,
		Registry:     c.URL,
		OutputStream: os.Stdout,
	}

	// If PullImage succeeds, image exists and
	// we return true.
	return c.client.PullImage(pullOpts) == nil
}

func (c *Client) PushImage(repository string, stream bool) {
	pushOpts := docker.PushImageOptions{
		Name:         c.URL + "/" + repository,
		Registry:     c.URL,
		OutputStream: ioutil.Discard,
	}
	if stream {
		pushOpts.OutputStream = os.Stdout
	}

	authConf := docker.AuthConfiguration{}

	if err := c.client.PushImage(pushOpts, authConf); err != nil {
		panic(err)
	}
}

func (c *Client) ImageExists(repository string) bool {
	imageName := c.URL + "/" + repository

	_, err := c.client.InspectImage(imageName)
	switch err {
	case docker.ErrNoSuchImage:
		return c.PullImage(repository)
	case nil:
		return true
	}

	panic(err)
}

func (c *Client) OverlayAndCommit(imageFrom, imageTo, bindFrom, bindTo string, tout time.Duration, runScript ...string) {
	containerConfig := &docker.Config{
		Cmd:   runScript,
		Image: c.URL + "/" + imageFrom,
		Volumes: map[string]struct{}{
			bindTo: struct{}{},
		},
	}
	hostConfig := &docker.HostConfig{
		Privileged: true,
		Binds: []string{
			fmt.Sprintf("%s:%s", bindFrom, bindTo),
		},
	}

	uniqName := fmt.Sprintf("%s-%d", path.Base(imageTo), time.Now().Unix())
	container, err := c.client.CreateContainer(docker.CreateContainerOptions{Name: uniqName, Config: containerConfig})
	if err != nil {
		panic(err)
	}

	// Don't clean the container if we paniced, exporting it is useful to get to provisioning logs.
	defer func() {
		if r := recover(); r == nil {
			c.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})
		} else {
			panic(r)
		}
	}()

	if err = c.client.StartContainer(container.ID, hostConfig); err != nil {
		panic(err)
	}

	if LogOutput {
		attachOptions := docker.AttachToContainerOptions{
			Container:    container.ID,
			OutputStream: os.Stdout,
			Stdout:       true,
			Stream:       true,
		}

		if err = c.client.AttachToContainer(attachOptions); err != nil {
			panic(err)
		}
	}

	result := make(chan int)
	go func() {
		for {
			inspect, err := c.client.InspectContainer(container.ID)
			if err != nil {
				panic(err)
			}

			if !inspect.State.Running {
				result <- inspect.State.ExitCode
				return
			}
			time.Sleep(time.Second)
		}
	}()

	select {
	case ec := <-result:
		if ec != 0 {
			panic(fmt.Sprintf("run script failed: %d", ec))
		}
	case <-time.After(tout):
		c.client.KillContainer(container.ID)
		panic(fmt.Sprintf("run script timed out in %s", tout))
	}

	// NOTE(jigish) Should we pass the bind mount and port configuration here during the build?
	opts := docker.CommitContainerOptions{Container: container.ID, Repository: c.URL + "/" + imageTo, Run: &docker.Config{}}
	c.client.CommitContainer(opts)
}
<commit_msg>If pushImage fails, retry one more time<commit_after>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 docker

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"io/ioutil"
	"os"
	"path"
	"time"
)

var LogOutput bool

type Client struct {
	URL    string
	client *docker.Client
}

func New(url string) *Client {
	dockerClient, err := docker.NewClient("unix:///var/run/docker.sock")
	if err != nil {
		panic(err)
	}
	return &Client{URL: url, client: dockerClient}
}

func (c *Client) PullImage(repository string) bool {
	pullOpts := docker.PullImageOptions{
		Repository:   c.URL + "/" + repository,
		Registry:     c.URL,
		OutputStream: os.Stdout,
	}

	// If PullImage succeeds, image exists and
	// we return true.
	return c.client.PullImage(pullOpts) == nil
}

func (c *Client) PushImage(repository string, stream bool) {
	pushOpts := docker.PushImageOptions{
		Name:         c.URL + "/" + repository,
		Registry:     c.URL,
		OutputStream: ioutil.Discard,
	}
	if stream {
		pushOpts.OutputStream = os.Stdout
	}

	authConf := docker.AuthConfiguration{}

	if err := c.client.PushImage(pushOpts, authConf); err != nil {
		fmt.Fprintf(os.Stderr, "PushImage error: %s\n", err.Error())
		time.Sleep(30)
		if err = c.client.PushImage(pushOpts, authConf); err != nil {
			defer c.client.RemoveImage(repository)
			panic(err)
		}
	}
}

func (c *Client) ImageExists(repository string) bool {
	imageName := c.URL + "/" + repository

	_, err := c.client.InspectImage(imageName)
	switch err {
	case docker.ErrNoSuchImage:
		return c.PullImage(repository)
	case nil:
		return true
	}

	panic(err)
}

func (c *Client) OverlayAndCommit(imageFrom, imageTo, bindFrom, bindTo string, tout time.Duration, runScript ...string) {
	containerConfig := &docker.Config{
		Cmd:   runScript,
		Image: c.URL + "/" + imageFrom,
		Volumes: map[string]struct{}{
			bindTo: struct{}{},
		},
	}
	hostConfig := &docker.HostConfig{
		Privileged: true,
		Binds: []string{
			fmt.Sprintf("%s:%s", bindFrom, bindTo),
		},
	}

	uniqName := fmt.Sprintf("%s-%d", path.Base(imageTo), time.Now().Unix())
	container, err := c.client.CreateContainer(docker.CreateContainerOptions{Name: uniqName, Config: containerConfig})
	if err != nil {
		panic(err)
	}

	// Don't clean the container if we paniced, exporting it is useful to get to provisioning logs.
	defer func() {
		if r := recover(); r == nil {
			c.client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})
		} else {
			panic(r)
		}
	}()

	if err = c.client.StartContainer(container.ID, hostConfig); err != nil {
		panic(err)
	}

	if LogOutput {
		attachOptions := docker.AttachToContainerOptions{
			Container:    container.ID,
			OutputStream: os.Stdout,
			Stdout:       true,
			Stream:       true,
		}

		if err = c.client.AttachToContainer(attachOptions); err != nil {
			panic(err)
		}
	}

	result := make(chan int)
	go func() {
		for {
			inspect, err := c.client.InspectContainer(container.ID)
			if err != nil {
				panic(err)
			}

			if !inspect.State.Running {
				result <- inspect.State.ExitCode
				return
			}
			time.Sleep(time.Second)
		}
	}()

	select {
	case ec := <-result:
		if ec != 0 {
			panic(fmt.Sprintf("run script failed: %d", ec))
		}
	case <-time.After(tout):
		c.client.KillContainer(container.ID)
		panic(fmt.Sprintf("run script timed out in %s", tout))
	}

	// NOTE(jigish) Should we pass the bind mount and port configuration here during the build?
	opts := docker.CommitContainerOptions{Container: container.ID, Repository: c.URL + "/" + imageTo, Run: &docker.Config{}}
	c.client.CommitContainer(opts)
}
<|endoftext|>


<commit_before>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 status

import (
	"atlantis/manager/datamodel"
	. "atlantis/manager/rpc/types"
	"atlantis/manager/supervisor"
)

func GetUsage() (map[string]*SupervisorUsage, error) {
	// for each supervisor
	//   get health check to figure out total CPUShares, Memory, Price
	//   get list of containers
	//   fill in data in SupervisorUsage
	supers, err := datamodel.ListSupervisors()
	if err != nil {
		return nil, err
	}
	usageMap := map[string]*SupervisorUsage{}
	for _, super := range supers {
		usage := &SupervisorUsage{Containers: map[string]*ContainerUsage{}}
		hreply, err := supervisor.HealthCheck(super)
		if err != nil {
			return nil, err
		}
		usage.Price = hreply.Price
		usage.TotalContainers = hreply.Containers.Total
		usage.TotalCPUShares = hreply.CPUShares.Total
		usage.TotalMemory = hreply.Memory.Total
		lreply, err := supervisor.List(super)
		if err != nil {
			return nil, err
		}
		for id, cont := range lreply.Containers {
			usage.Containers[id] = &ContainerUsage{
				ID:        id,
				App:       cont.App,
				Sha:       cont.Sha,
				Env:       cont.Env,
				CPUShares: cont.Manifest.CPUShares,
				Memory:    cont.Manifest.MemoryLimit,
			}
		}
	}
	return usageMap, nil
}
<commit_msg>add usage info to parent map<commit_after>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 status

import (
	"atlantis/manager/datamodel"
	. "atlantis/manager/rpc/types"
	"atlantis/manager/supervisor"
)

func GetUsage() (map[string]*SupervisorUsage, error) {
	// for each supervisor
	//   get health check to figure out total CPUShares, Memory, Price
	//   get list of containers
	//   fill in data in SupervisorUsage
	supers, err := datamodel.ListSupervisors()
	if err != nil {
		return nil, err
	}
	usageMap := map[string]*SupervisorUsage{}
	for _, super := range supers {
		usage := &SupervisorUsage{Containers: map[string]*ContainerUsage{}}
		hreply, err := supervisor.HealthCheck(super)
		if err != nil {
			return nil, err
		}
		usage.Price = hreply.Price
		usage.TotalContainers = hreply.Containers.Total
		usage.TotalCPUShares = hreply.CPUShares.Total
		usage.TotalMemory = hreply.Memory.Total
		lreply, err := supervisor.List(super)
		if err != nil {
			return nil, err
		}
		for id, cont := range lreply.Containers {
			usage.Containers[id] = &ContainerUsage{
				ID:        id,
				App:       cont.App,
				Sha:       cont.Sha,
				Env:       cont.Env,
				CPUShares: cont.Manifest.CPUShares,
				Memory:    cont.Manifest.MemoryLimit,
			}
		}
		usageMap[super] = usage
	}
	return usageMap, nil
}
<|endoftext|>


<commit_before>package x86_test

import (
	"bufio"
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"testing"
)

const testdata = `
MOVQ AX, AX -> MOVQ AX, AX

LEAQ name(SB), AX -> MOVQ name@GOT(SB), AX
LEAQ name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX
MOVQ $name(SB), AX -> MOVQ name@GOT(SB), AX
MOVQ $name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX

MOVQ name(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ (R15), AX
MOVQ name+10(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ 10(R15), AX

CMPQ name(SB), $0 -> NOP; MOVQ name@GOT(SB), R15; CMPQ (R15), $0

MOVQ $1, name(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, (R15)
MOVQ $1, name+10(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, 10(R15)
`

type ParsedTestData struct {
	input              string
	marks              []int
	marker_to_input    map[int][]string
	marker_to_expected map[int][]string
	marker_to_output   map[int][]string
}

const marker_start = 1234

func parseTestData(t *testing.T) *ParsedTestData {
	r := &ParsedTestData{}
	scanner := bufio.NewScanner(strings.NewReader(testdata))
	r.marker_to_input = make(map[int][]string)
	r.marker_to_expected = make(map[int][]string)
	marker := marker_start
	input_insns := []string{}
	for scanner.Scan() {
		line := scanner.Text()
		if len(strings.TrimSpace(line)) == 0 {
			continue
		}
		parts := strings.Split(line, "->")
		if len(parts) != 2 {
			t.Fatalf("malformed line %v", line)
		}
		r.marks = append(r.marks, marker)
		marker_insn := fmt.Sprintf("MOVQ $%d, AX", marker)
		input_insns = append(input_insns, marker_insn)
		for _, input_insn := range strings.Split(parts[0], ";") {
			input_insns = append(input_insns, input_insn)
			r.marker_to_input[marker] = append(r.marker_to_input[marker], normalize(input_insn))
		}
		for _, expected_insn := range strings.Split(parts[1], ";") {
			r.marker_to_expected[marker] = append(r.marker_to_expected[marker], normalize(expected_insn))
		}
		marker++
	}
	r.input = "TEXT ·foo(SB),$0\n" + strings.Join(input_insns, "\n") + "\n"
	return r
}

var spaces_re *regexp.Regexp = regexp.MustCompile("\\s+")

func normalize(s string) string {
	return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ")
}

func asmOutput(t *testing.T, s string) []byte {
	tmpdir, err := ioutil.TempDir("", "progedittest")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpdir)
	tmpfile, err := os.Create(filepath.Join(tmpdir, "input.s"))
	if err != nil {
		t.Fatal(err)
	}
	defer tmpfile.Close()
	_, err = tmpfile.WriteString(s)
	if err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(
		testenv.GoToolPath(t), "tool", "asm", "-S", "-dynlink",
		"-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name())

	var env []string
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOARCH=") {
			env = append(env, v)
		}
	}
	cmd.Env = append(env, "GOARCH=amd64")
	asmout, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("error %s output %s", err, asmout)
	}
	return asmout
}

func parseOutput(t *testing.T, td *ParsedTestData, asmout []byte) {
	scanner := bufio.NewScanner(bytes.NewReader(asmout))
	marker := regexp.MustCompile("MOVQ \\$([0-9]+), AX")
	mark := -1
	td.marker_to_output = make(map[int][]string)
	for scanner.Scan() {
		line := scanner.Text()
		if line[0] != '\t' {
			continue
		}
		parts := strings.SplitN(line, "\t", 3)
		if len(parts) != 3 {
			continue
		}
		n := normalize(parts[2])
		mark_matches := marker.FindStringSubmatch(n)
		if mark_matches != nil {
			mark, _ = strconv.Atoi(mark_matches[1])
			if _, ok := td.marker_to_input[mark]; !ok {
				t.Fatalf("unexpected marker %d", mark)
			}
		} else if mark != -1 {
			td.marker_to_output[mark] = append(td.marker_to_output[mark], n)
		}
	}
}

func TestDynlink(t *testing.T) {
	testenv.MustHaveGoBuild(t)

	if os.Getenv("GOHOSTARCH") != "" {
		// TODO: make this work? It was failing due to the
		// GOARCH= filtering above and skipping is easiest for
		// now.
		t.Skip("skipping when GOHOSTARCH is set")
	}

	testdata := parseTestData(t)
	asmout := asmOutput(t, testdata.input)
	parseOutput(t, testdata, asmout)
	for _, m := range testdata.marks {
		i := strings.Join(testdata.marker_to_input[m], "; ")
		o := strings.Join(testdata.marker_to_output[m], "; ")
		e := strings.Join(testdata.marker_to_expected[m], "; ")
		if o != e {
			if o == i {
				t.Errorf("%s was unchanged; should have become %s", i, e)
			} else {
				t.Errorf("%s became %s; should have become %s", i, o, e)
			}
		} else if i != e {
			t.Logf("%s correctly became %s", i, o)
		}
	}
}
<commit_msg>cmd/internal/obj/x86: use raw string literals in regexp<commit_after>package x86_test

import (
	"bufio"
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"testing"
)

const testdata = `
MOVQ AX, AX -> MOVQ AX, AX

LEAQ name(SB), AX -> MOVQ name@GOT(SB), AX
LEAQ name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX
MOVQ $name(SB), AX -> MOVQ name@GOT(SB), AX
MOVQ $name+10(SB), AX -> MOVQ name@GOT(SB), AX; LEAQ 10(AX), AX

MOVQ name(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ (R15), AX
MOVQ name+10(SB), AX -> NOP; MOVQ name@GOT(SB), R15; MOVQ 10(R15), AX

CMPQ name(SB), $0 -> NOP; MOVQ name@GOT(SB), R15; CMPQ (R15), $0

MOVQ $1, name(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, (R15)
MOVQ $1, name+10(SB) -> NOP; MOVQ name@GOT(SB), R15; MOVQ $1, 10(R15)
`

type ParsedTestData struct {
	input              string
	marks              []int
	marker_to_input    map[int][]string
	marker_to_expected map[int][]string
	marker_to_output   map[int][]string
}

const marker_start = 1234

func parseTestData(t *testing.T) *ParsedTestData {
	r := &ParsedTestData{}
	scanner := bufio.NewScanner(strings.NewReader(testdata))
	r.marker_to_input = make(map[int][]string)
	r.marker_to_expected = make(map[int][]string)
	marker := marker_start
	input_insns := []string{}
	for scanner.Scan() {
		line := scanner.Text()
		if len(strings.TrimSpace(line)) == 0 {
			continue
		}
		parts := strings.Split(line, "->")
		if len(parts) != 2 {
			t.Fatalf("malformed line %v", line)
		}
		r.marks = append(r.marks, marker)
		marker_insn := fmt.Sprintf("MOVQ $%d, AX", marker)
		input_insns = append(input_insns, marker_insn)
		for _, input_insn := range strings.Split(parts[0], ";") {
			input_insns = append(input_insns, input_insn)
			r.marker_to_input[marker] = append(r.marker_to_input[marker], normalize(input_insn))
		}
		for _, expected_insn := range strings.Split(parts[1], ";") {
			r.marker_to_expected[marker] = append(r.marker_to_expected[marker], normalize(expected_insn))
		}
		marker++
	}
	r.input = "TEXT ·foo(SB),$0\n" + strings.Join(input_insns, "\n") + "\n"
	return r
}

var spaces_re *regexp.Regexp = regexp.MustCompile(`\s+`)

func normalize(s string) string {
	return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ")
}

func asmOutput(t *testing.T, s string) []byte {
	tmpdir, err := ioutil.TempDir("", "progedittest")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpdir)
	tmpfile, err := os.Create(filepath.Join(tmpdir, "input.s"))
	if err != nil {
		t.Fatal(err)
	}
	defer tmpfile.Close()
	_, err = tmpfile.WriteString(s)
	if err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command(
		testenv.GoToolPath(t), "tool", "asm", "-S", "-dynlink",
		"-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name())

	var env []string
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, "GOARCH=") {
			env = append(env, v)
		}
	}
	cmd.Env = append(env, "GOARCH=amd64")
	asmout, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("error %s output %s", err, asmout)
	}
	return asmout
}

func parseOutput(t *testing.T, td *ParsedTestData, asmout []byte) {
	scanner := bufio.NewScanner(bytes.NewReader(asmout))
	marker := regexp.MustCompile(`MOVQ \$([0-9]+), AX`)
	mark := -1
	td.marker_to_output = make(map[int][]string)
	for scanner.Scan() {
		line := scanner.Text()
		if line[0] != '\t' {
			continue
		}
		parts := strings.SplitN(line, "\t", 3)
		if len(parts) != 3 {
			continue
		}
		n := normalize(parts[2])
		mark_matches := marker.FindStringSubmatch(n)
		if mark_matches != nil {
			mark, _ = strconv.Atoi(mark_matches[1])
			if _, ok := td.marker_to_input[mark]; !ok {
				t.Fatalf("unexpected marker %d", mark)
			}
		} else if mark != -1 {
			td.marker_to_output[mark] = append(td.marker_to_output[mark], n)
		}
	}
}

func TestDynlink(t *testing.T) {
	testenv.MustHaveGoBuild(t)

	if os.Getenv("GOHOSTARCH") != "" {
		// TODO: make this work? It was failing due to the
		// GOARCH= filtering above and skipping is easiest for
		// now.
		t.Skip("skipping when GOHOSTARCH is set")
	}

	testdata := parseTestData(t)
	asmout := asmOutput(t, testdata.input)
	parseOutput(t, testdata, asmout)
	for _, m := range testdata.marks {
		i := strings.Join(testdata.marker_to_input[m], "; ")
		o := strings.Join(testdata.marker_to_output[m], "; ")
		e := strings.Join(testdata.marker_to_expected[m], "; ")
		if o != e {
			if o == i {
				t.Errorf("%s was unchanged; should have become %s", i, e)
			} else {
				t.Errorf("%s became %s; should have become %s", i, o, e)
			}
		} else if i != e {
			t.Logf("%s correctly became %s", i, o)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go 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 fetch provides an extensible mechanism to fetch a profile
// from a data source.
package fetch

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"cmd/pprof/internal/plugin"
	"internal/pprof/profile"
)

// FetchProfile reads from a data source (network, file) and generates a
// profile.
func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
	return Fetcher(source, timeout, plugin.StandardUI())
}

// Fetcher is the plugin.Fetcher version of FetchProfile.
func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
	var f io.ReadCloser
	var err error

	url, err := url.Parse(source)
	if err == nil && url.Host != "" {
		f, err = FetchURL(source, timeout)
	} else {
		f, err = os.Open(source)
	}
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return profile.Parse(f)
}

// FetchURL fetches a profile from a URL using HTTP.
func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
	resp, err := httpGet(source, timeout)
	if err != nil {
		return nil, fmt.Errorf("http fetch %s: %v", source, err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}

	return resp.Body, nil
}

// PostURL issues a POST to a URL over HTTP.
func PostURL(source, post string) ([]byte, error) {
	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
	if err != nil {
		return nil, fmt.Errorf("http post %s: %v", source, err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// httpGet is a wrapper around http.Get; it is defined as a variable
// so it can be redefined during for testing.
var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
	url, err := url.Parse(source)
	if err != nil {
		return nil, err
	}

	var tlsConfig *tls.Config
	if url.Scheme == "https+insecure" {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
		url.Scheme = "https"
		source = url.String()
	}

	client := &http.Client{
		Transport: &http.Transport{
			ResponseHeaderTimeout: timeout + 5*time.Second,
			TLSClientConfig:       tlsConfig,
		},
	}
	return client.Get(source)
}
<commit_msg>cmd/pprof: remove redundant URLs from error messages in fetch.FetchURL<commit_after>// Copyright 2014 The Go 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 fetch provides an extensible mechanism to fetch a profile
// from a data source.
package fetch

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"cmd/pprof/internal/plugin"
	"internal/pprof/profile"
)

// FetchProfile reads from a data source (network, file) and generates a
// profile.
func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
	return Fetcher(source, timeout, plugin.StandardUI())
}

// Fetcher is the plugin.Fetcher version of FetchProfile.
func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
	var f io.ReadCloser
	var err error

	url, err := url.Parse(source)
	if err == nil && url.Host != "" {
		f, err = FetchURL(source, timeout)
	} else {
		f, err = os.Open(source)
	}
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return profile.Parse(f)
}

// FetchURL fetches a profile from a URL using HTTP.
func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
	resp, err := httpGet(source, timeout)
	if err != nil {
		return nil, fmt.Errorf("http fetch: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}

	return resp.Body, nil
}

// PostURL issues a POST to a URL over HTTP.
func PostURL(source, post string) ([]byte, error) {
	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
	if err != nil {
		return nil, fmt.Errorf("http post %s: %v", source, err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// httpGet is a wrapper around http.Get; it is defined as a variable
// so it can be redefined during for testing.
var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
	url, err := url.Parse(source)
	if err != nil {
		return nil, err
	}

	var tlsConfig *tls.Config
	if url.Scheme == "https+insecure" {
		tlsConfig = &tls.Config{
			InsecureSkipVerify: true,
		}
		url.Scheme = "https"
		source = url.String()
	}

	client := &http.Client{
		Transport: &http.Transport{
			ResponseHeaderTimeout: timeout + 5*time.Second,
			TLSClientConfig:       tlsConfig,
		},
	}
	return client.Get(source)
}
<|endoftext|>


<commit_before>package dayofprogrammer

import (
	"fmt"
	"time"
)

func Get(day, year int) string {
	calendar := getCalendarByYear(year)
	return calendar.get(day)
}

func getCalendarByYear(year int) calendar {
	switch {
	case 1700 <= year && year <= 1917:
		return newJulianCalendar(year)
	case 1700 <= year && year <= 1917:
		return newJulianCalendar(year)
	default:
		return newGregorianCalendar(year)
	}
}

func newJulianCalendar(year int) calendar {
	daysByMonth := map[time.Month]int{
		time.January:   31,
		time.February:  28,
		time.March:     31,
		time.April:     30,
		time.May:       31,
		time.June:      30,
		time.July:      31,
		time.August:    31,
		time.September: 30,
		time.October:   31,
		time.November:  30,
		time.December:  31,
	}

	if isJulianLeapYear(year) {
		daysByMonth[time.February]++
	}

	return calendar{year, daysByMonth}
}

func newGregorianCalendar(year int) calendar {
	daysByMonth := map[time.Month]int{
		time.January:   31,
		time.February:  28,
		time.March:     31,
		time.April:     30,
		time.May:       31,
		time.June:      30,
		time.July:      31,
		time.August:    31,
		time.September: 30,
		time.October:   31,
		time.November:  30,
		time.December:  31,
	}

	if isGregorianLeapYear(year) {
		daysByMonth[time.February]++
	}

	return calendar{year, daysByMonth}
}

func isJulianLeapYear(year int) bool {
	return year%4 == 0
}

func isGregorianLeapYear(year int) bool {
	return year%400 == 0 || (year%4 == 0 && year%100 != 0)
}

type calendar struct {
	year        int
	daysByMonth map[time.Month]int
}

func (c calendar) get(day int) string {
	var daysTranspired int
	months := []time.Month{
		time.January,
		time.February,
		time.March,
		time.April,
		time.May,
		time.June,
		time.July,
		time.August,
		time.September,
		time.October,
		time.November,
		time.December,
	}

	var month time.Month
	var dayInMonth int

	for _, m := range months {
		daysInMonth := c.daysByMonth[m]

		if c.year == 1918 && m == time.February {
			daysInMonth -= 13
		}

		if daysTranspired+daysInMonth < day {
			daysTranspired += daysInMonth
		} else {
			month = m
			dayInMonth = day - daysTranspired

			if c.year == 1918 && m == time.February {
				dayInMonth += 13
			}
			break
		}
	}

	return fmt.Sprintf("%02d.%02d.%d", dayInMonth, month, c.year)
}
<commit_msg>removes a duplicate case<commit_after>package dayofprogrammer

import (
	"fmt"
	"time"
)

func Get(day, year int) string {
	calendar := getCalendarByYear(year)
	return calendar.get(day)
}

func getCalendarByYear(year int) calendar {
	switch {
	case 1700 <= year && year <= 1917:
		return newJulianCalendar(year)
	default:
		return newGregorianCalendar(year)
	}
}

func newJulianCalendar(year int) calendar {
	daysByMonth := map[time.Month]int{
		time.January:   31,
		time.February:  28,
		time.March:     31,
		time.April:     30,
		time.May:       31,
		time.June:      30,
		time.July:      31,
		time.August:    31,
		time.September: 30,
		time.October:   31,
		time.November:  30,
		time.December:  31,
	}

	if isJulianLeapYear(year) {
		daysByMonth[time.February]++
	}

	return calendar{year, daysByMonth}
}

func newGregorianCalendar(year int) calendar {
	daysByMonth := map[time.Month]int{
		time.January:   31,
		time.February:  28,
		time.March:     31,
		time.April:     30,
		time.May:       31,
		time.June:      30,
		time.July:      31,
		time.August:    31,
		time.September: 30,
		time.October:   31,
		time.November:  30,
		time.December:  31,
	}

	if isGregorianLeapYear(year) {
		daysByMonth[time.February]++
	}

	return calendar{year, daysByMonth}
}

func isJulianLeapYear(year int) bool {
	return year%4 == 0
}

func isGregorianLeapYear(year int) bool {
	return year%400 == 0 || (year%4 == 0 && year%100 != 0)
}

type calendar struct {
	year        int
	daysByMonth map[time.Month]int
}

func (c calendar) get(day int) string {
	var daysTranspired int
	months := []time.Month{
		time.January,
		time.February,
		time.March,
		time.April,
		time.May,
		time.June,
		time.July,
		time.August,
		time.September,
		time.October,
		time.November,
		time.December,
	}

	var month time.Month
	var dayInMonth int

	for _, m := range months {
		daysInMonth := c.daysByMonth[m]

		if c.year == 1918 && m == time.February {
			daysInMonth -= 13
		}

		if daysTranspired+daysInMonth < day {
			daysTranspired += daysInMonth
		} else {
			month = m
			dayInMonth = day - daysTranspired

			if c.year == 1918 && m == time.February {
				dayInMonth += 13
			}
			break
		}
	}

	return fmt.Sprintf("%02d.%02d.%d", dayInMonth, month, c.year)
}
<|endoftext|>


<commit_before>package pgcatalog

import (
	"errors"
	"fmt"

	"github.com/cloudstax/firecamp/catalog"
	"github.com/cloudstax/firecamp/common"
	"github.com/cloudstax/firecamp/dns"
	"github.com/cloudstax/firecamp/manage"
	"github.com/cloudstax/firecamp/utils"
)

const (
	defaultVersion = "9.6"
	// ContainerImage is the main PostgreSQL running container.
	ContainerImage = common.ContainerNamePrefix + "postgres:" + defaultVersion
	// PostGISContainerImage is the container image for PostgreSQL with PostGIS.
	PostGISContainerImage = common.ContainerNamePrefix + "postgres-postgis:" + defaultVersion

	defaultPort = 5432

	containerRolePrimary     = "primary"
	containerRoleStandby     = "standby"
	primaryPGConfFileName    = "primary_postgresql.conf"
	standbyPGConfFileName    = "standby_postgresql.conf"
	primaryPGHbaConfFileName = "primary_pg_hba.conf"
	standbyPGHbaConfFileName = "standby_pg_hba.conf"
	recoveryConfFileName     = "recovery.conf"
)

// The default PostgreSQL catalog service. By default,
// 1) One PostgreSQL has 1 primary and 2 secondary replicas across 3 availability zones.
// 2) Listen on the standard port, 5432.

// ValidateRequest checks if the request is valid
func ValidateRequest(req *manage.CatalogCreatePostgreSQLRequest) error {
	// for now, limit the container image to postgres and postgres-postgis only.
	// after we get more requirements and finalize the design for the custom image, we may remove this check.
	if len(req.Options.ContainerImage) != 0 && req.Options.ContainerImage != ContainerImage && req.Options.ContainerImage != PostGISContainerImage {
		return errors.New("only postgres and postgres-postgis images are supported")
	}
	// use the dedicated volume for the WAL logs could help on performance.
	// But it introduces the potential data consistency risks.
	// When the server crashes, it might happen that some pages in the data
	// files are written only partially (because most disks have a much smaller
	// blocksize than the PostgreSQL page size (which is 8k by default)).
	// If the WAL volume happens to crash, server cannot reply the WAL log,
	// those half-written pages will not be repaired. Would need to restore database
	// from the backups.
	// While, this is probably ok. If WAL log is on the same volume with data, the volume
	// is possible to crash as well. Then we will have to recover database from the backups.
	// https://www.postgresql.org/docs/9.6/static/wal-internals.html
	// https://wiki.postgresql.org/wiki/Installation_and_Administration_Best_practices#WAL_Directory
	// https://www.postgresql.org/message-id/4573FEE0.2010406@logix-tt.com
	if req.Options.JournalVolume == nil {
		return errors.New("postgres should have separate volume for journal")
	}

	return nil
}

// GenDefaultCreateServiceRequest returns the default PostgreSQL creation request.
func GenDefaultCreateServiceRequest(platform string, region string, azs []string,
	cluster string, service string, res *common.Resources, opts *manage.CatalogPostgreSQLOptions) *manage.CreateServiceRequest {
	// generate service configs
	serviceCfgs := genServiceConfigs(platform, cluster, service, azs, defaultPort, opts)

	// generate member ReplicaConfigs
	replicaCfgs := genReplicaConfigs(platform, cluster, service, azs, defaultPort, opts)

	portmapping := common.PortMapping{
		ContainerPort: defaultPort,
		HostPort:      defaultPort,
		IsServicePort: true,
	}

	image := ContainerImage
	if len(opts.ContainerImage) != 0 {
		image = opts.ContainerImage
	}

	req := &manage.CreateServiceRequest{
		Service: &manage.ServiceCommonRequest{
			Region:      region,
			Cluster:     cluster,
			ServiceName: service,
			ServiceType: common.ServiceTypeStateful,
		},

		Resource:           res,
		CatalogServiceType: common.CatalogService_PostgreSQL,

		ContainerImage: image,
		Replicas:       opts.Replicas,
		PortMappings:   []common.PortMapping{portmapping},
		RegisterDNS:    true,

		ServiceConfigs: serviceCfgs,

		Volume:               opts.Volume,
		ContainerPath:        common.DefaultContainerMountPath,
		JournalVolume:        opts.JournalVolume,
		JournalContainerPath: common.DefaultJournalVolumeContainerMountPath,

		ReplicaConfigs: replicaCfgs,
	}
	return req
}

func genServiceConfigs(platform string, cluster string, service string, azs []string,
	port int64, opts *manage.CatalogPostgreSQLOptions) []*manage.ConfigFileContent {
	domain := dns.GenDefaultDomainName(cluster)
	primaryMember := utils.GenServiceMemberName(service, 0)
	primaryHost := dns.GenDNSName(primaryMember, domain)

	// create service.conf file
	content := fmt.Sprintf(servicefileContent, platform, primaryHost, port, opts.AdminPasswd, opts.ReplUser, opts.ReplUserPasswd)
	serviceCfg := &manage.ConfigFileContent{
		FileName: catalog.SERVICE_FILE_NAME,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	// create primary_postgresql.conf
	primaryCfg := &manage.ConfigFileContent{
		FileName: primaryPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  primaryPostgresConf,
	}

	// create standby_postgresql.conf
	standbyCfg := &manage.ConfigFileContent{
		FileName: standbyPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  standbyPostgresConf,
	}

	// create primary_pg_hba.conf
	primaryHbaCfg := &manage.ConfigFileContent{
		FileName: primaryPGHbaConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  primaryPgHbaConf,
	}

	// create standby_pg_hba.conf
	standbyHbaCfg := &manage.ConfigFileContent{
		FileName: standbyPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  standbyPgHbaConf,
	}

	// create recovery.conf
	primaryConnInfo := fmt.Sprintf(standbyPrimaryConnInfo, primaryHost, port, opts.ReplUser, opts.ReplUserPasswd)
	content = fmt.Sprintf(standbyRecoveryConf, primaryConnInfo, standbyRestoreCmd)
	recoveryCfg := &manage.ConfigFileContent{
		FileName: recoveryConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	return []*manage.ConfigFileContent{serviceCfg, primaryCfg, standbyCfg, primaryHbaCfg, standbyHbaCfg, recoveryCfg}
}

// genReplicaConfigs generates the replica configs.
// Note: if the number of availability zones is less than replicas, 2 or more replicas will run on the same zone.
func genReplicaConfigs(platform string, cluster string, service string, azs []string,
	port int64, opts *manage.CatalogPostgreSQLOptions) []*manage.ReplicaConfig {
	replicaCfgs := make([]*manage.ReplicaConfig, opts.Replicas)

	// generate the primary configs
	domain := dns.GenDefaultDomainName(cluster)
	primaryMember := utils.GenServiceMemberName(service, 0)
	primaryHost := dns.GenDNSName(primaryMember, domain)

	bindIP := primaryHost
	if platform == common.ContainerPlatformSwarm {
		bindIP = catalog.BindAllIP
	}

	content := fmt.Sprintf(memberfileContent, containerRolePrimary, azs[0], primaryHost, bindIP)
	primaryMemberCfg := &manage.ConfigFileContent{
		FileName: catalog.MEMBER_FILE_NAME,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	configs := []*manage.ConfigFileContent{primaryMemberCfg}
	replicaCfgs[0] = &manage.ReplicaConfig{Zone: azs[0], MemberName: primaryMember, Configs: configs}

	// generate the standby configs.
	// TODO support cascading replication, especially for cross-region replication.
	for i := int64(1); i < opts.Replicas; i++ {
		index := int(i) % len(azs)
		member := utils.GenServiceMemberName(service, i)
		memberHost := dns.GenDNSName(member, domain)

		bindIP := memberHost
		if platform == common.ContainerPlatformSwarm {
			bindIP = catalog.BindAllIP
		}

		content := fmt.Sprintf(memberfileContent, containerRoleStandby, azs[index], memberHost, bindIP)
		memberCfg := &manage.ConfigFileContent{
			FileName: catalog.MEMBER_FILE_NAME,
			FileMode: common.DefaultConfigFileMode,
			Content:  content,
		}

		configs := []*manage.ConfigFileContent{memberCfg}
		replicaCfgs[i] = &manage.ReplicaConfig{Zone: azs[index], MemberName: member, Configs: configs}
	}

	return replicaCfgs
}

const (
	servicefileContent = `
PLATFORM=%s
PRIMARY_HOST=%s
LISTEN_PORT=%d
POSTGRES_PASSWORD=%s
REPLICATION_USER=%s
REPLICATION_PASSWORD=%s
`

	memberfileContent = `
CONTAINER_ROLE=%s
AVAILABILITY_ZONE=%s
SERVICE_MEMBER=%s
BIND_IP=%s
`

	primaryPostgresConf = `
listen_addresses = 'localhost'

# To enable read-only queries on a standby server, wal_level must be set to
# "hot_standby".
wal_level = hot_standby

# allow up to 5 standby servers
max_wal_senders = 5

# To prevent the primary server from removing the WAL segments required for
# the standby server before shipping them, set the minimum number of segments
# retained in the pg_xlog directory. At least wal_keep_segments should be
# larger than the number of segments generated between the beginning of
# online-backup and the startup of streaming replication. If you enable WAL
# archiving to an archive directory accessible from the standby, this may
# not be necessary.
wal_keep_segments = 32

# Enable WAL archiving on the primary to an archive directory accessible from
# the standby. If wal_keep_segments is a high enough number to retain the WAL
# segments required for the standby server, this is not necessary.
# TODO enable it with archiving to S3
#archive_mode    = on
#archive_command = 'cp %p /path_to/archive/%f'

log_line_prefix = '%t %c %u %r '
`

	primaryPgHbaConf = `
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust

# the application hosts to access PostgreSQL
host    all             all               all                    md5

# the PostgreSQL standby hosts to access PostgreSQL replication
# need to allow all. The primary only gets the standby's ip address,
# then does reverse lookup, which returns EC2's private DNS name.
# The primary has no way to know the DNS name in Route53.
# There is no security concern for this. The EC2's inbound rule will
# limit the source from the specific security group.
host   replication      defaultReplUser                all                    md5
`

	standbyPostgresConf = `
listen_addresses = 'localhost'

hot_standby = on

log_line_prefix = '%t %c %u %r '
`

	standbyPgHbaConf = `
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust

# the application hosts to access PostgreSQL
host    all             all               all                    md5
`

	standbyPrimaryConnInfo = "host=%s port=%d user=%s password=%s"
	standbyRestoreCmd      = `cp /path_to/archive/%f "%p"`
	standbyRecoveryConf    = `
# Specifies whether to start the server as a standby. In streaming replication,
# this parameter must to be set to on.
standby_mode          = 'on'

# Specifies a connection string which is used for the standby server to connect
# with the primary.
primary_conninfo      = '%s'

# Specifies a trigger file whose presence should cause streaming replication to
# end (i.e., failover).
trigger_file = '/data/recovery-trigger'

# Specifies a command to load archive segments from the WAL archive. If
# wal_keep_segments is a high enough number to retain the WAL segments
# required for the standby server, this may not be necessary. But
# a large workload can cause segments to be recycled before the standby
# is fully synchronized, requiring you to start again from a new base backup.
# TODO enable it with archiving to S3
#restore_command = '%s'
`
)
<commit_msg>add pg image to service config<commit_after>package pgcatalog

import (
	"errors"
	"fmt"

	"github.com/cloudstax/firecamp/catalog"
	"github.com/cloudstax/firecamp/common"
	"github.com/cloudstax/firecamp/dns"
	"github.com/cloudstax/firecamp/manage"
	"github.com/cloudstax/firecamp/utils"
)

const (
	defaultVersion = "9.6"
	// ContainerImage is the main PostgreSQL running container.
	ContainerImage = common.ContainerNamePrefix + "postgres:" + defaultVersion
	// PostGISContainerImage is the container image for PostgreSQL with PostGIS.
	PostGISContainerImage = common.ContainerNamePrefix + "postgres-postgis:" + defaultVersion

	defaultPort = 5432

	containerRolePrimary     = "primary"
	containerRoleStandby     = "standby"
	primaryPGConfFileName    = "primary_postgresql.conf"
	standbyPGConfFileName    = "standby_postgresql.conf"
	primaryPGHbaConfFileName = "primary_pg_hba.conf"
	standbyPGHbaConfFileName = "standby_pg_hba.conf"
	recoveryConfFileName     = "recovery.conf"
)

// The default PostgreSQL catalog service. By default,
// 1) One PostgreSQL has 1 primary and 2 secondary replicas across 3 availability zones.
// 2) Listen on the standard port, 5432.

// ValidateRequest checks if the request is valid
func ValidateRequest(req *manage.CatalogCreatePostgreSQLRequest) error {
	// for now, limit the container image to postgres and postgres-postgis only.
	// after we get more requirements and finalize the design for the custom image, we may remove this check.
	if len(req.Options.ContainerImage) != 0 && req.Options.ContainerImage != ContainerImage && req.Options.ContainerImage != PostGISContainerImage {
		return errors.New("only postgres and postgres-postgis images are supported")
	}
	// use the dedicated volume for the WAL logs could help on performance.
	// But it introduces the potential data consistency risks.
	// When the server crashes, it might happen that some pages in the data
	// files are written only partially (because most disks have a much smaller
	// blocksize than the PostgreSQL page size (which is 8k by default)).
	// If the WAL volume happens to crash, server cannot reply the WAL log,
	// those half-written pages will not be repaired. Would need to restore database
	// from the backups.
	// While, this is probably ok. If WAL log is on the same volume with data, the volume
	// is possible to crash as well. Then we will have to recover database from the backups.
	// https://www.postgresql.org/docs/9.6/static/wal-internals.html
	// https://wiki.postgresql.org/wiki/Installation_and_Administration_Best_practices#WAL_Directory
	// https://www.postgresql.org/message-id/4573FEE0.2010406@logix-tt.com
	if req.Options.JournalVolume == nil {
		return errors.New("postgres should have separate volume for journal")
	}

	return nil
}

// GenDefaultCreateServiceRequest returns the default PostgreSQL creation request.
func GenDefaultCreateServiceRequest(platform string, region string, azs []string,
	cluster string, service string, res *common.Resources, opts *manage.CatalogPostgreSQLOptions) *manage.CreateServiceRequest {
	// generate service configs
	serviceCfgs := genServiceConfigs(platform, cluster, service, azs, defaultPort, opts)

	// generate member ReplicaConfigs
	replicaCfgs := genReplicaConfigs(platform, cluster, service, azs, defaultPort, opts)

	portmapping := common.PortMapping{
		ContainerPort: defaultPort,
		HostPort:      defaultPort,
		IsServicePort: true,
	}

	image := ContainerImage
	if len(opts.ContainerImage) != 0 {
		image = opts.ContainerImage
	}

	req := &manage.CreateServiceRequest{
		Service: &manage.ServiceCommonRequest{
			Region:      region,
			Cluster:     cluster,
			ServiceName: service,
			ServiceType: common.ServiceTypeStateful,
		},

		Resource:           res,
		CatalogServiceType: common.CatalogService_PostgreSQL,

		ContainerImage: image,
		Replicas:       opts.Replicas,
		PortMappings:   []common.PortMapping{portmapping},
		RegisterDNS:    true,

		ServiceConfigs: serviceCfgs,

		Volume:               opts.Volume,
		ContainerPath:        common.DefaultContainerMountPath,
		JournalVolume:        opts.JournalVolume,
		JournalContainerPath: common.DefaultJournalVolumeContainerMountPath,

		ReplicaConfigs: replicaCfgs,
	}
	return req
}

func genServiceConfigs(platform string, cluster string, service string, azs []string,
	port int64, opts *manage.CatalogPostgreSQLOptions) []*manage.ConfigFileContent {
	domain := dns.GenDefaultDomainName(cluster)
	primaryMember := utils.GenServiceMemberName(service, 0)
	primaryHost := dns.GenDNSName(primaryMember, domain)

	// create service.conf file
	content := fmt.Sprintf(servicefileContent, platform, opts.ContainerImage, primaryHost, port, opts.AdminPasswd, opts.ReplUser, opts.ReplUserPasswd)
	serviceCfg := &manage.ConfigFileContent{
		FileName: catalog.SERVICE_FILE_NAME,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	// create primary_postgresql.conf
	primaryCfg := &manage.ConfigFileContent{
		FileName: primaryPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  primaryPostgresConf,
	}

	// create standby_postgresql.conf
	standbyCfg := &manage.ConfigFileContent{
		FileName: standbyPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  standbyPostgresConf,
	}

	// create primary_pg_hba.conf
	primaryHbaCfg := &manage.ConfigFileContent{
		FileName: primaryPGHbaConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  primaryPgHbaConf,
	}

	// create standby_pg_hba.conf
	standbyHbaCfg := &manage.ConfigFileContent{
		FileName: standbyPGConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  standbyPgHbaConf,
	}

	// create recovery.conf
	primaryConnInfo := fmt.Sprintf(standbyPrimaryConnInfo, primaryHost, port, opts.ReplUser, opts.ReplUserPasswd)
	content = fmt.Sprintf(standbyRecoveryConf, primaryConnInfo, standbyRestoreCmd)
	recoveryCfg := &manage.ConfigFileContent{
		FileName: recoveryConfFileName,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	return []*manage.ConfigFileContent{serviceCfg, primaryCfg, standbyCfg, primaryHbaCfg, standbyHbaCfg, recoveryCfg}
}

// genReplicaConfigs generates the replica configs.
// Note: if the number of availability zones is less than replicas, 2 or more replicas will run on the same zone.
func genReplicaConfigs(platform string, cluster string, service string, azs []string,
	port int64, opts *manage.CatalogPostgreSQLOptions) []*manage.ReplicaConfig {
	replicaCfgs := make([]*manage.ReplicaConfig, opts.Replicas)

	// generate the primary configs
	domain := dns.GenDefaultDomainName(cluster)
	primaryMember := utils.GenServiceMemberName(service, 0)
	primaryHost := dns.GenDNSName(primaryMember, domain)

	bindIP := primaryHost
	if platform == common.ContainerPlatformSwarm {
		bindIP = catalog.BindAllIP
	}

	content := fmt.Sprintf(memberfileContent, containerRolePrimary, azs[0], primaryHost, bindIP)
	primaryMemberCfg := &manage.ConfigFileContent{
		FileName: catalog.MEMBER_FILE_NAME,
		FileMode: common.DefaultConfigFileMode,
		Content:  content,
	}

	configs := []*manage.ConfigFileContent{primaryMemberCfg}
	replicaCfgs[0] = &manage.ReplicaConfig{Zone: azs[0], MemberName: primaryMember, Configs: configs}

	// generate the standby configs.
	// TODO support cascading replication, especially for cross-region replication.
	for i := int64(1); i < opts.Replicas; i++ {
		index := int(i) % len(azs)
		member := utils.GenServiceMemberName(service, i)
		memberHost := dns.GenDNSName(member, domain)

		bindIP := memberHost
		if platform == common.ContainerPlatformSwarm {
			bindIP = catalog.BindAllIP
		}

		content := fmt.Sprintf(memberfileContent, containerRoleStandby, azs[index], memberHost, bindIP)
		memberCfg := &manage.ConfigFileContent{
			FileName: catalog.MEMBER_FILE_NAME,
			FileMode: common.DefaultConfigFileMode,
			Content:  content,
		}

		configs := []*manage.ConfigFileContent{memberCfg}
		replicaCfgs[i] = &manage.ReplicaConfig{Zone: azs[index], MemberName: member, Configs: configs}
	}

	return replicaCfgs
}

const (
	servicefileContent = `
PLATFORM=%s
CONTAINER_IMAGE=%s
PRIMARY_HOST=%s
LISTEN_PORT=%d
POSTGRES_PASSWORD=%s
REPLICATION_USER=%s
REPLICATION_PASSWORD=%s
`

	memberfileContent = `
CONTAINER_ROLE=%s
AVAILABILITY_ZONE=%s
SERVICE_MEMBER=%s
BIND_IP=%s
`

	primaryPostgresConf = `
listen_addresses = 'localhost'

# To enable read-only queries on a standby server, wal_level must be set to
# "hot_standby".
wal_level = hot_standby

# allow up to 5 standby servers
max_wal_senders = 5

# To prevent the primary server from removing the WAL segments required for
# the standby server before shipping them, set the minimum number of segments
# retained in the pg_xlog directory. At least wal_keep_segments should be
# larger than the number of segments generated between the beginning of
# online-backup and the startup of streaming replication. If you enable WAL
# archiving to an archive directory accessible from the standby, this may
# not be necessary.
wal_keep_segments = 32

# Enable WAL archiving on the primary to an archive directory accessible from
# the standby. If wal_keep_segments is a high enough number to retain the WAL
# segments required for the standby server, this is not necessary.
# TODO enable it with archiving to S3
#archive_mode    = on
#archive_command = 'cp %p /path_to/archive/%f'

log_line_prefix = '%t %c %u %r '
`

	primaryPgHbaConf = `
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust

# the application hosts to access PostgreSQL
host    all             all               all                    md5

# the PostgreSQL standby hosts to access PostgreSQL replication
# need to allow all. The primary only gets the standby's ip address,
# then does reverse lookup, which returns EC2's private DNS name.
# The primary has no way to know the DNS name in Route53.
# There is no security concern for this. The EC2's inbound rule will
# limit the source from the specific security group.
host   replication      defaultReplUser                all                    md5
`

	standbyPostgresConf = `
listen_addresses = 'localhost'

hot_standby = on

log_line_prefix = '%t %c %u %r '
`

	standbyPgHbaConf = `
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust

# the application hosts to access PostgreSQL
host    all             all               all                    md5
`

	standbyPrimaryConnInfo = "host=%s port=%d user=%s password=%s"
	standbyRestoreCmd      = `cp /path_to/archive/%f "%p"`
	standbyRecoveryConf    = `
# Specifies whether to start the server as a standby. In streaming replication,
# this parameter must to be set to on.
standby_mode          = 'on'

# Specifies a connection string which is used for the standby server to connect
# with the primary.
primary_conninfo      = '%s'

# Specifies a trigger file whose presence should cause streaming replication to
# end (i.e., failover).
trigger_file = '/data/recovery-trigger'

# Specifies a command to load archive segments from the WAL archive. If
# wal_keep_segments is a high enough number to retain the WAL segments
# required for the standby server, this may not be necessary. But
# a large workload can cause segments to be recycled before the standby
# is fully synchronized, requiring you to start again from a new base backup.
# TODO enable it with archiving to S3
#restore_command = '%s'
`
)
<|endoftext|>


<commit_before>package parser

import (
	"encoding/xml"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type Document struct {
	SaveGame
}

type SaveGame struct {
	Locations []Location `xml:"locations"`
}

type Location struct {
	GameLocations []GameLocation `xml:"GameLocation"`
	XML           string         `xml:",innerxml"`
}

type GameLocation struct {
	Name            string          `xml:"name"`
	Objects         Objects         `xml:"objects"`
	TerrainFeatures TerrainFeatures `xml:"terrainFeatures"`
	XML             string          `xml:",innerxml"`
}

type Objects struct {
	Items []ObjectItem `xml:"item"`
}

type TerrainFeatures struct {
	Items []TerrainItem `xml:"item"`
	XML   string        `xml:",innerxml"`
}

type TerrainItem struct {
	Key   ItemKey          `xml:"key"`
	Value TerrainItemValue `xml:"value"`
}

func (i TerrainItem) ItemName() string {
	return "tree:" + strconv.Itoa(i.Value.TerrainFeature.TreeType)
}

func (i TerrainItem) X() int {
	return i.Key.Vector2.X
}

func (i TerrainItem) Y() int {
	return i.Key.Vector2.Y
}

type ObjectItem struct {
	Key   ItemKey   `xml:"key"`
	Value ItemValue `xml:"value"`
}

func (i ObjectItem) ItemName() string {
	return i.Value.Object.Name
}

func (i ObjectItem) X() int {
	return i.Key.Vector2.X
}

func (i ObjectItem) Y() int {
	return i.Key.Vector2.Y
}

type Item interface {
	ItemName() string
	X() int
	Y() int
}

type TerrainItemValue struct {
	TerrainFeature TerrainFeature
}

type TerrainFeature struct {
	TreeType int `xml:"treeType"`
}

type ItemValue struct {
	Object Object
}

type Object struct {
	Name         string `xml:"name"`
	TileLocation Vector `xml:"tileLocation"`
}

type ItemKey struct {
	Vector2 Vector
}

type Vector struct {
	X int
	Y int
}

type FarmMap struct {
	Loc [100][100]string
}

func Parse(r io.Reader) (*FarmMap, error) {

	dec := xml.NewDecoder(r)
	v := Document{}
	if err := dec.Decode(&v); err != nil {
		return nil, fmt.Errorf("error: %v", err)
	}
	var farm GameLocation
	var farmMap FarmMap
	for _, loc := range v.Locations {
		for _, gameloc := range loc.GameLocations {
			if gameloc.Name == "Farm" {
				farm = gameloc
			}
		}
	}
	if farm.Name == "" {
		return nil, fmt.Errorf("Could not find farm in game save")
	}

	var allObjects []Item
	for _, i := range farm.TerrainFeatures.Items {
		allObjects = append(allObjects, i)
	}
	for _, i := range farm.Objects.Items {
		allObjects = append(allObjects, i)
	}
	for _, object := range allObjects {
		if object.Y() > 100 || object.X() > 100 {
			return nil, fmt.Errorf("Found object vector location outside normal bounds: X=%d, Y=%d", object.Y(), object.X())
		}
		farmMap.Loc[object.Y()][object.X()] = object.ItemName()

	}
	return &farmMap, nil
}

func ASCIIImage(farmMap *FarmMap) {
	for _, j := range farmMap.Loc {
		stuff := []string{}
		for _, what := range j {
			if strings.HasPrefix(what, "tree") {
				fmt.Print("T")
				stuff = append(stuff, what)
			} else if what != "" {
				fmt.Print("x")
				stuff = append(stuff, what)
			} else {
				fmt.Print(".")
			}
		}
		p := fmt.Sprint(stuff)
		if len(p) > 80 {
			p = p[0:79]
		}
		fmt.Print(p)
		fmt.Println()
	}
}
<commit_msg>Panic protection<commit_after>package parser

import (
	"encoding/xml"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type Document struct {
	SaveGame
}

type SaveGame struct {
	Locations []Location `xml:"locations"`
}

type Location struct {
	GameLocations []GameLocation `xml:"GameLocation"`
	XML           string         `xml:",innerxml"`
}

type GameLocation struct {
	Name            string          `xml:"name"`
	Objects         Objects         `xml:"objects"`
	TerrainFeatures TerrainFeatures `xml:"terrainFeatures"`
	XML             string          `xml:",innerxml"`
}

type Objects struct {
	Items []ObjectItem `xml:"item"`
}

type TerrainFeatures struct {
	Items []TerrainItem `xml:"item"`
	XML   string        `xml:",innerxml"`
}

type TerrainItem struct {
	Key   ItemKey          `xml:"key"`
	Value TerrainItemValue `xml:"value"`
}

func (i TerrainItem) ItemName() string {
	return "tree:" + strconv.Itoa(i.Value.TerrainFeature.TreeType)
}

func (i TerrainItem) X() int {
	return i.Key.Vector2.X
}

func (i TerrainItem) Y() int {
	return i.Key.Vector2.Y
}

type ObjectItem struct {
	Key   ItemKey   `xml:"key"`
	Value ItemValue `xml:"value"`
}

func (i ObjectItem) ItemName() string {
	return i.Value.Object.Name
}

func (i ObjectItem) X() int {
	return i.Key.Vector2.X
}

func (i ObjectItem) Y() int {
	return i.Key.Vector2.Y
}

type Item interface {
	ItemName() string
	X() int
	Y() int
}

type TerrainItemValue struct {
	TerrainFeature TerrainFeature
}

type TerrainFeature struct {
	TreeType int `xml:"treeType"`
}

type ItemValue struct {
	Object Object
}

type Object struct {
	Name         string `xml:"name"`
	TileLocation Vector `xml:"tileLocation"`
}

type ItemKey struct {
	Vector2 Vector
}

type Vector struct {
	X int
	Y int
}

type FarmMap struct {
	Loc [100][100]string
}

func Parse(r io.Reader) (*FarmMap, error) {

	dec := xml.NewDecoder(r)
	v := Document{}
	if err := dec.Decode(&v); err != nil {
		return nil, fmt.Errorf("error: %v", err)
	}
	var farm GameLocation
	var farmMap FarmMap
	for _, loc := range v.Locations {
		for _, gameloc := range loc.GameLocations {
			if gameloc.Name == "Farm" {
				farm = gameloc
			}
		}
	}
	if farm.Name == "" {
		return nil, fmt.Errorf("Could not find farm in game save")
	}

	var allObjects []Item
	for _, i := range farm.TerrainFeatures.Items {
		allObjects = append(allObjects, i)
	}
	for _, i := range farm.Objects.Items {
		allObjects = append(allObjects, i)
	}
	for _, object := range allObjects {
		if object.Y() >= len(farmMap.Loc) || object.X() >= len(farmMap.Loc[object.Y()]) {
			return nil, fmt.Errorf("Found object vector location outside normal bounds: X=%d, Y=%d", object.Y(), object.X())
		}
		farmMap.Loc[object.Y()][object.X()] = object.ItemName()

	}
	return &farmMap, nil
}

func ASCIIImage(farmMap *FarmMap) {
	for _, j := range farmMap.Loc {
		stuff := []string{}
		for _, what := range j {
			if strings.HasPrefix(what, "tree") {
				fmt.Print("T")
				stuff = append(stuff, what)
			} else if what != "" {
				fmt.Print("x")
				stuff = append(stuff, what)
			} else {
				fmt.Print(".")
			}
		}
		p := fmt.Sprint(stuff)
		if len(p) > 80 {
			p = p[0:79]
		}
		fmt.Print(p)
		fmt.Println()
	}
}
<|endoftext|>