Skip to content

Commit

Permalink
Merge pull request cloudfoundry-community#42 from elgohr/master
Browse files Browse the repository at this point in the history
Support private Docker repositories
  • Loading branch information
vito authored Nov 29, 2017
2 parents 0a0ad7c + 389bf6f commit ab31085
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ _testmain.go
/test.json
/*.tar.gz
/built*

# GoLand
.idea
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ be specified.
that this will re-deploy over. If this is set the resource will perform a
zero-downtime deploy.
* `environment_variables`: *Optional.* It is not necessary to set the variables in [manifest][cf-manifests] if this parameter is set.
* `docker_username`: *Optional.* This is used as the username to authenticate against a protected docker registry.
* `docker_password`: *Optional.* This should be the users password when authenticating against a protected docker registry.

## Pipeline example

Expand Down
9 changes: 7 additions & 2 deletions out/cloud_foundry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
"os/exec"
)

//go:generate counterfeiter . PAAS
type PAAS interface {
Login(api string, username string, password string, insecure bool) error
Target(organization string, space string) error
PushApp(manifest string, path string, currentAppName string) error
PushApp(manifest string, path string, currentAppName string, dockerUser string) error
}

type CloudFoundry struct{}
Expand All @@ -35,7 +36,7 @@ func (cf *CloudFoundry) Target(organization string, space string) error {
return cf.cf("target", "-o", organization, "-s", space).Run()
}

func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName string) error {
func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName string, dockerUser string) error {
args := []string{}

if currentAppName == "" {
Expand All @@ -44,6 +45,10 @@ func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName str
args = append(args, "zero-downtime-push", currentAppName, "-f", manifest)
}

if dockerUser != "" {
args = append(args, "--docker-username", dockerUser)
}

if path != "" {
stat, err := os.Stat(path)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions out/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"time"

"github.com/concourse/cf-resource"
"os"
)

const CfDockerPassword = "CF_DOCKER_PASSWORD"

type Command struct {
paas PAAS
}
Expand Down Expand Up @@ -39,10 +42,15 @@ func (command *Command) Run(request Request) (Response, error) {
return Response{}, err
}

if request.Params.DockerPassword != "" {
os.Setenv(CfDockerPassword, request.Params.DockerPassword)
}

err = command.paas.PushApp(
request.Params.ManifestPath,
request.Params.Path,
request.Params.CurrentAppName,
request.Params.DockerUsername,
)
if err != nil {
return Response{}, err
Expand Down
107 changes: 102 additions & 5 deletions out/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import (

"github.com/concourse/cf-resource"
"github.com/concourse/cf-resource/out"
"github.com/concourse/cf-resource/out/fakes"
"github.com/concourse/cf-resource/out/outfakes"
"io"
"io/ioutil"
)

var _ = Describe("Out Command", func() {
var (
cloudFoundry *fakes.FakePAAS
cloudFoundry *outfakes.FakePAAS
request out.Request
command *out.Command
)

BeforeEach(func() {
cloudFoundry = &fakes.FakePAAS{}
cloudFoundry = &outfakes.FakePAAS{}
command = out.NewCommand(cloudFoundry)

request = out.Request{
Expand Down Expand Up @@ -78,10 +78,11 @@ var _ = Describe("Out Command", func() {
By("pushing the app")
Expect(cloudFoundry.PushAppCallCount()).To(Equal(1))

manifest, path, currentAppName := cloudFoundry.PushAppArgsForCall(0)
manifest, path, currentAppName, dockerUser := cloudFoundry.PushAppArgsForCall(0)
Expect(manifest).To(Equal("assets/manifest.yml"))
Expect(path).To(Equal(""))
Expect(currentAppName).To(Equal(""))
Expect(dockerUser).To(Equal(""))
})

Describe("handling any errors", func() {
Expand Down Expand Up @@ -219,8 +220,104 @@ var _ = Describe("Out Command", func() {
By("pushing the app")
Expect(cloudFoundry.PushAppCallCount()).To(Equal(1))

_, _, currentAppName := cloudFoundry.PushAppArgsForCall(0)
_, _, currentAppName, _ := cloudFoundry.PushAppArgsForCall(0)
Expect(currentAppName).To(Equal("cool-app-name"))
})

It("lets people define a user for connecting to a docker registry", func() {
request = out.Request{
Source: resource.Source{
API: "https://api.run.pivotal.io",
Username: "awesome@example.com",
Password: "hunter2",
Organization: "secret",
Space: "volcano-base",
},
Params: out.Params{
ManifestPath: "a/path/to/a/manifest.yml",
CurrentAppName: "cool-app-name",
DockerUsername: "DOCKER_USER",
},
}

_, err := command.Run(request)
Expect(err).NotTo(HaveOccurred())

By("pushing the app")
Expect(cloudFoundry.PushAppCallCount()).To(Equal(1))

_, _, _, dockerUser := cloudFoundry.PushAppArgsForCall(0)
Expect(dockerUser).To(Equal("DOCKER_USER"))
})

Context("using a docker registry which requires authentication", func() {

var savedVar string

BeforeEach(func() {
savedVar = saveCurrentVariable()
})

AfterEach(func() {
restoreOldVariable(savedVar)
})

Context("docker password provided", func() {
It("sets the system environment variable", func() {
request = out.Request{
Source: resource.Source{
API: "https://api.run.pivotal.io",
Username: "awesome@example.com",
Password: "hunter2",
Organization: "secret",
Space: "volcano-base",
},
Params: out.Params{
ManifestPath: "a/path/to/a/manifest/using/docker.yml",
DockerPassword: "mySuperSecretPassword",
},
}
_, err := command.Run(request)
Expect(err).NotTo(HaveOccurred())

By("pushing the app")
Expect(os.Getenv(out.CfDockerPassword)).To(Equal("mySuperSecretPassword"))
Expect(cloudFoundry.PushAppCallCount()).To(Equal(1))
})
})

Context("no docker password provided", func() {
It("doesn't set the system environment variable", func() {
request = out.Request{
Source: resource.Source{
API: "https://api.run.pivotal.io",
Username: "awesome@example.com",
Password: "hunter2",
Organization: "secret",
Space: "volcano-base",
},
Params: out.Params{
ManifestPath: "a/path/to/a/manifest/using/docker.yml",
},
}
os.Setenv(out.CfDockerPassword, "MyOwnUntouchedVariable")

_, err := command.Run(request)
Expect(err).NotTo(HaveOccurred())

By("pushing the app")
Expect(os.Getenv(out.CfDockerPassword)).To(Equal("MyOwnUntouchedVariable"))
Expect(cloudFoundry.PushAppCallCount()).To(Equal(1))
})
})
})
})
})

func restoreOldVariable(currentEnvironmentVariable string) error {
return os.Setenv(out.CfDockerPassword, currentEnvironmentVariable)
}

func saveCurrentVariable() string {
return os.Getenv(out.CfDockerPassword)
}
34 changes: 34 additions & 0 deletions out/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,38 @@ var _ = Describe("Out", func() {
})
})
})

Context("when accessing a protected docker registry", func() {
BeforeEach(func() {
request.Params.DockerUsername = "DOCKER_USERNAME"
request.Params.DockerPassword = "DOCKER_PASSWORD"
})

It("pushes an application to cloud foundry", func() {
session, err := gexec.Start(
cmd,
GinkgoWriter,
GinkgoWriter,
)
Expect(err).NotTo(HaveOccurred())

Eventually(session).Should(gexec.Exit(0))

var response out.Response
err = json.Unmarshal(session.Out.Contents(), &response)
Expect(err).NotTo(HaveOccurred())

Expect(response.Version.Timestamp).To(BeTemporally("~", time.Now(), time.Second))

// shim outputs arguments
Expect(session.Err).To(gbytes.Say("cf api https://api.run.pivotal.io --skip-ssl-validation"))
Expect(session.Err).To(gbytes.Say("cf auth awesome@example.com hunter2"))
Expect(session.Err).To(gbytes.Say("cf target -o org -s space"))
Expect(session.Err).To(gbytes.Say("cf zero-downtime-push awesome-app -f %s --docker-username %s",
filepath.Join(tmpDir, "project/manifest.yml"),
request.Params.DockerUsername,
))
Expect(session.Err).To(gbytes.Say("CF_DOCKER_PASSWORD=DOCKER_PASSWORD"))
})
})
})
2 changes: 2 additions & 0 deletions out/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type Params struct {
Path string `json:"path"`
CurrentAppName string `json:"current_app_name"`
EnvironmentVariables map[string]string `json:"environment_variables"`
DockerUsername string `json:"docker_username"`
DockerPassword string `json:"docker_password"`
}

type Response struct {
Expand Down
Loading

0 comments on commit ab31085

Please sign in to comment.