Skip to content

Support request debug logging in the builder package #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 18, 2025
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
3 changes: 3 additions & 0 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"strings"

"github.com/alexellis/hmac/v2"
"github.com/openfaas/go-sdk/internal/httpclient"
)

const BuilderConfigFileName = "com.openfaas.docker.config"
Expand Down Expand Up @@ -80,6 +81,8 @@ func WithHmacAuth(secret string) BuilderOption {

// NewFunctionBuilder create a new builder for building OpenFaaS functions using the Function Builder API.
func NewFunctionBuilder(url *url.URL, client *http.Client, options ...BuilderOption) *FunctionBuilder {
client = httpclient.WithFaasTransport(client)

b := &FunctionBuilder{
URL: url,

Expand Down
115 changes: 29 additions & 86 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ import (
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"

"github.com/openfaas/faas-provider/logs"
"github.com/openfaas/faas-provider/types"
"github.com/openfaas/go-sdk/internal/httpclient"
)

// Client is used to manage OpenFaaS and invoke functions
Expand All @@ -39,25 +37,6 @@ type Client struct {
fnTokenCache TokenCache
}

// Wrap http request Do function to add default headers and support debug capabilities
func (s *Client) do(req *http.Request) (*http.Response, error) {
// Add default user-agent header
if len(req.Header.Get("User-Agent")) == 0 {
req.Header.Set("User-Agent", "openfaas-go-sdk")
}

if os.Getenv("FAAS_DEBUG") == "1" {
dump, err := dumpRequest(req)
if err != nil {
return nil, err
}

fmt.Println(dump)
}

return s.client.Do(req)
}

// ClientAuth an interface for client authentication.
// to add authentication to the client implement this interface
type ClientAuth interface {
Expand Down Expand Up @@ -96,6 +75,9 @@ func NewClient(gatewayURL *url.URL, auth ClientAuth, client *http.Client) *Clien
// NewClientWithOpts creates a Client for managing OpenFaaS and invoking functions
// It takes a list of ClientOptions to configure the client.
func NewClientWithOpts(gatewayURL *url.URL, client *http.Client, options ...ClientOption) *Client {
// Wrap http client to add default headers and support debug capabilities
client = httpclient.WithFaasTransport(client)

c := &Client{
GatewayURL: gatewayURL,

Expand Down Expand Up @@ -136,7 +118,7 @@ func (s *Client) GetNamespaces(ctx context.Context) ([]string, error) {
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return namespaces, fmt.Errorf("unable to make request: %w", err)
}
Expand Down Expand Up @@ -181,7 +163,7 @@ func (s *Client) GetNamespace(ctx context.Context, namespace string) (types.Func
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return types.FunctionNamespace{}, fmt.Errorf("unable to make HTTP request: %w", err)
}
Expand Down Expand Up @@ -240,14 +222,15 @@ func (s *Client) CreateNamespace(ctx context.Context, spec types.FunctionNamespa
if err != nil {
return http.StatusBadGateway, err
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to set Authorization header: %w", err)
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return http.StatusBadGateway, err
}
Expand Down Expand Up @@ -299,14 +282,15 @@ func (s *Client) UpdateNamespace(ctx context.Context, spec types.FunctionNamespa
if err != nil {
return http.StatusBadGateway, err
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to set Authorization header: %w", err)
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return http.StatusBadGateway, err
}
Expand Down Expand Up @@ -354,13 +338,14 @@ func (s *Client) DeleteNamespace(ctx context.Context, namespace string) error {
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", u.String(), err)
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return fmt.Errorf("unable to set Authorization header: %w", err)
}
}
res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", s.GatewayURL, err)

Expand Down Expand Up @@ -414,7 +399,7 @@ func (s *Client) GetFunctions(ctx context.Context, namespace string) ([]types.Fu
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return []types.FunctionStatus{}, fmt.Errorf("unable to make HTTP request: %w", err)
}
Expand Down Expand Up @@ -449,7 +434,7 @@ func (s *Client) GetInfo(ctx context.Context) (SystemInfo, error) {
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return SystemInfo{}, fmt.Errorf("unable to make HTTP request: %w", err)
}
Expand Down Expand Up @@ -491,7 +476,7 @@ func (s *Client) GetFunction(ctx context.Context, name, namespace string) (types
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return types.FunctionStatus{}, fmt.Errorf("unable to make HTTP request: %w", err)
}
Expand Down Expand Up @@ -536,14 +521,15 @@ func (s *Client) deploy(ctx context.Context, method string, spec types.FunctionD
if err != nil {
return http.StatusBadGateway, err
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to set Authorization header: %w", err)
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return http.StatusBadGateway, err
}
Expand Down Expand Up @@ -587,13 +573,14 @@ func (s *Client) ScaleFunction(ctx context.Context, functionName, namespace stri
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", u.String(), err)
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return fmt.Errorf("unable to set Authorization header: %w", err)
}
}
res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", s.GatewayURL, err)

Expand Down Expand Up @@ -645,13 +632,14 @@ func (s *Client) DeleteFunction(ctx context.Context, functionName, namespace str
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", u.String(), err)
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return fmt.Errorf("unable to set Authorization header: %w", err)
}
}
res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", s.GatewayURL, err)

Expand Down Expand Up @@ -705,7 +693,7 @@ func (s *Client) GetSecrets(ctx context.Context, namespace string) ([]types.Secr
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return []types.Secret{}, fmt.Errorf("unable to make HTTP request: %w", err)
}
Expand Down Expand Up @@ -742,14 +730,15 @@ func (s *Client) CreateSecret(ctx context.Context, spec types.Secret) (int, erro
if err != nil {
return http.StatusBadGateway, err
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to set Authorization header: %w", err)
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return http.StatusBadGateway, err
}
Expand Down Expand Up @@ -789,14 +778,15 @@ func (s *Client) UpdateSecret(ctx context.Context, spec types.Secret) (int, erro
if err != nil {
return http.StatusBadGateway, err
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return http.StatusInternalServerError, fmt.Errorf("unable to set Authorization header: %w", err)
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return http.StatusBadGateway, err
}
Expand Down Expand Up @@ -842,13 +832,14 @@ func (s *Client) DeleteSecret(ctx context.Context, secretName, namespace string)
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", u.String(), err)
}
req.Header.Set("Content-Type", "application/json")

if s.ClientAuth != nil {
if err := s.ClientAuth.Set(req); err != nil {
return fmt.Errorf("unable to set Authorization header: %w", err)
}
}
res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", s.GatewayURL, err)

Expand Down Expand Up @@ -925,7 +916,7 @@ func (s *Client) GetLogs(ctx context.Context, functionName, namespace string, fo
}
}

res, err := s.do(req)
res, err := s.client.Do(req)
if err != nil {
return nil, fmt.Errorf("cannot connect to OpenFaaS on URL: %s, error: %s", s.GatewayURL, err)

Expand Down Expand Up @@ -971,51 +962,3 @@ func (s *Client) GetLogs(ctx context.Context, functionName, namespace string, fo
}
return logStream, nil
}

func dumpRequest(req *http.Request) (string, error) {
var sb strings.Builder

// Get all header keys and sort them
keys := make([]string, 0, len(req.Header))
for k := range req.Header {
keys = append(keys, k)
}
sort.Strings(keys)

sb.WriteString(fmt.Sprintf("%s %s\n", req.Method, req.URL.String()))
for _, k := range keys {
v := req.Header[k]
if k == "Authorization" {
auth := "[REDACTED]"
if len(v) == 0 {
auth = "[NOT_SET]"
} else {
l, _, ok := strings.Cut(v[0], " ")
if ok && (l == "Basic" || l == "Bearer") {
auth = l + " [REDACTED]"
}
}
sb.WriteString(fmt.Sprintf("%s: %s\n", k, auth))

} else {
sb.WriteString(fmt.Sprintf("%s: %s\n", k, v))
}
}

if req.Body != nil {
r := io.NopCloser(req.Body)
buf := new(strings.Builder)
_, err := io.Copy(buf, r)
if err != nil {
return "", err
}
bodyDebug := buf.String()
if len(bodyDebug) > 0 {
sb.WriteString(fmt.Sprintf("%s\n", bodyDebug))

}
req.Body = io.NopCloser(strings.NewReader(buf.String()))
}

return sb.String(), nil
}
Loading