<commit_before>package base_image_test

import (
	"fmt"
	"strconv"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"

	"github.com/flant/werf/pkg/testing/utils"
	utilsDocker "github.com/flant/werf/pkg/testing/utils/docker"
)

var _ = Describe("from and fromLatest", func() {
	var fromBaseRepoImageState1ID, fromBaseRepoImageState2ID string
	var fromImage string

	fromBaseRepoImageState1IDFunc := func() string { return fromBaseRepoImageState1ID }
	fromBaseRepoImageState2IDFunc := func() string { return fromBaseRepoImageState2ID }

	registryProjectRepositoryLatestAs := func(imageName string) {
		Ω(utilsDocker.Pull(imageName)).Should(Succeed(), "docker pull")
		Ω(utilsDocker.CliTag(imageName, registryProjectRepository)).Should(Succeed(), "docker tag")
		Ω(utilsDocker.CliPush(registryProjectRepository)).Should(Succeed(), "docker push")
		Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
	}

	BeforeEach(func() {
		testDirPath = utils.FixturePath("from_and_from_latest")

		fromBaseRepoImageState1ID = utilsDocker.ImageID(suiteImage1)
		fromBaseRepoImageState2ID = utilsDocker.ImageID(suiteImage2)

		fromImage = registryProjectRepository

		stubs.SetEnv("FROM_IMAGE", fromImage)
		stubs.SetEnv("FROM_LATEST", "false")
	})

	type entry struct {
		fromLatest              bool
		expectedOutputMatchers  []types.GomegaMatcher
		expectedFromStageParent func() string
		expectedErr             bool
	}

	entryItBody := func(e entry) {
		stubs.SetEnv("FROM_LATEST", strconv.FormatBool(e.fromLatest))

		res, err := utils.RunCommand(
			testDirPath,
			werfBinPath,
			"build",
		)

		if e.expectedErr {
			Ω(err).Should(HaveOccurred())
		} else {
			Ω(err).ShouldNot(HaveOccurred())
		}

		for _, matcher := range e.expectedOutputMatchers {
			Ω(string(res)).Should(matcher)
		}

		if err == nil {
			resultImageName := utils.SucceedCommandOutputString(
				testDirPath,
				werfBinPath,
				"stage", "image",
			)

			Ω(utilsDocker.ImageParent(strings.TrimSpace(resultImageName))).Should(Equal(e.expectedFromStageParent()))
		}
	}

	Context("when from stage is not built", func() {
		Context("when registry from image does not exist", func() {
			Context("when local from image does not exist", func() {
				It("should fail during pulling (fromLatest: false)", func() {
					entryItBody(entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Trying to get from base image id from registry")),
							ContainSubstring("Pulling base image"),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: prepare base image %s failed", fromImage)),
						},
						expectedErr: true,
					})
				})

				It("should fail during getting actual id (fromLatest: true)", func() {
					entryItBody(entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: can not get base image id from registry (%s)", fromImage)),
						},
						expectedErr: true,
					})
				})
			})

			Context("when local from image exist", func() {
				BeforeEach(func() {
					Ω(utilsDocker.CliTag(suiteImage1, fromImage)).Should(Succeed(), "docker tag")
				})

				AfterEach(func() {
					utilsDocker.ImageRemoveIfExists(fromImage)
				})

				It("should be built with local image and warnings (fromLatest: false)", func() {
					entryItBody(entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							ContainSubstring("cannot get base image id"),
							ContainSubstring(fmt.Sprintf("using existing image %s without pull", fromImage)),
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState1IDFunc,
					})
				})

				It("should fail during getting actual id (fromLatest: true)", func() {
					entryItBody(entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: can not get base image id from registry (%s)", fromImage)),
						},
						expectedErr: true,
					})
				})
			})
		})

		Context("when registry from image exists", func() {
			BeforeEach(func() {
				registryProjectRepositoryLatestAs(suiteImage2)
			})

			AfterEach(func() {
				utilsDocker.ImageRemoveIfExists(registryProjectRepository)
			})

			Context("when local from image is actual", func() {
				BeforeEach(func() {
					Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
				})

				DescribeTable("checking from stage logic",
					entryItBody,
					Entry("should be built with local image (fromLatest: false)", entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					}),
					Entry("should be built with local image (fromLatest: true)", entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					}),
				)
			})

			Context("when local from image is not actual", func() {
				Context("when from image does not exist locally", func() {
					DescribeTable("checking from stage logic",
						entryItBody,
						Entry("should be built with actual image (fromLatest: false)", entry{
							fromLatest: false,
							expectedOutputMatchers: []types.GomegaMatcher{
								Not(ContainSubstring("Trying to get from base image id from registry")),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
						Entry("should be built with actual image (fromLatest: true)", entry{
							fromLatest: true,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
					)
				})

				Context("when from image exists locally", func() {
					BeforeEach(func() {
						Ω(utilsDocker.Pull(suiteImage1)).Should(Succeed(), "docker pull")
						Ω(utilsDocker.CliTag(suiteImage1, registryProjectRepository)).Should(Succeed(), "docker tag")
					})

					DescribeTable("checking from stage logic",
						entryItBody,
						Entry("should be built with actual image (fromLatest: false)", entry{
							fromLatest: false,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
						Entry("should be built with actual image (fromLatest: true)", entry{
							fromLatest: true,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
					)
				})
			})
		})
	})

	Context("when from stage is built", func() {
		BeforeEach(func() {
			registryProjectRepositoryLatestAs(suiteImage2)
		})

		AfterEach(func() {
			utilsDocker.ImageRemoveIfExists(registryProjectRepository)
		})

		type entryWithPreBuild struct {
			entry
			afterFirstBuildHook func()
		}

		entryWithPreBuildItBody := func(e entryWithPreBuild) {
			stubs.SetEnv("FROM_LATEST", strconv.FormatBool(e.fromLatest))

			utils.RunSucceedCommand(
				testDirPath,
				werfBinPath,
				"build",
			)

			if e.afterFirstBuildHook != nil {
				e.afterFirstBuildHook()
			}

			entryItBody(e.entry)
		}

		Context("when from stage image is actual", func() {
			DescribeTable("checking from stage logic",
				entryWithPreBuildItBody,
				Entry("should not be rebuilt (fromLatest: false)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
					},
					entry: entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Trying to get from base image id from registry")),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
				Entry("should not be rebuilt (fromLatest: true)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
					},
					entry: entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
			)
		})

		Context("when from stage image is not actual", func() {
			DescribeTable("checking from stage logic",
				entryWithPreBuildItBody,
				Entry("should not be rebuilt (fromLatest: false)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
						registryProjectRepositoryLatestAs(suiteImage1)
					},
					entry: entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Building stage from")),
							Not(ContainSubstring("Trying to get from base image id from registry")),
							Not(ContainSubstring("Pulling base image")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
				Entry("should be rebuilt with actual image (fromLatest: true)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
						registryProjectRepositoryLatestAs(suiteImage1)
					},
					entry: entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Building stage from"),
							ContainSubstring("Trying to get from base image id from registry"),
							ContainSubstring("Pulling base image"),
						},
						expectedFromStageParent: fromBaseRepoImageState1IDFunc,
					},
				}),
			)
		})
	})
})

var _ = Describe("fromCacheVersion", func() {
	BeforeEach(func() {
		testDirPath = utils.FixturePath("from_cache_version")
	})

	It("should be rebuilt", func() {
		specStep := func(fromCacheVersion string) {
			By(fmt.Sprintf("fromCacheVersion: %s", fromCacheVersion))
			stubs.SetEnv("FROM_CACHE_VERSION", fromCacheVersion)

			output := utils.SucceedCommandOutputString(
				testDirPath,
				werfBinPath,
				"build",
			)

			Ω(output).Should(ContainSubstring("Building stage from"))
		}

		specStep("0")
		specStep("1")
	})
})
<commit_msg>[tests] Optimize docker pulls in base image suite<commit_after>package base_image_test

import (
	"fmt"
	"strconv"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"

	"github.com/flant/werf/pkg/testing/utils"
	utilsDocker "github.com/flant/werf/pkg/testing/utils/docker"
)

var _ = Describe("from and fromLatest", func() {
	var fromBaseRepoImageState1ID, fromBaseRepoImageState2ID string
	var fromImage string

	fromBaseRepoImageState1IDFunc := func() string { return fromBaseRepoImageState1ID }
	fromBaseRepoImageState2IDFunc := func() string { return fromBaseRepoImageState2ID }

	registryProjectRepositoryLatestAs := func(imageName string) {
		if !utilsDocker.IsImageExist(imageName) {
			Ω(utilsDocker.Pull(imageName)).Should(Succeed(), "docker pull")
		}
		Ω(utilsDocker.CliTag(imageName, registryProjectRepository)).Should(Succeed(), "docker tag")
		Ω(utilsDocker.CliPush(registryProjectRepository)).Should(Succeed(), "docker push")
		Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
	}

	BeforeEach(func() {
		testDirPath = utils.FixturePath("from_and_from_latest")

		fromBaseRepoImageState1ID = utilsDocker.ImageID(suiteImage1)
		fromBaseRepoImageState2ID = utilsDocker.ImageID(suiteImage2)

		fromImage = registryProjectRepository

		stubs.SetEnv("FROM_IMAGE", fromImage)
		stubs.SetEnv("FROM_LATEST", "false")
	})

	type entry struct {
		fromLatest              bool
		expectedOutputMatchers  []types.GomegaMatcher
		expectedFromStageParent func() string
		expectedErr             bool
	}

	entryItBody := func(e entry) {
		stubs.SetEnv("FROM_LATEST", strconv.FormatBool(e.fromLatest))

		res, err := utils.RunCommand(
			testDirPath,
			werfBinPath,
			"build",
		)

		if e.expectedErr {
			Ω(err).Should(HaveOccurred())
		} else {
			Ω(err).ShouldNot(HaveOccurred())
		}

		for _, matcher := range e.expectedOutputMatchers {
			Ω(string(res)).Should(matcher)
		}

		if err == nil {
			resultImageName := utils.SucceedCommandOutputString(
				testDirPath,
				werfBinPath,
				"stage", "image",
			)

			Ω(utilsDocker.ImageParent(strings.TrimSpace(resultImageName))).Should(Equal(e.expectedFromStageParent()))
		}
	}

	Context("when from stage is not built", func() {
		Context("when registry from image does not exist", func() {
			Context("when local from image does not exist", func() {
				It("should fail during pulling (fromLatest: false)", func() {
					entryItBody(entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Trying to get from base image id from registry")),
							ContainSubstring("Pulling base image"),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: prepare base image %s failed", fromImage)),
						},
						expectedErr: true,
					})
				})

				It("should fail during getting actual id (fromLatest: true)", func() {
					entryItBody(entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: can not get base image id from registry (%s)", fromImage)),
						},
						expectedErr: true,
					})
				})
			})

			Context("when local from image exist", func() {
				BeforeEach(func() {
					Ω(utilsDocker.CliTag(suiteImage1, fromImage)).Should(Succeed(), "docker tag")
				})

				AfterEach(func() {
					utilsDocker.ImageRemoveIfExists(fromImage)
				})

				It("should be built with local image and warnings (fromLatest: false)", func() {
					entryItBody(entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							ContainSubstring("cannot get base image id"),
							ContainSubstring(fmt.Sprintf("using existing image %s without pull", fromImage)),
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState1IDFunc,
					})
				})

				It("should fail during getting actual id (fromLatest: true)", func() {
					entryItBody(entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
							ContainSubstring(fmt.Sprintf("Error: can not get base image id from registry (%s)", fromImage)),
						},
						expectedErr: true,
					})
				})
			})
		})

		Context("when registry from image exists", func() {
			BeforeEach(func() {
				registryProjectRepositoryLatestAs(suiteImage2)
			})

			AfterEach(func() {
				utilsDocker.ImageRemoveIfExists(registryProjectRepository)
			})

			Context("when local from image is actual", func() {
				BeforeEach(func() {
					Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
				})

				DescribeTable("checking from stage logic",
					entryItBody,
					Entry("should be built with local image (fromLatest: false)", entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					}),
					Entry("should be built with local image (fromLatest: true)", entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							ContainSubstring("Building stage from"),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					}),
				)
			})

			Context("when local from image is not actual", func() {
				Context("when from image does not exist locally", func() {
					DescribeTable("checking from stage logic",
						entryItBody,
						Entry("should be built with actual image (fromLatest: false)", entry{
							fromLatest: false,
							expectedOutputMatchers: []types.GomegaMatcher{
								Not(ContainSubstring("Trying to get from base image id from registry")),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
						Entry("should be built with actual image (fromLatest: true)", entry{
							fromLatest: true,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
					)
				})

				Context("when from image exists locally", func() {
					BeforeEach(func() {
						Ω(utilsDocker.CliTag(suiteImage1, registryProjectRepository)).Should(Succeed(), "docker tag")
					})

					DescribeTable("checking from stage logic",
						entryItBody,
						Entry("should be built with actual image (fromLatest: false)", entry{
							fromLatest: false,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
						Entry("should be built with actual image (fromLatest: true)", entry{
							fromLatest: true,
							expectedOutputMatchers: []types.GomegaMatcher{
								ContainSubstring("Trying to get from base image id from registry"),
								ContainSubstring("Pulling base image"),
								ContainSubstring("Building stage from"),
							},
							expectedFromStageParent: fromBaseRepoImageState2IDFunc,
						}),
					)
				})
			})
		})
	})

	Context("when from stage is built", func() {
		BeforeEach(func() {
			registryProjectRepositoryLatestAs(suiteImage2)
		})

		AfterEach(func() {
			utilsDocker.ImageRemoveIfExists(registryProjectRepository)
		})

		type entryWithPreBuild struct {
			entry
			afterFirstBuildHook func()
		}

		entryWithPreBuildItBody := func(e entryWithPreBuild) {
			stubs.SetEnv("FROM_LATEST", strconv.FormatBool(e.fromLatest))

			utils.RunSucceedCommand(
				testDirPath,
				werfBinPath,
				"build",
			)

			if e.afterFirstBuildHook != nil {
				e.afterFirstBuildHook()
			}

			entryItBody(e.entry)
		}

		Context("when from stage image is actual", func() {
			DescribeTable("checking from stage logic",
				entryWithPreBuildItBody,
				Entry("should not be rebuilt (fromLatest: false)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
					},
					entry: entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Trying to get from base image id from registry")),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
				Entry("should not be rebuilt (fromLatest: true)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.Pull(registryProjectRepository)).Should(Succeed(), "docker pull")
					},
					entry: entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Trying to get from base image id from registry"),
							Not(ContainSubstring("Pulling base image")),
							Not(ContainSubstring("Building stage from")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
			)
		})

		Context("when from stage image is not actual", func() {
			DescribeTable("checking from stage logic",
				entryWithPreBuildItBody,
				Entry("should not be rebuilt (fromLatest: false)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
						registryProjectRepositoryLatestAs(suiteImage1)
					},
					entry: entry{
						fromLatest: false,
						expectedOutputMatchers: []types.GomegaMatcher{
							Not(ContainSubstring("Building stage from")),
							Not(ContainSubstring("Trying to get from base image id from registry")),
							Not(ContainSubstring("Pulling base image")),
						},
						expectedFromStageParent: fromBaseRepoImageState2IDFunc,
					},
				}),
				Entry("should be rebuilt with actual image (fromLatest: true)", entryWithPreBuild{
					afterFirstBuildHook: func() {
						Ω(utilsDocker.CliRmi(registryProjectRepository)).Should(Succeed(), "docker rmi")
						registryProjectRepositoryLatestAs(suiteImage1)
					},
					entry: entry{
						fromLatest: true,
						expectedOutputMatchers: []types.GomegaMatcher{
							ContainSubstring("Building stage from"),
							ContainSubstring("Trying to get from base image id from registry"),
							ContainSubstring("Pulling base image"),
						},
						expectedFromStageParent: fromBaseRepoImageState1IDFunc,
					},
				}),
			)
		})
	})
})

var _ = Describe("fromCacheVersion", func() {
	BeforeEach(func() {
		testDirPath = utils.FixturePath("from_cache_version")
	})

	It("should be rebuilt", func() {
		specStep := func(fromCacheVersion string) {
			By(fmt.Sprintf("fromCacheVersion: %s", fromCacheVersion))
			stubs.SetEnv("FROM_CACHE_VERSION", fromCacheVersion)

			output := utils.SucceedCommandOutputString(
				testDirPath,
				werfBinPath,
				"build",
			)

			Ω(output).Should(ContainSubstring("Building stage from"))
		}

		specStep("0")
		specStep("1")
	})
})
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"

	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
	"github.com/pressly/chi/render"
)

func main() {
	r := chi.NewRouter()

	r.Use(middleware.RequestID)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)

	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("..."))
	})

	r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("pong"))
	})

	r.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
		panic("test")
	})

	// Slow handlers/operations.
	r.Group(func(r chi.Router) {
		// Stop processing when client disconnects.
		r.Use(middleware.CloseNotify)

		// Stop processing after 2.5 seconds.
		r.Use(middleware.Timeout(2500 * time.Millisecond))

		r.Get("/slow", func(w http.ResponseWriter, r *http.Request) {
			rand.Seed(time.Now().Unix())

			// Processing will take 1-5 seconds.
			processTime := time.Duration(rand.Intn(4)+1) * time.Second

			select {
			case <-r.Context().Done():
				return

			case <-time.After(processTime):
				// The above channel simulates some hard work.
			}

			w.Write([]byte(fmt.Sprintf("Processed in %v seconds\n", processTime)))
		})
	})

	// Throttle very expensive handlers/operations.
	r.Group(func(r chi.Router) {
		// Stop processing after 30 seconds.
		r.Use(middleware.Timeout(30 * time.Second))

		// Only one request will be processed at a time.
		r.Use(middleware.Throttle(1))

		r.Get("/throttled", func(w http.ResponseWriter, r *http.Request) {
			select {
			case <-r.Context().Done():
				switch r.Context().Err() {
				case context.DeadlineExceeded:
					w.WriteHeader(504)
					w.Write([]byte("Processing too slow\n"))
				default:
					w.Write([]byte("Canceled\n"))
				}
				return

			case <-time.After(5 * time.Second):
				// The above channel simulates some hard work.
			}

			w.Write([]byte("Processed\n"))
		})
	})

	// RESTy routes for "articles" resource
	r.Route("/articles", func(r chi.Router) {
		r.Get("/", chi.Use(paginate).Handler(listArticles)) // GET /articles
		r.Post("/", createArticle)                          // POST /articles

		r.Route("/:articleID", func(r chi.Router) {
			r.Use(ArticleCtx)
			r.Get("/", getArticle)       // GET /articles/123
			r.Put("/", updateArticle)    // PUT /articles/123
			r.Delete("/", deleteArticle) // DELETE /articles/123
		})
	})

	// Mount the admin sub-router
	r.Mount("/admin", adminRouter())

	http.ListenAndServe(":3333", r)
}

type Article struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}

func ArticleCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		articleID := chi.URLParam(r, "articleID")
		article, err := dbGetArticle(articleID)
		if err != nil {
			http.Error(w, http.StatusText(404), 404)
			return
		}
		ctx := context.WithValue(r.Context(), "article", article)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func listArticles(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("list of articles.."))
	// or render.Data(w, 200, []byte("list of articles.."))
}

func createArticle(w http.ResponseWriter, r *http.Request) {
	var article *Article

	// btw, you could do this body reading / marhsalling in a nice bind middleware
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), 422)
		return
	}
	defer r.Body.Close()

	if err := json.Unmarshal(data, &article); err != nil {
		http.Error(w, err.Error(), 422)
		return
	}

	// should really send back the json marshalled new article.
	// build your own responder :)
	w.Write([]byte(article.Title))
}

func getArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(422), 422)
		return
	}

	// Build your own responder, see the "./render" pacakge as a starting
	// point for your own.
	render.JSON(w, 200, article)

	// or..
	// w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

func updateArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// btw, you could do this body reading / marhsalling in a nice bind middleware
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), 422)
		return
	}
	defer r.Body.Close()

	uArticle := struct {
		*Article
		_ interface{} `json:"id,omitempty"` // prevents 'id' from being overridden
	}{Article: article}

	if err := json.Unmarshal(data, &uArticle); err != nil {
		http.Error(w, err.Error(), 422)
		return
	}

	render.JSON(w, 200, uArticle)

	// w.Write([]byte(fmt.Sprintf("updated article, title:%s", uArticle.Title)))
}

func deleteArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(422), 422)
		return
	}
	_ = article // delete the article from the data store..
	w.WriteHeader(204)
}

func dbGetArticle(id string) (*Article, error) {
	//.. fetch the article from a data store of some kind..
	return &Article{ID: id, Title: "Going all the way,"}, nil
}

func paginate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// just a stub.. some ideas are to look at URL query params for something like
		// the page number, or the limit, and send a query cursor down the chain
		next.ServeHTTP(w, r)
	})
}

// A completely separate router for administrator routes
func adminRouter() chi.Router {
	r := chi.NewRouter()
	r.Use(AdminOnly)
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("admin: index"))
	})
	r.Get("/accounts", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("admin: list accounts.."))
	})
	r.Get("/users/:userId", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParam(r, "userId"))))
	})
	return r
}

func AdminOnly(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		isAdmin, ok := r.Context().Value("acl.admin").(bool)
		if !ok || !isAdmin {
			http.Error(w, http.StatusText(403), 403)
			return
		}
		next.ServeHTTP(w, r)
	})
}
<commit_msg>REST example using render chi v2<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"time"

	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
	"github.com/pressly/chi/render"
)

func main() {
	r := chi.NewRouter()

	r.Use(middleware.RequestID)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)

	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("..."))
	})

	r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("pong"))
	})

	r.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
		panic("test")
	})

	// Slow handlers/operations.
	r.Group(func(r chi.Router) {
		// Stop processing when client disconnects.
		r.Use(middleware.CloseNotify)

		// Stop processing after 2.5 seconds.
		r.Use(middleware.Timeout(2500 * time.Millisecond))

		r.Get("/slow", func(w http.ResponseWriter, r *http.Request) {
			rand.Seed(time.Now().Unix())

			// Processing will take 1-5 seconds.
			processTime := time.Duration(rand.Intn(4)+1) * time.Second

			select {
			case <-r.Context().Done():
				return

			case <-time.After(processTime):
				// The above channel simulates some hard work.
			}

			w.Write([]byte(fmt.Sprintf("Processed in %v seconds\n", processTime)))
		})
	})

	// Throttle very expensive handlers/operations.
	r.Group(func(r chi.Router) {
		// Stop processing after 30 seconds.
		r.Use(middleware.Timeout(30 * time.Second))

		// Only one request will be processed at a time.
		r.Use(middleware.Throttle(1))

		r.Get("/throttled", func(w http.ResponseWriter, r *http.Request) {
			select {
			case <-r.Context().Done():
				switch r.Context().Err() {
				case context.DeadlineExceeded:
					w.WriteHeader(504)
					w.Write([]byte("Processing too slow\n"))
				default:
					w.Write([]byte("Canceled\n"))
				}
				return

			case <-time.After(5 * time.Second):
				// The above channel simulates some hard work.
			}

			w.Write([]byte("Processed\n"))
		})
	})

	// RESTy routes for "articles" resource
	r.Route("/articles", func(r chi.Router) {
		r.Get("/", chi.Use(paginate).Handler(listArticles)) // GET /articles
		r.Post("/", createArticle)                          // POST /articles

		r.Route("/:articleID", func(r chi.Router) {
			r.Use(ArticleCtx)
			r.Get("/", getArticle)       // GET /articles/123
			r.Put("/", updateArticle)    // PUT /articles/123
			r.Delete("/", deleteArticle) // DELETE /articles/123
		})
	})

	// Mount the admin sub-router
	r.Mount("/admin", adminRouter())

	http.ListenAndServe(":3333", r)
}

type Article struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}

func ArticleCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		articleID := chi.URLParam(r, "articleID")
		article, err := dbGetArticle(articleID)
		if err != nil {
			http.Error(w, http.StatusText(404), 404)
			return
		}
		ctx := context.WithValue(r.Context(), "article", article)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func listArticles(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("list of articles.."))
	// or render.Data(w, 200, []byte("list of articles.."))
}

func createArticle(w http.ResponseWriter, r *http.Request) {
	var article *Article

	// btw, you could do this body reading / marhsalling in a nice bind middleware
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), 422)
		return
	}
	defer r.Body.Close()

	if err := json.Unmarshal(data, &article); err != nil {
		http.Error(w, err.Error(), 422)
		return
	}

	// should really send back the json marshalled new article.
	// build your own responder :)
	w.Write([]byte(article.Title))
}

func getArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(422), 422)
		return
	}

	// You can use your own responder, see the "github.com/pressly/chi/render"
	// package as a starting point for your own.
	render.JSON(w, r, article)

	// or..
	// w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
}

func updateArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(404), 404)
		return
	}

	// btw, you could do this body reading / marhsalling in a nice bind middleware
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), 422)
		return
	}
	defer r.Body.Close()

	uArticle := struct {
		*Article
		_ interface{} `json:"id,omitempty"` // prevents 'id' from being overridden
	}{Article: article}

	if err := json.Unmarshal(data, &uArticle); err != nil {
		http.Error(w, err.Error(), 422)
		return
	}

	render.JSON(w, r, uArticle)

	// w.Write([]byte(fmt.Sprintf("updated article, title:%s", uArticle.Title)))
}

func deleteArticle(w http.ResponseWriter, r *http.Request) {
	article, ok := r.Context().Value("article").(*Article)
	if !ok {
		http.Error(w, http.StatusText(422), 422)
		return
	}
	_ = article // delete the article from the data store..
	w.WriteHeader(204)
}

func dbGetArticle(id string) (*Article, error) {
	//.. fetch the article from a data store of some kind..
	return &Article{ID: id, Title: "Going all the way,"}, nil
}

func paginate(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// just a stub.. some ideas are to look at URL query params for something like
		// the page number, or the limit, and send a query cursor down the chain
		next.ServeHTTP(w, r)
	})
}

// A completely separate router for administrator routes
func adminRouter() chi.Router {
	r := chi.NewRouter()
	r.Use(AdminOnly)
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("admin: index"))
	})
	r.Get("/accounts", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("admin: list accounts.."))
	})
	r.Get("/users/:userId", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParam(r, "userId"))))
	})
	return r
}

func AdminOnly(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		isAdmin, ok := r.Context().Value("acl.admin").(bool)
		if !ok || !isAdmin {
			http.Error(w, http.StatusText(403), 403)
			return
		}
		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package dns

import (
	"regexp"
)

// IPLocalhost is a regex patter for localhost IP address range.
const IPLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))`

var localhostIPRegexp = regexp.MustCompile(IPLocalhost)

// IsLocalhost returns true if ip matches the localhost IP regular expression.
// Used for determining if nameserver settings are being passed which are
// localhost addresses
func IsLocalhost(ip string) bool {
	return localhostIPRegexp.MatchString(ip)
}
<commit_msg>fix the regexp for matching an ip address<commit_after>package dns

import (
	"regexp"
)

// IPLocalhost is a regex patter for localhost IP address range.
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1))`

var localhostIPRegexp = regexp.MustCompile(IPLocalhost)

// IsLocalhost returns true if ip matches the localhost IP regular expression.
// Used for determining if nameserver settings are being passed which are
// localhost addresses
func IsLocalhost(ip string) bool {
	return localhostIPRegexp.MatchString(ip)
}
<|endoftext|>


<commit_before>package readline

import (
	"context"
	"fmt"
	"io"
	"strings"
	"unicode"
	"unicode/utf8"
)

func keyFuncIncSearch(ctx context.Context, this *Buffer) Result {
	var searchBuf strings.Builder
	foundStr := ""
	searchStr := ""
	lastFoundPos := this.History.Len() - 1
	this.Backspace(this.Cursor - this.ViewStart)

	update := func() {
		for i := this.History.Len() - 1; ; i-- {
			if i < 0 {
				foundStr = ""
				break
			}
			line := this.History.At(i)
			if strings.Contains(line, searchStr) {
				foundStr = line
				lastFoundPos = i
				break
			}
		}
	}
	for {
		drawStr := fmt.Sprintf("(i-search)[%s]:%s", searchStr, foundStr)
		drawWidth := 0
		for _, ch := range drawStr {
			w1 := GetCharWidth(ch)
			if drawWidth+w1 >= this.ViewWidth() {
				break
			}
			this.PutRune(ch)
			drawWidth += w1
		}
		this.Eraseline()
		io.WriteString(this.Out, ansiCursorOn)
		this.Out.Flush()
		key, err := getKey(this.TTY)
		if err != nil {
			println(err.Error())
			return CONTINUE
		}
		io.WriteString(this.Out, ansiCursorOff)
		this.Backspace(drawWidth)
		switch key {
		case "\b":
			searchBuf.Reset()
			// chop last char
			var lastchar rune
			for i, c := range searchStr {
				if i > 0 {
					searchBuf.WriteRune(lastchar)
				}
				lastchar = c
			}
			searchStr = searchBuf.String()
			update()
		case "\r":
			this.ViewStart = 0
			this.Length = 0
			this.Cursor = 0
			this.ReplaceAndRepaint(0, foundStr)
			return CONTINUE
		case "\x03", "\x07", "\x1B":
			w := 0
			var i int
			for i = this.ViewStart; i < this.Cursor; i++ {
				w += GetCharWidth(this.Buffer[i])
				this.PutRune(this.Buffer[i])
			}
			bs := 0
			for {
				if i >= this.Length {
					if drawWidth > w {
						this.PutRunes(' ', drawWidth-w)
						bs += (drawWidth - w)
					}
					break
				}
				w1 := GetCharWidth(this.Buffer[i])
				if w+w1 >= this.ViewWidth() {
					break
				}
				this.PutRune(this.Buffer[i])
				w += w1
				bs += w1
				i++
			}
			this.Backspace(bs)
			return CONTINUE
		case "\x12":
			for i := lastFoundPos - 1; ; i-- {
				if i < 0 {
					i = this.History.Len() - 1
				}
				if i == lastFoundPos {
					break
				}
				line := this.History.At(i)
				if strings.Contains(line, searchStr) && foundStr != line {
					foundStr = line
					lastFoundPos = i
					break
				}
			}
		case "\x13":
			for i := lastFoundPos + 1; ; i++ {
				if i >= this.History.Len() {
					break
				}
				if i == lastFoundPos {
					break
				}
				line := this.History.At(i)
				if strings.Contains(line, searchStr) && foundStr != line {
					foundStr = line
					lastFoundPos = i
					break
				}
			}
		default:
			charcode, _ := utf8.DecodeRuneInString(key)
			if unicode.IsControl(charcode) {
				break
			}
			searchBuf.WriteRune(charcode)
			searchStr = searchBuf.String()
			update()
		}
	}
}
<commit_msg>Fix: wrong the count of backspaces to move top on starting incremental-search<commit_after>package readline

import (
	"context"
	"fmt"
	"io"
	"strings"
	"unicode"
	"unicode/utf8"
)

func keyFuncIncSearch(ctx context.Context, this *Buffer) Result {
	var searchBuf strings.Builder
	foundStr := ""
	searchStr := ""
	lastFoundPos := this.History.Len() - 1
	this.Backspace(this.GetWidthBetween(this.ViewStart, this.Cursor))

	update := func() {
		for i := this.History.Len() - 1; ; i-- {
			if i < 0 {
				foundStr = ""
				break
			}
			line := this.History.At(i)
			if strings.Contains(line, searchStr) {
				foundStr = line
				lastFoundPos = i
				break
			}
		}
	}
	for {
		drawStr := fmt.Sprintf("(i-search)[%s]:%s", searchStr, foundStr)
		drawWidth := 0
		for _, ch := range drawStr {
			w1 := GetCharWidth(ch)
			if drawWidth+w1 >= this.ViewWidth() {
				break
			}
			this.PutRune(ch)
			drawWidth += w1
		}
		this.Eraseline()
		io.WriteString(this.Out, ansiCursorOn)
		this.Out.Flush()
		key, err := getKey(this.TTY)
		if err != nil {
			println(err.Error())
			return CONTINUE
		}
		io.WriteString(this.Out, ansiCursorOff)
		this.Backspace(drawWidth)
		switch key {
		case "\b":
			searchBuf.Reset()
			// chop last char
			var lastchar rune
			for i, c := range searchStr {
				if i > 0 {
					searchBuf.WriteRune(lastchar)
				}
				lastchar = c
			}
			searchStr = searchBuf.String()
			update()
		case "\r":
			this.ViewStart = 0
			this.Length = 0
			this.Cursor = 0
			this.ReplaceAndRepaint(0, foundStr)
			return CONTINUE
		case "\x03", "\x07", "\x1B":
			w := 0
			var i int
			for i = this.ViewStart; i < this.Cursor; i++ {
				w += GetCharWidth(this.Buffer[i])
				this.PutRune(this.Buffer[i])
			}
			bs := 0
			for {
				if i >= this.Length {
					if drawWidth > w {
						this.PutRunes(' ', drawWidth-w)
						bs += (drawWidth - w)
					}
					break
				}
				w1 := GetCharWidth(this.Buffer[i])
				if w+w1 >= this.ViewWidth() {
					break
				}
				this.PutRune(this.Buffer[i])
				w += w1
				bs += w1
				i++
			}
			this.Backspace(bs)
			return CONTINUE
		case "\x12":
			for i := lastFoundPos - 1; ; i-- {
				if i < 0 {
					i = this.History.Len() - 1
				}
				if i == lastFoundPos {
					break
				}
				line := this.History.At(i)
				if strings.Contains(line, searchStr) && foundStr != line {
					foundStr = line
					lastFoundPos = i
					break
				}
			}
		case "\x13":
			for i := lastFoundPos + 1; ; i++ {
				if i >= this.History.Len() {
					break
				}
				if i == lastFoundPos {
					break
				}
				line := this.History.At(i)
				if strings.Contains(line, searchStr) && foundStr != line {
					foundStr = line
					lastFoundPos = i
					break
				}
			}
		default:
			charcode, _ := utf8.DecodeRuneInString(key)
			if unicode.IsControl(charcode) {
				break
			}
			searchBuf.WriteRune(charcode)
			searchStr = searchBuf.String()
			update()
		}
	}
}
<|endoftext|>


<commit_before>package container_runtime

import (
	"context"
	"fmt"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"

	"github.com/werf/logboek"

	"github.com/werf/werf/pkg/docker"
)

type ContainerRuntime interface {
	RefreshImageObject(ctx context.Context, img Image) error
	PullImageFromRegistry(ctx context.Context, img Image) error
	RenameImage(ctx context.Context, img Image, newImageName string, removeOldName bool) error
	RemoveImage(ctx context.Context, img Image) error
	String() string
}

type LocalDockerServerRuntime struct{}

// GetImageInspect only avaliable for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) GetImageInspect(ctx context.Context, ref string) (*types.ImageInspect, error) {
	inspect, err := docker.ImageInspect(ctx, ref)
	if client.IsErrNotFound(err) {
		return nil, nil
	}
	return inspect, err
}

// PullImage only avaliable for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) PullImage(ctx context.Context, ref string) error {
	if err := docker.CliPull(ctx, ref); err != nil {
		return fmt.Errorf("unable to pull image %s: %s", ref, err)
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) RefreshImageObject(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if inspect, err := runtime.GetImageInspect(ctx, dockerImage.Image.Name()); err != nil {
		return err
	} else {
		dockerImage.Image.SetInspect(inspect)
	}
	return nil
}

func (runtime *LocalDockerServerRuntime) RenameImage(ctx context.Context, img Image, newImageName string, removeOldName bool) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Tagging image %s by name %s", dockerImage.Image.Name(), newImageName)).DoError(func() error {
		if err := docker.CliTag(ctx, dockerImage.Image.Name(), newImageName); err != nil {
			return fmt.Errorf("unable to tag image %s by name %s: %s", dockerImage.Image.Name(), newImageName, err)
		}
		return nil
	}); err != nil {
		return err
	}

	if removeOldName {
		if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Removing old image tag %s", dockerImage.Image.Name())).DoError(func() error {
			if err := docker.CliRmi(ctx, dockerImage.Image.Name()); err != nil {
				return err
			}
			return nil
		}); err != nil {
			return err
		}
	}

	dockerImage.Image.SetName(newImageName)

	return nil
}

func (runtime *LocalDockerServerRuntime) RemoveImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Removing image tag %s", dockerImage.Image.Name())).DoError(func() error {
		if err := docker.CliRmi(ctx, dockerImage.Image.Name()); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) PullImageFromRegistry(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := dockerImage.Image.Pull(ctx); err != nil {
		return fmt.Errorf("unable to export image %s: %s", dockerImage.Image.Name(), err)
	}

	if inspect, err := runtime.GetImageInspect(ctx, dockerImage.Image.Name()); err != nil {
		return fmt.Errorf("unable to get inspect of image %s: %s", dockerImage.Image.Name(), err)
	} else {
		dockerImage.Image.SetInspect(inspect)
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) PushImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Pushing %s", dockerImage.Image.Name())).DoError(func() error {
		return docker.CliPushWithRetries(ctx, dockerImage.Image.Name())
	}); err != nil {
		return err
	}

	return nil
}

// PushBuiltImage is only available for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) PushBuiltImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Tagging built image by name %s", dockerImage.Image.Name())).DoError(func() error {
		if err := dockerImage.Image.TagBuiltImage(ctx, dockerImage.Image.Name()); err != nil {
			return fmt.Errorf("unable to tag built image by name %s: %s", dockerImage.Image.Name(), err)
		}
		return nil
	}); err != nil {
		return err
	}

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Pushing %s", dockerImage.Image.Name())).DoError(func() error {
		return docker.CliPushWithRetries(ctx, dockerImage.Image.Name())
	}); err != nil {
		return err
	}

	return nil
}

// TagBuiltImageByName is only available for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) TagBuiltImageByName(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := dockerImage.Image.TagBuiltImage(ctx, dockerImage.Image.Name()); err != nil {
		return fmt.Errorf("unable to tag image %s: %s", dockerImage.Image.Name(), err)
	}
	return nil
}

func (runtime *LocalDockerServerRuntime) String() string {
	return "local-docker-server"
}

type LocalHostRuntime struct {
	ContainerRuntime // TODO: kaniko-like builds
}

func (runtime *LocalHostRuntime) String() string {
	return "localhost"
}
<commit_msg>Update pkg/container_runtime/container_runtime.go<commit_after>package container_runtime

import (
	"context"
	"fmt"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"

	"github.com/werf/logboek"

	"github.com/werf/werf/pkg/docker"
)

type ContainerRuntime interface {
	RefreshImageObject(ctx context.Context, img Image) error
	PullImageFromRegistry(ctx context.Context, img Image) error
	RenameImage(ctx context.Context, img Image, newImageName string, removeOldName bool) error
	RemoveImage(ctx context.Context, img Image) error
	String() string
}

type LocalDockerServerRuntime struct{}

// GetImageInspect only available for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) GetImageInspect(ctx context.Context, ref string) (*types.ImageInspect, error) {
	inspect, err := docker.ImageInspect(ctx, ref)
	if client.IsErrNotFound(err) {
		return nil, nil
	}
	return inspect, err
}

// PullImage only avaliable for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) PullImage(ctx context.Context, ref string) error {
	if err := docker.CliPull(ctx, ref); err != nil {
		return fmt.Errorf("unable to pull image %s: %s", ref, err)
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) RefreshImageObject(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if inspect, err := runtime.GetImageInspect(ctx, dockerImage.Image.Name()); err != nil {
		return err
	} else {
		dockerImage.Image.SetInspect(inspect)
	}
	return nil
}

func (runtime *LocalDockerServerRuntime) RenameImage(ctx context.Context, img Image, newImageName string, removeOldName bool) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Tagging image %s by name %s", dockerImage.Image.Name(), newImageName)).DoError(func() error {
		if err := docker.CliTag(ctx, dockerImage.Image.Name(), newImageName); err != nil {
			return fmt.Errorf("unable to tag image %s by name %s: %s", dockerImage.Image.Name(), newImageName, err)
		}
		return nil
	}); err != nil {
		return err
	}

	if removeOldName {
		if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Removing old image tag %s", dockerImage.Image.Name())).DoError(func() error {
			if err := docker.CliRmi(ctx, dockerImage.Image.Name()); err != nil {
				return err
			}
			return nil
		}); err != nil {
			return err
		}
	}

	dockerImage.Image.SetName(newImageName)

	return nil
}

func (runtime *LocalDockerServerRuntime) RemoveImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Removing image tag %s", dockerImage.Image.Name())).DoError(func() error {
		if err := docker.CliRmi(ctx, dockerImage.Image.Name()); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) PullImageFromRegistry(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := dockerImage.Image.Pull(ctx); err != nil {
		return fmt.Errorf("unable to export image %s: %s", dockerImage.Image.Name(), err)
	}

	if inspect, err := runtime.GetImageInspect(ctx, dockerImage.Image.Name()); err != nil {
		return fmt.Errorf("unable to get inspect of image %s: %s", dockerImage.Image.Name(), err)
	} else {
		dockerImage.Image.SetInspect(inspect)
	}

	return nil
}

func (runtime *LocalDockerServerRuntime) PushImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Pushing %s", dockerImage.Image.Name())).DoError(func() error {
		return docker.CliPushWithRetries(ctx, dockerImage.Image.Name())
	}); err != nil {
		return err
	}

	return nil
}

// PushBuiltImage is only available for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) PushBuiltImage(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Tagging built image by name %s", dockerImage.Image.Name())).DoError(func() error {
		if err := dockerImage.Image.TagBuiltImage(ctx, dockerImage.Image.Name()); err != nil {
			return fmt.Errorf("unable to tag built image by name %s: %s", dockerImage.Image.Name(), err)
		}
		return nil
	}); err != nil {
		return err
	}

	if err := logboek.Context(ctx).Info().LogProcess(fmt.Sprintf("Pushing %s", dockerImage.Image.Name())).DoError(func() error {
		return docker.CliPushWithRetries(ctx, dockerImage.Image.Name())
	}); err != nil {
		return err
	}

	return nil
}

// TagBuiltImageByName is only available for LocalDockerServerRuntime
func (runtime *LocalDockerServerRuntime) TagBuiltImageByName(ctx context.Context, img Image) error {
	dockerImage := img.(*DockerImage)

	if err := dockerImage.Image.TagBuiltImage(ctx, dockerImage.Image.Name()); err != nil {
		return fmt.Errorf("unable to tag image %s: %s", dockerImage.Image.Name(), err)
	}
	return nil
}

func (runtime *LocalDockerServerRuntime) String() string {
	return "local-docker-server"
}

type LocalHostRuntime struct {
	ContainerRuntime // TODO: kaniko-like builds
}

func (runtime *LocalHostRuntime) String() string {
	return "localhost"
}
<|endoftext|>


<commit_before>package plugincontext

import (
	"context"
	"encoding/json"
	"errors"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/backend"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/infra/localcache"
	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	"github.com/grafana/grafana/pkg/plugins/adapters"
	"github.com/grafana/grafana/pkg/services/datasources"
	"github.com/grafana/grafana/pkg/services/pluginsettings"
	"github.com/grafana/grafana/pkg/services/secrets"
	"github.com/grafana/grafana/pkg/util/errutil"
)

func ProvideService(bus bus.Bus, cacheService *localcache.CacheService, pluginStore plugins.Store,
	dataSourceCache datasources.CacheService, secretsService secrets.Service,
	pluginSettingsService *pluginsettings.Service) *Provider {
	return &Provider{
		Bus:                   bus,
		CacheService:          cacheService,
		pluginStore:           pluginStore,
		DataSourceCache:       dataSourceCache,
		SecretsService:        secretsService,
		PluginSettingsService: pluginSettingsService,
		logger:                log.New("plugincontext"),
	}
}

type Provider struct {
	Bus                   bus.Bus
	CacheService          *localcache.CacheService
	pluginStore           plugins.Store
	DataSourceCache       datasources.CacheService
	SecretsService        secrets.Service
	PluginSettingsService *pluginsettings.Service
	logger                log.Logger
}

// Get allows getting plugin context by its ID. If datasourceUID is not empty string
// then PluginContext.DataSourceInstanceSettings will be resolved and appended to
// returned context.
func (p *Provider) Get(ctx context.Context, pluginID string, datasourceUID string, user *models.SignedInUser, skipCache bool) (backend.PluginContext, bool, error) {
	pc := backend.PluginContext{}
	plugin, exists := p.pluginStore.Plugin(ctx, pluginID)
	if !exists {
		return pc, false, nil
	}

	jsonData := json.RawMessage{}
	decryptedSecureJSONData := map[string]string{}
	var updated time.Time

	ps, err := p.getCachedPluginSettings(ctx, pluginID, user)
	if err != nil {
		// models.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin).
		// If it's not this expected error something is wrong with cache or database and we return the error to the client.
		if !errors.Is(err, models.ErrPluginSettingNotFound) {
			return pc, false, errutil.Wrap("Failed to get plugin settings", err)
		}
	} else {
		jsonData, err = json.Marshal(ps.JsonData)
		if err != nil {
			return pc, false, errutil.Wrap("Failed to unmarshal plugin json data", err)
		}
		decryptedSecureJSONData = p.PluginSettingsService.DecryptedValues(ps)
		updated = ps.Updated
	}

	pCtx := backend.PluginContext{
		OrgID:    user.OrgId,
		PluginID: plugin.ID,
		User:     adapters.BackendUserFromSignedInUser(user),
		AppInstanceSettings: &backend.AppInstanceSettings{
			JSONData:                jsonData,
			DecryptedSecureJSONData: decryptedSecureJSONData,
			Updated:                 updated,
		},
	}

	if datasourceUID != "" {
		ds, err := p.DataSourceCache.GetDatasourceByUID(ctx, datasourceUID, user, skipCache)
		if err != nil {
			return pc, false, errutil.Wrap("Failed to get datasource", err)
		}
		datasourceSettings, err := adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn())
		if err != nil {
			return pc, false, errutil.Wrap("Failed to convert datasource", err)
		}
		pCtx.DataSourceInstanceSettings = datasourceSettings
	}

	return pCtx, true, nil
}

const pluginSettingsCacheTTL = 5 * time.Second
const pluginSettingsCachePrefix = "plugin-setting-"

func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, user *models.SignedInUser) (*models.PluginSetting, error) {
	cacheKey := pluginSettingsCachePrefix + pluginID

	if cached, found := p.CacheService.Get(cacheKey); found {
		ps := cached.(*models.PluginSetting)
		if ps.OrgId == user.OrgId {
			return ps, nil
		}
	}

	query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: user.OrgId}
	if err := p.Bus.Dispatch(ctx, &query); err != nil {
		return nil, err
	}

	p.CacheService.Set(cacheKey, query.Result, pluginSettingsCacheTTL)
	return query.Result, nil
}

func (p *Provider) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string {
	return func(m map[string][]byte) map[string]string {
		decryptedJsonData, err := p.SecretsService.DecryptJsonData(context.Background(), m)
		if err != nil {
			p.logger.Error("Failed to decrypt secure json data", "error", err)
		}
		return decryptedJsonData
	}
}
<commit_msg>Chore: Remove bus from plugin context service (#45633)<commit_after>package plugincontext

import (
	"context"
	"encoding/json"
	"errors"
	"time"

	"github.com/grafana/grafana-plugin-sdk-go/backend"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/infra/localcache"
	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/plugins"
	"github.com/grafana/grafana/pkg/plugins/adapters"
	"github.com/grafana/grafana/pkg/services/datasources"
	"github.com/grafana/grafana/pkg/services/pluginsettings"
	"github.com/grafana/grafana/pkg/services/secrets"
	"github.com/grafana/grafana/pkg/util/errutil"
)

func ProvideService(bus bus.Bus, cacheService *localcache.CacheService, pluginStore plugins.Store,
	dataSourceCache datasources.CacheService, secretsService secrets.Service,
	pluginSettingsService *pluginsettings.Service) *Provider {
	return &Provider{
		Bus:                   bus,
		CacheService:          cacheService,
		pluginStore:           pluginStore,
		DataSourceCache:       dataSourceCache,
		SecretsService:        secretsService,
		PluginSettingsService: pluginSettingsService,
		logger:                log.New("plugincontext"),
	}
}

type Provider struct {
	Bus                   bus.Bus
	CacheService          *localcache.CacheService
	pluginStore           plugins.Store
	DataSourceCache       datasources.CacheService
	SecretsService        secrets.Service
	PluginSettingsService *pluginsettings.Service
	logger                log.Logger
}

// Get allows getting plugin context by its ID. If datasourceUID is not empty string
// then PluginContext.DataSourceInstanceSettings will be resolved and appended to
// returned context.
func (p *Provider) Get(ctx context.Context, pluginID string, datasourceUID string, user *models.SignedInUser, skipCache bool) (backend.PluginContext, bool, error) {
	pc := backend.PluginContext{}
	plugin, exists := p.pluginStore.Plugin(ctx, pluginID)
	if !exists {
		return pc, false, nil
	}

	jsonData := json.RawMessage{}
	decryptedSecureJSONData := map[string]string{}
	var updated time.Time

	ps, err := p.getCachedPluginSettings(ctx, pluginID, user)
	if err != nil {
		// models.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin).
		// If it's not this expected error something is wrong with cache or database and we return the error to the client.
		if !errors.Is(err, models.ErrPluginSettingNotFound) {
			return pc, false, errutil.Wrap("Failed to get plugin settings", err)
		}
	} else {
		jsonData, err = json.Marshal(ps.JsonData)
		if err != nil {
			return pc, false, errutil.Wrap("Failed to unmarshal plugin json data", err)
		}
		decryptedSecureJSONData = p.PluginSettingsService.DecryptedValues(ps)
		updated = ps.Updated
	}

	pCtx := backend.PluginContext{
		OrgID:    user.OrgId,
		PluginID: plugin.ID,
		User:     adapters.BackendUserFromSignedInUser(user),
		AppInstanceSettings: &backend.AppInstanceSettings{
			JSONData:                jsonData,
			DecryptedSecureJSONData: decryptedSecureJSONData,
			Updated:                 updated,
		},
	}

	if datasourceUID != "" {
		ds, err := p.DataSourceCache.GetDatasourceByUID(ctx, datasourceUID, user, skipCache)
		if err != nil {
			return pc, false, errutil.Wrap("Failed to get datasource", err)
		}
		datasourceSettings, err := adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn())
		if err != nil {
			return pc, false, errutil.Wrap("Failed to convert datasource", err)
		}
		pCtx.DataSourceInstanceSettings = datasourceSettings
	}

	return pCtx, true, nil
}

const pluginSettingsCacheTTL = 5 * time.Second
const pluginSettingsCachePrefix = "plugin-setting-"

func (p *Provider) getCachedPluginSettings(ctx context.Context, pluginID string, user *models.SignedInUser) (*models.PluginSetting, error) {
	cacheKey := pluginSettingsCachePrefix + pluginID

	if cached, found := p.CacheService.Get(cacheKey); found {
		ps := cached.(*models.PluginSetting)
		if ps.OrgId == user.OrgId {
			return ps, nil
		}
	}

	query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: user.OrgId}
	if err := p.PluginSettingsService.GetPluginSettingById(ctx, &query); err != nil {
		return nil, err
	}

	p.CacheService.Set(cacheKey, query.Result, pluginSettingsCacheTTL)
	return query.Result, nil
}

func (p *Provider) decryptSecureJsonDataFn() func(map[string][]byte) map[string]string {
	return func(m map[string][]byte) map[string]string {
		decryptedJsonData, err := p.SecretsService.DecryptJsonData(context.Background(), m)
		if err != nil {
			p.logger.Error("Failed to decrypt secure json data", "error", err)
		}
		return decryptedJsonData
	}
}
<|endoftext|>


<commit_before>package acceptance_test

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"time"

	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
	"github.com/cloudfoundry/bosh-bootloader/acceptance-tests/actors"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Upgrade", func() {
	var (
		oldBBL  actors.BBL
		newBBL  actors.BBL
		boshcli actors.BOSHCLI

		sshSession *gexec.Session
		f          *os.File
	)

	BeforeEach(func() {
		acceptance.SkipUnless("upgrade")

		configuration, err := acceptance.LoadConfig()
		Expect(err).NotTo(HaveOccurred())

		var bblBinaryLocation string
		if runtime.GOOS == "darwin" {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v5.11.5/bbl-v5.11.5_osx"
		} else {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v5.11.5/bbl-v5.11.5_linux_x86-64"
		}

		resp, err := http.Get(bblBinaryLocation)
		Expect(err).NotTo(HaveOccurred())

		f, err = ioutil.TempFile("", "")
		Expect(err).NotTo(HaveOccurred())

		_, err = io.Copy(f, resp.Body)
		Expect(err).NotTo(HaveOccurred())

		err = os.Chmod(f.Name(), 0700)
		Expect(err).NotTo(HaveOccurred())

		err = f.Close()
		Expect(err).NotTo(HaveOccurred())

		envName := "upgrade-env"
		testName := os.Getenv("RUN_TEST")
		if testName != "" {
			envName = testName
		}
		oldBBL = actors.NewBBL(configuration.StateFileDir, f.Name(), configuration, envName)
		newBBL = actors.NewBBL(configuration.StateFileDir, pathToBBL, configuration, envName)
		boshcli = actors.NewBOSHCLI()
	})

	AfterEach(func() {
		acceptance.SkipUnless("upgrade")

		if sshSession != nil {
			sshSession.Interrupt()
			Eventually(sshSession, "5s").Should(gexec.Exit())
		}

		By("trying to destroy with the old bbl", func() {
			session := oldBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("trying to destroy with the latest bbl", func() {
			session := newBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		err := os.Remove(f.Name())
		Expect(err).NotTo(HaveOccurred())
	})

	It("is able to upgrade from an environment bbl'd up with an older version of bbl", func() {
		By("cleaning up any leftovers", func() {
			session := newBBL.CleanupLeftovers(newBBL.PredefinedEnvID())
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("bbl'ing up with old bbl", func() {
			session := oldBBL.Up("--name", oldBBL.PredefinedEnvID())
			Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
		})

		By("verifying the director has a private ip", func() {
			Expect(oldBBL.DirectorAddress()).To(Equal("https://10.0.0.6:25555"))
		})

		By("starting an ssh tunnel to talk to the director", func() {
			sshSession = oldBBL.StartSSHTunnel()
		})

		By("verifying the director exists", func() {
			exists, err := boshcli.DirectorExists(oldBBL.DirectorAddress(), oldBBL.DirectorUsername(), oldBBL.DirectorPassword(), oldBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})

		By("upgrading to the latest bbl", func() {
			session := newBBL.Plan()
			Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

			session = newBBL.Up()
			Eventually(session, 20*time.Minute).Should(gexec.Exit(0))
		})

		By("exporting BOSH_ALL_PROXY to talk to the director", func() {
			newBBL.ExportBoshAllProxy()
		})

		By("verifying the director still exists", func() {
			exists, err := boshcli.DirectorExists(newBBL.DirectorAddress(), newBBL.DirectorUsername(), newBBL.DirectorPassword(), newBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})
	})
})
<commit_msg>Extend bbl up on upgrade time.<commit_after>package acceptance_test

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"time"

	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
	"github.com/cloudfoundry/bosh-bootloader/acceptance-tests/actors"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Upgrade", func() {
	var (
		oldBBL  actors.BBL
		newBBL  actors.BBL
		boshcli actors.BOSHCLI

		sshSession *gexec.Session
		f          *os.File
	)

	BeforeEach(func() {
		acceptance.SkipUnless("upgrade")

		configuration, err := acceptance.LoadConfig()
		Expect(err).NotTo(HaveOccurred())

		var bblBinaryLocation string
		if runtime.GOOS == "darwin" {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v5.11.5/bbl-v5.11.5_osx"
		} else {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v5.11.5/bbl-v5.11.5_linux_x86-64"
		}

		resp, err := http.Get(bblBinaryLocation)
		Expect(err).NotTo(HaveOccurred())

		f, err = ioutil.TempFile("", "")
		Expect(err).NotTo(HaveOccurred())

		_, err = io.Copy(f, resp.Body)
		Expect(err).NotTo(HaveOccurred())

		err = os.Chmod(f.Name(), 0700)
		Expect(err).NotTo(HaveOccurred())

		err = f.Close()
		Expect(err).NotTo(HaveOccurred())

		envName := "upgrade-env"
		testName := os.Getenv("RUN_TEST")
		if testName != "" {
			envName = testName
		}
		oldBBL = actors.NewBBL(configuration.StateFileDir, f.Name(), configuration, envName)
		newBBL = actors.NewBBL(configuration.StateFileDir, pathToBBL, configuration, envName)
		boshcli = actors.NewBOSHCLI()
	})

	AfterEach(func() {
		acceptance.SkipUnless("upgrade")

		if sshSession != nil {
			sshSession.Interrupt()
			Eventually(sshSession, "5s").Should(gexec.Exit())
		}

		By("trying to destroy with the old bbl", func() {
			session := oldBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("trying to destroy with the latest bbl", func() {
			session := newBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		err := os.Remove(f.Name())
		Expect(err).NotTo(HaveOccurred())
	})

	It("is able to upgrade from an environment bbl'd up with an older version of bbl", func() {
		By("cleaning up any leftovers", func() {
			session := newBBL.CleanupLeftovers(newBBL.PredefinedEnvID())
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("bbl'ing up with old bbl", func() {
			session := oldBBL.Up("--name", oldBBL.PredefinedEnvID())
			Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
		})

		By("verifying the director has a private ip", func() {
			Expect(oldBBL.DirectorAddress()).To(Equal("https://10.0.0.6:25555"))
		})

		By("starting an ssh tunnel to talk to the director", func() {
			sshSession = oldBBL.StartSSHTunnel()
		})

		By("verifying the director exists", func() {
			exists, err := boshcli.DirectorExists(oldBBL.DirectorAddress(), oldBBL.DirectorUsername(), oldBBL.DirectorPassword(), oldBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})

		By("upgrading to the latest bbl", func() {
			session := newBBL.Plan()
			Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

			session = newBBL.Up()
			Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
		})

		By("exporting BOSH_ALL_PROXY to talk to the director", func() {
			newBBL.ExportBoshAllProxy()
		})

		By("verifying the director still exists", func() {
			exists, err := boshcli.DirectorExists(newBBL.DirectorAddress(), newBBL.DirectorUsername(), newBBL.DirectorPassword(), newBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})
	})
})
<|endoftext|>


<commit_before>package acceptance_test

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"time"

	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
	"github.com/cloudfoundry/bosh-bootloader/acceptance-tests/actors"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Upgrade", func() {
	var (
		oldBBL     actors.BBL
		newBBL     actors.BBL
		aws        actors.AWS
		boshcli    actors.BOSHCLI
		state      acceptance.State
		sshSession *gexec.Session
		f          *os.File
	)

	BeforeEach(func() {
		acceptance.SkipUnless("upgrade")

		configuration, err := acceptance.LoadConfig()
		Expect(err).NotTo(HaveOccurred())

		var bblBinaryLocation string
		if runtime.GOOS == "darwin" {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v4.10.3/bbl-v4.10.3_osx"
		} else {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v4.10.3/bbl-v4.10.3_linux_x86-64"
		}

		resp, err := http.Get(bblBinaryLocation)
		Expect(err).NotTo(HaveOccurred())

		f, err = ioutil.TempFile("", "")
		Expect(err).NotTo(HaveOccurred())

		_, err = io.Copy(f, resp.Body)
		Expect(err).NotTo(HaveOccurred())

		err = os.Chmod(f.Name(), 0700)
		Expect(err).NotTo(HaveOccurred())

		err = f.Close()
		Expect(err).NotTo(HaveOccurred())

		envName := "upgrade-env"
		testName := os.Getenv("RUN_TEST")
		if testName != "" {
			envName = testName
		}
		oldBBL = actors.NewBBL(configuration.StateFileDir, f.Name(), configuration, envName)
		newBBL = actors.NewBBL(configuration.StateFileDir, pathToBBL, configuration, envName)
		aws = actors.NewAWS(configuration)
		boshcli = actors.NewBOSHCLI()
		state = acceptance.NewState(configuration.StateFileDir)
	})

	AfterEach(func() {
		By("destroying with the old bbl", func() {
			session := oldBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("destroying with the latest bbl", func() {
			if sshSession != nil {
				sshSession.Interrupt()
				Eventually(sshSession, "5s").Should(gexec.Exit())
			}
			session := newBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		err := os.Remove(f.Name())
		Expect(err).NotTo(HaveOccurred())
	})

	Describe("Up", func() {
		It("is able to bbl up idempotently with a director", func() {
			By("bbl'ing up with old bbl", func() {
				session := oldBBL.Up("--name", oldBBL.PredefinedEnvID())
				Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
			})

			By("verifying the director has a public ip", func() {
				Expect(oldBBL.DirectorAddress()).NotTo(Equal("https://10.0.0.6:25555"))
			})

			By("verifying the director exists", func() {
				exists, err := boshcli.DirectorExists(oldBBL.DirectorAddress(), oldBBL.SaveDirectorCA())
				Expect(err).NotTo(HaveOccurred())
				Expect(exists).To(BeTrue())
			})

			By("upgrading to the latest bbl", func() {
				session := newBBL.Up()
				Eventually(session, 60*time.Minute).Should(gexec.Exit(0))
			})

			By("creating an ssh tunnel to the director in print-env", func() {
				sshSession = newBBL.StartSSHTunnel()
			})

			By("verifying the director has a non-public ip", func() {
				Expect(newBBL.DirectorAddress()).To(Equal("https://10.0.0.6:25555"))
			})

			By("verifying the director still exists", func() {
				exists, err := boshcli.DirectorExists(newBBL.DirectorAddress(), newBBL.SaveDirectorCA())
				Expect(err).NotTo(HaveOccurred())
				Expect(exists).To(BeTrue())
			})
		})
	})
})
<commit_msg>Improve wording in upgrade test<commit_after>package acceptance_test

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"time"

	acceptance "github.com/cloudfoundry/bosh-bootloader/acceptance-tests"
	"github.com/cloudfoundry/bosh-bootloader/acceptance-tests/actors"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Upgrade", func() {
	var (
		oldBBL     actors.BBL
		newBBL     actors.BBL
		aws        actors.AWS
		boshcli    actors.BOSHCLI
		state      acceptance.State
		sshSession *gexec.Session
		f          *os.File
	)

	BeforeEach(func() {
		acceptance.SkipUnless("upgrade")

		configuration, err := acceptance.LoadConfig()
		Expect(err).NotTo(HaveOccurred())

		var bblBinaryLocation string
		if runtime.GOOS == "darwin" {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v4.10.3/bbl-v4.10.3_osx"
		} else {
			bblBinaryLocation = "https://github.com/cloudfoundry/bosh-bootloader/releases/download/v4.10.3/bbl-v4.10.3_linux_x86-64"
		}

		resp, err := http.Get(bblBinaryLocation)
		Expect(err).NotTo(HaveOccurred())

		f, err = ioutil.TempFile("", "")
		Expect(err).NotTo(HaveOccurred())

		_, err = io.Copy(f, resp.Body)
		Expect(err).NotTo(HaveOccurred())

		err = os.Chmod(f.Name(), 0700)
		Expect(err).NotTo(HaveOccurred())

		err = f.Close()
		Expect(err).NotTo(HaveOccurred())

		envName := "upgrade-env"
		testName := os.Getenv("RUN_TEST")
		if testName != "" {
			envName = testName
		}
		oldBBL = actors.NewBBL(configuration.StateFileDir, f.Name(), configuration, envName)
		newBBL = actors.NewBBL(configuration.StateFileDir, pathToBBL, configuration, envName)
		aws = actors.NewAWS(configuration)
		boshcli = actors.NewBOSHCLI()
		state = acceptance.NewState(configuration.StateFileDir)
	})

	AfterEach(func() {
		By("destroying with the old bbl", func() {
			session := oldBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		By("destroying with the latest bbl", func() {
			if sshSession != nil {
				sshSession.Interrupt()
				Eventually(sshSession, "5s").Should(gexec.Exit())
			}
			session := newBBL.Destroy()
			Eventually(session, 10*time.Minute).Should(gexec.Exit())
		})

		err := os.Remove(f.Name())
		Expect(err).NotTo(HaveOccurred())
	})

	It("is able to upgrade from an environment bbl'd up with an older version of bbl", func() {
		By("bbl'ing up with old bbl", func() {
			session := oldBBL.Up("--name", oldBBL.PredefinedEnvID())
			Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
		})

		By("verifying the director has a public ip", func() {
			Expect(oldBBL.DirectorAddress()).NotTo(Equal("https://10.0.0.6:25555"))
		})

		By("verifying the director exists", func() {
			exists, err := boshcli.DirectorExists(oldBBL.DirectorAddress(), oldBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})

		By("upgrading to the latest bbl", func() {
			session := newBBL.Up()
			Eventually(session, 60*time.Minute).Should(gexec.Exit(0))
		})

		By("creating an ssh tunnel to the director in print-env", func() {
			sshSession = newBBL.StartSSHTunnel()
		})

		By("verifying the director has a non-public ip", func() {
			Expect(newBBL.DirectorAddress()).To(Equal("https://10.0.0.6:25555"))
		})

		By("verifying the director still exists", func() {
			exists, err := boshcli.DirectorExists(newBBL.DirectorAddress(), newBBL.SaveDirectorCA())
			Expect(err).NotTo(HaveOccurred())
			Expect(exists).To(BeTrue())
		})
	})
})
<|endoftext|>