<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/stunti/baby-api/api/global"
	"github.com/stunti/baby-api/api/handler"

	"github.com/codegangsta/negroni"
	r "github.com/dancannon/gorethink"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/phyber/negroni-gzip/gzip"
)

func init() {
	var err error
	var tmp_sess *r.Session

	dir := os.Getenv("SOURCE_PATH")

	log.Println("local path: " + dir)

	global.PrivateKey, _ = ioutil.ReadFile(dir + "/keys/app.rsa")
	global.PublicKey, _ = ioutil.ReadFile(dir + "/keys/app.rsa.pub")
	tmp_sess, err = r.Connect(r.ConnectOpts{
		Address: os.Getenv("HOST_IP") + ":28015",
	})
	r.DbCreate("api").Run(tmp_sess)
	if err != nil {
		log.Println("database already exists")
	}
	r.Db("api").TableCreate("user").Run(tmp_sess)
	if err != nil {
		//log.Println("table user already exists")
		log.Println("error: %v", err)
	}
	global.Session, err = r.Connect(r.ConnectOpts{
		Address:  os.Getenv("HOST_IP") + ":28015",
		Database: "api",
	})
}

func AuthMiddleware() negroni.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
			return global.PublicKey, nil
		})
		if err == nil && token.Valid {
			context.Set(r, "tokenUserId", token.Claims["user"])
			next(w, r)
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			fmt.Fprint(w, "GO HOME SON")
		}
	}
}

func main() {
	restrictedRouter := mux.NewRouter()
	openRouter := mux.NewRouter()
	n := negroni.Classic()

	//restricted api access
	restrictedRouter.HandleFunc("/user/profile", handler.UserProfileHandler)
	secure := negroni.New()
	secure.Use(AuthMiddleware())
	secure.UseHandler(restrictedRouter)

	openRouter.HandleFunc("/open/login", handler.UserLoginHandler)

	openRouter.Handle("/user/profile", secure)

	n.Use(gzip.Gzip(gzip.DefaultCompression))
	n.UseHandler(openRouter)
	http.ListenAndServe(":8180", n)
}
<commit_msg>use ACL. simplify auth dispatch process<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/stunti/baby-api/api/acl"
	"github.com/stunti/baby-api/api/global"
	"github.com/stunti/baby-api/api/handler"

	"github.com/codegangsta/negroni"
	r "github.com/dancannon/gorethink"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/phyber/negroni-gzip/gzip"
)

var restrictedRouter *mux.Router

func init() {
	var err error
	var tmp_sess *r.Session

	dir := os.Getenv("SOURCE_PATH")

	log.Println("local path: " + dir)

	global.PrivateKey, _ = ioutil.ReadFile(dir + "/keys/app.rsa")
	global.PublicKey, _ = ioutil.ReadFile(dir + "/keys/app.rsa.pub")

	tmp_sess, err = r.Connect(r.ConnectOpts{
		Address: os.Getenv("HOST_IP") + ":28015",
	})
	r.DbCreate("api").Run(tmp_sess)
	if err != nil {
		log.Println("database already exists")
	}
	r.Db("api").TableCreate("user").Run(tmp_sess)
	if err != nil {
		//log.Println("table user already exists")
		log.Println("error: %v", err)
	}
	global.Session, err = r.Connect(r.ConnectOpts{
		Address:  os.Getenv("HOST_IP") + ":28015",
		Database: "api",
	})
}

func AuthMiddleware() negroni.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

		//get status of matched route
		status := acl.GetAclStatus(r, restrictedRouter)
		log.Println("route status: " + strconv.Itoa(status))
		if status == acl.Open {
			next(w, r)
		} else {

			token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
				return global.PublicKey, nil
			})
			if err == nil && token.Valid {
				log.Println("user status: ", strconv.Itoa(int(token.Claims["role"].(float64))))
				//grab the role from token
				if (int(token.Claims["role"].(float64)) & status) != 0 {
					context.Set(r, "tokenUserId", token.Claims["user"])
					next(w, r)
				} else {
					w.WriteHeader(http.StatusUnauthorized)
					fmt.Fprint(w, "GO HOME SON you need ", status)
				}
			} else {
				w.WriteHeader(http.StatusUnauthorized)
				fmt.Fprint(w, "GO HOME SON")
			}
		}
	}
}

func main() {
	restrictedRouter = mux.NewRouter()

	restrictedRouter.HandleFunc("/v1/user/profile", handler.UserProfileHandler).Name("V1UserProfile")
	restrictedRouter.HandleFunc("/v1/user/login", handler.UserLoginHandler).Name("V1UserLogin")

	n := negroni.Classic()
	n.Use(AuthMiddleware())
	n.Use(gzip.Gzip(gzip.DefaultCompression))
	n.UseHandler(restrictedRouter)

	//lets save the handler for use in AuthMiddleware
	n.Run(":8180")
}
<|endoftext|>


<commit_before>package api

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/auth"
	"github.com/MyHomeworkSpace/api-server/config"
	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/errorlog"

	"github.com/julienschmidt/httprouter"

	"gopkg.in/redis.v5"
)

type routeFunc func(w http.ResponseWriter, r *http.Request, p httprouter.Params, c RouteContext)
type authLevel int

const (
	authLevelNone authLevel = iota
	authLevelLoggedIn
	authLevelAdmin
	authLevelInternal
)

var DB *sql.DB
var RedisClient *redis.Client

type statusResponse struct {
	Status string `json:"status"`
}

type errorResponse struct {
	Status string `json:"status"`
	Error  string `json:"error"`
}

// A RouteContext contains information relevant to the current route
type RouteContext struct {
	LoggedIn bool
	User     *data.User
}

func route(f routeFunc, level authLevel) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		// set up panic handler
		defer func() {
			if e := recover(); e != nil {
				// there was a panic, call it an internal server error
				writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})

				// and complain about it
				errorlog.LogError("unhandled panic - "+r.URL.Path+" - "+fmt.Sprintf("%s", e), nil)
			}
		}()

		// handle cors
		if config.GetCurrent().CORS.Enabled && len(config.GetCurrent().CORS.Origins) > 0 {
			foundOrigin := ""
			for _, origin := range config.GetCurrent().CORS.Origins {
				if origin == r.Header.Get("Origin") {
					foundOrigin = origin
				}
			}

			if foundOrigin == "" {
				foundOrigin = config.GetCurrent().CORS.Origins[0]
			}

			w.Header().Set("Access-Control-Allow-Origin", foundOrigin)
			w.Header().Set("Access-Control-Allow-Credentials", "true")
		}

		// some routes bypass session stuff
		bypassSession := strings.HasPrefix(r.URL.Path, "/application/requestAuth") || strings.HasPrefix(r.URL.Path, "/auth/completeEmailStart")
		if !bypassSession {
			_, err := r.Cookie("session")
			if err != nil {
				// user has no cookie, generate one
				cookie := new(http.Cookie)
				cookie.Name = "session"
				cookie.Path = "/"
				uid, err := auth.GenerateUID()
				if err != nil {
					errorlog.LogError("generating random string for session", err)
					writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
					return
				}
				cookie.Value = uid
				cookie.Expires = time.Now().Add(7 * 24 * time.Hour)
				http.SetCookie(w, cookie)
			}

			bypassCSRF := false

			// check if they have an authorization header
			if r.Header.Get("Authorization") != "" {
				// get the token
				headerParts := strings.Split(r.Header.Get("Authorization"), " ")
				if len(headerParts) == 2 {
					authToken := headerParts[1]

					// look up token
					rows, err := DB.Query("SELECT applications.cors FROM application_authorizations INNER JOIN applications ON application_authorizations.applicationId = applications.id WHERE application_authorizations.token = ?", authToken)
					if err == nil {
						// IMPORTANT: if there's an error with the token, we just continue with the request
						// this is for backwards compatibility with old versions, where the token would always bypass csrf and only be checked when authentication was needed
						// this is ok because if someone is able to add a new header, it should not be in a scenario where csrf would be a useful defense
						// TODO: it would be much cleaner to just fail here if the token is bad. do any applications actually rely on this behavior?

						defer rows.Close()
						if rows.Next() {
							cors := ""
							err = rows.Scan(&cors)

							if err == nil && cors != "" {
								w.Header().Set("Access-Control-Allow-Origin", cors)
								w.Header().Set("Access-Control-Allow-Headers", "authorization")
							}
						}
					}
				}

				// also bypass csrf
				bypassCSRF = true
			}

			// bypass csrf for special internal api (this requires the ip to be localhost so it's still secure)
			if strings.HasPrefix(r.URL.Path, "/internal") {
				bypassCSRF = true
			}

			if !bypassCSRF {
				csrfCookie, err := r.Cookie("csrfToken")
				csrfToken := ""
				hasNoToken := false
				if err != nil {
					// user has no cookie, generate one
					cookie := new(http.Cookie)
					cookie.Name = "csrfToken"
					cookie.Path = "/"
					uid, err := auth.GenerateRandomString(40)
					if err != nil {
						errorlog.LogError("generating random string", err)
						writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
						return
					}
					cookie.Value = uid
					cookie.Expires = time.Now().Add(12 * 4 * 7 * 24 * time.Hour)
					http.SetCookie(w, cookie)

					hasNoToken = true
					csrfToken = cookie.Value

					// let the next if block handle this
				} else {
					csrfToken = csrfCookie.Value
				}

				// bypass csrf token for /auth/csrf
				if strings.HasPrefix(r.URL.Path, "/auth/csrf") {
					// did we just make up a token?
					if hasNoToken {
						// if so, return it
						// auth.go won't know the new token yet
						writeJSON(w, http.StatusOK, csrfResponse{"ok", csrfToken})
						return
					}

					// we didn't, so just pass the request through
					bypassCSRF = true
				}

				if !bypassCSRF && (csrfToken != r.FormValue("csrfToken") || hasNoToken) {
					writeJSON(w, http.StatusBadRequest, errorResponse{"error", "csrfToken_invalid"})
					return
				}
			}
		}

		context := RouteContext{}

		// is this an internal-only thing?
		if level == authLevelInternal {
			// they need to be from a local ip then

			// are they?
			if isInternalRequest(r) {
				// yes, bypass other checks
				f(w, r, p, context)
				return
			}

			// no, bye
			writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "forbidden"})
			return
		}

		// are they logged in?
		sessionUserID := GetSessionUserID(r)

		if sessionUserID != -1 {
			context.LoggedIn = true
			user, err := data.GetUserByID(sessionUserID)
			if err != nil {
				errorlog.LogError("getting user information for request", err)
				writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
				return
			}
			context.User = &user
		}

		if level != authLevelNone {
			// are they logged in?
			if !context.LoggedIn {
				// no, bye
				writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "logged_out"})
				return
			}

			if level == authLevelAdmin {
				// are they an admin?
				if context.User.Level == 0 {
					// no, bye
					writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "forbidden"})
					return
				}
			}
		}

		f(w, r, p, context)
	}
}

func routeStatus(w http.ResponseWriter, r *http.Request, p httprouter.Params, c RouteContext) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Alive"))
}

func writeJSON(w http.ResponseWriter, status int, thing interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(thing)
}

// Init will initialize all available API endpoints
func Init(router *httprouter.Router) {
	router.GET("/status", route(routeStatus, authLevelNone))

	router.GET("/admin/getAllFeedback", route(routeAdminGetAllFeedback, authLevelAdmin))
	router.GET("/admin/getFeedbackScreenshot/:id", route(routeAdminGetFeedbackScreenshot, authLevelAdmin))
	router.GET("/admin/getUserCount", route(routeAdminGetUserCount, authLevelAdmin))
	router.POST("/admin/sendEmail", route(routeAdminSendEmail, authLevelAdmin))
	router.POST("/admin/triggerError", route(routeAdminTriggerError, authLevelAdmin))

	router.POST("/application/completeAuth", route(routeApplicationCompleteAuth, authLevelLoggedIn))
	router.GET("/application/get/:id", route(routeApplicationGet, authLevelLoggedIn))
	router.GET("/application/getAuthorizations", route(routeApplicationGetAuthorizations, authLevelLoggedIn))
	router.GET("/application/requestAuth/:id", route(routeApplicationRequestAuth, authLevelNone))
	router.POST("/application/revokeAuth", route(routeApplicationRevokeAuth, authLevelLoggedIn))
	router.POST("/application/revokeSelf", route(routeApplicationRevokeSelf, authLevelLoggedIn))

	router.POST("/application/manage/create", route(routeApplicationManageCreate, authLevelLoggedIn))
	router.GET("/application/manage/getAll", route(routeApplicationManageGetAll, authLevelLoggedIn))
	router.POST("/application/manage/update", route(routeApplicationManageUpdate, authLevelLoggedIn))
	router.POST("/application/manage/delete", route(routeApplicationManageDelete, authLevelLoggedIn))

	router.POST("/auth/changeEmail", route(routeAuthChangeEmail, authLevelLoggedIn))
	router.POST("/auth/changeName", route(routeAuthChangeName, authLevelLoggedIn))
	router.POST("/auth/changePassword", route(routeAuthChangePassword, authLevelLoggedIn))
	router.POST("/auth/clearMigrateFlag", route(routeAuthClearMigrateFlag, authLevelLoggedIn))
	router.GET("/auth/completeEmailStart/:token", route(routeAuthCompleteEmailStart, authLevelNone))
	router.POST("/auth/completeEmail", route(routeAuthCompleteEmail, authLevelNone))
	router.GET("/auth/context", route(routeAuthContext, authLevelLoggedIn))
	router.POST("/auth/createAccount", route(routeAuthCreateAccount, authLevelNone))
	router.GET("/auth/csrf", route(routeAuthCsrf, authLevelNone))
	router.POST("/auth/login", route(routeAuthLogin, authLevelNone))
	router.GET("/auth/me", route(routeAuthMe, authLevelLoggedIn))
	router.GET("/auth/logout", route(routeAuthLogout, authLevelLoggedIn))
	router.POST("/auth/resetPassword", route(routeAuthResetPassword, authLevelNone))
	router.POST("/auth/resendVerificationEmail", route(routeAuthResendVerificationEmail, authLevelNone))
	router.GET("/auth/session", route(routeAuthSession, authLevelNone))

	router.POST("/auth/2fa/beginEnroll", route(routeAuth2faBeginEnroll, authLevelLoggedIn))
	router.POST("/auth/2fa/completeEnroll", route(routeAuth2faCompleteEnroll, authLevelLoggedIn))
	router.GET("/auth/2fa/status", route(routeAuth2faStatus, authLevelLoggedIn))
	router.POST("/auth/2fa/unenroll", route(routeAuth2faUnenroll, authLevelLoggedIn))

	router.GET("/calendar/getStatus", route(routeCalendarGetStatus, authLevelLoggedIn))
	router.GET("/calendar/getView", route(routeCalendarGetView, authLevelLoggedIn))

	router.GET("/calendar/events/getWeek/:monday", route(routeCalendarEventsGetWeek, authLevelLoggedIn))

	router.POST("/calendar/events/add", route(routeCalendarEventsAdd, authLevelLoggedIn))
	router.POST("/calendar/events/edit", route(routeCalendarEventsEdit, authLevelLoggedIn))
	router.POST("/calendar/events/delete", route(routeCalendarEventsDelete, authLevelLoggedIn))

	router.POST("/calendar/hwEvents/add", route(routeCalendarHWEventsAdd, authLevelLoggedIn))
	router.POST("/calendar/hwEvents/edit", route(routeCalendarHWEventsEdit, authLevelLoggedIn))
	router.POST("/calendar/hwEvents/delete", route(routeCalendarHWEventsDelete, authLevelLoggedIn))

	router.GET("/calendar/eventChanges/get", route(routeCalendarEventChangesGet, authLevelLoggedIn))
	router.POST("/calendar/eventChanges/set", route(routeCalendarEventChangesSet, authLevelLoggedIn))

	router.GET("/classes/get", route(routeClassesGet, authLevelLoggedIn))
	router.GET("/classes/get/:id", route(routeClassesGetID, authLevelLoggedIn))
	router.GET("/classes/hwInfo/:id", route(routeClassesHWInfo, authLevelLoggedIn))
	router.POST("/classes/add", route(routeClassesAdd, authLevelLoggedIn))
	router.POST("/classes/edit", route(routeClassesEdit, authLevelLoggedIn))
	router.POST("/classes/delete", route(routeClassesDelete, authLevelLoggedIn))
	router.POST("/classes/swap", route(routeClassesSwap, authLevelLoggedIn))

	router.POST("/feedback/add", route(routeFeedbackAdd, authLevelLoggedIn))

	router.GET("/homework/get", route(routeHomeworkGet, authLevelLoggedIn))
	router.GET("/homework/getForClass/:classId", route(routeHomeworkGetForClass, authLevelLoggedIn))
	router.GET("/homework/getHWView", route(routeHomeworkGetHWView, authLevelLoggedIn))
	router.GET("/homework/getHWViewSorted", route(routeHomeworkGetHWViewSorted, authLevelLoggedIn))
	router.GET("/homework/get/:id", route(routeHomeworkGetID, authLevelLoggedIn))
	router.GET("/homework/getWeek/:monday", route(routeHomeworkGetWeek, authLevelLoggedIn))
	router.GET("/homework/getPickerSuggestions", route(routeHomeworkGetPickerSuggestions, authLevelLoggedIn))
	router.GET("/homework/search", route(routeHomeworkSearch, authLevelLoggedIn))
	router.POST("/homework/add", route(routeHomeworkAdd, authLevelLoggedIn))
	router.POST("/homework/edit", route(routeHomeworkEdit, authLevelLoggedIn))
	router.POST("/homework/delete", route(routeHomeworkDelete, authLevelLoggedIn))
	router.POST("/homework/markOverdueDone", route(routeHomeworkMarkOverdueDone, authLevelLoggedIn))

	router.POST("/internal/startTask", route(routeInternalStartTask, authLevelInternal))

	router.POST("/notifications/add", route(routeNotificationsAdd, authLevelAdmin))
	router.POST("/notifications/delete", route(routeNotificationsDelete, authLevelAdmin))
	router.GET("/notifications/get", route(routeNotificationsGet, authLevelLoggedIn))

	router.GET("/planner/getWeekInfo/:date", route(routePlannerGetWeekInfo, authLevelLoggedIn))

	router.GET("/prefixes/getDefaultList", route(routePrefixesGetDefaultList, authLevelNone))
	router.GET("/prefixes/getList", route(routePrefixesGetList, authLevelLoggedIn))
	router.POST("/prefixes/delete", route(routePrefixesDelete, authLevelLoggedIn))
	router.POST("/prefixes/add", route(routePrefixesAdd, authLevelLoggedIn))

	router.GET("/prefs/get/:key", route(routePrefsGet, authLevelLoggedIn))
	router.GET("/prefs/getAll", route(routePrefsGetAll, authLevelLoggedIn))
	router.POST("/prefs/set", route(routePrefsSet, authLevelLoggedIn))

	router.POST("/schools/enroll", route(routeSchoolsEnroll, authLevelLoggedIn))
	router.GET("/schools/lookup", route(routeSchoolsLookup, authLevelLoggedIn))
	router.POST("/schools/setEnabled", route(routeSchoolsSetEnabled, authLevelLoggedIn))
	router.POST("/schools/unenroll", route(routeSchoolsUnenroll, authLevelLoggedIn))

	router.GET("/schools/settings/get", route(routeSchoolsSettingsGet, authLevelLoggedIn))
	router.POST("/schools/settings/set", route(routeSchoolsSettingsSet, authLevelLoggedIn))
}
<commit_msg>handle error in writeJSON<commit_after>package api

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/auth"
	"github.com/MyHomeworkSpace/api-server/config"
	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/errorlog"

	"github.com/julienschmidt/httprouter"

	"gopkg.in/redis.v5"
)

type routeFunc func(w http.ResponseWriter, r *http.Request, p httprouter.Params, c RouteContext)
type authLevel int

const (
	authLevelNone authLevel = iota
	authLevelLoggedIn
	authLevelAdmin
	authLevelInternal
)

var DB *sql.DB
var RedisClient *redis.Client

type statusResponse struct {
	Status string `json:"status"`
}

type errorResponse struct {
	Status string `json:"status"`
	Error  string `json:"error"`
}

// A RouteContext contains information relevant to the current route
type RouteContext struct {
	LoggedIn bool
	User     *data.User
}

func route(f routeFunc, level authLevel) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
		// set up panic handler
		defer func() {
			if e := recover(); e != nil {
				// first complain about it
				errorlog.LogError("unhandled panic - "+r.URL.Path+" - "+fmt.Sprintf("%s", e), nil)

				// then tell the client that it's an internal server error
				writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
			}
		}()

		// handle cors
		if config.GetCurrent().CORS.Enabled && len(config.GetCurrent().CORS.Origins) > 0 {
			foundOrigin := ""
			for _, origin := range config.GetCurrent().CORS.Origins {
				if origin == r.Header.Get("Origin") {
					foundOrigin = origin
				}
			}

			if foundOrigin == "" {
				foundOrigin = config.GetCurrent().CORS.Origins[0]
			}

			w.Header().Set("Access-Control-Allow-Origin", foundOrigin)
			w.Header().Set("Access-Control-Allow-Credentials", "true")
		}

		// some routes bypass session stuff
		bypassSession := strings.HasPrefix(r.URL.Path, "/application/requestAuth") || strings.HasPrefix(r.URL.Path, "/auth/completeEmailStart")
		if !bypassSession {
			_, err := r.Cookie("session")
			if err != nil {
				// user has no cookie, generate one
				cookie := new(http.Cookie)
				cookie.Name = "session"
				cookie.Path = "/"
				uid, err := auth.GenerateUID()
				if err != nil {
					errorlog.LogError("generating random string for session", err)
					writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
					return
				}
				cookie.Value = uid
				cookie.Expires = time.Now().Add(7 * 24 * time.Hour)
				http.SetCookie(w, cookie)
			}

			bypassCSRF := false

			// check if they have an authorization header
			if r.Header.Get("Authorization") != "" {
				// get the token
				headerParts := strings.Split(r.Header.Get("Authorization"), " ")
				if len(headerParts) == 2 {
					authToken := headerParts[1]

					// look up token
					rows, err := DB.Query("SELECT applications.cors FROM application_authorizations INNER JOIN applications ON application_authorizations.applicationId = applications.id WHERE application_authorizations.token = ?", authToken)
					if err == nil {
						// IMPORTANT: if there's an error with the token, we just continue with the request
						// this is for backwards compatibility with old versions, where the token would always bypass csrf and only be checked when authentication was needed
						// this is ok because if someone is able to add a new header, it should not be in a scenario where csrf would be a useful defense
						// TODO: it would be much cleaner to just fail here if the token is bad. do any applications actually rely on this behavior?

						defer rows.Close()
						if rows.Next() {
							cors := ""
							err = rows.Scan(&cors)

							if err == nil && cors != "" {
								w.Header().Set("Access-Control-Allow-Origin", cors)
								w.Header().Set("Access-Control-Allow-Headers", "authorization")
							}
						}
					}
				}

				// also bypass csrf
				bypassCSRF = true
			}

			// bypass csrf for special internal api (this requires the ip to be localhost so it's still secure)
			if strings.HasPrefix(r.URL.Path, "/internal") {
				bypassCSRF = true
			}

			if !bypassCSRF {
				csrfCookie, err := r.Cookie("csrfToken")
				csrfToken := ""
				hasNoToken := false
				if err != nil {
					// user has no cookie, generate one
					cookie := new(http.Cookie)
					cookie.Name = "csrfToken"
					cookie.Path = "/"
					uid, err := auth.GenerateRandomString(40)
					if err != nil {
						errorlog.LogError("generating random string", err)
						writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
						return
					}
					cookie.Value = uid
					cookie.Expires = time.Now().Add(12 * 4 * 7 * 24 * time.Hour)
					http.SetCookie(w, cookie)

					hasNoToken = true
					csrfToken = cookie.Value

					// let the next if block handle this
				} else {
					csrfToken = csrfCookie.Value
				}

				// bypass csrf token for /auth/csrf
				if strings.HasPrefix(r.URL.Path, "/auth/csrf") {
					// did we just make up a token?
					if hasNoToken {
						// if so, return it
						// auth.go won't know the new token yet
						writeJSON(w, http.StatusOK, csrfResponse{"ok", csrfToken})
						return
					}

					// we didn't, so just pass the request through
					bypassCSRF = true
				}

				if !bypassCSRF && (csrfToken != r.FormValue("csrfToken") || hasNoToken) {
					writeJSON(w, http.StatusBadRequest, errorResponse{"error", "csrfToken_invalid"})
					return
				}
			}
		}

		context := RouteContext{}

		// is this an internal-only thing?
		if level == authLevelInternal {
			// they need to be from a local ip then

			// are they?
			if isInternalRequest(r) {
				// yes, bypass other checks
				f(w, r, p, context)
				return
			}

			// no, bye
			writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "forbidden"})
			return
		}

		// are they logged in?
		sessionUserID := GetSessionUserID(r)

		if sessionUserID != -1 {
			context.LoggedIn = true
			user, err := data.GetUserByID(sessionUserID)
			if err != nil {
				errorlog.LogError("getting user information for request", err)
				writeJSON(w, http.StatusInternalServerError, errorResponse{"error", "internal_server_error"})
				return
			}
			context.User = &user
		}

		if level != authLevelNone {
			// are they logged in?
			if !context.LoggedIn {
				// no, bye
				writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "logged_out"})
				return
			}

			if level == authLevelAdmin {
				// are they an admin?
				if context.User.Level == 0 {
					// no, bye
					writeJSON(w, http.StatusUnauthorized, errorResponse{"error", "forbidden"})
					return
				}
			}
		}

		f(w, r, p, context)
	}
}

func routeStatus(w http.ResponseWriter, r *http.Request, p httprouter.Params, c RouteContext) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Alive"))
}

func writeJSON(w http.ResponseWriter, status int, thing interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(status)
	err := json.NewEncoder(w).Encode(thing)
	if err != nil {
		panic(err)
	}
}

// Init will initialize all available API endpoints
func Init(router *httprouter.Router) {
	router.GET("/status", route(routeStatus, authLevelNone))

	router.GET("/admin/getAllFeedback", route(routeAdminGetAllFeedback, authLevelAdmin))
	router.GET("/admin/getFeedbackScreenshot/:id", route(routeAdminGetFeedbackScreenshot, authLevelAdmin))
	router.GET("/admin/getUserCount", route(routeAdminGetUserCount, authLevelAdmin))
	router.POST("/admin/sendEmail", route(routeAdminSendEmail, authLevelAdmin))
	router.POST("/admin/triggerError", route(routeAdminTriggerError, authLevelAdmin))

	router.POST("/application/completeAuth", route(routeApplicationCompleteAuth, authLevelLoggedIn))
	router.GET("/application/get/:id", route(routeApplicationGet, authLevelLoggedIn))
	router.GET("/application/getAuthorizations", route(routeApplicationGetAuthorizations, authLevelLoggedIn))
	router.GET("/application/requestAuth/:id", route(routeApplicationRequestAuth, authLevelNone))
	router.POST("/application/revokeAuth", route(routeApplicationRevokeAuth, authLevelLoggedIn))
	router.POST("/application/revokeSelf", route(routeApplicationRevokeSelf, authLevelLoggedIn))

	router.POST("/application/manage/create", route(routeApplicationManageCreate, authLevelLoggedIn))
	router.GET("/application/manage/getAll", route(routeApplicationManageGetAll, authLevelLoggedIn))
	router.POST("/application/manage/update", route(routeApplicationManageUpdate, authLevelLoggedIn))
	router.POST("/application/manage/delete", route(routeApplicationManageDelete, authLevelLoggedIn))

	router.POST("/auth/changeEmail", route(routeAuthChangeEmail, authLevelLoggedIn))
	router.POST("/auth/changeName", route(routeAuthChangeName, authLevelLoggedIn))
	router.POST("/auth/changePassword", route(routeAuthChangePassword, authLevelLoggedIn))
	router.POST("/auth/clearMigrateFlag", route(routeAuthClearMigrateFlag, authLevelLoggedIn))
	router.GET("/auth/completeEmailStart/:token", route(routeAuthCompleteEmailStart, authLevelNone))
	router.POST("/auth/completeEmail", route(routeAuthCompleteEmail, authLevelNone))
	router.GET("/auth/context", route(routeAuthContext, authLevelLoggedIn))
	router.POST("/auth/createAccount", route(routeAuthCreateAccount, authLevelNone))
	router.GET("/auth/csrf", route(routeAuthCsrf, authLevelNone))
	router.POST("/auth/login", route(routeAuthLogin, authLevelNone))
	router.GET("/auth/me", route(routeAuthMe, authLevelLoggedIn))
	router.GET("/auth/logout", route(routeAuthLogout, authLevelLoggedIn))
	router.POST("/auth/resetPassword", route(routeAuthResetPassword, authLevelNone))
	router.POST("/auth/resendVerificationEmail", route(routeAuthResendVerificationEmail, authLevelNone))
	router.GET("/auth/session", route(routeAuthSession, authLevelNone))

	router.POST("/auth/2fa/beginEnroll", route(routeAuth2faBeginEnroll, authLevelLoggedIn))
	router.POST("/auth/2fa/completeEnroll", route(routeAuth2faCompleteEnroll, authLevelLoggedIn))
	router.GET("/auth/2fa/status", route(routeAuth2faStatus, authLevelLoggedIn))
	router.POST("/auth/2fa/unenroll", route(routeAuth2faUnenroll, authLevelLoggedIn))

	router.GET("/calendar/getStatus", route(routeCalendarGetStatus, authLevelLoggedIn))
	router.GET("/calendar/getView", route(routeCalendarGetView, authLevelLoggedIn))

	router.GET("/calendar/events/getWeek/:monday", route(routeCalendarEventsGetWeek, authLevelLoggedIn))

	router.POST("/calendar/events/add", route(routeCalendarEventsAdd, authLevelLoggedIn))
	router.POST("/calendar/events/edit", route(routeCalendarEventsEdit, authLevelLoggedIn))
	router.POST("/calendar/events/delete", route(routeCalendarEventsDelete, authLevelLoggedIn))

	router.POST("/calendar/hwEvents/add", route(routeCalendarHWEventsAdd, authLevelLoggedIn))
	router.POST("/calendar/hwEvents/edit", route(routeCalendarHWEventsEdit, authLevelLoggedIn))
	router.POST("/calendar/hwEvents/delete", route(routeCalendarHWEventsDelete, authLevelLoggedIn))

	router.GET("/calendar/eventChanges/get", route(routeCalendarEventChangesGet, authLevelLoggedIn))
	router.POST("/calendar/eventChanges/set", route(routeCalendarEventChangesSet, authLevelLoggedIn))

	router.GET("/classes/get", route(routeClassesGet, authLevelLoggedIn))
	router.GET("/classes/get/:id", route(routeClassesGetID, authLevelLoggedIn))
	router.GET("/classes/hwInfo/:id", route(routeClassesHWInfo, authLevelLoggedIn))
	router.POST("/classes/add", route(routeClassesAdd, authLevelLoggedIn))
	router.POST("/classes/edit", route(routeClassesEdit, authLevelLoggedIn))
	router.POST("/classes/delete", route(routeClassesDelete, authLevelLoggedIn))
	router.POST("/classes/swap", route(routeClassesSwap, authLevelLoggedIn))

	router.POST("/feedback/add", route(routeFeedbackAdd, authLevelLoggedIn))

	router.GET("/homework/get", route(routeHomeworkGet, authLevelLoggedIn))
	router.GET("/homework/getForClass/:classId", route(routeHomeworkGetForClass, authLevelLoggedIn))
	router.GET("/homework/getHWView", route(routeHomeworkGetHWView, authLevelLoggedIn))
	router.GET("/homework/getHWViewSorted", route(routeHomeworkGetHWViewSorted, authLevelLoggedIn))
	router.GET("/homework/get/:id", route(routeHomeworkGetID, authLevelLoggedIn))
	router.GET("/homework/getWeek/:monday", route(routeHomeworkGetWeek, authLevelLoggedIn))
	router.GET("/homework/getPickerSuggestions", route(routeHomeworkGetPickerSuggestions, authLevelLoggedIn))
	router.GET("/homework/search", route(routeHomeworkSearch, authLevelLoggedIn))
	router.POST("/homework/add", route(routeHomeworkAdd, authLevelLoggedIn))
	router.POST("/homework/edit", route(routeHomeworkEdit, authLevelLoggedIn))
	router.POST("/homework/delete", route(routeHomeworkDelete, authLevelLoggedIn))
	router.POST("/homework/markOverdueDone", route(routeHomeworkMarkOverdueDone, authLevelLoggedIn))

	router.POST("/internal/startTask", route(routeInternalStartTask, authLevelInternal))

	router.POST("/notifications/add", route(routeNotificationsAdd, authLevelAdmin))
	router.POST("/notifications/delete", route(routeNotificationsDelete, authLevelAdmin))
	router.GET("/notifications/get", route(routeNotificationsGet, authLevelLoggedIn))

	router.GET("/planner/getWeekInfo/:date", route(routePlannerGetWeekInfo, authLevelLoggedIn))

	router.GET("/prefixes/getDefaultList", route(routePrefixesGetDefaultList, authLevelNone))
	router.GET("/prefixes/getList", route(routePrefixesGetList, authLevelLoggedIn))
	router.POST("/prefixes/delete", route(routePrefixesDelete, authLevelLoggedIn))
	router.POST("/prefixes/add", route(routePrefixesAdd, authLevelLoggedIn))

	router.GET("/prefs/get/:key", route(routePrefsGet, authLevelLoggedIn))
	router.GET("/prefs/getAll", route(routePrefsGetAll, authLevelLoggedIn))
	router.POST("/prefs/set", route(routePrefsSet, authLevelLoggedIn))

	router.POST("/schools/enroll", route(routeSchoolsEnroll, authLevelLoggedIn))
	router.GET("/schools/lookup", route(routeSchoolsLookup, authLevelLoggedIn))
	router.POST("/schools/setEnabled", route(routeSchoolsSetEnabled, authLevelLoggedIn))
	router.POST("/schools/unenroll", route(routeSchoolsUnenroll, authLevelLoggedIn))

	router.GET("/schools/settings/get", route(routeSchoolsSettingsGet, authLevelLoggedIn))
	router.POST("/schools/settings/set", route(routeSchoolsSettingsSet, authLevelLoggedIn))
}
<|endoftext|>


<commit_before>// Package git implements the Pipe interface getting and validating the
// current git repository state
package git

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"text/template"
	"time"

	"github.com/apex/log"
	"github.com/goreleaser/goreleaser/context"
)

// ErrInvalidVersionFormat is return when the version isnt in a valid format
type ErrInvalidVersionFormat struct {
	version string
}

func (e ErrInvalidVersionFormat) Error() string {
	return fmt.Sprintf("%v is not in a valid version format", e.version)
}

// ErrDirty happens when the repo has uncommitted/unstashed changes
type ErrDirty struct {
	status string
}

func (e ErrDirty) Error() string {
	return fmt.Sprintf("git is currently in a dirty state:\n%v", e.status)
}

// ErrWrongRef happens when the HEAD reference is different from the tag being built
type ErrWrongRef struct {
	commit, tag string
}

func (e ErrWrongRef) Error() string {
	return fmt.Sprintf("git tag %v was not made against commit %v", e.tag, e.commit)
}

// ErrNoTag happens if the underlying git repository doesn't contain any tags
// but no snapshot-release was requested.
var ErrNoTag = fmt.Errorf("git doesn't contain any tags. Either add a tag or use --snapshot")

// Pipe for brew deployment
type Pipe struct{}

// Description of the pipe
func (Pipe) Description() string {
	return "Getting and validating git state"
}

// Run the pipe
func (Pipe) Run(ctx *context.Context) (err error) {
	tag, commit, err := getInfo()
	if err != nil {
		return
	}
	if tag == "" && !ctx.Snapshot {
		return ErrNoTag
	}
	ctx.Git = context.GitInfo{
		CurrentTag: tag,
		Commit:     commit,
	}
	if err = setLog(ctx, tag, commit); err != nil {
		return
	}
	if err = setVersion(ctx, tag, commit); err != nil {
		return
	}
	if !ctx.Validate {
		log.Warn("skipped validations because --skip-validate is set")
		return nil
	}
	return validate(ctx, commit, tag)
}

func setVersion(ctx *context.Context, tag, commit string) (err error) {
	if ctx.Snapshot {
		snapshotName, err := getSnapshotName(ctx, tag, commit)
		if err != nil {
			return fmt.Errorf("failed to generate snapshot name: %s", err.Error())
		}
		ctx.Version = snapshotName
		return nil
	}
	// removes usual `v` prefix
	ctx.Version = strings.TrimPrefix(tag, "v")
	return
}

func setLog(ctx *context.Context, tag, commit string) (err error) {
	if ctx.ReleaseNotes != "" {
		return
	}
	var log string
	if tag == "" {
		log, err = getChangelog(commit)
	} else {
		log, err = getChangelog(tag)
	}
	if err != nil {
		return err
	}
	ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log)
	return nil
}

type snapshotNameData struct {
	Commit    string
	Tag       string
	Timestamp int64
}

func getSnapshotName(ctx *context.Context, tag, commit string) (string, error) {
	tmpl, err := template.New("snapshot").Parse(ctx.Config.Snapshot.NameTemplate)
	var out bytes.Buffer
	if err != nil {
		return "", err
	}
	var data = snapshotNameData{
		Commit:    commit,
		Tag:       tag,
		Timestamp: time.Now().Unix(),
	}
	err = tmpl.Execute(&out, data)
	return out.String(), err
}

func validate(ctx *context.Context, commit, tag string) error {
	out, err := git("status", "-s")
	if strings.TrimSpace(out) != "" || err != nil {
		return ErrDirty{out}
	}
	if ctx.Snapshot {
		return nil
	}
	if !regexp.MustCompile("^[0-9.]+").MatchString(ctx.Version) {
		return ErrInvalidVersionFormat{ctx.Version}
	}
	_, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag)
	if err != nil {
		return ErrWrongRef{commit, tag}
	}
	return nil
}

func getChangelog(tag string) (string, error) {
	prev, err := previous(tag)
	if err != nil {
		return "", err
	}
	if !prev.Tag {
		return gitLog(prev.SHA, tag)
	}
	return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag))
}

func gitLog(refs ...string) (string, error) {
	var args = []string{"log", "--pretty=oneline", "--abbrev-commit"}
	args = append(args, refs...)
	return git(args...)
}

func getInfo() (tag, commit string, err error) {
	tag, err = cleanGit("describe", "--tags", "--abbrev=0")
	if err != nil {
		log.WithError(err).Info("failed to retrieve current tag")
	}
	commit, err = cleanGit("show", "--format='%H'", "HEAD")
	return
}

func previous(tag string) (result ref, err error) {
	result.Tag = true
	result.SHA, err = cleanGit("describe", "--tags", "--abbrev=0", tag+"^")
	if err != nil {
		result.Tag = false
		result.SHA, err = cleanGit("rev-list", "--max-parents=0", "HEAD")
	}
	return
}

type ref struct {
	Tag bool
	SHA string
}
<commit_msg>Use git status --porcelain<commit_after>// Package git implements the Pipe interface getting and validating the
// current git repository state
package git

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"text/template"
	"time"

	"github.com/apex/log"
	"github.com/goreleaser/goreleaser/context"
)

// ErrInvalidVersionFormat is return when the version isnt in a valid format
type ErrInvalidVersionFormat struct {
	version string
}

func (e ErrInvalidVersionFormat) Error() string {
	return fmt.Sprintf("%v is not in a valid version format", e.version)
}

// ErrDirty happens when the repo has uncommitted/unstashed changes
type ErrDirty struct {
	status string
}

func (e ErrDirty) Error() string {
	return fmt.Sprintf("git is currently in a dirty state:\n%v", e.status)
}

// ErrWrongRef happens when the HEAD reference is different from the tag being built
type ErrWrongRef struct {
	commit, tag string
}

func (e ErrWrongRef) Error() string {
	return fmt.Sprintf("git tag %v was not made against commit %v", e.tag, e.commit)
}

// ErrNoTag happens if the underlying git repository doesn't contain any tags
// but no snapshot-release was requested.
var ErrNoTag = fmt.Errorf("git doesn't contain any tags. Either add a tag or use --snapshot")

// Pipe for brew deployment
type Pipe struct{}

// Description of the pipe
func (Pipe) Description() string {
	return "Getting and validating git state"
}

// Run the pipe
func (Pipe) Run(ctx *context.Context) (err error) {
	tag, commit, err := getInfo()
	if err != nil {
		return
	}
	if tag == "" && !ctx.Snapshot {
		return ErrNoTag
	}
	ctx.Git = context.GitInfo{
		CurrentTag: tag,
		Commit:     commit,
	}
	if err = setLog(ctx, tag, commit); err != nil {
		return
	}
	if err = setVersion(ctx, tag, commit); err != nil {
		return
	}
	if !ctx.Validate {
		log.Warn("skipped validations because --skip-validate is set")
		return nil
	}
	return validate(ctx, commit, tag)
}

func setVersion(ctx *context.Context, tag, commit string) (err error) {
	if ctx.Snapshot {
		snapshotName, err := getSnapshotName(ctx, tag, commit)
		if err != nil {
			return fmt.Errorf("failed to generate snapshot name: %s", err.Error())
		}
		ctx.Version = snapshotName
		return nil
	}
	// removes usual `v` prefix
	ctx.Version = strings.TrimPrefix(tag, "v")
	return
}

func setLog(ctx *context.Context, tag, commit string) (err error) {
	if ctx.ReleaseNotes != "" {
		return
	}
	var log string
	if tag == "" {
		log, err = getChangelog(commit)
	} else {
		log, err = getChangelog(tag)
	}
	if err != nil {
		return err
	}
	ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log)
	return nil
}

type snapshotNameData struct {
	Commit    string
	Tag       string
	Timestamp int64
}

func getSnapshotName(ctx *context.Context, tag, commit string) (string, error) {
	tmpl, err := template.New("snapshot").Parse(ctx.Config.Snapshot.NameTemplate)
	var out bytes.Buffer
	if err != nil {
		return "", err
	}
	var data = snapshotNameData{
		Commit:    commit,
		Tag:       tag,
		Timestamp: time.Now().Unix(),
	}
	err = tmpl.Execute(&out, data)
	return out.String(), err
}

func validate(ctx *context.Context, commit, tag string) error {
	out, err := git("status", "--porcelain")
	if strings.TrimSpace(out) != "" || err != nil {
		return ErrDirty{out}
	}
	if ctx.Snapshot {
		return nil
	}
	if !regexp.MustCompile("^[0-9.]+").MatchString(ctx.Version) {
		return ErrInvalidVersionFormat{ctx.Version}
	}
	_, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag)
	if err != nil {
		return ErrWrongRef{commit, tag}
	}
	return nil
}

func getChangelog(tag string) (string, error) {
	prev, err := previous(tag)
	if err != nil {
		return "", err
	}
	if !prev.Tag {
		return gitLog(prev.SHA, tag)
	}
	return gitLog(fmt.Sprintf("%v..%v", prev.SHA, tag))
}

func gitLog(refs ...string) (string, error) {
	var args = []string{"log", "--pretty=oneline", "--abbrev-commit"}
	args = append(args, refs...)
	return git(args...)
}

func getInfo() (tag, commit string, err error) {
	tag, err = cleanGit("describe", "--tags", "--abbrev=0")
	if err != nil {
		log.WithError(err).Info("failed to retrieve current tag")
	}
	commit, err = cleanGit("show", "--format='%H'", "HEAD")
	return
}

func previous(tag string) (result ref, err error) {
	result.Tag = true
	result.SHA, err = cleanGit("describe", "--tags", "--abbrev=0", tag+"^")
	if err != nil {
		result.Tag = false
		result.SHA, err = cleanGit("rev-list", "--max-parents=0", "HEAD")
	}
	return
}

type ref struct {
	Tag bool
	SHA string
}
<|endoftext|>


<commit_before>package adaptor

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	"github.com/compose/transporter/pkg/message"
	"github.com/compose/transporter/pkg/pipe"
)

// File is an adaptor that can be used as a
// source / sink for file's on disk, as well as a sink to stdout.
type File struct {
	uri        string
	pipe       *pipe.Pipe
	path       string
	filehandle *os.File
}

// NewFile returns a File Adaptor
func NewFile(p *pipe.Pipe, path string, extra Config) (StopStartListener, error) {
	var (
		conf FileConfig
		err  error
	)
	if err = extra.Construct(&conf); err != nil {
		return nil, NewError(CRITICAL, path, fmt.Sprintf("Can't configure adaptor (%s)", err.Error()), nil)
	}

	return &File{
		uri:  conf.URI,
		pipe: p,
		path: path,
	}, nil
}

// Start the file adaptor
// TODO: we only know how to listen on stdout for now
func (d *File) Start() (err error) {
	defer func() {
		d.Stop()
	}()

	return d.readFile()
}

// Listen starts the listen loop
func (d *File) Listen() (err error) {
	defer func() {
		d.Stop()
	}()

	if strings.HasPrefix(d.uri, "file://") {
		filename := strings.Replace(d.uri, "file://", "", 1)
		d.filehandle, err = os.Create(filename)
		if err != nil {
			d.pipe.Err <- NewError(CRITICAL, d.path, fmt.Sprintf("Can't open output file (%s)", err.Error()), nil)
			return err
		}
	}

	return d.pipe.Listen(d.dumpMessage, regexp.MustCompile(`.*`))
}

// Stop the adaptor
func (d *File) Stop() error {
	d.pipe.Stop()
	return nil
}

// read each message from the file
func (d *File) readFile() (err error) {
	filename := strings.Replace(d.uri, "file://", "", 1)
	d.filehandle, err = os.Open(filename)
	if err != nil {
		d.pipe.Err <- NewError(CRITICAL, d.path, fmt.Sprintf("Can't open input file (%s)", err.Error()), nil)
		return err
	}

	decoder := json.NewDecoder(d.filehandle)
	for {
		var doc map[string]interface{}
		if err := decoder.Decode(&doc); err == io.EOF {
			break
		} else if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Can't marshal document (%s)", err.Error()), nil)
			return err
		}
		d.pipe.Send(message.NewMsg(message.Insert, doc, fmt.Sprint("file.%s", filename)))
	}
	return nil
}

/*
 * dump each message to the file
 */
func (d *File) dumpMessage(msg *message.Msg) (*message.Msg, error) {
	var line string

	if msg.IsMap() {
		ba, err := json.Marshal(msg.Map())
		if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Can't unmarshal document (%s)", err.Error()), msg.Data)
			return msg, nil
		}
		line = string(ba)
	} else {
		line = fmt.Sprintf("%v", msg.Data)
	}

	if strings.HasPrefix(d.uri, "stdout://") {
		fmt.Println(line)
	} else {
		_, err := fmt.Fprintln(d.filehandle, line)
		if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Error writing to file (%s)", err.Error()), msg.Data)
			return msg, nil
		}
	}

	return msg, nil
}

// FileConfig is used to configure the File Adaptor,
type FileConfig struct {
	// URI pointing to the resource.  We only recognize file:// and stdout:// currently
	URI string `json:"uri" doc:"the uri to connect to, ie stdout://, file:///tmp/output"`
}
<commit_msg>Fixes namespace creation<commit_after>package adaptor

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"

	"github.com/compose/transporter/pkg/message"
	"github.com/compose/transporter/pkg/pipe"
)

// File is an adaptor that can be used as a
// source / sink for file's on disk, as well as a sink to stdout.
type File struct {
	uri        string
	pipe       *pipe.Pipe
	path       string
	filehandle *os.File
}

// NewFile returns a File Adaptor
func NewFile(p *pipe.Pipe, path string, extra Config) (StopStartListener, error) {
	var (
		conf FileConfig
		err  error
	)
	if err = extra.Construct(&conf); err != nil {
		return nil, NewError(CRITICAL, path, fmt.Sprintf("Can't configure adaptor (%s)", err.Error()), nil)
	}

	return &File{
		uri:  conf.URI,
		pipe: p,
		path: path,
	}, nil
}

// Start the file adaptor
// TODO: we only know how to listen on stdout for now
func (d *File) Start() (err error) {
	defer func() {
		d.Stop()
	}()

	return d.readFile()
}

// Listen starts the listen loop
func (d *File) Listen() (err error) {
	defer func() {
		d.Stop()
	}()

	if strings.HasPrefix(d.uri, "file://") {
		filename := strings.Replace(d.uri, "file://", "", 1)
		d.filehandle, err = os.Create(filename)
		if err != nil {
			d.pipe.Err <- NewError(CRITICAL, d.path, fmt.Sprintf("Can't open output file (%s)", err.Error()), nil)
			return err
		}
	}

	return d.pipe.Listen(d.dumpMessage, regexp.MustCompile(`.*`))
}

// Stop the adaptor
func (d *File) Stop() error {
	d.pipe.Stop()
	return nil
}

// read each message from the file
func (d *File) readFile() (err error) {
	filename := strings.Replace(d.uri, "file://", "", 1)
	d.filehandle, err = os.Open(filename)
	if err != nil {
		d.pipe.Err <- NewError(CRITICAL, d.path, fmt.Sprintf("Can't open input file (%s)", err.Error()), nil)
		return err
	}

	decoder := json.NewDecoder(d.filehandle)
	for {
		var doc map[string]interface{}
		if err := decoder.Decode(&doc); err == io.EOF {
			break
		} else if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Can't marshal document (%s)", err.Error()), nil)
			return err
		}
		d.pipe.Send(message.NewMsg(message.Insert, doc, fmt.Sprintf("file.%s", filename)))
	}
	return nil
}

/*
 * dump each message to the file
 */
func (d *File) dumpMessage(msg *message.Msg) (*message.Msg, error) {
	var line string

	if msg.IsMap() {
		ba, err := json.Marshal(msg.Map())
		if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Can't unmarshal document (%s)", err.Error()), msg.Data)
			return msg, nil
		}
		line = string(ba)
	} else {
		line = fmt.Sprintf("%v", msg.Data)
	}

	if strings.HasPrefix(d.uri, "stdout://") {
		fmt.Println(line)
	} else {
		_, err := fmt.Fprintln(d.filehandle, line)
		if err != nil {
			d.pipe.Err <- NewError(ERROR, d.path, fmt.Sprintf("Error writing to file (%s)", err.Error()), msg.Data)
			return msg, nil
		}
	}

	return msg, nil
}

// FileConfig is used to configure the File Adaptor,
type FileConfig struct {
	// URI pointing to the resource.  We only recognize file:// and stdout:// currently
	URI string `json:"uri" doc:"the uri to connect to, ie stdout://, file:///tmp/output"`
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"fmt"

	"github.com/checkr/flagr/pkg/config"
	"github.com/checkr/flagr/pkg/entity"
	"github.com/checkr/flagr/pkg/util"
	"github.com/checkr/flagr/swagger_gen/models"
	"github.com/checkr/flagr/swagger_gen/restapi/operations/evaluation"

	"github.com/davecgh/go-spew/spew"
	"github.com/go-openapi/runtime/middleware"
	"github.com/zhouzhuojie/conditions"
)

// Eval is the Eval interface
type Eval interface {
	PostEvaluation(evaluation.PostEvaluationParams) middleware.Responder
	PostEvaluationBatch(evaluation.PostEvaluationBatchParams) middleware.Responder
}

// NewEval creates a new Eval instance
func NewEval() Eval {
	return &eval{}
}

type eval struct{}

func (e *eval) PostEvaluation(params evaluation.PostEvaluationParams) middleware.Responder {
	evalContext := params.Body
	if evalContext == nil {
		return evaluation.NewPostEvaluationDefault(400).WithPayload(
			ErrorMessage("empty body"))
	}

	evalResult := evalFlag(*evalContext)
	resp := evaluation.NewPostEvaluationOK()
	resp.SetPayload(evalResult)
	return resp
}

func (e *eval) PostEvaluationBatch(params evaluation.PostEvaluationBatchParams) middleware.Responder {
	entities := params.Body.Entities
	flagIDs := params.Body.FlagIds
	results := &models.EvaluationBatchResponse{}

	// TODO make it concurrent
	for _, entity := range entities {
		for _, flagID := range flagIDs {
			evalContext := models.EvalContext{
				EnableDebug:   params.Body.EnableDebug,
				EntityContext: entity.EntityContext,
				EntityID:      entity.EntityID,
				EntityType:    entity.EntityType,
				FlagID:        util.Int64Ptr(flagID),
			}
			evalResult := evalFlag(evalContext)
			results.EvaluationResults = append(results.EvaluationResults, evalResult)
		}
	}

	resp := evaluation.NewPostEvaluationBatchOK()
	resp.SetPayload(results)
	return resp
}

// BlankResult creates a blank result
func BlankResult(f *entity.Flag, evalContext models.EvalContext, msg string) *models.EvalResult {
	flagID := uint(0)
	if f != nil {
		flagID = f.ID
	}
	return &models.EvalResult{
		EvalContext: &evalContext,
		EvalDebugLog: &models.EvalDebugLog{
			Msg:              msg,
			SegmentDebugLogs: nil,
		},
		FlagID:    util.Int64Ptr(int64(flagID)),
		SegmentID: nil,
		VariantID: nil,
		Timestamp: util.StringPtr(util.TimeNow()),
	}
}

var evalFlag = func(evalContext models.EvalContext) *models.EvalResult {
	cache := GetEvalCache()
	flagID := util.SafeUint(evalContext.FlagID)
	f := cache.GetByFlagID(flagID)

	if f == nil {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v not found", flagID))
	}
	if !f.Enabled {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v is not enabled", flagID))
	}

	if len(f.Segments) == 0 {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v has no segments", flagID))
	}

	logs := []*models.SegmentDebugLog{}
	var vID *int64
	var sID *int64

	for _, segment := range f.Segments {
		variantID, log := evalSegment(evalContext, segment)
		if evalContext.EnableDebug {
			logs = append(logs, log)
		}
		if variantID != nil {
			sID = util.Int64Ptr(int64(segment.ID))
			vID = util.Int64Ptr(int64(*variantID))
			break
		}
	}
	evalResult := BlankResult(f, evalContext, "")
	evalResult.EvalDebugLog.SegmentDebugLogs = logs
	evalResult.SegmentID = sID
	evalResult.VariantID = vID
	evalResult.FlagSnapshotID = int64(f.SnapshotID)
	v := f.FlagEvaluation.VariantsMap[util.SafeUint(vID)]
	if v != nil {
		evalResult.VariantAttachment = v.Attachment
		evalResult.VariantKey = util.StringPtr(v.Key)
	}

	logEvalResult(evalResult, f.DataRecordsEnabled)
	return evalResult
}

var logEvalResult = func(r *models.EvalResult, dataRecordsEnabled bool) {
	jsonStr, _ := json.Marshal(struct{ FlagEvalResult *models.EvalResult }{FlagEvalResult: r})
	fmt.Println(string(jsonStr))

	if !config.Config.RecorderEnabled || !dataRecordsEnabled {
		return
	}
	rec := GetDataRecorder()
	rec.AsyncRecord(r)
}

var evalSegment = func(
	evalContext models.EvalContext,
	segment entity.Segment,
) (
	vID *uint, // returns VariantID
	log *models.SegmentDebugLog,
) {
	if len(segment.Constraints) != 0 {
		m, ok := evalContext.EntityContext.(map[string]interface{})
		if !ok {
			log = &models.SegmentDebugLog{
				Msg:       fmt.Sprintf("constraints are present in the segment_id %v, but got invalid entity_context: %s.", segment.ID, spew.Sdump(evalContext.EntityContext)),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}

		expr := segment.SegmentEvaluation.ConditionsExpr
		match, err := conditions.Evaluate(expr, m)
		if err != nil {
			log = &models.SegmentDebugLog{
				Msg:       err.Error(),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}
		if !match {
			log = &models.SegmentDebugLog{
				Msg:       debugConstraintMsg(expr, m),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}
	}

	vID, debugMsg := segment.SegmentEvaluation.DistributionArray.Rollout(
		evalContext.EntityID,
		fmt.Sprint(*evalContext.FlagID), // default use the flagID as salt
		segment.RolloutPercent,
	)

	log = &models.SegmentDebugLog{
		Msg:       "matched all constraints. " + debugMsg,
		SegmentID: int64(segment.ID),
	}

	return vID, log
}

func debugConstraintMsg(expr conditions.Expr, m map[string]interface{}) string {
	return fmt.Sprintf("constraint not match. constraint: %s, entity_context: %+v.", expr, m)
}
<commit_msg>Fix flag snapshot ID in eval<commit_after>package handler

import (
	"encoding/json"
	"fmt"

	"github.com/checkr/flagr/pkg/config"
	"github.com/checkr/flagr/pkg/entity"
	"github.com/checkr/flagr/pkg/util"
	"github.com/checkr/flagr/swagger_gen/models"
	"github.com/checkr/flagr/swagger_gen/restapi/operations/evaluation"

	"github.com/davecgh/go-spew/spew"
	"github.com/go-openapi/runtime/middleware"
	"github.com/zhouzhuojie/conditions"
)

// Eval is the Eval interface
type Eval interface {
	PostEvaluation(evaluation.PostEvaluationParams) middleware.Responder
	PostEvaluationBatch(evaluation.PostEvaluationBatchParams) middleware.Responder
}

// NewEval creates a new Eval instance
func NewEval() Eval {
	return &eval{}
}

type eval struct{}

func (e *eval) PostEvaluation(params evaluation.PostEvaluationParams) middleware.Responder {
	evalContext := params.Body
	if evalContext == nil {
		return evaluation.NewPostEvaluationDefault(400).WithPayload(
			ErrorMessage("empty body"))
	}

	evalResult := evalFlag(*evalContext)
	resp := evaluation.NewPostEvaluationOK()
	resp.SetPayload(evalResult)
	return resp
}

func (e *eval) PostEvaluationBatch(params evaluation.PostEvaluationBatchParams) middleware.Responder {
	entities := params.Body.Entities
	flagIDs := params.Body.FlagIds
	results := &models.EvaluationBatchResponse{}

	// TODO make it concurrent
	for _, entity := range entities {
		for _, flagID := range flagIDs {
			evalContext := models.EvalContext{
				EnableDebug:   params.Body.EnableDebug,
				EntityContext: entity.EntityContext,
				EntityID:      entity.EntityID,
				EntityType:    entity.EntityType,
				FlagID:        util.Int64Ptr(flagID),
			}
			evalResult := evalFlag(evalContext)
			results.EvaluationResults = append(results.EvaluationResults, evalResult)
		}
	}

	resp := evaluation.NewPostEvaluationBatchOK()
	resp.SetPayload(results)
	return resp
}

// BlankResult creates a blank result
func BlankResult(f *entity.Flag, evalContext models.EvalContext, msg string) *models.EvalResult {
	flagID := uint(0)
	flagSnapshotID := uint(0)
	if f != nil {
		flagID = f.ID
		flagSnapshotID = f.SnapshotID
	}
	return &models.EvalResult{
		EvalContext: &evalContext,
		EvalDebugLog: &models.EvalDebugLog{
			Msg:              msg,
			SegmentDebugLogs: nil,
		},
		FlagID:         util.Int64Ptr(int64(flagID)),
		FlagSnapshotID: int64(flagSnapshotID),
		SegmentID:      nil,
		VariantID:      nil,
		Timestamp:      util.StringPtr(util.TimeNow()),
	}
}

var evalFlag = func(evalContext models.EvalContext) *models.EvalResult {
	cache := GetEvalCache()
	flagID := util.SafeUint(evalContext.FlagID)
	f := cache.GetByFlagID(flagID)

	if f == nil {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v not found", flagID))
	}
	if !f.Enabled {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v is not enabled", flagID))
	}

	if len(f.Segments) == 0 {
		return BlankResult(f, evalContext, fmt.Sprintf("flagID %v has no segments", flagID))
	}

	logs := []*models.SegmentDebugLog{}
	var vID *int64
	var sID *int64

	for _, segment := range f.Segments {
		variantID, log := evalSegment(evalContext, segment)
		if evalContext.EnableDebug {
			logs = append(logs, log)
		}
		if variantID != nil {
			sID = util.Int64Ptr(int64(segment.ID))
			vID = util.Int64Ptr(int64(*variantID))
			break
		}
	}
	evalResult := BlankResult(f, evalContext, "")
	evalResult.EvalDebugLog.SegmentDebugLogs = logs
	evalResult.SegmentID = sID
	evalResult.VariantID = vID
	v := f.FlagEvaluation.VariantsMap[util.SafeUint(vID)]
	if v != nil {
		evalResult.VariantAttachment = v.Attachment
		evalResult.VariantKey = util.StringPtr(v.Key)
	}

	logEvalResult(evalResult, f.DataRecordsEnabled)
	return evalResult
}

var logEvalResult = func(r *models.EvalResult, dataRecordsEnabled bool) {
	jsonStr, _ := json.Marshal(struct{ FlagEvalResult *models.EvalResult }{FlagEvalResult: r})
	fmt.Println(string(jsonStr))

	if !config.Config.RecorderEnabled || !dataRecordsEnabled {
		return
	}
	rec := GetDataRecorder()
	rec.AsyncRecord(r)
}

var evalSegment = func(
	evalContext models.EvalContext,
	segment entity.Segment,
) (
	vID *uint, // returns VariantID
	log *models.SegmentDebugLog,
) {
	if len(segment.Constraints) != 0 {
		m, ok := evalContext.EntityContext.(map[string]interface{})
		if !ok {
			log = &models.SegmentDebugLog{
				Msg:       fmt.Sprintf("constraints are present in the segment_id %v, but got invalid entity_context: %s.", segment.ID, spew.Sdump(evalContext.EntityContext)),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}

		expr := segment.SegmentEvaluation.ConditionsExpr
		match, err := conditions.Evaluate(expr, m)
		if err != nil {
			log = &models.SegmentDebugLog{
				Msg:       err.Error(),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}
		if !match {
			log = &models.SegmentDebugLog{
				Msg:       debugConstraintMsg(expr, m),
				SegmentID: int64(segment.ID),
			}
			return nil, log
		}
	}

	vID, debugMsg := segment.SegmentEvaluation.DistributionArray.Rollout(
		evalContext.EntityID,
		fmt.Sprint(*evalContext.FlagID), // default use the flagID as salt
		segment.RolloutPercent,
	)

	log = &models.SegmentDebugLog{
		Msg:       "matched all constraints. " + debugMsg,
		SegmentID: int64(segment.ID),
	}

	return vID, log
}

func debugConstraintMsg(expr conditions.Expr, m map[string]interface{}) string {
	return fmt.Sprintf("constraint not match. constraint: %s, entity_context: %+v.", expr, m)
}
<|endoftext|>


<commit_before>package model

import (
	"fmt"
	"time"
)

const (
	CreationTimeFormat = time.RFC822
)

func TimestampFormat(timestamp time.Time) string {
	age := time.Now().Sub(timestamp)
	ageString := age.String()
	switch {
	case age.Hours() > 24:
		days := uint64(age.Hours()) / 24
		hours := uint64(age.Hours()) % 24
		ageString = fmt.Sprintf("%dd%02dh", days, hours)
	case age.Hours() <= 24 && age.Hours() > 1:
		hours := uint64(age.Hours())
		minutes := uint64(age.Minutes()) % 60
		ageString = fmt.Sprintf("%dh%02dm", hours, minutes)
	case age.Hours() < 1 && age.Minutes() > 1:
		minutes := uint64(age.Minutes())
		seconds := uint64(age.Seconds()) % 60
		ageString = fmt.Sprintf("%dm%02ds", minutes, seconds)
	default:
		seconds := uint64(age.Seconds())
		ageString = fmt.Sprintf("%ds", seconds)
	}
	return ageString
}
<commit_msg>change timestamp formatting<commit_after>package model

import (
	"fmt"
	"time"
)

const (
	CreationTimeFormat = time.RFC822
)

func TimestampFormat(timestamp time.Time) string {
	age := time.Now().Sub(timestamp)
	ageString := age.String()
	switch {
	case age.Hours() > 365*24:
		years := uint64(age.Hours()) / (365 * 24)
		days := uint64(age.Hours()) % (365 * 24)
		ageString = fmt.Sprintf("%dy-%03d", years, days)
	case age.Hours() > 24:
		days := uint64(age.Hours()) / 24
		hours := uint64(age.Hours()) % 24
		ageString = fmt.Sprintf("%03dd-%02dh", days, hours)
	case age.Hours() <= 24 && age.Hours() > 1:
		hours := uint64(age.Hours())
		minutes := uint64(age.Minutes()) % 60
		ageString = fmt.Sprintf("%dh-%02dm", hours, minutes)
	case age.Hours() < 1 && age.Minutes() > 1:
		minutes := uint64(age.Minutes())
		seconds := uint64(age.Seconds()) % 60
		ageString = fmt.Sprintf("%dm-%02ds", minutes, seconds)
	default:
		seconds := uint64(age.Seconds())
		ageString = fmt.Sprintf("%ds", seconds)
	}
	return ageString
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package sql

import (
	"fmt"

	"github.com/cockroachdb/cockroach/pkg/config"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/keys"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/sql/parser"
	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
	"github.com/cockroachdb/cockroach/pkg/util/log"
	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
	"github.com/pkg/errors"
)

// databaseKey implements sqlbase.DescriptorKey.
type databaseKey struct {
	name string
}

func (dk databaseKey) Key() roachpb.Key {
	return sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, dk.name)
}

func (dk databaseKey) Name() string {
	return dk.name
}

// databaseCache holds a cache from database name to database ID. It is
// populated as database IDs are requested and a new cache is created whenever
// the system config changes. As such, no attempt is made to limit its size
// which is naturally limited by the number of database descriptors in the
// system the periodic reset whenever the system config is gossiped.
type databaseCache struct {
	mu        syncutil.Mutex
	databases map[string]sqlbase.ID
}

func (s *databaseCache) getID(name string) sqlbase.ID {
	if s == nil {
		return 0
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.databases[name]
}

func (s *databaseCache) setID(name string, id sqlbase.ID) {
	if s == nil {
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.databases[name] = id
}

func makeDatabaseDesc(p *parser.CreateDatabase) sqlbase.DatabaseDescriptor {
	return sqlbase.DatabaseDescriptor{
		Name:       string(p.Name),
		Privileges: sqlbase.NewDefaultPrivilegeDescriptor(),
	}
}

// getKeysForDatabaseDescriptor retrieves the KV keys corresponding to
// the zone, name and descriptor of a database.
func getKeysForDatabaseDescriptor(
	dbDesc *sqlbase.DatabaseDescriptor,
) (zoneKey roachpb.Key, nameKey roachpb.Key, descKey roachpb.Key) {
	zoneKey = sqlbase.MakeZoneKey(dbDesc.ID)
	nameKey = sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, dbDesc.GetName())
	descKey = sqlbase.MakeDescMetadataKey(dbDesc.ID)
	return
}

// DatabaseAccessor provides helper methods for using SQL database descriptors.
type DatabaseAccessor interface {
	// getDatabaseDesc looks up the database descriptor given its name,
	// returning nil if the descriptor is not found. If you want the "not
	// found" condition to return an error, use mustGetDatabaseDesc() instead.
	getDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error)

	// mustGetDatabaseDesc looks up the database descriptor given its name,
	// returning an error if the descriptor is not found.
	mustGetDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error)

	// getCachedDatabaseDesc looks up the database descriptor from
	// the descriptor cache, given its name.
	// TODO(nvanbenschoten) This method doesn't belong in the interface.
	getCachedDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error)

	// getAllDatabaseDescs looks up and returns all available database
	// descriptors.
	getAllDatabaseDescs() ([]*sqlbase.DatabaseDescriptor, error)

	// getDatabaseID returns the ID of a database given its name.  It
	// uses the descriptor cache if possible, otherwise falls back to KV
	// operations.
	getDatabaseID(name string) (sqlbase.ID, error)

	// createDatabase attempts to create a database with the provided DatabaseDescriptor.
	// Returns true if the database is actually created, false if it already existed,
	// or an error if one was encountered. The ifNotExists flag is used to declare
	// if the "already existed" state should be an error (false) or a no-op (true).
	createDatabase(desc *sqlbase.DatabaseDescriptor, ifNotExists bool) (bool, error)

	// renameDatabase attempts to rename the database with the provided DatabaseDescriptor
	// to a new name. The method will mutate the provided DatabaseDescriptor, updating its
	// name with the new name.
	renameDatabase(oldDesc *sqlbase.DatabaseDescriptor, newName string) error
}

var _ DatabaseAccessor = &planner{}

func (p *planner) getDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	return getDatabaseDesc(p.txn, &p.session.virtualSchemas, name)
}

func getDatabaseDesc(
	txn *client.Txn, vt VirtualTabler, name string,
) (*sqlbase.DatabaseDescriptor, error) {
	if virtual := vt.getVirtualDatabaseDesc(name); virtual != nil {
		return virtual, nil
	}
	desc := &sqlbase.DatabaseDescriptor{}
	found, err := getDescriptor(txn, databaseKey{name}, desc)
	if !found {
		return nil, err
	}
	return desc, err
}

// mustGetDatabaseDesc implements the DatabaseAccessor interface.
func (p *planner) mustGetDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	return MustGetDatabaseDesc(p.txn, &p.session.virtualSchemas, name)
}

// MustGetDatabaseDesc looks up the database descriptor given its name,
// returning an error if the descriptor is not found.
func MustGetDatabaseDesc(
	txn *client.Txn, vt VirtualTabler, name string,
) (*sqlbase.DatabaseDescriptor, error) {
	desc, err := getDatabaseDesc(txn, vt, name)
	if err != nil {
		return nil, err
	}
	if desc == nil {
		return nil, sqlbase.NewUndefinedDatabaseError(name)
	}
	return desc, nil
}

// getCachedDatabaseDesc implements the DatabaseAccessor interface.
func (p *planner) getCachedDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	if name == sqlbase.SystemDB.Name {
		return &sqlbase.SystemDB, nil
	}

	nameKey := databaseKey{name}
	nameVal := p.systemConfig.GetValue(nameKey.Key())
	if nameVal == nil {
		return nil, fmt.Errorf("database %q does not exist in system cache", name)
	}

	id, err := nameVal.GetInt()
	if err != nil {
		return nil, err
	}

	descKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(id))
	descVal := p.systemConfig.GetValue(descKey)
	if descVal == nil {
		return nil, fmt.Errorf("database %q has name entry, but no descriptor in system cache", name)
	}

	desc := &sqlbase.Descriptor{}
	if err := descVal.GetProto(desc); err != nil {
		return nil, err
	}

	database := desc.GetDatabase()
	if database == nil {
		return nil, errors.Errorf("%q is not a database", name)
	}

	return database, database.Validate()
}

// getAllDatabaseDescs implements the DatabaseAccessor interface.
func (p *planner) getAllDatabaseDescs() ([]*sqlbase.DatabaseDescriptor, error) {
	descs, err := p.getAllDescriptors()
	if err != nil {
		return nil, err
	}

	var dbDescs []*sqlbase.DatabaseDescriptor
	for _, desc := range descs {
		if dbDesc, ok := desc.(*sqlbase.DatabaseDescriptor); ok {
			dbDescs = append(dbDescs, dbDesc)
		}
	}
	return dbDescs, nil
}

// getDatabaseID implements the DatabaseAccessor interface.
func (p *planner) getDatabaseID(name string) (sqlbase.ID, error) {
	if virtual := p.session.virtualSchemas.getVirtualDatabaseDesc(name); virtual != nil {
		return virtual.GetID(), nil
	}

	if id := p.databaseCache.getID(name); id != 0 {
		return id, nil
	}

	// Lookup the database in the cache first, falling back to the KV store if it
	// isn't present. The cache might cause the usage of a recently renamed
	// database, but that's a race that could occur anyways.
	desc, err := p.getCachedDatabaseDesc(name)
	if err != nil {
		if log.V(3) {
			log.Infof(p.ctx(), "%v", err)
		}
		var err error
		desc, err = p.mustGetDatabaseDesc(name)
		if err != nil {
			return 0, err
		}
	}

	p.databaseCache.setID(name, desc.ID)
	return desc.ID, nil
}

// createDatabase implements the DatabaseAccessor interface.
func (p *planner) createDatabase(desc *sqlbase.DatabaseDescriptor, ifNotExists bool) (bool, error) {
	if p.session.virtualSchemas.isVirtualDatabase(desc.Name) {
		if ifNotExists {
			// Noop.
			return false, nil
		}
		return false, descriptorAlreadyExistsErr{desc, desc.Name}
	}
	return p.createDescriptor(databaseKey{desc.Name}, desc, ifNotExists)
}

// renameDatabase implements the DatabaseAccessor interface.
func (p *planner) renameDatabase(oldDesc *sqlbase.DatabaseDescriptor, newName string) error {
	onAlreadyExists := func() error {
		return fmt.Errorf("the new database name %q already exists", newName)
	}

	if p.session.virtualSchemas.isVirtualDatabase(newName) {
		return onAlreadyExists()
	}

	oldName := oldDesc.Name
	oldDesc.SetName(newName)
	if err := oldDesc.Validate(); err != nil {
		return err
	}

	oldKey := databaseKey{oldName}.Key()
	newKey := databaseKey{newName}.Key()
	descID := oldDesc.GetID()
	descKey := sqlbase.MakeDescMetadataKey(descID)
	descDesc := sqlbase.WrapDescriptor(oldDesc)

	b := &client.Batch{}
	b.CPut(newKey, descID, nil)
	b.Put(descKey, descDesc)
	b.Del(oldKey)

	if err := p.txn.Run(b); err != nil {
		if _, ok := err.(*roachpb.ConditionFailedError); ok {
			return onAlreadyExists()
		}
		return err
	}

	p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error {
		if err := expectDescriptorID(systemConfig, newKey, descID); err != nil {
			return err
		}
		if err := expectDescriptor(systemConfig, descKey, descDesc); err != nil {
			return err
		}
		return expectDeleted(systemConfig, oldKey)
	})
	return nil
}
<commit_msg>sql: Remove getCachedDatabaseDesc from DatabaseAccessor interface<commit_after>// Copyright 2015 The Cockroach 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.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package sql

import (
	"fmt"

	"github.com/cockroachdb/cockroach/pkg/config"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/keys"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/sql/parser"
	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
	"github.com/cockroachdb/cockroach/pkg/util/log"
	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
	"github.com/pkg/errors"
)

// databaseKey implements sqlbase.DescriptorKey.
type databaseKey struct {
	name string
}

func (dk databaseKey) Key() roachpb.Key {
	return sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, dk.name)
}

func (dk databaseKey) Name() string {
	return dk.name
}

// databaseCache holds a cache from database name to database ID. It is
// populated as database IDs are requested and a new cache is created whenever
// the system config changes. As such, no attempt is made to limit its size
// which is naturally limited by the number of database descriptors in the
// system the periodic reset whenever the system config is gossiped.
type databaseCache struct {
	mu        syncutil.Mutex
	databases map[string]sqlbase.ID
}

func (s *databaseCache) getID(name string) sqlbase.ID {
	if s == nil {
		return 0
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.databases[name]
}

func (s *databaseCache) setID(name string, id sqlbase.ID) {
	if s == nil {
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.databases[name] = id
}

func makeDatabaseDesc(p *parser.CreateDatabase) sqlbase.DatabaseDescriptor {
	return sqlbase.DatabaseDescriptor{
		Name:       string(p.Name),
		Privileges: sqlbase.NewDefaultPrivilegeDescriptor(),
	}
}

// getKeysForDatabaseDescriptor retrieves the KV keys corresponding to
// the zone, name and descriptor of a database.
func getKeysForDatabaseDescriptor(
	dbDesc *sqlbase.DatabaseDescriptor,
) (zoneKey roachpb.Key, nameKey roachpb.Key, descKey roachpb.Key) {
	zoneKey = sqlbase.MakeZoneKey(dbDesc.ID)
	nameKey = sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, dbDesc.GetName())
	descKey = sqlbase.MakeDescMetadataKey(dbDesc.ID)
	return
}

// DatabaseAccessor provides helper methods for using SQL database descriptors.
type DatabaseAccessor interface {
	// getDatabaseDesc looks up the database descriptor given its name,
	// returning nil if the descriptor is not found. If you want the "not
	// found" condition to return an error, use mustGetDatabaseDesc() instead.
	getDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error)

	// mustGetDatabaseDesc looks up the database descriptor given its name,
	// returning an error if the descriptor is not found.
	mustGetDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error)

	// getAllDatabaseDescs looks up and returns all available database
	// descriptors.
	getAllDatabaseDescs() ([]*sqlbase.DatabaseDescriptor, error)

	// getDatabaseID returns the ID of a database given its name.  It
	// uses the descriptor cache if possible, otherwise falls back to KV
	// operations.
	getDatabaseID(name string) (sqlbase.ID, error)

	// createDatabase attempts to create a database with the provided DatabaseDescriptor.
	// Returns true if the database is actually created, false if it already existed,
	// or an error if one was encountered. The ifNotExists flag is used to declare
	// if the "already existed" state should be an error (false) or a no-op (true).
	createDatabase(desc *sqlbase.DatabaseDescriptor, ifNotExists bool) (bool, error)

	// renameDatabase attempts to rename the database with the provided DatabaseDescriptor
	// to a new name. The method will mutate the provided DatabaseDescriptor, updating its
	// name with the new name.
	renameDatabase(oldDesc *sqlbase.DatabaseDescriptor, newName string) error
}

var _ DatabaseAccessor = &planner{}

func (p *planner) getDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	return getDatabaseDesc(p.txn, &p.session.virtualSchemas, name)
}

func getDatabaseDesc(
	txn *client.Txn, vt VirtualTabler, name string,
) (*sqlbase.DatabaseDescriptor, error) {
	if virtual := vt.getVirtualDatabaseDesc(name); virtual != nil {
		return virtual, nil
	}
	desc := &sqlbase.DatabaseDescriptor{}
	found, err := getDescriptor(txn, databaseKey{name}, desc)
	if !found {
		return nil, err
	}
	return desc, err
}

// mustGetDatabaseDesc implements the DatabaseAccessor interface.
func (p *planner) mustGetDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	return MustGetDatabaseDesc(p.txn, &p.session.virtualSchemas, name)
}

// MustGetDatabaseDesc looks up the database descriptor given its name,
// returning an error if the descriptor is not found.
func MustGetDatabaseDesc(
	txn *client.Txn, vt VirtualTabler, name string,
) (*sqlbase.DatabaseDescriptor, error) {
	desc, err := getDatabaseDesc(txn, vt, name)
	if err != nil {
		return nil, err
	}
	if desc == nil {
		return nil, sqlbase.NewUndefinedDatabaseError(name)
	}
	return desc, nil
}

// getCachedDatabaseDesc looks up the database descriptor from the descriptor cache,
// given its name.
func (p *planner) getCachedDatabaseDesc(name string) (*sqlbase.DatabaseDescriptor, error) {
	if name == sqlbase.SystemDB.Name {
		return &sqlbase.SystemDB, nil
	}

	nameKey := databaseKey{name}
	nameVal := p.systemConfig.GetValue(nameKey.Key())
	if nameVal == nil {
		return nil, fmt.Errorf("database %q does not exist in system cache", name)
	}

	id, err := nameVal.GetInt()
	if err != nil {
		return nil, err
	}

	descKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(id))
	descVal := p.systemConfig.GetValue(descKey)
	if descVal == nil {
		return nil, fmt.Errorf("database %q has name entry, but no descriptor in system cache", name)
	}

	desc := &sqlbase.Descriptor{}
	if err := descVal.GetProto(desc); err != nil {
		return nil, err
	}

	database := desc.GetDatabase()
	if database == nil {
		return nil, errors.Errorf("%q is not a database", name)
	}

	return database, database.Validate()
}

// getAllDatabaseDescs implements the DatabaseAccessor interface.
func (p *planner) getAllDatabaseDescs() ([]*sqlbase.DatabaseDescriptor, error) {
	descs, err := p.getAllDescriptors()
	if err != nil {
		return nil, err
	}

	var dbDescs []*sqlbase.DatabaseDescriptor
	for _, desc := range descs {
		if dbDesc, ok := desc.(*sqlbase.DatabaseDescriptor); ok {
			dbDescs = append(dbDescs, dbDesc)
		}
	}
	return dbDescs, nil
}

// getDatabaseID implements the DatabaseAccessor interface.
func (p *planner) getDatabaseID(name string) (sqlbase.ID, error) {
	if virtual := p.session.virtualSchemas.getVirtualDatabaseDesc(name); virtual != nil {
		return virtual.GetID(), nil
	}

	if id := p.databaseCache.getID(name); id != 0 {
		return id, nil
	}

	// Lookup the database in the cache first, falling back to the KV store if it
	// isn't present. The cache might cause the usage of a recently renamed
	// database, but that's a race that could occur anyways.
	desc, err := p.getCachedDatabaseDesc(name)
	if err != nil {
		if log.V(3) {
			log.Infof(p.ctx(), "%v", err)
		}
		var err error
		desc, err = p.mustGetDatabaseDesc(name)
		if err != nil {
			return 0, err
		}
	}

	p.databaseCache.setID(name, desc.ID)
	return desc.ID, nil
}

// createDatabase implements the DatabaseAccessor interface.
func (p *planner) createDatabase(desc *sqlbase.DatabaseDescriptor, ifNotExists bool) (bool, error) {
	if p.session.virtualSchemas.isVirtualDatabase(desc.Name) {
		if ifNotExists {
			// Noop.
			return false, nil
		}
		return false, descriptorAlreadyExistsErr{desc, desc.Name}
	}
	return p.createDescriptor(databaseKey{desc.Name}, desc, ifNotExists)
}

// renameDatabase implements the DatabaseAccessor interface.
func (p *planner) renameDatabase(oldDesc *sqlbase.DatabaseDescriptor, newName string) error {
	onAlreadyExists := func() error {
		return fmt.Errorf("the new database name %q already exists", newName)
	}

	if p.session.virtualSchemas.isVirtualDatabase(newName) {
		return onAlreadyExists()
	}

	oldName := oldDesc.Name
	oldDesc.SetName(newName)
	if err := oldDesc.Validate(); err != nil {
		return err
	}

	oldKey := databaseKey{oldName}.Key()
	newKey := databaseKey{newName}.Key()
	descID := oldDesc.GetID()
	descKey := sqlbase.MakeDescMetadataKey(descID)
	descDesc := sqlbase.WrapDescriptor(oldDesc)

	b := &client.Batch{}
	b.CPut(newKey, descID, nil)
	b.Put(descKey, descDesc)
	b.Del(oldKey)

	if err := p.txn.Run(b); err != nil {
		if _, ok := err.(*roachpb.ConditionFailedError); ok {
			return onAlreadyExists()
		}
		return err
	}

	p.setTestingVerifyMetadata(func(systemConfig config.SystemConfig) error {
		if err := expectDescriptorID(systemConfig, newKey, descID); err != nil {
			return err
		}
		if err := expectDescriptor(systemConfig, descKey, descDesc); err != nil {
			return err
		}
		return expectDeleted(systemConfig, oldKey)
	})
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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 qos

import (
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/sets"
)

var supportedQoSComputeResources = sets.NewString(string(corev1.ResourceCPU), string(corev1.ResourceMemory))

func isSupportedQoSComputeResource(name corev1.ResourceName) bool {
	return supportedQoSComputeResources.Has(string(name))
}

// GetPodQOS returns the QoS class of a pod.
// A pod is besteffort if none of its containers have specified any requests or limits.
// A pod is guaranteed only when requests and limits are specified for all the containers and they are equal.
// A pod is burstable if limits and requests do not match across all containers.
func GetPodQOS(pod *corev1.Pod) corev1.PodQOSClass {
	requests := corev1.ResourceList{}
	limits := corev1.ResourceList{}
	zeroQuantity := resource.MustParse("0")
	isGuaranteed := true
	for _, container := range pod.Spec.Containers {
		// process requests
		for name, quantity := range container.Resources.Requests {
			if !isSupportedQoSComputeResource(name) {
				continue
			}
			if quantity.Cmp(zeroQuantity) == 1 {
				delta := quantity.DeepCopy()
				if _, exists := requests[name]; !exists {
					requests[name] = delta
				} else {
					delta.Add(requests[name])
					requests[name] = delta
				}
			}
		}
		// process limits
		qosLimitsFound := sets.NewString()
		for name, quantity := range container.Resources.Limits {
			if !isSupportedQoSComputeResource(name) {
				continue
			}
			if quantity.Cmp(zeroQuantity) == 1 {
				qosLimitsFound.Insert(string(name))
				delta := quantity.DeepCopy()
				if _, exists := limits[name]; !exists {
					limits[name] = delta
				} else {
					delta.Add(limits[name])
					limits[name] = delta
				}
			}
		}

		if !qosLimitsFound.HasAll(string(corev1.ResourceMemory), string(corev1.ResourceCPU)) {
			isGuaranteed = false
		}
	}
	if len(requests) == 0 && len(limits) == 0 {
		return corev1.PodQOSBestEffort
	}
	// Check is requests match limits for all resources.
	if isGuaranteed {
		for name, req := range requests {
			if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 {
				isGuaranteed = false
				break
			}
		}
	}
	if isGuaranteed &&
		len(requests) == len(limits) {
		return corev1.PodQOSGuaranteed
	}
	return corev1.PodQOSBurstable
}
<commit_msg>fix: include init containers when determining pod QoS that keep consistent with kubelet<commit_after>/*
Copyright 2015 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 qos

import (
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/util/sets"
)

var supportedQoSComputeResources = sets.NewString(string(corev1.ResourceCPU), string(corev1.ResourceMemory))

func isSupportedQoSComputeResource(name corev1.ResourceName) bool {
	return supportedQoSComputeResources.Has(string(name))
}

// GetPodQOS returns the QoS class of a pod.
// A pod is besteffort if none of its containers have specified any requests or limits.
// A pod is guaranteed only when requests and limits are specified for all the containers and they are equal.
// A pod is burstable if limits and requests do not match across all containers.
func GetPodQOS(pod *corev1.Pod) corev1.PodQOSClass {
	requests := corev1.ResourceList{}
	limits := corev1.ResourceList{}
	zeroQuantity := resource.MustParse("0")
	isGuaranteed := true
	allContainers := []corev1.Container{}
	allContainers = append(allContainers, pod.Spec.Containers...)
	allContainers = append(allContainers, pod.Spec.InitContainers...)
	for _, container := range allContainers {
		// process requests
		for name, quantity := range container.Resources.Requests {
			if !isSupportedQoSComputeResource(name) {
				continue
			}
			if quantity.Cmp(zeroQuantity) == 1 {
				delta := quantity.DeepCopy()
				if _, exists := requests[name]; !exists {
					requests[name] = delta
				} else {
					delta.Add(requests[name])
					requests[name] = delta
				}
			}
		}
		// process limits
		qosLimitsFound := sets.NewString()
		for name, quantity := range container.Resources.Limits {
			if !isSupportedQoSComputeResource(name) {
				continue
			}
			if quantity.Cmp(zeroQuantity) == 1 {
				qosLimitsFound.Insert(string(name))
				delta := quantity.DeepCopy()
				if _, exists := limits[name]; !exists {
					limits[name] = delta
				} else {
					delta.Add(limits[name])
					limits[name] = delta
				}
			}
		}

		if !qosLimitsFound.HasAll(string(corev1.ResourceMemory), string(corev1.ResourceCPU)) {
			isGuaranteed = false
		}
	}
	if len(requests) == 0 && len(limits) == 0 {
		return corev1.PodQOSBestEffort
	}
	// Check is requests match limits for all resources.
	if isGuaranteed {
		for name, req := range requests {
			if lim, exists := limits[name]; !exists || lim.Cmp(req) != 0 {
				isGuaranteed = false
				break
			}
		}
	}
	if isGuaranteed &&
		len(requests) == len(limits) {
		return corev1.PodQOSGuaranteed
	}
	return corev1.PodQOSBurstable
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build-tools/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "1"  // major version, always numeric
	gitMinor string = "5+" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v1.5.0-beta.0+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<commit_msg>Kubernetes version v1.5.0-beta.1<commit_after>/*
Copyright 2014 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 version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build-tools/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "1"  // major version, always numeric
	gitMinor string = "5+" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v1.5.0-beta.1+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<|endoftext|>