<commit_before>package tasks

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/mit"
	"github.com/MyHomeworkSpace/api-server/util"

	"github.com/MyHomeworkSpace/api-server/config"
)

// some classes have weird times and aren't on the catalog, so we just give up on them
var skipClasses = map[string][]string{
	"2021SP": {"20.051", "21M.442"},
	"2022FA": {"15.S24", "15.830", "21M.460"},
}

type catalogListing struct {
	ID         string `json:"id"`
	ShortTitle string `json:"short"`
	Title      string `json:"title"`

	OfferedFall   bool `bool:"fall"`
	OfferedIAP    bool `bool:"iap"`
	OfferedSpring bool `bool:"spring"`

	FallInstructors   string `json:"fallI"`
	SpringInstructors string `json:"springI"`
}

type subjectOffering struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Section string `json:"section"`
	Term    string `json:"term"`

	Time  string `json:"time"`
	Place string `json:"place"`

	FacultyID   string `json:"facultyID"`
	FacultyName string `json:"facultyName"`

	IsFake   bool `json:"fake"`
	IsMaster bool `json:"master"`

	IsDesign     bool `json:"design"`
	IsLab        bool `json:"lab"`
	IsLecture    bool `json:"lecture"`
	IsRecitation bool `json:"recitation"`
}

// StartImportFromMIT begins an import of the given data from the MIT Data Warehouse.
func StartImportFromMIT(source string, db *sql.DB) error {
	if source != "catalog" && source != "coursews" && source != "offerings" {
		return errors.New("tasks: invalid parameter")
	}

	taskID := "mit_" + source
	taskName := "MIT Import - " + source

	go taskWatcher(taskID, taskName, importFromMIT, source, db)
	return nil
}

func importFromMIT(lastCompletion *time.Time, source string, db *sql.DB) (taskResponse, error) {
	mitConfig := config.GetCurrent().MIT
	params := url.Values{}

	params.Add("source", source)

	currentTerm := mit.GetCurrentTerm()
	params.Add("termCode", currentTerm.Code)
	params.Add("academicYear", currentTerm.Code[:4])

	// TODO: remove
	params.Add("lastUpdateDate", "2019-01-01")

	requestURL := mitConfig.DataProxyURL + "fetch?" + params.Encode()
	if source == "coursews" {
		// actually use the secret coursews API
		requestURL = "https://coursews.mit.edu/coursews/"
	}

	client := &http.Client{}
	request, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return taskResponse{}, err
	}

	if source != "coursews" {
		request.Header.Add("X-MHS-Auth", mitConfig.ProxyToken)
	}

	response, err := client.Do(request)
	if err != nil {
		return taskResponse{}, err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		bodyBytes, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return taskResponse{}, err
		}

		return taskResponse{}, fmt.Errorf(
			"tasks: MIT data server returned status code %d, body: '%s'",
			response.StatusCode,
			string(bodyBytes),
		)
	}

	tx, err := db.Begin()
	if err != nil {
		return taskResponse{}, err
	}

	rowsAffected := int64(0)

	if source == "catalog" {
		listings := []catalogListing{}
		err = json.NewDecoder(response.Body).Decode(&listings)
		if err != nil {
			return taskResponse{}, err
		}

		for _, listing := range listings {
			result, err := tx.Exec(
				`INSERT INTO
					mit_listings(id, shortTitle, title, offeredFall, offeredIAP, offeredSpring, fallInstructors, springInstructors)
					VALUES(?, ?, ?, ?, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					shortTitle = VALUES(shortTitle),
					title = VALUES(title),
					offeredFall = VALUES(offeredFall),
					offeredIAP = VALUES(offeredIAP),
					offeredSpring = VALUES(offeredSpring),
					fallInstructors = VALUES(fallInstructors),
					springInstructors = VALUES(springInstructors)
				`,
				listing.ID,
				listing.ShortTitle,
				listing.Title,
				listing.OfferedFall,
				listing.OfferedIAP,
				listing.OfferedSpring,
				listing.FallInstructors,
				listing.SpringInstructors,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	} else if source == "coursews" {
		// unfortunately, this API gives us data in a rather annoying format
		// so we cannot just parse them into a go struct
		// instead we have to use a hacky series of typecasts :(
		wsData := map[string]interface{}{}
		err = json.NewDecoder(response.Body).Decode(&wsData)
		if err != nil {
			return taskResponse{}, err
		}

		items := wsData["items"].([]interface{})

		// assume it's all the same term
		termInfo, err := mit.GetTermByCode(currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		// first, clear out any data from a previous term
		_, err = tx.Exec("DELETE FROM mit_offerings WHERE term <> ?", currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		for _, itemInterface := range items {
			item := itemInterface.(map[string]interface{})

			itemType := item["type"].(string)

			if itemType == "Class" {
				// we actually don't care about classes
				continue
			}

			if itemType != "LectureSession" && itemType != "LabSession" && itemType != "RecitationSession" {
				return taskResponse{}, fmt.Errorf("tasks: unknown coursews item type '%s'", itemType)
			}

			itemLabel := item["label"].(string)
			itemSectionOf := item["section-of"].(string)
			itemTimeAndPlace := item["timeAndPlace"].(string)

			if itemTimeAndPlace == "null null" {
				// this record tells us absolutely nothing
				// ignore it
				continue
			}

			// for some reason, the labels are the section + class number joined together
			// for example, section L01 of 6.003 has a label of "L016.003"
			// parse out the section ID
			sectionID := strings.Replace(itemLabel, itemSectionOf, "", -1)

			// in order to maximize suffering, the coursews api also combines the time and place fields
			// examples of this include "MW9.30-11 4-251" (easy)
			// or "TR9-11 (MEETS 4/7 TO 5/14) MEC-209" and "M EVE (6-8 PM) BOSTON PRE-REL" (why??)
			// the high quality algorithm to parse this is to break the string into spaces, and keep removing words until it works
			timeAndPlaceParts := strings.Split(itemTimeAndPlace, " ")
			currentTimeString := ""
			parsed := false
			for i := len(timeAndPlaceParts); i > 0; i-- {
				currentTimeString = ""
				for j := 0; j < i; j++ {
					if j != 0 {
						currentTimeString += " "
					}
					currentTimeString += timeAndPlaceParts[j]
				}

				// attempt
				_, err = mit.ParseTimeInfo(currentTimeString, termInfo)
				if err != nil {
					// oofie
					continue
				}

				// we survived!
				parsed = true
				break
			}

			if !parsed {
				return taskResponse{}, fmt.Errorf("tasks: failed to parse timeAndPlace '%s'", itemTimeAndPlace)
			}

			time := currentTimeString
			place := strings.TrimSpace(strings.Replace(itemTimeAndPlace, currentTimeString, "", -1))

			if place == "" {
				return taskResponse{}, fmt.Errorf("tasks: didn't get place from timeAndPlace '%s'", itemTimeAndPlace)
			}

			if place == "null" {
				place = ""
			}

			isDesign := false // design sections seem to not be included?
			isLab := (sectionID[0] == 'B')
			isLecture := (sectionID[0] == 'L')
			isRecitation := (sectionID[0] == 'R')

			// now, try to insert this new record
			// since we have very little info, we do NOT overwrite existing faculty/extra data if we have some
			result, err := tx.Exec(
				`INSERT INTO
					mit_offerings(id, title, section, term, time, place, facultyID, facultyName, isFake, isMaster, isDesign, isLab, isLecture, isRecitation)
					VALUES(?, '', ?, ?, ?, ?, '', '', 0, 0, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					section = VALUES(section),
					term = VALUES(term),
					time = VALUES(time),
					place = VALUES(place),
					isDesign = VALUES(isDesign),
					isLab = VALUES(isLab),
					isLecture = VALUES(isLecture),
					isRecitation = VALUES(isRecitation)`,
				itemSectionOf,
				sectionID,
				currentTerm.Code,
				time,
				place,
				isDesign,
				isLab,
				isLecture,
				isRecitation,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	} else if source == "offerings" {
		offerings := []subjectOffering{}
		err = json.NewDecoder(response.Body).Decode(&offerings)
		if err != nil {
			return taskResponse{}, err
		}

		// first, clear out any data from a previous term
		_, err = tx.Exec("DELETE FROM mit_offerings WHERE term <> ?", currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		for _, offering := range offerings {
			if offering.Time == "" {
				continue
			}

			if util.StringSliceContains(skipClasses[currentTerm.Code], offering.ID) {
				continue
			}

			termInfo, err := mit.GetTermByCode(offering.Term)
			if err != nil {
				return taskResponse{}, err
			}

			// check that we can parse the time info
			_, err = mit.ParseTimeInfo(offering.Time, termInfo)
			if err != nil {
				return taskResponse{}, fmt.Errorf("mit: failed to parse time of offering of %s (%s): %s", offering.ID, offering.Section, err.Error())
			}

			result, err := tx.Exec(
				`INSERT INTO
					mit_offerings(id, title, section, term, time, place, facultyID, facultyName, isFake, isMaster, isDesign, isLab, isLecture, isRecitation)
					VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					title = VALUES(title),
					section = VALUES(section),
					term = VALUES(term),
					time = VALUES(time),
					place = VALUES(place),
					facultyID = VALUES(facultyID),
					facultyName = VALUES(facultyName),
					isFake = VALUES(isFake),
					isMaster = VALUES(isMaster),
					isDesign = VALUES(isDesign),
					isLab = VALUES(isLab),
					isLecture = VALUES(isLecture),
					isRecitation = VALUES(isRecitation)`,
				offering.ID,
				offering.Title,
				offering.Section,
				offering.Term,
				offering.Time,
				offering.Place,
				offering.FacultyID,
				offering.FacultyName,
				offering.IsFake,
				offering.IsMaster,
				offering.IsDesign,
				offering.IsLab,
				offering.IsLecture,
				offering.IsRecitation,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	}

	return taskResponse{
		RowsAffected: rowsAffected,
	}, tx.Commit()
}
<commit_msg>tasks/mitImport: skip 21M.138<commit_after>package tasks

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/mit"
	"github.com/MyHomeworkSpace/api-server/util"

	"github.com/MyHomeworkSpace/api-server/config"
)

// some classes have weird times and aren't on the catalog, so we just give up on them
var skipClasses = map[string][]string{
	"2021SP": {"20.051", "21M.442"},
	"2022FA": {"15.S24", "15.830", "21M.138", "21M.460"},
}

type catalogListing struct {
	ID         string `json:"id"`
	ShortTitle string `json:"short"`
	Title      string `json:"title"`

	OfferedFall   bool `bool:"fall"`
	OfferedIAP    bool `bool:"iap"`
	OfferedSpring bool `bool:"spring"`

	FallInstructors   string `json:"fallI"`
	SpringInstructors string `json:"springI"`
}

type subjectOffering struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Section string `json:"section"`
	Term    string `json:"term"`

	Time  string `json:"time"`
	Place string `json:"place"`

	FacultyID   string `json:"facultyID"`
	FacultyName string `json:"facultyName"`

	IsFake   bool `json:"fake"`
	IsMaster bool `json:"master"`

	IsDesign     bool `json:"design"`
	IsLab        bool `json:"lab"`
	IsLecture    bool `json:"lecture"`
	IsRecitation bool `json:"recitation"`
}

// StartImportFromMIT begins an import of the given data from the MIT Data Warehouse.
func StartImportFromMIT(source string, db *sql.DB) error {
	if source != "catalog" && source != "coursews" && source != "offerings" {
		return errors.New("tasks: invalid parameter")
	}

	taskID := "mit_" + source
	taskName := "MIT Import - " + source

	go taskWatcher(taskID, taskName, importFromMIT, source, db)
	return nil
}

func importFromMIT(lastCompletion *time.Time, source string, db *sql.DB) (taskResponse, error) {
	mitConfig := config.GetCurrent().MIT
	params := url.Values{}

	params.Add("source", source)

	currentTerm := mit.GetCurrentTerm()
	params.Add("termCode", currentTerm.Code)
	params.Add("academicYear", currentTerm.Code[:4])

	// TODO: remove
	params.Add("lastUpdateDate", "2019-01-01")

	requestURL := mitConfig.DataProxyURL + "fetch?" + params.Encode()
	if source == "coursews" {
		// actually use the secret coursews API
		requestURL = "https://coursews.mit.edu/coursews/"
	}

	client := &http.Client{}
	request, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return taskResponse{}, err
	}

	if source != "coursews" {
		request.Header.Add("X-MHS-Auth", mitConfig.ProxyToken)
	}

	response, err := client.Do(request)
	if err != nil {
		return taskResponse{}, err
	}

	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		bodyBytes, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return taskResponse{}, err
		}

		return taskResponse{}, fmt.Errorf(
			"tasks: MIT data server returned status code %d, body: '%s'",
			response.StatusCode,
			string(bodyBytes),
		)
	}

	tx, err := db.Begin()
	if err != nil {
		return taskResponse{}, err
	}

	rowsAffected := int64(0)

	if source == "catalog" {
		listings := []catalogListing{}
		err = json.NewDecoder(response.Body).Decode(&listings)
		if err != nil {
			return taskResponse{}, err
		}

		for _, listing := range listings {
			result, err := tx.Exec(
				`INSERT INTO
					mit_listings(id, shortTitle, title, offeredFall, offeredIAP, offeredSpring, fallInstructors, springInstructors)
					VALUES(?, ?, ?, ?, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					shortTitle = VALUES(shortTitle),
					title = VALUES(title),
					offeredFall = VALUES(offeredFall),
					offeredIAP = VALUES(offeredIAP),
					offeredSpring = VALUES(offeredSpring),
					fallInstructors = VALUES(fallInstructors),
					springInstructors = VALUES(springInstructors)
				`,
				listing.ID,
				listing.ShortTitle,
				listing.Title,
				listing.OfferedFall,
				listing.OfferedIAP,
				listing.OfferedSpring,
				listing.FallInstructors,
				listing.SpringInstructors,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	} else if source == "coursews" {
		// unfortunately, this API gives us data in a rather annoying format
		// so we cannot just parse them into a go struct
		// instead we have to use a hacky series of typecasts :(
		wsData := map[string]interface{}{}
		err = json.NewDecoder(response.Body).Decode(&wsData)
		if err != nil {
			return taskResponse{}, err
		}

		items := wsData["items"].([]interface{})

		// assume it's all the same term
		termInfo, err := mit.GetTermByCode(currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		// first, clear out any data from a previous term
		_, err = tx.Exec("DELETE FROM mit_offerings WHERE term <> ?", currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		for _, itemInterface := range items {
			item := itemInterface.(map[string]interface{})

			itemType := item["type"].(string)

			if itemType == "Class" {
				// we actually don't care about classes
				continue
			}

			if itemType != "LectureSession" && itemType != "LabSession" && itemType != "RecitationSession" {
				return taskResponse{}, fmt.Errorf("tasks: unknown coursews item type '%s'", itemType)
			}

			itemLabel := item["label"].(string)
			itemSectionOf := item["section-of"].(string)
			itemTimeAndPlace := item["timeAndPlace"].(string)

			if itemTimeAndPlace == "null null" {
				// this record tells us absolutely nothing
				// ignore it
				continue
			}

			// for some reason, the labels are the section + class number joined together
			// for example, section L01 of 6.003 has a label of "L016.003"
			// parse out the section ID
			sectionID := strings.Replace(itemLabel, itemSectionOf, "", -1)

			// in order to maximize suffering, the coursews api also combines the time and place fields
			// examples of this include "MW9.30-11 4-251" (easy)
			// or "TR9-11 (MEETS 4/7 TO 5/14) MEC-209" and "M EVE (6-8 PM) BOSTON PRE-REL" (why??)
			// the high quality algorithm to parse this is to break the string into spaces, and keep removing words until it works
			timeAndPlaceParts := strings.Split(itemTimeAndPlace, " ")
			currentTimeString := ""
			parsed := false
			for i := len(timeAndPlaceParts); i > 0; i-- {
				currentTimeString = ""
				for j := 0; j < i; j++ {
					if j != 0 {
						currentTimeString += " "
					}
					currentTimeString += timeAndPlaceParts[j]
				}

				// attempt
				_, err = mit.ParseTimeInfo(currentTimeString, termInfo)
				if err != nil {
					// oofie
					continue
				}

				// we survived!
				parsed = true
				break
			}

			if !parsed {
				return taskResponse{}, fmt.Errorf("tasks: failed to parse timeAndPlace '%s'", itemTimeAndPlace)
			}

			time := currentTimeString
			place := strings.TrimSpace(strings.Replace(itemTimeAndPlace, currentTimeString, "", -1))

			if place == "" {
				return taskResponse{}, fmt.Errorf("tasks: didn't get place from timeAndPlace '%s'", itemTimeAndPlace)
			}

			if place == "null" {
				place = ""
			}

			isDesign := false // design sections seem to not be included?
			isLab := (sectionID[0] == 'B')
			isLecture := (sectionID[0] == 'L')
			isRecitation := (sectionID[0] == 'R')

			// now, try to insert this new record
			// since we have very little info, we do NOT overwrite existing faculty/extra data if we have some
			result, err := tx.Exec(
				`INSERT INTO
					mit_offerings(id, title, section, term, time, place, facultyID, facultyName, isFake, isMaster, isDesign, isLab, isLecture, isRecitation)
					VALUES(?, '', ?, ?, ?, ?, '', '', 0, 0, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					section = VALUES(section),
					term = VALUES(term),
					time = VALUES(time),
					place = VALUES(place),
					isDesign = VALUES(isDesign),
					isLab = VALUES(isLab),
					isLecture = VALUES(isLecture),
					isRecitation = VALUES(isRecitation)`,
				itemSectionOf,
				sectionID,
				currentTerm.Code,
				time,
				place,
				isDesign,
				isLab,
				isLecture,
				isRecitation,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	} else if source == "offerings" {
		offerings := []subjectOffering{}
		err = json.NewDecoder(response.Body).Decode(&offerings)
		if err != nil {
			return taskResponse{}, err
		}

		// first, clear out any data from a previous term
		_, err = tx.Exec("DELETE FROM mit_offerings WHERE term <> ?", currentTerm.Code)
		if err != nil {
			return taskResponse{}, err
		}

		for _, offering := range offerings {
			if offering.Time == "" {
				continue
			}

			if util.StringSliceContains(skipClasses[currentTerm.Code], offering.ID) {
				continue
			}

			termInfo, err := mit.GetTermByCode(offering.Term)
			if err != nil {
				return taskResponse{}, err
			}

			// check that we can parse the time info
			_, err = mit.ParseTimeInfo(offering.Time, termInfo)
			if err != nil {
				return taskResponse{}, fmt.Errorf("mit: failed to parse time of offering of %s (%s): %s", offering.ID, offering.Section, err.Error())
			}

			result, err := tx.Exec(
				`INSERT INTO
					mit_offerings(id, title, section, term, time, place, facultyID, facultyName, isFake, isMaster, isDesign, isLab, isLecture, isRecitation)
					VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
				ON DUPLICATE KEY UPDATE
					id = VALUES(id),
					title = VALUES(title),
					section = VALUES(section),
					term = VALUES(term),
					time = VALUES(time),
					place = VALUES(place),
					facultyID = VALUES(facultyID),
					facultyName = VALUES(facultyName),
					isFake = VALUES(isFake),
					isMaster = VALUES(isMaster),
					isDesign = VALUES(isDesign),
					isLab = VALUES(isLab),
					isLecture = VALUES(isLecture),
					isRecitation = VALUES(isRecitation)`,
				offering.ID,
				offering.Title,
				offering.Section,
				offering.Term,
				offering.Time,
				offering.Place,
				offering.FacultyID,
				offering.FacultyName,
				offering.IsFake,
				offering.IsMaster,
				offering.IsDesign,
				offering.IsLab,
				offering.IsLecture,
				offering.IsRecitation,
			)
			if err != nil {
				return taskResponse{}, err
			}

			rowAffected, err := result.RowsAffected()
			if err != nil {
				return taskResponse{}, err
			}
			if rowAffected > 0 {
				rowsAffected++
			}
		}
	}

	return taskResponse{
		RowsAffected: rowsAffected,
	}, tx.Commit()
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
)

func StackCreateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "create",
		Short: "create a stack",
		Long:  `Create a stack.`,
		Run:   createStack,
	}

	return cmd
}

func createStack(cmd *cobra.Command, args []string) {
	/*
		c := client.NewClient(client.NewApiConfig(cmd))
		body, err := c.AuthGet("/alm/stack")
		d.ErrorExit(err, 1)

		var stacks []stack.ListStack
		err = json.Unmarshal(body, &stacks)
		d.ErrorExit(err, 1)

		pfmt := cli.GetCliStringFlag(cmd, "fmt")
		switch pfmt {
		case "raw":
			fmt.Println(string(body))

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				err = ioutil.WriteFile(f, body, 0644)
				d.ErrorExit(err, 1)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		case "text":
			indent := cli.GetCliIntFlag(cmd, "indent")
			stack.PrintR(os.Stdout, &stacks[0], 0, indent)

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				fp, err := os.Create(f)
				d.ErrorExit(err, 1)

				defer fp.Close()
				w := bufio.NewWriter(fp)
				defer w.Flush()
				stack.PrintR(w, &stacks[0], 0, indent)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		case "json":
			indent := cli.GetCliIntFlag(cmd, "indent")
			mi, err := json.MarshalIndent(stacks, "", pretty.Indent(indent))
			d.ErrorExit(err, 1)

			fmt.Println(string(mi))

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				err = ioutil.WriteFile(f, mi, 0644)
				d.ErrorExit(err, 1)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		default:
			if pfmt == "min" || pfmt == "" {
				stbl := term.NewTable(0, 10, 5, ' ', 0)
				fmt.Fprintf(stbl, "STACK ID\tSTACK NAME\tPLATFORM\tSTATUS\tREGION\tLAUNCHED\n")
				for _, s := range stacks {
					timestr := s.CreateTime
					t, err := time.Parse(time.RFC3339, s.CreateTime)
					if err == nil {
						timestr = t.Format(time.RFC1123)
					}

					platform := "?"
					if s.Configuration.AWS != "" {
						platform = "AWS"
					}

					fmt.Fprintf(stbl, "%s\t%s\t%s\t%s\t%s\t%s\n",
						s.StackId,
						s.Nickname,
						platform,
						s.StackStatus,
						s.Configuration.Region,
						timestr)
				}

				term.Print(stbl)
				term.Flush()
			}
		}
	*/
}
<commit_msg>Updates to stack create.<commit_after>package cmd

import (
	"github.com/mobingilabs/mocli/pkg/cli"
	d "github.com/mobingilabs/mocli/pkg/debug"
	"github.com/spf13/cobra"
)

func StackCreateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "create",
		Short: "create a stack",
		Long: `Create a stack. For now, 'aws' is the only supported vendor.

You can get your credential id using the command:

  $ ` + cli.BinName() + ` creds list

If credential id is empty, cli will attempt to get the list using the
command above and use the first one in the list (if more than one).`,
		Run: createStack,
	}

	cmd.Flags().String("vendor", "aws", "vendor/provider")
	cmd.Flags().String("cred", "", "credential id")
	return cmd
}

func createStack(cmd *cobra.Command, args []string) {
	d.Info("Todo")
	/*
		c := client.NewClient(client.NewApiConfig(cmd))
		body, err := c.AuthGet("/alm/stack")
		d.ErrorExit(err, 1)

		var stacks []stack.ListStack
		err = json.Unmarshal(body, &stacks)
		d.ErrorExit(err, 1)

		pfmt := cli.GetCliStringFlag(cmd, "fmt")
		switch pfmt {
		case "raw":
			fmt.Println(string(body))

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				err = ioutil.WriteFile(f, body, 0644)
				d.ErrorExit(err, 1)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		case "text":
			indent := cli.GetCliIntFlag(cmd, "indent")
			stack.PrintR(os.Stdout, &stacks[0], 0, indent)

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				fp, err := os.Create(f)
				d.ErrorExit(err, 1)

				defer fp.Close()
				w := bufio.NewWriter(fp)
				defer w.Flush()
				stack.PrintR(w, &stacks[0], 0, indent)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		case "json":
			indent := cli.GetCliIntFlag(cmd, "indent")
			mi, err := json.MarshalIndent(stacks, "", pretty.Indent(indent))
			d.ErrorExit(err, 1)

			fmt.Println(string(mi))

			// write to file option
			f := cli.GetCliStringFlag(cmd, "out")
			if f != "" {
				err = ioutil.WriteFile(f, mi, 0644)
				d.ErrorExit(err, 1)
				d.Info(fmt.Sprintf("Output written to %s.", f))
			}
		default:
			if pfmt == "min" || pfmt == "" {
				stbl := term.NewTable(0, 10, 5, ' ', 0)
				fmt.Fprintf(stbl, "STACK ID\tSTACK NAME\tPLATFORM\tSTATUS\tREGION\tLAUNCHED\n")
				for _, s := range stacks {
					timestr := s.CreateTime
					t, err := time.Parse(time.RFC3339, s.CreateTime)
					if err == nil {
						timestr = t.Format(time.RFC1123)
					}

					platform := "?"
					if s.Configuration.AWS != "" {
						platform = "AWS"
					}

					fmt.Fprintf(stbl, "%s\t%s\t%s\t%s\t%s\t%s\n",
						s.StackId,
						s.Nickname,
						platform,
						s.StackStatus,
						s.Configuration.Region,
						timestr)
				}

				term.Print(stbl)
				term.Flush()
			}
		}
	*/
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var configVars = [...]string{
	"endpoint",
	"auth-endpoint",
	"user",
	"account",
	"token",
	"debug-level",
	"yubikey",
}

// ConfigVar is a struct which contains a name-value-source triplet
// Source is up to two words separated by a space. The first word is the source type: FLAG, ENV, DIR, CODE.
// The second is the name of the flag/file/environment var used.
type ConfigVar struct {
	Name   string
	Value  string
	Source string
}

// ConfigManager is an interface defining a key->value store that also knows where the values were set from.
type ConfigManager interface {
	Get(string) (string, error)
	GetIgnoreErr(string) string
	GetBool(string) (bool, error)
	GetV(string) (ConfigVar, error)
	GetAll() ([]ConfigVar, error)
	Set(string, string, string)
	SetPersistent(string, string, string) error
	Unset(string) error
	GetDebugLevel() int
	Force() bool
	Silent() bool
	EndpointName() string
	PanelURL() string

	ImportFlags(*flag.FlagSet) []string
}

// Params currently used:
// token - an OAuth 2.0 bearer token to use when authenticating
// username - the default username to use - if not present, $USER
// endpoint - the default endpoint to use - if not present, https://uk0.bigv.io
// auth-endpoint - the default auth API endpoint to use - if not present, https://auth.bytemark.co.uk

// A Config determines the configuration of the bigv client.
// It's responsible for handling things like the credentials to use and what endpoints to talk to.
//
// Each configuration item is read from the following places, falling back to successive places:
//
// Per-command command-line flags, global command-line flags, environment variables, configuration directory, hard-coded defaults
//
//The location of the configuration directory is read from global command-line flags, or is otherwise ~/.go-bigv
//
type Config struct {
	debugLevel  int
	mainFlags   *flag.FlagSet
	Dir         string
	Memo        map[string]ConfigVar
	Definitions map[string]string
}

type ConfigDirInvalidError struct {
	Path string
}

func (e *ConfigDirInvalidError) Error() string {
	return fmt.Sprintf("The config directory is '%s' but it doesn't seem to be a directory.", e.Path)
}

type CannotLoadDefinitionsError struct {
	Err error
}

func (e *CannotLoadDefinitionsError) Error() string {
	return fmt.Sprintf("Unable to load the definitions file from the BigV API.")
}

type ConfigReadError struct {
	Name string
	Path string
	Err  error
}

func (e *ConfigReadError) Error() string {
	return fmt.Sprintf("Unable to read config for %s from %s.", e.Name, e.Path)
}

type ConfigWriteError struct {
	Name string
	Path string
	Err  error
}

func (e *ConfigWriteError) Error() string {
	return fmt.Sprintf("Unable to write persistent config for %s (%s).", e.Name, e.Path)
}

// Do I really need to have the flags passed in here?
// Yes. Doing commands will be sorted out in a different place, and I don't want to touch it here.

// NewConfig sets up a new config struct. Pass in an empty string to default to ~/.go-bigv
func NewConfig(configDir string, flags *flag.FlagSet) (config *Config, err error) {
	config = new(Config)
	config.Memo = make(map[string]ConfigVar)
	config.Dir = filepath.Join(os.Getenv("HOME"), "/.go-bigv")
	config.mainFlags = flags
	if os.Getenv("BIGV_CONFIG_DIR") != "" {
		config.Dir = os.Getenv("BIGV_CONFIG_DIR")
	}

	if configDir != "" {
		config.Dir = configDir
	}

	err = os.MkdirAll(config.Dir, 0700)
	if err != nil {

		return nil, err
	}

	stat, err := os.Stat(config.Dir)
	if err != nil {
		return nil, err
	}

	if !stat.IsDir() {
		return nil, &ConfigDirInvalidError{config.Dir}
	}

	config.ImportFlags(flags)
	strDL, err := config.Get("debug-level")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else {
		debugLevel, err := strconv.ParseInt(strDL, 10, 0)
		if err == nil {
			config.debugLevel = int(debugLevel)
		}
	}
	return config, nil
}

func (config *Config) ImportFlags(flags *flag.FlagSet) []string {
	if flags != nil {
		if flags.Parsed() {
			// dump all the flags into the memo
			// should be reet...reet?
			flags.Visit(func(f *flag.Flag) {
				config.Memo[f.Name] = ConfigVar{
					f.Name,
					f.Value.String(),
					"FLAG " + f.Name,
				}
			})
			if flags != config.mainFlags {

				args := flags.Args()
				for _, arg := range args {
					if strings.HasPrefix(arg, "-") {
						fmt.Fprintf(os.Stderr, "Flag-like argument '%s' specified after your arguments\r\nBe aware that only flags placed before your arguments are parsed.\r\nSee the help for the command you're calling for invocation examples.\r\n\r\n", arg)
						break
					}
				}
			}
			return flags.Args()
		}
	}
	return nil
}

// GetDebugLevel returns the current debug-level as an integer. This is used throughout the bigv.io/client library to determine verbosity of output.
func (config *Config) GetDebugLevel() int {
	return config.debugLevel
}

// GetPath joins the given string onto the end of the Config.Dir path
func (config *Config) GetPath(name string) string {
	return filepath.Join(config.Dir, name)
}

// LoadDefinitions reads the local copy of the definitions json file, or downloads it from the endpoint if it's too old or nonexistant.
// Eventually this will be used to provide information on various things throughout the application
func (config *Config) LoadDefinitions() error {
	endpoint, err := config.Get("endpoint")
	if err != nil {
		return err
	}
	defPath := config.GetPath("definitions-" + endpoint + ".json")
	stat, err := os.Stat(defPath)

	if err != nil || time.Since(stat.ModTime()) > 24*time.Hour {
		endpoint, err := config.Get("endpoint")
		if err != nil {
			return err
		}
		if !config.Silent() {
			fmt.Fprintf(os.Stderr, "Downloading definitions for %s...\r\n", endpoint)
		}
		c := &http.Client{}
		req, err := http.NewRequest("GET", endpoint+"/definitions.json", nil)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/json")
		res, err := c.Do(req)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		if res.StatusCode == 200 {
			responseBody, err := ioutil.ReadAll(res.Body)
			if err != nil {
				return &CannotLoadDefinitionsError{err}
			}
			ioutil.WriteFile(defPath, responseBody, 0660)

			err = json.Unmarshal(responseBody, config.Definitions)
			fmt.Fprintln(os.Stderr)
			return err
		} else {
			// TODO(telyn): Unexpected status code
		}
	} else {
		defs, err := ioutil.ReadFile(defPath)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		err = json.Unmarshal(defs, config.Definitions)
		return err
	}
	return nil

}

// Get returns the value of a ConfigVar. Used to simplify code when the source is unnecessary.
func (config *Config) Get(name string) (string, error) {
	v, err := config.GetV(name)
	return v.Value, err
}

// GetIgnoreErr returns the value of a ConfigVar or an empty string , if it was unable to read it for whatever reason.
func (config *Config) GetIgnoreErr(name string) string {
	s, _ := config.Get(name)
	return s
}

// GetV returns the ConfigVar for the given key.
func (config *Config) GetV(name string) (ConfigVar, error) {
	// try to read the Memo
	name = strings.ToLower(name)
	if val, ok := config.Memo[name]; ok {
		return val, nil
	}
	return config.read(name)
}

// GetAll returns all of the available ConfigVars in the Config.
func (config *Config) GetAll() (vars []ConfigVar, err error) {
	vars = make([]ConfigVar, len(configVars))
	for i, v := range configVars {
		vars[i], err = config.GetV(v)
		if err != nil {
			return nil, err
		}
	}
	return vars, nil
}

// GetDefault returns the default ConfigVar for the given key.
func (config *Config) GetDefault(name string) ConfigVar {
	// ideally most of these should just be	os.Getenv("BIGV_"+name.Upcase().Replace("-","_"))
	switch name {
	case "user":
		// we don't actually want to default to USER - that will happen during Dispatcher's PromptForCredentials so it can be all "Hey you should bigv config set user <youruser>"
		return ConfigVar{"user", os.Getenv("BIGV_USER"), "ENV BIGV_USER"}
	case "endpoint":
		v := ConfigVar{"endpoint", "https://uk0.bigv.io", "CODE"}

		val := os.Getenv("BIGV_ENDPOINT")
		if val != "" {
			v.Value = val
			v.Source = "ENV BIGV_ENDPOINT"
		}
		return v
	case "auth-endpoint":
		v := ConfigVar{"auth-endpoint", "https://auth.bytemark.co.uk", "CODE"}

		val := os.Getenv("BIGV_AUTH_ENDPOINT")
		if val != "" {
			v.Value = val
			v.Source = "ENV BIGV_AUTH_ENDPOINT"
		}
		return v
	case "account":
		val := os.Getenv("BIGV_ACCOUNT")
		if val != "" {
			return ConfigVar{
				"account",
				val,
				"ENV BIGV_AUTH_ENDPOINT",
			}
		}
		def := config.GetDefault("user")
		def.Name = "account"
		return def
	case "debug-level":
		v := ConfigVar{"debug-level", "0", "CODE"}
		if val := os.Getenv("BIGV_DEBUG_LEVEL"); val != "" {
			v.Value = val
		}
		return v
	case "silent":
		return ConfigVar{"silent", "false", "CODE"}
	case "force":
		return ConfigVar{"force", "false", "CODE"}
	}
	return ConfigVar{"", "", ""}
}

func (config *Config) GetBool(name string) (bool, error) {
	v, err := config.Get(name)
	if err != nil {
		return false, err
	}
	return !(v == "" || v == "false"), nil
}

func (config *Config) read(name string) (ConfigVar, error) {
	path := config.GetPath(name)
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return config.GetDefault(name), nil
		}

		return config.GetDefault(name), &ConfigReadError{Name: name, Path: path, Err: err}
	}

	return ConfigVar{name, string(contents), "FILE " + path}, nil
}

// Set stores the given key-value pair in config's Memo. This storage does not persist once the program terminates.
func (config *Config) Set(name, value, source string) {
	config.Memo[name] = ConfigVar{name, value, source}
}

// SetPersistent writes a file to the config directory for the given key-value pair.
func (config *Config) SetPersistent(name, value, source string) error {
	path := config.GetPath(name)
	config.Set(name, value, source)
	err := ioutil.WriteFile(path, []byte(value), 0600)
	if err != nil {
		return &ConfigWriteError{Name: name, Path: path, Err: err}
	}
	return nil
}

// Unset removes the named key from both config's Memo and the user's config directory.
func (config *Config) Unset(name string) error {
	delete(config.Memo, name)
	return os.Remove(config.GetPath(name))
}

func (config *Config) Force() bool {
	force, _ := config.GetBool("force")
	return force
}
func (config *Config) Silent() bool {
	silent, _ := config.GetBool("silent")
	return silent
}

func (config *Config) PanelURL() string {
	endpoint := config.EndpointName()
	if strings.EqualFold(endpoint, "uk0.bigv.io") {
		return "https://panel-beta.bytemark.co.uk"
	}
	if strings.EqualFold(endpoint, "int.bigv.io") {
		// worrying leaky code?
		return "https://panel-int.vlan863.bytemark.uk0.bigv.io"
	}
	panel := config.GetIgnoreErr("panel-address")
	if panel == "" {
		panel = "https://your.panel.address"
	}
	return panel
}

func (config *Config) EndpointName() string {
	endpoint := config.GetIgnoreErr("endpoint")
	endpoint = strings.TrimPrefix(endpoint, "https://")
	endpoint = strings.TrimPrefix(endpoint, "http://") // it never hurts to be prepared
	return endpoint
}
<commit_msg>Move .go-bigv into %APPDATA% on windows<commit_after>package util

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
)

var configVars = [...]string{
	"endpoint",
	"auth-endpoint",
	"user",
	"account",
	"token",
	"debug-level",
	"yubikey",
}

// ConfigVar is a struct which contains a name-value-source triplet
// Source is up to two words separated by a space. The first word is the source type: FLAG, ENV, DIR, CODE.
// The second is the name of the flag/file/environment var used.
type ConfigVar struct {
	Name   string
	Value  string
	Source string
}

// ConfigManager is an interface defining a key->value store that also knows where the values were set from.
type ConfigManager interface {
	Get(string) (string, error)
	GetIgnoreErr(string) string
	GetBool(string) (bool, error)
	GetV(string) (ConfigVar, error)
	GetAll() ([]ConfigVar, error)
	Set(string, string, string)
	SetPersistent(string, string, string) error
	Unset(string) error
	GetDebugLevel() int
	Force() bool
	Silent() bool
	EndpointName() string
	PanelURL() string

	ImportFlags(*flag.FlagSet) []string
}

// Params currently used:
// token - an OAuth 2.0 bearer token to use when authenticating
// username - the default username to use - if not present, $USER
// endpoint - the default endpoint to use - if not present, https://uk0.bigv.io
// auth-endpoint - the default auth API endpoint to use - if not present, https://auth.bytemark.co.uk

// A Config determines the configuration of the bigv client.
// It's responsible for handling things like the credentials to use and what endpoints to talk to.
//
// Each configuration item is read from the following places, falling back to successive places:
//
// Per-command command-line flags, global command-line flags, environment variables, configuration directory, hard-coded defaults
//
//The location of the configuration directory is read from global command-line flags, or is otherwise ~/.go-bigv
//
type Config struct {
	debugLevel  int
	mainFlags   *flag.FlagSet
	Dir         string
	Memo        map[string]ConfigVar
	Definitions map[string]string
}

type ConfigDirInvalidError struct {
	Path string
}

func (e *ConfigDirInvalidError) Error() string {
	return fmt.Sprintf("The config directory is '%s' but it doesn't seem to be a directory.", e.Path)
}

type CannotLoadDefinitionsError struct {
	Err error
}

func (e *CannotLoadDefinitionsError) Error() string {
	return fmt.Sprintf("Unable to load the definitions file from the BigV API.")
}

type ConfigReadError struct {
	Name string
	Path string
	Err  error
}

func (e *ConfigReadError) Error() string {
	return fmt.Sprintf("Unable to read config for %s from %s.", e.Name, e.Path)
}

type ConfigWriteError struct {
	Name string
	Path string
	Err  error
}

func (e *ConfigWriteError) Error() string {
	return fmt.Sprintf("Unable to write persistent config for %s (%s).", e.Name, e.Path)
}

// Do I really need to have the flags passed in here?
// Yes. Doing commands will be sorted out in a different place, and I don't want to touch it here.

// NewConfig sets up a new config struct. Pass in an empty string to default to ~/.go-bigv
func NewConfig(configDir string, flags *flag.FlagSet) (config *Config, err error) {
	config = new(Config)
	config.Memo = make(map[string]ConfigVar)
	home := os.Getenv("HOME")
	if runtime.GOOS == "windows" {
		home = os.Getenv("APPDATA")

	}
	config.Dir = filepath.Join(home, "/.go-bigv")
	config.mainFlags = flags
	if os.Getenv("BIGV_CONFIG_DIR") != "" {
		config.Dir = os.Getenv("BIGV_CONFIG_DIR")
	}

	if configDir != "" {
		config.Dir = configDir
	}

	err = os.MkdirAll(config.Dir, 0700)
	if err != nil {

		return nil, err
	}

	stat, err := os.Stat(config.Dir)
	if err != nil {
		return nil, err
	}

	if !stat.IsDir() {
		return nil, &ConfigDirInvalidError{config.Dir}
	}

	config.ImportFlags(flags)
	strDL, err := config.Get("debug-level")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else {
		debugLevel, err := strconv.ParseInt(strDL, 10, 0)
		if err == nil {
			config.debugLevel = int(debugLevel)
		}
	}
	return config, nil
}

func (config *Config) ImportFlags(flags *flag.FlagSet) []string {
	if flags != nil {
		if flags.Parsed() {
			// dump all the flags into the memo
			// should be reet...reet?
			flags.Visit(func(f *flag.Flag) {
				config.Memo[f.Name] = ConfigVar{
					f.Name,
					f.Value.String(),
					"FLAG " + f.Name,
				}
			})
			if flags != config.mainFlags {

				args := flags.Args()
				for _, arg := range args {
					if strings.HasPrefix(arg, "-") {
						fmt.Fprintf(os.Stderr, "Flag-like argument '%s' specified after your arguments\r\nBe aware that only flags placed before your arguments are parsed.\r\nSee the help for the command you're calling for invocation examples.\r\n\r\n", arg)
						break
					}
				}
			}
			return flags.Args()
		}
	}
	return nil
}

// GetDebugLevel returns the current debug-level as an integer. This is used throughout the bigv.io/client library to determine verbosity of output.
func (config *Config) GetDebugLevel() int {
	return config.debugLevel
}

// GetPath joins the given string onto the end of the Config.Dir path
func (config *Config) GetPath(name string) string {
	return filepath.Join(config.Dir, name)
}

// LoadDefinitions reads the local copy of the definitions json file, or downloads it from the endpoint if it's too old or nonexistant.
// Eventually this will be used to provide information on various things throughout the application
func (config *Config) LoadDefinitions() error {
	endpoint, err := config.Get("endpoint")
	if err != nil {
		return err
	}
	defPath := config.GetPath("definitions-" + endpoint + ".json")
	stat, err := os.Stat(defPath)

	if err != nil || time.Since(stat.ModTime()) > 24*time.Hour {
		endpoint, err := config.Get("endpoint")
		if err != nil {
			return err
		}
		if !config.Silent() {
			fmt.Fprintf(os.Stderr, "Downloading definitions for %s...\r\n", endpoint)
		}
		c := &http.Client{}
		req, err := http.NewRequest("GET", endpoint+"/definitions.json", nil)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/json")
		res, err := c.Do(req)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		if res.StatusCode == 200 {
			responseBody, err := ioutil.ReadAll(res.Body)
			if err != nil {
				return &CannotLoadDefinitionsError{err}
			}
			ioutil.WriteFile(defPath, responseBody, 0660)

			err = json.Unmarshal(responseBody, config.Definitions)
			fmt.Fprintln(os.Stderr)
			return err
		} else {
			// TODO(telyn): Unexpected status code
		}
	} else {
		defs, err := ioutil.ReadFile(defPath)
		if err != nil {
			return &CannotLoadDefinitionsError{err}
		}
		err = json.Unmarshal(defs, config.Definitions)
		return err
	}
	return nil

}

// Get returns the value of a ConfigVar. Used to simplify code when the source is unnecessary.
func (config *Config) Get(name string) (string, error) {
	v, err := config.GetV(name)
	return v.Value, err
}

// GetIgnoreErr returns the value of a ConfigVar or an empty string , if it was unable to read it for whatever reason.
func (config *Config) GetIgnoreErr(name string) string {
	s, _ := config.Get(name)
	return s
}

// GetV returns the ConfigVar for the given key.
func (config *Config) GetV(name string) (ConfigVar, error) {
	// try to read the Memo
	name = strings.ToLower(name)
	if val, ok := config.Memo[name]; ok {
		return val, nil
	}
	return config.read(name)
}

// GetAll returns all of the available ConfigVars in the Config.
func (config *Config) GetAll() (vars []ConfigVar, err error) {
	vars = make([]ConfigVar, len(configVars))
	for i, v := range configVars {
		vars[i], err = config.GetV(v)
		if err != nil {
			return nil, err
		}
	}
	return vars, nil
}

// GetDefault returns the default ConfigVar for the given key.
func (config *Config) GetDefault(name string) ConfigVar {
	// ideally most of these should just be	os.Getenv("BIGV_"+name.Upcase().Replace("-","_"))
	switch name {
	case "user":
		// we don't actually want to default to USER - that will happen during Dispatcher's PromptForCredentials so it can be all "Hey you should bigv config set user <youruser>"
		return ConfigVar{"user", os.Getenv("BIGV_USER"), "ENV BIGV_USER"}
	case "endpoint":
		v := ConfigVar{"endpoint", "https://uk0.bigv.io", "CODE"}

		val := os.Getenv("BIGV_ENDPOINT")
		if val != "" {
			v.Value = val
			v.Source = "ENV BIGV_ENDPOINT"
		}
		return v
	case "auth-endpoint":
		v := ConfigVar{"auth-endpoint", "https://auth.bytemark.co.uk", "CODE"}

		val := os.Getenv("BIGV_AUTH_ENDPOINT")
		if val != "" {
			v.Value = val
			v.Source = "ENV BIGV_AUTH_ENDPOINT"
		}
		return v
	case "account":
		val := os.Getenv("BIGV_ACCOUNT")
		if val != "" {
			return ConfigVar{
				"account",
				val,
				"ENV BIGV_AUTH_ENDPOINT",
			}
		}
		def := config.GetDefault("user")
		def.Name = "account"
		return def
	case "debug-level":
		v := ConfigVar{"debug-level", "0", "CODE"}
		if val := os.Getenv("BIGV_DEBUG_LEVEL"); val != "" {
			v.Value = val
		}
		return v
	case "silent":
		return ConfigVar{"silent", "false", "CODE"}
	case "force":
		return ConfigVar{"force", "false", "CODE"}
	}
	return ConfigVar{"", "", ""}
}

func (config *Config) GetBool(name string) (bool, error) {
	v, err := config.Get(name)
	if err != nil {
		return false, err
	}
	return !(v == "" || v == "false"), nil
}

func (config *Config) read(name string) (ConfigVar, error) {
	path := config.GetPath(name)
	contents, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return config.GetDefault(name), nil
		}

		return config.GetDefault(name), &ConfigReadError{Name: name, Path: path, Err: err}
	}

	return ConfigVar{name, string(contents), "FILE " + path}, nil
}

// Set stores the given key-value pair in config's Memo. This storage does not persist once the program terminates.
func (config *Config) Set(name, value, source string) {
	config.Memo[name] = ConfigVar{name, value, source}
}

// SetPersistent writes a file to the config directory for the given key-value pair.
func (config *Config) SetPersistent(name, value, source string) error {
	path := config.GetPath(name)
	config.Set(name, value, source)
	err := ioutil.WriteFile(path, []byte(value), 0600)
	if err != nil {
		return &ConfigWriteError{Name: name, Path: path, Err: err}
	}
	return nil
}

// Unset removes the named key from both config's Memo and the user's config directory.
func (config *Config) Unset(name string) error {
	delete(config.Memo, name)
	return os.Remove(config.GetPath(name))
}

func (config *Config) Force() bool {
	force, _ := config.GetBool("force")
	return force
}
func (config *Config) Silent() bool {
	silent, _ := config.GetBool("silent")
	return silent
}

func (config *Config) PanelURL() string {
	endpoint := config.EndpointName()
	if strings.EqualFold(endpoint, "uk0.bigv.io") {
		return "https://panel-beta.bytemark.co.uk"
	}
	if strings.EqualFold(endpoint, "int.bigv.io") {
		// worrying leaky code?
		return "https://panel-int.vlan863.bytemark.uk0.bigv.io"
	}
	panel := config.GetIgnoreErr("panel-address")
	if panel == "" {
		panel = "https://your.panel.address"
	}
	return panel
}

func (config *Config) EndpointName() string {
	endpoint := config.GetIgnoreErr("endpoint")
	endpoint = strings.TrimPrefix(endpoint, "https://")
	endpoint = strings.TrimPrefix(endpoint, "http://") // it never hurts to be prepared
	return endpoint
}
<|endoftext|>


<commit_before>package buildkite

import "fmt"
import "strings"

// You can overriden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/buildkite.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "1.0-beta.15"
var buildVersion string = ""

func Version() string {
	// Only output the build version if a pre-release
	if strings.Contains(baseVersion, "beta") || strings.Contains(baseVersion, "alpha") {
		// Use a default buildVersion if one doesn't exist
		actualBuildVersion := buildVersion
		if actualBuildVersion == "" {
			actualBuildVersion = "x"
		}

		return fmt.Sprintf("%s.%s", baseVersion, actualBuildVersion)
	} else {
		return baseVersion
	}
}
<commit_msg>Bumping to 1.0-beta.16<commit_after>package buildkite

import "fmt"
import "strings"

// You can overriden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/buildkite.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "1.0-beta.16"
var buildVersion string = ""

func Version() string {
	// Only output the build version if a pre-release
	if strings.Contains(baseVersion, "beta") || strings.Contains(baseVersion, "alpha") {
		// Use a default buildVersion if one doesn't exist
		actualBuildVersion := buildVersion
		if actualBuildVersion == "" {
			actualBuildVersion = "x"
		}

		return fmt.Sprintf("%s.%s", baseVersion, actualBuildVersion)
	} else {
		return baseVersion
	}
}
<|endoftext|>


<commit_before>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/instancewriter"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators)
	load() error
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(vol Volume) bool

	// Export struct details.
	Name() string
	Config() map[string]string
	Logger() logger.Logger

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	// Mount mounts a storage pool if needed, returns true if we caused a new mount, false if already mounted.
	Mount() (bool, error)

	// Unmount unmounts a storage pool if needed, returns true if unmounted, false if was not mounted.
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)
	Validate(config map[string]string) error
	Update(changedConfig map[string]string) error
	ApplyPatch(name string) error

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error
	DeleteVolume(vol Volume, op *operations.Operation) error
	RenameVolume(vol Volume, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error
	GetVolumeUsage(vol Volume) (int64, error)
	SetVolumeQuota(vol Volume, size string, op *operations.Operation) error
	GetVolumeDiskPath(vol Volume) (string, error)

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(vol Volume, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(vol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType, refresh bool) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error

	// Backup.
	BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error
	CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error)
}
<commit_msg>lxd/storage/drivers/interface: Adds isRemote function<commit_after>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/instancewriter"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRules *Validators)
	load() error
	isRemote() bool
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(vol Volume) bool

	// Export struct details.
	Name() string
	Config() map[string]string
	Logger() logger.Logger

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	// Mount mounts a storage pool if needed, returns true if we caused a new mount, false if already mounted.
	Mount() (bool, error)

	// Unmount unmounts a storage pool if needed, returns true if unmounted, false if was not mounted.
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)
	Validate(config map[string]string) error
	Update(changedConfig map[string]string) error
	ApplyPatch(name string) error

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error
	DeleteVolume(vol Volume, op *operations.Operation) error
	RenameVolume(vol Volume, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error
	GetVolumeUsage(vol Volume) (int64, error)
	SetVolumeQuota(vol Volume, size string, op *operations.Operation) error
	GetVolumeDiskPath(vol Volume) (string, error)

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(vol Volume, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(vol Volume, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error
	RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType, refresh bool) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error

	// Backup.
	BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots bool, op *operations.Operation) error
	CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error)
}
<|endoftext|>


<commit_before>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<commit_msg>lxd/storage/drivers/interface: Adds GetVolumeUsage<commit_after>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, logger logger.Logger, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error
	UpdateVolume(vol Volume, changedConfig map[string]string) error
	GetVolumeUsage(volType VolumeType, volName string) (int64, error)

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)
	RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<|endoftext|>


<commit_before>// Copyright 2011 Julian Phillips.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// #include "utils.h"
// static inline int moduleCheck(PyObject *o) { return PyModule_Check(o); }
// static inline void decref(PyObject *obj) { Py_DECREF(obj); }
import "C"

import (
	"fmt"
	"os"
	"unsafe"
)

type Module struct {
	BaseObject
}

func moduleCheck(obj Object) bool {
	if obj == nil {
		return false
	}
	return C.moduleCheck(c(obj)) != 0
}

func newModule(obj *C.PyObject) *Module {
	return (*Module)(unsafe.Pointer(obj))
}

type Method struct {
	Name string
	Func interface{}
	Doc  string
}

var funcs []interface{}

func saveFunc(f interface{}) *C.PyObject {
	funcs = append(funcs, f)
	return C.PyInt_FromLong(C.long(len(funcs) - 1))
}

//export callWithoutArgs
func callWithoutArgs(self, args unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func() (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	ret, err := f()
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

//export callWithArgs
func callWithArgs(self, args unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func(a *Tuple) (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	a := newTuple((*C.PyObject)(args))
	ret, err := f(a)
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

//export callWithKeywords
func callWithKeywords(self, args, kw unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func(a *Tuple, k *Dict) (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	a := newTuple((*C.PyObject)(args))
	k := newDict((*C.PyObject)(kw))
	ret, err := f(a, k)
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

func Import(name string) (*Module, os.Error) {
	s := C.CString(name)
	defer C.free(unsafe.Pointer(s))

	pyName := C.PyString_FromString(s)
	defer C.decref(pyName)

	obj := C.PyImport_Import(pyName)
	if obj == nil {
		return nil, exception()
	}

	return newModule(obj), nil
}

func InitModule(name string, methods []Method) (*Module, os.Error) {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	m := C.Py_InitModule4(cName, nil, nil, nil, C.PYTHON_API_VERSION)
	if m == nil {
		return nil, exception()
	}

	if len(methods) == 0 {
		return newModule(m), nil
	}

	n := C.PyString_FromString(cName)
	if n == nil {
		return nil, exception()
	}

	d := C.PyModule_GetDict(m)
	if d == nil {
		return nil, exception()
	}

	for _, method := range methods {
		ml := C.newMethodDef()

		ml.ml_name = C.CString(method.Name)
		ml.ml_doc = C.CString(method.Doc)

		switch f := method.Func.(type) {

		case func() (Object, os.Error):
			C.set_call_noargs(&ml.ml_meth)
			ml.ml_flags = C.METH_NOARGS

		case func(a *Tuple) (Object, os.Error):
			C.set_call_args(&ml.ml_meth)
			ml.ml_flags = C.METH_VARARGS

		case func(a *Tuple, k *Dict) (Object, os.Error):
			C.set_call_keywords(&ml.ml_meth)
			ml.ml_flags = C.METH_VARARGS | C.METH_KEYWORDS

		default:
			return nil, fmt.Errorf("InitModule: unknown func type for %s", method.Name)

		}

		v := C.PyCFunction_NewEx(ml, saveFunc(method.Func), n)
		if v == nil {
			return nil, exception()
		}

		if C.PyDict_SetItemString(d, ml.ml_name, v) != 0 {
			return nil, exception()
		}
	}

	return newModule(m), nil
}

func (mod *Module) AddObject(name string, obj Object) os.Error {
	if obj == nil {
		return fmt.Errorf("ValueError: obj == nil!")
	}

	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.PyModule_AddObject(c(mod), cname, c(obj))

	if ret < 0 {
		return exception()
	}

	return nil
}
<commit_msg>Add more PyModule_ functions<commit_after>// Copyright 2011 Julian Phillips.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// #include "utils.h"
// static inline int moduleCheck(PyObject *o) { return PyModule_Check(o); }
// static inline int moduleCheckE(PyObject *o) { return PyModule_CheckExact(o); }
// static inline void decref(PyObject *obj) { Py_DECREF(obj); }
import "C"

import (
	"fmt"
	"os"
	"unsafe"
)

type Module struct {
	BaseObject
}

func moduleCheck(obj Object) bool {
	if obj == nil {
		return false
	}
	return C.moduleCheck(c(obj)) != 0
}

func newModule(obj *C.PyObject) *Module {
	return (*Module)(unsafe.Pointer(obj))
}

type Method struct {
	Name string
	Func interface{}
	Doc  string
}

var funcs []interface{}

func saveFunc(f interface{}) *C.PyObject {
	funcs = append(funcs, f)
	return C.PyInt_FromLong(C.long(len(funcs) - 1))
}

//export callWithoutArgs
func callWithoutArgs(self, args unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func() (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	ret, err := f()
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

//export callWithArgs
func callWithArgs(self, args unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func(a *Tuple) (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	a := newTuple((*C.PyObject)(args))
	ret, err := f(a)
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

//export callWithKeywords
func callWithKeywords(self, args, kw unsafe.Pointer) unsafe.Pointer {
	_idx := (*C.PyObject)(self)
	idx := C.PyInt_AsLong(_idx)
	f, ok := funcs[idx].(func(a *Tuple, k *Dict) (Object, os.Error))
	if !ok {
		fmt.Printf("invalid index: %d\n", idx)
		return nil
	}
	a := newTuple((*C.PyObject)(args))
	k := newDict((*C.PyObject)(kw))
	ret, err := f(a, k)
	if err != nil {
		raise(err)
		return nil
	}
	return unsafe.Pointer(c(ret))
}

func Import(name string) (*Module, os.Error) {
	s := C.CString(name)
	defer C.free(unsafe.Pointer(s))

	pyName := C.PyString_FromString(s)
	defer C.decref(pyName)

	obj := C.PyImport_Import(pyName)
	if obj == nil {
		return nil, exception()
	}

	return newModule(obj), nil
}

func InitModule(name string, methods []Method) (*Module, os.Error) {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	m := C.Py_InitModule4(cName, nil, nil, nil, C.PYTHON_API_VERSION)
	if m == nil {
		return nil, exception()
	}

	if len(methods) == 0 {
		return newModule(m), nil
	}

	n := C.PyString_FromString(cName)
	if n == nil {
		return nil, exception()
	}

	d := C.PyModule_GetDict(m)
	if d == nil {
		return nil, exception()
	}

	for _, method := range methods {
		ml := C.newMethodDef()

		ml.ml_name = C.CString(method.Name)
		ml.ml_doc = C.CString(method.Doc)

		switch f := method.Func.(type) {

		case func() (Object, os.Error):
			C.set_call_noargs(&ml.ml_meth)
			ml.ml_flags = C.METH_NOARGS

		case func(a *Tuple) (Object, os.Error):
			C.set_call_args(&ml.ml_meth)
			ml.ml_flags = C.METH_VARARGS

		case func(a *Tuple, k *Dict) (Object, os.Error):
			C.set_call_keywords(&ml.ml_meth)
			ml.ml_flags = C.METH_VARARGS | C.METH_KEYWORDS

		default:
			return nil, fmt.Errorf("InitModule: unknown func type for %s", method.Name)

		}

		v := C.PyCFunction_NewEx(ml, saveFunc(method.Func), n)
		if v == nil {
			return nil, exception()
		}

		if C.PyDict_SetItemString(d, ml.ml_name, v) != 0 {
			return nil, exception()
		}
	}

	return newModule(m), nil
}

func Module_New(name string) (*Module, os.Error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.PyModule_New(cname)
	if ret == nil {
		return nil, exception()
	}

	return newModule(ret), nil
}

func (mod *Module) CheckExact() bool {
	return C.moduleCheckE(c(mod)) != 0
}

func (mod *Module) Dict() *Dict {
	ret := C.PyModule_GetDict(c(mod))
	return newDict(ret)
}

func (mod *Module) Name() (string, os.Error) {
	ret := C.PyModule_GetName(c(mod))
	if ret == nil {
		return "", exception()
	}
	return C.GoString(ret), nil
}

func (mod *Module) Filename() (string, os.Error) {
	ret := C.PyModule_GetFilename(c(mod))
	if ret == nil {
		return "", exception()
	}
	return C.GoString(ret), nil
}

func (mod *Module) AddObject(name string, obj Object) os.Error {
	if obj == nil {
		return fmt.Errorf("ValueError: obj == nil!")
	}

	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.PyModule_AddObject(c(mod), cname, c(obj))
	if ret < 0 {
		return exception()
	}

	return nil
}

func (mod *Module) AddIntConstant(name string, value int) os.Error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.PyModule_AddIntConstant(c(mod), cname, C.long(value))
	if ret < 0 {
		return exception()
	}

	return nil
}

func (mod *Module) AddStringConstant(name, value string) os.Error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	ret := C.PyModule_AddStringConstant(c(mod), cname, cvalue)
	if ret < 0 {
		return exception()
	}

	return nil
}
<|endoftext|>


<commit_before>// +build darwin

package cmds

import "fmt"

func printWarning() {
	fmt.Println(`WARNING: FUSE is not officially supported on macOS.
You may encounter issues depending on which version of macOS you're on.`)
}
<commit_msg>Rework warning message.<commit_after>// +build darwin

package cmds

import "fmt"

func printWarning() {
	fmt.Println(`WARNING: Mount is supported on macOS versions 1.10.5 and earlier. Mount is implemented using FUSE which isn't supported in macOS 1.11. FUSE on macOS differs from Linux FUSE which causes some issues known issues. For the best experience we recommend using mount on Linux.`)
}
<|endoftext|>


<commit_before>// Package redis provides a Redis Image Cache
package redis

import (
	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/imageserver"
	"strconv"
	"time"
)

// RedisCache represents a Redis Image Cache
//
// It uses Gary Burd's Redis client https://github.com/garyburd/redigo
type RedisCache struct {
	Pool *redigo.Pool

	Expire time.Duration // optional
}

// Get gets an Image from Redis
func (cache *RedisCache) Get(key string, parameters imageserver.Parameters) (*imageserver.Image, error) {
	data, err := cache.getData(key, parameters)
	if err != nil {
		return nil, imageserver.NewCacheMissError(key, cache, err)
	}

	image, err := imageserver.NewImageUnmarshalBinary(data)
	if err != nil {
		return nil, err
	}

	return image, nil
}

func (cache *RedisCache) getData(key string, parameters imageserver.Parameters) ([]byte, error) {
	conn := cache.Pool.Get()
	defer conn.Close()

	return redigo.Bytes(conn.Do("GET", key))
}

// Set sets an Image to Redis
func (cache *RedisCache) Set(key string, image *imageserver.Image, parameters imageserver.Parameters) error {
	data, _ := image.MarshalBinary()

	params := []interface{}{key, data}

	if cache.Expire != 0 {
		params = append(params, "EX", strconv.Itoa(int(cache.Expire.Seconds())))
	}

	conn := cache.Pool.Get()
	defer conn.Close()

	_, err := conn.Do("SET", params...)
	if err != nil {
		return err
	}

	return nil
}

func (cache *RedisCache) Close() error {
	return cache.Pool.Close()
}
<commit_msg>add comment on RedisCache.Close()<commit_after>// Package redis provides a Redis Image Cache
package redis

import (
	redigo "github.com/garyburd/redigo/redis"
	"github.com/pierrre/imageserver"
	"strconv"
	"time"
)

// RedisCache represents a Redis Image Cache
//
// It uses Gary Burd's Redis client https://github.com/garyburd/redigo
type RedisCache struct {
	Pool *redigo.Pool

	Expire time.Duration // optional
}

// Get gets an Image from Redis
func (cache *RedisCache) Get(key string, parameters imageserver.Parameters) (*imageserver.Image, error) {
	data, err := cache.getData(key, parameters)
	if err != nil {
		return nil, imageserver.NewCacheMissError(key, cache, err)
	}

	image, err := imageserver.NewImageUnmarshalBinary(data)
	if err != nil {
		return nil, err
	}

	return image, nil
}

func (cache *RedisCache) getData(key string, parameters imageserver.Parameters) ([]byte, error) {
	conn := cache.Pool.Get()
	defer conn.Close()

	return redigo.Bytes(conn.Do("GET", key))
}

// Set sets an Image to Redis
func (cache *RedisCache) Set(key string, image *imageserver.Image, parameters imageserver.Parameters) error {
	data, _ := image.MarshalBinary()

	params := []interface{}{key, data}

	if cache.Expire != 0 {
		params = append(params, "EX", strconv.Itoa(int(cache.Expire.Seconds())))
	}

	conn := cache.Pool.Get()
	defer conn.Close()

	_, err := conn.Do("SET", params...)
	if err != nil {
		return err
	}

	return nil
}

// Close closes the underlying Redigo pool
func (cache *RedisCache) Close() error {
	return cache.Pool.Close()
}
<|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 healthcheck

import (
	"fmt"
	"net/http"
	"sync/atomic"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/events"
	"k8s.io/klog/v2"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/utils/clock"
)

// ProxierHealthUpdater allows callers to update healthz timestamp only.
type ProxierHealthUpdater interface {
	// QueuedUpdate should be called when the proxier receives a Service or Endpoints
	// event containing information that requires updating service rules.
	QueuedUpdate()

	// Updated should be called when the proxier has successfully updated the service
	// rules to reflect the current state.
	Updated()

	// Run starts the healthz HTTP server and blocks until it exits.
	Run() error
}

var _ ProxierHealthUpdater = &proxierHealthServer{}

// proxierHealthServer returns 200 "OK" by default. It verifies that the delay between
// QueuedUpdate() calls and Updated() calls never exceeds healthTimeout.
type proxierHealthServer struct {
	listener    listener
	httpFactory httpServerFactory
	clock       clock.Clock

	addr          string
	healthTimeout time.Duration
	recorder      events.EventRecorder
	nodeRef       *v1.ObjectReference

	lastUpdated atomic.Value
	lastQueued  atomic.Value
}

// NewProxierHealthServer returns a proxier health http server.
func NewProxierHealthServer(addr string, healthTimeout time.Duration, recorder events.EventRecorder, nodeRef *v1.ObjectReference) ProxierHealthUpdater {
	return newProxierHealthServer(stdNetListener{}, stdHTTPServerFactory{}, clock.RealClock{}, addr, healthTimeout, recorder, nodeRef)
}

func newProxierHealthServer(listener listener, httpServerFactory httpServerFactory, c clock.Clock, addr string, healthTimeout time.Duration, recorder events.EventRecorder, nodeRef *v1.ObjectReference) *proxierHealthServer {
	return &proxierHealthServer{
		listener:      listener,
		httpFactory:   httpServerFactory,
		clock:         c,
		addr:          addr,
		healthTimeout: healthTimeout,
		recorder:      recorder,
		nodeRef:       nodeRef,
	}
}

// Updated updates the lastUpdated timestamp.
func (hs *proxierHealthServer) Updated() {
	hs.lastUpdated.Store(hs.clock.Now())
}

// QueuedUpdate updates the lastQueued timestamp.
func (hs *proxierHealthServer) QueuedUpdate() {
	hs.lastQueued.Store(hs.clock.Now())
}

// Run starts the healthz HTTP server and blocks until it exits.
func (hs *proxierHealthServer) Run() error {
	serveMux := http.NewServeMux()
	serveMux.Handle("/healthz", healthzHandler{hs: hs})
	server := hs.httpFactory.New(hs.addr, serveMux)

	listener, err := hs.listener.Listen(hs.addr)
	if err != nil {
		msg := fmt.Sprintf("failed to start proxier healthz on %s: %v", hs.addr, err)
		// TODO(thockin): move eventing back to caller
		if hs.recorder != nil {
			hs.recorder.Eventf(hs.nodeRef, nil, api.EventTypeWarning, "FailedToStartProxierHealthcheck", "StartKubeProxy", msg)
		}
		return fmt.Errorf("%v", msg)
	}

	klog.V(3).InfoS("Starting healthz HTTP server", "address", hs.addr)

	if err := server.Serve(listener); err != nil {
		return fmt.Errorf("proxier healthz closed with error: %v", err)
	}
	return nil
}

type healthzHandler struct {
	hs *proxierHealthServer
}

func (h healthzHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	var lastQueued, lastUpdated time.Time
	if val := h.hs.lastQueued.Load(); val != nil {
		lastQueued = val.(time.Time)
	}
	if val := h.hs.lastUpdated.Load(); val != nil {
		lastUpdated = val.(time.Time)
	}
	currentTime := h.hs.clock.Now()

	healthy := false
	switch {
	case lastUpdated.IsZero():
		// The proxy is healthy while it's starting up
		// TODO: this makes it useless as a readinessProbe. Consider changing
		// to only become healthy after the proxy is fully synced.
		healthy = true
	case lastUpdated.After(lastQueued):
		// We've processed all updates
		healthy = true
	case currentTime.Sub(lastQueued) < h.hs.healthTimeout:
		// There's an unprocessed update queued, but it's not late yet
		healthy = true
	}

	resp.Header().Set("Content-Type", "application/json")
	resp.Header().Set("X-Content-Type-Options", "nosniff")
	if !healthy {
		resp.WriteHeader(http.StatusServiceUnavailable)
	} else {
		resp.WriteHeader(http.StatusOK)

		// In older releases, the returned "lastUpdated" time indicated the last
		// time the proxier sync loop ran, even if nothing had changed. To
		// preserve compatibility, we use the same semantics: the returned
		// lastUpdated value is "recent" if the server is healthy. The kube-proxy
		// metrics provide more detailed information.
		lastUpdated = currentTime

	}
	fmt.Fprintf(resp, `{"lastUpdated": %q,"currentTime": %q}`, lastUpdated, currentTime)
}
<commit_msg>kube-proxy: fix healthz return unexpect code 200<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 healthcheck

import (
	"fmt"
	"net/http"
	"sync/atomic"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/events"
	"k8s.io/klog/v2"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/utils/clock"
)

// ProxierHealthUpdater allows callers to update healthz timestamp only.
type ProxierHealthUpdater interface {
	// QueuedUpdate should be called when the proxier receives a Service or Endpoints
	// event containing information that requires updating service rules.
	QueuedUpdate()

	// Updated should be called when the proxier has successfully updated the service
	// rules to reflect the current state.
	Updated()

	// Run starts the healthz HTTP server and blocks until it exits.
	Run() error
}

var _ ProxierHealthUpdater = &proxierHealthServer{}
var zeroTime = time.Time{}

// proxierHealthServer returns 200 "OK" by default. It verifies that the delay between
// QueuedUpdate() calls and Updated() calls never exceeds healthTimeout.
type proxierHealthServer struct {
	listener    listener
	httpFactory httpServerFactory
	clock       clock.Clock

	addr          string
	healthTimeout time.Duration
	recorder      events.EventRecorder
	nodeRef       *v1.ObjectReference

	lastUpdated         atomic.Value
	oldestPendingQueued atomic.Value
}

// NewProxierHealthServer returns a proxier health http server.
func NewProxierHealthServer(addr string, healthTimeout time.Duration, recorder events.EventRecorder, nodeRef *v1.ObjectReference) ProxierHealthUpdater {
	return newProxierHealthServer(stdNetListener{}, stdHTTPServerFactory{}, clock.RealClock{}, addr, healthTimeout, recorder, nodeRef)
}

func newProxierHealthServer(listener listener, httpServerFactory httpServerFactory, c clock.Clock, addr string, healthTimeout time.Duration, recorder events.EventRecorder, nodeRef *v1.ObjectReference) *proxierHealthServer {
	return &proxierHealthServer{
		listener:      listener,
		httpFactory:   httpServerFactory,
		clock:         c,
		addr:          addr,
		healthTimeout: healthTimeout,
		recorder:      recorder,
		nodeRef:       nodeRef,
	}
}

// Updated updates the lastUpdated timestamp.
func (hs *proxierHealthServer) Updated() {
	hs.oldestPendingQueued.Store(zeroTime)
	hs.lastUpdated.Store(hs.clock.Now())
}

// QueuedUpdate updates the lastQueued timestamp.
func (hs *proxierHealthServer) QueuedUpdate() {
	// Set oldestPendingQueued only if it's currently zero
	hs.oldestPendingQueued.CompareAndSwap(zeroTime, hs.clock.Now())
}

// Run starts the healthz HTTP server and blocks until it exits.
func (hs *proxierHealthServer) Run() error {
	serveMux := http.NewServeMux()
	serveMux.Handle("/healthz", healthzHandler{hs: hs})
	server := hs.httpFactory.New(hs.addr, serveMux)

	listener, err := hs.listener.Listen(hs.addr)
	if err != nil {
		msg := fmt.Sprintf("failed to start proxier healthz on %s: %v", hs.addr, err)
		// TODO(thockin): move eventing back to caller
		if hs.recorder != nil {
			hs.recorder.Eventf(hs.nodeRef, nil, api.EventTypeWarning, "FailedToStartProxierHealthcheck", "StartKubeProxy", msg)
		}
		return fmt.Errorf("%v", msg)
	}

	klog.V(3).InfoS("Starting healthz HTTP server", "address", hs.addr)

	if err := server.Serve(listener); err != nil {
		return fmt.Errorf("proxier healthz closed with error: %v", err)
	}
	return nil
}

type healthzHandler struct {
	hs *proxierHealthServer
}

func (h healthzHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	var oldestPendingQueued, lastUpdated time.Time
	if val := h.hs.oldestPendingQueued.Load(); val != nil {
		oldestPendingQueued = val.(time.Time)
	}
	if val := h.hs.lastUpdated.Load(); val != nil {
		lastUpdated = val.(time.Time)
	}
	currentTime := h.hs.clock.Now()

	healthy := false
	switch {
	case oldestPendingQueued.IsZero():
		// The proxy is healthy while it's starting up
		// or the proxy is fully synced.
		healthy = true
	case currentTime.Sub(oldestPendingQueued) < h.hs.healthTimeout:
		// There's an unprocessed update queued, but it's not late yet
		healthy = true
	}

	resp.Header().Set("Content-Type", "application/json")
	resp.Header().Set("X-Content-Type-Options", "nosniff")
	if !healthy {
		resp.WriteHeader(http.StatusServiceUnavailable)
	} else {
		resp.WriteHeader(http.StatusOK)
		// In older releases, the returned "lastUpdated" time indicated the last
		// time the proxier sync loop ran, even if nothing had changed. To
		// preserve compatibility, we use the same semantics: the returned
		// lastUpdated value is "recent" if the server is healthy. The kube-proxy
		// metrics provide more detailed information.
		lastUpdated = currentTime
	}
	fmt.Fprintf(resp, `{"lastUpdated": %q,"currentTime": %q}`, lastUpdated, currentTime)
}
<|endoftext|>