mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-08 16:06:47 +00:00
* Fixes upstream deprecations for containerd * Fixes deprecations in ioutil package usage * Break out separate files for function handlers * Upgrades containerd to 1.7.22 * Fixes default namespace functionality * Pre-deploy checks as per license agreement * Removes extra log messages for payload in HTTP handlers, you can enable FAAS_DEBUG=1 in the CLI instead to see this from the client's perspective * Add additional Google DNS server 8.8.4.4 for functions Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
215 lines
5.3 KiB
Go
215 lines
5.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"path"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/sethvargo/go-password/password"
|
|
"github.com/spf13/cobra"
|
|
flag "github.com/spf13/pflag"
|
|
|
|
units "github.com/docker/go-units"
|
|
"github.com/openfaas/faasd/pkg"
|
|
)
|
|
|
|
// upConfig are the CLI flags used by the `faasd up` command to deploy the faasd service
|
|
type upConfig struct {
|
|
// composeFilePath is the path to the compose file specifying the faasd service configuration
|
|
// See https://compose-spec.io/ for more information about the spec,
|
|
//
|
|
// currently, this must be the name of a file in workingDir, which is set to the value of
|
|
// `faasdwd = /var/lib/faasd`
|
|
composeFilePath string
|
|
|
|
// working directory to assume the compose file is in, should be faasdwd.
|
|
// this is not configurable but may be in the future.
|
|
workingDir string
|
|
}
|
|
|
|
func init() {
|
|
configureUpFlags(upCmd.Flags())
|
|
}
|
|
|
|
var upCmd = &cobra.Command{
|
|
Use: "up",
|
|
Short: "Start faasd",
|
|
RunE: runUp,
|
|
PreRunE: preRunE,
|
|
}
|
|
|
|
func runUp(cmd *cobra.Command, _ []string) error {
|
|
|
|
printVersion()
|
|
|
|
cfg, err := parseUpFlags(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
services, err := loadServiceDefinition(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
basicAuthErr := makeBasicAuthFiles(path.Join(cfg.workingDir, "secrets"))
|
|
if basicAuthErr != nil {
|
|
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files")
|
|
}
|
|
|
|
start := time.Now()
|
|
supervisor, err := pkg.NewSupervisor("/run/containerd/containerd.sock")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("Supervisor created in: %s\n", units.HumanDuration(time.Since(start)))
|
|
|
|
start = time.Now()
|
|
if err := supervisor.Start(services); err != nil {
|
|
return err
|
|
}
|
|
defer supervisor.Close()
|
|
|
|
log.Printf("Supervisor init done in: %s\n", units.HumanDuration(time.Since(start)))
|
|
|
|
shutdownTimeout := time.Second * 1
|
|
timeout := time.Second * 60
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
go func() {
|
|
sig := make(chan os.Signal, 1)
|
|
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
|
|
|
|
log.Printf("faasd: waiting for SIGTERM or SIGINT\n")
|
|
<-sig
|
|
|
|
log.Printf("Signal received.. shutting down server in %s\n", shutdownTimeout.String())
|
|
err := supervisor.Remove(services)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
// TODO: close proxies
|
|
time.AfterFunc(shutdownTimeout, func() {
|
|
wg.Done()
|
|
})
|
|
}()
|
|
|
|
localResolver := pkg.NewLocalResolver(path.Join(cfg.workingDir, "hosts"))
|
|
go localResolver.Start()
|
|
|
|
proxies := map[uint32]*pkg.Proxy{}
|
|
for _, svc := range services {
|
|
for _, port := range svc.Ports {
|
|
|
|
listenPort := port.Port
|
|
if _, ok := proxies[listenPort]; ok {
|
|
return fmt.Errorf("port %d already allocated", listenPort)
|
|
}
|
|
|
|
hostIP := "0.0.0.0"
|
|
if len(port.HostIP) > 0 {
|
|
hostIP = port.HostIP
|
|
}
|
|
|
|
upstream := fmt.Sprintf("%s:%d", svc.Name, port.TargetPort)
|
|
proxies[listenPort] = pkg.NewProxy(upstream, listenPort, hostIP, timeout, localResolver)
|
|
}
|
|
}
|
|
|
|
// TODO: track proxies for later cancellation when receiving sigint/term
|
|
for _, v := range proxies {
|
|
go v.Start()
|
|
}
|
|
|
|
wg.Wait()
|
|
return nil
|
|
}
|
|
|
|
func makeBasicAuthFiles(wd string) error {
|
|
|
|
pwdFile := path.Join(wd, "basic-auth-password")
|
|
authPassword, err := password.Generate(63, 10, 0, false, true)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = makeFile(pwdFile, authPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userFile := path.Join(wd, "basic-auth-user")
|
|
err = makeFile(userFile, "admin")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// makeFile will create a file with the specified content if it does not exist yet.
|
|
// if the file already exists, the method is a noop.
|
|
func makeFile(filePath, fileContents string) error {
|
|
_, err := os.Stat(filePath)
|
|
if err == nil {
|
|
log.Printf("File exists: %q\n", filePath)
|
|
return nil
|
|
} else if os.IsNotExist(err) {
|
|
log.Printf("Writing to: %q\n", filePath)
|
|
return os.WriteFile(filePath, []byte(fileContents), workingDirectoryPermission)
|
|
} else {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// load the docker compose file and then parse it as supervisor Services
|
|
// the logic for loading the compose file comes from the compose reference implementation
|
|
// https://github.com/compose-spec/compose-ref/blob/master/compose-ref.go#L353
|
|
func loadServiceDefinition(cfg upConfig) ([]pkg.Service, error) {
|
|
|
|
serviceConfig, err := pkg.LoadComposeFile(cfg.workingDir, cfg.composeFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pkg.ParseCompose(serviceConfig)
|
|
}
|
|
|
|
// ConfigureUpFlags will define the flags for the `faasd up` command. The flag struct, configure, and
|
|
// parse are split like this to simplify testability.
|
|
func configureUpFlags(flags *flag.FlagSet) {
|
|
flags.StringP("file", "f", "docker-compose.yaml", "compose file specifying the faasd service configuration")
|
|
}
|
|
|
|
// ParseUpFlags will load the flag values into an upFlags object. Errors will be underlying
|
|
// Get errors from the pflag library.
|
|
func parseUpFlags(cmd *cobra.Command) (upConfig, error) {
|
|
parsed := upConfig{}
|
|
path, err := cmd.Flags().GetString("file")
|
|
if err != nil {
|
|
return parsed, errors.Wrap(err, "can not parse compose file path flag")
|
|
}
|
|
|
|
parsed.composeFilePath = path
|
|
parsed.workingDir = faasdwd
|
|
return parsed, err
|
|
}
|
|
|
|
func preRunE(cmd *cobra.Command, _ []string) error {
|
|
if err := pkg.ConnectivityCheck(); err != nil {
|
|
return fmt.Errorf("the OpenFaaS CE EULA requires Internet access, upgrade to faasd Pro to continue")
|
|
}
|
|
|
|
return nil
|
|
}
|