mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-09 00:16:46 +00:00
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>
This commit is contained in:
parent
a0110b3019
commit
be8574ecd0
156
cmd/provider.go
156
cmd/provider.go
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user