<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/Sirupsen/logrus"
)

type MarathonTasks struct {
	Tasks []struct {
		AppId              string `json:"appId"`
		HealthCheckResults []struct {
			Alive bool `json:"alive"`
		} `json:"healthCheckResults"`
		Host         string  `json:"host"`
		Id           string  `json:"id"`
		Ports        []int64 `json:"ports"`
		ServicePorts []int64 `json:"servicePorts"`
		StagedAt     string  `json:"stagedAt"`
		StartedAt    string  `json:"startedAt"`
		Version      string  `json:"version"`
	} `json:"tasks"`
}

type MarathonApps struct {
	Apps []struct {
		Id              string            `json:"id"`
		Labels          map[string]string `json:"labels"`
		Env             map[string]string `json:"env"`
		HealthChecks    []interface{}     `json:"healthChecks"`
		PortDefinitions []struct {
			Port     int64             `json:"port"`
			Protocol string            `json:"protocol"`
			Labels   map[string]string `json:"labels"`
		} `json:"portDefinitions"`
	} `json:"apps"`
}

func eventStream() {
	go func() {
		client := &http.Client{
			Timeout:   0 * time.Second,
			Transport: tr,
		}
		ticker := time.NewTicker(1 * time.Second)
		for _ = range ticker.C {
			var endpoint string
			for _, es := range health.Endpoints {
				if es.Healthy == true {
					endpoint = es.Endpoint
					break
				}
			}
			if endpoint == "" {
				logger.Error("all endpoints are down")
				continue
			}
			req, err := http.NewRequest("GET", endpoint+"/v2/events", nil)
			if err != nil {
				logger.WithFields(logrus.Fields{
					"error":    err.Error(),
					"endpoint": endpoint,
				}).Error("unable to create event stream request")
				continue
			}
			req.Header.Set("Accept", "text/event-stream")
			if config.User != "" {
				req.SetBasicAuth(config.User, config.Pass)
			}
			cancel := make(chan struct{})
			// initial request cancellation timer of 15s
			timer := time.AfterFunc(15*time.Second, func() {
				defer func() {
					recover()
				}()
				defer close(cancel)
				logger.Warn("event stream request was cancelled")
			})
			req.Cancel = cancel
			resp, err := client.Do(req)
			if err != nil {
				logger.WithFields(logrus.Fields{
					"error":    err.Error(),
					"endpoint": endpoint,
				}).Error("unable to access Marathon event stream")
				// expire request cancellation timer immediately
				timer.Reset(100 * time.Millisecond)
				continue
			}
			reader := bufio.NewReader(resp.Body)
			for {
				// reset request cancellation timer to 15s (should be >10s to avoid unnecessary reconnects
				// since ~10s seems to be the rate for dummy/keepalive events on the marathon event stream
				timer.Reset(15 * time.Second)
				line, err := reader.ReadString('\n')
				if err != nil {
					logger.WithFields(logrus.Fields{
						"error":    err.Error(),
						"endpoint": endpoint,
					}).Error("error reading Marathon event stream")
					resp.Body.Close()
					break
				}
				if !strings.HasPrefix(line, "event: ") {
					continue
				}
				logger.WithFields(logrus.Fields{
					"event":    strings.TrimSpace(line[6:]),
					"endpoint": endpoint,
				}).Info("marathon event received")
				select {
				case eventqueue <- true: // add reload to our queue channel, unless it is full of course.
				default:
					logger.Warn("queue is full")
				}
			}
			resp.Body.Close()
			logger.Warn("event stream connection was closed, re-opening")
		}
	}()
}

func endpointHealth() {
	go func() {
		ticker := time.NewTicker(10 * time.Second)
		for {
			select {
			case <-ticker.C:
				for i, es := range health.Endpoints {
					client := &http.Client{
						Timeout:   5 * time.Second,
						Transport: tr,
					}
					req, err := http.NewRequest("GET", es.Endpoint+"/ping", nil)
					if err != nil {
						logger.WithFields(logrus.Fields{
							"error":    err.Error(),
							"endpoint": es.Endpoint,
						}).Error("an error occurred creating endpoint health request")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = err.Error()
						continue
					}
					if config.User != "" {
						req.SetBasicAuth(config.User, config.Pass)
					}
					resp, err := client.Do(req)
					if err != nil {
						logger.WithFields(logrus.Fields{
							"error":    err.Error(),
							"endpoint": es.Endpoint,
						}).Error("endpoint is down")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = err.Error()
						continue
					}
					resp.Body.Close()
					if resp.StatusCode != 200 {
						logger.WithFields(logrus.Fields{
							"status":   resp.StatusCode,
							"endpoint": es.Endpoint,
						}).Error("endpoint check failed")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = resp.Status
						continue
					}
					health.Endpoints[i].Healthy = true
					health.Endpoints[i].Message = "OK"
				}
			}
		}
	}()
}

func eventWorker() {
	go func() {
		// a ticker channel to limit reloads to marathon, 1s is enough for now.
		ticker := time.NewTicker(1 * time.Second)
		for {
			select {
			case <-ticker.C:
				<-eventqueue
				start := time.Now()
				err := reload()
				elapsed := time.Since(start)
				if err != nil {
					logger.Error("config update failed")
					go statsCount("reload.failed", 1)
				} else {
					logger.WithFields(logrus.Fields{
						"took": elapsed,
					}).Info("config updated")
					go statsCount("reload.success", 1)
					go statsTiming("reload.time", elapsed)
				}
			}
		}
	}()
}

func fetchApps(jsontasks *MarathonTasks, jsonapps *MarathonApps) error {
	var endpoint string
	for _, es := range health.Endpoints {
		if es.Healthy == true {
			endpoint = es.Endpoint
			break
		}
	}
	if endpoint == "" {
		err := errors.New("all endpoints are down")
		return err
	}
	client := &http.Client{
		Timeout:   5 * time.Second,
		Transport: tr,
	}
	// take advantage of goroutines and run both reqs concurrent.
	appschn := make(chan error)
	taskschn := make(chan error)
	go func() {
		req, err := http.NewRequest("GET", endpoint+"/v2/tasks", nil)
		if err != nil {
			taskschn <- err
			return
		}
		req.Header.Set("Accept", "application/json")
		if config.User != "" {
			req.SetBasicAuth(config.User, config.Pass)
		}
		resp, err := client.Do(req)
		if err != nil {
			taskschn <- err
			return
		}
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		err = decoder.Decode(&jsontasks)
		if err != nil {
			taskschn <- err
			return
		}
		taskschn <- nil
	}()
	go func() {
		req, err := http.NewRequest("GET", endpoint+"/v2/apps", nil)
		if err != nil {
			appschn <- err
			return
		}
		req.Header.Set("Accept", "application/json")
		if config.User != "" {
			req.SetBasicAuth(config.User, config.Pass)
		}
		resp, err := client.Do(req)
		if err != nil {
			appschn <- err
			return
		}
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		err = decoder.Decode(&jsonapps)
		if err != nil {
			appschn <- err
			return
		}
		appschn <- nil
	}()
	appserr := <-appschn
	taskserr := <-taskschn
	if appserr != nil {
		return appserr
	}
	if taskserr != nil {
		return taskserr
	}
	return nil
}

func syncApps(jsontasks *MarathonTasks, jsonapps *MarathonApps) {
	config.Lock()
	defer config.Unlock()
	config.Apps = make(map[string]App)
	for _, app := range jsonapps.Apps {
	OUTER:
		for _, task := range jsontasks.Tasks {
			if task.AppId != app.Id {
				continue
			}
			// lets skip tasks that does not expose any ports.
			if len(task.Ports) == 0 {
				continue
			}
			if len(app.HealthChecks) > 0 {
				if len(task.HealthCheckResults) == 0 {
					// this means tasks is being deployed but not yet monitored as alive. Assume down.
					continue
				}
				alive := true
				for _, health := range task.HealthCheckResults {
					// check if health check is alive
					if health.Alive == false {
						alive = false
					}
				}
				if alive != true {
					// at least one health check has failed. Assume down.
					continue
				}
			}
			if _, ok := config.Apps[app.Id]; !ok {
				var newapp = App{}
				if s, ok := app.Labels["subdomain"]; ok {
					hosts := strings.Split(s, " ")
					for _, host := range hosts {
						newapp.Hosts = append(newapp.Hosts, host)
					}
				} else if s, ok := app.Labels["moxy_subdomain"]; ok {
					// to be compatible with moxy
					hosts := strings.Split(s, " ")
					for _, host := range hosts {
						newapp.Hosts = append(newapp.Hosts, host)
					}
				} else {
					// Return app id where '/' are replaced with '.'.
					// Needed if directories are used.
					fields := strings.FieldsFunc(app.Id,
						func(c rune) bool { return c == '/' })
					newapp.Hosts = append(newapp.Hosts, strings.Join(fields, "."))
				}
				for _, confapp := range config.Apps {
					for _, host := range confapp.Hosts {
						for _, newhost := range newapp.Hosts {
							if newhost == host {
								logger.WithFields(logrus.Fields{
									"app":       app.Id,
									"subdomain": host,
								}).Warn("duplicate subdomain label, ignoring app")
								continue OUTER
							}
						}
					}
				}
				newapp.Labels = app.Labels
				newapp.Env = app.Env
				for _, pds := range app.PortDefinitions {
					pd := PortDefinitions{
						Port:     pds.Port,
						Protocol: pds.Protocol,
						Labels:   pds.Labels,
					}
					newapp.PortDefinitions = append(newapp.PortDefinitions, pd)
				}
				config.Apps[app.Id] = newapp
			}
			newapp := config.Apps[app.Id]
			var newtask = Task{}
			newtask.Host = task.Host
			newtask.Ports = task.Ports
			newtask.ServicePorts = task.ServicePorts
			newtask.StagedAt = task.StagedAt
			newtask.StartedAt = task.StartedAt
			newtask.Version = task.Version
			newapp.Tasks = append(newapp.Tasks, newtask)
			config.Apps[app.Id] = newapp
		}
	}
}

func writeConf() error {
	template, err := template.New(filepath.Base(config.Nginx_template)).ParseFiles(config.Nginx_template)
	if err != nil {
		return err
	}

	parent := filepath.Dir(config.Nginx_config)
	tmpFile, err := ioutil.TempFile(parent, ".nginx.conf.tmp-")
	defer tmpFile.Close()

	err = template.Execute(tmpFile, config)
	if err != nil {
		return err
	}
	config.LastUpdates.LastConfigRendered = time.Now()

	err = checkConf(tmpFile.Name())
	if err != nil {
		return err
	}

	err = os.Rename(tmpFile.Name(), config.Nginx_config)
	if err != nil {
		return err
	}

	return nil
}

func checkTmpl() error {
	t, err := template.New(filepath.Base(config.Nginx_template)).ParseFiles(config.Nginx_template)
	if err != nil {
		return err
	}
	err = t.Execute(ioutil.Discard, config)
	if err != nil {
		return err
	}
	return nil
}

func checkConf(path string) error {
	cmd := exec.Command(config.Nginx_cmd, "-c", path, "-t")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run() // will wait for command to return
	if err != nil {
		msg := fmt.Sprint(err) + ": " + stderr.String()
		errstd := errors.New(msg)
		return errstd
	}
	return nil
}

func reloadNginx() error {
	cmd := exec.Command(config.Nginx_cmd, "-s", "reload")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run() // will wait for command to return
	if err != nil {
		msg := fmt.Sprint(err) + ": " + stderr.String()
		errstd := errors.New(msg)
		return errstd
	}
	return nil
}

func reload() error {
	jsontasks := MarathonTasks{}
	jsonapps := MarathonApps{}
	err := fetchApps(&jsontasks, &jsonapps)
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to sync from marathon")
		return err
	}
	syncApps(&jsontasks, &jsonapps)
	config.LastUpdates.LastSync = time.Now()
	err = writeConf()
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to generate nginx config")
		return err
	}
	config.LastUpdates.LastConfigValid = time.Now()
	err = reloadNginx()
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to reload nginx")
		return err
	}
	config.LastUpdates.LastNginxReload = time.Now()
	return nil
}
<commit_msg>improved default behavior if directories are used in marathon<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/Sirupsen/logrus"
)

type MarathonTasks struct {
	Tasks []struct {
		AppId              string `json:"appId"`
		HealthCheckResults []struct {
			Alive bool `json:"alive"`
		} `json:"healthCheckResults"`
		Host         string  `json:"host"`
		Id           string  `json:"id"`
		Ports        []int64 `json:"ports"`
		ServicePorts []int64 `json:"servicePorts"`
		StagedAt     string  `json:"stagedAt"`
		StartedAt    string  `json:"startedAt"`
		Version      string  `json:"version"`
	} `json:"tasks"`
}

type MarathonApps struct {
	Apps []struct {
		Id              string            `json:"id"`
		Labels          map[string]string `json:"labels"`
		Env             map[string]string `json:"env"`
		HealthChecks    []interface{}     `json:"healthChecks"`
		PortDefinitions []struct {
			Port     int64             `json:"port"`
			Protocol string            `json:"protocol"`
			Labels   map[string]string `json:"labels"`
		} `json:"portDefinitions"`
	} `json:"apps"`
}

func eventStream() {
	go func() {
		client := &http.Client{
			Timeout:   0 * time.Second,
			Transport: tr,
		}
		ticker := time.NewTicker(1 * time.Second)
		for _ = range ticker.C {
			var endpoint string
			for _, es := range health.Endpoints {
				if es.Healthy == true {
					endpoint = es.Endpoint
					break
				}
			}
			if endpoint == "" {
				logger.Error("all endpoints are down")
				continue
			}
			req, err := http.NewRequest("GET", endpoint+"/v2/events", nil)
			if err != nil {
				logger.WithFields(logrus.Fields{
					"error":    err.Error(),
					"endpoint": endpoint,
				}).Error("unable to create event stream request")
				continue
			}
			req.Header.Set("Accept", "text/event-stream")
			if config.User != "" {
				req.SetBasicAuth(config.User, config.Pass)
			}
			cancel := make(chan struct{})
			// initial request cancellation timer of 15s
			timer := time.AfterFunc(15*time.Second, func() {
				defer func() {
					recover()
				}()
				defer close(cancel)
				logger.Warn("event stream request was cancelled")
			})
			req.Cancel = cancel
			resp, err := client.Do(req)
			if err != nil {
				logger.WithFields(logrus.Fields{
					"error":    err.Error(),
					"endpoint": endpoint,
				}).Error("unable to access Marathon event stream")
				// expire request cancellation timer immediately
				timer.Reset(100 * time.Millisecond)
				continue
			}
			reader := bufio.NewReader(resp.Body)
			for {
				// reset request cancellation timer to 15s (should be >10s to avoid unnecessary reconnects
				// since ~10s seems to be the rate for dummy/keepalive events on the marathon event stream
				timer.Reset(15 * time.Second)
				line, err := reader.ReadString('\n')
				if err != nil {
					logger.WithFields(logrus.Fields{
						"error":    err.Error(),
						"endpoint": endpoint,
					}).Error("error reading Marathon event stream")
					resp.Body.Close()
					break
				}
				if !strings.HasPrefix(line, "event: ") {
					continue
				}
				logger.WithFields(logrus.Fields{
					"event":    strings.TrimSpace(line[6:]),
					"endpoint": endpoint,
				}).Info("marathon event received")
				select {
				case eventqueue <- true: // add reload to our queue channel, unless it is full of course.
				default:
					logger.Warn("queue is full")
				}
			}
			resp.Body.Close()
			logger.Warn("event stream connection was closed, re-opening")
		}
	}()
}

func endpointHealth() {
	go func() {
		ticker := time.NewTicker(10 * time.Second)
		for {
			select {
			case <-ticker.C:
				for i, es := range health.Endpoints {
					client := &http.Client{
						Timeout:   5 * time.Second,
						Transport: tr,
					}
					req, err := http.NewRequest("GET", es.Endpoint+"/ping", nil)
					if err != nil {
						logger.WithFields(logrus.Fields{
							"error":    err.Error(),
							"endpoint": es.Endpoint,
						}).Error("an error occurred creating endpoint health request")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = err.Error()
						continue
					}
					if config.User != "" {
						req.SetBasicAuth(config.User, config.Pass)
					}
					resp, err := client.Do(req)
					if err != nil {
						logger.WithFields(logrus.Fields{
							"error":    err.Error(),
							"endpoint": es.Endpoint,
						}).Error("endpoint is down")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = err.Error()
						continue
					}
					resp.Body.Close()
					if resp.StatusCode != 200 {
						logger.WithFields(logrus.Fields{
							"status":   resp.StatusCode,
							"endpoint": es.Endpoint,
						}).Error("endpoint check failed")
						health.Endpoints[i].Healthy = false
						health.Endpoints[i].Message = resp.Status
						continue
					}
					health.Endpoints[i].Healthy = true
					health.Endpoints[i].Message = "OK"
				}
			}
		}
	}()
}

func eventWorker() {
	go func() {
		// a ticker channel to limit reloads to marathon, 1s is enough for now.
		ticker := time.NewTicker(1 * time.Second)
		for {
			select {
			case <-ticker.C:
				<-eventqueue
				start := time.Now()
				err := reload()
				elapsed := time.Since(start)
				if err != nil {
					logger.Error("config update failed")
					go statsCount("reload.failed", 1)
				} else {
					logger.WithFields(logrus.Fields{
						"took": elapsed,
					}).Info("config updated")
					go statsCount("reload.success", 1)
					go statsTiming("reload.time", elapsed)
				}
			}
		}
	}()
}

func fetchApps(jsontasks *MarathonTasks, jsonapps *MarathonApps) error {
	var endpoint string
	for _, es := range health.Endpoints {
		if es.Healthy == true {
			endpoint = es.Endpoint
			break
		}
	}
	if endpoint == "" {
		err := errors.New("all endpoints are down")
		return err
	}
	client := &http.Client{
		Timeout:   5 * time.Second,
		Transport: tr,
	}
	// take advantage of goroutines and run both reqs concurrent.
	appschn := make(chan error)
	taskschn := make(chan error)
	go func() {
		req, err := http.NewRequest("GET", endpoint+"/v2/tasks", nil)
		if err != nil {
			taskschn <- err
			return
		}
		req.Header.Set("Accept", "application/json")
		if config.User != "" {
			req.SetBasicAuth(config.User, config.Pass)
		}
		resp, err := client.Do(req)
		if err != nil {
			taskschn <- err
			return
		}
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		err = decoder.Decode(&jsontasks)
		if err != nil {
			taskschn <- err
			return
		}
		taskschn <- nil
	}()
	go func() {
		req, err := http.NewRequest("GET", endpoint+"/v2/apps", nil)
		if err != nil {
			appschn <- err
			return
		}
		req.Header.Set("Accept", "application/json")
		if config.User != "" {
			req.SetBasicAuth(config.User, config.Pass)
		}
		resp, err := client.Do(req)
		if err != nil {
			appschn <- err
			return
		}
		defer resp.Body.Close()
		decoder := json.NewDecoder(resp.Body)
		err = decoder.Decode(&jsonapps)
		if err != nil {
			appschn <- err
			return
		}
		appschn <- nil
	}()
	appserr := <-appschn
	taskserr := <-taskschn
	if appserr != nil {
		return appserr
	}
	if taskserr != nil {
		return taskserr
	}
	return nil
}

func syncApps(jsontasks *MarathonTasks, jsonapps *MarathonApps) {
	config.Lock()
	defer config.Unlock()
	config.Apps = make(map[string]App)
	for _, app := range jsonapps.Apps {
	OUTER:
		for _, task := range jsontasks.Tasks {
			if task.AppId != app.Id {
				continue
			}
			// lets skip tasks that does not expose any ports.
			if len(task.Ports) == 0 {
				continue
			}
			if len(app.HealthChecks) > 0 {
				if len(task.HealthCheckResults) == 0 {
					// this means tasks is being deployed but not yet monitored as alive. Assume down.
					continue
				}
				alive := true
				for _, health := range task.HealthCheckResults {
					// check if health check is alive
					if health.Alive == false {
						alive = false
					}
				}
				if alive != true {
					// at least one health check has failed. Assume down.
					continue
				}
			}
			if _, ok := config.Apps[app.Id]; !ok {
				var newapp = App{}
				if s, ok := app.Labels["subdomain"]; ok {
					hosts := strings.Split(s, " ")
					for _, host := range hosts {
						newapp.Hosts = append(newapp.Hosts, host)
					}
				} else if s, ok := app.Labels["moxy_subdomain"]; ok {
					// to be compatible with moxy
					hosts := strings.Split(s, " ")
					for _, host := range hosts {
						newapp.Hosts = append(newapp.Hosts, host)
					}
				} else {
					// If directories are used lets use them as subdomain dividers.
					// Ex: /project/app becomes app.project
					// Ex: /app becomes just app
					domains := strings.Split(app.Id[1:], "/")
					for i, j := 0, len(domains)-1; i < j; i, j = i+1, j-1 {
						domains[i], domains[j] = domains[j], domains[i]
					}
					host := strings.Join(domains, ".")
					fmt.Println(host)
					newapp.Hosts = append(newapp.Hosts, host)
				}
				for _, confapp := range config.Apps {
					for _, host := range confapp.Hosts {
						for _, newhost := range newapp.Hosts {
							if newhost == host {
								logger.WithFields(logrus.Fields{
									"app":       app.Id,
									"subdomain": host,
								}).Warn("duplicate subdomain label, ignoring app")
								continue OUTER
							}
						}
					}
				}
				newapp.Labels = app.Labels
				newapp.Env = app.Env
				for _, pds := range app.PortDefinitions {
					pd := PortDefinitions{
						Port:     pds.Port,
						Protocol: pds.Protocol,
						Labels:   pds.Labels,
					}
					newapp.PortDefinitions = append(newapp.PortDefinitions, pd)
				}
				config.Apps[app.Id] = newapp
			}
			newapp := config.Apps[app.Id]
			var newtask = Task{}
			newtask.Host = task.Host
			newtask.Ports = task.Ports
			newtask.ServicePorts = task.ServicePorts
			newtask.StagedAt = task.StagedAt
			newtask.StartedAt = task.StartedAt
			newtask.Version = task.Version
			newapp.Tasks = append(newapp.Tasks, newtask)
			config.Apps[app.Id] = newapp
		}
	}
}

func writeConf() error {
	template, err := template.New(filepath.Base(config.Nginx_template)).ParseFiles(config.Nginx_template)
	if err != nil {
		return err
	}

	parent := filepath.Dir(config.Nginx_config)
	tmpFile, err := ioutil.TempFile(parent, ".nginx.conf.tmp-")
	defer tmpFile.Close()

	err = template.Execute(tmpFile, config)
	if err != nil {
		return err
	}
	config.LastUpdates.LastConfigRendered = time.Now()

	err = checkConf(tmpFile.Name())
	if err != nil {
		return err
	}

	err = os.Rename(tmpFile.Name(), config.Nginx_config)
	if err != nil {
		return err
	}

	return nil
}

func checkTmpl() error {
	t, err := template.New(filepath.Base(config.Nginx_template)).ParseFiles(config.Nginx_template)
	if err != nil {
		return err
	}
	err = t.Execute(ioutil.Discard, config)
	if err != nil {
		return err
	}
	return nil
}

func checkConf(path string) error {
	cmd := exec.Command(config.Nginx_cmd, "-c", path, "-t")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run() // will wait for command to return
	if err != nil {
		msg := fmt.Sprint(err) + ": " + stderr.String()
		errstd := errors.New(msg)
		return errstd
	}
	return nil
}

func reloadNginx() error {
	cmd := exec.Command(config.Nginx_cmd, "-s", "reload")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	err := cmd.Run() // will wait for command to return
	if err != nil {
		msg := fmt.Sprint(err) + ": " + stderr.String()
		errstd := errors.New(msg)
		return errstd
	}
	return nil
}

func reload() error {
	jsontasks := MarathonTasks{}
	jsonapps := MarathonApps{}
	err := fetchApps(&jsontasks, &jsonapps)
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to sync from marathon")
		return err
	}
	syncApps(&jsontasks, &jsonapps)
	config.LastUpdates.LastSync = time.Now()
	err = writeConf()
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to generate nginx config")
		return err
	}
	config.LastUpdates.LastConfigValid = time.Now()
	err = reloadNginx()
	if err != nil {
		logger.WithFields(logrus.Fields{
			"error": err.Error(),
		}).Error("unable to reload nginx")
		return err
	}
	config.LastUpdates.LastNginxReload = time.Now()
	return nil
}
<|endoftext|>


<commit_before>package docker

import (
	"errors"
	"io"
	"io/ioutil"
	"os/exec"
)

type Archive io.Reader

type Compression uint32

const (
	Uncompressed Compression = iota
	Bzip2
	Gzip
)

func (compression *Compression) Flag() string {
	switch *compression {
	case Bzip2:
		return "j"
	case Gzip:
		return "z"
	}
	return ""
}

func Tar(path string, compression Compression) (io.Reader, error) {
	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
	return CmdStream(cmd)
}

func Untar(archive io.Reader, path string) error {
	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
	cmd.Stdin = archive
	output, err := cmd.CombinedOutput()
	if err != nil {
		return errors.New(err.Error() + ": " + string(output))
	}
	return nil
}

func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}
	pipeR, pipeW := io.Pipe()
	go func() {
		_, err := io.Copy(pipeW, stdout)
		if err != nil {
			pipeW.CloseWithError(err)
		}
		errText, e := ioutil.ReadAll(stderr)
		if e != nil {
			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
		}
		if err := cmd.Wait(); err != nil {
			// FIXME: can this block if stderr outputs more than the size of StderrPipe()'s buffer?
			pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
		} else {
			pipeW.Close()
		}
	}()
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	return pipeR, nil
}
<commit_msg>Comment to explain CmdStream<commit_after>package docker

import (
	"errors"
	"io"
	"io/ioutil"
	"os/exec"
)

type Archive io.Reader

type Compression uint32

const (
	Uncompressed Compression = iota
	Bzip2
	Gzip
)

func (compression *Compression) Flag() string {
	switch *compression {
	case Bzip2:
		return "j"
	case Gzip:
		return "z"
	}
	return ""
}

func Tar(path string, compression Compression) (io.Reader, error) {
	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")
	return CmdStream(cmd)
}

func Untar(archive io.Reader, path string) error {
	cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x")
	cmd.Stdin = archive
	output, err := cmd.CombinedOutput()
	if err != nil {
		return errors.New(err.Error() + ": " + string(output))
	}
	return nil
}

// CmdStream executes a command, and returns its stdout as a stream.
// If the command fails to run or doesn't complete successfully, an error
// will be returned, including anything written on stderr.
func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}
	pipeR, pipeW := io.Pipe()
	go func() {
		_, err := io.Copy(pipeW, stdout)
		if err != nil {
			pipeW.CloseWithError(err)
		}
		errText, e := ioutil.ReadAll(stderr)
		if e != nil {
			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
		}
		if err := cmd.Wait(); err != nil {
			// FIXME: can this block if stderr outputs more than the size of StderrPipe()'s buffer?
			pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
		} else {
			pipeW.Close()
		}
	}()
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	return pipeR, nil
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 builder

import (
	"os"
	"path/filepath"
	"regexp"

	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/resolve"
	"mynewt.apache.org/newt/newt/syscfg"
	"mynewt.apache.org/newt/newt/toolchain"
	"mynewt.apache.org/newt/util"
)

type BuildPackage struct {
	rpkg              *resolve.ResolvePackage
	SourceDirectories []string
	ci                *toolchain.CompilerInfo
}

func NewBuildPackage(rpkg *resolve.ResolvePackage) *BuildPackage {
	bpkg := &BuildPackage{
		rpkg: rpkg,
	}

	return bpkg
}

// Recursively iterates through an pkg's dependencies, adding each pkg
// encountered to the supplied set.
func (bpkg *BuildPackage) collectDepsAux(b *Builder,
	set *map[*BuildPackage]bool) error {

	if (*set)[bpkg] {
		return nil
	}

	(*set)[bpkg] = true

	for _, dep := range bpkg.rpkg.Deps {
		dbpkg := b.PkgMap[dep.Rpkg]
		if dbpkg == nil {
			return util.FmtNewtError("Package not found %s; required by %s",
				dep.Rpkg.Lpkg.Name(), bpkg.rpkg.Lpkg.Name())
		}

		if err := dbpkg.collectDepsAux(b, set); err != nil {
			return err
		}
	}

	return nil
}

// Recursively iterates through an pkg's dependencies.  The resulting array
// contains a pointer to each encountered pkg.
func (bpkg *BuildPackage) collectDeps(b *Builder) ([]*BuildPackage, error) {
	set := map[*BuildPackage]bool{}

	err := bpkg.collectDepsAux(b, &set)
	if err != nil {
		return nil, err
	}

	arr := []*BuildPackage{}
	for p, _ := range set {
		arr = append(arr, p)
	}

	return arr, nil
}

// Calculates the include paths exported by the specified pkg and all of
// its recursive dependencies.
func (bpkg *BuildPackage) recursiveIncludePaths(
	b *Builder) ([]string, error) {

	deps, err := bpkg.collectDeps(b)
	if err != nil {
		return nil, err
	}

	incls := []string{}
	for _, p := range deps {
		incls = append(incls, p.publicIncludeDirs(b.targetBuilder.bspPkg)...)
	}

	return incls, nil
}

// Replaces instances of "@<repo-name>" with repo paths.
func expandFlags(flags []string) {
	for i, f := range flags {
		newFlag, changed := newtutil.ReplaceRepoDesignators(f)
		if changed {
			flags[i] = newFlag
		}
	}
}

// Retrieves the build package's build profile override, as specified in its
// `pkg.yml` file.  If the package does not override the build profile, "" is
// returned.
func (bpkg *BuildPackage) BuildProfile(b *Builder) string {
	settings := b.cfg.AllSettingsForLpkg(bpkg.rpkg.Lpkg)
	return bpkg.rpkg.Lpkg.PkgY.GetValString("pkg.build_profile", settings)
}

func (bpkg *BuildPackage) CompilerInfo(
	b *Builder) (*toolchain.CompilerInfo, error) {

	// If this package's compiler info has already been generated, return the
	// cached copy.
	if bpkg.ci != nil {
		return bpkg.ci, nil
	}

	ci := toolchain.NewCompilerInfo()
	settings := b.cfg.AllSettingsForLpkg(bpkg.rpkg.Lpkg)

	// Read each set of flags and expand repo designators ("@<repo-name>") into
	// paths.
	ci.Cflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.cflags", settings)
	expandFlags(ci.Cflags)

	ci.CXXflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.cxxflags", settings)
	expandFlags(ci.CXXflags)

	ci.Lflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.lflags", settings)
	expandFlags(ci.Lflags)

	ci.Aflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.aflags", settings)
	expandFlags(ci.Aflags)

	// Package-specific injected settings get specified as C flags on the
	// command line.
	for k, _ := range bpkg.rpkg.Lpkg.InjectedSettings() {
		ci.Cflags = append(ci.Cflags, syscfg.FeatureToCflag(k))
	}

	ci.IgnoreFiles = []*regexp.Regexp{}
	ignPats := bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_files", settings)
	for _, str := range ignPats {
		re, err := regexp.Compile(str)
		if err != nil {
			return nil, util.NewNewtError(
				"Ignore files, unable to compile re: " + err.Error())
		}
		ci.IgnoreFiles = append(ci.IgnoreFiles, re)
	}

	ci.IgnoreDirs = []*regexp.Regexp{}
	ignPats = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_dirs", settings)
	for _, str := range ignPats {
		re, err := regexp.Compile(str)
		if err != nil {
			return nil, util.NewNewtError(
				"Ignore dirs, unable to compile re: " + err.Error())
		}
		ci.IgnoreDirs = append(ci.IgnoreDirs, re)
	}

	bpkg.SourceDirectories = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice(
		"pkg.src_dirs", settings)

	includePaths, err := bpkg.recursiveIncludePaths(b)
	if err != nil {
		return nil, err
	}

	ci.Includes = append(bpkg.privateIncludeDirs(b), includePaths...)
	bpkg.ci = ci

	return bpkg.ci, nil
}

func (bpkg *BuildPackage) findSdkIncludes() []string {
	sdkDir := bpkg.rpkg.Lpkg.BasePath() + "/src/ext/"

	sdkPathList := []string{}
	err := filepath.Walk(sdkDir,
		func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() {
				return nil
			}

			sdkPathList = append(sdkPathList, path)
			return nil
		})
	if err != nil {
		return []string{}
	}

	return sdkPathList
}

func (bpkg *BuildPackage) publicIncludeDirs(bspPkg *pkg.BspPackage) []string {
	pkgBase := filepath.Base(bpkg.rpkg.Lpkg.Name())
	bp := bpkg.rpkg.Lpkg.BasePath()

	incls := []string{
		bp + "/include",
		bp + "/include/" + pkgBase + "/arch/" + bspPkg.Arch,
	}

	if bpkg.rpkg.Lpkg.Type() == pkg.PACKAGE_TYPE_SDK {
		incls = append(incls, bspPkg.BasePath()+"/include/bsp/")

		sdkIncls := bpkg.findSdkIncludes()
		incls = append(incls, sdkIncls...)
	}

	return incls
}

func (bpkg *BuildPackage) privateIncludeDirs(b *Builder) []string {
	srcDir := bpkg.rpkg.Lpkg.BasePath() + "/src/"

	incls := []string{}
	incls = append(incls, srcDir)
	incls = append(incls, srcDir+"/arch/"+b.targetBuilder.bspPkg.Arch)

	switch bpkg.rpkg.Lpkg.Type() {
	case pkg.PACKAGE_TYPE_SDK:
		// If pkgType == SDK, include all the items in "ext" directly into the
		// include path
		incls = append(incls, b.bspPkg.rpkg.Lpkg.BasePath()+"/include/bsp/")

		sdkIncls := bpkg.findSdkIncludes()
		incls = append(incls, sdkIncls...)

	case pkg.PACKAGE_TYPE_UNITTEST:
		// A unittest package gets access to its parent package's private
		// includes.
		parentPkg := b.testOwner(bpkg)
		if parentPkg != nil {
			parentIncls := parentPkg.privateIncludeDirs(b)
			incls = append(incls, parentIncls...)
		}

	default:
	}

	return incls
}
<commit_msg>builder; fix crash when inspecting malformed SDK pkg missing src/ext<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 builder

import (
	"os"
	"path/filepath"
	"regexp"

	"mynewt.apache.org/newt/newt/newtutil"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/resolve"
	"mynewt.apache.org/newt/newt/syscfg"
	"mynewt.apache.org/newt/newt/toolchain"
	"mynewt.apache.org/newt/util"
)

type BuildPackage struct {
	rpkg              *resolve.ResolvePackage
	SourceDirectories []string
	ci                *toolchain.CompilerInfo
}

func NewBuildPackage(rpkg *resolve.ResolvePackage) *BuildPackage {
	bpkg := &BuildPackage{
		rpkg: rpkg,
	}

	return bpkg
}

// Recursively iterates through an pkg's dependencies, adding each pkg
// encountered to the supplied set.
func (bpkg *BuildPackage) collectDepsAux(b *Builder,
	set *map[*BuildPackage]bool) error {

	if (*set)[bpkg] {
		return nil
	}

	(*set)[bpkg] = true

	for _, dep := range bpkg.rpkg.Deps {
		dbpkg := b.PkgMap[dep.Rpkg]
		if dbpkg == nil {
			return util.FmtNewtError("Package not found %s; required by %s",
				dep.Rpkg.Lpkg.Name(), bpkg.rpkg.Lpkg.Name())
		}

		if err := dbpkg.collectDepsAux(b, set); err != nil {
			return err
		}
	}

	return nil
}

// Recursively iterates through an pkg's dependencies.  The resulting array
// contains a pointer to each encountered pkg.
func (bpkg *BuildPackage) collectDeps(b *Builder) ([]*BuildPackage, error) {
	set := map[*BuildPackage]bool{}

	err := bpkg.collectDepsAux(b, &set)
	if err != nil {
		return nil, err
	}

	arr := []*BuildPackage{}
	for p, _ := range set {
		arr = append(arr, p)
	}

	return arr, nil
}

// Calculates the include paths exported by the specified pkg and all of
// its recursive dependencies.
func (bpkg *BuildPackage) recursiveIncludePaths(
	b *Builder) ([]string, error) {

	deps, err := bpkg.collectDeps(b)
	if err != nil {
		return nil, err
	}

	incls := []string{}
	for _, p := range deps {
		incls = append(incls, p.publicIncludeDirs(b.targetBuilder.bspPkg)...)
	}

	return incls, nil
}

// Replaces instances of "@<repo-name>" with repo paths.
func expandFlags(flags []string) {
	for i, f := range flags {
		newFlag, changed := newtutil.ReplaceRepoDesignators(f)
		if changed {
			flags[i] = newFlag
		}
	}
}

// Retrieves the build package's build profile override, as specified in its
// `pkg.yml` file.  If the package does not override the build profile, "" is
// returned.
func (bpkg *BuildPackage) BuildProfile(b *Builder) string {
	settings := b.cfg.AllSettingsForLpkg(bpkg.rpkg.Lpkg)
	return bpkg.rpkg.Lpkg.PkgY.GetValString("pkg.build_profile", settings)
}

func (bpkg *BuildPackage) CompilerInfo(
	b *Builder) (*toolchain.CompilerInfo, error) {

	// If this package's compiler info has already been generated, return the
	// cached copy.
	if bpkg.ci != nil {
		return bpkg.ci, nil
	}

	ci := toolchain.NewCompilerInfo()
	settings := b.cfg.AllSettingsForLpkg(bpkg.rpkg.Lpkg)

	// Read each set of flags and expand repo designators ("@<repo-name>") into
	// paths.
	ci.Cflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.cflags", settings)
	expandFlags(ci.Cflags)

	ci.CXXflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.cxxflags", settings)
	expandFlags(ci.CXXflags)

	ci.Lflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.lflags", settings)
	expandFlags(ci.Lflags)

	ci.Aflags = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.aflags", settings)
	expandFlags(ci.Aflags)

	// Package-specific injected settings get specified as C flags on the
	// command line.
	for k, _ := range bpkg.rpkg.Lpkg.InjectedSettings() {
		ci.Cflags = append(ci.Cflags, syscfg.FeatureToCflag(k))
	}

	ci.IgnoreFiles = []*regexp.Regexp{}
	ignPats := bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_files", settings)
	for _, str := range ignPats {
		re, err := regexp.Compile(str)
		if err != nil {
			return nil, util.NewNewtError(
				"Ignore files, unable to compile re: " + err.Error())
		}
		ci.IgnoreFiles = append(ci.IgnoreFiles, re)
	}

	ci.IgnoreDirs = []*regexp.Regexp{}
	ignPats = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice("pkg.ign_dirs", settings)
	for _, str := range ignPats {
		re, err := regexp.Compile(str)
		if err != nil {
			return nil, util.NewNewtError(
				"Ignore dirs, unable to compile re: " + err.Error())
		}
		ci.IgnoreDirs = append(ci.IgnoreDirs, re)
	}

	bpkg.SourceDirectories = bpkg.rpkg.Lpkg.PkgY.GetValStringSlice(
		"pkg.src_dirs", settings)

	includePaths, err := bpkg.recursiveIncludePaths(b)
	if err != nil {
		return nil, err
	}

	ci.Includes = append(bpkg.privateIncludeDirs(b), includePaths...)
	bpkg.ci = ci

	return bpkg.ci, nil
}

func (bpkg *BuildPackage) findSdkIncludes() []string {
	sdkDir := bpkg.rpkg.Lpkg.BasePath() + "/src/ext/"

	if _, err := os.Stat(sdkDir); err != nil {
		return []string{}
	}

	sdkPathList := []string{}
	err := filepath.Walk(sdkDir,
		func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() {
				return nil
			}

			sdkPathList = append(sdkPathList, path)
			return nil
		})
	if err != nil {
		return []string{}
	}

	return sdkPathList
}

func (bpkg *BuildPackage) publicIncludeDirs(bspPkg *pkg.BspPackage) []string {
	pkgBase := filepath.Base(bpkg.rpkg.Lpkg.Name())
	bp := bpkg.rpkg.Lpkg.BasePath()

	incls := []string{
		bp + "/include",
		bp + "/include/" + pkgBase + "/arch/" + bspPkg.Arch,
	}

	if bpkg.rpkg.Lpkg.Type() == pkg.PACKAGE_TYPE_SDK {
		incls = append(incls, bspPkg.BasePath()+"/include/bsp/")

		sdkIncls := bpkg.findSdkIncludes()
		incls = append(incls, sdkIncls...)
	}

	return incls
}

func (bpkg *BuildPackage) privateIncludeDirs(b *Builder) []string {
	srcDir := bpkg.rpkg.Lpkg.BasePath() + "/src/"

	incls := []string{}
	incls = append(incls, srcDir)
	incls = append(incls, srcDir+"/arch/"+b.targetBuilder.bspPkg.Arch)

	switch bpkg.rpkg.Lpkg.Type() {
	case pkg.PACKAGE_TYPE_SDK:
		// If pkgType == SDK, include all the items in "ext" directly into the
		// include path
		incls = append(incls, b.bspPkg.rpkg.Lpkg.BasePath()+"/include/bsp/")

		sdkIncls := bpkg.findSdkIncludes()
		incls = append(incls, sdkIncls...)

	case pkg.PACKAGE_TYPE_UNITTEST:
		// A unittest package gets access to its parent package's private
		// includes.
		parentPkg := b.testOwner(bpkg)
		if parentPkg != nil {
			parentIncls := parentPkg.privateIncludeDirs(b)
			incls = append(incls, parentIncls...)
		}

	default:
	}

	return incls
}
<|endoftext|>


<commit_before>// Based on code from the Cephes Mathematical Library
// http://www.moshier.net/#Cephes
// or http://www.netlib.org/cephes/single.tgz
// Copyright Stephen L. Moshier
// From the README:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.

package math

//------------------------------------------------------------------------------

// `Tan` returns the tangent of `x`.
func Tan(x float32) float32 {
	// (comment from the original source)
	// DESCRIPTION:
	//
	// Returns the circular tangent of the radian argument x.
	//
	// Range reduction is modulo pi/4.  A polynomial approximation
	// is employed in the basic interval [0, pi/4].
	//
	// ACCURACY:
	//
	//                      Relative error:
	// arithmetic   domain     # trials      peak         rms
	//    IEEE     +-4096        100000     3.3e-7      4.5e-8
	//
	// ERROR MESSAGES:
	//
	//   message         condition          value returned
	// tanf total loss   x > 2^24              0.0

	const (
		DP1  = float32(0.78515625)
		DP2  = float32(2.4187564849853515625e-4)
		DP3  = float32(3.77489497744594108e-8)
		FOPI = float32(1.27323954473516) // 4/pi
	)

	// Make argument positive but save the sign.
	sign := false
	if x < 0 {
		x = -x
		sign = true
	}

	j := uint64(x * FOPI) // Integer part of `x/(Pi/4)`, as integer for tests on the phase angle.
	y := float32(j)       // Integer part of `x/(Pi/4)`, as float.

	// Map zeros and singularities to origin.
	if j&1 == 1 {
		j += 1
		y += 1
	}

	z := ((x - y*DP1) - y*DP2) - y*DP3
	zz := z * z

	if x > 1e-4 {
		// 1.7e-8 relative error in [-pi/4, +pi/4]
		const (
			COEFA = 9.38540185543E-3
			COEFB = 3.11992232697E-3
			COEFC = 2.44301354525E-2
			COEFD = 5.34112807005E-2
			COEFE = 1.33387994085E-1
			COEFF = 3.33331568548E-1
		)
		y = (((((COEFA*zz+COEFB)*zz+COEFC)*zz+COEFD)*zz+COEFE)*zz+COEFF)*zz*z + z
	} else {
		y = z
	}
	if j&2 == 2 {
		y = -1 / y
	}
	if sign {
		y = -y
	}
	return y
}

//------------------------------------------------------------------------------
<commit_msg>Tweaks.<commit_after>// Based on code from the Cephes Mathematical Library
// http://www.moshier.net/#Cephes
// or http://www.netlib.org/cephes/single.tgz
// Copyright Stephen L. Moshier
// From the README:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.

package math

//------------------------------------------------------------------------------

// `Tan` returns the tangent of `x`.
func Tan(x float32) float32 {
	// (comment from the original source)
	// DESCRIPTION:
	//
	// Returns the circular tangent of the radian argument x.
	//
	// Range reduction is modulo pi/4.  A polynomial approximation
	// is employed in the basic interval [0, pi/4].
	//
	// ACCURACY:
	//
	//                      Relative error:
	// arithmetic   domain     # trials      peak         rms
	//    IEEE     +-4096        100000     3.3e-7      4.5e-8
	//
	// ERROR MESSAGES:
	//
	//   message         condition          value returned
	// tanf total loss   x > 2^24              0.0

	const (
		DP1  = float32(0.78515625)
		DP2  = float32(2.4187564849853515625e-4)
		DP3  = float32(3.77489497744594108e-8)
		FOPI = float32(1.27323954473516) // 4/pi
	)

	// Make argument positive but save the sign.
	sign := float32(1)
	if x < 0 {
		x = -x
		sign = -1
	}

	j := uint64(x * FOPI) // Integer part of `x/(Pi/4)`, as integer for tests on the phase angle.
	y := float32(j)       // Integer part of `x/(Pi/4)`, as float.

	// Map zeros and singularities to origin.
	if j&1 == 1 {
		j += 1
		y += 1
	}

	z := ((x - y*DP1) - y*DP2) - y*DP3
	zz := z * z

	if x > 1e-4 {
		// 1.7e-8 relative error in [-pi/4, +pi/4]
		const (
			COEFA = 9.38540185543E-3
			COEFB = 3.11992232697E-3
			COEFC = 2.44301354525E-2
			COEFD = 5.34112807005E-2
			COEFE = 1.33387994085E-1
			COEFF = 3.33331568548E-1
		)
		y = (((((COEFA*zz+COEFB)*zz+COEFC)*zz+COEFD)*zz+COEFE)*zz+COEFF)*zz*z + z
	} else {
		y = z
	}

	if j&2 == 2 {
		y = -1 / y
	}

	return sign * y
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>package amigo

import (
	"os"

	"github.com/pelletier/go-toml"
)

const EnvMapKey = "envmap"

type Config struct {
	env      map[string]string
	confFile *toml.TomlTree
}

// Associate a given config file key with an environment var
func (c *Config) Env(confKey string, envKey string) {
	if val := os.Getenv(envKey); val != "" {
		c.env[confKey] = val
	}
}

func (c *Config) Get(key string) interface{} {
	if val, ok := c.env[key]; ok {
		return val
	}
	return c.confFile.Get(key)
}

// Return a new configuration object for use by library consumers
func New(filepath string) (*Config, error) {
	file, err := toml.LoadFile(filepath)

	if err != nil {
		return nil, err
	}

	c := &Config{}
	c.env = make(map[string]string)
	c.confFile = file

	// if a envmap table is defined, associate the specified keys with the env
	// vars defined there
	if file.Has(EnvMapKey) {
		envmap := file.Get(EnvMapKey).(*toml.TomlTree)
		for _, confKey := range envmap.Keys() {
			envKey := envmap.Get(confKey).(string)
			c.Env(confKey, envKey)
		}
	}
	return c, nil
}
<commit_msg>fixing issue with quoted toml keys that contain dots<commit_after>package amigo

import (
	"os"

	"github.com/pelletier/go-toml"
)

const EnvMapKey = "envmap"

type Config struct {
	env      map[string]string
	confFile *toml.TomlTree
}

// Associate a given config file key with an environment var
func (c *Config) Env(confKey string, envKey string) {
	if val := os.Getenv(envKey); val != "" {
		c.env[confKey] = val
	}
}

func (c *Config) Get(key string) interface{} {
	if val, ok := c.env[key]; ok {
		return val
	}
	return c.confFile.Get(key)
}

// Return a new configuration object for use by library consumers
func New(filepath string) (*Config, error) {
	file, err := toml.LoadFile(filepath)

	if err != nil {
		return nil, err
	}

	c := &Config{}
	c.env = make(map[string]string)
	c.confFile = file

	// if a envmap table is defined, associate the specified keys with the env
	// vars defined there
	if file.Has(EnvMapKey) {
		envmap := file.Get(EnvMapKey).(*toml.TomlTree)
		for _, confKey := range envmap.Keys() {
			envKey := envmap.GetPath([]string{confKey}).(string)
			c.Env(confKey, envKey)
		}
	}
	return c, nil
}
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/rand"
	"github.com/conformal/btcec"
	"math/big"
)

type Key interface {
	Sign(b []byte) ([]byte, error)
	PublicCompressed() []byte
	PrivateBytes() []byte
}

type baseKey struct {
	priv btcec.PrivateKey
}

// Returns DER encoded signature from input hash
func (k *baseKey) Sign(hash []byte) ([]byte, error) {
	sig, err := k.priv.Sign(hash)
	if err != nil {
		return nil, err
	}
	return sig.Serialize(), nil
}

// Verifies a hash using DER encoded signature
func Verify(pubKey, signature, hash []byte) (bool, error) {
	sig, err := btcec.ParseDERSignature(signature, btcec.S256())
	if err != nil {
		return false, err
	}
	pk, err := btcec.ParsePubKey(pubKey, btcec.S256())
	if err != nil {
		return false, nil
	}
	return sig.Verify(hash, pk), nil
}

func (k *baseKey) PrivateBytes() []byte {
	return k.priv.D.Bytes()
}

func (k *baseKey) PublicCompressed() []byte {
	return (*btcec.PublicKey)(&k.priv.PublicKey).SerializeCompressed()
}

type RootDeterministicKey struct {
	baseKey
	Seed Hash
}

type AccountKey struct {
	baseKey
}

var order = btcec.S256().N

type genFunc func(*big.Int) bool

func newKey(priv, inc *big.Int, f genFunc) *baseKey {
	pk := big.NewInt(0).Set(priv)
	for ; f(pk); inc.Add(inc, one) {
		pk.SetBytes(Sha512Half(inc.Bytes()))
	}
	privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pk.Bytes())
	return &baseKey{*privKey}
}

func ParsePublicKey(b []byte) (*btcec.PublicKey, error) {
	key, err := btcec.ParsePubKey(b, btcec.S256())
	return (*btcec.PublicKey)(key), err
}

func ParsePublicKeyFromHash(hash []byte) (*btcec.PublicKey, error) {
	h, err := NewRippleHash(string(hash))
	if err != nil {
		return nil, err
	}
	return ParsePublicKey(h.Payload())
}

// If seed is nil, generate a random one
func GenerateRootDeterministicKey(seed []byte) (*RootDeterministicKey, error) {
	if seed == nil {
		seed = make([]byte, 16)
		_, err := rand.Read(seed)
		if err != nil {
			return nil, err
		}
	}
	s, err := NewRippleFamilySeed(seed)
	if err != nil {
		return nil, err
	}
	inc := big.NewInt(0).SetBytes(seed)
	inc.Lsh(inc, 32)
	f := func(priv *big.Int) bool { return priv.Cmp(order) >= 0 }
	key := newKey(order, inc, f)
	key.priv.X, key.priv.Y = key.priv.ScalarBaseMult(key.PrivateBytes())
	return &RootDeterministicKey{
		baseKey: *key,
		Seed:    s,
	}, nil
}

func (r *RootDeterministicKey) GenerateAccountId(sequence int32) (Hash, error) {
	inc := big.NewInt(0).SetBytes(r.PublicCompressed())
	inc.Lsh(inc, 32).Add(inc, big.NewInt(int64(sequence))).Lsh(inc, 32).Add(inc, zero)
	f := func(priv *big.Int) bool { return priv.Cmp(order) >= 0 }
	key := newKey(order, inc, f)
	key.priv.X, key.priv.Y = key.priv.ScalarBaseMult(key.PrivateBytes())
	key.priv.X, key.priv.Y = key.priv.Add(key.priv.X, key.priv.Y, r.priv.X, r.priv.Y)
	b := Sha256RipeMD160(key.PublicCompressed())
	return NewRippleAccount(b)
}

func (r *RootDeterministicKey) GenerateAccountKey(sequence int32) (*AccountKey, error) {
	generator := big.NewInt(0).SetBytes(r.PublicCompressed())
	inc := big.NewInt(0).Set(generator)
	inc.Lsh(inc, 32).Add(inc, big.NewInt(int64(sequence))).Lsh(inc, 32).Add(inc, zero)
	f := func(priv *big.Int) bool { return priv.Cmp(generator) >= 0 || priv.Cmp(zero) <= 0 }
	key := newKey(zero, inc, f)
	key.priv.D.Add(key.priv.D, r.priv.D).Mod(key.priv.D, order)
	key.priv.X, key.priv.Y = key.priv.Curve.ScalarBaseMult(key.PrivateBytes())
	return &AccountKey{baseKey: *key}, nil
}

func (r *RootDeterministicKey) PublicGenerator() (Hash, error) {
	return NewRippleFamilyGenerator(r.PublicCompressed())
}

func (r *RootDeterministicKey) PublicNodeKey() (Hash, error) {
	return NewRipplePublicNode(r.PublicCompressed())
}

func (r *RootDeterministicKey) PrivateNodeKey() (Hash, error) {
	return NewRipplePrivateNode(r.PrivateBytes())
}

func (a *AccountKey) PublicAccountKey() (Hash, error) {
	return NewRipplePublicAccount(a.PublicCompressed())
}

func (a *AccountKey) PrivateAccountKey() (Hash, error) {
	return NewRipplePrivateAccount(a.PrivateBytes())
}
<commit_msg>Error handling on one line<commit_after>package crypto

import (
	"crypto/rand"
	"github.com/conformal/btcec"
	"math/big"
)

type Key interface {
	Sign(b []byte) ([]byte, error)
	PublicCompressed() []byte
	PrivateBytes() []byte
}

type baseKey struct {
	priv btcec.PrivateKey
}

// Returns DER encoded signature from input hash
func (k *baseKey) Sign(hash []byte) ([]byte, error) {
	sig, err := k.priv.Sign(hash)
	if err != nil {
		return nil, err
	}
	return sig.Serialize(), nil
}

// Verifies a hash using DER encoded signature
func Verify(pubKey, signature, hash []byte) (bool, error) {
	sig, err := btcec.ParseDERSignature(signature, btcec.S256())
	if err != nil {
		return false, err
	}
	pk, err := btcec.ParsePubKey(pubKey, btcec.S256())
	if err != nil {
		return false, nil
	}
	return sig.Verify(hash, pk), nil
}

func (k *baseKey) PrivateBytes() []byte {
	return k.priv.D.Bytes()
}

func (k *baseKey) PublicCompressed() []byte {
	return (*btcec.PublicKey)(&k.priv.PublicKey).SerializeCompressed()
}

type RootDeterministicKey struct {
	baseKey
	Seed Hash
}

type AccountKey struct {
	baseKey
}

var order = btcec.S256().N

type genFunc func(*big.Int) bool

func newKey(priv, inc *big.Int, f genFunc) *baseKey {
	pk := big.NewInt(0).Set(priv)
	for ; f(pk); inc.Add(inc, one) {
		pk.SetBytes(Sha512Half(inc.Bytes()))
	}
	privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pk.Bytes())
	return &baseKey{*privKey}
}

func ParsePublicKey(b []byte) (*btcec.PublicKey, error) {
	key, err := btcec.ParsePubKey(b, btcec.S256())
	return (*btcec.PublicKey)(key), err
}

func ParsePublicKeyFromHash(hash []byte) (*btcec.PublicKey, error) {
	h, err := NewRippleHash(string(hash))
	if err != nil {
		return nil, err
	}
	return ParsePublicKey(h.Payload())
}

// If seed is nil, generate a random one
func GenerateRootDeterministicKey(seed []byte) (*RootDeterministicKey, error) {
	if seed == nil {
		seed = make([]byte, 16)
		if _, err := rand.Read(seed); err != nil {
			return nil, err
		}
	}
	s, err := NewRippleFamilySeed(seed)
	if err != nil {
		return nil, err
	}
	inc := big.NewInt(0).SetBytes(seed)
	inc.Lsh(inc, 32)
	f := func(priv *big.Int) bool { return priv.Cmp(order) >= 0 }
	key := newKey(order, inc, f)
	key.priv.X, key.priv.Y = key.priv.ScalarBaseMult(key.PrivateBytes())
	return &RootDeterministicKey{
		baseKey: *key,
		Seed:    s,
	}, nil
}

func (r *RootDeterministicKey) GenerateAccountId(sequence int32) (Hash, error) {
	inc := big.NewInt(0).SetBytes(r.PublicCompressed())
	inc.Lsh(inc, 32).Add(inc, big.NewInt(int64(sequence))).Lsh(inc, 32).Add(inc, zero)
	f := func(priv *big.Int) bool { return priv.Cmp(order) >= 0 }
	key := newKey(order, inc, f)
	key.priv.X, key.priv.Y = key.priv.ScalarBaseMult(key.PrivateBytes())
	key.priv.X, key.priv.Y = key.priv.Add(key.priv.X, key.priv.Y, r.priv.X, r.priv.Y)
	b := Sha256RipeMD160(key.PublicCompressed())
	return NewRippleAccount(b)
}

func (r *RootDeterministicKey) GenerateAccountKey(sequence int32) (*AccountKey, error) {
	generator := big.NewInt(0).SetBytes(r.PublicCompressed())
	inc := big.NewInt(0).Set(generator)
	inc.Lsh(inc, 32).Add(inc, big.NewInt(int64(sequence))).Lsh(inc, 32).Add(inc, zero)
	f := func(priv *big.Int) bool { return priv.Cmp(generator) >= 0 || priv.Cmp(zero) <= 0 }
	key := newKey(zero, inc, f)
	key.priv.D.Add(key.priv.D, r.priv.D).Mod(key.priv.D, order)
	key.priv.X, key.priv.Y = key.priv.Curve.ScalarBaseMult(key.PrivateBytes())
	return &AccountKey{baseKey: *key}, nil
}

func (r *RootDeterministicKey) PublicGenerator() (Hash, error) {
	return NewRippleFamilyGenerator(r.PublicCompressed())
}

func (r *RootDeterministicKey) PublicNodeKey() (Hash, error) {
	return NewRipplePublicNode(r.PublicCompressed())
}

func (r *RootDeterministicKey) PrivateNodeKey() (Hash, error) {
	return NewRipplePrivateNode(r.PrivateBytes())
}

func (a *AccountKey) PublicAccountKey() (Hash, error) {
	return NewRipplePublicAccount(a.PublicCompressed())
}

func (a *AccountKey) PrivateAccountKey() (Hash, error) {
	return NewRipplePrivateAccount(a.PrivateBytes())
}
<|endoftext|>


<commit_before>package model

import "time"

type TimeSeriesPayload struct {
	Thing          string                `json:"thing"`
	ThingType      string                `json:"thingType"`
	Promoted       bool                  `json:"promoted"`
	Device         string                `json:"device"`
	Channel        string                `json:"channel"`
	Schema         string                `json:"schema"`
	Event          string                `json:"event"`
	Points         []TimeSeriesDatapoint `json:"points"`
	Time           string                `json:"time"`
	TimeZone       string                `json:"timeZone"`
	TimeOffset     int                   `json:"timeOffset"`
	Site           string                `json:"site"`
	ReportingZones map[string]string     `json:"zones"`
	UserOverride   string                `json:"_"`
	NodeOverride   string                `json:"_"`
}

type TimeSeriesDatapoint struct {
	Path  string      `json:"path"`
	Value interface{} `json:"value"`
	Type  string      `json:"type"`
}

func (p *TimeSeriesPayload) GetTime() (time.Time, error) {
	return time.Parse(time.RFC3339, p.Time)
}

func (p *TimeSeriesPayload) GetPath(path string) interface{} {
	for _, point := range p.Points {
		if point.Path == path {
			return point.Value
		}
	}
	return nil
}
<commit_msg>Remove ReportingZones from model object.<commit_after>package model

import "time"

type TimeSeriesPayload struct {
	Thing        string                `json:"thing"`
	ThingType    string                `json:"thingType"`
	Promoted     bool                  `json:"promoted"`
	Device       string                `json:"device"`
	Channel      string                `json:"channel"`
	Schema       string                `json:"schema"`
	Event        string                `json:"event"`
	Points       []TimeSeriesDatapoint `json:"points"`
	Time         string                `json:"time"`
	TimeZone     string                `json:"timeZone"`
	TimeOffset   int                   `json:"timeOffset"`
	Site         string                `json:"site"`
	UserOverride string                `json:"_"`
	NodeOverride string                `json:"_"`
}

type TimeSeriesDatapoint struct {
	Path  string      `json:"path"`
	Value interface{} `json:"value"`
	Type  string      `json:"type"`
}

func (p *TimeSeriesPayload) GetTime() (time.Time, error) {
	return time.Parse(time.RFC3339, p.Time)
}

func (p *TimeSeriesPayload) GetPath(path string) interface{} {
	for _, point := range p.Points {
		if point.Path == path {
			return point.Value
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package resource

import x "github.com/javinc/puto"

// Find resource
func Find(o Options) ([]Model, error) {
	models := []Model{}

	// fields defaults
	if len(o.Fields) == 0 {
		o.Fields = []string{"*"}
	}

	// limit & offset
	if len(o.Limits) == 0 {
		o.Limits = []int{-1, -1}
	} else if len(o.Limits) == 1 {
		o.Limits = []int{o.Limits[0], -1}
	}

	// sort
	sort := o.Sorts.Asc
	if o.Sorts.Desc != "" {
		sort = o.Sorts.Desc + " desc"
		if o.Sorts.Asc != "" {
			sort += ", " + o.Sorts.Asc
		}
	}

	e := x.MySQL.
		Select(o.Fields).
		Limit(o.Limits[0]).
		Offset(o.Limits[1]).
		Order(sort).
		Find(&models, o.Filters).
		Error

	return models, e
}

// Get resource
func Get(o Options) (Model, error) {
	model := Model{}
	e := x.MySQL.First(&model, o.Filters).Error

	return model, e
}
<commit_msg>fix sort<commit_after>package resource

import x "github.com/javinc/puto"

// Find resource
func Find(o Options) ([]Model, error) {
	models := []Model{}

	// fields defaults
	if len(o.Fields) == 0 {
		o.Fields = []string{"*"}
	}

	// limit & offset
	if len(o.Limits) == 0 {
		o.Limits = []int{-1, -1}
	} else if len(o.Limits) == 1 {
		o.Limits = []int{o.Limits[0], -1}
	}

	// sort
	if o.Sorts.Desc != "" {
		o.Sorts.Desc += " desc"
	}

	e := x.MySQL.
		Select(o.Fields).
		Limit(o.Limits[0]).
		Offset(o.Limits[1]).
		Order(o.Sorts.Asc).
		Order(o.Sorts.Desc).
		Find(&models, o.Filters).
		Error

	return models, e
}

// Get resource
func Get(o Options) (Model, error) {
	model := Model{}
	e := x.MySQL.First(&model, o.Filters).Error

	return model, e
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

type Metadata struct {
	TVShows  []TVShowMetadata
	Movies   []MovieMetadata
	Capacity string
}

type MovieMetadata struct {
	TMDBId      int
	Name        string
	Overview    string
	Image       string
	Backdrop    string
	ReleaseDate string
	Vote        float32
}

type TVShowMetadata struct {
	TMDBId       int
	Name         string
	Overview     string
	Image        string
	Backdrop     string
	FirstAirDate string
	LastAirDate  string
	Seasons      []TVSeasonMetadata
}

type TVSeasonMetadata struct {
	TMDBId   int
	Season   int
	Name     string
	Overview string
	Image    string
	AirDate  string
	Episodes []TVEpisodeMetadata
}

type TVEpisodeMetadata struct {
	TMDBId         int
	Episode        int
	Name           string
	Overview       string
	Image          string
	Media          string
	AirDate        string
	ProductionCode string
	Vote           float32
}

// Generates metadata for everything.
func generateMetadata(paths Paths) {
	log.Println("Generating metadata")

	shows := make([]TVShowMetadata, 0)
	for _, showFolder := range directoriesIn(paths.TV) {

		// Load the metadata.
		var showDetails *TmdbTvShowDetails
		if err := readAndUnmarshal(showFolder, metadataFilename, &showDetails); err != nil {
			continue
		}

		// Create the model.
		image, _ := filepath.Rel(paths.Root, filepath.Join(showFolder, imageFilename))
		backdrop, _ := filepath.Rel(paths.Root, filepath.Join(showFolder, imageBackdropFilename))
		show := TVShowMetadata{
			TMDBId:       showDetails.Id,
			Name:         showDetails.Name,
			Overview:     showDetails.Overview,
			Image:        image,
			Backdrop:     backdrop,
			FirstAirDate: showDetails.FirstAirDate,
			LastAirDate:  showDetails.LastAirDate,
		}

		// Find the seasons.
		for _, seasonFolder := range directoriesIn(showFolder) {
			var seasonDetails *TmdbTvSeasonDetails
			if err := readAndUnmarshal(seasonFolder, metadataFilename, &seasonDetails); err != nil {
				continue
			}

			image, _ := filepath.Rel(paths.Root, filepath.Join(seasonFolder, imageFilename))
			season := TVSeasonMetadata{
				TMDBId:   seasonDetails.Id,
				Season:   seasonDetails.SeasonNumber,
				Name:     seasonDetails.Name,
				Overview: seasonDetails.Overview,
				Image:    image,
				AirDate:  seasonDetails.AirDate,
			}

			// Find the episodes.
			for _, epFolder := range directoriesIn(seasonFolder) {
				var epDetails *TmdbTvEpisodeDetails
				if err := readAndUnmarshal(epFolder, metadataFilename, &epDetails); err != nil {
					continue
				}

				image, _ := filepath.Rel(paths.Root, filepath.Join(epFolder, imageFilename))
				media, _ := filepath.Rel(paths.Root, filepath.Join(epFolder, hlsFilename))
				episode := TVEpisodeMetadata{
					TMDBId:         epDetails.Id,
					Episode:        epDetails.EpisodeNumber,
					Name:           epDetails.Name,
					Overview:       epDetails.Overview,
					Image:          image,
					Media:          media,
					AirDate:        epDetails.AirDate,
					ProductionCode: epDetails.ProductionCode,
					Vote:           epDetails.VoteAverage,
				}
				season.Episodes = append(season.Episodes, episode)
			}
			sort.Sort(ByEpisode(season.Episodes))

			show.Seasons = append(show.Seasons, season)
			generateSeasonHTML(seasonFolder, season, paths)
		}
		sort.Sort(BySeason(show.Seasons))

		shows = append(shows, show)
		generateShowHTML(showFolder, show, paths)
	}

	// Find the movies.
	movies := make([]MovieMetadata, 0)
	for _, folder := range directoriesIn(paths.Movies) {
		// Load the metadata.
		var details *TmdbMovieSearchResult
		if err := readAndUnmarshal(folder, metadataFilename, &details); err != nil {
			continue
		}

		// Create the model.
		image, _ := filepath.Rel(paths.Root, filepath.Join(folder, imageFilename))
		backdrop, _ := filepath.Rel(paths.Root, filepath.Join(folder, imageBackdropFilename))
		movie := MovieMetadata{
			TMDBId:      details.Id,
			Name:        details.Title,
			Overview:    details.Overview,
			Image:       image,
			Backdrop:    backdrop,
			ReleaseDate: details.ReleaseDate,
			Vote:        details.VoteAverage,
		}
		movies = append(movies, movie)
	}

	// Make the root metadata.
	capacity := capacity(paths)
	metadata := Metadata{
		TVShows:  shows,
		Movies:   movies,
		Capacity: capacity,
	}

	// Save.
	data, _ := json.MarshalIndent(metadata, "", "    ")
	ioutil.WriteFile(filepath.Join(paths.Root, metadataFilename), data, os.ModePerm)

	generateRootHTML(capacity, paths)
}

// List of full directories (eg path + name).
func directoriesIn(path string) []string {
	directories := make([]string, 0)
	infos, _ := ioutil.ReadDir(path)
	for _, info := range infos {
		if info.IsDir() {
			directory := filepath.Join(path, info.Name())
			directories = append(directories, directory)
		}
	}
	return directories
}

func readAndUnmarshal(folder string, file string, v interface{}) error {
	path := filepath.Join(folder, file)
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}

// Sorting.

type ByEpisode []TVEpisodeMetadata

func (a ByEpisode) Len() int      { return len(a) }
func (a ByEpisode) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByEpisode) Less(i, j int) bool {
	return a[i].Episode < a[j].Episode
}

type BySeason []TVSeasonMetadata

func (a BySeason) Len() int      { return len(a) }
func (a BySeason) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a BySeason) Less(i, j int) bool {
	return a[i].Season < a[j].Season
}

/// Generate a season's html (which is the list of episodes in it).
func generateSeasonHTML(seasonPath string, season TVSeasonMetadata, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	for _, episode := range season.Episodes {
		if isLeft {
			html += "
"
			trOpen = true
		}

		linkPath, _ := filepath.Rel(seasonPath, filepath.Join(paths.Root, episode.Media))
		imagePath, _ := filepath.Rel(seasonPath, filepath.Join(paths.Root, episode.Image))
		name := fmt.Sprintf("S%02d E%02d %s", season.Season, episode.Episode, episode.Name)

		h := htmlTd
		h = strings.Replace(h, "LINK", linkPath, -1)
		h = strings.Replace(h, "IMAGE", imagePath, -1)
		h = strings.Replace(h, "NAME", name, -1)
		html += h

		if !isLeft {
			html += "
"
			trOpen = false
		}

		isLeft = !isLeft
	}

	if trOpen {
		html += "
"
	}
	html += htmlEnd

	// Save.
	outPath := filepath.Join(seasonPath, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Generates the root index.html
func generateRootHTML(capacity string, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	// Movies.
	movieFiles, _ := ioutil.ReadDir(paths.Movies)
	for _, fileInfo := range movieFiles {
		if fileInfo.IsDir() {
			if isLeft {
				html += "
"
				trOpen = true
			}

			linkPath := paths.MoviesRelativeToRoot + "/" + fileInfo.Name() + "/" + hlsFilename
			imagePath := paths.MoviesRelativeToRoot + "/" + fileInfo.Name() + "/" + imageFilename

			h := htmlTd
			h = strings.Replace(h, "LINK", linkPath, -1)
			h = strings.Replace(h, "IMAGE", imagePath, -1)
			h = strings.Replace(h, "NAME", fileInfo.Name(), -1)
			html += h

			if !isLeft {
				html += "
"
				trOpen = false
			}

			isLeft = !isLeft
		}
	}

	// TV Shows.
	tvFiles, _ := ioutil.ReadDir(paths.TV)
	for _, fileInfo := range tvFiles {
		if fileInfo.IsDir() {
			if isLeft {
				html += "
"
				trOpen = true
			}

			linkPath := paths.TVRelativeToRoot + "/" + fileInfo.Name()
			imagePath := paths.TVRelativeToRoot + "/" + fileInfo.Name() + "/" + imageFilename

			h := htmlTd
			h = strings.Replace(h, "LINK", linkPath, -1)
			h = strings.Replace(h, "IMAGE", imagePath, -1)
			h = strings.Replace(h, "NAME", fileInfo.Name(), -1)
			html += h

			if !isLeft {
				html += "
"
				trOpen = false
			}

			isLeft = !isLeft
		}
	}

	if trOpen {
		html += "
"
	}

	// Add the html trailer.
	end := strings.Replace(htmlEnd, "CAPACITY", capacity, -1)
	html += end

	// Save.
	outPath := filepath.Join(paths.Root, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Generate a show's html (which is the list of seasons in it).
func generateShowHTML(showPath string, show TVShowMetadata, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	for _, season := range show.Seasons {
		if isLeft {
			html += "
"
			trOpen = true
		}

		linkPath := tvSeasonFolderNameFor(season.Season)
		imagePath := linkPath + "/" + imageFilename
		name := season.Name

		h := htmlTd
		h = strings.Replace(h, "LINK", linkPath, -1)
		h = strings.Replace(h, "IMAGE", imagePath, -1)
		h = strings.Replace(h, "NAME", name, -1)
		html += h

		if !isLeft {
			html += "
"
			trOpen = false
		}

		isLeft = !isLeft
	}

	if trOpen {
		html += "
"
	}
	html += htmlEnd

	// Save.
	outPath := filepath.Join(showPath, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Figure out how much disk space is left.
func capacity(paths Paths) string {
	cmd := exec.Command("df", "-h", paths.Root)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()
	// TODO split and transpose
	return strings.TrimSpace(out.String())
}
<commit_msg>Movie media path<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
)

type Metadata struct {
	TVShows  []TVShowMetadata
	Movies   []MovieMetadata
	Capacity string
}

type MovieMetadata struct {
	TMDBId      int
	Name        string
	Overview    string
	Image       string
	Backdrop    string
	ReleaseDate string
	Vote        float32
	Media       string
}

type TVShowMetadata struct {
	TMDBId       int
	Name         string
	Overview     string
	Image        string
	Backdrop     string
	FirstAirDate string
	LastAirDate  string
	Seasons      []TVSeasonMetadata
}

type TVSeasonMetadata struct {
	TMDBId   int
	Season   int
	Name     string
	Overview string
	Image    string
	AirDate  string
	Episodes []TVEpisodeMetadata
}

type TVEpisodeMetadata struct {
	TMDBId         int
	Episode        int
	Name           string
	Overview       string
	Image          string
	Media          string
	AirDate        string
	ProductionCode string
	Vote           float32
}

// Generates metadata for everything.
func generateMetadata(paths Paths) {
	log.Println("Generating metadata")

	shows := make([]TVShowMetadata, 0)
	for _, showFolder := range directoriesIn(paths.TV) {

		// Load the metadata.
		var showDetails *TmdbTvShowDetails
		if err := readAndUnmarshal(showFolder, metadataFilename, &showDetails); err != nil {
			continue
		}

		// Create the model.
		image, _ := filepath.Rel(paths.Root, filepath.Join(showFolder, imageFilename))
		backdrop, _ := filepath.Rel(paths.Root, filepath.Join(showFolder, imageBackdropFilename))
		show := TVShowMetadata{
			TMDBId:       showDetails.Id,
			Name:         showDetails.Name,
			Overview:     showDetails.Overview,
			Image:        image,
			Backdrop:     backdrop,
			FirstAirDate: showDetails.FirstAirDate,
			LastAirDate:  showDetails.LastAirDate,
		}

		// Find the seasons.
		for _, seasonFolder := range directoriesIn(showFolder) {
			var seasonDetails *TmdbTvSeasonDetails
			if err := readAndUnmarshal(seasonFolder, metadataFilename, &seasonDetails); err != nil {
				continue
			}

			image, _ := filepath.Rel(paths.Root, filepath.Join(seasonFolder, imageFilename))
			season := TVSeasonMetadata{
				TMDBId:   seasonDetails.Id,
				Season:   seasonDetails.SeasonNumber,
				Name:     seasonDetails.Name,
				Overview: seasonDetails.Overview,
				Image:    image,
				AirDate:  seasonDetails.AirDate,
			}

			// Find the episodes.
			for _, epFolder := range directoriesIn(seasonFolder) {
				var epDetails *TmdbTvEpisodeDetails
				if err := readAndUnmarshal(epFolder, metadataFilename, &epDetails); err != nil {
					continue
				}

				image, _ := filepath.Rel(paths.Root, filepath.Join(epFolder, imageFilename))
				media, _ := filepath.Rel(paths.Root, filepath.Join(epFolder, hlsFilename))
				episode := TVEpisodeMetadata{
					TMDBId:         epDetails.Id,
					Episode:        epDetails.EpisodeNumber,
					Name:           epDetails.Name,
					Overview:       epDetails.Overview,
					Image:          image,
					Media:          media,
					AirDate:        epDetails.AirDate,
					ProductionCode: epDetails.ProductionCode,
					Vote:           epDetails.VoteAverage,
				}
				season.Episodes = append(season.Episodes, episode)
			}
			sort.Sort(ByEpisode(season.Episodes))

			show.Seasons = append(show.Seasons, season)
			generateSeasonHTML(seasonFolder, season, paths)
		}
		sort.Sort(BySeason(show.Seasons))

		shows = append(shows, show)
		generateShowHTML(showFolder, show, paths)
	}

	// Find the movies.
	movies := make([]MovieMetadata, 0)
	for _, folder := range directoriesIn(paths.Movies) {
		// Load the metadata.
		var details *TmdbMovieSearchResult
		if err := readAndUnmarshal(folder, metadataFilename, &details); err != nil {
			continue
		}

		// Create the model.
		image, _ := filepath.Rel(paths.Root, filepath.Join(folder, imageFilename))
		backdrop, _ := filepath.Rel(paths.Root, filepath.Join(folder, imageBackdropFilename))
		media, _ := filepath.Rel(paths.Root, filepath.Join(folder, hlsFilename))
		movie := MovieMetadata{
			TMDBId:      details.Id,
			Name:        details.Title,
			Overview:    details.Overview,
			Image:       image,
			Backdrop:    backdrop,
			ReleaseDate: details.ReleaseDate,
			Vote:        details.VoteAverage,
			Media:       media,
		}
		movies = append(movies, movie)
	}

	// Make the root metadata.
	capacity := capacity(paths)
	metadata := Metadata{
		TVShows:  shows,
		Movies:   movies,
		Capacity: capacity,
	}

	// Save.
	data, _ := json.MarshalIndent(metadata, "", "    ")
	ioutil.WriteFile(filepath.Join(paths.Root, metadataFilename), data, os.ModePerm)

	generateRootHTML(capacity, paths)
}

// List of full directories (eg path + name).
func directoriesIn(path string) []string {
	directories := make([]string, 0)
	infos, _ := ioutil.ReadDir(path)
	for _, info := range infos {
		if info.IsDir() {
			directory := filepath.Join(path, info.Name())
			directories = append(directories, directory)
		}
	}
	return directories
}

func readAndUnmarshal(folder string, file string, v interface{}) error {
	path := filepath.Join(folder, file)
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}

// Sorting.

type ByEpisode []TVEpisodeMetadata

func (a ByEpisode) Len() int      { return len(a) }
func (a ByEpisode) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByEpisode) Less(i, j int) bool {
	return a[i].Episode < a[j].Episode
}

type BySeason []TVSeasonMetadata

func (a BySeason) Len() int      { return len(a) }
func (a BySeason) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a BySeason) Less(i, j int) bool {
	return a[i].Season < a[j].Season
}

/// Generate a season's html (which is the list of episodes in it).
func generateSeasonHTML(seasonPath string, season TVSeasonMetadata, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	for _, episode := range season.Episodes {
		if isLeft {
			html += "
"
			trOpen = true
		}

		linkPath, _ := filepath.Rel(seasonPath, filepath.Join(paths.Root, episode.Media))
		imagePath, _ := filepath.Rel(seasonPath, filepath.Join(paths.Root, episode.Image))
		name := fmt.Sprintf("S%02d E%02d %s", season.Season, episode.Episode, episode.Name)

		h := htmlTd
		h = strings.Replace(h, "LINK", linkPath, -1)
		h = strings.Replace(h, "IMAGE", imagePath, -1)
		h = strings.Replace(h, "NAME", name, -1)
		html += h

		if !isLeft {
			html += "
"
			trOpen = false
		}

		isLeft = !isLeft
	}

	if trOpen {
		html += "
"
	}
	html += htmlEnd

	// Save.
	outPath := filepath.Join(seasonPath, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Generates the root index.html
func generateRootHTML(capacity string, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	// Movies.
	movieFiles, _ := ioutil.ReadDir(paths.Movies)
	for _, fileInfo := range movieFiles {
		if fileInfo.IsDir() {
			if isLeft {
				html += "
"
				trOpen = true
			}

			linkPath := paths.MoviesRelativeToRoot + "/" + fileInfo.Name() + "/" + hlsFilename
			imagePath := paths.MoviesRelativeToRoot + "/" + fileInfo.Name() + "/" + imageFilename

			h := htmlTd
			h = strings.Replace(h, "LINK", linkPath, -1)
			h = strings.Replace(h, "IMAGE", imagePath, -1)
			h = strings.Replace(h, "NAME", fileInfo.Name(), -1)
			html += h

			if !isLeft {
				html += "
"
				trOpen = false
			}

			isLeft = !isLeft
		}
	}

	// TV Shows.
	tvFiles, _ := ioutil.ReadDir(paths.TV)
	for _, fileInfo := range tvFiles {
		if fileInfo.IsDir() {
			if isLeft {
				html += "
"
				trOpen = true
			}

			linkPath := paths.TVRelativeToRoot + "/" + fileInfo.Name()
			imagePath := paths.TVRelativeToRoot + "/" + fileInfo.Name() + "/" + imageFilename

			h := htmlTd
			h = strings.Replace(h, "LINK", linkPath, -1)
			h = strings.Replace(h, "IMAGE", imagePath, -1)
			h = strings.Replace(h, "NAME", fileInfo.Name(), -1)
			html += h

			if !isLeft {
				html += "
"
				trOpen = false
			}

			isLeft = !isLeft
		}
	}

	if trOpen {
		html += "
"
	}

	// Add the html trailer.
	end := strings.Replace(htmlEnd, "CAPACITY", capacity, -1)
	html += end

	// Save.
	outPath := filepath.Join(paths.Root, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Generate a show's html (which is the list of seasons in it).
func generateShowHTML(showPath string, show TVShowMetadata, paths Paths) {
	html := htmlStart
	isLeft := true
	trOpen := false

	for _, season := range show.Seasons {
		if isLeft {
			html += "
"
			trOpen = true
		}

		linkPath := tvSeasonFolderNameFor(season.Season)
		imagePath := linkPath + "/" + imageFilename
		name := season.Name

		h := htmlTd
		h = strings.Replace(h, "LINK", linkPath, -1)
		h = strings.Replace(h, "IMAGE", imagePath, -1)
		h = strings.Replace(h, "NAME", name, -1)
		html += h

		if !isLeft {
			html += "
"
			trOpen = false
		}

		isLeft = !isLeft
	}

	if trOpen {
		html += "
"
	}
	html += htmlEnd

	// Save.
	outPath := filepath.Join(showPath, indexHtml)
	ioutil.WriteFile(outPath, []byte(html), os.ModePerm)
}

/// Figure out how much disk space is left.
func capacity(paths Paths) string {
	cmd := exec.Command("df", "-h", paths.Root)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()
	// TODO split and transpose
	return strings.TrimSpace(out.String())
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 openstack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
)

// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4
// link-local address as documented in "OpenStack Cloud Administrator Guide",
// chapter Compute - Networking with nova-network.
// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service
const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json"

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/2012-08-10/meta_data.json"

var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid")

// Assumes the "2012-08-10" meta_data.json format.
// See http://docs.openstack.org/user-guide/cli_config_drive.html
type Metadata struct {
	Uuid             string `json:"uuid"`
	Name             string `json:"name"`
	AvailabilityZone string `json:"availability_zone"`
	// .. and other fields we don't care about.  Expand as necessary.
}

// parseMetadataUUID reads JSON from OpenStack metadata server and parses
// instance ID out of it.
func parseMetadata(r io.Reader) (*Metadata, error) {
	var metadata Metadata
	json := json.NewDecoder(r)
	if err := json.Decode(&metadata); err != nil {
		return nil, err
	}

	if metadata.Uuid == "" {
		return nil, ErrBadMetadata
	}

	return &metadata, nil
}

func getMetadataFromConfigDrive() (*Metadata, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		out, err := exec.New().Command(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		).CombinedOutput()
		if err != nil {
			glog.V(2).Infof("Unable to run blkid: %v", err)
			return nil, err
		}
		dev = strings.TrimSpace(string(out))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return nil, err
	}
	defer os.Remove(mntdir)

	glog.V(4).Infof("Attempting to mount configdrive %s on %s", dev, mntdir)

	mounter := mount.New("" /* default mount path */)
	err = mounter.Mount(dev, mntdir, "iso9660", []string{"ro"})
	if err != nil {
		err = mounter.Mount(dev, mntdir, "vfat", []string{"ro"})
	}
	if err != nil {
		glog.Errorf("Error mounting configdrive %s: %v", dev, err)
		return nil, err
	}
	defer mounter.Unmount(mntdir)

	glog.V(4).Infof("Configdrive mounted on %s", mntdir)

	f, err := os.Open(
		filepath.Join(mntdir, configDrivePath))
	if err != nil {
		glog.Errorf("Error reading %s on config drive: %v", configDrivePath, err)
		return nil, err
	}
	defer f.Close()

	return parseMetadata(f)
}

func getMetadataFromMetadataService() (*Metadata, error) {
	// Try to get JSON from metdata server.
	glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl)
	resp, err := http.Get(metadataUrl)
	if err != nil {
		glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status)
		glog.V(3).Infof("%v", err)
		return nil, err
	}

	return parseMetadata(resp.Body)
}

// Metadata is fixed for the current host, so cache the value process-wide
var metadataCache *Metadata

func getMetadata() (*Metadata, error) {
	if metadataCache == nil {
		md, err := getMetadataFromConfigDrive()
		if err != nil {
			md, err = getMetadataFromMetadataService()
		}
		if err != nil {
			return nil, err
		}
		metadataCache = md
	}
	return metadataCache, nil
}
<commit_msg>Adding a root filesystem override for kubelet mounter<commit_after>/*
Copyright 2016 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 openstack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/glog"
	"k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/mount"
)

// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4
// link-local address as documented in "OpenStack Cloud Administrator Guide",
// chapter Compute - Networking with nova-network.
// http://docs.openstack.org/admin-guide-cloud/compute-networking-nova.html#metadata-service
const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json"

// Config drive is defined as an iso9660 or vfat (deprecated) drive
// with the "config-2" label.
// http://docs.openstack.org/user-guide/cli-config-drive.html
const configDriveLabel = "config-2"
const configDrivePath = "openstack/2012-08-10/meta_data.json"

var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid")

// Assumes the "2012-08-10" meta_data.json format.
// See http://docs.openstack.org/user-guide/cli_config_drive.html
type Metadata struct {
	Uuid             string `json:"uuid"`
	Name             string `json:"name"`
	AvailabilityZone string `json:"availability_zone"`
	// .. and other fields we don't care about.  Expand as necessary.
}

// parseMetadataUUID reads JSON from OpenStack metadata server and parses
// instance ID out of it.
func parseMetadata(r io.Reader) (*Metadata, error) {
	var metadata Metadata
	json := json.NewDecoder(r)
	if err := json.Decode(&metadata); err != nil {
		return nil, err
	}

	if metadata.Uuid == "" {
		return nil, ErrBadMetadata
	}

	return &metadata, nil
}

func getMetadataFromConfigDrive() (*Metadata, error) {
	// Try to read instance UUID from config drive.
	dev := "/dev/disk/by-label/" + configDriveLabel
	if _, err := os.Stat(dev); os.IsNotExist(err) {
		out, err := exec.New().Command(
			"blkid", "-l",
			"-t", "LABEL="+configDriveLabel,
			"-o", "device",
		).CombinedOutput()
		if err != nil {
			glog.V(2).Infof("Unable to run blkid: %v", err)
			return nil, err
		}
		dev = strings.TrimSpace(string(out))
	}

	mntdir, err := ioutil.TempDir("", "configdrive")
	if err != nil {
		return nil, err
	}
	defer os.Remove(mntdir)

	glog.V(4).Infof("Attempting to mount configdrive %s on %s", dev, mntdir)

	mounter := mount.New()
	err = mounter.Mount(dev, mntdir, "iso9660", []string{"ro"})
	if err != nil {
		err = mounter.Mount(dev, mntdir, "vfat", []string{"ro"})
	}
	if err != nil {
		glog.Errorf("Error mounting configdrive %s: %v", dev, err)
		return nil, err
	}
	defer mounter.Unmount(mntdir)

	glog.V(4).Infof("Configdrive mounted on %s", mntdir)

	f, err := os.Open(
		filepath.Join(mntdir, configDrivePath))
	if err != nil {
		glog.Errorf("Error reading %s on config drive: %v", configDrivePath, err)
		return nil, err
	}
	defer f.Close()

	return parseMetadata(f)
}

func getMetadataFromMetadataService() (*Metadata, error) {
	// Try to get JSON from metdata server.
	glog.V(4).Infof("Attempting to fetch metadata from %s", metadataUrl)
	resp, err := http.Get(metadataUrl)
	if err != nil {
		glog.V(3).Infof("Cannot read %s: %v", metadataUrl, err)
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Unexpected status code when reading metadata from %s: %s", metadataUrl, resp.Status)
		glog.V(3).Infof("%v", err)
		return nil, err
	}

	return parseMetadata(resp.Body)
}

// Metadata is fixed for the current host, so cache the value process-wide
var metadataCache *Metadata

func getMetadata() (*Metadata, error) {
	if metadataCache == nil {
		md, err := getMetadataFromConfigDrive()
		if err != nil {
			md, err = getMetadataFromMetadataService()
		}
		if err != nil {
			return nil, err
		}
		metadataCache = md
	}
	return metadataCache, nil
}
<|endoftext|>