<commit_before>package uploaders

import (
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/secsy/goftp"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/types"
)

// FTPUpload uploades the file using FTP. Job Destination should be
// in format: ftp://login:password@host/path
func FTPUpload(logger lager.Logger, dbInstance db.Storage, jobID string) error {
	log := logger.Session("ftp-upload")
	log.Info("start", lager.Data{"job": jobID})
	defer log.Info("finished")

	job, err := dbInstance.RetrieveJob(jobID)
	if err != nil {
		log.Error("retrieving-job", err)
		return err
	}

	job.Status = types.JobUploading
	job, err = dbInstance.UpdateJob(job.ID, job)
	if err != nil {
		log.Error("updating-job", err)
		return err
	}

	u, err := url.Parse(job.Destination)
	if err != nil {
		return err
	}

	pw, isSet := u.User.Password()
	if !isSet {
		pw = ""
	}

	config := goftp.Config{
		User:               u.User.Username(),
		Password:           pw,
		ConnectionsPerHost: 10,
		Timeout:            10 * time.Second,
		Logger:             os.Stderr,
	}

	client, err := goftp.DialConfig(config, u.Host+":21")
	if err != nil {
		log.Error("dial-config-failed", err)
		return err
	}

	fileInfo, err := os.Stat(job.LocalDestination)
	if err != nil {
		log.Error("get-destination-info", err)
		return err
	}

	if fileInfo.IsDir() {
		base := path.Base(job.LocalDestination)
		client.Mkdir(base)
		files, err := ioutil.ReadDir(job.LocalDestination)
		if err != nil {
			log.Error("listing-files", err)
			return err
		}
		for _, file := range files {
			localFile, err := os.Open(job.LocalDestination + "/" + file.Name())
			defer localFile.Close()
			if err != nil {
				log.Error("opening-local-destination-failed", err)
				return err
			}
			client.Store("."+u.Path+"/"+file.Name(), localFile)

		}

	} else {
		localFile, err := os.Open(job.LocalDestination)
		defer localFile.Close()
		if err != nil {
			log.Error("opening-local-destination-failed", err)
			return err
		}

		err = client.Store("."+u.Path, localFile)
		if err != nil {
			log.Error("storing-file-failed", err)
			return err
		}
	}

	return err
}
<commit_msg>Ensure directories are created when they don’t exist + close connection<commit_after>package uploaders

import (
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/secsy/goftp"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/types"
)

// FTPUpload uploades the file using FTP. Job Destination should be
// in format: ftp://login:password@host/path
func FTPUpload(logger lager.Logger, dbInstance db.Storage, jobID string) error {
	log := logger.Session("ftp-upload")
	log.Info("start", lager.Data{"job": jobID})
	defer log.Info("finished")

	job, err := dbInstance.RetrieveJob(jobID)
	if err != nil {
		log.Error("retrieving-job", err)
		return err
	}

	job.Status = types.JobUploading
	job, err = dbInstance.UpdateJob(job.ID, job)
	if err != nil {
		log.Error("updating-job", err)
		return err
	}

	u, err := url.Parse(job.Destination)
	if err != nil {
		return err
	}

	pw, isSet := u.User.Password()
	if !isSet {
		pw = ""
	}

	config := goftp.Config{
		User:               u.User.Username(),
		Password:           pw,
		ConnectionsPerHost: 10,
		Timeout:            10 * time.Second,
		Logger:             os.Stderr,
	}

	client, err := goftp.DialConfig(config, u.Host+":21")
	if err != nil {
		log.Error("dial-config-failed", err)
		client.Close()
		return err
	}

	fileInfo, err := os.Stat(job.LocalDestination)
	if err != nil {
		log.Error("get-destination-info", err)
		client.Close()
		return err
	}

	remotePath := "." + path.Dir(u.Path)
	log.Info("check-remote-path", lager.Data{"path": remotePath})

	_, err = client.Stat(remotePath)
	if err != nil {
		err = nil
		_, errMk := client.Mkdir(remotePath)
		if errMk != nil {
			log.Error("no-create-path", errMk)
			client.Close()
			return errMk
		}
	}

	if fileInfo.IsDir() {
		base := path.Base(job.LocalDestination)
		client.Mkdir(remotePath + "/" + base)
		files, err := ioutil.ReadDir(job.LocalDestination)
		if err != nil {
			log.Error("listing-files", err)
			client.Close()
			return err
		}
		for _, file := range files {
			localFile, err := os.Open(job.LocalDestination + "/" + file.Name())
			defer localFile.Close()
			if err != nil {
				log.Error("opening-local-destination-failed", err)
				client.Close()
				return err
			}
			client.Store("."+u.Path+"/"+file.Name(), localFile)

		}

	} else {
		localFile, err := os.Open(job.LocalDestination)
		defer localFile.Close()
		if err != nil {
			log.Error("opening-local-destination-failed", err)
			client.Close()
			return err
		}

		err = client.Store("."+u.Path, localFile)
		if err != nil {
			log.Error("storing-file-failed", err)
			client.Close()
			return err
		}
	}
	client.Close()
	return err
}
<|endoftext|>


<commit_before>// Package sessions contains middleware for easy session management in Martini.
//
//  package main
//
//  import (
//    "github.com/codegangsta/martini"
//    "github.com/codegangsta/martini-contrib/sessions"
//  )
//
//  func main() {
// 	  m := martini.Classic()
//
// 	  store := sessions.NewCookieStore([]byte("secret123"))
// 	  m.Use(sessions.Sessions("my_session", store))
//
// 	  m.Get("/", func(session sessions.Session) string {
// 		  session.Set("hello", "world")
// 	  })
//  }
package sessions

import (
	"github.com/codegangsta/martini"
	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"log"
	"net/http"
)

const (
	errorFormat = "[sessions] ERROR! %s\n"
)

// Store is an interface for custom session stores.
type Store interface {
	sessions.Store
}

// NewCookieStore returns a new CookieStore.
//
// Keys are defined in pairs to allow key rotation, but the common case is to set a single
// authentication key and optionally an encryption key.
//
// The first key in a pair is used for authentication and the second for encryption. The
// encryption key can be set to nil or omitted in the last pair, but the authentication key
// is required in all pairs.
//
// It is recommended to use an authentication key with 32 or 64 bytes. The encryption key,
// if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
func NewCookieStore(keyPairs ...[]byte) Store {
	return sessions.NewCookieStore(keyPairs...)
}

// Session stores the values and optional configuration for a session.
type Session interface {
	// Get returns the session value associated to the given key.
	Get(key interface{}) interface{}
	// Set sets the session value associated to the given key.
	Set(key interface{}, val interface{})
	// AddFlash adds a flash message to the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	AddFlash(value interface{}, vars ...string)
	// Flashes returns a slice of flash messages from the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	Flashes(vars ...string) []interface{}
}

// Sessions is a Middleware that maps a session.Session service into the Martini handler chain.
// Sessions can use a number of storage solutions with the given store.
func Sessions(name string, store Store) martini.Handler {
	return func(res http.ResponseWriter, r *http.Request, c martini.Context, l *log.Logger) {
		s, err := store.Get(r, name)
		// clear the context, we don't need to use
		// gorilla context and we don't want memory leaks
		defer context.Clear(r)

		check(err, l)

		// Map to the Session interface
		c.MapTo(&session{s}, (*Session)(nil))

		// Use before hook to save out the session
		rw := res.(martini.ResponseWriter)
		rw.Before(func(martini.ResponseWriter) {
			check(s.Save(r, res), l)
		})
	}
}

type session struct {
	*sessions.Session
}

func (s *session) Get(key interface{}) interface{} {
	return s.Session.Values[key]
}

func (s *session) Set(key interface{}, val interface{}) {
	s.Session.Values[key] = val
}

func check(err error, l *log.Logger) {
	if err != nil {
		l.Printf(errorFormat, err)
	}
}
<commit_msg>JMS #38: Some major optimizations for the sessions middleware.<commit_after>// Package sessions contains middleware for easy session management in Martini.
//
//  package main
//
//  import (
//    "github.com/codegangsta/martini"
//    "github.com/codegangsta/martini-contrib/sessions"
//  )
//
//  func main() {
// 	  m := martini.Classic()
//
// 	  store := sessions.NewCookieStore([]byte("secret123"))
// 	  m.Use(sessions.Sessions("my_session", store))
//
// 	  m.Get("/", func(session sessions.Session) string {
// 		  session.Set("hello", "world")
// 	  })
//  }
package sessions

import (
	"github.com/codegangsta/martini"
	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"log"
	"net/http"
)

const (
	errorFormat = "[sessions] ERROR! %s\n"
)

// Store is an interface for custom session stores.
type Store interface {
	sessions.Store
}

// NewCookieStore returns a new CookieStore.
//
// Keys are defined in pairs to allow key rotation, but the common case is to set a single
// authentication key and optionally an encryption key.
//
// The first key in a pair is used for authentication and the second for encryption. The
// encryption key can be set to nil or omitted in the last pair, but the authentication key
// is required in all pairs.
//
// It is recommended to use an authentication key with 32 or 64 bytes. The encryption key,
// if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
func NewCookieStore(keyPairs ...[]byte) Store {
	return sessions.NewCookieStore(keyPairs...)
}

// Session stores the values and optional configuration for a session.
type Session interface {
	// Get returns the session value associated to the given key.
	Get(key interface{}) interface{}
	// Set sets the session value associated to the given key.
	Set(key interface{}, val interface{})
	// AddFlash adds a flash message to the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	//AddFlash(value interface{}, vars ...string)
	// Flashes returns a slice of flash messages from the session.
	// A single variadic argument is accepted, and it is optional: it defines the flash key.
	// If not defined "_flash" is used by default.
	//Flashes(vars ...string) []interface{}
}

// Sessions is a Middleware that maps a session.Session service into the Martini handler chain.
// Sessions can use a number of storage solutions with the given store.
func Sessions(name string, store Store) martini.Handler {
	return func(res http.ResponseWriter, r *http.Request, c martini.Context, l *log.Logger) {
		// Map to the Session interface
    s := &session{name, r, l, store, nil, false}
		c.MapTo(s, (*Session)(nil))

		// clear the context, we don't need to use
		// gorilla context and we don't want memory leaks
		defer context.Clear(r)

		// Use before hook to save out the session
		rw := res.(martini.ResponseWriter)
		rw.Before(func(martini.ResponseWriter) {
      if s.Written {
        check(s.Session.Save(r, res), l)
      }
		})
	}
}

type session struct {
  Name string
  Request *http.Request
  Logger *log.Logger
  Store Store
  Session *sessions.Session
  Written bool
}

func (s *session) Get(key interface{}) interface{} {
  s.EnsureSession()
	return s.Session.Values[key]
}

func (s *session) Set(key interface{}, val interface{}) {
  s.EnsureSession()
  s.Written = true
	s.Session.Values[key] = val
}

func (s *session) EnsureSession() {
  if s.Session == nil {
    s.Session, _ = s.Store.Get(s.Request, s.Name)
  }
}

func check(err error, l *log.Logger) {
	if err != nil {
		l.Printf(errorFormat, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/auth"
	"github.com/MG-RAST/Shock/shock-server/conf"
	ncon "github.com/MG-RAST/Shock/shock-server/controller/node"
	acon "github.com/MG-RAST/Shock/shock-server/controller/node/acl"
	icon "github.com/MG-RAST/Shock/shock-server/controller/node/index"
	pcon "github.com/MG-RAST/Shock/shock-server/controller/preauth"
	"github.com/MG-RAST/Shock/shock-server/db"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/preauth"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/Shock/shock-server/user"
	"github.com/MG-RAST/Shock/shock-server/util"
	"github.com/MG-RAST/golib/stretchr/goweb"
	"github.com/MG-RAST/golib/stretchr/goweb/context"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type resource struct {
	A []string `json:"attribute_indexes"`
	C string   `json:"contact"`
	D string   `json:"documentation"`
	I string   `json:"id"`
	R []string `json:"resources"`
	T string   `json:"type"`
	U string   `json:"url"`
	V string   `json:"version"`
}

func mapRoutes() {
	goweb.MapBefore(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("%s REQ RECEIVED \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.MapAfter(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("RESPONDED TO %s \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.Map("/preauth/{id}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		pcon.PreAuthRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/{type}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		acon.AclTypedRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		acon.AclRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/index/{idxType}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		icon.IndexTypedRequest(ctx)
		return nil
	})

	goweb.Map("/", func(ctx context.Context) error {
		host := util.ApiUrl(ctx)

		attrs := strings.Split(conf.Conf["mongodb-attribute-indexes"], ",")
		for k, v := range attrs {
			attrs[k] = strings.TrimSpace(v)
		}

		r := resource{
			A: attrs,
			C: conf.Conf["admin-email"],
			D: host + "/wiki/",
			I: "Shock",
			R: []string{"node"},
			T: "Shock",
			U: host + "/",
			V: "[% VERSION %]",
		}
		return responder.WriteResponseObject(ctx, http.StatusOK, r)
	})

	nodeController := new(ncon.NodeController)
	goweb.MapController(nodeController)

	goweb.MapStatic("/wiki", conf.Conf["site-path"])

	// Map the favicon
	//goweb.MapStaticFile("/favicon.ico", "static-files/favicon.ico")

	// Catch-all handler for everything that we don't understand
	goweb.Map(func(ctx context.Context) error {
		return responder.RespondWithError(ctx, http.StatusBadRequest, "Parameters do not match a valid Shock request type.")
	})
}

func main() {
	// init(s)
	conf.Initialize()
	logger.Initialize()
	if err := db.Initialize(); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
		logger.Error("ERROR: " + err.Error())
		os.Exit(1)
	}
	user.Initialize()
	node.Initialize()
	preauth.Initialize()
	auth.Initialize()

	// print conf
	printLogo()
	conf.Print()

	// check if necessary directories exist or created
	for _, path := range []string{conf.Conf["site-path"], conf.Conf["data-path"], conf.Conf["logs-path"], conf.Conf["data-path"] + "/temp"} {
		if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
			if err := os.Mkdir(path, 0777); err != nil {
				fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
				logger.Error("ERROR: " + err.Error())
				os.Exit(1)
			}
		}
	}

	// reload
	if conf.RELOAD != "" {
		fmt.Println("####### Reloading #######")
		err := reload(conf.RELOAD)
		if err != nil {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			logger.Error("ERROR: " + err.Error())
			os.Exit(1)
		}
		fmt.Println("Done")
	}

	// setting GOMAXPROCS
	var procs int
	avail := runtime.NumCPU()
	if avail <= 2 {
		procs = 1
	} else if avail == 3 {
		procs = 2
	} else {
		procs = avail - 2
	}

	fmt.Println("##### Procs #####")
	fmt.Printf("Number of available CPUs = %d\n", avail)
	if conf.Conf["GOMAXPROCS"] != "" {
		if setting, err := strconv.Atoi(conf.Conf["GOMAXPROCS"]); err != nil {
			err_msg := "ERROR: could not interpret configured GOMAXPROCS value as integer.\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		} else {
			procs = setting
		}
	}

	if procs <= avail {
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", procs)
		runtime.GOMAXPROCS(procs)
	} else {
		fmt.Println("GOMAXPROCS config value is greater than available number of CPUs.")
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", avail)
		runtime.GOMAXPROCS(avail)
	}

	if conf.Conf["pidfile"] != "" {
		f, err := os.Create(conf.Conf["pidfile"])
		if err != nil {
			err_msg := "Could not create pid file: " + conf.Conf["pidfile"] + "\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		}
		defer f.Close()

		pid := os.Getpid()
		fmt.Fprintln(f, pid)

		fmt.Println("##### pidfile #####")
		fmt.Printf("pid: %d saved to file: %s\n\n", pid, conf.Conf["pidfile"])
	}

	Address := conf.Conf["api-ip"] + ":" + conf.Conf["api-port"]
	mapRoutes()

	s := &http.Server{
		Addr:           ":" + Address,
		Handler:        goweb.DefaultHttpHandler(),
		ReadTimeout:    48 * time.Hour,
		WriteTimeout:   48 * time.Hour,
		MaxHeaderBytes: 1 << 20,
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	listener, listenErr := net.Listen("tcp", Address)

	if listenErr != nil {
		err_msg := "Could not listen - " + listenErr.Error() + "\n"
		fmt.Fprintf(os.Stderr, err_msg)
		logger.Error("ERROR: " + err_msg)
		os.Exit(1)
	}

	go func() {
		for _ = range c {
			// sig is a ^C, handle it

			// stop the HTTP server
			fmt.Fprintln(os.Stderr, "Stopping the server...")
			listener.Close()
		}
	}()

	fmt.Fprintf(os.Stderr, "Error in Serve: %s\n", s.Serve(listener))
}
<commit_msg>Added anonymous permissions information to base resource call.<commit_after>package main

import (
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/auth"
	"github.com/MG-RAST/Shock/shock-server/conf"
	ncon "github.com/MG-RAST/Shock/shock-server/controller/node"
	acon "github.com/MG-RAST/Shock/shock-server/controller/node/acl"
	icon "github.com/MG-RAST/Shock/shock-server/controller/node/index"
	pcon "github.com/MG-RAST/Shock/shock-server/controller/preauth"
	"github.com/MG-RAST/Shock/shock-server/db"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/preauth"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/Shock/shock-server/user"
	"github.com/MG-RAST/Shock/shock-server/util"
	"github.com/MG-RAST/golib/stretchr/goweb"
	"github.com/MG-RAST/golib/stretchr/goweb/context"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type resource struct {
	A []string        `json:"attribute_indexes"`
	C string          `json:"contact"`
	D string          `json:"documentation"`
	I string          `json:"id"`
	P map[string]bool `json:"anonymous_permissions"`
	R []string        `json:"resources"`
	T string          `json:"type"`
	U string          `json:"url"`
	V string          `json:"version"`
}

func mapRoutes() {
	goweb.MapBefore(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("%s REQ RECEIVED \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.MapAfter(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("RESPONDED TO %s \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.Map("/preauth/{id}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		pcon.PreAuthRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/{type}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		acon.AclTypedRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		acon.AclRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/index/{idxType}", func(ctx context.Context) error {
		if ctx.HttpRequest().Method == "OPTIONS" {
			return responder.RespondOK(ctx)
		}
		icon.IndexTypedRequest(ctx)
		return nil
	})

	goweb.Map("/", func(ctx context.Context) error {
		host := util.ApiUrl(ctx)

		attrs := strings.Split(conf.Conf["mongodb-attribute-indexes"], ",")
		for k, v := range attrs {
			attrs[k] = strings.TrimSpace(v)
		}

		perms := make(map[string]bool)
		perms["read"] = conf.ANON_READ
		perms["write"] = conf.ANON_WRITE
		perms["delete"] = conf.ANON_DELETE

		r := resource{
			A: attrs,
			C: conf.Conf["admin-email"],
			D: host + "/wiki/",
			I: "Shock",
			P: perms,
			R: []string{"node"},
			T: "Shock",
			U: host + "/",
			V: "[% VERSION %]",
		}
		return responder.WriteResponseObject(ctx, http.StatusOK, r)
	})

	nodeController := new(ncon.NodeController)
	goweb.MapController(nodeController)

	goweb.MapStatic("/wiki", conf.Conf["site-path"])

	// Map the favicon
	//goweb.MapStaticFile("/favicon.ico", "static-files/favicon.ico")

	// Catch-all handler for everything that we don't understand
	goweb.Map(func(ctx context.Context) error {
		return responder.RespondWithError(ctx, http.StatusBadRequest, "Parameters do not match a valid Shock request type.")
	})
}

func main() {
	// init(s)
	conf.Initialize()
	logger.Initialize()
	if err := db.Initialize(); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
		logger.Error("ERROR: " + err.Error())
		os.Exit(1)
	}
	user.Initialize()
	node.Initialize()
	preauth.Initialize()
	auth.Initialize()

	// print conf
	printLogo()
	conf.Print()

	// check if necessary directories exist or created
	for _, path := range []string{conf.Conf["site-path"], conf.Conf["data-path"], conf.Conf["logs-path"], conf.Conf["data-path"] + "/temp"} {
		if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
			if err := os.Mkdir(path, 0777); err != nil {
				fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
				logger.Error("ERROR: " + err.Error())
				os.Exit(1)
			}
		}
	}

	// reload
	if conf.RELOAD != "" {
		fmt.Println("####### Reloading #######")
		err := reload(conf.RELOAD)
		if err != nil {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			logger.Error("ERROR: " + err.Error())
			os.Exit(1)
		}
		fmt.Println("Done")
	}

	// setting GOMAXPROCS
	var procs int
	avail := runtime.NumCPU()
	if avail <= 2 {
		procs = 1
	} else if avail == 3 {
		procs = 2
	} else {
		procs = avail - 2
	}

	fmt.Println("##### Procs #####")
	fmt.Printf("Number of available CPUs = %d\n", avail)
	if conf.Conf["GOMAXPROCS"] != "" {
		if setting, err := strconv.Atoi(conf.Conf["GOMAXPROCS"]); err != nil {
			err_msg := "ERROR: could not interpret configured GOMAXPROCS value as integer.\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		} else {
			procs = setting
		}
	}

	if procs <= avail {
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", procs)
		runtime.GOMAXPROCS(procs)
	} else {
		fmt.Println("GOMAXPROCS config value is greater than available number of CPUs.")
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", avail)
		runtime.GOMAXPROCS(avail)
	}

	if conf.Conf["pidfile"] != "" {
		f, err := os.Create(conf.Conf["pidfile"])
		if err != nil {
			err_msg := "Could not create pid file: " + conf.Conf["pidfile"] + "\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		}
		defer f.Close()

		pid := os.Getpid()
		fmt.Fprintln(f, pid)

		fmt.Println("##### pidfile #####")
		fmt.Printf("pid: %d saved to file: %s\n\n", pid, conf.Conf["pidfile"])
	}

	Address := conf.Conf["api-ip"] + ":" + conf.Conf["api-port"]
	mapRoutes()

	s := &http.Server{
		Addr:           ":" + Address,
		Handler:        goweb.DefaultHttpHandler(),
		ReadTimeout:    48 * time.Hour,
		WriteTimeout:   48 * time.Hour,
		MaxHeaderBytes: 1 << 20,
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	listener, listenErr := net.Listen("tcp", Address)

	if listenErr != nil {
		err_msg := "Could not listen - " + listenErr.Error() + "\n"
		fmt.Fprintf(os.Stderr, err_msg)
		logger.Error("ERROR: " + err_msg)
		os.Exit(1)
	}

	go func() {
		for _ = range c {
			// sig is a ^C, handle it

			// stop the HTTP server
			fmt.Fprintln(os.Stderr, "Stopping the server...")
			listener.Close()
		}
	}()

	fmt.Fprintf(os.Stderr, "Error in Serve: %s\n", s.Serve(listener))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/auth"
	"github.com/MG-RAST/Shock/shock-server/conf"
	ncon "github.com/MG-RAST/Shock/shock-server/controller/node"
	acon "github.com/MG-RAST/Shock/shock-server/controller/node/acl"
	icon "github.com/MG-RAST/Shock/shock-server/controller/node/index"
	pcon "github.com/MG-RAST/Shock/shock-server/controller/preauth"
	"github.com/MG-RAST/Shock/shock-server/db"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/preauth"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/Shock/shock-server/user"
	"github.com/MG-RAST/Shock/shock-server/util"
	"github.com/stretchr/goweb"
	"github.com/stretchr/goweb/context"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
)

type resource struct {
	R []string `json:"resources"`
	U string   `json:"url"`
	D string   `json:"documentation"`
	C string   `json:"contact"`
	I string   `json:"id"`
	T string   `json:"type"`
}

func mapRoutes() {
	goweb.MapBefore(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("%s REQ RECEIVED \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.MapAfter(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("RESPONDED TO %s \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.Map("/preauth/{id}", func(ctx context.Context) error {
		pcon.PreAuthRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/{type}", func(ctx context.Context) error {
		acon.AclTypedRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/", func(ctx context.Context) error {
		acon.AclRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/index/{idxType}", func(ctx context.Context) error {
		icon.IndexTypedRequest(ctx)
		return nil
	})

	goweb.Map("/", func(ctx context.Context) error {
		host := util.ApiUrl(ctx)
		r := resource{
			R: []string{"node"},
			U: host + "/",
			D: host + "/documentation.html",
			C: conf.Conf["admin-email"],
			I: "Shock",
			T: "Shock",
		}
		return responder.WriteResponseObject(ctx, http.StatusOK, r)
	})

	nodeController := new(ncon.NodeController)
	goweb.MapController(nodeController)

	goweb.MapStatic("/assets", conf.Conf["site-path"]+"/assets")
	goweb.MapStaticFile("/documentation.html", conf.Conf["site-path"]+"/pages/main.html")

	// Map the favicon
	//goweb.MapStaticFile("/favicon.ico", "static-files/favicon.ico")

	// Catch-all handler for everything that we don't understand
	goweb.Map(func(ctx context.Context) error {
		return responder.RespondWithError(ctx, http.StatusBadRequest, "Parameters do not match a valid Shock request type.")
	})
}

func main() {
	// init(s)
	conf.Initialize()
	logger.Initialize()
	if err := db.Initialize(); err != nil {
		logger.Error(err.Error())
	}
	user.Initialize()
	node.Initialize()
	preauth.Initialize()
	auth.Initialize()

	// print conf
	printLogo()
	conf.Print()

	if _, err := os.Stat(conf.Conf["data-path"] + "/temp"); err != nil && os.IsNotExist(err) {
		if err := os.Mkdir(conf.Conf["data-path"]+"/temp", 0777); err != nil {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			logger.Error("ERROR: " + err.Error())
			os.Exit(1)
		}
	}

	// reload
	if conf.RELOAD != "" {
		fmt.Println("####### Reloading #######")
		err := reload(conf.RELOAD)
		if err != nil {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			logger.Error("ERROR: " + err.Error())
			os.Exit(1)
		}
		fmt.Println("Done")
	}

	// setting GOMAXPROCS
	var procs int
	avail := runtime.NumCPU()
	if avail <= 2 {
		procs = 1
	} else if avail == 3 {
		procs = 2
	} else {
		procs = avail - 2
	}

	fmt.Println("##### Procs #####")
	fmt.Printf("Number of available CPUs = %d\n", avail)
	if conf.Conf["GOMAXPROCS"] != "" {
		if setting, err := strconv.Atoi(conf.Conf["GOMAXPROCS"]); err != nil {
			err_msg := "ERROR: could not interpret configured GOMAXPROCS value as integer.\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		} else {
			procs = setting
		}
	}

	if procs <= avail {
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", procs)
		runtime.GOMAXPROCS(procs)
	} else {
		fmt.Println("GOMAXPROCS config value is greater than available number of CPUs.")
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", avail)
		runtime.GOMAXPROCS(avail)
	}

	if conf.Conf["pidfile"] != "" {
		f, err := os.Create(conf.Conf["pidfile"])
		if err != nil {
			err_msg := "Could not create pid file: " + conf.Conf["pidfile"] + "\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		}
		defer f.Close()

		pid := os.Getpid()
		fmt.Fprintln(f, pid)

		fmt.Println("##### pidfile #####")
		fmt.Printf("pid: %d saved to file: %s\n\n", pid, conf.Conf["pidfile"])
	}

	Address := conf.Conf["api-ip"] + ":" + conf.Conf["api-port"]
	mapRoutes()

	s := &http.Server{
		Addr:           ":" + Address,
		Handler:        goweb.DefaultHttpHandler(),
		ReadTimeout:    0,
		WriteTimeout:   0,
		MaxHeaderBytes: 1 << 20,
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	listener, listenErr := net.Listen("tcp", Address)

	if listenErr != nil {
		err_msg := "Could not listen - " + listenErr.Error() + "\n"
		fmt.Fprintf(os.Stderr, err_msg)
		logger.Error("ERROR: " + err_msg)
		os.Exit(1)
	}

	go func() {
		for _ = range c {
			// sig is a ^C, handle it

			// stop the HTTP server
			fmt.Fprintln(os.Stderr, "Stopping the server...")
			listener.Close()
		}
	}()

	fmt.Fprintf(os.Stderr, "Error in Serve: %s\n", s.Serve(listener))
}
<commit_msg>Added code to exit shock server gracefully if mongo is not available. Added code to check for existence or at least creatibility of config defined directory paths.<commit_after>package main

import (
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/auth"
	"github.com/MG-RAST/Shock/shock-server/conf"
	ncon "github.com/MG-RAST/Shock/shock-server/controller/node"
	acon "github.com/MG-RAST/Shock/shock-server/controller/node/acl"
	icon "github.com/MG-RAST/Shock/shock-server/controller/node/index"
	pcon "github.com/MG-RAST/Shock/shock-server/controller/preauth"
	"github.com/MG-RAST/Shock/shock-server/db"
	"github.com/MG-RAST/Shock/shock-server/logger"
	"github.com/MG-RAST/Shock/shock-server/node"
	"github.com/MG-RAST/Shock/shock-server/preauth"
	"github.com/MG-RAST/Shock/shock-server/responder"
	"github.com/MG-RAST/Shock/shock-server/user"
	"github.com/MG-RAST/Shock/shock-server/util"
	"github.com/stretchr/goweb"
	"github.com/stretchr/goweb/context"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
)

type resource struct {
	R []string `json:"resources"`
	U string   `json:"url"`
	D string   `json:"documentation"`
	C string   `json:"contact"`
	I string   `json:"id"`
	T string   `json:"type"`
}

func mapRoutes() {
	goweb.MapBefore(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("%s REQ RECEIVED \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.MapAfter(func(ctx context.Context) error {
		req := ctx.HttpRequest()
		host, _, _ := net.SplitHostPort(req.RemoteAddr)
		if host == "::1" {
			host = "localhost"
		}
		suffix := ""
		if _, ok := req.Header["Authorization"]; ok {
			suffix += " AUTH"
		}
		if l, has := req.Header["Content-Length"]; has {
			suffix += " Content-Length: " + l[0]
		}
		logger.Info("access", fmt.Sprintf("RESPONDED TO %s \"%s %s%s\"", host, ctx.MethodString(), req.RequestURI, suffix))
		return nil
	})

	goweb.Map("/preauth/{id}", func(ctx context.Context) error {
		pcon.PreAuthRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/{type}", func(ctx context.Context) error {
		acon.AclTypedRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/acl/", func(ctx context.Context) error {
		acon.AclRequest(ctx)
		return nil
	})

	goweb.Map("/node/{nid}/index/{idxType}", func(ctx context.Context) error {
		icon.IndexTypedRequest(ctx)
		return nil
	})

	goweb.Map("/", func(ctx context.Context) error {
		host := util.ApiUrl(ctx)
		r := resource{
			R: []string{"node"},
			U: host + "/",
			D: host + "/documentation.html",
			C: conf.Conf["admin-email"],
			I: "Shock",
			T: "Shock",
		}
		return responder.WriteResponseObject(ctx, http.StatusOK, r)
	})

	nodeController := new(ncon.NodeController)
	goweb.MapController(nodeController)

	goweb.MapStatic("/assets", conf.Conf["site-path"]+"/assets")
	goweb.MapStaticFile("/documentation.html", conf.Conf["site-path"]+"/pages/main.html")

	// Map the favicon
	//goweb.MapStaticFile("/favicon.ico", "static-files/favicon.ico")

	// Catch-all handler for everything that we don't understand
	goweb.Map(func(ctx context.Context) error {
		return responder.RespondWithError(ctx, http.StatusBadRequest, "Parameters do not match a valid Shock request type.")
	})
}

func main() {
	// init(s)
	conf.Initialize()
	logger.Initialize()
	if err := db.Initialize(); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
		logger.Error("ERROR: " + err.Error())
		os.Exit(1)
	}
	user.Initialize()
	node.Initialize()
	preauth.Initialize()
	auth.Initialize()

	// print conf
	printLogo()
	conf.Print()

	// check if necessary directories exist or created
	for _, path := range []string{conf.Conf["site-path"], conf.Conf["data-path"], conf.Conf["logs-path"], conf.Conf["data-path"] + "/temp"} {
		if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
			if err := os.Mkdir(path, 0777); err != nil {
				fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
				logger.Error("ERROR: " + err.Error())
				os.Exit(1)
			}
		}
	}

	// reload
	if conf.RELOAD != "" {
		fmt.Println("####### Reloading #######")
		err := reload(conf.RELOAD)
		if err != nil {
			fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
			logger.Error("ERROR: " + err.Error())
			os.Exit(1)
		}
		fmt.Println("Done")
	}

	// setting GOMAXPROCS
	var procs int
	avail := runtime.NumCPU()
	if avail <= 2 {
		procs = 1
	} else if avail == 3 {
		procs = 2
	} else {
		procs = avail - 2
	}

	fmt.Println("##### Procs #####")
	fmt.Printf("Number of available CPUs = %d\n", avail)
	if conf.Conf["GOMAXPROCS"] != "" {
		if setting, err := strconv.Atoi(conf.Conf["GOMAXPROCS"]); err != nil {
			err_msg := "ERROR: could not interpret configured GOMAXPROCS value as integer.\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		} else {
			procs = setting
		}
	}

	if procs <= avail {
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", procs)
		runtime.GOMAXPROCS(procs)
	} else {
		fmt.Println("GOMAXPROCS config value is greater than available number of CPUs.")
		fmt.Printf("Running Shock server with GOMAXPROCS = %d\n\n", avail)
		runtime.GOMAXPROCS(avail)
	}

	if conf.Conf["pidfile"] != "" {
		f, err := os.Create(conf.Conf["pidfile"])
		if err != nil {
			err_msg := "Could not create pid file: " + conf.Conf["pidfile"] + "\n"
			fmt.Fprintf(os.Stderr, err_msg)
			logger.Error("ERROR: " + err_msg)
			os.Exit(1)
		}
		defer f.Close()

		pid := os.Getpid()
		fmt.Fprintln(f, pid)

		fmt.Println("##### pidfile #####")
		fmt.Printf("pid: %d saved to file: %s\n\n", pid, conf.Conf["pidfile"])
	}

	Address := conf.Conf["api-ip"] + ":" + conf.Conf["api-port"]
	mapRoutes()

	s := &http.Server{
		Addr:           ":" + Address,
		Handler:        goweb.DefaultHttpHandler(),
		ReadTimeout:    0,
		WriteTimeout:   0,
		MaxHeaderBytes: 1 << 20,
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	listener, listenErr := net.Listen("tcp", Address)

	if listenErr != nil {
		err_msg := "Could not listen - " + listenErr.Error() + "\n"
		fmt.Fprintf(os.Stderr, err_msg)
		logger.Error("ERROR: " + err_msg)
		os.Exit(1)
	}

	go func() {
		for _ = range c {
			// sig is a ^C, handle it

			// stop the HTTP server
			fmt.Fprintln(os.Stderr, "Stopping the server...")
			listener.Close()
		}
	}()

	fmt.Fprintf(os.Stderr, "Error in Serve: %s\n", s.Serve(listener))
}
<|endoftext|>


<commit_before>package system

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/dbus"
)

// fakeMachineID is placed on non-usr CoreOS images and should
// never be used as a true MachineID
const fakeMachineID = "42000000000000000000000000000042"

type Unit struct {
	Name    string
	Runtime bool
	Content string
	Command string
}

func (u *Unit) Type() string {
	ext := filepath.Ext(u.Name)
	return strings.TrimLeft(ext, ".")
}

func (u *Unit) Group() (group string) {
	t := u.Type()
	if t == "network" || t == "netdev" || t == "link" {
		group = "network"
	} else {
		group = "system"
	}
	return
}

type Script []byte

func PlaceUnit(u *Unit, root string) (string, error) {
	dir := "etc"
	if u.Runtime {
		dir = "run"
	}

	dst := path.Join(root, dir, "systemd", u.Group())
	if _, err := os.Stat(dst); os.IsNotExist(err) {
		if err := os.MkdirAll(dst, os.FileMode(0755)); err != nil {
			return "", err
		}
	}

	dst = path.Join(dst, u.Name)

	file := File{
		Path: dst,
		Content: u.Content,
		RawFilePermissions: "0644",
	}

	err := WriteFile(&file)
	if err != nil {
		return "", err
	}

	return dst, nil
}

func EnableUnitFile(file string, runtime bool) error {
	conn, err := dbus.New()
	if err != nil {
		return err
	}

	files := []string{file}
	_, _, err = conn.EnableUnitFiles(files, runtime, true)
	return err
}

func RunUnitCommand(command, unit string) (string, error) {
	conn, err := dbus.New()
	if err != nil {
		return "", err
	}

	var fn func(string, string) (string, error)
	switch command {
	case "start":
		fn = conn.StartUnit
	case "stop":
		fn = conn.StopUnit
	case "restart":
		fn = conn.RestartUnit
	case "reload":
		fn = conn.ReloadUnit
	case "try-restart":
		fn = conn.TryRestartUnit
	case "reload-or-restart":
		fn = conn.ReloadOrRestartUnit
	case "reload-or-try-restart":
		fn = conn.ReloadOrTryRestartUnit
	default:
		return "", fmt.Errorf("Unsupported systemd command %q", command)
	}

	return fn(unit, "replace")
}

func DaemonReload() error {
	conn, err := dbus.New()
	if err != nil {
		return err
	}

	_, err = conn.Reload()
	return err
}

func ExecuteScript(scriptPath string) (string, error) {
	props := []dbus.Property{
		dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"),
		dbus.PropExecStart([]string{"/bin/bash", scriptPath}, false),
	}

	base := path.Base(scriptPath)
	name := fmt.Sprintf("coreos-cloudinit-%s.service", base)

	log.Printf("Creating transient systemd unit '%s'", name)

	conn, err := dbus.New()
	if err != nil {
		return "", err
	}

	_, err = conn.StartTransientUnit(name, "replace", props...)
	return name, err
}

func SetHostname(hostname string) error {
	return exec.Command("hostnamectl", "set-hostname", hostname).Run()
}

func Hostname() (string, error) {
	return os.Hostname()
}

func MachineID(root string) string {
	contents, _ := ioutil.ReadFile(path.Join(root, "etc", "machine-id"))
	id := strings.TrimSpace(string(contents))

	if id == fakeMachineID {
		id = ""
	}

	return id
}
<commit_msg>fix(systemd): Update usage of dbus.Reload<commit_after>package system

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/dbus"
)

// fakeMachineID is placed on non-usr CoreOS images and should
// never be used as a true MachineID
const fakeMachineID = "42000000000000000000000000000042"

type Unit struct {
	Name    string
	Runtime bool
	Content string
	Command string
}

func (u *Unit) Type() string {
	ext := filepath.Ext(u.Name)
	return strings.TrimLeft(ext, ".")
}

func (u *Unit) Group() (group string) {
	t := u.Type()
	if t == "network" || t == "netdev" || t == "link" {
		group = "network"
	} else {
		group = "system"
	}
	return
}

type Script []byte

func PlaceUnit(u *Unit, root string) (string, error) {
	dir := "etc"
	if u.Runtime {
		dir = "run"
	}

	dst := path.Join(root, dir, "systemd", u.Group())
	if _, err := os.Stat(dst); os.IsNotExist(err) {
		if err := os.MkdirAll(dst, os.FileMode(0755)); err != nil {
			return "", err
		}
	}

	dst = path.Join(dst, u.Name)

	file := File{
		Path: dst,
		Content: u.Content,
		RawFilePermissions: "0644",
	}

	err := WriteFile(&file)
	if err != nil {
		return "", err
	}

	return dst, nil
}

func EnableUnitFile(file string, runtime bool) error {
	conn, err := dbus.New()
	if err != nil {
		return err
	}

	files := []string{file}
	_, _, err = conn.EnableUnitFiles(files, runtime, true)
	return err
}

func RunUnitCommand(command, unit string) (string, error) {
	conn, err := dbus.New()
	if err != nil {
		return "", err
	}

	var fn func(string, string) (string, error)
	switch command {
	case "start":
		fn = conn.StartUnit
	case "stop":
		fn = conn.StopUnit
	case "restart":
		fn = conn.RestartUnit
	case "reload":
		fn = conn.ReloadUnit
	case "try-restart":
		fn = conn.TryRestartUnit
	case "reload-or-restart":
		fn = conn.ReloadOrRestartUnit
	case "reload-or-try-restart":
		fn = conn.ReloadOrTryRestartUnit
	default:
		return "", fmt.Errorf("Unsupported systemd command %q", command)
	}

	return fn(unit, "replace")
}

func DaemonReload() error {
	conn, err := dbus.New()
	if err != nil {
		return err
	}

	return conn.Reload()
}

func ExecuteScript(scriptPath string) (string, error) {
	props := []dbus.Property{
		dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"),
		dbus.PropExecStart([]string{"/bin/bash", scriptPath}, false),
	}

	base := path.Base(scriptPath)
	name := fmt.Sprintf("coreos-cloudinit-%s.service", base)

	log.Printf("Creating transient systemd unit '%s'", name)

	conn, err := dbus.New()
	if err != nil {
		return "", err
	}

	_, err = conn.StartTransientUnit(name, "replace", props...)
	return name, err
}

func SetHostname(hostname string) error {
	return exec.Command("hostnamectl", "set-hostname", hostname).Run()
}

func Hostname() (string, error) {
	return os.Hostname()
}

func MachineID(root string) string {
	contents, _ := ioutil.ReadFile(path.Join(root, "etc", "machine-id"))
	id := strings.TrimSpace(string(contents))

	if id == fakeMachineID {
		id = ""
	}

	return id
}
<|endoftext|>


<commit_before>/*
 * Copyright 2019-2020, EnMasse authors.
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */

package v1alpha1

import (
	"github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta1"
	"github.com/enmasseproject/enmasse/pkg/util"
	"github.com/pkg/errors"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ v1beta1.ImageOverridesProvider = &IoTConfig{}
var _ v1beta1.ImageOverridesProvider = &IoTConfigSpec{}

// Get the IoT tenant name from an IoT project.
// This is not in any way encoded
func (project *IoTProject) TenantName() string {
	return util.TenantNameForObject(project)
}

func (config *IoTConfig) WantDefaultRoutes(endpoint EndpointConfig) bool {

	if endpoint.EnableDefaultRoute != nil {
		return *endpoint.EnableDefaultRoute
	}

	return config.Spec.WantDefaultRoutes()
}

func (spec *IoTConfigSpec) WantDefaultRoutes() bool {

	if spec.EnableDefaultRoutes != nil {
		return *spec.EnableDefaultRoutes
	}

	return util.IsOpenshift()

}

func (config *IoTConfig) GetImageOverrides() map[string]v1beta1.ImageOverride {
	return config.Spec.ImageOverrides
}

func (spec *IoTConfigSpec) GetImageOverrides() map[string]v1beta1.ImageOverride {
	return spec.ImageOverrides
}

func (spec *IoTConfigSpec) HasNoInterServiceConfig() bool {
	if spec.InterServiceCertificates == nil {
		return true
	}

	return spec.InterServiceCertificates.ServiceCAStrategy == nil && spec.InterServiceCertificates.SecretCertificatesStrategy == nil
}

// Evaluates if the adapter endpoint uses a custom certificate setup
func (a *EndpointConfig) HasCustomCertificate() bool {
	return a.SecretNameStrategy != nil
}

func (c *IoTConfigSpec) DefaultNativeTlsRequired() bool {
	if c.JavaDefaults.RequireNativeTls != nil {
		return *c.JavaDefaults.RequireNativeTls
	}
	return util.DefaultJavaRequiresNativeTls()
}

//region MeshConfig

func (m MeshConfig) TlsVersions(config *IoTConfig) []string {
	if len(m.Tls.Versions) > 0 {
		return m.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

//endregion

// region CommonAdapterConfig

func (c *CommonAdapterConfig) IsNativeTlsRequired(config *IoTConfig) bool {
	if c.Containers.Adapter.RequireNativeTls == nil {
		return config.Spec.DefaultNativeTlsRequired()
	}
	return *c.Containers.Adapter.RequireNativeTls
}

func (c *CommonAdapterConfig) TlsVersions(config *IoTConfig) []string {
	if len(c.Tls.Versions) > 0 {
		return c.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

// endregion

// region CommonServiceConfig

func (c *CommonServiceConfig) IsNativeTlsRequired(config *IoTConfig) bool {
	if c.Container.RequireNativeTls == nil {
		return config.Spec.DefaultNativeTlsRequired()
	}
	return *c.Container.RequireNativeTls
}

func (c *CommonServiceConfig) TlsVersions(config *IoTConfig) []string {
	if len(c.Tls.Versions) > 0 {
		return c.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

// endregion

func (p *IoTProjectStatus) GetProjectCondition(t ProjectConditionType) *ProjectCondition {
	for i, c := range p.Conditions {
		if c.Type == t {
			return &p.Conditions[i]
		}
	}

	nc := ProjectCondition{
		Type: t,
		CommonCondition: CommonCondition{
			Status:             corev1.ConditionUnknown,
			LastTransitionTime: metav1.Now(),
		},
	}

	p.Conditions = append(p.Conditions, nc)

	return &p.Conditions[len(p.Conditions)-1]
}

func (config *IoTConfigStatus) GetConfigCondition(t ConfigConditionType) *ConfigCondition {
	for i, c := range config.Conditions {
		if c.Type == t {
			return &config.Conditions[i]
		}
	}

	nc := ConfigCondition{
		Type: t,
		CommonCondition: CommonCondition{
			Status:             corev1.ConditionUnknown,
			LastTransitionTime: metav1.Now(),
		},
	}

	config.Conditions = append(config.Conditions, nc)

	return &config.Conditions[len(config.Conditions)-1]
}

//region CommonCondition

func (config *IoTConfigStatus) RemoveCondition(t ConfigConditionType) {

	for i := len(config.Conditions) - 1; i >= 0; i-- {
		c := config.Conditions[i]
		if c.Type == t {
			config.Conditions = append(
				config.Conditions[:i],
				config.Conditions[i+1:]...,
			)
		}
	}

}

func (c *CommonCondition) SetStatusAsBoolean(status bool, reason string, message string) {
	if status {
		c.SetStatus(corev1.ConditionTrue, reason, message)
	} else {
		c.SetStatus(corev1.ConditionFalse, reason, message)
	}
}

func (c *CommonCondition) SetStatus(status corev1.ConditionStatus, reason string, message string) {

	if c.Status != status {
		c.Status = status
		c.LastTransitionTime = metav1.Now()
	}

	c.Reason = reason
	c.Message = message

}

func (c *CommonCondition) IsOk() bool {
	return c.Status == corev1.ConditionTrue
}

// Sets the status to "True". "Reason" and "Message" to empty.
func (c *CommonCondition) SetStatusOk() {
	c.SetStatus(corev1.ConditionTrue, "", "")
}

func (c *CommonCondition) SetStatusError(reason string, message string) {
	c.SetStatus(corev1.ConditionFalse, reason, message)
}

// Call SetStatusOk() when "ok" is true. Otherwise calls SetStatus() with the provided
// reason and message.
func (c *CommonCondition) SetStatusOkOrElse(ok bool, reason string, message string) {
	if ok {
		c.SetStatusOk()
	} else {
		c.SetStatus(corev1.ConditionFalse, reason, message)
	}
}

// runs the provided function, using the error result as
// status for the condition.
func (c *CommonCondition) RunWith(reason string, processor func() error) error {

	err := processor()

	if err != nil {
		cause := errors.Cause(err)
		var message string
		if cause != nil {
			message = cause.Error()
		} else {
			message = err.Error()
		}
		c.SetStatus(corev1.ConditionFalse, reason, message)
	} else {
		c.SetStatusOk()
	}

	return err
}

//endregion

//region DeviceConnection

type DeviceConnectionImplementation int

const (
	DeviceConnectionDefault = iota
	DeviceConnectionIllegal
	DeviceConnectionInfinispan
	DeviceConnectionJdbc
)

func (config IoTConfig) EvalDeviceConnectionImplementation() DeviceConnectionImplementation {

	var infinispan = config.Spec.ServicesConfig.DeviceConnection.Infinispan
	if infinispan != nil && infinispan.Disabled {
		infinispan = nil
	}
	var jdbc = config.Spec.ServicesConfig.DeviceConnection.JDBC
	if jdbc != nil && jdbc.Disabled {
		jdbc = nil
	}

	if false { // this is just here to align the other lines
	} else if infinispan == nil && jdbc == nil {
		return DeviceConnectionIllegal
	} else if infinispan != nil && jdbc == nil {
		return DeviceConnectionInfinispan
	} else if infinispan == nil && jdbc != nil {
		return DeviceConnectionJdbc
	}
	return DeviceConnectionIllegal
}

//endregion

//region Common DeviceRegistry

type DeviceRegistryImplementation int

const (
	DeviceRegistryDefault = iota
	DeviceRegistryIllegal
	DeviceRegistryInfinispan
	DeviceRegistryJdbc
)

func (config IoTConfig) EvalDeviceRegistryImplementation() DeviceRegistryImplementation {

	var infinispan = config.Spec.ServicesConfig.DeviceRegistry.Infinispan
	if infinispan != nil && infinispan.Disabled {
		infinispan = nil
	}
	var jdbc = config.Spec.ServicesConfig.DeviceRegistry.JDBC
	if jdbc != nil && jdbc.Disabled {
		jdbc = nil
	}

	if false { // this is just here to align the other lines
	} else if infinispan == nil && jdbc == nil {
		return DeviceRegistryIllegal
	} else if infinispan != nil && jdbc == nil {
		return DeviceRegistryInfinispan
	} else if infinispan == nil && jdbc != nil {
		return DeviceRegistryJdbc
	}

	return DeviceRegistryIllegal
}

//endregion

//region JDBC device registry

func (r JdbcDeviceRegistry) IsSplitRegistry() (bool, error) {
	if r.Server.External != nil {
		return r.Server.External.Management != nil && r.Server.External.Adapter != nil, nil
	} else {
		return false, util.NewConfigurationError("unsupported device registry configuration")
	}
}

//endregion

// region Logging

type DefaultLoggingRenderer func(rootLogger LogLevel, loggers map[string]LogLevel) string

func (log LogbackConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string {

	// did we get overridden
	if override != "" {
		return override
	}

	// if we have an explicit configuration
	if log.Logback != "" {
		// use it
		return log.Logback
	}

	// if we have a local level or loggers
	if log.Level != "" || log.Loggers != nil {
		// if the local root level is empty
		if log.Level == "" {
			// use the global root level
			log.Level = config.Spec.Logging.Level
		}
		defaultRenderer(log.Level, log.Loggers)
	}

	// if we have a global explicit logback config
	if config.Spec.Logging.Defaults.Logback != "" {
		// use it
		return config.Spec.Logging.Defaults.Logback
	}

	// generate some reasonable defaults
	return defaultRenderer(config.Spec.Logging.Level, config.Spec.Logging.Loggers)

}

func (csc CommonServiceConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string {
	return csc.Container.Logback.RenderConfiguration(config, defaultRenderer, override)
}

// endregion
<commit_msg>Prevent global logging to overide local configuration (#4632)<commit_after>/*
 * Copyright 2019-2020, EnMasse authors.
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */

package v1alpha1

import (
	"github.com/enmasseproject/enmasse/pkg/apis/enmasse/v1beta1"
	"github.com/enmasseproject/enmasse/pkg/util"
	"github.com/pkg/errors"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ v1beta1.ImageOverridesProvider = &IoTConfig{}
var _ v1beta1.ImageOverridesProvider = &IoTConfigSpec{}

// Get the IoT tenant name from an IoT project.
// This is not in any way encoded
func (project *IoTProject) TenantName() string {
	return util.TenantNameForObject(project)
}

func (config *IoTConfig) WantDefaultRoutes(endpoint EndpointConfig) bool {

	if endpoint.EnableDefaultRoute != nil {
		return *endpoint.EnableDefaultRoute
	}

	return config.Spec.WantDefaultRoutes()
}

func (spec *IoTConfigSpec) WantDefaultRoutes() bool {

	if spec.EnableDefaultRoutes != nil {
		return *spec.EnableDefaultRoutes
	}

	return util.IsOpenshift()

}

func (config *IoTConfig) GetImageOverrides() map[string]v1beta1.ImageOverride {
	return config.Spec.ImageOverrides
}

func (spec *IoTConfigSpec) GetImageOverrides() map[string]v1beta1.ImageOverride {
	return spec.ImageOverrides
}

func (spec *IoTConfigSpec) HasNoInterServiceConfig() bool {
	if spec.InterServiceCertificates == nil {
		return true
	}

	return spec.InterServiceCertificates.ServiceCAStrategy == nil && spec.InterServiceCertificates.SecretCertificatesStrategy == nil
}

// Evaluates if the adapter endpoint uses a custom certificate setup
func (a *EndpointConfig) HasCustomCertificate() bool {
	return a.SecretNameStrategy != nil
}

func (c *IoTConfigSpec) DefaultNativeTlsRequired() bool {
	if c.JavaDefaults.RequireNativeTls != nil {
		return *c.JavaDefaults.RequireNativeTls
	}
	return util.DefaultJavaRequiresNativeTls()
}

//region MeshConfig

func (m MeshConfig) TlsVersions(config *IoTConfig) []string {
	if len(m.Tls.Versions) > 0 {
		return m.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

//endregion

// region CommonAdapterConfig

func (c *CommonAdapterConfig) IsNativeTlsRequired(config *IoTConfig) bool {
	if c.Containers.Adapter.RequireNativeTls == nil {
		return config.Spec.DefaultNativeTlsRequired()
	}
	return *c.Containers.Adapter.RequireNativeTls
}

func (c *CommonAdapterConfig) TlsVersions(config *IoTConfig) []string {
	if len(c.Tls.Versions) > 0 {
		return c.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

// endregion

// region CommonServiceConfig

func (c *CommonServiceConfig) IsNativeTlsRequired(config *IoTConfig) bool {
	if c.Container.RequireNativeTls == nil {
		return config.Spec.DefaultNativeTlsRequired()
	}
	return *c.Container.RequireNativeTls
}

func (c *CommonServiceConfig) TlsVersions(config *IoTConfig) []string {
	if len(c.Tls.Versions) > 0 {
		return c.Tls.Versions
	} else {
		return config.Spec.TlsDefaults.Versions
	}
}

// endregion

func (p *IoTProjectStatus) GetProjectCondition(t ProjectConditionType) *ProjectCondition {
	for i, c := range p.Conditions {
		if c.Type == t {
			return &p.Conditions[i]
		}
	}

	nc := ProjectCondition{
		Type: t,
		CommonCondition: CommonCondition{
			Status:             corev1.ConditionUnknown,
			LastTransitionTime: metav1.Now(),
		},
	}

	p.Conditions = append(p.Conditions, nc)

	return &p.Conditions[len(p.Conditions)-1]
}

func (config *IoTConfigStatus) GetConfigCondition(t ConfigConditionType) *ConfigCondition {
	for i, c := range config.Conditions {
		if c.Type == t {
			return &config.Conditions[i]
		}
	}

	nc := ConfigCondition{
		Type: t,
		CommonCondition: CommonCondition{
			Status:             corev1.ConditionUnknown,
			LastTransitionTime: metav1.Now(),
		},
	}

	config.Conditions = append(config.Conditions, nc)

	return &config.Conditions[len(config.Conditions)-1]
}

//region CommonCondition

func (config *IoTConfigStatus) RemoveCondition(t ConfigConditionType) {

	for i := len(config.Conditions) - 1; i >= 0; i-- {
		c := config.Conditions[i]
		if c.Type == t {
			config.Conditions = append(
				config.Conditions[:i],
				config.Conditions[i+1:]...,
			)
		}
	}

}

func (c *CommonCondition) SetStatusAsBoolean(status bool, reason string, message string) {
	if status {
		c.SetStatus(corev1.ConditionTrue, reason, message)
	} else {
		c.SetStatus(corev1.ConditionFalse, reason, message)
	}
}

func (c *CommonCondition) SetStatus(status corev1.ConditionStatus, reason string, message string) {

	if c.Status != status {
		c.Status = status
		c.LastTransitionTime = metav1.Now()
	}

	c.Reason = reason
	c.Message = message

}

func (c *CommonCondition) IsOk() bool {
	return c.Status == corev1.ConditionTrue
}

// Sets the status to "True". "Reason" and "Message" to empty.
func (c *CommonCondition) SetStatusOk() {
	c.SetStatus(corev1.ConditionTrue, "", "")
}

func (c *CommonCondition) SetStatusError(reason string, message string) {
	c.SetStatus(corev1.ConditionFalse, reason, message)
}

// Call SetStatusOk() when "ok" is true. Otherwise calls SetStatus() with the provided
// reason and message.
func (c *CommonCondition) SetStatusOkOrElse(ok bool, reason string, message string) {
	if ok {
		c.SetStatusOk()
	} else {
		c.SetStatus(corev1.ConditionFalse, reason, message)
	}
}

// runs the provided function, using the error result as
// status for the condition.
func (c *CommonCondition) RunWith(reason string, processor func() error) error {

	err := processor()

	if err != nil {
		cause := errors.Cause(err)
		var message string
		if cause != nil {
			message = cause.Error()
		} else {
			message = err.Error()
		}
		c.SetStatus(corev1.ConditionFalse, reason, message)
	} else {
		c.SetStatusOk()
	}

	return err
}

//endregion

//region DeviceConnection

type DeviceConnectionImplementation int

const (
	DeviceConnectionDefault = iota
	DeviceConnectionIllegal
	DeviceConnectionInfinispan
	DeviceConnectionJdbc
)

func (config IoTConfig) EvalDeviceConnectionImplementation() DeviceConnectionImplementation {

	var infinispan = config.Spec.ServicesConfig.DeviceConnection.Infinispan
	if infinispan != nil && infinispan.Disabled {
		infinispan = nil
	}
	var jdbc = config.Spec.ServicesConfig.DeviceConnection.JDBC
	if jdbc != nil && jdbc.Disabled {
		jdbc = nil
	}

	if false { // this is just here to align the other lines
	} else if infinispan == nil && jdbc == nil {
		return DeviceConnectionIllegal
	} else if infinispan != nil && jdbc == nil {
		return DeviceConnectionInfinispan
	} else if infinispan == nil && jdbc != nil {
		return DeviceConnectionJdbc
	}
	return DeviceConnectionIllegal
}

//endregion

//region Common DeviceRegistry

type DeviceRegistryImplementation int

const (
	DeviceRegistryDefault = iota
	DeviceRegistryIllegal
	DeviceRegistryInfinispan
	DeviceRegistryJdbc
)

func (config IoTConfig) EvalDeviceRegistryImplementation() DeviceRegistryImplementation {

	var infinispan = config.Spec.ServicesConfig.DeviceRegistry.Infinispan
	if infinispan != nil && infinispan.Disabled {
		infinispan = nil
	}
	var jdbc = config.Spec.ServicesConfig.DeviceRegistry.JDBC
	if jdbc != nil && jdbc.Disabled {
		jdbc = nil
	}

	if false { // this is just here to align the other lines
	} else if infinispan == nil && jdbc == nil {
		return DeviceRegistryIllegal
	} else if infinispan != nil && jdbc == nil {
		return DeviceRegistryInfinispan
	} else if infinispan == nil && jdbc != nil {
		return DeviceRegistryJdbc
	}

	return DeviceRegistryIllegal
}

//endregion

//region JDBC device registry

func (r JdbcDeviceRegistry) IsSplitRegistry() (bool, error) {
	if r.Server.External != nil {
		return r.Server.External.Management != nil && r.Server.External.Adapter != nil, nil
	} else {
		return false, util.NewConfigurationError("unsupported device registry configuration")
	}
}

//endregion

// region Logging

type DefaultLoggingRenderer func(rootLogger LogLevel, loggers map[string]LogLevel) string

func (log LogbackConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string {

	// did we get overridden
	if override != "" {
		return override
	}

	// if we have an explicit configuration
	if log.Logback != "" {
		// use it
		return log.Logback
	}

	// if we have a local level or loggers
	if log.Level != "" || log.Loggers != nil {
		// if the local root level is empty
		if log.Level == "" {
			// use the global root level
			log.Level = config.Spec.Logging.Level
		}
		return defaultRenderer(log.Level, log.Loggers)
	}

	// if we have a global explicit logback config
	if config.Spec.Logging.Defaults.Logback != "" {
		// use it
		return config.Spec.Logging.Defaults.Logback
	}

	// generate some reasonable defaults
	return defaultRenderer(config.Spec.Logging.Level, config.Spec.Logging.Loggers)

}

func (csc CommonServiceConfig) RenderConfiguration(config *IoTConfig, defaultRenderer DefaultLoggingRenderer, override string) string {
	return csc.Container.Logback.RenderConfiguration(config, defaultRenderer, override)
}

// endregion
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package delete

import (
	"time"

	"github.com/spf13/cobra"

	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/dynamic"
)

// DeleteFlags composes common printer flag structs
// used for commands requiring deletion logic.
type DeleteFlags struct {
	FileNameFlags *genericclioptions.FileNameFlags
	LabelSelector *string
	FieldSelector *string

	All            *bool
	AllNamespaces  *bool
	Cascade        *bool
	Force          *bool
	GracePeriod    *int
	IgnoreNotFound *bool
	Now            *bool
	Timeout        *time.Duration
	Wait           *bool
	Output         *string
	Raw            *string
}

func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams genericclioptions.IOStreams) *DeleteOptions {
	options := &DeleteOptions{
		DynamicClient: dynamicClient,
		IOStreams:     streams,
	}

	// add filename options
	if f.FileNameFlags != nil {
		options.FilenameOptions = f.FileNameFlags.ToOptions()
	}
	if f.LabelSelector != nil {
		options.LabelSelector = *f.LabelSelector
	}
	if f.FieldSelector != nil {
		options.FieldSelector = *f.FieldSelector
	}

	// add output format
	if f.Output != nil {
		options.Output = *f.Output
	}

	if f.All != nil {
		options.DeleteAll = *f.All
	}
	if f.AllNamespaces != nil {
		options.DeleteAllNamespaces = *f.AllNamespaces
	}
	if f.Cascade != nil {
		options.Cascade = *f.Cascade
	}
	if f.Force != nil {
		options.ForceDeletion = *f.Force
	}
	if f.GracePeriod != nil {
		options.GracePeriod = *f.GracePeriod
	}
	if f.IgnoreNotFound != nil {
		options.IgnoreNotFound = *f.IgnoreNotFound
	}
	if f.Now != nil {
		options.DeleteNow = *f.Now
	}
	if f.Timeout != nil {
		options.Timeout = *f.Timeout
	}
	if f.Wait != nil {
		options.WaitForDeletion = *f.Wait
	}
	if f.Raw != nil {
		options.Raw = *f.Raw
	}

	return options
}

func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
	f.FileNameFlags.AddFlags(cmd.Flags())
	if f.LabelSelector != nil {
		cmd.Flags().StringVarP(f.LabelSelector, "selector", "l", *f.LabelSelector, "Selector (label query) to filter on, not including uninitialized ones.")
	}
	if f.FieldSelector != nil {
		cmd.Flags().StringVarP(f.FieldSelector, "field-selector", "", *f.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
	}
	if f.All != nil {
		cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
	}
	if f.AllNamespaces != nil {
		cmd.Flags().BoolVarP(f.AllNamespaces, "all-namespaces", "A", *f.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
	}
	if f.Force != nil {
		cmd.Flags().BoolVar(f.Force, "force", *f.Force, "If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
	}
	if f.Cascade != nil {
		cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).  Default true.")
	}
	if f.Now != nil {
		cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
	}
	if f.GracePeriod != nil {
		cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
	}
	if f.Timeout != nil {
		cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
	}
	if f.IgnoreNotFound != nil {
		cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
	}
	if f.Wait != nil {
		cmd.Flags().BoolVar(f.Wait, "wait", *f.Wait, "If true, wait for resources to be gone before returning. This waits for finalizers.")
	}
	if f.Output != nil {
		cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
	}
	if f.Raw != nil {
		cmd.Flags().StringVar(f.Raw, "raw", *f.Raw, "Raw URI to DELETE to the server.  Uses the transport specified by the kubeconfig file.")
	}
}

// NewDeleteCommandFlags provides default flags and values for use with the "delete" command
func NewDeleteCommandFlags(usage string) *DeleteFlags {
	cascade := true
	gracePeriod := -1

	// setup command defaults
	all := false
	allNamespaces := false
	force := false
	ignoreNotFound := false
	now := false
	output := ""
	labelSelector := ""
	fieldSelector := ""
	timeout := time.Duration(0)
	wait := true
	raw := ""

	filenames := []string{}
	recursive := false
	kustomize := ""

	return &DeleteFlags{
		// Not using helpers.go since it provides function to add '-k' for FileNameOptions, but not FileNameFlags
		FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},
		LabelSelector: &labelSelector,
		FieldSelector: &fieldSelector,

		Cascade:     &cascade,
		GracePeriod: &gracePeriod,

		All:            &all,
		AllNamespaces:  &allNamespaces,
		Force:          &force,
		IgnoreNotFound: &ignoreNotFound,
		Now:            &now,
		Timeout:        &timeout,
		Wait:           &wait,
		Output:         &output,
		Raw:            &raw,
	}
}

// NewDeleteFlags provides default flags and values for use in commands outside of "delete"
func NewDeleteFlags(usage string) *DeleteFlags {
	cascade := true
	gracePeriod := -1

	force := false
	timeout := time.Duration(0)
	wait := false

	filenames := []string{}
	kustomize := ""
	recursive := false

	return &DeleteFlags{
		FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},

		Cascade:     &cascade,
		GracePeriod: &gracePeriod,

		// add non-defaults
		Force:   &force,
		Timeout: &timeout,
		Wait:    &wait,
	}
}
<commit_msg>Mention background deletion in kubectl delete cascade help<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package delete

import (
	"time"

	"github.com/spf13/cobra"

	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/dynamic"
)

// DeleteFlags composes common printer flag structs
// used for commands requiring deletion logic.
type DeleteFlags struct {
	FileNameFlags *genericclioptions.FileNameFlags
	LabelSelector *string
	FieldSelector *string

	All            *bool
	AllNamespaces  *bool
	Cascade        *bool
	Force          *bool
	GracePeriod    *int
	IgnoreNotFound *bool
	Now            *bool
	Timeout        *time.Duration
	Wait           *bool
	Output         *string
	Raw            *string
}

func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams genericclioptions.IOStreams) *DeleteOptions {
	options := &DeleteOptions{
		DynamicClient: dynamicClient,
		IOStreams:     streams,
	}

	// add filename options
	if f.FileNameFlags != nil {
		options.FilenameOptions = f.FileNameFlags.ToOptions()
	}
	if f.LabelSelector != nil {
		options.LabelSelector = *f.LabelSelector
	}
	if f.FieldSelector != nil {
		options.FieldSelector = *f.FieldSelector
	}

	// add output format
	if f.Output != nil {
		options.Output = *f.Output
	}

	if f.All != nil {
		options.DeleteAll = *f.All
	}
	if f.AllNamespaces != nil {
		options.DeleteAllNamespaces = *f.AllNamespaces
	}
	if f.Cascade != nil {
		options.Cascade = *f.Cascade
	}
	if f.Force != nil {
		options.ForceDeletion = *f.Force
	}
	if f.GracePeriod != nil {
		options.GracePeriod = *f.GracePeriod
	}
	if f.IgnoreNotFound != nil {
		options.IgnoreNotFound = *f.IgnoreNotFound
	}
	if f.Now != nil {
		options.DeleteNow = *f.Now
	}
	if f.Timeout != nil {
		options.Timeout = *f.Timeout
	}
	if f.Wait != nil {
		options.WaitForDeletion = *f.Wait
	}
	if f.Raw != nil {
		options.Raw = *f.Raw
	}

	return options
}

func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
	f.FileNameFlags.AddFlags(cmd.Flags())
	if f.LabelSelector != nil {
		cmd.Flags().StringVarP(f.LabelSelector, "selector", "l", *f.LabelSelector, "Selector (label query) to filter on, not including uninitialized ones.")
	}
	if f.FieldSelector != nil {
		cmd.Flags().StringVarP(f.FieldSelector, "field-selector", "", *f.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
	}
	if f.All != nil {
		cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
	}
	if f.AllNamespaces != nil {
		cmd.Flags().BoolVarP(f.AllNamespaces, "all-namespaces", "A", *f.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
	}
	if f.Force != nil {
		cmd.Flags().BoolVar(f.Force, "force", *f.Force, "If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
	}
	if f.Cascade != nil {
		cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, run background cascade deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).  Default true.")
	}
	if f.Now != nil {
		cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
	}
	if f.GracePeriod != nil {
		cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
	}
	if f.Timeout != nil {
		cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
	}
	if f.IgnoreNotFound != nil {
		cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
	}
	if f.Wait != nil {
		cmd.Flags().BoolVar(f.Wait, "wait", *f.Wait, "If true, wait for resources to be gone before returning. This waits for finalizers.")
	}
	if f.Output != nil {
		cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
	}
	if f.Raw != nil {
		cmd.Flags().StringVar(f.Raw, "raw", *f.Raw, "Raw URI to DELETE to the server.  Uses the transport specified by the kubeconfig file.")
	}
}

// NewDeleteCommandFlags provides default flags and values for use with the "delete" command
func NewDeleteCommandFlags(usage string) *DeleteFlags {
	cascade := true
	gracePeriod := -1

	// setup command defaults
	all := false
	allNamespaces := false
	force := false
	ignoreNotFound := false
	now := false
	output := ""
	labelSelector := ""
	fieldSelector := ""
	timeout := time.Duration(0)
	wait := true
	raw := ""

	filenames := []string{}
	recursive := false
	kustomize := ""

	return &DeleteFlags{
		// Not using helpers.go since it provides function to add '-k' for FileNameOptions, but not FileNameFlags
		FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},
		LabelSelector: &labelSelector,
		FieldSelector: &fieldSelector,

		Cascade:     &cascade,
		GracePeriod: &gracePeriod,

		All:            &all,
		AllNamespaces:  &allNamespaces,
		Force:          &force,
		IgnoreNotFound: &ignoreNotFound,
		Now:            &now,
		Timeout:        &timeout,
		Wait:           &wait,
		Output:         &output,
		Raw:            &raw,
	}
}

// NewDeleteFlags provides default flags and values for use in commands outside of "delete"
func NewDeleteFlags(usage string) *DeleteFlags {
	cascade := true
	gracePeriod := -1

	force := false
	timeout := time.Duration(0)
	wait := false

	filenames := []string{}
	kustomize := ""
	recursive := false

	return &DeleteFlags{
		FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},

		Cascade:     &cascade,
		GracePeriod: &gracePeriod,

		// add non-defaults
		Force:   &force,
		Timeout: &timeout,
		Wait:    &wait,
	}
}
<|endoftext|>


<commit_before>package router

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/router"
	controllerfactory "github.com/openshift/origin/pkg/router/controller/factory"
	templateplugin "github.com/openshift/origin/plugins/router/template"
)

const longCommandDesc = `
Start an OpenShift router

This command launches a router connected to your OpenShift master. The router listens for routes and endpoints
created by users and keeps a local router configuration up to date with those changes.
`

type templateRouterConfig struct {
	Config       *clientcmd.Config
	TemplateFile string
	ReloadScript string
}

// NewCommndTemplateRouter provides CLI handler for the template router backend
func NewCommandTemplateRouter(name string) *cobra.Command {
	cfg := &templateRouterConfig{
		Config: clientcmd.NewConfig(),
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s%s", name, clientcmd.ConfigSyntax),
		Short: "Start an OpenShift router",
		Long:  longCommandDesc,
		Run: func(c *cobra.Command, args []string) {
			plugin, err := makeTemplatePlugin(cfg)
			if err != nil {
				glog.Fatal(err)
			}

			if err = start(cfg.Config, plugin); err != nil {
				glog.Fatal(err)
			}
		},
	}

	flag := cmd.Flags()
	cfg.Config.Bind(flag)
	flag.StringVar(&cfg.TemplateFile, "template", util.Env("TEMPLATE_FILE", ""), "The path to the template file to use")
	flag.StringVar(&cfg.ReloadScript, "reload", util.Env("RELOAD_SCRIPT", ""), "The path to the reload script to use")

	return cmd
}

func makeTemplatePlugin(cfg *templateRouterConfig) (*templateplugin.TemplatePlugin, error) {
	return templateplugin.NewTemplatePlugin(cfg.TemplateFile, cfg.ReloadScript)
}

// start launches the load balancer.
func start(cfg *clientcmd.Config, plugin router.Plugin) error {
	kubeClient, osClient, err := cfg.Clients()
	if err != nil {
		return err
	}

	factory := controllerfactory.RouterControllerFactory{kubeClient, osClient}
	controller := factory.Create(plugin)
	controller.Run()

	select {}

	return nil
}
<commit_msg>Fix bug #686<commit_after>package router

import (
	"errors"
	"fmt"

	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util"
	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/router"
	controllerfactory "github.com/openshift/origin/pkg/router/controller/factory"
	templateplugin "github.com/openshift/origin/plugins/router/template"
)

const longCommandDesc = `
Start an OpenShift router

This command launches a router connected to your OpenShift master. The router listens for routes and endpoints
created by users and keeps a local router configuration up to date with those changes.
`

type templateRouterConfig struct {
	Config       *clientcmd.Config
	TemplateFile string
	ReloadScript string
}

// NewCommndTemplateRouter provides CLI handler for the template router backend
func NewCommandTemplateRouter(name string) *cobra.Command {
	cfg := &templateRouterConfig{
		Config: clientcmd.NewConfig(),
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s%s", name, clientcmd.ConfigSyntax),
		Short: "Start an OpenShift router",
		Long:  longCommandDesc,
		Run: func(c *cobra.Command, args []string) {
			plugin, err := makeTemplatePlugin(cfg)
			if err != nil {
				glog.Fatal(err)
			}

			if err = start(cfg.Config, plugin); err != nil {
				glog.Fatal(err)
			}
		},
	}

	flag := cmd.Flags()
	cfg.Config.Bind(flag)
	flag.StringVar(&cfg.TemplateFile, "template", util.Env("TEMPLATE_FILE", ""), "The path to the template file to use")
	flag.StringVar(&cfg.ReloadScript, "reload", util.Env("RELOAD_SCRIPT", ""), "The path to the reload script to use")

	return cmd
}

func makeTemplatePlugin(cfg *templateRouterConfig) (*templateplugin.TemplatePlugin, error) {
	if cfg.TemplateFile == "" {
		return nil, errors.New("Template file must be specified")
	}

	if cfg.ReloadScript == "" {
		return nil, errors.New("Reload script must be specified")
	}

	return templateplugin.NewTemplatePlugin(cfg.TemplateFile, cfg.ReloadScript)
}

// start launches the load balancer.
func start(cfg *clientcmd.Config, plugin router.Plugin) error {
	kubeClient, osClient, err := cfg.Clients()
	if err != nil {
		return err
	}

	factory := controllerfactory.RouterControllerFactory{kubeClient, osClient}
	controller := factory.Create(plugin)
	controller.Run()

	select {}

	return nil
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"fmt"
	"time"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/pkg/errors"
)

// Operation holds information about a single LXD operation running on a node
// in the cluster.
type Operation struct {
	ID          int64         // Stable database identifier
	UUID        string        // User-visible identifier
	NodeAddress string        // Address of the node the operation is running on
	Type        OperationType // Type of the operation
}

// GetLocalOperations returns all operations associated with this node.
func (c *ClusterTx) GetLocalOperations() ([]Operation, error) {
	return c.operations("node_id=?", c.nodeID)
}

// GetLocalOperationsUUIDs returns the UUIDs of all operations associated with this
// node.
func (c *ClusterTx) GetLocalOperationsUUIDs() ([]string, error) {
	stmt := "SELECT uuid FROM operations WHERE node_id=?"
	return query.SelectStrings(c.tx, stmt, c.nodeID)
}

// GetNodesWithRunningOperations returns a list of nodes that have running operations
func (c *ClusterTx) GetNodesWithRunningOperations(project string) ([]string, error) {
	stmt := `
SELECT DISTINCT nodes.address
  FROM operations
  LEFT OUTER JOIN projects ON projects.id = operations.project_id
  JOIN nodes ON nodes.id = operations.node_id
 WHERE projects.name = ? OR operations.project_id IS NULL
`
	return query.SelectStrings(c.tx, stmt, project)
}

// GetOnlineNodesWithRunningOperationsOfType returns a list of online nodes that have running operations of type.
func (c *ClusterTx) GetOnlineNodesWithRunningOperationsOfType(projectName string, opType OperationType) ([]string, error) {
	var addresses []string

	offlineThreshold, err := c.GetNodeOfflineThreshold()
	if err != nil {
		return nil, err
	}

	stmt := `
SELECT DISTINCT nodes.address, nodes.heartbeat
  FROM operations
  LEFT OUTER JOIN projects ON projects.id = operations.project_id
  JOIN nodes ON nodes.id = operations.node_id
 WHERE (projects.name = ? OR operations.project_id IS NULL) AND type = ?
`
	rows, err := c.tx.Query(stmt, projectName, opType)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var address string
		var heartbeat time.Time

		err := rows.Scan(&address, &heartbeat)
		if err != nil {
			return nil, err
		}

		if nodeIsOffline(offlineThreshold, heartbeat) {
			continue
		}

		addresses = append(addresses, address)
	}
	if rows.Err() != nil {
		return nil, err
	}

	return query.SelectStrings(c.tx, stmt, projectName)
}

// GetOperationWithID returns the operation with the given ID.
func (c *ClusterTx) GetOperationWithID(opID int) (Operation, error) {
	null := Operation{}
	operations, err := c.operations("id=?", opID)
	if err != nil {
		return null, err
	}
	switch len(operations) {
	case 0:
		return null, ErrNoSuchObject
	case 1:
		return operations[0], nil
	default:
		return null, fmt.Errorf("More than one operation matches")
	}
}

// GetOperationByUUID returns the operation with the given UUID.
func (c *ClusterTx) GetOperationByUUID(uuid string) (Operation, error) {
	null := Operation{}
	operations, err := c.operations("uuid=?", uuid)
	if err != nil {
		return null, err
	}
	switch len(operations) {
	case 0:
		return null, ErrNoSuchObject
	case 1:
		return operations[0], nil
	default:
		return null, fmt.Errorf("More than one operation matches")
	}
}

// CreateOperation adds a new operations to the table.
func (c *ClusterTx) CreateOperation(project, uuid string, typ OperationType) (int64, error) {
	var projectID interface{}

	if project != "" {
		var err error
		projectID, err = c.GetProjectID(project)
		if err != nil {
			return -1, errors.Wrap(err, "Fetch project ID")
		}
	} else {
		projectID = nil
	}

	columns := []string{"uuid", "node_id", "type", "project_id"}
	values := []interface{}{uuid, c.nodeID, typ, projectID}
	return query.UpsertObject(c.tx, "operations", columns, values)
}

// RemoveOperation removes the operation with the given UUID.
func (c *ClusterTx) RemoveOperation(uuid string) error {
	result, err := c.tx.Exec("DELETE FROM operations WHERE uuid=?", uuid)
	if err != nil {
		return err
	}
	n, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return fmt.Errorf("query deleted %d rows instead of 1", n)
	}
	return nil
}

// Remove all operations for the given node.
func (c *ClusterTx) removeNodeOperations(nodeID int64) error {
	_, err := c.tx.Exec("DELETE FROM operations WHERE node_id=?", nodeID)
	if err != nil {
		return err
	}

	return nil
}

// Operations returns all operations in the cluster, filtered by the given clause.
func (c *ClusterTx) operations(where string, args ...interface{}) ([]Operation, error) {
	operations := []Operation{}
	dest := func(i int) []interface{} {
		operations = append(operations, Operation{})
		return []interface{}{
			&operations[i].ID,
			&operations[i].UUID,
			&operations[i].NodeAddress,
			&operations[i].Type,
		}
	}
	sql := `
SELECT operations.id, uuid, nodes.address, type FROM operations JOIN nodes ON nodes.id = node_id `
	if where != "" {
		sql += fmt.Sprintf("WHERE %s ", where)
	}
	sql += "ORDER BY operations.id"
	stmt, err := c.tx.Prepare(sql)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to fetch operations")
	}
	return operations, nil
}
<commit_msg>lxd/db/operations: Import ordering<commit_after>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"fmt"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db/query"
)

// Operation holds information about a single LXD operation running on a node
// in the cluster.
type Operation struct {
	ID          int64         // Stable database identifier
	UUID        string        // User-visible identifier
	NodeAddress string        // Address of the node the operation is running on
	Type        OperationType // Type of the operation
}

// GetLocalOperations returns all operations associated with this node.
func (c *ClusterTx) GetLocalOperations() ([]Operation, error) {
	return c.operations("node_id=?", c.nodeID)
}

// GetLocalOperationsUUIDs returns the UUIDs of all operations associated with this
// node.
func (c *ClusterTx) GetLocalOperationsUUIDs() ([]string, error) {
	stmt := "SELECT uuid FROM operations WHERE node_id=?"
	return query.SelectStrings(c.tx, stmt, c.nodeID)
}

// GetNodesWithRunningOperations returns a list of nodes that have running operations
func (c *ClusterTx) GetNodesWithRunningOperations(project string) ([]string, error) {
	stmt := `
SELECT DISTINCT nodes.address
  FROM operations
  LEFT OUTER JOIN projects ON projects.id = operations.project_id
  JOIN nodes ON nodes.id = operations.node_id
 WHERE projects.name = ? OR operations.project_id IS NULL
`
	return query.SelectStrings(c.tx, stmt, project)
}

// GetOnlineNodesWithRunningOperationsOfType returns a list of online nodes that have running operations of type.
func (c *ClusterTx) GetOnlineNodesWithRunningOperationsOfType(projectName string, opType OperationType) ([]string, error) {
	var addresses []string

	offlineThreshold, err := c.GetNodeOfflineThreshold()
	if err != nil {
		return nil, err
	}

	stmt := `
SELECT DISTINCT nodes.address, nodes.heartbeat
  FROM operations
  LEFT OUTER JOIN projects ON projects.id = operations.project_id
  JOIN nodes ON nodes.id = operations.node_id
 WHERE (projects.name = ? OR operations.project_id IS NULL) AND type = ?
`
	rows, err := c.tx.Query(stmt, projectName, opType)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var address string
		var heartbeat time.Time

		err := rows.Scan(&address, &heartbeat)
		if err != nil {
			return nil, err
		}

		if nodeIsOffline(offlineThreshold, heartbeat) {
			continue
		}

		addresses = append(addresses, address)
	}
	if rows.Err() != nil {
		return nil, err
	}

	return query.SelectStrings(c.tx, stmt, projectName)
}

// GetOperationWithID returns the operation with the given ID.
func (c *ClusterTx) GetOperationWithID(opID int) (Operation, error) {
	null := Operation{}
	operations, err := c.operations("id=?", opID)
	if err != nil {
		return null, err
	}
	switch len(operations) {
	case 0:
		return null, ErrNoSuchObject
	case 1:
		return operations[0], nil
	default:
		return null, fmt.Errorf("More than one operation matches")
	}
}

// GetOperationByUUID returns the operation with the given UUID.
func (c *ClusterTx) GetOperationByUUID(uuid string) (Operation, error) {
	null := Operation{}
	operations, err := c.operations("uuid=?", uuid)
	if err != nil {
		return null, err
	}
	switch len(operations) {
	case 0:
		return null, ErrNoSuchObject
	case 1:
		return operations[0], nil
	default:
		return null, fmt.Errorf("More than one operation matches")
	}
}

// CreateOperation adds a new operations to the table.
func (c *ClusterTx) CreateOperation(project, uuid string, typ OperationType) (int64, error) {
	var projectID interface{}

	if project != "" {
		var err error
		projectID, err = c.GetProjectID(project)
		if err != nil {
			return -1, errors.Wrap(err, "Fetch project ID")
		}
	} else {
		projectID = nil
	}

	columns := []string{"uuid", "node_id", "type", "project_id"}
	values := []interface{}{uuid, c.nodeID, typ, projectID}
	return query.UpsertObject(c.tx, "operations", columns, values)
}

// RemoveOperation removes the operation with the given UUID.
func (c *ClusterTx) RemoveOperation(uuid string) error {
	result, err := c.tx.Exec("DELETE FROM operations WHERE uuid=?", uuid)
	if err != nil {
		return err
	}
	n, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if n != 1 {
		return fmt.Errorf("query deleted %d rows instead of 1", n)
	}
	return nil
}

// Remove all operations for the given node.
func (c *ClusterTx) removeNodeOperations(nodeID int64) error {
	_, err := c.tx.Exec("DELETE FROM operations WHERE node_id=?", nodeID)
	if err != nil {
		return err
	}

	return nil
}

// Operations returns all operations in the cluster, filtered by the given clause.
func (c *ClusterTx) operations(where string, args ...interface{}) ([]Operation, error) {
	operations := []Operation{}
	dest := func(i int) []interface{} {
		operations = append(operations, Operation{})
		return []interface{}{
			&operations[i].ID,
			&operations[i].UUID,
			&operations[i].NodeAddress,
			&operations[i].Type,
		}
	}
	sql := `
SELECT operations.id, uuid, nodes.address, type FROM operations JOIN nodes ON nodes.id = node_id `
	if where != "" {
		sql += fmt.Sprintf("WHERE %s ", where)
	}
	sql += "ORDER BY operations.id"
	stmt, err := c.tx.Prepare(sql)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, args...)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to fetch operations")
	}
	return operations, nil
}
<|endoftext|>