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

package engi

import "math"

// A rather basic camera
type Camera struct {
	pos, to  Point
	tracking *Entity // The entity that is currently being followed
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	cam.to = space.Position
	cam.centerCam(Width(), Height(), 1, space)
}

func (cam *Camera) Update(dt float32) {
	// maxPoints := 8
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		cam.centerCam(Width(), Height(), 0.09, space)
	}
}

func (cam *Camera) centerCam(width, height, lerp float32, space *SpaceComponent) {
	lvl := Files.Level("test.tmx")

	maxX := float32(lvl.Width * lvl.TileWidth)
	maxY := float32(lvl.Height * lvl.TileHeight)

	cam.to.X += ((space.Position.X + space.Width/2) - (cam.to.X + width/2)) * lerp
	cam.to.Y += ((space.Position.Y + space.Height/2) - (cam.to.Y + height/2)) * lerp

	if !(cam.to.X+width >= maxX) && !(cam.to.X <= 0) {
		cam.pos.X = cam.to.X
	}
	if !(float32(math.Abs(float64(cam.to.Y)))+height/2 >= maxY) {
		cam.pos.Y = cam.to.Y
	}
}
<commit_msg>camera: remove floating point errors<commit_after>// Copyright 2014 Harrison Shoebridge. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engi

import "math"

// A rather basic camera
type Camera struct {
	pos, to  Point
	tracking *Entity // The entity that is currently being followed
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	cam.to = space.Position
	cam.centerCam(Width(), Height(), 1, space)
}

func (cam *Camera) Update(dt float32) {
	// maxPoints := 8
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		cam.centerCam(Width(), Height(), 0.09, space)
	}
}

func (cam *Camera) centerCam(width, height, lerp float32, space *SpaceComponent) {
	lvl := Files.Level("test.tmx")

	maxX := float32(lvl.Width * lvl.TileWidth)
	maxY := float32(lvl.Height * lvl.TileHeight)

	cam.to.X += ((space.Position.X + space.Width/2) - (cam.to.X + width/2)) * lerp
	cam.to.Y += ((space.Position.Y + space.Height/2) - (cam.to.Y + height/2)) * lerp

	if !(cam.to.X+width >= maxX) && !(cam.to.X <= 0) {
		cam.pos.X = fif(cam.to.X)
	}
	if !(float32(math.Abs(float64(cam.to.Y)))+height/2 >= maxY) {
		cam.pos.Y = fif(cam.to.Y)
	}
}

// floatIntFloat is for those dark days when you need to remove an unwanted
// decimal place value.
// TODO check performance of this vs traditional versions
func floatIntFloat(i float32) float32 {
	return float32(int(i))
}
<|endoftext|>


<commit_before>package rocker_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"

	"github.com/cloudcredo/cloudrocker/config"
	"github.com/cloudcredo/cloudrocker/rocker"
	"github.com/cloudcredo/cloudrocker/utils"

	. "github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
)

var _ = Describe("Rocker", func() {
	var (
		testrocker *rocker.Rocker
		buffer     *gbytes.Buffer
	)
	BeforeEach(func() {
		testrocker = rocker.NewRocker()
		buffer = gbytes.NewBuffer()
	})

	Describe("Displaying the docker version", func() {
		Context("REALDOCKER", func() {
			It("should tell Docker to output its version", func() {
				rocker.DockerVersion(buffer)
				Eventually(buffer).Should(gbytes.Say(`Checking Docker version`))
				Eventually(buffer).Should(gbytes.Say(`Client API version: `))
				Eventually(buffer).Should(gbytes.Say(`Go version \(client\): go`))
			})
		})
	})

	Describe("Bootstrapping the base image", func() {
		//This works, but speed depends on your net connection
		XIt("should download and tag the lucid64 filesystem", func() {
			fmt.Println("Downloading lucid64 - this could take a while")
			rocker.ImportRootfsImage(buffer)
			Eventually(buffer, 600).Should(gbytes.Say(`[a-f0-9]{64}`))
		})
	})

	Describe("Managing buildpacks", func() {
		var (
			buildpackDir string
		)

		BeforeEach(func() {
			buildpackDir, _ = ioutil.TempDir(os.TempDir(), "crocker-buildpack-test")
		})

		AfterEach(func() {
			os.RemoveAll(buildpackDir)
		})

		Describe("Adding a buildpack", func() {
			It("should download the buildpack and add it to the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				Eventually(buffer).Should(gbytes.Say(`Downloading buildpack...`))
				Eventually(buffer, 10).Should(gbytes.Say(`Downloaded buildpack.`))
			})
		})

		Describe("Deleting a buildpack", func() {
			It("should delete the buildpack from the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				testrocker.DeleteBuildpack(buffer, "not-a-buildpack")
				Eventually(buffer).Should(gbytes.Say(`Deleted buildpack.`))
			})
		})

		Describe("Listing buildpacks", func() {
			It("should list the buildpacks in the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				testrocker.ListBuildpacks(buffer)
				Eventually(buffer).Should(gbytes.Say(`not-a-buildpack`))
			})
		})
	})

	Describe("Staging an application", func() {
		var (
			cloudrockerHome string
			originalDir     string
		)

		BeforeEach(func() {
			cloudrockerHome, _ = ioutil.TempDir(os.TempDir(), "rocker-staging-test")
			os.Setenv("CLOUDROCKER_HOME", cloudrockerHome)
		})

		AfterEach(func() {
			os.RemoveAll(cloudrockerHome)
			os.Chdir(originalDir)
		})

		Context("with a detected buildpack", func() {
			Context("REALDOCKER", func() {
				BeforeEach(func() {
					cp("fixtures/stage/buildpacks", cloudrockerHome)
					originalDir = utils.Pwd()
					os.Chdir("fixtures/stage/apps/bash-app")
					testrocker = rocker.NewRocker()
					err := testrocker.RunStager(buffer)
					Expect(err).ShouldNot(HaveOccurred())
				})

				It("should transfer dotfiles to the staging directory", func() {
					stagingDir, err := os.Open(config.NewDirectories(cloudrockerHome).Staging())
					stagingDirContents, err := stagingDir.Readdirnames(0)
					Expect(stagingDirContents, err).Should(ContainElement(".testdotfile"))
				})

				It("should create the droplet", func() {
					dropletDir, err := os.Open(config.NewDirectories(cloudrockerHome).Tmp())
					dropletDirContents, err := dropletDir.Readdirnames(0)
					Expect(dropletDirContents, err).Should(ContainElement("droplet"))
				})
			})
		})

		Context("with a buildpack that doesn't detect", func() {
			Context("REALDOCKER", func() {
				It("tell us we don't have a valid buildpack", func() {
					cp("fixtures/runtime/buildpacks", cloudrockerHome)
					originalDir = utils.Pwd()
					os.Chdir("fixtures/stage/apps/bash-app")
					testrocker = rocker.NewRocker()
					err := testrocker.RunStager(buffer)
					Expect(err).Should(MatchError("Staging failed - have you added a buildpack for this type of application?"))
				})
			})
		})
	})

	Describe("Managing applications", func() {
		Context("REALDOCKER", func() {
			var (
				cloudrockerHome string
				appDir          string
				originalDir     string
			)

			BeforeEach(func() {
				cloudrockerHome, _ = ioutil.TempDir(os.TempDir(), "rocker-runtime-test")
				os.Setenv("CLOUDROCKER_HOME", cloudrockerHome)
				cp("fixtures/runtime/buildpacks", cloudrockerHome)

				appDir, _ = ioutil.TempDir(os.TempDir(), "rocker-test-app")
				cp("fixtures/runtime/apps/cf-test-buildpack-app", appDir)

				originalDir = utils.Pwd()
				os.Chdir(appDir + "/cf-test-buildpack-app")

				testrocker = rocker.NewRocker()
				testrocker.RunStager(buffer)
			})

			AfterEach(func() {
				os.RemoveAll(cloudrockerHome)
				os.RemoveAll(appDir)
				os.Chdir(originalDir)
			})

			Describe("when running an application", func() {
				Context("without a currently running application", func() {
					It("should output a valid URL for the running application", func() {
						testrocker.RunRuntime(buffer)
						Eventually(buffer).Should(gbytes.Say(`Connect to your running application at http://localhost:8080/`))
						Eventually(statusCodeChecker).Should(Equal(200))
						testrocker.StopRuntime(buffer)
					})
				})
				Context("with a currently running application", func() {
					It("should delete the current container and output a valid URL for the new running application", func() {
						testrocker.RunRuntime(buffer)
						Eventually(buffer).Should(gbytes.Say(`Connect to your running application at http://localhost:8080/`))
						Eventually(statusCodeChecker).Should(Equal(200))
						testrocker.RunRuntime(buffer)
						Consistently(buffer).ShouldNot(gbytes.Say(`Conflict`))
						Eventually(statusCodeChecker).Should(Equal(200))
					})
				})
			})

			Describe("when stopping a running an application", func() {
				It("should stop the application", func() {
					testrocker.RunStager(buffer)
					testrocker.RunRuntime(buffer)
					Eventually(statusCodeChecker).Should(Equal(200))
					testrocker.StopRuntime(buffer)
					Eventually(statusCodeChecker).Should(Equal(0))
				})
			})
			Describe("when outputting a runnable application as a docker image", func() {
				It("should output the built image ID", func() {
					testrocker.RunStager(buffer)
					testrocker.BuildRuntimeImage(buffer)
					Eventually(buffer).Should(gbytes.Say(`Successfully built [a-f0-9]{12}`))
				})
			})
		})
		Describe("Creating and cleaning application directories", func() {
			Context("without a previously staged application", func() {
				It("should create the correct directory structure", func() {
					cloudrockerHome, _ := ioutil.TempDir(os.TempDir(), "utils-test-create-clean")
					err := rocker.CreateAndCleanAppDirs(config.NewDirectories(cloudrockerHome))
					Expect(err).ShouldNot(HaveOccurred())
					cloudrockerHomeFile, err := os.Open(cloudrockerHome)
					cloudrockerHomeContents, err := cloudrockerHomeFile.Readdirnames(0)
					Expect(cloudrockerHomeContents, err).Should(ContainElement("buildpacks"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("tmp"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("staging"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("droplet"))
					os.RemoveAll(cloudrockerHome)
				})
			})
			Context("with a previously staged application", func() {
				It("should clean the directory structure appropriately", func() {
					cloudrockerHome, _ := ioutil.TempDir(os.TempDir(), "utils-test-create-clean")
					dirs := map[string]bool{"/buildpacks": false, "/droplet": true, "/tmp/cache": false, "/staging": true}
					for dir, _ := range dirs {
						os.MkdirAll(cloudrockerHome+dir, 0755)
						ioutil.WriteFile(cloudrockerHome+dir+"/testfile", []byte("test"), 0644)
					}
					err := rocker.CreateAndCleanAppDirs(config.NewDirectories(cloudrockerHome))
					Expect(err).ShouldNot(HaveOccurred())
					for dir, clean := range dirs {
						dirFile, err := os.Open(cloudrockerHome + dir)
						dirContents, err := dirFile.Readdirnames(0)
						if clean {
							Expect(dirContents, err).ShouldNot(ContainElement("testfile"))
						} else {
							Expect(dirContents, err).Should(ContainElement("testfile"))
						}
					}
					os.RemoveAll(cloudrockerHome)
				})
			})
		})
	})
})

func statusCodeChecker() int {
	res, err := http.Get("http://localhost:8080/")
	if err != nil {
		return 0
	} else {
		return res.StatusCode
	}
}

func cp(src string, dst string) {
	session, err := gexec.Start(
		exec.Command("cp", "-a", src, dst),
		GinkgoWriter,
		GinkgoWriter,
	)
	Ω(err).ShouldNot(HaveOccurred())
	Eventually(session).Should(gexec.Exit(0))
}
<commit_msg>Changed to rootfs from lucid in test label<commit_after>package rocker_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"

	"github.com/cloudcredo/cloudrocker/config"
	"github.com/cloudcredo/cloudrocker/rocker"
	"github.com/cloudcredo/cloudrocker/utils"

	. "github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega"
	"github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	"github.com/cloudcredo/cloudrocker/Godeps/_workspace/src/github.com/onsi/gomega/gexec"
)

var _ = Describe("Rocker", func() {
	var (
		testrocker *rocker.Rocker
		buffer     *gbytes.Buffer
	)
	BeforeEach(func() {
		testrocker = rocker.NewRocker()
		buffer = gbytes.NewBuffer()
	})

	Describe("Displaying the docker version", func() {
		Context("REALDOCKER", func() {
			It("should tell Docker to output its version", func() {
				rocker.DockerVersion(buffer)
				Eventually(buffer).Should(gbytes.Say(`Checking Docker version`))
				Eventually(buffer).Should(gbytes.Say(`Client API version: `))
				Eventually(buffer).Should(gbytes.Say(`Go version \(client\): go`))
			})
		})
	})

	Describe("Bootstrapping the base image", func() {
		//This works, but speed depends on your net connection
		XIt("should download and tag the raw filesystem", func() {
			fmt.Println("Downloading rootfs - this could take a while")
			rocker.ImportRootfsImage(buffer)
			Eventually(buffer, 600).Should(gbytes.Say(`[a-f0-9]{64}`))
		})
	})

	Describe("Managing buildpacks", func() {
		var (
			buildpackDir string
		)

		BeforeEach(func() {
			buildpackDir, _ = ioutil.TempDir(os.TempDir(), "crocker-buildpack-test")
		})

		AfterEach(func() {
			os.RemoveAll(buildpackDir)
		})

		Describe("Adding a buildpack", func() {
			It("should download the buildpack and add it to the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				Eventually(buffer).Should(gbytes.Say(`Downloading buildpack...`))
				Eventually(buffer, 10).Should(gbytes.Say(`Downloaded buildpack.`))
			})
		})

		Describe("Deleting a buildpack", func() {
			It("should delete the buildpack from the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				testrocker.DeleteBuildpack(buffer, "not-a-buildpack")
				Eventually(buffer).Should(gbytes.Say(`Deleted buildpack.`))
			})
		})

		Describe("Listing buildpacks", func() {
			It("should list the buildpacks in the buildpack directory", func() {
				testrocker.AddBuildpack(buffer, "https://github.com/hatofmonkeys/not-a-buildpack", buildpackDir)
				testrocker.ListBuildpacks(buffer)
				Eventually(buffer).Should(gbytes.Say(`not-a-buildpack`))
			})
		})
	})

	Describe("Staging an application", func() {
		var (
			cloudrockerHome string
			originalDir     string
		)

		BeforeEach(func() {
			cloudrockerHome, _ = ioutil.TempDir(os.TempDir(), "rocker-staging-test")
			os.Setenv("CLOUDROCKER_HOME", cloudrockerHome)
		})

		AfterEach(func() {
			os.RemoveAll(cloudrockerHome)
			os.Chdir(originalDir)
		})

		Context("with a detected buildpack", func() {
			Context("REALDOCKER", func() {
				BeforeEach(func() {
					cp("fixtures/stage/buildpacks", cloudrockerHome)
					originalDir = utils.Pwd()
					os.Chdir("fixtures/stage/apps/bash-app")
					testrocker = rocker.NewRocker()
					err := testrocker.RunStager(buffer)
					Expect(err).ShouldNot(HaveOccurred())
				})

				It("should transfer dotfiles to the staging directory", func() {
					stagingDir, err := os.Open(config.NewDirectories(cloudrockerHome).Staging())
					stagingDirContents, err := stagingDir.Readdirnames(0)
					Expect(stagingDirContents, err).Should(ContainElement(".testdotfile"))
				})

				It("should create the droplet", func() {
					dropletDir, err := os.Open(config.NewDirectories(cloudrockerHome).Tmp())
					dropletDirContents, err := dropletDir.Readdirnames(0)
					Expect(dropletDirContents, err).Should(ContainElement("droplet"))
				})
			})
		})

		Context("with a buildpack that doesn't detect", func() {
			Context("REALDOCKER", func() {
				It("tell us we don't have a valid buildpack", func() {
					cp("fixtures/runtime/buildpacks", cloudrockerHome)
					originalDir = utils.Pwd()
					os.Chdir("fixtures/stage/apps/bash-app")
					testrocker = rocker.NewRocker()
					err := testrocker.RunStager(buffer)
					Expect(err).Should(MatchError("Staging failed - have you added a buildpack for this type of application?"))
				})
			})
		})
	})

	Describe("Managing applications", func() {
		Context("REALDOCKER", func() {
			var (
				cloudrockerHome string
				appDir          string
				originalDir     string
			)

			BeforeEach(func() {
				cloudrockerHome, _ = ioutil.TempDir(os.TempDir(), "rocker-runtime-test")
				os.Setenv("CLOUDROCKER_HOME", cloudrockerHome)
				cp("fixtures/runtime/buildpacks", cloudrockerHome)

				appDir, _ = ioutil.TempDir(os.TempDir(), "rocker-test-app")
				cp("fixtures/runtime/apps/cf-test-buildpack-app", appDir)

				originalDir = utils.Pwd()
				os.Chdir(appDir + "/cf-test-buildpack-app")

				testrocker = rocker.NewRocker()
				testrocker.RunStager(buffer)
			})

			AfterEach(func() {
				os.RemoveAll(cloudrockerHome)
				os.RemoveAll(appDir)
				os.Chdir(originalDir)
			})

			Describe("when running an application", func() {
				Context("without a currently running application", func() {
					It("should output a valid URL for the running application", func() {
						testrocker.RunRuntime(buffer)
						Eventually(buffer).Should(gbytes.Say(`Connect to your running application at http://localhost:8080/`))
						Eventually(statusCodeChecker).Should(Equal(200))
						testrocker.StopRuntime(buffer)
					})
				})
				Context("with a currently running application", func() {
					It("should delete the current container and output a valid URL for the new running application", func() {
						testrocker.RunRuntime(buffer)
						Eventually(buffer).Should(gbytes.Say(`Connect to your running application at http://localhost:8080/`))
						Eventually(statusCodeChecker).Should(Equal(200))
						testrocker.RunRuntime(buffer)
						Consistently(buffer).ShouldNot(gbytes.Say(`Conflict`))
						Eventually(statusCodeChecker).Should(Equal(200))
					})
				})
			})

			Describe("when stopping a running an application", func() {
				It("should stop the application", func() {
					testrocker.RunStager(buffer)
					testrocker.RunRuntime(buffer)
					Eventually(statusCodeChecker).Should(Equal(200))
					testrocker.StopRuntime(buffer)
					Eventually(statusCodeChecker).Should(Equal(0))
				})
			})
			Describe("when outputting a runnable application as a docker image", func() {
				It("should output the built image ID", func() {
					testrocker.RunStager(buffer)
					testrocker.BuildRuntimeImage(buffer)
					Eventually(buffer).Should(gbytes.Say(`Successfully built [a-f0-9]{12}`))
				})
			})
		})
		Describe("Creating and cleaning application directories", func() {
			Context("without a previously staged application", func() {
				It("should create the correct directory structure", func() {
					cloudrockerHome, _ := ioutil.TempDir(os.TempDir(), "utils-test-create-clean")
					err := rocker.CreateAndCleanAppDirs(config.NewDirectories(cloudrockerHome))
					Expect(err).ShouldNot(HaveOccurred())
					cloudrockerHomeFile, err := os.Open(cloudrockerHome)
					cloudrockerHomeContents, err := cloudrockerHomeFile.Readdirnames(0)
					Expect(cloudrockerHomeContents, err).Should(ContainElement("buildpacks"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("tmp"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("staging"))
					Expect(cloudrockerHomeContents, err).Should(ContainElement("droplet"))
					os.RemoveAll(cloudrockerHome)
				})
			})
			Context("with a previously staged application", func() {
				It("should clean the directory structure appropriately", func() {
					cloudrockerHome, _ := ioutil.TempDir(os.TempDir(), "utils-test-create-clean")
					dirs := map[string]bool{"/buildpacks": false, "/droplet": true, "/tmp/cache": false, "/staging": true}
					for dir, _ := range dirs {
						os.MkdirAll(cloudrockerHome+dir, 0755)
						ioutil.WriteFile(cloudrockerHome+dir+"/testfile", []byte("test"), 0644)
					}
					err := rocker.CreateAndCleanAppDirs(config.NewDirectories(cloudrockerHome))
					Expect(err).ShouldNot(HaveOccurred())
					for dir, clean := range dirs {
						dirFile, err := os.Open(cloudrockerHome + dir)
						dirContents, err := dirFile.Readdirnames(0)
						if clean {
							Expect(dirContents, err).ShouldNot(ContainElement("testfile"))
						} else {
							Expect(dirContents, err).Should(ContainElement("testfile"))
						}
					}
					os.RemoveAll(cloudrockerHome)
				})
			})
		})
	})
})

func statusCodeChecker() int {
	res, err := http.Get("http://localhost:8080/")
	if err != nil {
		return 0
	} else {
		return res.StatusCode
	}
}

func cp(src string, dst string) {
	session, err := gexec.Start(
		exec.Command("cp", "-a", src, dst),
		GinkgoWriter,
		GinkgoWriter,
	)
	Ω(err).ShouldNot(HaveOccurred())
	Eventually(session).Should(gexec.Exit(0))
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

type DeployClient interface {
	Doer
	Deploy(deployPayload *DeployPayload) (*DeployResults, error)
	GetApplyResult(deployId string) (string, error)
}

type (
	applicationDeploymentRef struct {
		Environment string `json:"environment"`
		Application string `json:"application"`
	}

	AuroraConfigFieldSource struct {
		Value interface{} `json:"value"`
	}

	DeploymentSpec map[string]AuroraConfigFieldSource

	DeployResults struct {
		Message string
		Success bool
		Results []DeployResult
	}

	DeployResult struct {
		DeployId       string         `json:"deployId"`
		DeploymentSpec DeploymentSpec `json:"deploymentSpec"`
		Success        bool           `json:"success"`
		Ignored        bool           `json:"ignored"`
		Reason         string         `json:"reason"`
	}

	DeployPayload struct {
		ApplicationDeploymentRefs []applicationDeploymentRef `json:"applicationDeploymentRefs"`
		Overrides                 map[string]string          `json:"overrides"`
		Deploy                    bool                       `json:"deploy"`
	}
)

func (spec DeploymentSpec) Get(name string) interface{} {
	field, ok := spec[name]
	if !ok {
		return nil
	}
	return field.Value
}

func (spec DeploymentSpec) GetString(name string) string {
	if value := spec.Get(name); value != nil {
		return value.(string)
	} else {
		return ""
	}
}

func (spec DeploymentSpec) Cluster() string {
	return spec.GetString("cluster")
}

func (spec DeploymentSpec) Environment() string {
	return spec.GetString("envName")
}

func (spec DeploymentSpec) Name() string {
	return spec.GetString("name")
}

func (spec DeploymentSpec) Version() string {
	return spec.GetString("version")
}

func NewAuroraConfigFieldSource(value interface{}) AuroraConfigFieldSource {
	return AuroraConfigFieldSource{
		Value: value,
	}
}

func NewApplicationDeploymentRef(name string) *applicationDeploymentRef {
	slice := strings.Split(name, "/")
	return &applicationDeploymentRef{
		Environment: slice[0],
		Application: slice[1],
	}
}

func NewDeployPayload(applications []string, overrides map[string]string) *DeployPayload {
	applicationDeploymentRefs := createApplicationDeploymentRefs(applications)
	return &DeployPayload{
		ApplicationDeploymentRefs: applicationDeploymentRefs,
		Overrides:                 overrides,
		Deploy:                    true,
	}
}

func (api *ApiClient) Deploy(deployPayload *DeployPayload) (*DeployResults, error) {

	payload, err := json.Marshal(deployPayload)
	if err != nil {
		return nil, errors.New("failed to marshal DeployPayload")

	}

	endpoint := fmt.Sprintf("/apply/%s", api.Affiliation)
	response, err := api.Do(http.MethodPut, endpoint, payload)
	if err != nil {
		return nil, err
	}

	var deploys DeployResults
	err = json.Unmarshal(response.Items, &deploys.Results)
	if err != nil {
		return nil, err
	}

	if !response.Success {
		for _, deploy := range deploys.Results {
			// Hack-ish solution since validation errors and deploy errors have
			// different payload. TODO: Fix error response from Boober.
			if deploy.DeploymentSpec.Name() == "" {
				return nil, response.Error()
			}
		}
	}

	deploys.Message = response.Message
	if response.Success {
		deploys.Success = true
	}

	return &deploys, nil
}

func createApplicationDeploymentRefs(apps []string) []applicationDeploymentRef {
	var applicationDeploymentRefs []applicationDeploymentRef
	for _, app := range apps {
		applicationDeploymentRefs = append(applicationDeploymentRefs, *NewApplicationDeploymentRef(app))
	}
	return applicationDeploymentRefs
}
<commit_msg>SITJ-519<commit_after>package client

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
)

type DeployClient interface {
	Doer
	Deploy(deployPayload *DeployPayload) (*DeployResults, error)
	GetApplyResult(deployId string) (string, error)
}

type (
	applicationDeploymentRef struct {
		Environment string `json:"environment"`
		Application string `json:"application"`
	}

	AuroraConfigFieldSource struct {
		Value interface{} `json:"value"`
	}

	DeploymentSpec map[string]AuroraConfigFieldSource

	DeployResults struct {
		Message string
		Success bool
		Results []DeployResult
	}

	DeployResult struct {
		DeployId       string         `json:"deployId"`
		DeploymentSpec DeploymentSpec `json:"deploymentSpec"`
		Success        bool           `json:"success"`
		Ignored        bool           `json:"ignored"`
		Reason         string         `json:"reason"`
	}

	DeployPayload struct {
		ApplicationDeploymentRefs []applicationDeploymentRef `json:"applicationDeploymentRefs"`
		Overrides                 map[string]string          `json:"overrides"`
		Deploy                    bool                       `json:"deploy"`
	}
)

func (spec DeploymentSpec) Get(name string) interface{} {
	field, ok := spec[name]
	if !ok {
		return nil
	}
	return field.Value
}

func (spec DeploymentSpec) GetString(name string) string {

	if value := spec.Get(name); value != nil {
		return fmt.Sprintf("%v", value)
	} else {
		return ""
	}
}

func (spec DeploymentSpec) Cluster() string {
	return spec.GetString("cluster")
}

func (spec DeploymentSpec) Environment() string {
	return spec.GetString("envName")
}

func (spec DeploymentSpec) Name() string {
	return spec.GetString("name")
}

func (spec DeploymentSpec) Version() string {
	return spec.GetString("version")
}

func NewAuroraConfigFieldSource(value interface{}) AuroraConfigFieldSource {
	return AuroraConfigFieldSource{
		Value: value,
	}
}

func NewApplicationDeploymentRef(name string) *applicationDeploymentRef {
	slice := strings.Split(name, "/")
	return &applicationDeploymentRef{
		Environment: slice[0],
		Application: slice[1],
	}
}

func NewDeployPayload(applications []string, overrides map[string]string) *DeployPayload {
	applicationDeploymentRefs := createApplicationDeploymentRefs(applications)
	return &DeployPayload{
		ApplicationDeploymentRefs: applicationDeploymentRefs,
		Overrides:                 overrides,
		Deploy:                    true,
	}
}

func (api *ApiClient) Deploy(deployPayload *DeployPayload) (*DeployResults, error) {

	payload, err := json.Marshal(deployPayload)
	if err != nil {
		return nil, errors.New("failed to marshal DeployPayload")

	}

	endpoint := fmt.Sprintf("/apply/%s", api.Affiliation)
	response, err := api.Do(http.MethodPut, endpoint, payload)
	if err != nil {
		return nil, err
	}

	var deploys DeployResults
	err = json.Unmarshal(response.Items, &deploys.Results)
	if err != nil {
		return nil, err
	}

	if !response.Success {
		for _, deploy := range deploys.Results {
			// Hack-ish solution since validation errors and deploy errors have
			// different payload. TODO: Fix error response from Boober.
			if deploy.DeploymentSpec.Name() == "" {
				return nil, response.Error()
			}
		}
	}

	deploys.Message = response.Message
	if response.Success {
		deploys.Success = true
	}

	return &deploys, nil
}

func createApplicationDeploymentRefs(apps []string) []applicationDeploymentRef {
	var applicationDeploymentRefs []applicationDeploymentRef
	for _, app := range apps {
		applicationDeploymentRefs = append(applicationDeploymentRefs, *NewApplicationDeploymentRef(app))
	}
	return applicationDeploymentRefs
}
<|endoftext|>


<commit_before>//go:build integ
// +build integ

//  Copyright Istio 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 eccsignaturealgorithm

import (
	"crypto/x509"
	"encoding/pem"
	"strings"
	"testing"

	"istio.io/istio/pkg/test/echo/common/scheme"
	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/echo"
	"istio.io/istio/pkg/test/util/tmpl"
	"istio.io/istio/tests/integration/security/util"
	"istio.io/istio/tests/integration/security/util/cert"
)

const (
	DestinationRuleConfigIstioMutual = `
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: server
  namespace: {{.AppNamespace}}
spec:
  host: "server.{{.AppNamespace}}.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
`

	PeerAuthenticationConfig = `
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: {{.AppNamespace}}
spec:
  mtls:
    mode: STRICT
`
)

func TestStrictMTLS(t *testing.T) {
	framework.
		NewTest(t).
		Features("security.peer.ecc-signature-algorithm").
		Run(func(t framework.TestContext) {
			peerTemplate := tmpl.EvaluateOrFail(t, PeerAuthenticationConfig, map[string]string{"AppNamespace": apps.Namespace.Name()})
			t.ConfigIstio().ApplyYAMLOrFail(t, apps.Namespace.Name(), peerTemplate)
			util.WaitForConfig(t, apps.Namespace, peerTemplate)

			drTemplate := tmpl.EvaluateOrFail(t, DestinationRuleConfigIstioMutual, map[string]string{"AppNamespace": apps.Namespace.Name()})
			t.ConfigIstio().ApplyYAMLOrFail(t, apps.Namespace.Name(), drTemplate)
			util.WaitForConfig(t, apps.Namespace, drTemplate)

			response := apps.Client.CallOrFail(t, echo.CallOptions{
				Target:   apps.Server,
				PortName: "http",
				Scheme:   scheme.HTTP,
				Count:    1,
			})

			if err := response.CheckOK(); err != nil {
				t.Fatalf("client could not reach server: %v", err)
			}

			certPEMs := cert.DumpCertFromSidecar(t, apps.Client, apps.Server, "http")
			block, _ := pem.Decode([]byte(strings.Join(certPEMs, "\n")))
			if block == nil { // nolint: staticcheck
				t.Fatalf("failed to parse certificate PEM")
			}

			certificate, err := x509.ParseCertificate(block.Bytes) // nolint: staticcheck
			if err != nil {
				t.Fatalf("failed to parse certificate: %v", err)
			}

			if certificate.PublicKeyAlgorithm != x509.ECDSA {
				t.Fatalf("public key used in server cert is not ECDSA: %v", certificate.PublicKeyAlgorithm)
			}
		})
}
<commit_msg>Add more robust calls to ecc_signature test (#37171)<commit_after>//go:build integ
// +build integ

//  Copyright Istio 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 eccsignaturealgorithm

import (
	"crypto/x509"
	"encoding/pem"
	"strings"
	"testing"

	"istio.io/istio/pkg/test/echo/common/scheme"
	"istio.io/istio/pkg/test/framework"
	"istio.io/istio/pkg/test/framework/components/echo"
	"istio.io/istio/pkg/test/util/tmpl"
	"istio.io/istio/tests/integration/security/util"
	"istio.io/istio/tests/integration/security/util/cert"
)

const (
	DestinationRuleConfigIstioMutual = `
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: server
  namespace: {{.AppNamespace}}
spec:
  host: "server.{{.AppNamespace}}.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
`

	PeerAuthenticationConfig = `
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: {{.AppNamespace}}
spec:
  mtls:
    mode: STRICT
`
)

func TestStrictMTLS(t *testing.T) {
	framework.
		NewTest(t).
		Features("security.peer.ecc-signature-algorithm").
		Run(func(t framework.TestContext) {
			peerTemplate := tmpl.EvaluateOrFail(t, PeerAuthenticationConfig, map[string]string{"AppNamespace": apps.Namespace.Name()})
			t.ConfigIstio().ApplyYAMLOrFail(t, apps.Namespace.Name(), peerTemplate)
			util.WaitForConfig(t, apps.Namespace, peerTemplate)

			drTemplate := tmpl.EvaluateOrFail(t, DestinationRuleConfigIstioMutual, map[string]string{"AppNamespace": apps.Namespace.Name()})
			t.ConfigIstio().ApplyYAMLOrFail(t, apps.Namespace.Name(), drTemplate)
			util.WaitForConfig(t, apps.Namespace, drTemplate)

			apps.Client.CallWithRetryOrFail(t, echo.CallOptions{
				Target:    apps.Server,
				PortName:  "http",
				Scheme:    scheme.HTTP,
				Count:     1,
				Validator: echo.ExpectOK(),
			})

			certPEMs := cert.DumpCertFromSidecar(t, apps.Client, apps.Server, "http")
			block, _ := pem.Decode([]byte(strings.Join(certPEMs, "\n")))
			if block == nil { // nolint: staticcheck
				t.Fatalf("failed to parse certificate PEM")
			}

			certificate, err := x509.ParseCertificate(block.Bytes) // nolint: staticcheck
			if err != nil {
				t.Fatalf("failed to parse certificate: %v", err)
			}

			if certificate.PublicKeyAlgorithm != x509.ECDSA {
				t.Fatalf("public key used in server cert is not ECDSA: %v", certificate.PublicKeyAlgorithm)
			}
		})
}
<|endoftext|>


<commit_before>// Dummy no-op pluggable transport client. Works only as a managed proxy.
//
// Usage (in torrc):
// 	UseBridges 1
// 	Bridge dummy X.X.X.X:YYYY
// 	ClientTransportPlugin dummy exec dummy-client
//
// Because this transport doesn't do anything to the traffic, you can use any
// ordinary relay's ORPort in the Bridge line; it doesn't have to declare
// support for the dummy transport.
package main

import (
	"io"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

var ptInfo pt.ClientInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func copyLoop(a, b net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(b, a)
		wg.Done()
	}()
	go func() {
		io.Copy(a, b)
		wg.Done()
	}()

	wg.Wait()
}

func handler(conn *pt.SocksConn) error {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	defer conn.Close()
	remote, err := net.Dial("tcp", conn.Req.Target)
	if err != nil {
		conn.Reject()
		return err
	}
	defer remote.Close()
	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
	if err != nil {
		return err
	}

	copyLoop(conn, remote)

	return nil
}

func acceptLoop(ln *pt.SocksListener) error {
	defer ln.Close()
	for {
		conn, err := ln.AcceptSocks()
		if err != nil {
			if e, ok := err.(net.Error); ok && !e.Temporary() {
				return err
			}
			continue
		}
		go handler(conn)
	}
}

func main() {
	var err error

	ptInfo, err = pt.ClientSetup([]string{"dummy"})
	if err != nil {
		os.Exit(1)
	}

	listeners := make([]net.Listener, 0)
	for _, methodName := range ptInfo.MethodNames {
		switch methodName {
		case "dummy":
			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
			if err != nil {
				pt.CmethodError(methodName, err.Error())
				break
			}
			go acceptLoop(ln)
			pt.Cmethod(methodName, ln.Version(), ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.CmethodError(methodName, "no such method")
		}
	}
	pt.CmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
}
<commit_msg>Actually relays need DirPort if you treat them as a bridge.<commit_after>// Dummy no-op pluggable transport client. Works only as a managed proxy.
//
// Usage (in torrc):
// 	UseBridges 1
// 	Bridge dummy X.X.X.X:YYYY
// 	ClientTransportPlugin dummy exec dummy-client
//
// Because this transport doesn't do anything to the traffic, you can use the
// ORPort of any ordinary bridge (or relay that has DirPort set) in the bridge
// line; it doesn't have to declare support for the dummy transport.
package main

import (
	"io"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

var ptInfo pt.ClientInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func copyLoop(a, b net.Conn) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		io.Copy(b, a)
		wg.Done()
	}()
	go func() {
		io.Copy(a, b)
		wg.Done()
	}()

	wg.Wait()
}

func handler(conn *pt.SocksConn) error {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	defer conn.Close()
	remote, err := net.Dial("tcp", conn.Req.Target)
	if err != nil {
		conn.Reject()
		return err
	}
	defer remote.Close()
	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
	if err != nil {
		return err
	}

	copyLoop(conn, remote)

	return nil
}

func acceptLoop(ln *pt.SocksListener) error {
	defer ln.Close()
	for {
		conn, err := ln.AcceptSocks()
		if err != nil {
			if e, ok := err.(net.Error); ok && !e.Temporary() {
				return err
			}
			continue
		}
		go handler(conn)
	}
}

func main() {
	var err error

	ptInfo, err = pt.ClientSetup([]string{"dummy"})
	if err != nil {
		os.Exit(1)
	}

	listeners := make([]net.Listener, 0)
	for _, methodName := range ptInfo.MethodNames {
		switch methodName {
		case "dummy":
			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
			if err != nil {
				pt.CmethodError(methodName, err.Error())
				break
			}
			go acceptLoop(ln)
			pt.Cmethod(methodName, ln.Version(), ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.CmethodError(methodName, "no such method")
		}
	}
	pt.CmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
}
<|endoftext|>


<commit_before>package cluster

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"time"

	"github.com/canonical/go-dqlite/client"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/pkg/errors"
)

// NotifyUpgradeCompleted sends a notification to all other nodes in the
// cluster that any possible pending database update has been applied, and any
// nodes which was waiting for this node to be upgraded should re-check if it's
// okay to move forward.
func NotifyUpgradeCompleted(state *state.State, networkCert *shared.CertInfo, serverCert *shared.CertInfo) error {
	notifier, err := NewNotifier(state, networkCert, serverCert, NotifyTryAll)
	if err != nil {
		return err
	}

	return notifier(func(client lxd.InstanceServer) error {
		info, err := client.GetConnectionInfo()
		if err != nil {
			return errors.Wrap(err, "failed to get connection info")
		}

		url := fmt.Sprintf("%s%s", info.Addresses[0], databaseEndpoint)
		request, err := http.NewRequest("PATCH", url, nil)
		if err != nil {
			return errors.Wrap(err, "failed to create database notify upgrade request")
		}
		setDqliteVersionHeader(request)

		httpClient, err := client.GetHTTPClient()
		if err != nil {
			return errors.Wrap(err, "failed to get HTTP client")
		}

		httpClient.Timeout = 5 * time.Second
		response, err := httpClient.Do(request)
		if err != nil {
			return errors.Wrap(err, "failed to notify node about completed upgrade")
		}

		if response.StatusCode != http.StatusOK {
			return fmt.Errorf("database upgrade notification failed: %s", response.Status)
		}

		return nil
	})
}

// MaybeUpdate Check this node's version and possibly run LXD_CLUSTER_UPDATE.
func MaybeUpdate(state *state.State) error {
	shouldUpdate := false

	enabled, err := Enabled(state.Node)
	if err != nil {
		return errors.Wrap(err, "Failed to check clustering is enabled")
	}
	if !enabled {
		return nil
	}

	if state.Cluster == nil {
		return fmt.Errorf("Failed checking cluster update, state not initialised yet")
	}

	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		outdated, err := tx.NodeIsOutdated()
		if err != nil {
			return err
		}
		shouldUpdate = outdated
		return nil
	})

	if err != nil {
		// Just log the error and return.
		return errors.Wrap(err, "Failed to check if this node is out-of-date")
	}

	if !shouldUpdate {
		logger.Debugf("Cluster node is up-to-date")
		return nil
	}

	return triggerUpdate()
}

func triggerUpdate() error {
	logger.Infof("Node is out-of-date with respect to other cluster nodes")

	updateExecutable := os.Getenv("LXD_CLUSTER_UPDATE")
	if updateExecutable == "" {
		logger.Debug("No LXD_CLUSTER_UPDATE variable set, skipping auto-update")
		return nil
	}

	// Wait a random amout of seconds (up to 30) in order to avoid
	// restarting all cluster members at the same time, and make the
	// upgrade more graceful.
	wait := time.Duration(rand.Intn(30)) * time.Second
	logger.Infof("Triggering cluster update in %s using: %s", wait, updateExecutable)
	time.Sleep(wait)

	_, err := shared.RunCommand(updateExecutable)
	if err != nil {
		logger.Errorf("Cluster upgrade failed: '%v'", err.Error())
		return err
	}
	return nil
}

// UpgradeMembersWithoutRole assigns the Spare raft role to all cluster members
// that are not currently part of the raft configuration. It's used for
// upgrading a cluster from a version without roles support.
func UpgradeMembersWithoutRole(gateway *Gateway, members []db.NodeInfo) error {
	nodes, err := gateway.currentRaftNodes()
	if err == ErrNotLeader {
		return nil
	}
	if err != nil {
		return errors.Wrap(err, "Failed to get current raft nodes")
	}

	// Used raft IDs.
	ids := map[uint64]bool{}
	for _, node := range nodes {
		ids[node.ID] = true
	}

	dqliteClient, err := gateway.getClient()
	if err != nil {
		return errors.Wrap(err, "Failed to connect to local dqlite node")
	}
	defer dqliteClient.Close()

	// Check that each member is present in the raft configuration, and add
	// it if not.
	for _, member := range members {
		found := false
		for _, node := range nodes {
			if member.ID == 1 && node.ID == 1 || member.Address == node.Address {
				found = true
				break
			}
		}
		if found {
			continue
		}

		// Try to use the same ID as the node, but it might not be
		// possible if it's use.
		id := uint64(member.ID)
		if _, ok := ids[id]; ok {
			for _, other := range members {
				if _, ok := ids[uint64(other.ID)]; !ok {
					id = uint64(other.ID)
					break
				}
			}

			// This can't really happen since there are always at least as many members as there are
			// nodes, and all of them have different IDs.
			if id == uint64(member.ID) {
				return fmt.Errorf("No available raft ID for cluster member ID %d", member.ID)
			}
		}
		ids[id] = true

		info := db.RaftNode{
			NodeInfo: client.NodeInfo{
				ID:      id,
				Address: member.Address,
				Role:    db.RaftSpare,
			},
			Name: "",
		}

		logger.Info("Add spare dqlite node", log15.Ctx{"id": info.ID, "address": info.Address})

		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		err = dqliteClient.Add(ctx, info.NodeInfo)
		if err != nil {
			return errors.Wrap(err, "Failed to add dqlite node")
		}

	}

	return nil
}
<commit_msg>lxd/cluster/upgrade: Comment clean up in UpgradeMembersWithoutRole<commit_after>package cluster

import (
	"context"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"time"

	"github.com/canonical/go-dqlite/client"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/pkg/errors"
)

// NotifyUpgradeCompleted sends a notification to all other nodes in the
// cluster that any possible pending database update has been applied, and any
// nodes which was waiting for this node to be upgraded should re-check if it's
// okay to move forward.
func NotifyUpgradeCompleted(state *state.State, networkCert *shared.CertInfo, serverCert *shared.CertInfo) error {
	notifier, err := NewNotifier(state, networkCert, serverCert, NotifyTryAll)
	if err != nil {
		return err
	}

	return notifier(func(client lxd.InstanceServer) error {
		info, err := client.GetConnectionInfo()
		if err != nil {
			return errors.Wrap(err, "failed to get connection info")
		}

		url := fmt.Sprintf("%s%s", info.Addresses[0], databaseEndpoint)
		request, err := http.NewRequest("PATCH", url, nil)
		if err != nil {
			return errors.Wrap(err, "failed to create database notify upgrade request")
		}
		setDqliteVersionHeader(request)

		httpClient, err := client.GetHTTPClient()
		if err != nil {
			return errors.Wrap(err, "failed to get HTTP client")
		}

		httpClient.Timeout = 5 * time.Second
		response, err := httpClient.Do(request)
		if err != nil {
			return errors.Wrap(err, "failed to notify node about completed upgrade")
		}

		if response.StatusCode != http.StatusOK {
			return fmt.Errorf("database upgrade notification failed: %s", response.Status)
		}

		return nil
	})
}

// MaybeUpdate Check this node's version and possibly run LXD_CLUSTER_UPDATE.
func MaybeUpdate(state *state.State) error {
	shouldUpdate := false

	enabled, err := Enabled(state.Node)
	if err != nil {
		return errors.Wrap(err, "Failed to check clustering is enabled")
	}
	if !enabled {
		return nil
	}

	if state.Cluster == nil {
		return fmt.Errorf("Failed checking cluster update, state not initialised yet")
	}

	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		outdated, err := tx.NodeIsOutdated()
		if err != nil {
			return err
		}
		shouldUpdate = outdated
		return nil
	})

	if err != nil {
		// Just log the error and return.
		return errors.Wrap(err, "Failed to check if this node is out-of-date")
	}

	if !shouldUpdate {
		logger.Debugf("Cluster node is up-to-date")
		return nil
	}

	return triggerUpdate()
}

func triggerUpdate() error {
	logger.Infof("Node is out-of-date with respect to other cluster nodes")

	updateExecutable := os.Getenv("LXD_CLUSTER_UPDATE")
	if updateExecutable == "" {
		logger.Debug("No LXD_CLUSTER_UPDATE variable set, skipping auto-update")
		return nil
	}

	// Wait a random amout of seconds (up to 30) in order to avoid
	// restarting all cluster members at the same time, and make the
	// upgrade more graceful.
	wait := time.Duration(rand.Intn(30)) * time.Second
	logger.Infof("Triggering cluster update in %s using: %s", wait, updateExecutable)
	time.Sleep(wait)

	_, err := shared.RunCommand(updateExecutable)
	if err != nil {
		logger.Errorf("Cluster upgrade failed: '%v'", err.Error())
		return err
	}
	return nil
}

// UpgradeMembersWithoutRole assigns the Spare raft role to all cluster members that are not currently part of the
// raft configuration. It's used for upgrading a cluster from a version without roles support.
func UpgradeMembersWithoutRole(gateway *Gateway, members []db.NodeInfo) error {
	nodes, err := gateway.currentRaftNodes()
	if err == ErrNotLeader {
		return nil
	}
	if err != nil {
		return errors.Wrap(err, "Failed to get current raft nodes")
	}

	// Used raft IDs.
	ids := map[uint64]bool{}
	for _, node := range nodes {
		ids[node.ID] = true
	}

	dqliteClient, err := gateway.getClient()
	if err != nil {
		return errors.Wrap(err, "Failed to connect to local dqlite node")
	}
	defer dqliteClient.Close()

	// Check that each member is present in the raft configuration, and add it if not.
	for _, member := range members {
		found := false
		for _, node := range nodes {
			if member.ID == 1 && node.ID == 1 || member.Address == node.Address {
				found = true
				break
			}
		}
		if found {
			continue
		}

		// Try to use the same ID as the node, but it might not be possible if it's use.
		id := uint64(member.ID)
		if _, ok := ids[id]; ok {
			for _, other := range members {
				if _, ok := ids[uint64(other.ID)]; !ok {
					id = uint64(other.ID)
					break
				}
			}

			// This can't really happen (but has in the past) since there are always at least as many
			// members as there are nodes, and all of them have different IDs.
			if id == uint64(member.ID) {
				return fmt.Errorf("No available raft ID for cluster member ID %d", member.ID)
			}
		}
		ids[id] = true

		info := db.RaftNode{
			NodeInfo: client.NodeInfo{
				ID:      id,
				Address: member.Address,
				Role:    db.RaftSpare,
			},
			Name: "",
		}

		logger.Info("Add spare dqlite node", log15.Ctx{"id": info.ID, "address": info.Address})

		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		err = dqliteClient.Add(ctx, info.NodeInfo)
		if err != nil {
			return errors.Wrap(err, "Failed to add dqlite node")
		}

	}

	return nil
}
<|endoftext|>


<commit_before>package golibxml

import "testing"
import "io/ioutil"

const TEST_HTML_FILE = "test/test.html"
const TEST_HTML_EXPECTED = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n\n"

func TestParseHTMLDoc(t *testing.T) {
	doc := ParseHTMLDoc("", "UTF-8")
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestParseHTMLDocLeak(t *testing.T) {
	for i := 0; i < 100000; i++ {
		TestParseHTMLDoc(t)
	}
	if getRSS() > 4000 {
		t.Fatal("Memory leak")
	}
}

func TestParseHTMLFile(t *testing.T) {
	doc := ParseHTMLFile(TEST_HTML_FILE, "UTF-8")
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestParseHTMLFileLeak(t *testing.T) {
	for i := 0; i < 100000; i++ {
		TestParseHTMLFile(t)
	}
	if getRSS() > 4000 {
		t.Fatal("Memory leak")
	}
}

func TestReadHTMLDoc(t *testing.T) {
	doc := ReadHTMLDoc("", "", "", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestReadHTMLFile(t *testing.T) {
	doc := ReadHTMLFile(TEST_HTML_FILE, "UTF-8", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestReadHTMLMemory(t *testing.T) {
	buf, err := ioutil.ReadFile(TEST_HTML_FILE)
	if err != nil {
		t.Fatal(err)
	}
	doc := ReadHTMLMemory(buf, "/", "UTF-8", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

<commit_msg>Disable these tests since they take a while<commit_after>package golibxml

import "testing"
import "io/ioutil"

const TEST_HTML_FILE = "test/test.html"
const TEST_HTML_EXPECTED = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n\n"

func TestParseHTMLDoc(t *testing.T) {
	doc := ParseHTMLDoc("", "UTF-8")
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}
/*
func TestParseHTMLDocLeak(t *testing.T) {
	for i := 0; i < 100000; i++ {
		TestParseHTMLDoc(t)
	}
	if getRSS() > 4000 {
		t.Fatal("Memory leak")
	}
}
*/
func TestParseHTMLFile(t *testing.T) {
	doc := ParseHTMLFile(TEST_HTML_FILE, "UTF-8")
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}
/*
func TestParseHTMLFileLeak(t *testing.T) {
	for i := 0; i < 100000; i++ {
		TestParseHTMLFile(t)
	}
	if getRSS() > 4000 {
		t.Fatal("Memory leak")
	}
}
*/
func TestReadHTMLDoc(t *testing.T) {
	doc := ReadHTMLDoc("", "", "", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestReadHTMLFile(t *testing.T) {
	doc := ReadHTMLFile(TEST_HTML_FILE, "UTF-8", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

func TestReadHTMLMemory(t *testing.T) {
	buf, err := ioutil.ReadFile(TEST_HTML_FILE)
	if err != nil {
		t.Fatal(err)
	}
	doc := ReadHTMLMemory(buf, "/", "UTF-8", 0)
	if doc == nil {
		t.Fail()
	}
	defer doc.Free()
	compareResult(t, doc.String(), TEST_HTML_EXPECTED)
}

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

var FIELD = regexp.MustCompile(`(?:[^,\\]|\\.)*`)

type Selector interface {
	DropHeaders() bool
	ParseHeaders(headers []string) error
	Select(recode []string) ([]string, error)
}

type All struct {
}

func NewAll() *All {
	return &All{}
}

func (a *All) DropHeaders() bool {
	return false
}

func (a *All) ParseHeaders(headers []string) error {
	return nil
}

func (a *All) Select(recode []string) ([]string, error) {
	return recode, nil
}

type Indexes struct {
	indexes []int
}

func NewIndexes(list string) (*Indexes, error) {
	fields := FIELD.FindAllString(list, -1)

	indexes := make([]int, len(fields))
	for i, field := range fields {
		index, err := strconv.Atoi(field)
		if err != nil {
			return nil, err
		}
		if index < 1 {
			return nil, fmt.Errorf("indexes are numberd from 1")
		}
		indexes[i] = index - 1
	}
	return &Indexes{
		indexes: indexes,
	}, nil
}

func (i *Indexes) DropHeaders() bool {
	return false
}

func (i *Indexes) ParseHeaders(headers []string) error {
	return nil
}

func (i *Indexes) Select(recode []string) ([]string, error) {
	a := make([]string, len(i.indexes))
	for j, index := range i.indexes {
		if index >= 0 && index < len(recode) {
			a[j] = recode[index]
		}
	}
	return a, nil
}

type Headers struct {
	indexes []int
	headers []string
}

func NewHeaders(list string) (*Headers, error) {
	headers := FIELD.FindAllString(list, -1)
	for i := 0; i < len(headers); i++ {
		headers[i] = strings.Replace(headers[i], `\,`, `,`, -1)
	}
	return &Headers{
		headers: headers,
	}, nil
}

func (h *Headers) DropHeaders() bool {
	return true
}

func (h *Headers) ParseHeaders(headers []string) error {
	indexMap := make(map[string]int)
	for i, header := range headers {
		if _, ok := indexMap[header]; ok {
			return fmt.Errorf("%q: duplicated header", header)
		}
		indexMap[header] = i
	}

	h.indexes = make([]int, len(h.headers))
	for i, header := range h.headers {
		if index, ok := indexMap[header]; ok {
			h.indexes[i] = index
		} else {
			h.indexes[i] = -1
		}
	}
	return nil
}

func (h *Headers) Select(recode []string) ([]string, error) {
	a := make([]string, len(h.indexes))
	for i, index := range h.indexes {
		if index != -1 {
			a[i] = recode[index]
		}
	}
	return a, nil
}
<commit_msg>Allow empty index<commit_after>package main

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

var FIELD = regexp.MustCompile(`(?:[^,\\]|\\.)*`)

type Selector interface {
	DropHeaders() bool
	ParseHeaders(headers []string) error
	Select(recode []string) ([]string, error)
}

type All struct {
}

func NewAll() *All {
	return &All{}
}

func (a *All) DropHeaders() bool {
	return false
}

func (a *All) ParseHeaders(headers []string) error {
	return nil
}

func (a *All) Select(recode []string) ([]string, error) {
	return recode, nil
}

type Indexes struct {
	indexes []int
}

func NewIndexes(list string) (*Indexes, error) {
	if list == "" {
		return &Indexes{
			indexes: []int{},
		}, nil
	}
	fields := FIELD.FindAllString(list, -1)

	indexes := make([]int, len(fields))
	for i, field := range fields {
		index, err := strconv.Atoi(field)
		if err != nil {
			return nil, err
		}
		if index < 1 {
			return nil, fmt.Errorf("indexes are numberd from 1")
		}
		indexes[i] = index - 1
	}
	return &Indexes{
		indexes: indexes,
	}, nil
}

func (i *Indexes) DropHeaders() bool {
	return false
}

func (i *Indexes) ParseHeaders(headers []string) error {
	return nil
}

func (i *Indexes) Select(recode []string) ([]string, error) {
	a := make([]string, len(i.indexes))
	for j, index := range i.indexes {
		if index >= 0 && index < len(recode) {
			a[j] = recode[index]
		}
	}
	return a, nil
}

type Headers struct {
	indexes []int
	headers []string
}

func NewHeaders(list string) (*Headers, error) {
	headers := FIELD.FindAllString(list, -1)
	for i := 0; i < len(headers); i++ {
		headers[i] = strings.Replace(headers[i], `\,`, `,`, -1)
	}
	return &Headers{
		headers: headers,
	}, nil
}

func (h *Headers) DropHeaders() bool {
	return true
}

func (h *Headers) ParseHeaders(headers []string) error {
	indexMap := make(map[string]int)
	for i, header := range headers {
		if _, ok := indexMap[header]; ok {
			return fmt.Errorf("%q: duplicated header", header)
		}
		indexMap[header] = i
	}

	h.indexes = make([]int, len(h.headers))
	for i, header := range h.headers {
		if index, ok := indexMap[header]; ok {
			h.indexes[i] = index
		} else {
			h.indexes[i] = -1
		}
	}
	return nil
}

func (h *Headers) Select(recode []string) ([]string, error) {
	a := make([]string, len(h.indexes))
	for i, index := range h.indexes {
		if index != -1 {
			a[i] = recode[index]
		}
	}
	return a, nil
}
<|endoftext|>


<commit_before>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua ./lua/3rdparty ./lua/libs

import (
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/bradfitz/http2"
	"github.com/carbonsrv/carbon/modules/luaconf"
	"github.com/carbonsrv/carbon/modules/middleware"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"github.com/vifino/contrib/gzip"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv http.Handler, en_http bool, en_https bool, en_http2 bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind, en_http2)
	}
	if en_https {
		go serveHTTPS(srv, binds, en_http2, cert, key)
	}
	<-end
}
func serveHTTP(srv http.Handler, bind string, en_http2 bool) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv http.Handler, bind string, en_http2 bool, cert string, key string) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServeTLS(cert, key)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache
	kvstore = cache.New(-1, -1)                    // Key-Value Storage

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 8443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")
	var en_http2 = flag.Bool("http2", false, "Enable HTTP/2")

	wrkrs := 2
	if runtime.NumCPU() > 2 {
		wrkrs = runtime.NumCPU()
	}
	jobs = flag.Int("states", wrkrs, "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", wrkrs, "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()                   // Run the scheduler.
	go middleware.Preloader()            // Run the Preloader.
	middleware.Init(*jobs, cfe, kvstore) // Run init sequence.
	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	}
}
<commit_msg>One day, one day my code will work without fixing it afterwards.<commit_after>package main

//go:generate go-bindata -o modules/glue/generated_lua.go -pkg=glue -prefix "./lua" ./lua ./lua/3rdparty ./lua/libs

import (
	"bufio"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/bradfitz/http2"
	"github.com/carbonsrv/carbon/modules/luaconf"
	"github.com/carbonsrv/carbon/modules/middleware"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/gin-gonic/gin"
	"github.com/namsral/flag"
	"github.com/pmylund/go-cache"
	"github.com/vifino/contrib/gzip"
	"log"
	"net/http"
	"path/filepath"
	"runtime"
	"strconv"
	"time"
)

// General
var jobs *int

// Cache
var cfe *cache.Cache
var kvstore *cache.Cache

func cacheRead(c *cache.Cache, file string) (string, error) {
	res := ""
	data_tmp, found := c.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err
		}
		c.Set(file, data, cache.DefaultExpiration)
	} else {
		debug("Using cache for %s" + file)
		res = data_tmp.(string)
	}
	return res, nil
}

// File sytem functions
var filesystem http.FileSystem

func initPhysFS(path string) http.FileSystem {
	err := physfs.Init()
	if err != nil {
		panic(err)
	}
	err = physfs.Mount(path, "/", true)
	if err != nil {
		panic(err)
	}
	return physfs.FileSystem()
}

/*func fileExists(file string) bool {
	if _, err := os.Stat(file); os.IsNotExist(err) {
		return false
	}
	return true
}*/
func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}
func fileExists(file string) bool {
	return physfs.Exists(file)
}
func cacheFileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := fileExists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

// Logging
var doLog bool = false

func debug(str string) {
	if doLog {
		log.Print(str)
	}
}

// Server
func new_server() *gin.Engine {
	return gin.New()
}
func bootstrap(srv *gin.Engine, dir string, cfe *cache.Cache) {
	switcher := middleware.ExtRoute(middleware.Plan{
		".lua": middleware.Lua(),
		"***":  staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe),
	})
	/*srv.GET(`/:file`, switcher)
	srv.POST(`/:file`, switcher)*/
	srv.Use(switcher)
	/*st := staticServe.ServeCached("", staticServe.PhysFS("", true, true), cfe)
	lr := luaroute(dir)
	srv.Use(lr)
	srv.Use(st)*/
}

func serve(srv http.Handler, en_http bool, en_https bool, en_http2 bool, bind string, binds string, cert string, key string) {
	end := make(chan bool)
	if en_http {
		go serveHTTP(srv, bind, en_http2)
	}
	if en_https {
		go serveHTTPS(srv, binds, en_http2, cert, key)
	}
	<-end
}
func serveHTTP(srv http.Handler, bind string, en_http2 bool) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
func serveHTTPS(srv http.Handler, bind string, en_http2 bool, cert string, key string) {
	s := &http.Server{
		Addr:           bind,
		Handler:        srv,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if en_http2 {
		http2.ConfigureServer(s, nil)
	}
	err := s.ListenAndServeTLS(cert, key)
	if err != nil {
		panic(err)
	}
}

func main() {
	cfe = cache.New(5*time.Minute, 30*time.Second) // File-Exists Cache
	kvstore = cache.New(-1, -1)                    // Key-Value Storage

	// Use config
	flag.String("config", "", "Parse Config File")
	var script = flag.String("script", "", "Parse Lua Script as initialization")

	var host = flag.String("host", "", "IP of Host to bind the Webserver on")
	var port = flag.Int("port", 8080, "Port to run Webserver on (HTTP)")
	var ports = flag.Int("ports", 8443, "Port to run Webserver on (HTTPS)")
	var cert = flag.String("cert", "", "Certificate File for HTTPS")
	var key = flag.String("key", "", "Key File for HTTPS")
	var en_http = flag.Bool("http", true, "Listen HTTP")
	var en_https = flag.Bool("https", false, "Listen HTTPS")
	var en_http2 = flag.Bool("http2", false, "Enable HTTP/2")

	wrkrs := 2
	if runtime.NumCPU() > 2 {
		wrkrs = runtime.NumCPU()
	}
	jobs = flag.Int("states", wrkrs, "Number of Preinitialized Lua States")
	var workers = flag.Int("workers", wrkrs, "Number of Worker threads.")
	var webroot = flag.String("root", ".", "Path to Web Root")

	// Middleware options
	useRecovery := flag.Bool("recovery", false, "Recover from Panics")
	useLogger := flag.Bool("logger", true, "Log Requests and Cache information")
	useGzip := flag.Bool("gzip", false, "Use GZIP")

	flag.Parse()

	if *en_https {
		if *key == "" || *cert == "" {
			panic("Need to have a Key and a Cert defined.")
		}
	}

	runtime.GOMAXPROCS(*workers)

	root, _ := filepath.Abs(*webroot)
	filesystem = initPhysFS(root)
	defer physfs.Deinit()
	go scheduler.Run()                   // Run the scheduler.
	go middleware.Preloader()            // Run the Preloader.
	middleware.Init(*jobs, cfe, kvstore) // Run init sequence.
	if *script == "" {
		srv := new_server()
		if *useLogger {
			doLog = true
			srv.Use(gin.Logger())
		}
		if *useRecovery {
			srv.Use(gin.Recovery())
		}
		if *useGzip {
			srv.Use(gzip.Gzip(gzip.DefaultCompression))
		}
		bootstrap(srv, "", cfe)
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	} else {
		srv, err := luaconf.Configure(*script, cfe, *webroot)
		if err != nil {
			panic(err)
		}
		serve(srv, *en_http, *en_https, *en_http2, *host+":"+strconv.Itoa(*port), *host+":"+strconv.Itoa(*ports), *cert, *key)
	}
}
<|endoftext|>


<commit_before>package http_test

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	gohttp "net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tonto/kit/http"
	"github.com/tonto/kit/http/respond"
)

type req struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (r *req) Validate() error {
	if r.ID == 999 {
		return fmt.Errorf("invalid id")
	}
	return nil
}

type resp struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func TestResponsesAndAdapters(t *testing.T) {
	cases := []struct {
		name       string
		verb       string
		path       string
		adapters   []http.Adapter
		req        req
		want       response
		sslEnabled bool
		wantCode   int
		wantErr    bool
	}{
		{
			name: "post handler",
			verb: "POST",
			path: "/svc/post_handler",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "John Doe"},
				Errors: nil,
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "post handler with apt",
			verb: "POST",
			path: "/svc/post_handler_apt",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "msg1 msg2 John Doe"},
				Errors: nil,
			},
			adapters: []http.Adapter{
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt2", "msg2 ")
						h(c, w, r)
					}
				},
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt1", "msg1 ")
						h(c, w, r)
					}
				},
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler",
			verb: "POST",
			path: "/svc/post_ep",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "John Doe"},
				Errors: nil,
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler with apt",
			verb: "POST",
			path: "/svc/post_ep_apt",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "msg1 msg2 John Doe"},
				Errors: nil,
			},
			adapters: []http.Adapter{
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt2", "msg2 ")
						h(c, w, r)
					}
				},
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt1", "msg1 ")
						h(c, w, r)
					}
				},
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler go error",
			verb: "POST",
			path: "/svc/post_ep_gerr",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusInternalServerError,
				Errors: []string{"endpoint error"},
			},
			wantCode: gohttp.StatusInternalServerError,
			wantErr:  false,
		},
		{
			name: "endpoint handler http error",
			verb: "POST",
			path: "/svc/post_ep_herr",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusBadRequest,
				Errors: []string{"endpoint error"},
			},
			wantCode:   gohttp.StatusBadRequest,
			wantErr:    false,
			sslEnabled: true,
		},

		// TODO - Test adapters and errors
	}

	for i, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			opts := []http.ServerOption{}
			if c.adapters != nil {
				opts = append(opts, http.WithAdapters(c.adapters...))
			}

			if c.sslEnabled {
				opts = append(opts, http.WithTLSConfig("testdata/server.crt", "testdata/server.key"))
			}
			s := http.NewServer(opts...)

			svc := newHSvc()

			err := s.RegisterServices(svc)
			if c.wantErr {
				assert.NotNil(t, err)
				return
			}
			assert.Nil(t, err)

			ch := make(chan struct{})
			go func(i int) {
				s.Run(9000 + i)
				ch <- struct{}{}
			}(i)

			body, _ := json.Marshal(c.req)
			url := fmt.Sprintf("http://localhost:%d%s", 9000+i, c.path)
			var client *gohttp.Client = gohttp.DefaultClient
			if c.sslEnabled {
				url = fmt.Sprintf("https://localhost%s", c.path)
				tr := &gohttp.Transport{
					TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
				}
				client = &gohttp.Client{Transport: tr}
			}
			req, err := gohttp.NewRequest(c.verb, url, bytes.NewReader(body))
			if err != nil {
				log.Fatal(err)
			}
			rsp, err := client.Do(req)
			if err != nil {
				log.Fatal(err)
			}
			jresp := response{}
			json.NewDecoder(rsp.Body).Decode(&jresp)

			assert.Equal(t, c.want, jresp)
			assert.Equal(t, c.wantCode, rsp.StatusCode)

			s.Stop()
			<-ch
		})
	}
}

type response struct {
	Code   int      `json:"code"`
	Data   *resp    `json:"data,omitempty"`
	Errors []string `json:"errors,omitempty"`
}

func newHSvc() *hsvc {
	s := hsvc{}
	s.RegisterHandler("POST", "/post_handler", s.postHandler)
	s.RegisterHandler("POST", "/post_handler_apt", s.postHandlerApt)
	s.RegisterEndpoint("POST", "/post_ep", s.postEndpoint)
	s.RegisterEndpoint("POST", "/post_ep_apt", s.postEndpointApt)
	s.RegisterEndpoint("POST", "/post_ep_gerr", s.postEndpointGErr)
	s.RegisterEndpoint("POST", "/post_ep_herr", s.postEndpointHErr)
	return &s
}

type hsvc struct {
	http.BaseService
}

func (s *hsvc) Prefix() string { return "svc" }

func (s *hsvc) postHandler(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
	req := req{}
	json.NewDecoder(r.Body).Decode(&req)
	respond.WithJSON(w, r, http.NewResponse(resp{ID: req.ID, Name: req.Name}, gohttp.StatusOK))
}

func (s *hsvc) postHandlerApt(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
	req := req{}
	json.NewDecoder(r.Body).Decode(&req)
	msg1 := c.Value("apt1").(string)
	msg2 := c.Value("apt2").(string)
	respond.WithJSON(w, r, http.NewResponse(resp{ID: req.ID, Name: msg1 + msg2 + req.Name}, gohttp.StatusOK))
}

func (s *hsvc) postEndpoint(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return http.NewResponse(&resp{ID: rq.ID, Name: rq.Name}, gohttp.StatusOK), nil
}

func (s *hsvc) postEndpointApt(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	msg1 := c.Value("apt1").(string)
	msg2 := c.Value("apt2").(string)
	return http.NewResponse(&resp{ID: rq.ID, Name: msg1 + msg2 + rq.Name}, gohttp.StatusOK), nil
}

func (s *hsvc) postEndpointGErr(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return nil, fmt.Errorf("endpoint error")
}

func (s *hsvc) postEndpointHErr(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return nil, http.NewError(gohttp.StatusBadRequest, fmt.Errorf("endpoint error"))
}
<commit_msg>Removed ssl test<commit_after>package http_test

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	gohttp "net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tonto/kit/http"
	"github.com/tonto/kit/http/respond"
)

type req struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (r *req) Validate() error {
	if r.ID == 999 {
		return fmt.Errorf("invalid id")
	}
	return nil
}

type resp struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func TestResponsesAndAdapters(t *testing.T) {
	cases := []struct {
		name       string
		verb       string
		path       string
		adapters   []http.Adapter
		req        req
		want       response
		sslEnabled bool
		wantCode   int
		wantErr    bool
	}{
		{
			name: "post handler",
			verb: "POST",
			path: "/svc/post_handler",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "John Doe"},
				Errors: nil,
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "post handler with apt",
			verb: "POST",
			path: "/svc/post_handler_apt",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "msg1 msg2 John Doe"},
				Errors: nil,
			},
			adapters: []http.Adapter{
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt2", "msg2 ")
						h(c, w, r)
					}
				},
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt1", "msg1 ")
						h(c, w, r)
					}
				},
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler",
			verb: "POST",
			path: "/svc/post_ep",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "John Doe"},
				Errors: nil,
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler with apt",
			verb: "POST",
			path: "/svc/post_ep_apt",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusOK,
				Data:   &resp{ID: 1, Name: "msg1 msg2 John Doe"},
				Errors: nil,
			},
			adapters: []http.Adapter{
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt2", "msg2 ")
						h(c, w, r)
					}
				},
				func(h http.HandlerFunc) http.HandlerFunc {
					return func(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
						c = context.WithValue(c, "apt1", "msg1 ")
						h(c, w, r)
					}
				},
			},
			wantCode: gohttp.StatusOK,
			wantErr:  false,
		},
		{
			name: "endpoint handler go error",
			verb: "POST",
			path: "/svc/post_ep_gerr",
			req:  req{ID: 1, Name: "John Doe"},
			want: response{
				Code:   gohttp.StatusInternalServerError,
				Errors: []string{"endpoint error"},
			},
			wantCode: gohttp.StatusInternalServerError,
			wantErr:  false,
		},
		// {
		// 	name: "endpoint handler http error",
		// 	verb: "POST",
		// 	path: "/svc/post_ep_herr",
		// 	req:  req{ID: 1, Name: "John Doe"},
		// 	want: response{
		// 		Code:   gohttp.StatusBadRequest,
		// 		Errors: []string{"endpoint error"},
		// 	},
		// 	wantCode:   gohttp.StatusBadRequest,
		// 	wantErr:    false,
		// 	sslEnabled: true,
		// },

		// TODO - Test adapters and errors
	}

	for i, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			opts := []http.ServerOption{}
			if c.adapters != nil {
				opts = append(opts, http.WithAdapters(c.adapters...))
			}

			if c.sslEnabled {
				opts = append(opts, http.WithTLSConfig("testdata/server.crt", "testdata/server.key"))
			}
			s := http.NewServer(opts...)

			svc := newHSvc()

			err := s.RegisterServices(svc)
			if c.wantErr {
				assert.NotNil(t, err)
				return
			}
			assert.Nil(t, err)

			ch := make(chan struct{})
			go func(i int) {
				s.Run(9000 + i)
				ch <- struct{}{}
			}(i)

			body, _ := json.Marshal(c.req)
			url := fmt.Sprintf("http://localhost:%d%s", 9000+i, c.path)
			var client *gohttp.Client = gohttp.DefaultClient
			if c.sslEnabled {
				url = fmt.Sprintf("https://localhost%s", c.path)
				tr := &gohttp.Transport{
					TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
				}
				client = &gohttp.Client{Transport: tr}
			}
			req, err := gohttp.NewRequest(c.verb, url, bytes.NewReader(body))
			if err != nil {
				log.Fatal(err)
			}
			rsp, err := client.Do(req)
			if err != nil {
				log.Fatal(err)
			}
			jresp := response{}
			json.NewDecoder(rsp.Body).Decode(&jresp)

			assert.Equal(t, c.want, jresp)
			assert.Equal(t, c.wantCode, rsp.StatusCode)

			s.Stop()
			<-ch
		})
	}
}

type response struct {
	Code   int      `json:"code"`
	Data   *resp    `json:"data,omitempty"`
	Errors []string `json:"errors,omitempty"`
}

func newHSvc() *hsvc {
	s := hsvc{}
	s.RegisterHandler("POST", "/post_handler", s.postHandler)
	s.RegisterHandler("POST", "/post_handler_apt", s.postHandlerApt)
	s.RegisterEndpoint("POST", "/post_ep", s.postEndpoint)
	s.RegisterEndpoint("POST", "/post_ep_apt", s.postEndpointApt)
	s.RegisterEndpoint("POST", "/post_ep_gerr", s.postEndpointGErr)
	s.RegisterEndpoint("POST", "/post_ep_herr", s.postEndpointHErr)
	return &s
}

type hsvc struct {
	http.BaseService
}

func (s *hsvc) Prefix() string { return "svc" }

func (s *hsvc) postHandler(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
	req := req{}
	json.NewDecoder(r.Body).Decode(&req)
	respond.WithJSON(w, r, http.NewResponse(resp{ID: req.ID, Name: req.Name}, gohttp.StatusOK))
}

func (s *hsvc) postHandlerApt(c context.Context, w gohttp.ResponseWriter, r *gohttp.Request) {
	req := req{}
	json.NewDecoder(r.Body).Decode(&req)
	msg1 := c.Value("apt1").(string)
	msg2 := c.Value("apt2").(string)
	respond.WithJSON(w, r, http.NewResponse(resp{ID: req.ID, Name: msg1 + msg2 + req.Name}, gohttp.StatusOK))
}

func (s *hsvc) postEndpoint(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return http.NewResponse(&resp{ID: rq.ID, Name: rq.Name}, gohttp.StatusOK), nil
}

func (s *hsvc) postEndpointApt(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	msg1 := c.Value("apt1").(string)
	msg2 := c.Value("apt2").(string)
	return http.NewResponse(&resp{ID: rq.ID, Name: msg1 + msg2 + rq.Name}, gohttp.StatusOK), nil
}

func (s *hsvc) postEndpointGErr(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return nil, fmt.Errorf("endpoint error")
}

func (s *hsvc) postEndpointHErr(c context.Context, w gohttp.ResponseWriter, rq *req) (*http.Response, error) {
	return nil, http.NewError(gohttp.StatusBadRequest, fmt.Errorf("endpoint error"))
}
<|endoftext|>


<commit_before>package standard

import (
	"github.com/denverdino/aliyungo/common"
)

type DeletionProtection string

const (
	DeletionProtectionEnabled  = DeletionProtection("Enabled")
	DeletionProtectionDisabled = DeletionProtection("Disabled")
)

//https://help.aliyun.com/document_detail/28910.html?spm=5176.doc50083.6.580.b5wkQr
type CreateStackRequest struct {
	RegionId           common.Region
	StackName          string
	DisableRollback    bool
	TemplateBody       string
	TemplateURL        string
	Parameters         interface{}
	StackPolicyURL     string
	TimeoutInMinutes   int
	StackPolicyBody    string
	ClientToken        string
	NotificationURLs   []string
	DeletionProtection DeletionProtection
	RamRoleName        string
}

type CreateStackResponse struct {
	StackId string
	common.Response
}

type ListStackEventsRequest struct {
	common.Pagination
	RegionId          common.Region
	StackId           string
	Status            []string
	ResourceType      []string
	LogicalResourceId []string
}

type ListStackEventsResponse struct {
	common.Response
	common.PaginationResult
	RegionId common.Region
	Events   []Event
}

type Event struct {
	StackId            string
	Status             string
	StackName          string
	StatusReason       string
	EventId            string
	LogicalResourceId  string
	ResourceType       string
	PhysicalResourceId string
	CreateTime         string
}

func (client *Client) ListStackEvents(args *ListStackEventsRequest) (*ListStackEventsResponse, error) {
	response := &ListStackEventsResponse{}
	err := client.Invoke("ListStackEvents", args, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func (client *Client) CreateStack(args *CreateStackRequest) (*CreateStackResponse, error) {
	stack := &CreateStackResponse{}
	err := client.Invoke("CreateStack", args, stack)
	if err != nil {
		return nil, err
	}

	return stack, nil
}

//https://help.aliyun.com/document_detail/28911.html?spm=5176.doc28910.6.581.etoi2Z
type DeleteStackRequest struct {
	RegionId           common.Region
	StackId            string
	RetainAllResources bool
	RetainResources    []string
	RamRoleName        string
}

type DeleteStackResponse struct {
	common.Response
}

func (client *Client) DeleteStack(req *DeleteStackRequest) (*DeleteStackResponse, error) {
	response := &DeleteStackResponse{}
	err := client.Invoke("DeleteStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type GetStackRequest struct {
	RegionId    common.Region
	StackId     string
	ClientToken string
}

type GetStackResponse struct {
	CreateTime          string
	Description         string
	DisableRollback     bool
	NotificationURLs    []string
	Outputs             []Output
	ParentStackId       string
	RegionId            common.Region
	Status              string
	StackId             string
	StackName           string
	Parameters          []Parameter
	UpdateTime          string
	StatusReason        string
	TemplateDescription string
	TimeoutInMinutes    int

	RequestId          string
	DeletionProtection DeletionProtection
	DriftDetectionTime string
	RamRoleName        string
	RootStackId        string
	StackDriftStatus   string
	StackType          string
}

type Parameter struct {
	ParameterKey   string
	ParameterValue string
}

type Output struct {
	Description string
	OutputKey   string
	OutputValue interface{}
}

func (client *Client) GetStack(req *GetStackRequest) (*GetStackResponse, error) {
	response := &GetStackResponse{}
	err := client.Invoke("GetStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type ListStacksRequest struct {
	RegionId        common.Region
	StackId         string
	Status          []string
	ParentStackId   string
	StackName       []string
	ShowNestedStack bool
	Tag             []Tag
	common.Pagination
}

type ListStacksResponse struct {
	common.PaginationResult
	common.Response
	Stacks []Stack
}

type Stack struct {
	CreateTime string

	DisableRollback    bool
	DriftDetectionTime string
	ParentStackId      string
	RegionId           common.Region
	StackDriftStatus   string
	StackId            string
	StackName          string
	Status             string
	StatusReason       string

	TimeoutInMinutes int
	UpdateTime       string
}

type Tag struct {
	Key   string
	Value string
}

func (client *Client) ListStacks(req *ListStacksRequest) (*ListStacksResponse, error) {
	response := &ListStacksResponse{}
	err := client.Invoke("ListStacks", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type UpdateStackRequest struct {
	Parameters                  []Parameter
	RegionId                    string
	StackId                     string
	ClientToken                 string
	StackPolicyDuringUpdateBody string
	TimeoutInMinutes            int
	TemplateBody                string
	StackPolicyURL              string
	StackPolicyDuringUpdateURL  string
	StackPolicyBody             string
	UsePreviousParameters       bool
	DisableRollback             bool
	TemplateURL                 string
	RamRoleName                 string
	ReplacementOption           string
}

type UpdateStackResponse struct {
	StackId string
	common.Response
}

func (client *Client) UpdateStack(req *UpdateStackRequest) (*UpdateStackResponse, error) {
	response := &UpdateStackResponse{}
	err := client.Invoke("UpdateStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}
<commit_msg>resource attributie (#408)<commit_after>package standard

import (
	"github.com/denverdino/aliyungo/common"
)

type DeletionProtection string

const (
	DeletionProtectionEnabled  = DeletionProtection("Enabled")
	DeletionProtectionDisabled = DeletionProtection("Disabled")
)

//https://help.aliyun.com/document_detail/28910.html?spm=5176.doc50083.6.580.b5wkQr
type CreateStackRequest struct {
	RegionId           common.Region
	StackName          string
	DisableRollback    bool
	TemplateBody       string
	TemplateURL        string
	Parameters         interface{}
	StackPolicyURL     string
	TimeoutInMinutes   int
	StackPolicyBody    string
	ClientToken        string
	NotificationURLs   []string
	DeletionProtection DeletionProtection
	RamRoleName        string
}

type CreateStackResponse struct {
	StackId string
	common.Response
}

type ListStackEventsRequest struct {
	common.Pagination
	RegionId          common.Region
	StackId           string
	Status            []string
	ResourceType      []string
	LogicalResourceId []string
}

type ListStackEventsResponse struct {
	common.Response
	common.PaginationResult
	RegionId common.Region
	Events   []Event
}

type Event struct {
	StackId            string
	Status             string
	StackName          string
	StatusReason       string
	EventId            string
	LogicalResourceId  string
	ResourceType       string
	PhysicalResourceId string
	CreateTime         string
}

func (client *Client) ListStackEvents(args *ListStackEventsRequest) (*ListStackEventsResponse, error) {
	response := &ListStackEventsResponse{}
	err := client.Invoke("ListStackEvents", args, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

func (client *Client) CreateStack(args *CreateStackRequest) (*CreateStackResponse, error) {
	stack := &CreateStackResponse{}
	err := client.Invoke("CreateStack", args, stack)
	if err != nil {
		return nil, err
	}

	return stack, nil
}

//https://help.aliyun.com/document_detail/28911.html?spm=5176.doc28910.6.581.etoi2Z
type DeleteStackRequest struct {
	RegionId           common.Region
	StackId            string
	RetainAllResources bool
	RetainResources    []string
	RamRoleName        string
}

type DeleteStackResponse struct {
	common.Response
}

func (client *Client) DeleteStack(req *DeleteStackRequest) (*DeleteStackResponse, error) {
	response := &DeleteStackResponse{}
	err := client.Invoke("DeleteStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type GetStackRequest struct {
	RegionId    common.Region
	StackId     string
	ClientToken string
}

type GetStackResponse struct {
	CreateTime          string
	Description         string
	DisableRollback     bool
	NotificationURLs    []string
	Outputs             []Output
	ParentStackId       string
	RegionId            common.Region
	Status              string
	StackId             string
	StackName           string
	Parameters          []Parameter
	UpdateTime          string
	StatusReason        string
	TemplateDescription string
	TimeoutInMinutes    int

	RequestId          string
	DeletionProtection DeletionProtection
	DriftDetectionTime string
	RamRoleName        string
	RootStackId        string
	StackDriftStatus   string
	StackType          string
}

type Parameter struct {
	ParameterKey   string
	ParameterValue string
}

type Output struct {
	Description string
	OutputKey   string
	OutputValue interface{}
}

func (client *Client) GetStack(req *GetStackRequest) (*GetStackResponse, error) {
	response := &GetStackResponse{}
	err := client.Invoke("GetStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type ListStacksRequest struct {
	RegionId        common.Region
	StackId         string
	Status          []string
	ParentStackId   string
	StackName       []string
	ShowNestedStack bool
	Tag             []Tag
	common.Pagination
}

type ListStacksResponse struct {
	common.PaginationResult
	common.Response
	Stacks []Stack
}

type Stack struct {
	CreateTime string

	DisableRollback    bool
	DriftDetectionTime string
	ParentStackId      string
	RegionId           common.Region
	StackDriftStatus   string
	StackId            string
	StackName          string
	Status             string
	StatusReason       string

	TimeoutInMinutes int
	UpdateTime       string
}

type Tag struct {
	Key   string
	Value string
}

func (client *Client) ListStacks(req *ListStacksRequest) (*ListStacksResponse, error) {
	response := &ListStacksResponse{}
	err := client.Invoke("ListStacks", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type UpdateStackRequest struct {
	Parameters                  []Parameter
	RegionId                    string
	StackId                     string
	ClientToken                 string
	StackPolicyDuringUpdateBody string
	TimeoutInMinutes            int
	TemplateBody                string
	StackPolicyURL              string
	StackPolicyDuringUpdateURL  string
	StackPolicyBody             string
	UsePreviousParameters       bool
	DisableRollback             bool
	TemplateURL                 string
	RamRoleName                 string
	ReplacementOption           string
}

type UpdateStackResponse struct {
	StackId string
	common.Response
}

func (client *Client) UpdateStack(req *UpdateStackRequest) (*UpdateStackResponse, error) {
	response := &UpdateStackResponse{}
	err := client.Invoke("UpdateStack", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type GetStackResourceResponse struct {
	StackId                string
	LogicalResourceId      string
	ClientToken            string
	ShowResourceAttributes bool
	RegionId               common.Region
}

type GetStackResourceRequest struct {
	Status            string
	Description       string
	LogicalResourceId string
	StackId           string

	StackName           string
	StatusReason        string
	PhysicalResourceId  string
	ResourceType        string
	CreateTime          string
	Metadata            map[string]string
	UpdateTime          string
	ResourceAttributes  []ResourceAttribute
	RequestId           string
	DriftDetectionTime  string
	ResourceDriftStatus string
}
type ResourceAttribute struct {
	ResourceAttributeValue string
	ResourceAttributeKey   string
}

func (client *Client) GetStackResource(req *GetStackResourceRequest) (*GetStackResourceResponse, error) {
	response := &GetStackResourceResponse{}
	err := client.Invoke("GetStackResource", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}

type ListStackResourcesRequest struct {
	RegionId common.Region
	StackId  string
}

type ListStackResourcesResponse struct {
	common.Response
	Resources []Resource
}

type Resource struct {
	CreateTime         string
	DriftDetectionTime string
	LogicalResourceId  string
	PhysicalResourceId string

	ResourceDriftStatus string
	ResourceType        string
	StackId             string
	StackName           string
	Status              string
	StatusReason        string
	UpdateTime          string
}

func (client *Client) ListStackResources(req *ListStackResourcesRequest) (*ListStackResourcesResponse, error) {
	response := &ListStackResourcesResponse{}
	err := client.Invoke("ListStackResources", req, response)
	if err != nil {
		return nil, err
	}

	return response, nil
}
<|endoftext|>