diff --git a/cmd/provider.go b/cmd/provider.go index bc9ffb6..327f0b7 100644 --- a/cmd/provider.go +++ b/cmd/provider.go @@ -19,82 +19,96 @@ import ( "github.com/spf13/cobra" ) -var providerCmd = &cobra.Command{ - Use: "provider", - Short: "Run the faasd-provider", - RunE: runProvider, -} - -func runProvider(_ *cobra.Command, _ []string) error { - - config, providerConfig, err := config.ReadFromEnv(types.OsEnv{}) - if err != nil { - return err +func makeProviderCmd() *cobra.Command { + var command = &cobra.Command{ + Use: "provider", + Short: "Run the faasd-provider", } - 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() - if err != nil { - return err + command.RunE = func(_ *cobra.Command, _ []string) error { + + 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"), - []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 + return command } func listNamespaces() func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/root.go b/cmd/root.go index a63494a..cb398b4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,7 +14,7 @@ func init() { rootCommand.AddCommand(versionCmd) rootCommand.AddCommand(upCmd) rootCommand.AddCommand(installCmd) - rootCommand.AddCommand(providerCmd) + rootCommand.AddCommand(makeProviderCmd()) rootCommand.AddCommand(collectCmd) } diff --git a/cmd/up.go b/cmd/up.go index 95351f5..326597c 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -14,8 +14,8 @@ import ( "github.com/pkg/errors" - "github.com/openfaas/faasd/pkg" "github.com/alexellis/k3sup/pkg/env" + "github.com/openfaas/faasd/pkg" "github.com/sethvargo/go-password/password" "github.com/spf13/cobra" ) @@ -116,6 +116,7 @@ func runUp(_ *cobra.Command, _ []string) error { log.Println(fileErr) return } + host := "" lines := strings.Split(string(fileData), "\n") for _, line := range lines { diff --git a/pkg/provider/handlers/deploy.go b/pkg/provider/handlers/deploy.go index f382be8..64a95b8 100644 --- a/pkg/provider/handlers/deploy.go +++ b/pkg/provider/handlers/deploy.go @@ -23,7 +23,7 @@ import ( "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) { @@ -54,7 +54,7 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath name := req.Service ctx := namespaces.WithNamespace(context.Background(), FunctionNamespace) - deployErr := deploy(ctx, req, client, cni, secretMountPath) + deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull) if deployErr != nil { log.Printf("[Deploy] error deploying %s, error: %s\n", name, deployErr) 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) if err != nil { return err @@ -75,7 +75,7 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container snapshotter = val } - image, err := service.PrepareImage(ctx, client, imgRef, snapshotter) + image, err := service.PrepareImage(ctx, client, imgRef, snapshotter, alwaysPull) if err != nil { return errors.Wrapf(err, "unable to pull image %s", imgRef) } diff --git a/pkg/provider/handlers/update.go b/pkg/provider/handlers/update.go index 088f1b4..4999b86 100644 --- a/pkg/provider/handlers/update.go +++ b/pkg/provider/handlers/update.go @@ -8,15 +8,15 @@ import ( "log" "net/http" - "github.com/openfaas/faasd/pkg/cninetwork" - "github.com/openfaas/faasd/pkg/service" "github.com/containerd/containerd" "github.com/containerd/containerd/namespaces" gocni "github.com/containerd/go-cni" "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) { @@ -68,7 +68,7 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath return } - deployErr := deploy(ctx, req, client, cni, secretMountPath) + deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull) if deployErr != nil { log.Printf("[Update] error deploying %s, error: %s\n", name, deployErr) http.Error(w, deployErr.Error(), http.StatusBadRequest) diff --git a/pkg/service/service.go b/pkg/service/service.go index f61ea7f..5459d10 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -122,11 +122,12 @@ func getResolver(ctx context.Context, configFile *configfile.ConfigFile) (remote 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 ( empty containerd.Image resolver remotes.Resolver ) + if _, stErr := os.Stat(filepath.Join(dockerConfigDir, config.ConfigFileName)); stErr == nil { configFile, err := config.Load(dockerConfigDir) if err != nil { @@ -140,22 +141,29 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna return empty, stErr } - image, err := client.GetImage(ctx, imageName) - if err != nil { - if !errdefs.IsNotFound(err) { + var image containerd.Image + if pullAlways { + img, err := pullImage(ctx, client, resolver, imageName) + if err != nil { 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 + } 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) @@ -171,3 +179,21 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna 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 +} diff --git a/pkg/supervisor.go b/pkg/supervisor.go index de114f7..96d95ea 100644 --- a/pkg/supervisor.go +++ b/pkg/supervisor.go @@ -8,13 +8,13 @@ import ( "os" "path" - "github.com/openfaas/faasd/pkg/cninetwork" - "github.com/openfaas/faasd/pkg/service" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/oci" 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/opencontainers/runtime-spec/specs-go" @@ -24,7 +24,8 @@ const ( defaultSnapshotter = "overlayfs" workingDirectoryPermission = 0644 // faasdNamespace is the containerd namespace services are created - faasdNamespace = "default" + faasdNamespace = "default" + faasServicesPullAlways = false ) type Service struct { @@ -88,7 +89,7 @@ func (s *Supervisor) Start(svcs []Service) error { for _, svc := range svcs { 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 { return err }