Compare commits

...

3 Commits
0.7.9 ... 0.8.0

Author SHA1 Message Date
70e7e0d25a Apply gofmt
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-03-01 20:13:18 +00:00
be8574ecd0 Always pull images by default
The behaviour prior to this patch caused some confusion for
users since they expected a behaviour like Swarm / Kubernetes
which always pulls images by default, even if cached. I've tested
the change and it is working as expected. By default images are
always pulled upon deployment.

To revert to the prior behaviour, simply add to faasd up:
--pull-policy=IfNotPresent

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-03-01 20:13:18 +00:00
a0110b3019 Makefile: added labels to test-e2e
Signed-off-by: kadern0 <kaderno@gmail.com>
2020-02-27 21:56:43 +00:00
9 changed files with 157 additions and 114 deletions

View File

@ -37,9 +37,10 @@ prepare-test:
.PHONY: test-e2e .PHONY: test-e2e
test-e2e: test-e2e:
sudo cat /var/lib/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin sudo cat /var/lib/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin
/usr/local/bin/faas-cli store deploy figlet --env write_timeout=1s --env read_timeout=1s /usr/local/bin/faas-cli store deploy figlet --env write_timeout=1s --env read_timeout=1s --label testing=true
sleep 5 sleep 5
/usr/local/bin/faas-cli list -v /usr/local/bin/faas-cli list -v
/usr/local/bin/faas-cli describe figlet | grep testing
uname | /usr/local/bin/faas-cli invoke figlet uname | /usr/local/bin/faas-cli invoke figlet
uname | /usr/local/bin/faas-cli invoke figlet --async uname | /usr/local/bin/faas-cli invoke figlet --async
sleep 10 sleep 10

View File

@ -19,82 +19,96 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var providerCmd = &cobra.Command{ func makeProviderCmd() *cobra.Command {
Use: "provider", var command = &cobra.Command{
Short: "Run the faasd-provider", Use: "provider",
RunE: runProvider, Short: "Run the faasd-provider",
}
func runProvider(_ *cobra.Command, _ []string) error {
config, providerConfig, err := config.ReadFromEnv(types.OsEnv{})
if err != nil {
return err
} }
log.Printf("faasd-provider starting..\tService Timeout: %s\n", config.WriteTimeout.String()) command.Flags().String("pull-policy", "Always", `Set to "Always" to force a pull of images upon deployment, or "IfNotPresent" to try to use a cached image.`)
wd, err := os.Getwd() command.RunE = func(_ *cobra.Command, _ []string) error {
if err != nil {
return err pullPolicy, flagErr := command.Flags().GetString("pull-policy")
if flagErr != nil {
return flagErr
}
alwaysPull := false
if pullPolicy == "Always" {
alwaysPull = true
}
config, providerConfig, err := config.ReadFromEnv(types.OsEnv{})
if err != nil {
return err
}
log.Printf("faasd-provider starting..\tService Timeout: %s\n", config.WriteTimeout.String())
wd, err := os.Getwd()
if err != nil {
return err
}
writeHostsErr := ioutil.WriteFile(path.Join(wd, "hosts"),
[]byte(`127.0.0.1 localhost`), workingDirectoryPermission)
if writeHostsErr != nil {
return fmt.Errorf("cannot write hosts file: %s", writeHostsErr)
}
writeResolvErr := ioutil.WriteFile(path.Join(wd, "resolv.conf"),
[]byte(`nameserver 8.8.8.8`), workingDirectoryPermission)
if writeResolvErr != nil {
return fmt.Errorf("cannot write resolv.conf file: %s", writeResolvErr)
}
cni, err := cninetwork.InitNetwork()
if err != nil {
return err
}
client, err := containerd.New(providerConfig.Sock)
if err != nil {
return err
}
defer client.Close()
invokeResolver := handlers.NewInvokeResolver(client)
userSecretPath := path.Join(wd, "secrets")
bootstrapHandlers := types.FaaSHandlers{
FunctionProxy: proxy.NewHandlerFunc(*config, invokeResolver),
DeleteHandler: handlers.MakeDeleteHandler(client, cni),
DeployHandler: handlers.MakeDeployHandler(client, cni, userSecretPath, alwaysPull),
FunctionReader: handlers.MakeReadHandler(client),
ReplicaReader: handlers.MakeReplicaReaderHandler(client),
ReplicaUpdater: handlers.MakeReplicaUpdateHandler(client, cni),
UpdateHandler: handlers.MakeUpdateHandler(client, cni, userSecretPath, alwaysPull),
HealthHandler: func(w http.ResponseWriter, r *http.Request) {},
InfoHandler: handlers.MakeInfoHandler(Version, GitCommit),
ListNamespaceHandler: listNamespaces(),
SecretHandler: handlers.MakeSecretHandler(client, userSecretPath),
LogHandler: func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
defer r.Body.Close()
}
w.WriteHeader(http.StatusNotImplemented)
w.Write([]byte(`Logs are not implemented for faasd`))
},
}
log.Printf("Listening on TCP port: %d\n", *config.TCPPort)
bootstrap.Serve(&bootstrapHandlers, config)
return nil
} }
writeHostsErr := ioutil.WriteFile(path.Join(wd, "hosts"), return command
[]byte(`127.0.0.1 localhost`), workingDirectoryPermission)
if writeHostsErr != nil {
return fmt.Errorf("cannot write hosts file: %s", writeHostsErr)
}
writeResolvErr := ioutil.WriteFile(path.Join(wd, "resolv.conf"),
[]byte(`nameserver 8.8.8.8`), workingDirectoryPermission)
if writeResolvErr != nil {
return fmt.Errorf("cannot write resolv.conf file: %s", writeResolvErr)
}
cni, err := cninetwork.InitNetwork()
if err != nil {
return err
}
client, err := containerd.New(providerConfig.Sock)
if err != nil {
return err
}
defer client.Close()
invokeResolver := handlers.NewInvokeResolver(client)
userSecretPath := path.Join(wd, "secrets")
bootstrapHandlers := types.FaaSHandlers{
FunctionProxy: proxy.NewHandlerFunc(*config, invokeResolver),
DeleteHandler: handlers.MakeDeleteHandler(client, cni),
DeployHandler: handlers.MakeDeployHandler(client, cni, userSecretPath),
FunctionReader: handlers.MakeReadHandler(client),
ReplicaReader: handlers.MakeReplicaReaderHandler(client),
ReplicaUpdater: handlers.MakeReplicaUpdateHandler(client, cni),
UpdateHandler: handlers.MakeUpdateHandler(client, cni, userSecretPath),
HealthHandler: func(w http.ResponseWriter, r *http.Request) {},
InfoHandler: handlers.MakeInfoHandler(Version, GitCommit),
ListNamespaceHandler: listNamespaces(),
SecretHandler: handlers.MakeSecretHandler(client, userSecretPath),
LogHandler: func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
defer r.Body.Close()
}
w.WriteHeader(http.StatusNotImplemented)
w.Write([]byte(`Logs are not implemented for faasd`))
},
}
log.Printf("Listening on TCP port: %d\n", *config.TCPPort)
bootstrap.Serve(&bootstrapHandlers, config)
return nil
} }
func listNamespaces() func(w http.ResponseWriter, r *http.Request) { func listNamespaces() func(w http.ResponseWriter, r *http.Request) {

View File

@ -14,7 +14,7 @@ func init() {
rootCommand.AddCommand(versionCmd) rootCommand.AddCommand(versionCmd)
rootCommand.AddCommand(upCmd) rootCommand.AddCommand(upCmd)
rootCommand.AddCommand(installCmd) rootCommand.AddCommand(installCmd)
rootCommand.AddCommand(providerCmd) rootCommand.AddCommand(makeProviderCmd())
rootCommand.AddCommand(collectCmd) rootCommand.AddCommand(collectCmd)
} }

View File

@ -14,8 +14,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/openfaas/faasd/pkg"
"github.com/alexellis/k3sup/pkg/env" "github.com/alexellis/k3sup/pkg/env"
"github.com/openfaas/faasd/pkg"
"github.com/sethvargo/go-password/password" "github.com/sethvargo/go-password/password"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -116,6 +116,7 @@ func runUp(_ *cobra.Command, _ []string) error {
log.Println(fileErr) log.Println(fileErr)
return return
} }
host := "" host := ""
lines := strings.Split(string(fileData), "\n") lines := strings.Split(string(fileData), "\n")
for _, line := range lines { for _, line := range lines {
@ -172,7 +173,7 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
wd, _ := os.Getwd() wd, _ := os.Getwd()
return []pkg.Service{ return []pkg.Service{
pkg.Service{ {
Name: "basic-auth-plugin", Name: "basic-auth-plugin",
Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix, Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix,
Env: []string{ Env: []string{
@ -182,11 +183,11 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
"pass_filename=basic-auth-password", "pass_filename=basic-auth-password",
}, },
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(containerSecretMountDir, "basic-auth-password"), Dest: path.Join(containerSecretMountDir, "basic-auth-password"),
}, },
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(containerSecretMountDir, "basic-auth-user"), Dest: path.Join(containerSecretMountDir, "basic-auth-user"),
}, },
@ -194,26 +195,26 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
Args: nil, Args: nil,
}, },
pkg.Service{ {
Name: "nats", Name: "nats",
Env: []string{""}, Env: []string{""},
Image: "docker.io/library/nats-streaming:0.11.2", Image: "docker.io/library/nats-streaming:0.11.2",
Caps: []string{}, Caps: []string{},
Args: []string{"/nats-streaming-server", "-m", "8222", "--store=memory", "--cluster_id=faas-cluster"}, Args: []string{"/nats-streaming-server", "-m", "8222", "--store=memory", "--cluster_id=faas-cluster"},
}, },
pkg.Service{ {
Name: "prometheus", Name: "prometheus",
Env: []string{}, Env: []string{},
Image: "docker.io/prom/prometheus:v2.14.0", Image: "docker.io/prom/prometheus:v2.14.0",
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ {
Src: path.Join(wd, "prometheus.yml"), Src: path.Join(wd, "prometheus.yml"),
Dest: "/etc/prometheus/prometheus.yml", Dest: "/etc/prometheus/prometheus.yml",
}, },
}, },
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
}, },
pkg.Service{ {
Name: "gateway", Name: "gateway",
Env: []string{ Env: []string{
"basic_auth=true", "basic_auth=true",
@ -231,18 +232,18 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
}, },
Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix, Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix,
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(containerSecretMountDir, "basic-auth-password"), Dest: path.Join(containerSecretMountDir, "basic-auth-password"),
}, },
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(containerSecretMountDir, "basic-auth-user"), Dest: path.Join(containerSecretMountDir, "basic-auth-user"),
}, },
}, },
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
}, },
pkg.Service{ {
Name: "queue-worker", Name: "queue-worker",
Env: []string{ Env: []string{
"faas_nats_address=nats", "faas_nats_address=nats",
@ -257,11 +258,11 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
}, },
Image: "docker.io/openfaas/queue-worker:0.9.0", Image: "docker.io/openfaas/queue-worker:0.9.0",
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(containerSecretMountDir, "basic-auth-password"), Dest: path.Join(containerSecretMountDir, "basic-auth-password"),
}, },
pkg.Mount{ {
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(containerSecretMountDir, "basic-auth-user"), Dest: path.Join(containerSecretMountDir, "basic-auth-user"),
}, },

View File

@ -8,12 +8,12 @@ import (
"log" "log"
"net/http" "net/http"
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
gocni "github.com/containerd/go-cni" gocni "github.com/containerd/go-cni"
"github.com/openfaas/faas/gateway/requests" "github.com/openfaas/faas/gateway/requests"
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
) )
func MakeDeleteHandler(client *containerd.Client, cni gocni.CNI) func(w http.ResponseWriter, r *http.Request) { func MakeDeleteHandler(client *containerd.Client, cni gocni.CNI) func(w http.ResponseWriter, r *http.Request) {

View File

@ -23,7 +23,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string) func(w http.ResponseWriter, r *http.Request) { func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -54,7 +54,7 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
name := req.Service name := req.Service
ctx := namespaces.WithNamespace(context.Background(), FunctionNamespace) ctx := namespaces.WithNamespace(context.Background(), FunctionNamespace)
deployErr := deploy(ctx, req, client, cni, secretMountPath) deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull)
if deployErr != nil { if deployErr != nil {
log.Printf("[Deploy] error deploying %s, error: %s\n", name, deployErr) log.Printf("[Deploy] error deploying %s, error: %s\n", name, deployErr)
http.Error(w, deployErr.Error(), http.StatusBadRequest) http.Error(w, deployErr.Error(), http.StatusBadRequest)
@ -63,7 +63,7 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
} }
} }
func deploy(ctx context.Context, req types.FunctionDeployment, client *containerd.Client, cni gocni.CNI, secretMountPath string) error { func deploy(ctx context.Context, req types.FunctionDeployment, client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) error {
r, err := reference.ParseNormalizedNamed(req.Image) r, err := reference.ParseNormalizedNamed(req.Image)
if err != nil { if err != nil {
return err return err
@ -75,7 +75,7 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container
snapshotter = val snapshotter = val
} }
image, err := service.PrepareImage(ctx, client, imgRef, snapshotter) image, err := service.PrepareImage(ctx, client, imgRef, snapshotter, alwaysPull)
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to pull image %s", imgRef) return errors.Wrapf(err, "unable to pull image %s", imgRef)
} }

View File

@ -8,15 +8,15 @@ import (
"log" "log"
"net/http" "net/http"
"github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
gocni "github.com/containerd/go-cni" gocni "github.com/containerd/go-cni"
"github.com/openfaas/faas-provider/types" "github.com/openfaas/faas-provider/types"
"github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
) )
func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string) func(w http.ResponseWriter, r *http.Request) { func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
@ -68,7 +68,7 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
return return
} }
deployErr := deploy(ctx, req, client, cni, secretMountPath) deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull)
if deployErr != nil { if deployErr != nil {
log.Printf("[Update] error deploying %s, error: %s\n", name, deployErr) log.Printf("[Update] error deploying %s, error: %s\n", name, deployErr)
http.Error(w, deployErr.Error(), http.StatusBadRequest) http.Error(w, deployErr.Error(), http.StatusBadRequest)

View File

@ -122,11 +122,12 @@ func getResolver(ctx context.Context, configFile *configfile.ConfigFile) (remote
return docker.NewResolver(opts), nil return docker.NewResolver(opts), nil
} }
func PrepareImage(ctx context.Context, client *containerd.Client, imageName, snapshotter string) (containerd.Image, error) { func PrepareImage(ctx context.Context, client *containerd.Client, imageName, snapshotter string, pullAlways bool) (containerd.Image, error) {
var ( var (
empty containerd.Image empty containerd.Image
resolver remotes.Resolver resolver remotes.Resolver
) )
if _, stErr := os.Stat(filepath.Join(dockerConfigDir, config.ConfigFileName)); stErr == nil { if _, stErr := os.Stat(filepath.Join(dockerConfigDir, config.ConfigFileName)); stErr == nil {
configFile, err := config.Load(dockerConfigDir) configFile, err := config.Load(dockerConfigDir)
if err != nil { if err != nil {
@ -140,22 +141,29 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna
return empty, stErr return empty, stErr
} }
image, err := client.GetImage(ctx, imageName) var image containerd.Image
if err != nil { if pullAlways {
if !errdefs.IsNotFound(err) { img, err := pullImage(ctx, client, resolver, imageName)
if err != nil {
return empty, err return empty, err
} }
rOpts := []containerd.RemoteOpt{
containerd.WithPullUnpack,
}
if resolver != nil {
rOpts = append(rOpts, containerd.WithResolver(resolver))
}
img, err := client.Pull(ctx, imageName, rOpts...)
if err != nil {
return empty, fmt.Errorf("cannot pull: %s", err)
}
image = img image = img
} else {
img, err := client.GetImage(ctx, imageName)
if err != nil {
if !errdefs.IsNotFound(err) {
return empty, err
}
img, err := pullImage(ctx, client, resolver, imageName)
if err != nil {
return empty, err
}
image = img
} else {
image = img
}
} }
unpacked, err := image.IsUnpacked(ctx, snapshotter) unpacked, err := image.IsUnpacked(ctx, snapshotter)
@ -171,3 +179,21 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna
return image, nil return image, nil
} }
func pullImage(ctx context.Context, client *containerd.Client, resolver remotes.Resolver, imageName string) (containerd.Image, error) {
var empty containerd.Image
rOpts := []containerd.RemoteOpt{
containerd.WithPullUnpack,
}
if resolver != nil {
rOpts = append(rOpts, containerd.WithResolver(resolver))
}
img, err := client.Pull(ctx, imageName, rOpts...)
if err != nil {
return empty, fmt.Errorf("cannot pull: %s", err)
}
return img, nil
}

View File

@ -8,13 +8,13 @@ import (
"os" "os"
"path" "path"
"github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cio" "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
gocni "github.com/containerd/go-cni" gocni "github.com/containerd/go-cni"
"github.com/openfaas/faasd/pkg/cninetwork"
"github.com/openfaas/faasd/pkg/service"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@ -24,7 +24,8 @@ const (
defaultSnapshotter = "overlayfs" defaultSnapshotter = "overlayfs"
workingDirectoryPermission = 0644 workingDirectoryPermission = 0644
// faasdNamespace is the containerd namespace services are created // faasdNamespace is the containerd namespace services are created
faasdNamespace = "default" faasdNamespace = "default"
faasServicesPullAlways = false
) )
type Service struct { type Service struct {
@ -88,7 +89,7 @@ func (s *Supervisor) Start(svcs []Service) error {
for _, svc := range svcs { for _, svc := range svcs {
fmt.Printf("Preparing: %s with image: %s\n", svc.Name, svc.Image) fmt.Printf("Preparing: %s with image: %s\n", svc.Name, svc.Image)
img, err := service.PrepareImage(ctx, s.client, svc.Image, defaultSnapshotter) img, err := service.PrepareImage(ctx, s.client, svc.Image, defaultSnapshotter, faasServicesPullAlways)
if err != nil { if err != nil {
return err return err
} }