-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into update/pipeline
- Loading branch information
Showing
18 changed files
with
413 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright 2018-2021 the original author or 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 | ||
* | ||
* https://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 libcnb | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/buildpacks/libcnb/internal" | ||
) | ||
|
||
//go:generate mockery --name ExecD --case=underscore | ||
|
||
// ExecD describes an interface for types that follow the Exec.d specification. | ||
// It should return a map of environment variables and their values as output. | ||
type ExecD interface { | ||
Execute() (map[string]string, error) | ||
} | ||
|
||
// RunExecD is called by the main function of a buildpack's execd binary, encompassing multiple execd | ||
// executors in one binary. | ||
func RunExecD(execDMap map[string]ExecD, options ...Option) { | ||
config := Config{ | ||
arguments: os.Args, | ||
execdWriter: internal.NewExecDWriter(), | ||
exitHandler: internal.NewExitHandler(), | ||
} | ||
|
||
for _, option := range options { | ||
config = option(config) | ||
} | ||
|
||
if len(config.arguments) == 0 { | ||
config.exitHandler.Error(fmt.Errorf("expected command name")) | ||
|
||
return | ||
} | ||
|
||
c := filepath.Base(config.arguments[0]) | ||
e, ok := execDMap[c] | ||
if !ok { | ||
config.exitHandler.Error(fmt.Errorf("unsupported command %s", c)) | ||
return | ||
} | ||
|
||
r, err := e.Execute() | ||
if err != nil { | ||
config.exitHandler.Error(err) | ||
return | ||
} | ||
|
||
if err := config.execdWriter.Write(r); err != nil { | ||
config.exitHandler.Error(err) | ||
return | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* Copyright 2018-2021 the original author or 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 | ||
* | ||
* https://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 libcnb_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
"github.com/sclevine/spec" | ||
"github.com/stretchr/testify/mock" | ||
|
||
"github.com/buildpacks/libcnb" | ||
"github.com/buildpacks/libcnb/mocks" | ||
) | ||
|
||
func testExecD(t *testing.T, context spec.G, it spec.S) { | ||
var ( | ||
Expect = NewWithT(t).Expect | ||
|
||
exitHandler *mocks.ExitHandler | ||
execdWriter *mocks.ExecDWriter | ||
) | ||
|
||
it.Before(func() { | ||
execdWriter = &mocks.ExecDWriter{} | ||
execdWriter.On("Write", mock.Anything).Return(nil) | ||
exitHandler = &mocks.ExitHandler{} | ||
exitHandler.On("Error", mock.Anything) | ||
exitHandler.On("Pass", mock.Anything) | ||
exitHandler.On("Fail", mock.Anything) | ||
}) | ||
|
||
it("encounters the wrong number of arguments", func() { | ||
libcnb.RunExecD(map[string]libcnb.ExecD{}, | ||
libcnb.WithArguments([]string{}), | ||
libcnb.WithExitHandler(exitHandler), | ||
) | ||
|
||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected command name")) | ||
}) | ||
|
||
it("encounters an unsupported execd binary name", func() { | ||
libcnb.RunExecD(map[string]libcnb.ExecD{}, | ||
libcnb.WithArguments([]string{"/dne"}), | ||
libcnb.WithExitHandler(exitHandler), | ||
) | ||
|
||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unsupported command dne")) | ||
}) | ||
|
||
it("calls the appropriate execd for a given execd invoker binary", func() { | ||
execd1 := &mocks.ExecD{} | ||
execd2 := &mocks.ExecD{} | ||
execd1.On("Execute", mock.Anything).Return(map[string]string{}, nil) | ||
|
||
libcnb.RunExecD(map[string]libcnb.ExecD{"execd1": execd1, "execd2": execd2}, | ||
libcnb.WithArguments([]string{"execd1"}), | ||
libcnb.WithExitHandler(exitHandler), | ||
libcnb.WithExecDWriter(execdWriter), | ||
) | ||
|
||
Expect(execd1.Calls).To(HaveLen(1)) | ||
Expect(execd2.Calls).To(BeEmpty()) | ||
}) | ||
|
||
it("calls exitHandler with the error from the execd", func() { | ||
e := &mocks.ExecD{} | ||
err := fmt.Errorf("example error") | ||
e.On("Execute", mock.Anything).Return(nil, err) | ||
|
||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e}, | ||
libcnb.WithArguments([]string{"/bin/e"}), | ||
libcnb.WithExitHandler(exitHandler), | ||
libcnb.WithExecDWriter(execdWriter), | ||
) | ||
|
||
Expect(e.Calls).To(HaveLen(1)) | ||
Expect(execdWriter.Calls).To(HaveLen(0)) | ||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(err)) | ||
}) | ||
|
||
it("calls execdWriter.write with the appropriate input", func() { | ||
e := &mocks.ExecD{} | ||
o := map[string]string{"test": "test"} | ||
e.On("Execute", mock.Anything).Return(o, nil) | ||
|
||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e}, | ||
libcnb.WithArguments([]string{"/bin/e"}), | ||
libcnb.WithExitHandler(exitHandler), | ||
libcnb.WithExecDWriter(execdWriter), | ||
) | ||
|
||
Expect(e.Calls).To(HaveLen(1)) | ||
Expect(execdWriter.Calls).To(HaveLen(1)) | ||
Expect(execdWriter.Calls[0].Method).To(BeIdenticalTo("Write")) | ||
Expect(execdWriter.Calls[0].Arguments).To(HaveLen(1)) | ||
Expect(execdWriter.Calls[0].Arguments[0]).To(Equal(o)) | ||
}) | ||
|
||
it("calls exitHandler with the error from the execd", func() { | ||
e := &mocks.ExecD{} | ||
err := fmt.Errorf("example error") | ||
e.On("Execute", mock.Anything).Return(nil, err) | ||
|
||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e}, | ||
libcnb.WithArguments([]string{"/bin/e"}), | ||
libcnb.WithExitHandler(exitHandler), | ||
libcnb.WithExecDWriter(execdWriter), | ||
) | ||
|
||
Expect(e.Calls).To(HaveLen(1)) | ||
Expect(execdWriter.Calls).To(HaveLen(0)) | ||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(err)) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright 2018-2021 the original author or 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 | ||
* | ||
* https://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 internal | ||
|
||
import ( | ||
"io" | ||
"os" | ||
|
||
"github.com/BurntSushi/toml" | ||
) | ||
|
||
// ExecDWriter is a type used to write TOML files to fd3. | ||
type ExecDWriter struct { | ||
outputWriter io.Writer | ||
} | ||
|
||
// Option is a function for configuring an ExitHandler instance. | ||
type ExecDOption func(handler ExecDWriter) ExecDWriter | ||
|
||
// WithExecDOutputWriter creates an Option that configures the writer. | ||
func WithExecDOutputWriter(writer io.Writer) ExecDOption { | ||
return func(execdWriter ExecDWriter) ExecDWriter { | ||
execdWriter.outputWriter = writer | ||
return execdWriter | ||
} | ||
} | ||
|
||
// NewExitHandler creates a new instance that calls os.Exit and writes to os.stderr. | ||
func NewExecDWriter(options ...ExecDOption) ExecDWriter { | ||
h := ExecDWriter{ | ||
outputWriter: os.NewFile(3, "/dev/fd/3"), | ||
} | ||
|
||
for _, option := range options { | ||
h = option(h) | ||
} | ||
|
||
return h | ||
} | ||
|
||
// Write outputs the value serialized in TOML format to the appropriate writer. | ||
func (e ExecDWriter) Write(value map[string]string) error { | ||
if value == nil { | ||
return nil | ||
} | ||
|
||
return toml.NewEncoder(e.outputWriter).Encode(value) | ||
} |
Oops, something went wrong.