<commit_before>package aws

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
)

// RestClient is the underlying client for REST-JSON and REST-XML APIs.
type RestClient struct {
	Context    Context
	Client     *http.Client
	Endpoint   string
	APIVersion string
}

// Do sends an HTTP request and returns an HTTP response, following policy
// (e.g. redirects, cookies, auth) as configured on the client.
func (c *RestClient) Do(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", "aws-go")
	if err := c.Context.sign(req); err != nil {
		return nil, err
	}

	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		var err restErrorResponse
		switch resp.Header.Get("Content-Type") {
		case "application/json":
			if err := json.NewDecoder(resp.Body).Decode(&err); err != nil {
				return nil, err
			}
			return nil, err.Err()
		case "application/xml", "text/xml":
			if err := xml.NewDecoder(resp.Body).Decode(&err); err != nil {
				return nil, err
			}
			return nil, err.Err()
		default:
			b, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return nil, err
			}
			return nil, errors.New(string(b))
		}
	}

	return resp, nil
}

type restErrorResponse struct {
	XMLName xml.Name `xml:"ErrorResponse",json:"-"`
	Error   restError
}

type restError struct {
	XMLName    xml.Name `xml:"Error",json:"-"`
	Code       string
	BucketName string
	Message    string
	RequestID  string
	HostID     string
}

func (e restErrorResponse) Err() error {
	return APIError{
		Code:      e.Error.Code,
		Message:   e.Error.Message,
		RequestID: e.Error.RequestID,
		HostID:    e.Error.HostID,
		Specifics: map[string]string{
			"BucketName": e.Error.BucketName,
		},
	}
}
<commit_msg>Supporting both XML REST error response formats<commit_after>package aws

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
)

// RestClient is the underlying client for REST-JSON and REST-XML APIs.
type RestClient struct {
	Context    Context
	Client     *http.Client
	Endpoint   string
	APIVersion string
}

// Do sends an HTTP request and returns an HTTP response, following policy
// (e.g. redirects, cookies, auth) as configured on the client.
func (c *RestClient) Do(req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", "aws-go")
	if err := c.Context.sign(req); err != nil {
		return nil, err
	}

	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		var err restError
		switch resp.Header.Get("Content-Type") {
		case "application/json":
			if err := json.NewDecoder(resp.Body).Decode(&err); err != nil {
				return nil, err
			}
			return nil, err.Err()
		case "application/xml", "text/xml":
			bodyBytes, _ := ioutil.ReadAll(resp.Body)
			resp.Body.Close()
			body := bytes.NewReader(bodyBytes)

			// AWS XML error documents can have a couple of different formats.
			// Try each before returning a decode error.
			var wrappedErr restErrorResponse
			if err := xml.NewDecoder(body).Decode(&wrappedErr); err == nil {
				return nil, wrappedErr.Error.Err()
			}
			body.Seek(0, 0)
			if err := xml.NewDecoder(body).Decode(&err); err != nil {
				return nil, err
			}
			return nil, err.Err()
		default:
			b, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return nil, err
			}
			return nil, errors.New(string(b))
		}
	}

	return resp, nil
}

type restErrorResponse struct {
	XMLName xml.Name `xml:"ErrorResponse",json:"-"`
	Error   restError
}

type restError struct {
	XMLName    xml.Name `xml:"Error",json:"-"`
	Code       string
	BucketName string
	Message    string
	RequestID  string
	HostID     string
}

func (e restError) Err() error {
	return APIError{
		Code:      e.Code,
		Message:   e.Message,
		RequestID: e.RequestID,
		HostID:    e.HostID,
		Specifics: map[string]string{
			"BucketName": e.BucketName,
		},
	}
}
<|endoftext|>


<commit_before>package gonzo

import (
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"
)

/** Handle principal calls */
type AccessLogger struct {
	next ContextHandler
}

// Wrap the request with access logging
func (p AccessLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	accessLog := prepareAccessLog(r)
	defer writeAccessLog(accessLog, start)
	myRW := &responseWriter{ResponseWriter: w}
	p.next(myRW, r)
	accessLog["status"] = myRW.statusCode
}

// We need this to remember the status code
type responseWriter struct {
	http.ResponseWriter
	statusCode int
}

// Write the access-log to stdout
func writeAccessLog(accessLog map[string]interface{}, start time.Time) {

	durationMillis := float64(time.Now().Sub(start).Nanoseconds()) / 1000000.0
	accessLog["durationMillis"] = durationMillis

	if result, err := json.Marshal(accessLog); err == nil {
		log.Println(string(result))
	} else {
		log.Printf("cannot convert access log to json: %v\n", err)
	}
}

// Create a map with relevant access-log data
func prepareAccessLog(req *http.Request) map[string]interface{} {

	result := make(map[string]interface{})

	for k, v := range req.Header {
		result[k] = strings.Join(v, ", ")
	}

	result["requestUri"] = req.RequestURI
	result["log-type"] = "access"
	result["remoteAddress"] = req.RemoteAddr
	result["requestMethod"] = req.Method
	result["originAddress"] = originAddress(req)

	return result
}

// Get last part of the Forwarded-For field
func originAddress(req *http.Request) string {

	xff := req.Header.Get("X-Forwarded-For")

	if xff == "" {
		return req.RemoteAddr
	}

	i := strings.Index(xff, ",")
	if i == -1 {
		return xff
	}

	return xff[:i]
}
<commit_msg>fixed logging after failed merge<commit_after>package gonzo

import (
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"
)

/** Handle principal calls */
type AccessLogger struct {
	next ContextHandler
}

// Wrap the request with access logging
func (p AccessLogger) ServeHTTP(w http.ResponseWriter, r *http.Request, c *Context) {
	start := time.Now()
	accessLog := prepareAccessLog(r)
	defer writeAccessLog(accessLog, start)
	myRW := &responseWriter{ResponseWriter: w}
	p.next(myRW, r, c)
	accessLog["status"] = myRW.statusCode
}

// We need this to remember the status code
type responseWriter struct {
	http.ResponseWriter
	statusCode int
}

// Write the access-log to stdout
func writeAccessLog(accessLog map[string]interface{}, start time.Time) {

	durationMillis := float64(time.Now().Sub(start).Nanoseconds()) / 1000000.0
	accessLog["durationMillis"] = durationMillis

	if result, err := json.Marshal(accessLog); err == nil {
		log.Println(string(result))
	} else {
		log.Printf("cannot convert access log to json: %v\n", err)
	}
}

// Create a map with relevant access-log data
func prepareAccessLog(req *http.Request) map[string]interface{} {

	result := make(map[string]interface{})

	for k, v := range req.Header {
		result[k] = strings.Join(v, ", ")
	}

	result["requestUri"] = req.RequestURI
	result["log-type"] = "access"
	result["remoteAddress"] = req.RemoteAddr
	result["requestMethod"] = req.Method
	result["originAddress"] = originAddress(req)

	return result
}

// Get last part of the Forwarded-For field
func originAddress(req *http.Request) string {

	xff := req.Header.Get("X-Forwarded-For")

	if xff == "" {
		return req.RemoteAddr
	}

	i := strings.Index(xff, ",")
	if i == -1 {
		return xff
	}

	return xff[:i]
}
<|endoftext|>


<commit_before>package api

import (
	r "github.com/pgpst/pgpst/internal/github.com/dancannon/gorethink"
	"github.com/pgpst/pgpst/internal/github.com/gin-gonic/gin"

	"github.com/pgpst/pgpst/pkg/models"
)

func (a *API) getLabelThreads(c *gin.Context) {
	// Token and account from context
	var (
		account = c.MustGet("account").(*models.Account)
		token   = c.MustGet("token").(*models.Token)
	)

	// Resolve the ID from the URL
	id := c.Param("id")

	// Get label from the database
	cursor, err := r.Table("labels").Get(id).Run(a.Rethink)
	if err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}
	var label *models.Label
	if err := cursor.One(&label); err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}

	// Check the ownership and scope
	if label.Owner == account.ID {
		if !models.InScope(token.Scope, []string{"labels:read"}) {
			c.JSON(403, &gin.H{
				"code":  0,
				"error": "Your token has insufficient scope",
			})
			return
		}
	} else {
		if !models.InScope(token.Scope, []string{"admin"}) {
			c.JSON(403, &gin.H{
				"code":  0,
				"error": "Your token has insufficient scope",
			})
			return
		}
	}

	// Get threads from the database
	cursor, err = r.Table("threads").GetAllByIndex("labels", label.ID).Default([]interface{}{}).OrderBy(r.Desc("date_modified")).Run(a.Rethink)
	if err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}
	var threads []*models.Thread
	if err := cursor.All(&threads); err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}

	// Write the response
	c.JSON(200, threads)
	return
}
<commit_msg>Second try<commit_after>package api

import (
	r "github.com/pgpst/pgpst/internal/github.com/dancannon/gorethink"
	"github.com/pgpst/pgpst/internal/github.com/gin-gonic/gin"

	"github.com/pgpst/pgpst/pkg/models"
)

func (a *API) getLabelThreads(c *gin.Context) {
	// Token and account from context
	var (
		account = c.MustGet("account").(*models.Account)
		token   = c.MustGet("token").(*models.Token)
	)

	// Resolve the ID from the URL
	id := c.Param("id")

	// Get label from the database
	cursor, err := r.Table("labels").Get(id).Run(a.Rethink)
	if err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}
	var label *models.Label
	if err := cursor.One(&label); err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}

	// Check the ownership and scope
	if label.Owner == account.ID {
		if !models.InScope(token.Scope, []string{"labels:read"}) {
			c.JSON(403, &gin.H{
				"code":  0,
				"error": "Your token has insufficient scope",
			})
			return
		}
	} else {
		if !models.InScope(token.Scope, []string{"admin"}) {
			c.JSON(403, &gin.H{
				"code":  0,
				"error": "Your token has insufficient scope",
			})
			return
		}
	}

	// Get threads from the database
	cursor, err = r.Table("threads").GetAllByIndex("labels", label.ID).OrderBy(r.Desc("date_modified")).Run(a.Rethink)
	if err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}
	var threads []*models.Thread
	if err := cursor.All(&threads); err != nil {
		c.JSON(500, &gin.H{
			"code":  0,
			"error": err.Error(),
		})
		return
	}
	if threads == nil {
		threads = []*models.Thread{}
	}

	// Write the response
	c.JSON(200, threads)
	return
}
<|endoftext|>


<commit_before>package http

import (
	"encoding/json"
	"fmt"
	"io/fs"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/filebrowser/filebrowser/v2/auth"
	"github.com/filebrowser/filebrowser/v2/settings"
	"github.com/filebrowser/filebrowser/v2/storage"
	"github.com/filebrowser/filebrowser/v2/version"
)

func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
	w.Header().Set("Content-Type", contentType)

	auther, err := d.store.Auth.Get(d.settings.AuthMethod)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	data := map[string]interface{}{
		"Name":            d.settings.Branding.Name,
		"DisableExternal": d.settings.Branding.DisableExternal,
		"BaseURL":         d.server.BaseURL,
		"Version":         version.Version,
		"StaticURL":       path.Join(d.server.BaseURL, "/static"),
		"Signup":          d.settings.Signup,
		"NoAuth":          d.settings.AuthMethod == auth.MethodNoAuth,
		"AuthMethod":      d.settings.AuthMethod,
		"LoginPage":       auther.LoginPage(),
		"CSS":             false,
		"ReCaptcha":       false,
		"Theme":           d.settings.Branding.Theme,
		"EnableThumbs":    d.server.EnableThumbnails,
		"ResizePreview":   d.server.ResizePreview,
		"EnableExec":      d.server.EnableExec,
	}

	if d.settings.Branding.Files != "" {
		fPath := filepath.Join(d.settings.Branding.Files, "custom.css")
		_, err := os.Stat(fPath) //nolint:govet

		if err != nil && !os.IsNotExist(err) {
			log.Printf("couldn't load custom styles: %v", err)
		}

		if err == nil {
			data["CSS"] = true
		}
	}

	if d.settings.AuthMethod == auth.MethodJSONAuth {
		raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet
		if err != nil {
			return http.StatusInternalServerError, err
		}

		auther := raw.(*auth.JSONAuth)

		if auther.ReCaptcha != nil {
			data["ReCaptcha"] = auther.ReCaptcha.Key != "" && auther.ReCaptcha.Secret != ""
			data["ReCaptchaHost"] = auther.ReCaptcha.Host
			data["ReCaptchaKey"] = auther.ReCaptcha.Key
		}
	}

	b, err := json.Marshal(data)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	data["Json"] = string(b)

	fileContents, err := fs.ReadFile(fSys, file)
	if err != nil {
		if err == os.ErrNotExist {
			return http.StatusNotFound, err
		}
		return http.StatusInternalServerError, err
	}
	index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(string(fileContents)))
	err = index.Execute(w, data)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}

func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs fs.FS) (index, static http.Handler) {
	index = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.Method != http.MethodGet {
			return http.StatusNotFound, nil
		}

		w.Header().Set("x-xss-protection", "1; mode=block")
		return handleWithStaticData(w, r, d, assetsFs, "index.html", "text/html; charset=utf-8")
	}, "", store, server)

	static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.Method != http.MethodGet {
			return http.StatusNotFound, nil
		}

		const maxAge = 86400 // 1 day
		w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))

		if d.settings.Branding.Files != "" {
			if strings.HasPrefix(r.URL.Path, "img/") {
				fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
				if _, err := os.Stat(fPath); err == nil {
					http.ServeFile(w, r, fPath)
					return 0, nil
				}
			} else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" {
				http.ServeFile(w, r, filepath.Join(d.settings.Branding.Files, "custom.css"))
				return 0, nil
			}
		}

		if !strings.HasSuffix(r.URL.Path, ".js") {
			http.FileServer(http.FS(assetsFs)).ServeHTTP(w, r)
			return 0, nil
		}

		fileContents, err := fs.ReadFile(assetsFs, r.URL.Path+".gz")
		if err != nil {
			return http.StatusNotFound, err
		}

		w.Header().Set("Content-Encoding", "gzip")
		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")

		if _, err := w.Write(fileContents); err != nil {
			return http.StatusInternalServerError, err
		}

		return 0, nil
	}, "/static/", store, server)

	return index, static
}
<commit_msg>fix: escape quote on index template<commit_after>package http

import (
	"encoding/json"
	"fmt"
	"io/fs"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/filebrowser/filebrowser/v2/auth"
	"github.com/filebrowser/filebrowser/v2/settings"
	"github.com/filebrowser/filebrowser/v2/storage"
	"github.com/filebrowser/filebrowser/v2/version"
)

func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys fs.FS, file, contentType string) (int, error) {
	w.Header().Set("Content-Type", contentType)

	auther, err := d.store.Auth.Get(d.settings.AuthMethod)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	data := map[string]interface{}{
		"Name":            d.settings.Branding.Name,
		"DisableExternal": d.settings.Branding.DisableExternal,
		"BaseURL":         d.server.BaseURL,
		"Version":         version.Version,
		"StaticURL":       path.Join(d.server.BaseURL, "/static"),
		"Signup":          d.settings.Signup,
		"NoAuth":          d.settings.AuthMethod == auth.MethodNoAuth,
		"AuthMethod":      d.settings.AuthMethod,
		"LoginPage":       auther.LoginPage(),
		"CSS":             false,
		"ReCaptcha":       false,
		"Theme":           d.settings.Branding.Theme,
		"EnableThumbs":    d.server.EnableThumbnails,
		"ResizePreview":   d.server.ResizePreview,
		"EnableExec":      d.server.EnableExec,
	}

	if d.settings.Branding.Files != "" {
		fPath := filepath.Join(d.settings.Branding.Files, "custom.css")
		_, err := os.Stat(fPath) //nolint:govet

		if err != nil && !os.IsNotExist(err) {
			log.Printf("couldn't load custom styles: %v", err)
		}

		if err == nil {
			data["CSS"] = true
		}
	}

	if d.settings.AuthMethod == auth.MethodJSONAuth {
		raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:govet
		if err != nil {
			return http.StatusInternalServerError, err
		}

		auther := raw.(*auth.JSONAuth)

		if auther.ReCaptcha != nil {
			data["ReCaptcha"] = auther.ReCaptcha.Key != "" && auther.ReCaptcha.Secret != ""
			data["ReCaptchaHost"] = auther.ReCaptcha.Host
			data["ReCaptchaKey"] = auther.ReCaptcha.Key
		}
	}

	b, err := json.Marshal(data)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	data["Json"] = strings.ReplaceAll(string(b), `'`, `\'`)

	fileContents, err := fs.ReadFile(fSys, file)
	if err != nil {
		if err == os.ErrNotExist {
			return http.StatusNotFound, err
		}
		return http.StatusInternalServerError, err
	}
	index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(string(fileContents)))
	err = index.Execute(w, data)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	return 0, nil
}

func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs fs.FS) (index, static http.Handler) {
	index = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.Method != http.MethodGet {
			return http.StatusNotFound, nil
		}

		w.Header().Set("x-xss-protection", "1; mode=block")
		return handleWithStaticData(w, r, d, assetsFs, "index.html", "text/html; charset=utf-8")
	}, "", store, server)

	static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
		if r.Method != http.MethodGet {
			return http.StatusNotFound, nil
		}

		const maxAge = 86400 // 1 day
		w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))

		if d.settings.Branding.Files != "" {
			if strings.HasPrefix(r.URL.Path, "img/") {
				fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
				if _, err := os.Stat(fPath); err == nil {
					http.ServeFile(w, r, fPath)
					return 0, nil
				}
			} else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" {
				http.ServeFile(w, r, filepath.Join(d.settings.Branding.Files, "custom.css"))
				return 0, nil
			}
		}

		if !strings.HasSuffix(r.URL.Path, ".js") {
			http.FileServer(http.FS(assetsFs)).ServeHTTP(w, r)
			return 0, nil
		}

		fileContents, err := fs.ReadFile(assetsFs, r.URL.Path+".gz")
		if err != nil {
			return http.StatusNotFound, err
		}

		w.Header().Set("Content-Encoding", "gzip")
		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")

		if _, err := w.Write(fileContents); err != nil {
			return http.StatusInternalServerError, err
		}

		return 0, nil
	}, "/static/", store, server)

	return index, static
}
<|endoftext|>


<commit_before>package switchboard

import (
	"fmt"
	"time"

	"github.com/pivotal-golang/lager"
)

type Backends []Backend

func NewBackends(backendIPs []string, backendPorts []uint, healthcheckPorts []uint, healthcheckTimeout time.Duration, logger lager.Logger) Backends {
	healthchecks := newHealthchecks(backendIPs, healthcheckPorts, healthcheckTimeout, logger)
	backends := make([]Backend, len(backendIPs))
	for i, ip := range backendIPs {
		backends[i] = NewBackend(fmt.Sprintf("Backend-%d", i), ip, backendPorts[i], healthchecks[i])
	}
	return backends
}

func newHealthchecks(backendIPs []string, healthcheckPorts []uint, timeout time.Duration, logger lager.Logger) []Healthcheck {
	healthchecks := make([]Healthcheck, len(backendIPs))
	for i, ip := range backendIPs {
		healthchecks[i] = NewHttpHealthCheck(
			ip,
			healthcheckPorts[i],
			timeout,
			logger)
	}
	return healthchecks
}

func (backends Backends) StartHealthchecks() {
	for _, backend := range backends {
		backend.StartHealthcheck()
	}
}

func (backends Backends) CurrentBackend() Backend {
	currentBackendIndex := 0
	return backends[currentBackendIndex]
}
<commit_msg>Introduce Backend interface<commit_after>package switchboard

import (
	"fmt"
	"time"

	"github.com/pivotal-golang/lager"
)

type Backends interface {
	StartHealthchecks()
	CurrentBackend() Backend
}

type backends []Backend

func NewBackends(backendIPs []string, backendPorts []uint, healthcheckPorts []uint, healthcheckTimeout time.Duration, logger lager.Logger) Backends {
	healthchecks := newHealthchecks(backendIPs, healthcheckPorts, healthcheckTimeout, logger)
	backendSlice := make([]Backend, len(backendIPs))
	for i, ip := range backendIPs {
		backendSlice[i] = NewBackend(fmt.Sprintf("Backend-%d", i), ip, backendPorts[i], healthchecks[i])
	}
	return backends(backendSlice)
}

func newHealthchecks(backendIPs []string, healthcheckPorts []uint, timeout time.Duration, logger lager.Logger) []Healthcheck {
	healthchecks := make([]Healthcheck, len(backendIPs))
	for i, ip := range backendIPs {
		healthchecks[i] = NewHttpHealthCheck(
			ip,
			healthcheckPorts[i],
			timeout,
			logger)
	}
	return healthchecks
}

func (backends backends) StartHealthchecks() {
	for _, backend := range backends {
		backend.StartHealthcheck()
	}
}

func (backends backends) CurrentBackend() Backend {
	currentBackendIndex := 0
	return backends[currentBackendIndex]
}
<|endoftext|>


<commit_before>// Copyright 2020 Authors of Cilium
//
// 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 peer

import (
	"fmt"
	"io"

	peerpb "github.com/cilium/cilium/api/v1/peer"
	"github.com/cilium/cilium/pkg/lock"
)

type buffer struct {
	max    int
	buf    []*peerpb.ChangeNotification
	mu     lock.Mutex
	notify chan struct{}
	stop   chan struct{}
}

// newBuffer creates a buffer of ChangeNotification that is safe for concurrent
// use. The buffer is created with an initial size of 0 and is allowed to grow
// until max is reached.
func newBuffer(max int) *buffer {
	return &buffer{
		max:    max,
		notify: nil,
		stop:   make(chan struct{}),
	}
}

// Len returns the number of elements in the buffer.
func (b *buffer) Len() int {
	b.mu.Lock()
	defer b.mu.Unlock()
	return len(b.buf)
}

// Cap returns the capacity of the buffer.
func (b *buffer) Cap() int {
	b.mu.Lock()
	defer b.mu.Unlock()
	return cap(b.buf)
}

// Push appends cn to the end of the buffer. An error is returned if its
// maximum capacity is reached.
func (b *buffer) Push(cn *peerpb.ChangeNotification) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.buf) == b.max {
		return fmt.Errorf("max buffer size=%d reached", b.max)
	}
	b.buf = append(b.buf, cn)
	if b.notify != nil {
		close(b.notify)
		b.notify = nil
	}
	return nil
}

// Pop removes and returns the first element in the buffer. If the buffer is
// empty, Pop blocks until an element is added or Close is called in which case
// io.EOF is returned.
func (b *buffer) Pop() (*peerpb.ChangeNotification, error) {
	b.mu.Lock()
	if len(b.buf) == 0 {
		if b.notify == nil {
			b.notify = make(chan struct{})
		}
		notify := b.notify
		b.mu.Unlock()
		select {
		case <-notify:
			b.mu.Lock()
		case <-b.stop:
			return nil, io.EOF
		}
	}
	cn := b.buf[0]
	b.buf[0] = nil
	b.buf = b.buf[1:]
	b.mu.Unlock()
	return cn, nil
}

// Close closes the buffer and frees the underlying memory.
func (b *buffer) Close() {
	close(b.stop)
	b.mu.Lock()
	b.buf = nil
	b.mu.Unlock()
}
<commit_msg>hubble/peer: fix buf.Pop() crash issue<commit_after>// Copyright 2020 Authors of Cilium
//
// 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 peer

import (
	"fmt"
	"io"

	peerpb "github.com/cilium/cilium/api/v1/peer"
	"github.com/cilium/cilium/pkg/lock"
)

type buffer struct {
	max    int
	buf    []*peerpb.ChangeNotification
	mu     lock.Mutex
	notify chan struct{}
	stop   chan struct{}
}

// newBuffer creates a buffer of ChangeNotification that is safe for concurrent
// use. The buffer is created with an initial size of 0 and is allowed to grow
// until max is reached.
func newBuffer(max int) *buffer {
	return &buffer{
		max:    max,
		notify: nil,
		stop:   make(chan struct{}),
	}
}

// Len returns the number of elements in the buffer.
func (b *buffer) Len() int {
	b.mu.Lock()
	defer b.mu.Unlock()
	return len(b.buf)
}

// Cap returns the capacity of the buffer.
func (b *buffer) Cap() int {
	b.mu.Lock()
	defer b.mu.Unlock()
	return cap(b.buf)
}

// Push appends cn to the end of the buffer. An error is returned if its
// maximum capacity is reached.
func (b *buffer) Push(cn *peerpb.ChangeNotification) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.buf) == b.max {
		return fmt.Errorf("max buffer size=%d reached", b.max)
	}
	b.buf = append(b.buf, cn)
	if b.notify != nil {
		close(b.notify)
		b.notify = nil
	}
	return nil
}

// Pop removes and returns the first element in the buffer. If the buffer is
// empty, Pop blocks until an element is added or Close is called in which case
// io.EOF is returned.
func (b *buffer) Pop() (*peerpb.ChangeNotification, error) {
	b.mu.Lock()
	if len(b.buf) == 0 {
		if b.notify == nil {
			b.notify = make(chan struct{})
		}
		notify := b.notify
		b.mu.Unlock()
		select {
		case <-notify:
			b.mu.Lock()
		case <-b.stop:
			return nil, io.EOF
		}
	}
	//While waiting for b.mu.Lock, b.buffer may be closed.
	select {
	case <-b.stop:
		b.mu.Unlock()
		return nil, io.EOF
	default:
	}
	cn := b.buf[0]
	b.buf[0] = nil
	b.buf = b.buf[1:]
	b.mu.Unlock()
	return cn, nil
}

// Close closes the buffer and frees the underlying memory.
func (b *buffer) Close() {
	close(b.stop)
	b.mu.Lock()
	b.buf = nil
	b.mu.Unlock()
}
<|endoftext|>


<commit_before>// Copyright 2016 CodisLabs. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package fsclient

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"syscall"
	"time"

	"github.com/CodisLabs/codis/pkg/utils/errors"
	"github.com/CodisLabs/codis/pkg/utils/log"
)

var ErrClosedClient = errors.New("use of closed fs client")

type Client struct {
	sync.Mutex

	RootDir  string
	DataDir  string
	TempDir  string
	LockFile string

	lockfd *os.File
	closed bool
}

func New(dir string) (*Client, error) {
	fullpath, err := filepath.Abs(dir)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return &Client{
		RootDir:  fullpath,
		DataDir:  filepath.Join(fullpath, "data"),
		TempDir:  filepath.Join(fullpath, "temp"),
		LockFile: filepath.Join(fullpath, "lock"),
	}, nil
}

func (c *Client) realpath(path string) string {
	return filepath.Join(c.DataDir, filepath.Clean(path))
}

func mkdirAll(dir string) error {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func mkdirFor(file string) error {
	dir, _ := filepath.Split(file)
	if dir != "" {
		return mkdirAll(dir)
	}
	return nil
}

func (c *Client) lockFs() error {
	if c.lockfd != nil {
		return errors.Errorf("lock again")
	}
	if err := mkdirFor(c.LockFile); err != nil {
		return err
	}
	f, err := os.OpenFile(c.LockFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return errors.Trace(err)
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		f.Close()
		return errors.Trace(err)
	}
	var data = map[string]interface{}{
		"pid": os.Getpid(),
		"now": time.Now().String(),
	}
	b, err := json.MarshalIndent(data, "", "    ")
	if err != nil {
		log.WarnErrorf(err, "fsclient - lock encode json failed")
	} else if err := f.Truncate(0); err != nil {
		log.WarnErrorf(err, "fsclient - lock truncate failed")
	} else if _, err := f.Write(b); err != nil {
		log.WarnErrorf(err, "fsclient - lock write failed")
	}
	c.lockfd = f
	return nil
}

func (c *Client) unlockFs() {
	if c.lockfd == nil {
		log.Panicf("unlock again")
	}
	var f = c.lockfd
	if err := f.Truncate(0); err != nil {
		log.WarnErrorf(err, "fsclient - unlock truncate failed")
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.WarnErrorf(err, "fsclient - unlock close failed")
		}
	}()

	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
		log.ErrorErrorf(err, "fsclient - unlock flock failed")
	}
	c.lockfd = nil
}

func (c *Client) Close() error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil
	}
	c.closed = true
	return nil
}

func (c *Client) newTempFile() (*os.File, error) {
	if err := mkdirAll(c.TempDir); err != nil {
		return nil, err
	}
	prefix := fmt.Sprintf("%d.", int(time.Now().Unix()))
	f, err := ioutil.TempFile(c.TempDir, prefix)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return f, nil
}

func (c *Client) writeFile(realpath string, data []byte, noexists bool) error {
	if noexists {
		_, err := os.Stat(realpath)
		if err == nil {
			return errors.Errorf("file already exists")
		} else if !os.IsNotExist(err) {
			return errors.Trace(err)
		}
	}
	if err := mkdirFor(realpath); err != nil {
		return err
	}

	f, err := c.newTempFile()
	if err != nil {
		return err
	}
	defer f.Close()

	var failed = true

	defer func() {
		if !failed {
			return
		}
		os.Remove(f.Name())
	}()

	if _, err := f.Write(data); err != nil {
		return errors.Trace(err)
	}
	if err := f.Close(); err != nil {
		return errors.Trace(err)
	}
	if err := os.Rename(f.Name(), realpath); err != nil {
		return errors.Trace(err)
	}
	failed = false
	return nil
}

func (c *Client) Create(path string, data []byte) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := c.writeFile(c.realpath(path), data, true); err != nil {
		log.Warnf("fsclient - create %s failed", path)
		return err
	} else {
		log.Infof("fsclient - create %s OK", path)
		return nil
	}
}

func (c *Client) Update(path string, data []byte) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := c.writeFile(c.realpath(path), data, false); err != nil {
		log.Warnf("fsclient - update %s failed", path)
		return err
	} else {
		log.Infof("fsclient - update %s OK", path)
		return nil
	}
}

func (c *Client) Delete(path string) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := os.RemoveAll(c.realpath(path)); err != nil {
		log.Warnf("fsclient - delete %s failed", path)
		return errors.Trace(err)
	} else {
		log.Infof("fsclient - delete %s OK", path)
		return nil
	}
}

func (c *Client) Read(path string, must bool) ([]byte, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return nil, err
	}
	defer c.unlockFs()

	realpath := c.realpath(path)
	if !must {
		_, err := os.Stat(realpath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, errors.Trace(err)
			}
			return nil, nil
		}
	}

	b, err := ioutil.ReadFile(realpath)
	if err != nil {
		log.Warnf("fsclient - read %s failed", path)
		return nil, errors.Trace(err)
	}
	return b, nil
}

func (c *Client) List(path string, must bool) ([]string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return nil, err
	}
	defer c.unlockFs()

	realpath := c.realpath(path)
	if !must {
		_, err := os.Stat(realpath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, errors.Trace(err)
			}
			return nil, nil
		}
	}

	f, err := os.Open(realpath)
	if err != nil {
		log.Warnf("fsclient - list %s failed", path)
		return nil, errors.Trace(err)
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		log.Warnf("fsclient - list %s failed", path)
		return nil, errors.Trace(err)
	}
	sort.Strings(names)

	var results []string
	for _, name := range names {
		results = append(results, filepath.Join(path, name))
	}
	return results, nil
}

var ErrNotSupported = errors.New("not supported")

func (c *Client) WatchInOrder(path string) (<-chan struct{}, []string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, nil, errors.Trace(ErrClosedClient)
	}
	return nil, nil, errors.Trace(ErrNotSupported)
}

func (c *Client) CreateEphemeral(path string, data []byte) (<-chan struct{}, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}
	return nil, errors.Trace(ErrNotSupported)
}

func (c *Client) CreateEphemeralInOrder(path string, data []byte) (<-chan struct{}, string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, "", errors.Trace(ErrClosedClient)
	}
	return nil, "", errors.Trace(ErrNotSupported)
}
<commit_msg>fsclient: rename lock to data.lck<commit_after>// Copyright 2016 CodisLabs. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package fsclient

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"syscall"
	"time"

	"github.com/CodisLabs/codis/pkg/utils/errors"
	"github.com/CodisLabs/codis/pkg/utils/log"
)

var ErrClosedClient = errors.New("use of closed fs client")

type Client struct {
	sync.Mutex

	RootDir  string
	DataDir  string
	TempDir  string
	LockFile string

	lockfd *os.File
	closed bool
}

func New(dir string) (*Client, error) {
	fullpath, err := filepath.Abs(dir)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return &Client{
		RootDir:  fullpath,
		DataDir:  filepath.Join(fullpath, "data"),
		TempDir:  filepath.Join(fullpath, "temp"),
		LockFile: filepath.Join(fullpath, "data.lck"),
	}, nil
}

func (c *Client) realpath(path string) string {
	return filepath.Join(c.DataDir, filepath.Clean(path))
}

func mkdirAll(dir string) error {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func mkdirFor(file string) error {
	dir, _ := filepath.Split(file)
	if dir != "" {
		return mkdirAll(dir)
	}
	return nil
}

func (c *Client) lockFs() error {
	if c.lockfd != nil {
		return errors.Errorf("lock again")
	}
	if err := mkdirFor(c.LockFile); err != nil {
		return err
	}
	f, err := os.OpenFile(c.LockFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return errors.Trace(err)
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		f.Close()
		return errors.Trace(err)
	}
	var data = map[string]interface{}{
		"pid": os.Getpid(),
		"now": time.Now().String(),
	}
	b, err := json.MarshalIndent(data, "", "    ")
	if err != nil {
		log.WarnErrorf(err, "fsclient - lock encode json failed")
	} else if err := f.Truncate(0); err != nil {
		log.WarnErrorf(err, "fsclient - lock truncate failed")
	} else if _, err := f.Write(b); err != nil {
		log.WarnErrorf(err, "fsclient - lock write failed")
	}
	c.lockfd = f
	return nil
}

func (c *Client) unlockFs() {
	if c.lockfd == nil {
		log.Panicf("unlock again")
	}
	var f = c.lockfd
	if err := f.Truncate(0); err != nil {
		log.WarnErrorf(err, "fsclient - unlock truncate failed")
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.WarnErrorf(err, "fsclient - unlock close failed")
		}
	}()

	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
		log.ErrorErrorf(err, "fsclient - unlock flock failed")
	}
	c.lockfd = nil
}

func (c *Client) Close() error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil
	}
	c.closed = true
	return nil
}

func (c *Client) newTempFile() (*os.File, error) {
	if err := mkdirAll(c.TempDir); err != nil {
		return nil, err
	}
	prefix := fmt.Sprintf("%d.", int(time.Now().Unix()))
	f, err := ioutil.TempFile(c.TempDir, prefix)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return f, nil
}

func (c *Client) writeFile(realpath string, data []byte, noexists bool) error {
	if noexists {
		_, err := os.Stat(realpath)
		if err == nil {
			return errors.Errorf("file already exists")
		} else if !os.IsNotExist(err) {
			return errors.Trace(err)
		}
	}
	if err := mkdirFor(realpath); err != nil {
		return err
	}

	f, err := c.newTempFile()
	if err != nil {
		return err
	}
	defer f.Close()

	var failed = true

	defer func() {
		if !failed {
			return
		}
		os.Remove(f.Name())
	}()

	if _, err := f.Write(data); err != nil {
		return errors.Trace(err)
	}
	if err := f.Close(); err != nil {
		return errors.Trace(err)
	}
	if err := os.Rename(f.Name(), realpath); err != nil {
		return errors.Trace(err)
	}
	failed = false
	return nil
}

func (c *Client) Create(path string, data []byte) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := c.writeFile(c.realpath(path), data, true); err != nil {
		log.Warnf("fsclient - create %s failed", path)
		return err
	} else {
		log.Infof("fsclient - create %s OK", path)
		return nil
	}
}

func (c *Client) Update(path string, data []byte) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := c.writeFile(c.realpath(path), data, false); err != nil {
		log.Warnf("fsclient - update %s failed", path)
		return err
	} else {
		log.Infof("fsclient - update %s OK", path)
		return nil
	}
}

func (c *Client) Delete(path string) error {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return err
	}
	defer c.unlockFs()

	if err := os.RemoveAll(c.realpath(path)); err != nil {
		log.Warnf("fsclient - delete %s failed", path)
		return errors.Trace(err)
	} else {
		log.Infof("fsclient - delete %s OK", path)
		return nil
	}
}

func (c *Client) Read(path string, must bool) ([]byte, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return nil, err
	}
	defer c.unlockFs()

	realpath := c.realpath(path)
	if !must {
		_, err := os.Stat(realpath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, errors.Trace(err)
			}
			return nil, nil
		}
	}

	b, err := ioutil.ReadFile(realpath)
	if err != nil {
		log.Warnf("fsclient - read %s failed", path)
		return nil, errors.Trace(err)
	}
	return b, nil
}

func (c *Client) List(path string, must bool) ([]string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}

	if err := c.lockFs(); err != nil {
		return nil, err
	}
	defer c.unlockFs()

	realpath := c.realpath(path)
	if !must {
		_, err := os.Stat(realpath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, errors.Trace(err)
			}
			return nil, nil
		}
	}

	f, err := os.Open(realpath)
	if err != nil {
		log.Warnf("fsclient - list %s failed", path)
		return nil, errors.Trace(err)
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		log.Warnf("fsclient - list %s failed", path)
		return nil, errors.Trace(err)
	}
	sort.Strings(names)

	var results []string
	for _, name := range names {
		results = append(results, filepath.Join(path, name))
	}
	return results, nil
}

var ErrNotSupported = errors.New("not supported")

func (c *Client) WatchInOrder(path string) (<-chan struct{}, []string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, nil, errors.Trace(ErrClosedClient)
	}
	return nil, nil, errors.Trace(ErrNotSupported)
}

func (c *Client) CreateEphemeral(path string, data []byte) (<-chan struct{}, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, errors.Trace(ErrClosedClient)
	}
	return nil, errors.Trace(ErrNotSupported)
}

func (c *Client) CreateEphemeralInOrder(path string, data []byte) (<-chan struct{}, string, error) {
	c.Lock()
	defer c.Unlock()
	if c.closed {
		return nil, "", errors.Trace(ErrClosedClient)
	}
	return nil, "", errors.Trace(ErrNotSupported)
}
<|endoftext|>


<commit_before>package plugin

import (
	"github.com/mrosset/via/pkg"
	"testing"
)

func TestPluginBuild(t *testing.T) {
	config := via.GetConfig()
	err := Build(config)
	if err != nil {
		t.Error(err)
	}
}
<commit_msg>test: don't run plugin tests<commit_after>package plugin

import (
	"github.com/mrosset/via/pkg"
	"testing"
)

func testPluginBuild(t *testing.T) {
	config := via.GetConfig()
	err := Build(config)
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package system

import (
	"os/exec"
	"syscall"
)

func Chroot(dir string) error {
	return syscall.Chroot(dir)
}

func Chdir(dir string) error {
	return syscall.Chdir(dir)
}

func Exec(cmd string, args []string, env []string) error {
	return syscall.Exec(cmd, args, env)
}

func Execv(cmd string, args []string, env []string) error {
	name, err := exec.LookPath(cmd)
	if err != nil {
		return err
	}
	return Exec(name, args, env)
}

func Fork() (int, error) {
	syscall.ForkLock.Lock()
	pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
	syscall.ForkLock.Unlock()
	if err != 0 {
		return -1, err
	}
	return int(pid), nil
}

func Mount(source, target, fstype string, flags uintptr, data string) error {
	return syscall.Mount(source, target, fstype, flags, data)
}

func Unmount(target string, flags int) error {
	return syscall.Unmount(target, flags)
}

func Pivotroot(newroot, putold string) error {
	return syscall.PivotRoot(newroot, putold)
}

func Unshare(flags int) error {
	return syscall.Unshare(flags)
}

func Clone(flags uintptr) (int, error) {
	syscall.ForkLock.Lock()
	pid, _, err := syscall.RawSyscall(syscall.SYS_CLONE, flags, 0, 0)
	syscall.ForkLock.Unlock()
	if err != 0 {
		return -1, err
	}
	return int(pid), nil
}

func UsetCloseOnExec(fd uintptr) error {
	if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0); err != 0 {
		return err
	}
	return nil
}

func Setgroups(gids []int) error {
	return syscall.Setgroups(gids)
}

func Setresgid(rgid, egid, sgid int) error {
	return syscall.Setresgid(rgid, egid, sgid)
}

func Setresuid(ruid, euid, suid int) error {
	return syscall.Setresuid(ruid, euid, suid)
}

func Setgid(gid int) error {
	return syscall.Setgid(gid)
}

func Setuid(uid int) error {
	return syscall.Setuid(uid)
}

func Sethostname(name string) error {
	return syscall.Sethostname([]byte(name))
}

func Setsid() (int, error) {
	return syscall.Setsid()
}

func Ioctl(fd uintptr, flag, data uintptr) error {
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
		return err
	}
	return nil
}

func Closefd(fd uintptr) error {
	return syscall.Close(int(fd))
}

func Dup2(fd1, fd2 uintptr) error {
	return syscall.Dup2(int(fd1), int(fd2))
}

func Mknod(path string, mode uint32, dev int) error {
	return syscall.Mknod(path, mode, dev)
}

func ParentDeathSignal(sig uintptr) error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
		return err
	}
	return nil
}

func Setctty() error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
		return err
	}
	return nil
}

func Mkfifo(name string, mode uint32) error {
	return syscall.Mkfifo(name, mode)
}

func Umask(mask int) int {
	return syscall.Umask(mask)
}

func SetCloneFlags(cmd *exec.Cmd, flag uintptr) {
	if cmd.SysProcAttr == nil {
		cmd.SysProcAttr = &syscall.SysProcAttr{}
	}
	cmd.SysProcAttr.Cloneflags = flag
}

func Gettid() int {
	return syscall.Gettid()
}
<commit_msg>Add GetParentDeathSignal() to pkg/system<commit_after>package system

import (
	"os/exec"
	"syscall"
	"unsafe"
)

func Chroot(dir string) error {
	return syscall.Chroot(dir)
}

func Chdir(dir string) error {
	return syscall.Chdir(dir)
}

func Exec(cmd string, args []string, env []string) error {
	return syscall.Exec(cmd, args, env)
}

func Execv(cmd string, args []string, env []string) error {
	name, err := exec.LookPath(cmd)
	if err != nil {
		return err
	}
	return Exec(name, args, env)
}

func Fork() (int, error) {
	syscall.ForkLock.Lock()
	pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
	syscall.ForkLock.Unlock()
	if err != 0 {
		return -1, err
	}
	return int(pid), nil
}

func Mount(source, target, fstype string, flags uintptr, data string) error {
	return syscall.Mount(source, target, fstype, flags, data)
}

func Unmount(target string, flags int) error {
	return syscall.Unmount(target, flags)
}

func Pivotroot(newroot, putold string) error {
	return syscall.PivotRoot(newroot, putold)
}

func Unshare(flags int) error {
	return syscall.Unshare(flags)
}

func Clone(flags uintptr) (int, error) {
	syscall.ForkLock.Lock()
	pid, _, err := syscall.RawSyscall(syscall.SYS_CLONE, flags, 0, 0)
	syscall.ForkLock.Unlock()
	if err != 0 {
		return -1, err
	}
	return int(pid), nil
}

func UsetCloseOnExec(fd uintptr) error {
	if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0); err != 0 {
		return err
	}
	return nil
}

func Setgroups(gids []int) error {
	return syscall.Setgroups(gids)
}

func Setresgid(rgid, egid, sgid int) error {
	return syscall.Setresgid(rgid, egid, sgid)
}

func Setresuid(ruid, euid, suid int) error {
	return syscall.Setresuid(ruid, euid, suid)
}

func Setgid(gid int) error {
	return syscall.Setgid(gid)
}

func Setuid(uid int) error {
	return syscall.Setuid(uid)
}

func Sethostname(name string) error {
	return syscall.Sethostname([]byte(name))
}

func Setsid() (int, error) {
	return syscall.Setsid()
}

func Ioctl(fd uintptr, flag, data uintptr) error {
	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
		return err
	}
	return nil
}

func Closefd(fd uintptr) error {
	return syscall.Close(int(fd))
}

func Dup2(fd1, fd2 uintptr) error {
	return syscall.Dup2(int(fd1), int(fd2))
}

func Mknod(path string, mode uint32, dev int) error {
	return syscall.Mknod(path, mode, dev)
}

func ParentDeathSignal(sig uintptr) error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, sig, 0); err != 0 {
		return err
	}
	return nil
}

func GetParentDeathSignal() (int, error) {
	var sig int

	_, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0)

	if err != 0 {
		return -1, err
	}

	return sig, nil
}

func Setctty() error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_IOCTL, 0, uintptr(syscall.TIOCSCTTY), 0); err != 0 {
		return err
	}
	return nil
}

func Mkfifo(name string, mode uint32) error {
	return syscall.Mkfifo(name, mode)
}

func Umask(mask int) int {
	return syscall.Umask(mask)
}

func SetCloneFlags(cmd *exec.Cmd, flag uintptr) {
	if cmd.SysProcAttr == nil {
		cmd.SysProcAttr = &syscall.SysProcAttr{}
	}
	cmd.SysProcAttr.Cloneflags = flag
}

func Gettid() int {
	return syscall.Gettid()
}
<|endoftext|>


<commit_before><commit_msg>Added stubs for handling directional movement<commit_after><|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 v1

const (
	// If you add a new topology domain here, also consider adding it to the set of default values
	// for the scheduler's --failure-domain command-line argument.
	LabelHostname          = "kubernetes.io/hostname"
	LabelZoneFailureDomain = "failure-domain.beta.kubernetes.io/zone"
	LabelZoneRegion        = "failure-domain.beta.kubernetes.io/region"

	LabelInstanceType = "beta.kubernetes.io/instance-type"

	LabelOS   = "beta.kubernetes.io/os"
	LabelArch = "beta.kubernetes.io/arch"
)

// Role labels are applied to Nodes to mark their purpose.  In particular, we
// usually want to distinguish the master, so that we can isolate privileged
// pods and operations.
//
// Originally we relied on not registering the master, on the fact that the
// master was Unschedulable, and on static manifests for master components.
// But we now do register masters in many environments, are generally moving
// away from static manifests (for better manageability), and working towards
// deprecating the unschedulable field (replacing it with taints & tolerations
// instead).
//
// Even with tainting, a label remains the easiest way of making a positive
// selection, so that pods can schedule only to master nodes for example, and
// thus installations will likely define a label for their master nodes.
//
// So that we can recognize master nodes in consequent places though (such as
// kubectl get nodes), we encourage installations to use the well-known labels.
// We define NodeLabelRole, which is the preferred form, but we will also recognize
// other forms that are known to be in widespread use (NodeLabelKubeadmAlphaRole).

const (
	// NodeLabelRole is the preferred label applied to a Node as a hint that it has a particular purpose (defined by the value).
	NodeLabelRole = "kubernetes.io/role"

	// NodeLabelKubeadmAlphaRole is a label that kubeadm applies to a Node as a hint that it has a particular purpose.
	// Use of NodeLabelRole is preferred.
	NodeLabelKubeadmAlphaRole = "kubeadm.alpha.kubernetes.io/role"

	// NodeLabelRoleMaster is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a master node.
	// A master node typically runs kubernetes system components and will not typically run user workloads.
	NodeLabelRoleMaster = "master"

	// NodeLabelRoleNode is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a "normal" node,
	// as opposed to a RoleMaster node.
	NodeLabelRoleNode = "node"
)
<commit_msg>Added upgrade story from manifest pod to ds<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 v1

const (
	// If you add a new topology domain here, also consider adding it to the set of default values
	// for the scheduler's --failure-domain command-line argument.
	LabelHostname          = "kubernetes.io/hostname"
	LabelZoneFailureDomain = "failure-domain.beta.kubernetes.io/zone"
	LabelZoneRegion        = "failure-domain.beta.kubernetes.io/region"

	LabelInstanceType = "beta.kubernetes.io/instance-type"

	LabelOS   = "beta.kubernetes.io/os"
	LabelArch = "beta.kubernetes.io/arch"

	// Historically fluentd was a manifest pod the was migrated to DaemonSet.
	// To avoid situation during cluster upgrade when there are two instances
	// of fluentd running on a node, kubelet need to mark node on which
	// fluentd in not running as a manifest pod with LabelFluentdDsReady.
	LabelFluentdDsReady = "alpha.kubernetes.io/fluentd-ds-ready"
)

// Role labels are applied to Nodes to mark their purpose.  In particular, we
// usually want to distinguish the master, so that we can isolate privileged
// pods and operations.
//
// Originally we relied on not registering the master, on the fact that the
// master was Unschedulable, and on static manifests for master components.
// But we now do register masters in many environments, are generally moving
// away from static manifests (for better manageability), and working towards
// deprecating the unschedulable field (replacing it with taints & tolerations
// instead).
//
// Even with tainting, a label remains the easiest way of making a positive
// selection, so that pods can schedule only to master nodes for example, and
// thus installations will likely define a label for their master nodes.
//
// So that we can recognize master nodes in consequent places though (such as
// kubectl get nodes), we encourage installations to use the well-known labels.
// We define NodeLabelRole, which is the preferred form, but we will also recognize
// other forms that are known to be in widespread use (NodeLabelKubeadmAlphaRole).

const (
	// NodeLabelRole is the preferred label applied to a Node as a hint that it has a particular purpose (defined by the value).
	NodeLabelRole = "kubernetes.io/role"

	// NodeLabelKubeadmAlphaRole is a label that kubeadm applies to a Node as a hint that it has a particular purpose.
	// Use of NodeLabelRole is preferred.
	NodeLabelKubeadmAlphaRole = "kubeadm.alpha.kubernetes.io/role"

	// NodeLabelRoleMaster is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a master node.
	// A master node typically runs kubernetes system components and will not typically run user workloads.
	NodeLabelRoleMaster = "master"

	// NodeLabelRoleNode is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a "normal" node,
	// as opposed to a RoleMaster node.
	NodeLabelRoleNode = "node"
)
<|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 controller

import (
	"github.com/golang/glog"

	"github.com/imdario/mergo"

	api "k8s.io/api/core/v1"

	"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress"
)

// DeniedKeyName name of the key that contains the reason to deny a location
const DeniedKeyName = "Denied"

// newUpstream creates an upstream without servers.
func newUpstream(name string) *ingress.Backend {
	return &ingress.Backend{
		Name:      name,
		Endpoints: []ingress.Endpoint{},
		Service:   &api.Service{},
		SessionAffinity: ingress.SessionAffinityConfig{
			CookieSessionAffinity: ingress.CookieSessionAffinity{
				Locations: make(map[string][]string),
			},
		},
	}
}

func mergeLocationAnnotations(loc *ingress.Location, anns map[string]interface{}) {
	if _, ok := anns[DeniedKeyName]; ok {
		loc.Denied = anns[DeniedKeyName].(error)
	}
	delete(anns, DeniedKeyName)
	err := mergo.Map(loc, anns)
	if err != nil {
		glog.Errorf("unexpected error merging extracted annotations in location type: %v", err)
	}
}
<commit_msg>Fix merge Locations<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 controller

import (
	"github.com/golang/glog"

	"github.com/imdario/mergo"

	api "k8s.io/api/core/v1"

	"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress"
)

// DeniedKeyName name of the key that contains the reason to deny a location
const DeniedKeyName = "Denied"

// newUpstream creates an upstream without servers.
func newUpstream(name string) *ingress.Backend {
	return &ingress.Backend{
		Name:      name,
		Endpoints: []ingress.Endpoint{},
		Service:   &api.Service{},
		SessionAffinity: ingress.SessionAffinityConfig{
			CookieSessionAffinity: ingress.CookieSessionAffinity{
				Locations: make(map[string][]string),
			},
		},
	}
}

func mergeLocationAnnotations(loc *ingress.Location, anns map[string]interface{}) {
	if _, ok := anns[DeniedKeyName]; ok {
		loc.Denied = anns[DeniedKeyName].(error)
	}
	delete(anns, DeniedKeyName)
	err := mergo.MapWithOverwrite(loc, anns)
	if err != nil {
		glog.Errorf("unexpected error merging extracted annotations in location type: %v", err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 acmechallenges

import (
	"context"
	"fmt"
	"reflect"
	"time"

	corev1 "k8s.io/api/core/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"

	"github.com/jetstack/cert-manager/pkg/acme"
	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
	dnsutil "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
	logf "github.com/jetstack/cert-manager/pkg/logs"
	acmeapi "github.com/jetstack/cert-manager/third_party/crypto/acme"
)

const (
	reasonDomainVerified = "DomainVerified"
)

// solver solves ACME challenges by presenting the given token and key in an
// appropriate way given the config in the Issuer and Certificate.
type solver interface {
	// Present the challenge value with the given solver.
	Present(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
	// Check returns an Error if the propagation check didn't succeed.
	Check(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
	// CleanUp will remove challenge records for a given solver.
	// This may involve deleting resources in the Kubernetes API Server, or
	// communicating with other external components (e.g. DNS providers).
	CleanUp(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
}

// Sync will process this ACME Challenge.
// It is the core control function for ACME challenges.
func (c *Controller) Sync(ctx context.Context, ch *cmapi.Challenge) (err error) {
	log := logf.FromContext(ctx).WithValues("dnsName", ch.Spec.DNSName, "type", ch.Spec.Type)
	ctx = logf.NewContext(ctx, log)
	oldChal := ch
	ch = ch.DeepCopy()

	defer func() {
		// TODO: replace with more efficient comparison
		if reflect.DeepEqual(oldChal.Status, ch.Status) && len(oldChal.Finalizers) == len(ch.Finalizers) {
			return
		}
		_, updateErr := c.CMClient.CertmanagerV1alpha1().Challenges(ch.Namespace).Update(ch)
		if err != nil {
			err = utilerrors.NewAggregate([]error{err, updateErr})
		}
	}()

	if ch.DeletionTimestamp != nil {
		return c.handleFinalizer(ctx, ch)
	}

	// bail out early on if processing=false, as this challenge has not been
	// scheduled yet.
	if ch.Status.Processing == false {
		return nil
	}

	genericIssuer, err := c.helper.GetGenericIssuer(ch.Spec.IssuerRef, ch.Namespace)
	if err != nil {
		return fmt.Errorf("error reading (cluster)issuer %q: %v", ch.Spec.IssuerRef.Name, err)
	}

	// if a challenge is in a final state, we bail out early as there is nothing
	// left for us to do here.
	if acme.IsFinalState(ch.Status.State) {
		if ch.Status.Presented {
			solver, err := c.solverFor(ch.Spec.Type)
			if err != nil {
				log.Error(err, "error getting solver for challenge")
				return err
			}

			err = solver.CleanUp(ctx, genericIssuer, ch)
			if err != nil {
				log.Error(err, "error cleaning up challenge")
				return err
			}

			ch.Status.Presented = false
		}

		ch.Status.Processing = false

		return nil
	}

	cl, err := c.acmeHelper.ClientForIssuer(genericIssuer)
	if err != nil {
		return err
	}

	if ch.Status.State == "" {
		err := c.syncChallengeStatus(ctx, cl, ch)
		if err != nil {
			// TODO: check acme error types and potentially mark the challenge
			// as failed if there is some known error
			return err
		}

		// if the state has not changed, return an error
		if ch.Status.State == "" {
			return fmt.Errorf("could not determine acme challenge status. retrying after applying back-off")
		}

		// the change in the challenges status will trigger a resync.
		// this ensures our cache is consistent so we don't call Present twice
		// due to the http01 solver creating resources that this controller
		// watches/syncs on
		return nil
	}

	// check for CAA records.
	// CAA records are static, so we don't have to present anything
	// before we check for them.

	// Find out which identity the ACME server says it will use.
	dir, err := cl.Discover(ctx)
	if err != nil {
		return err
	}
	// TODO(dmo): figure out if missing CAA identity in directory
	// means no CAA check is performed by ACME server or if any valid
	// CAA would stop issuance (strongly suspect the former)
	if len(dir.CAA) != 0 {
		err := dnsutil.ValidateCAA(ch.Spec.DNSName, dir.CAA, ch.Spec.Wildcard, c.Context.DNS01Nameservers)
		if err != nil {
			ch.Status.Reason = fmt.Sprintf("CAA self-check failed: %s", err)
			return err
		}
	}

	solver, err := c.solverFor(ch.Spec.Type)
	if err != nil {
		return err
	}

	if !ch.Status.Presented {
		err := solver.Present(ctx, genericIssuer, ch)
		if err != nil {
			return err
		}

		ch.Status.Presented = true
		c.Recorder.Eventf(ch, corev1.EventTypeNormal, "Presented", "Presented challenge using %s challenge mechanism", ch.Spec.Type)
	}

	err = solver.Check(ctx, genericIssuer, ch)
	if err != nil {
		log.Error(err, "propagation check failed")
		ch.Status.Reason = fmt.Sprintf("Waiting for %s challenge propagation: %s", ch.Spec.Type, err)

		key, err := controllerpkg.KeyFunc(ch)
		// This is an unexpected edge case and should never occur
		if err != nil {
			return err
		}

		// retry after 10s
		c.queue.AddAfter(key, time.Second*10)

		return nil
	}

	err = c.acceptChallenge(ctx, cl, ch)
	if err != nil {
		return err
	}

	return nil
}

func (c *Controller) handleFinalizer(ctx context.Context, ch *cmapi.Challenge) error {
	log := logf.FromContext(ctx, "finalizer")
	if len(ch.Finalizers) == 0 {
		return nil
	}
	if ch.Finalizers[0] != cmapi.ACMEFinalizer {
		log.V(logf.DebugLevel).Info("waiting to run challenge finalization...")
		return nil
	}
	ch.Finalizers = ch.Finalizers[1:]

	if !ch.Status.Processing {
		return nil
	}

	genericIssuer, err := c.helper.GetGenericIssuer(ch.Spec.IssuerRef, ch.Namespace)
	if err != nil {
		return fmt.Errorf("error reading (cluster)issuer %q: %v", ch.Spec.IssuerRef.Name, err)
	}

	solver, err := c.solverFor(ch.Spec.Type)
	if err != nil {
		log.Error(err, "error getting solver for challenge")
		return nil
	}

	err = solver.CleanUp(ctx, genericIssuer, ch)
	if err != nil {
		log.Error(err, "error cleaning up challenge")
		return nil
	}

	return nil
}

// syncChallengeStatus will communicate with the ACME server to retrieve the current
// state of the Challenge. It will then update the Challenge's status block with the new
// state of the Challenge.
func (c *Controller) syncChallengeStatus(ctx context.Context, cl acmecl.Interface, ch *cmapi.Challenge) error {
	if ch.Spec.URL == "" {
		return fmt.Errorf("challenge URL is blank - challenge has not been created yet")
	}

	acmeChallenge, err := cl.GetChallenge(ctx, ch.Spec.URL)
	if err != nil {
		return err
	}

	// TODO: should we validate the State returned by the ACME server here?
	cmState := cmapi.State(acmeChallenge.Status)
	// be nice to our users and check if there is an error that we
	// can tell them about in the reason field
	// TODO(dmo): problems may be compound and they may be tagged with
	// a type field that suggests changes we should make (like provisioning
	// an account). We might be able to handle errors more gracefully using
	// this info
	ch.Status.Reason = ""
	if acmeChallenge.Error != nil {
		ch.Status.Reason = acmeChallenge.Error.Detail
	}
	ch.Status.State = cmState

	return nil
}

// acceptChallenge will accept the challenge with the acme server and then wait
// for the authorization to reach a 'final' state.
// It will update the challenge's status to reflect the final state of the
// challenge if it failed, or the final state of the challenge's authorization
// if accepting the challenge succeeds.
func (c *Controller) acceptChallenge(ctx context.Context, cl acmecl.Interface, ch *cmapi.Challenge) error {
	log := logf.FromContext(ctx, "acceptChallenge")

	log.Info("accepting challenge with ACME server")
	// We manually construct an ACME challenge here from our own internal type
	// to save additional round trips to the ACME server.
	acmeChal := &acmeapi.Challenge{
		URL:   ch.Spec.URL,
		Token: ch.Spec.Token,
	}
	acmeChal, err := cl.AcceptChallenge(ctx, acmeChal)
	if acmeChal != nil {
		ch.Status.State = cmapi.State(acmeChal.Status)
	}
	if err != nil {
		log.Error(err, "error accepting challenge")
		ch.Status.Reason = fmt.Sprintf("Error accepting challenge: %v", err)
		return err
	}

	log.Info("waiting for authorization for domain")
	authorization, err := cl.WaitAuthorization(ctx, ch.Spec.AuthzURL)
	if err != nil {
		log.Error(err, "error waiting for authorization")

		authErr, ok := err.(acmeapi.AuthorizationError)
		if !ok {
			return err
		}

		ch.Status.State = cmapi.State(authErr.Authorization.Status)
		ch.Status.Reason = fmt.Sprintf("Error accepting authorization: %v", authErr)

		c.Recorder.Eventf(ch, corev1.EventTypeWarning, "Failed", "Accepting challenge authorization failed: %v", authErr)

		// return nil here, as accepting the challenge did not error, the challenge
		// simply failed
		return nil
	}

	ch.Status.State = cmapi.State(authorization.Status)
	ch.Status.Reason = "Successfully authorized domain"
	c.Context.Recorder.Eventf(ch, corev1.EventTypeNormal, reasonDomainVerified, "Domain %q verified with %q validation", ch.Spec.DNSName, ch.Spec.Type)

	return nil
}

func (c *Controller) solverFor(challengeType string) (solver, error) {
	switch challengeType {
	case "http-01":
		return c.httpSolver, nil
	case "dns-01":
		return c.dnsSolver, nil
	}
	return nil, fmt.Errorf("no solver for %q implemented", challengeType)
}
<commit_msg>Set Reason field on ACME challenges during Present/CleanUp<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 acmechallenges

import (
	"context"
	"fmt"
	"reflect"
	"time"

	corev1 "k8s.io/api/core/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"

	"github.com/jetstack/cert-manager/pkg/acme"
	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha1"
	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
	dnsutil "github.com/jetstack/cert-manager/pkg/issuer/acme/dns/util"
	logf "github.com/jetstack/cert-manager/pkg/logs"
	acmeapi "github.com/jetstack/cert-manager/third_party/crypto/acme"
)

const (
	reasonDomainVerified = "DomainVerified"
)

// solver solves ACME challenges by presenting the given token and key in an
// appropriate way given the config in the Issuer and Certificate.
type solver interface {
	// Present the challenge value with the given solver.
	Present(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
	// Check returns an Error if the propagation check didn't succeed.
	Check(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
	// CleanUp will remove challenge records for a given solver.
	// This may involve deleting resources in the Kubernetes API Server, or
	// communicating with other external components (e.g. DNS providers).
	CleanUp(ctx context.Context, issuer cmapi.GenericIssuer, ch *cmapi.Challenge) error
}

// Sync will process this ACME Challenge.
// It is the core control function for ACME challenges.
func (c *Controller) Sync(ctx context.Context, ch *cmapi.Challenge) (err error) {
	log := logf.FromContext(ctx).WithValues("dnsName", ch.Spec.DNSName, "type", ch.Spec.Type)
	ctx = logf.NewContext(ctx, log)
	oldChal := ch
	ch = ch.DeepCopy()

	defer func() {
		// TODO: replace with more efficient comparison
		if reflect.DeepEqual(oldChal.Status, ch.Status) && len(oldChal.Finalizers) == len(ch.Finalizers) {
			return
		}
		_, updateErr := c.CMClient.CertmanagerV1alpha1().Challenges(ch.Namespace).Update(ch)
		if err != nil {
			err = utilerrors.NewAggregate([]error{err, updateErr})
		}
	}()

	if ch.DeletionTimestamp != nil {
		return c.handleFinalizer(ctx, ch)
	}

	// bail out early on if processing=false, as this challenge has not been
	// scheduled yet.
	if ch.Status.Processing == false {
		return nil
	}

	genericIssuer, err := c.helper.GetGenericIssuer(ch.Spec.IssuerRef, ch.Namespace)
	if err != nil {
		return fmt.Errorf("error reading (cluster)issuer %q: %v", ch.Spec.IssuerRef.Name, err)
	}

	// if a challenge is in a final state, we bail out early as there is nothing
	// left for us to do here.
	if acme.IsFinalState(ch.Status.State) {
		if ch.Status.Presented {
			solver, err := c.solverFor(ch.Spec.Type)
			if err != nil {
				log.Error(err, "error getting solver for challenge")
				return err
			}

			err = solver.CleanUp(ctx, genericIssuer, ch)
			if err != nil {
				c.Recorder.Eventf(ch, corev1.EventTypeWarning, "CleanUpError", "Error cleaning up challenge: %v", err)
				ch.Status.Reason = err.Error()
				log.Error(err, "error cleaning up challenge")
				return err
			}

			ch.Status.Presented = false
		}

		ch.Status.Processing = false

		return nil
	}

	cl, err := c.acmeHelper.ClientForIssuer(genericIssuer)
	if err != nil {
		return err
	}

	if ch.Status.State == "" {
		err := c.syncChallengeStatus(ctx, cl, ch)
		if err != nil {
			// TODO: check acme error types and potentially mark the challenge
			// as failed if there is some known error
			return err
		}

		// if the state has not changed, return an error
		if ch.Status.State == "" {
			return fmt.Errorf("could not determine acme challenge status. retrying after applying back-off")
		}

		// the change in the challenges status will trigger a resync.
		// this ensures our cache is consistent so we don't call Present twice
		// due to the http01 solver creating resources that this controller
		// watches/syncs on
		return nil
	}

	// check for CAA records.
	// CAA records are static, so we don't have to present anything
	// before we check for them.

	// Find out which identity the ACME server says it will use.
	dir, err := cl.Discover(ctx)
	if err != nil {
		return err
	}
	// TODO(dmo): figure out if missing CAA identity in directory
	// means no CAA check is performed by ACME server or if any valid
	// CAA would stop issuance (strongly suspect the former)
	if len(dir.CAA) != 0 {
		err := dnsutil.ValidateCAA(ch.Spec.DNSName, dir.CAA, ch.Spec.Wildcard, c.Context.DNS01Nameservers)
		if err != nil {
			ch.Status.Reason = fmt.Sprintf("CAA self-check failed: %s", err)
			return err
		}
	}

	solver, err := c.solverFor(ch.Spec.Type)
	if err != nil {
		return err
	}

	if !ch.Status.Presented {
		err := solver.Present(ctx, genericIssuer, ch)
		if err != nil {
			c.Recorder.Eventf(ch, corev1.EventTypeWarning, "PresentError", "Error presenting challenge: %v", err)
			ch.Status.Reason = err.Error()
			return err
		}

		ch.Status.Presented = true
		c.Recorder.Eventf(ch, corev1.EventTypeNormal, "Presented", "Presented challenge using %s challenge mechanism", ch.Spec.Type)
	}

	err = solver.Check(ctx, genericIssuer, ch)
	if err != nil {
		log.Error(err, "propagation check failed")
		ch.Status.Reason = fmt.Sprintf("Waiting for %s challenge propagation: %s", ch.Spec.Type, err)

		key, err := controllerpkg.KeyFunc(ch)
		// This is an unexpected edge case and should never occur
		if err != nil {
			return err
		}

		// retry after 10s
		c.queue.AddAfter(key, time.Second*10)

		return nil
	}

	err = c.acceptChallenge(ctx, cl, ch)
	if err != nil {
		return err
	}

	return nil
}

func (c *Controller) handleFinalizer(ctx context.Context, ch *cmapi.Challenge) error {
	log := logf.FromContext(ctx, "finalizer")
	if len(ch.Finalizers) == 0 {
		return nil
	}
	if ch.Finalizers[0] != cmapi.ACMEFinalizer {
		log.V(logf.DebugLevel).Info("waiting to run challenge finalization...")
		return nil
	}
	ch.Finalizers = ch.Finalizers[1:]

	if !ch.Status.Processing {
		return nil
	}

	genericIssuer, err := c.helper.GetGenericIssuer(ch.Spec.IssuerRef, ch.Namespace)
	if err != nil {
		return fmt.Errorf("error reading (cluster)issuer %q: %v", ch.Spec.IssuerRef.Name, err)
	}

	solver, err := c.solverFor(ch.Spec.Type)
	if err != nil {
		log.Error(err, "error getting solver for challenge")
		return nil
	}

	err = solver.CleanUp(ctx, genericIssuer, ch)
	if err != nil {
		c.Recorder.Eventf(ch, corev1.EventTypeWarning, "CleanUpError", "Error cleaning up challenge: %v", err)
		ch.Status.Reason = err.Error()
		log.Error(err, "error cleaning up challenge")
		return nil
	}

	return nil
}

// syncChallengeStatus will communicate with the ACME server to retrieve the current
// state of the Challenge. It will then update the Challenge's status block with the new
// state of the Challenge.
func (c *Controller) syncChallengeStatus(ctx context.Context, cl acmecl.Interface, ch *cmapi.Challenge) error {
	if ch.Spec.URL == "" {
		return fmt.Errorf("challenge URL is blank - challenge has not been created yet")
	}

	acmeChallenge, err := cl.GetChallenge(ctx, ch.Spec.URL)
	if err != nil {
		return err
	}

	// TODO: should we validate the State returned by the ACME server here?
	cmState := cmapi.State(acmeChallenge.Status)
	// be nice to our users and check if there is an error that we
	// can tell them about in the reason field
	// TODO(dmo): problems may be compound and they may be tagged with
	// a type field that suggests changes we should make (like provisioning
	// an account). We might be able to handle errors more gracefully using
	// this info
	ch.Status.Reason = ""
	if acmeChallenge.Error != nil {
		ch.Status.Reason = acmeChallenge.Error.Detail
	}
	ch.Status.State = cmState

	return nil
}

// acceptChallenge will accept the challenge with the acme server and then wait
// for the authorization to reach a 'final' state.
// It will update the challenge's status to reflect the final state of the
// challenge if it failed, or the final state of the challenge's authorization
// if accepting the challenge succeeds.
func (c *Controller) acceptChallenge(ctx context.Context, cl acmecl.Interface, ch *cmapi.Challenge) error {
	log := logf.FromContext(ctx, "acceptChallenge")

	log.Info("accepting challenge with ACME server")
	// We manually construct an ACME challenge here from our own internal type
	// to save additional round trips to the ACME server.
	acmeChal := &acmeapi.Challenge{
		URL:   ch.Spec.URL,
		Token: ch.Spec.Token,
	}
	acmeChal, err := cl.AcceptChallenge(ctx, acmeChal)
	if acmeChal != nil {
		ch.Status.State = cmapi.State(acmeChal.Status)
	}
	if err != nil {
		log.Error(err, "error accepting challenge")
		ch.Status.Reason = fmt.Sprintf("Error accepting challenge: %v", err)
		return err
	}

	log.Info("waiting for authorization for domain")
	authorization, err := cl.WaitAuthorization(ctx, ch.Spec.AuthzURL)
	if err != nil {
		log.Error(err, "error waiting for authorization")

		authErr, ok := err.(acmeapi.AuthorizationError)
		if !ok {
			return err
		}

		ch.Status.State = cmapi.State(authErr.Authorization.Status)
		ch.Status.Reason = fmt.Sprintf("Error accepting authorization: %v", authErr)

		c.Recorder.Eventf(ch, corev1.EventTypeWarning, "Failed", "Accepting challenge authorization failed: %v", authErr)

		// return nil here, as accepting the challenge did not error, the challenge
		// simply failed
		return nil
	}

	ch.Status.State = cmapi.State(authorization.Status)
	ch.Status.Reason = "Successfully authorized domain"
	c.Context.Recorder.Eventf(ch, corev1.EventTypeNormal, reasonDomainVerified, "Domain %q verified with %q validation", ch.Spec.DNSName, ch.Spec.Type)

	return nil
}

func (c *Controller) solverFor(challengeType string) (solver, error) {
	switch challengeType {
	case "http-01":
		return c.httpSolver, nil
	case "dns-01":
		return c.dnsSolver, nil
	}
	return nil, fmt.Errorf("no solver for %q implemented", challengeType)
}
<|endoftext|>