Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,31 @@ $ uname -a | curl http://127.0.0.1:8080/function/nodejs-echo--data-binary @-
* `OPENFAAS_TEMPLATE_URL` - to set the default URL to pull templates from
* `OPENFAAS_PREFIX` - for use with `faas-cli new` - this can act in place of `--prefix`
* `OPENFAAS_URL` - to override the default gateway URL
* `OPENFAAS_REMOTE_BUILDER` - default value for `--remote-builder`
* `OPENFAAS_PAYLOAD_SECRET` - default value for `--payload-secret`
* `OPENFAAS_BUILDER_PUBLIC_KEY` - builder public key as a literal value, or a path to a file containing raw base64 or the JSON response from `/public-key`
* `OPENFAAS_BUILDER_KEY_ID` - default value for `--builder-key-id` when pinning a raw base64 public key file
* `OPENFAAS_CONFIG` - to override the location of the configuration folder, which contains auth configuration.
* `CI` - to override the location of the configuration folder, when true, the configuration folder is `.openfaas` in the current working directory. This value is ignored if `OPENFAAS_CONFIG` is set.

For encrypted remote-builder builds, the safest option is to read the builder public key from a file rather than putting the key inline on the command line. The file can contain either:

* the raw base64 public key
* or the JSON document returned by `GET /public-key`

The `--builder-public-key` flag and `OPENFAAS_BUILDER_PUBLIC_KEY` env var also accept a literal value directly. If the value points to an existing file, the CLI reads the file; otherwise it treats the value itself as the key material.

Basic remote-builder example using automatic `GET /public-key` discovery:

```sh
faas-cli publish \
--remote-builder http://127.0.0.1:8081 \
--payload-secret /var/openfaas/secrets/payload-secret \
-f stack.yml
```

If any functions in `stack.yml` define `build_secrets`, the CLI will fetch `/public-key` from the builder automatically unless `--builder-public-key` is set.

### Contributing

See [contributing guide](https://github.com/openfaas/faas-cli/blob/master/CONTRIBUTING.md).
Expand Down
45 changes: 8 additions & 37 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
package builder

import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path"
Expand All @@ -22,7 +20,7 @@ import (
v2execute "github.com/alexellis/go-execute/v2"
"github.com/openfaas/faas-cli/schema"
vcs "github.com/openfaas/faas-cli/versioncontrol"
"github.com/openfaas/go-sdk/builder"
sdkbuilder "github.com/openfaas/go-sdk/builder"
"github.com/openfaas/go-sdk/stack"
)

Expand Down Expand Up @@ -69,7 +67,7 @@ func getTemplate(lang string) (string, *stack.LanguageTemplate, error) {

// BuildImage construct Docker image from function parameters
// TODO: refactor signature to a struct to simplify the length of the method header
func BuildImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string, buildOptions []string, tagFormat schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, remoteBuilder, payloadSecretPath string, forcePull bool) error {
func BuildImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string, buildOptions []string, tagFormat schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, buildSecrets map[string]string, remoteBuilder, payloadSecretPath, builderPublicKeyPath, builderKeyID string, forcePull bool) error {

_, langTemplate, err := getTemplate(language)
if err != nil {
Expand All @@ -85,12 +83,12 @@ func BuildImage(image string, handler string, functionName string, language stri
return fmt.Errorf("building %s, %s is an invalid path", functionName, handler)
}

opts := []builder.BuildContextOption{}
opts := []sdkbuilder.BuildContextOption{}
if len(langTemplate.HandlerFolder) > 0 {
opts = append(opts, builder.WithHandlerOverlay(langTemplate.HandlerFolder))
opts = append(opts, sdkbuilder.WithHandlerOverlay(langTemplate.HandlerFolder))
}

buildContext, err := builder.CreateBuildContext(functionName, handler, language, copyExtraPaths, opts...)
buildContext, err := sdkbuilder.CreateBuildContext(functionName, handler, language, copyExtraPaths, opts...)
if err != nil {
return err
}
Expand Down Expand Up @@ -125,51 +123,24 @@ func BuildImage(image string, handler string, functionName string, language stri

tarPath := path.Join(tempDir, "req.tar")

buildConfig := builder.BuildConfig{
buildConfig := sdkbuilder.BuildConfig{
Image: imageName,
BuildArgs: buildArgMap,
}

// Prepare a tar archive that contains the build config and build context.
if err := builder.MakeTar(tarPath, path.Join("build", functionName), &buildConfig); err != nil {
if err := sdkbuilder.MakeTar(tarPath, path.Join("build", functionName), &buildConfig); err != nil {
return fmt.Errorf("failed to create tar file for %s, error: %w", functionName, err)
}

// Get the HMAC secret used for payload authentication with the builder API.
payloadSecret, err := os.ReadFile(payloadSecretPath)
if err != nil {
return fmt.Errorf("failed to read payload secret: %w", err)
}
payloadSecret = bytes.TrimSpace(payloadSecret)

// Initialize a new builder client.
u, _ := url.Parse(remoteBuilder)
builderURL := &url.URL{
Scheme: u.Scheme,
Host: u.Host,
}
b := builder.NewFunctionBuilder(builderURL, http.DefaultClient, builder.WithHmacAuth(string(payloadSecret)))

stream, err := b.BuildWithStream(tarPath)
if err != nil {
if err := runRemoteBuild(builderURL, tarPath, payloadSecretPath, builderPublicKeyPath, builderKeyID, buildSecrets, quietBuild, functionName, imageName); err != nil {
return fmt.Errorf("failed to invoke builder: %w", err)
}
defer stream.Close()

for result := range stream.Results() {
if !quietBuild {
for _, logMsg := range result.Log {
fmt.Printf("%s\n", logMsg)
}
}

switch result.Status {
case builder.BuildSuccess:
log.Printf("%s success building and pushing image: %s", functionName, result.Image)
case builder.BuildFailed:
return fmt.Errorf("%s failure while building or pushing image %s: %s", functionName, imageName, result.Error)
}
}

} else {
dockerBuildVal := dockerBuild{
Expand Down
48 changes: 9 additions & 39 deletions builder/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,23 @@
package builder

import (
"bytes"
"context"
"fmt"
"log"
"net/http"
"net/url"
"os"
"path"
"strings"

v2execute "github.com/alexellis/go-execute/v2"
"github.com/openfaas/faas-cli/schema"
"github.com/openfaas/go-sdk/builder"
sdkbuilder "github.com/openfaas/go-sdk/builder"
"github.com/openfaas/go-sdk/stack"
)

// PublishImage will publish images as multi-arch
// TODO: refactor signature to a struct to simplify the length of the method header
func PublishImage(image string, handler string, functionName string, language string, nocache bool, squash bool, shrinkwrap bool, buildArgMap map[string]string,
buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, platforms string, extraTags []string, remoteBuilder, payloadSecretPath string, forcePull bool) error {
buildOptions []string, tagMode schema.BuildFormat, buildLabelMap map[string]string, quietBuild bool, copyExtraPaths []string, buildSecrets map[string]string, platforms string, extraTags []string, remoteBuilder, payloadSecretPath, builderPublicKeyPath, builderKeyID string, forcePull bool) error {

if stack.IsValidTemplate(language) {
pathToTemplateYAML := fmt.Sprintf("./template/%s/template.yml", language)
Expand All @@ -40,12 +37,12 @@ func PublishImage(image string, handler string, functionName string, language st
return fmt.Errorf("building %s, %s is an invalid path", functionName, handler)
}

opts := []builder.BuildContextOption{}
opts := []sdkbuilder.BuildContextOption{}
if len(langTemplate.HandlerFolder) > 0 {
opts = append(opts, builder.WithHandlerOverlay(langTemplate.HandlerFolder))
opts = append(opts, sdkbuilder.WithHandlerOverlay(langTemplate.HandlerFolder))
}

buildContext, err := builder.CreateBuildContext(functionName, handler, language, copyExtraPaths, opts...)
buildContext, err := sdkbuilder.CreateBuildContext(functionName, handler, language, copyExtraPaths, opts...)
if err != nil {
return err
}
Expand Down Expand Up @@ -85,51 +82,24 @@ func PublishImage(image string, handler string, functionName string, language st
tarPath := path.Join(tempDir, "req.tar")

builderPlatforms := strings.Split(platforms, ",")
buildConfig := builder.BuildConfig{
buildConfig := sdkbuilder.BuildConfig{
Image: imageName,
BuildArgs: buildArgMap,
Platforms: builderPlatforms,
}

// Prepare a tar archive that contains the build config and build context.
if err := builder.MakeTar(tarPath, path.Join("build", functionName), &buildConfig); err != nil {
if err := sdkbuilder.MakeTar(tarPath, path.Join("build", functionName), &buildConfig); err != nil {
return fmt.Errorf("failed to create tar file for %s, error: %w", functionName, err)
}

// Get the HMAC secret used for payload authentication with the builder API.
payloadSecret, err := os.ReadFile(payloadSecretPath)
if err != nil {
return fmt.Errorf("failed to read payload secret: %w", err)
}
payloadSecret = bytes.TrimSpace(payloadSecret)

// Initialize a new builder client.
u, _ := url.Parse(remoteBuilder)
builderURL := &url.URL{
Scheme: u.Scheme,
Host: u.Host,
}
b := builder.NewFunctionBuilder(builderURL, http.DefaultClient, builder.WithHmacAuth(string(payloadSecret)))

stream, err := b.BuildWithStream(tarPath)
if err != nil {
return fmt.Errorf("failed to invoke builder:: %w", err)
}
defer stream.Close()

for result := range stream.Results() {
if !quietBuild {
for _, logMsg := range result.Log {
fmt.Printf("%s\n", logMsg)
}
}

switch result.Status {
case builder.BuildSuccess:
log.Printf("%s success building and pushing image: %s", functionName, result.Image)
case builder.BuildFailed:
return fmt.Errorf("%s failure while building or pushing image %s: %s", functionName, imageName, result.Error)
}
if err := runRemoteBuild(builderURL, tarPath, payloadSecretPath, builderPublicKeyPath, builderKeyID, buildSecrets, quietBuild, functionName, imageName); err != nil {
return fmt.Errorf("failed to invoke builder: %w", err)
}

} else {
Expand Down
Loading
Loading