<commit_before>// sunspec is an API that allows navigation around SunSpec device maps.
//
// The key abstractions are: Array, Device, Model, Block and Point with
// Arrays being collection of Devices, Devices collections of Models and so
// on. At the lowest level are Points which provide for strongly typed
// access to blocks of 16 bit registers.
//
// The API is intended to be backed by different physical implementations, including:
//
//  * Modbus RTU/TCP (for purposes of connecting to remote devices)
//  * XML (for purposes of data interchange)
//  * memory (for purposes of testing)
//
package sunspec

import (
	"errors"
)

var (
	ErrNoSuchBlock   = errors.New("no such block")
	ErrNoSuchModel   = errors.New("no such model")
	ErrTooManyModels = errors.New("too many models")
	ErrNoSuchPoint   = errors.New("no such point")
)

// ModelId is the type of model identifiers used with the Device.Model and Device.MustModel
// calls.
//
// To guarantee that a model's SMDX document is loaded into the SMDX registry you can use
// the value of modelXXXX.ModelID from the github.com/crabmusket/gosunspec/models/modelXXXX
// package to refer to the model, a side effect of which is to register the related SMDX model.
type ModelId uint16

// These data types are defined in the SunSpec Information Model Specification.
// http://sunspec.org/wp-content/uploads/2015/06/SunSpec-Information-Models-12041.pdf
// In Version 1.8 of the document, the data type definitions can be found on
// page 13.

// A 16 bit accumulator in the range 0-65535. The master must detect rollovers.
type Acc16 uint16

// A 16 bit accumulator in the range 0-4294967295. The master must detect rollovers.
type Acc32 uint32

// A 16 bit accumulator in the range 0-9223372036854775807. The master must detect rollovers.
type Acc64 uint64

// A 16 bit register with values from 0x0000-0x7fff. If the high bit 0x8000 is set, all
// other bits should be ignored.
type Bitfield16 uint16

// A 32 bit register with values from 0x0000-0x7fffffff. If the high bit 0x80000000 is set, all
// other bits should be ignored.
type Bitfield32 uint32

// A 16 bit register used to indicate the number of repeat blocks.
//
// NB: meaning inferred from use by some SMDX documents since SunSpec Information Model does not explicitly specify the semantics of this type.
type Count uint16

// A 16 bit register used to represent enumerated values. 65535 is reserved.
type Enum16 uint16

// A 32 bit register used to represent enumerated values. 4294967295 is reserved.
type Enum32 uint32

// An hardware address (like a MAC address) - see https://standards.ieee.org/develop/regauth/tut/eui48.pdf
type Eui48 [6]byte

// A 32bit IPv4 address (binary)
type Ipaddr [4]byte

// A 128 bit IPv6 address (binary)
type Ipv6addr [16]byte

// A 16-bit pad register. Always 0x8000.
type Pad uint16

// A 16-bit scaling factor used to scale the value of some other integer register.
type ScaleFactor int16

// An array is a collection of devices.
//
// This type is intended to be used when the underlying physical
// implementation is an XML document that may contain multiple
// 'd' elements but may also be used in cases where a Modbus address space
// has several device instances, in sequence, after the "Suns"
// marker bytes.
type Array interface {
	Do(func(d Device)) // Iterate over all the devices.
	Collect(func(d Device) bool) []Device
}

var AllDevices = func(d Device) bool { return true }

// A Device is a collection of Models that provides an uniform abstraction of
// physical devices of various kinds (Modbus, memory, XML documents)
type Device interface {
	// MustModel answers the first and only model specified by id, or panics otherwise
	// This method should not be used unless it is known for sure that the device
	// does not have multiple models with the specified id.
	MustModel(id ModelId) Model

	// Do iterates over all the models supported by the device
	Do(func(m Model))

	// Collects the subset of models that match the filter specified.
	//
	// for example:
	//     if m, err := ExactlyOneModel(d.Collect(SameModelId(model101.ModelID))); err == nil {
	//          // operate on the one and only model m
	//     }
	Collect(func(m Model) bool) []Model
}

var AllModels = func(m Model) bool { return true }

// A filter that can be used with Device.Collect to select a subset
// of the models that have the model id specified.
func SameModelId(id ModelId) func(m Model) bool {
	return func(m Model) bool {
		return id == m.Id()
	}
}

// ExactlyOneModel returns a model iff the slice contains
// exactly one model or an error otherwise.
func ExactlyOneModel(models []Model) (Model, error) {
	if len(models) > 1 {
		return nil, ErrTooManyModels
	} else if len(models) < 1 {
		return nil, ErrNoSuchModel
	} else {
		return models[0], nil
	}
}

// A Model is a collection of Blocks which represents a relocatable
// region of the physical device's address space and whose contents is specified
// by an SMDX document.
//
// By relocatable, we mean that the physical address of model is determined by
// device manufacturer, not some standard.
//
// Block 0 is the fixed block of the model, if any, otherwise it is the first repeating
// block. The remaining blocks, if any, are repeating blocks.
type Model interface {
	Id() ModelId                // Answer the model identifier of the receiver
	Blocks() int                // Answer the number of blocks
	Block(i int) (Block, error) // Answer the nth Block if it exists, or error otherwise.
	MustBlock(i int) Block      // Answer the block specified by i, or panics otherwise.
	Do(func(b Block))           // Iterate across all the blocks, in order.
}

// A Block is a contiguous collection of Points within a Model. The Points that
// comprise the Block are specified by a block element of a model's SMDX
// document.
//
// Subsets of a Block's Points can be read from or written to the underlying
// physical device using the Read and Write methods.
//
// A Block's Points exist in an error state prior to reading from underlying
// physical device. Upon a successful Read, the Points move from the error state
// to a populated state at which point the values can be accessed using the
// Point getter methods.
type Block interface {
	// Point answers the specified point if it exists or returns error otherwise.
	Point(id string) (Point, error)

	// MustPoint must answer the specified point or else it panics.
	MustPoint(id string) Point

	// Do iterates over all the points in the block in the specification order.
	Do(func(p Point))

	// Do iterates over all the scale factor points, then the non-scale factor
	// points. Both groups are processed in specification order. This method
	// is useful when multiple points in the block need to be updated and the
	// set of points to be updated includes both scale factor points and
	// value points related to the scale factor points.
	DoScaleFactorsFirst(func(p Point))

	// Read the registers implied by the specified pointIds from the physical
	// device. If no pointIds are specified, then all the points in the block
	// are read.
	//
	// If any specified or implied point has a related scaling factor, then the
	// related scaling factor point is also read.
	//
	// A successful read implies that subsequent access to the specified (and
	// implied) points will be error free.
	Read(pointIds ...string) error

	// Write the specified pointIds (or all, if none specified) into the
	// physical device. Like the Read case, if not pointIds are specified, then
	// all Blocks are written into the physical device. Unlike the Read case,
	// related scaling factors are not written unless explicitly specified.
	Write(pointIds ...string) error
}

// A Point is block of one or more 16 bit registers that can be mapped
// onto a value of a golang type of the correct shape.
//
// A Point is initially in an error state meaning its Error() method yields a
// non-nil value. While in the error state, any attempt to access the value of
// the Point will result in a panic. The error state is cleared by successfully
// reading the value of the Point from the physical device (using Block.Read) or
// by successfully setting its value with one of the Point setter methods.
//
// A program can assume that, immediately after a successful call to Block.Read(),
// then all Points that were either implicitly or explicitly specified by
// the arguments to Block.Read() were successfully read and hence will
// not be in an error state.
//
// If a program cannot prove that a Point is not in an error state, then
// the error state should be checked by calling Error() method prior
// to attempting to access the value of the Point.
//
// A Point can return to an error state if a related scaling factor is read
// (without reading the Point itself) or if the value of a related scaling
// factor is changed by calling one of its setter methods. One implication of
// this behaviour is that if you need to modify a value and its related scaling
// factor, you should always modify the related scaling factor first.
//
// Points also know how to apply scaling factors. If the ScaledValue() method is
// called for a Point with a 16, 32 or 64 bit numeric register type, then the
// register value will be multiplied by 10^scaling factor to produce a float64
// result containing scaled value of the Point's register value. Note that
// ScaledValue() will panic if either the Point or its related scaling factor
// Point are in an error state.
//
// A note about use of panics in the definition of this interface
//
// The intent of extensive use of panics in the definition of this interface is
// to permit detection of unsafe access to data without requiring each and every
// call to be guarded with an error check which, in a correctly written program,
// will not be required.
type Point interface {
	// The id of the point - see the models/modelXXX and the
	// related SMDX documents for a list of these.
	Id() string

	// The type, as defined by SunSpec. See the typelabel package for a list of these.
	Type() string

	// Answers the error associated with the point. This will be non nil, if:
	// - the parent block has not been read with a range of points that includes the receiver
	// - an attempt was made to read the parent block, but an error occurred during the read
	// - the related scaling factor point (if any) has been updated by a read or by a set
	Error() error

	// getter methods - calls will panic if Error() is not nil or if incorrectly typed method is called
	Acc16() Acc16
	Acc32() Acc32
	Acc64() Acc64
	Bitfield16() Bitfield16
	Bitfield32() Bitfield32
	Count() Count
	Enum16() Enum16
	Enum32() Enum32
	Eui48() Eui48
	Float32() float32
	Int16() int16
	Int32() int32
	Int64() int64
	Ipaddr() Ipaddr
	Ipv6addr() Ipv6addr
	Pad() Pad
	StringValue() string
	ScaleFactor() ScaleFactor
	Uint16() uint16
	Uint32() uint32
	Uint64() uint64

	// setter methods - calls will panic if incorrectly typed method is called.
	SetAcc16(v Acc16)
	SetAcc32(v Acc32)
	SetAcc64(v Acc64)
	SetBitfield16(v Bitfield16)
	SetBitfield32(v Bitfield32)
	SetCount(v Count)
	SetEnum16(v Enum16)
	SetEnum32(v Enum32)
	SetEui48(v Eui48)
	SetFloat32(v float32)
	SetInt16(v int16)
	SetInt32(v int32)
	SetInt64(v int64)
	SetIpaddr(v Ipaddr)
	SetIpv6addr(v Ipv6addr)
	SetPad(v Pad)
	SetStringValue(v string)
	SetScaleFactor(v ScaleFactor)
	SetUint16(v uint16)
	SetUint32(v uint32)
	SetUint64(v uint64)

	// Answer the scaled value of the point as a float64. This method
	// will panic if the Error() method of either the point or the
	// related scaling factor is not nil.
	ScaledValue() float64

	// Answer the value of the point. This method will panic if Error() is not nil.
	Value() interface{}

	// Set the value associated with the point. This method will panic if the value
	// type is not consistent with the type described by Type().
	SetValue(v interface{}) error
}
<commit_msg>Add OneOfSeveralModelIds filter.<commit_after>// sunspec is an API that allows navigation around SunSpec device maps.
//
// The key abstractions are: Array, Device, Model, Block and Point with
// Arrays being collection of Devices, Devices collections of Models and so
// on. At the lowest level are Points which provide for strongly typed
// access to blocks of 16 bit registers.
//
// The API is intended to be backed by different physical implementations, including:
//
//  * Modbus RTU/TCP (for purposes of connecting to remote devices)
//  * XML (for purposes of data interchange)
//  * memory (for purposes of testing)
//
package sunspec

import (
	"errors"
)

var (
	ErrNoSuchBlock   = errors.New("no such block")
	ErrNoSuchModel   = errors.New("no such model")
	ErrTooManyModels = errors.New("too many models")
	ErrNoSuchPoint   = errors.New("no such point")
)

// ModelId is the type of model identifiers used with the Device.Model and Device.MustModel
// calls.
//
// To guarantee that a model's SMDX document is loaded into the SMDX registry you can use
// the value of modelXXXX.ModelID from the github.com/crabmusket/gosunspec/models/modelXXXX
// package to refer to the model, a side effect of which is to register the related SMDX model.
type ModelId uint16

// These data types are defined in the SunSpec Information Model Specification.
// http://sunspec.org/wp-content/uploads/2015/06/SunSpec-Information-Models-12041.pdf
// In Version 1.8 of the document, the data type definitions can be found on
// page 13.

// A 16 bit accumulator in the range 0-65535. The master must detect rollovers.
type Acc16 uint16

// A 16 bit accumulator in the range 0-4294967295. The master must detect rollovers.
type Acc32 uint32

// A 16 bit accumulator in the range 0-9223372036854775807. The master must detect rollovers.
type Acc64 uint64

// A 16 bit register with values from 0x0000-0x7fff. If the high bit 0x8000 is set, all
// other bits should be ignored.
type Bitfield16 uint16

// A 32 bit register with values from 0x0000-0x7fffffff. If the high bit 0x80000000 is set, all
// other bits should be ignored.
type Bitfield32 uint32

// A 16 bit register used to indicate the number of repeat blocks.
//
// NB: meaning inferred from use by some SMDX documents since SunSpec Information Model does not explicitly specify the semantics of this type.
type Count uint16

// A 16 bit register used to represent enumerated values. 65535 is reserved.
type Enum16 uint16

// A 32 bit register used to represent enumerated values. 4294967295 is reserved.
type Enum32 uint32

// An hardware address (like a MAC address) - see https://standards.ieee.org/develop/regauth/tut/eui48.pdf
type Eui48 [6]byte

// A 32bit IPv4 address (binary)
type Ipaddr [4]byte

// A 128 bit IPv6 address (binary)
type Ipv6addr [16]byte

// A 16-bit pad register. Always 0x8000.
type Pad uint16

// A 16-bit scaling factor used to scale the value of some other integer register.
type ScaleFactor int16

// An array is a collection of devices.
//
// This type is intended to be used when the underlying physical
// implementation is an XML document that may contain multiple
// 'd' elements but may also be used in cases where a Modbus address space
// has several device instances, in sequence, after the "Suns"
// marker bytes.
type Array interface {
	Do(func(d Device)) // Iterate over all the devices.
	Collect(func(d Device) bool) []Device
}

var AllDevices = func(d Device) bool { return true }

// A Device is a collection of Models that provides an uniform abstraction of
// physical devices of various kinds (Modbus, memory, XML documents)
type Device interface {
	// MustModel answers the first and only model specified by id, or panics otherwise
	// This method should not be used unless it is known for sure that the device
	// does not have multiple models with the specified id.
	MustModel(id ModelId) Model

	// Do iterates over all the models supported by the device
	Do(func(m Model))

	// Collects the subset of models that match the filter specified.
	//
	// for example:
	//     if m, err := ExactlyOneModel(d.Collect(SameModelId(model101.ModelID))); err == nil {
	//          // operate on the one and only model m
	//     }
	Collect(func(m Model) bool) []Model
}

var AllModels = func(m Model) bool { return true }

// A filter that can be used with Device.Collect to select a subset
// of the models that have the model id specified.
func SameModelId(id ModelId) func(m Model) bool {
	return func(m Model) bool {
		return id == m.Id()
	}
}

// Returns a function that matches a number of possible ModelIds
func OneOfSeveralModelIds(candidates []ModelId) func(m Model) bool {
	return func(m Model) bool {
		id := m.Id()
		for _, c := range candidates {
			if c == id {
				return true
			}
		}
		return false
	}
}

// ExactlyOneModel returns a model iff the slice contains
// exactly one model or an error otherwise.
func ExactlyOneModel(models []Model) (Model, error) {
	if len(models) > 1 {
		return nil, ErrTooManyModels
	} else if len(models) < 1 {
		return nil, ErrNoSuchModel
	} else {
		return models[0], nil
	}
}

// A Model is a collection of Blocks which represents a relocatable
// region of the physical device's address space and whose contents is specified
// by an SMDX document.
//
// By relocatable, we mean that the physical address of model is determined by
// device manufacturer, not some standard.
//
// Block 0 is the fixed block of the model, if any, otherwise it is the first repeating
// block. The remaining blocks, if any, are repeating blocks.
type Model interface {
	Id() ModelId                // Answer the model identifier of the receiver
	Blocks() int                // Answer the number of blocks
	Block(i int) (Block, error) // Answer the nth Block if it exists, or error otherwise.
	MustBlock(i int) Block      // Answer the block specified by i, or panics otherwise.
	Do(func(b Block))           // Iterate across all the blocks, in order.
}

// A Block is a contiguous collection of Points within a Model. The Points that
// comprise the Block are specified by a block element of a model's SMDX
// document.
//
// Subsets of a Block's Points can be read from or written to the underlying
// physical device using the Read and Write methods.
//
// A Block's Points exist in an error state prior to reading from underlying
// physical device. Upon a successful Read, the Points move from the error state
// to a populated state at which point the values can be accessed using the
// Point getter methods.
type Block interface {
	// Point answers the specified point if it exists or returns error otherwise.
	Point(id string) (Point, error)

	// MustPoint must answer the specified point or else it panics.
	MustPoint(id string) Point

	// Do iterates over all the points in the block in the specification order.
	Do(func(p Point))

	// Do iterates over all the scale factor points, then the non-scale factor
	// points. Both groups are processed in specification order. This method
	// is useful when multiple points in the block need to be updated and the
	// set of points to be updated includes both scale factor points and
	// value points related to the scale factor points.
	DoScaleFactorsFirst(func(p Point))

	// Read the registers implied by the specified pointIds from the physical
	// device. If no pointIds are specified, then all the points in the block
	// are read.
	//
	// If any specified or implied point has a related scaling factor, then the
	// related scaling factor point is also read.
	//
	// A successful read implies that subsequent access to the specified (and
	// implied) points will be error free.
	Read(pointIds ...string) error

	// Write the specified pointIds (or all, if none specified) into the
	// physical device. Like the Read case, if not pointIds are specified, then
	// all Blocks are written into the physical device. Unlike the Read case,
	// related scaling factors are not written unless explicitly specified.
	Write(pointIds ...string) error
}

// A Point is block of one or more 16 bit registers that can be mapped
// onto a value of a golang type of the correct shape.
//
// A Point is initially in an error state meaning its Error() method yields a
// non-nil value. While in the error state, any attempt to access the value of
// the Point will result in a panic. The error state is cleared by successfully
// reading the value of the Point from the physical device (using Block.Read) or
// by successfully setting its value with one of the Point setter methods.
//
// A program can assume that, immediately after a successful call to Block.Read(),
// then all Points that were either implicitly or explicitly specified by
// the arguments to Block.Read() were successfully read and hence will
// not be in an error state.
//
// If a program cannot prove that a Point is not in an error state, then
// the error state should be checked by calling Error() method prior
// to attempting to access the value of the Point.
//
// A Point can return to an error state if a related scaling factor is read
// (without reading the Point itself) or if the value of a related scaling
// factor is changed by calling one of its setter methods. One implication of
// this behaviour is that if you need to modify a value and its related scaling
// factor, you should always modify the related scaling factor first.
//
// Points also know how to apply scaling factors. If the ScaledValue() method is
// called for a Point with a 16, 32 or 64 bit numeric register type, then the
// register value will be multiplied by 10^scaling factor to produce a float64
// result containing scaled value of the Point's register value. Note that
// ScaledValue() will panic if either the Point or its related scaling factor
// Point are in an error state.
//
// A note about use of panics in the definition of this interface
//
// The intent of extensive use of panics in the definition of this interface is
// to permit detection of unsafe access to data without requiring each and every
// call to be guarded with an error check which, in a correctly written program,
// will not be required.
type Point interface {
	// The id of the point - see the models/modelXXX and the
	// related SMDX documents for a list of these.
	Id() string

	// The type, as defined by SunSpec. See the typelabel package for a list of these.
	Type() string

	// Answers the error associated with the point. This will be non nil, if:
	// - the parent block has not been read with a range of points that includes the receiver
	// - an attempt was made to read the parent block, but an error occurred during the read
	// - the related scaling factor point (if any) has been updated by a read or by a set
	Error() error

	// getter methods - calls will panic if Error() is not nil or if incorrectly typed method is called
	Acc16() Acc16
	Acc32() Acc32
	Acc64() Acc64
	Bitfield16() Bitfield16
	Bitfield32() Bitfield32
	Count() Count
	Enum16() Enum16
	Enum32() Enum32
	Eui48() Eui48
	Float32() float32
	Int16() int16
	Int32() int32
	Int64() int64
	Ipaddr() Ipaddr
	Ipv6addr() Ipv6addr
	Pad() Pad
	StringValue() string
	ScaleFactor() ScaleFactor
	Uint16() uint16
	Uint32() uint32
	Uint64() uint64

	// setter methods - calls will panic if incorrectly typed method is called.
	SetAcc16(v Acc16)
	SetAcc32(v Acc32)
	SetAcc64(v Acc64)
	SetBitfield16(v Bitfield16)
	SetBitfield32(v Bitfield32)
	SetCount(v Count)
	SetEnum16(v Enum16)
	SetEnum32(v Enum32)
	SetEui48(v Eui48)
	SetFloat32(v float32)
	SetInt16(v int16)
	SetInt32(v int32)
	SetInt64(v int64)
	SetIpaddr(v Ipaddr)
	SetIpv6addr(v Ipv6addr)
	SetPad(v Pad)
	SetStringValue(v string)
	SetScaleFactor(v ScaleFactor)
	SetUint16(v uint16)
	SetUint32(v uint32)
	SetUint64(v uint64)

	// Answer the scaled value of the point as a float64. This method
	// will panic if the Error() method of either the point or the
	// related scaling factor is not nil.
	ScaledValue() float64

	// Answer the value of the point. This method will panic if Error() is not nil.
	Value() interface{}

	// Set the value associated with the point. This method will panic if the value
	// type is not consistent with the type described by Type().
	SetValue(v interface{}) error
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"strings"
)

// containsString is the function to lookup the slice of strings for an passd
// item; if the item is found in the slice, then it returns true, neither it
// returns false value.
func containsString(slice []string, item string) bool {
	for _, value := range slice {
		if value == item {
			return true
		}
	}
	return false
}

// execCmdAndPrintResult is used to execute system commands and prints the
// result of the command from stdio to the interactive session.
func execCmdAndPrintResult(cmdWithArg string) {
	var cmdFields = strings.Fields(cmdWithArg)
	cmd := exec.Command(cmdFields[0], cmdFields[1])
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		fmt.Println("Don't mind, something's broken!")
	}
	fmt.Println(strings.TrimSpace(out.String()))
}
<commit_msg>fix: pass slice splats as argument while executing system command<commit_after>package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"strings"
)

// containsString is the function to lookup the slice of strings for an passd
// item; if the item is found in the slice, then it returns true, neither it
// returns false value.
func containsString(slice []string, item string) bool {
	for _, value := range slice {
		if value == item {
			return true
		}
	}
	return false
}

// execCmdAndPrintResult is used to execute system commands and prints the
// result of the command from stdio to the interactive session.
func execCmdAndPrintResult(cmdWithArg string) {
	var cmdFields = strings.Fields(cmdWithArg)
	cmd := exec.Command(cmdFields[0], cmdFields[1:]...)
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		fmt.Println("Don't mind, something's broken!", "\n", err)
	}
	fmt.Println(strings.TrimSpace(out.String()))
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package envs

import (
	"net/http"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// ForceDeletion : Deletes a service by name forcing it
func ForceDeletion(au models.User, name string) (int, []byte) {
	var e models.Env

	if st, res := h.IsAuthorizedToResource(&au, h.DeleteEnvForce, e.GetType(), name); st != 200 {
		return st, res
	}

	if err := e.DeleteByName(name); err != nil {
		h.L.Error(err.Error())
		return 500, models.NewJSONError(err.Error())
	}

	return http.StatusOK, []byte(`{"status":"ok"}`)
}
<commit_msg>removing roles on deletion<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package envs

import (
	"net/http"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// ForceDeletion : Deletes a service by name forcing it
func ForceDeletion(au models.User, name string) (int, []byte) {
	var e models.Env
	var r models.Role
	var roles []models.Role

	if st, res := h.IsAuthorizedToResource(&au, h.DeleteEnvForce, e.GetType(), name); st != 200 {
		return st, res
	}

	if err := e.DeleteByName(name); err != nil {
		h.L.Error(err.Error())
		return 500, models.NewJSONError(err.Error())
	}

	err := r.FindAllByResource(e.GetID(), e.GetType(), &roles)
	if err != nil {
		h.L.Error(err.Error())
		return 500, models.NewJSONError(err.Error())
	}

	for _, role := range roles {
		err = role.Delete()
		if err != nil {
			h.L.Error(err.Error())
			return 500, models.NewJSONError(err.Error())
		}
	}

	return http.StatusOK, []byte(`{"status":"ok"}`)
}
<|endoftext|>


<commit_before><commit_msg>Makes TestRestartAll more resilient.<commit_after><|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/test/gtest"
)

func Test_Router_Hook_Basic(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE:  func(r *ghttp.Request) { r.Response.Write("1") },
		ghttp.HOOK_AFTER_SERVE:   func(r *ghttp.Request) { r.Response.Write("2") },
		ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) { r.Response.Write("3") },
		ghttp.HOOK_AFTER_OUTPUT:  func(r *ghttp.Request) { r.Response.Write("4") },
	})
	s.BindHandler("/test/test", func(r *ghttp.Request) {
		r.Response.Write("test")
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "123")
		t.Assert(client.GetContent("/test/test"), "1test23")
	})
}

func Test_Router_Hook_Fuzzy_Router(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	i := 1000
	pattern1 := "/:name/info"
	s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.SetParam("uid", i)
			i++
		},
	})
	s.BindHandler(pattern1, func(r *ghttp.Request) {
		r.Response.Write(r.Get("uid"))
	})

	pattern2 := "/{object}/list/{page}.java"
	s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) {
			r.Response.SetBuffer([]byte(
				fmt.Sprint(r.Get("object"), "&", r.Get("page"), "&", i),
			))
		},
	})
	s.BindHandler(pattern2, func(r *ghttp.Request) {
		r.Response.Write(r.Router.Uri)
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/john"), "Not Found")
		t.Assert(client.GetContent("/john/info"), "1000")
		t.Assert(client.GetContent("/john/info"), "1001")
		t.Assert(client.GetContent("/john/list/1.java"), "john&1&1002")
		t.Assert(client.GetContent("/john/list/2.java"), "john&2&1002")
	})
}

func Test_Router_Hook_Priority(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/priority/show", func(r *ghttp.Request) {
		r.Response.Write("show")
	})

	s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("1")
		},
	})
	s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("2")
		},
	})
	s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("3")
		},
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "Not Found")
		t.Assert(client.GetContent("/priority/show"), "312show")
		t.Assert(client.GetContent("/priority/any/any"), "2")
		t.Assert(client.GetContent("/priority/name"), "12")
	})
}

func Test_Router_Hook_Multi(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/multi-hook", func(r *ghttp.Request) {
		r.Response.Write("show")
	})

	s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("1")
		},
	})
	s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("2")
		},
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "Not Found")
		t.Assert(client.GetContent("/multi-hook"), "12show")
	})
}
<commit_msg>add more unit testing case for ghttp.Server<commit_after>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp_test

import (
	"fmt"
	"testing"
	"time"

	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/test/gtest"
)

func Test_Router_Hook_Basic(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE:  func(r *ghttp.Request) { r.Response.Write("1") },
		ghttp.HOOK_AFTER_SERVE:   func(r *ghttp.Request) { r.Response.Write("2") },
		ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) { r.Response.Write("3") },
		ghttp.HOOK_AFTER_OUTPUT:  func(r *ghttp.Request) { r.Response.Write("4") },
	})
	s.BindHandler("/test/test", func(r *ghttp.Request) {
		r.Response.Write("test")
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "123")
		t.Assert(client.GetContent("/test/test"), "1test23")
	})
}

func Test_Router_Hook_Fuzzy_Router(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	i := 1000
	pattern1 := "/:name/info"
	s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.SetParam("uid", i)
			i++
		},
	})
	s.BindHandler(pattern1, func(r *ghttp.Request) {
		r.Response.Write(r.Get("uid"))
	})

	pattern2 := "/{object}/list/{page}.java"
	s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) {
			r.Response.SetBuffer([]byte(
				fmt.Sprint(r.Get("object"), "&", r.Get("page"), "&", i),
			))
		},
	})
	s.BindHandler(pattern2, func(r *ghttp.Request) {
		r.Response.Write(r.Router.Uri)
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/john"), "Not Found")
		t.Assert(client.GetContent("/john/info"), "1000")
		t.Assert(client.GetContent("/john/info"), "1001")
		t.Assert(client.GetContent("/john/list/1.java"), "john&1&1002")
		t.Assert(client.GetContent("/john/list/2.java"), "john&2&1002")
	})
}

func Test_Router_Hook_Priority(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/priority/show", func(r *ghttp.Request) {
		r.Response.Write("show")
	})

	s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("1")
		},
	})
	s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("2")
		},
	})
	s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("3")
		},
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "Not Found")
		t.Assert(client.GetContent("/priority/show"), "312show")
		t.Assert(client.GetContent("/priority/any/any"), "2")
		t.Assert(client.GetContent("/priority/name"), "12")
	})
}

func Test_Router_Hook_Multi(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/multi-hook", func(r *ghttp.Request) {
		r.Response.Write("show")
	})

	s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("1")
		},
	})
	s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc{
		ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
			r.Response.Write("2")
		},
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/"), "Not Found")
		t.Assert(client.GetContent("/multi-hook"), "12show")
	})
}

func Test_Router_Hook_ExitAll(t *testing.T) {
	p, _ := ports.PopRand()
	s := g.Server(p)
	s.BindHandler("/test", func(r *ghttp.Request) {
		r.Response.Write("test")
	})
	s.Group("/hook", func(group *ghttp.RouterGroup) {
		group.Middleware(func(r *ghttp.Request) {
			r.Response.Write("1")
			r.Middleware.Next()
		})
		group.ALL("/test", func(r *ghttp.Request) {
			r.Response.Write("2")
		})
	})

	s.BindHookHandler("/hook/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
		r.Response.Write("hook")
		r.ExitAll()
	})
	s.SetPort(p)
	s.SetDumpRouterMap(false)
	s.Start()
	defer s.Shutdown()

	time.Sleep(100 * time.Millisecond)
	gtest.C(t, func(t *gtest.T) {
		client := ghttp.NewClient()
		client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))

		t.Assert(client.GetContent("/test"), "test")
		t.Assert(client.GetContent("/hook/test"), "hook")
	})
}
<|endoftext|>


<commit_before>package keycloakoidc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
)

//account defines properties an account in keycloak has
type account struct {
	ID            string `json:"id,omitempty"`
	Email         string `json:"email,omitempty"`
	EmailVerified bool   `json:"emailVerified,omitempty"`
	Username      string `json:"username,omitempty"`
	Enabled       bool   `json:"enabled,omitempty"`
	Name          string `json:"firstName,omitempty"`
	LastName      string `json:"lastName,omitempty"`
	Type          string
}

//Group defines properties a group in keycloak has
type Group struct {
	ID        string  `json:"id,omitempty"`
	Name      string  `json:"name,omitempty"`
	Subgroups []Group `json:"subGroups,omitempty"`
}

//KeyCloakClient implements a httpclient for keycloak
type KeyCloakClient struct {
	httpClient *http.Client
}

func (k *KeyCloakClient) searchPrincipals(searchTerm, principalType string, accessToken string, config *v32.OIDCConfig) ([]account, error) {
	var accounts []account
	sURL, err := getSearchURL(config.Issuer)
	if err != nil {
		return accounts, err
	}
	if principalType == "" || principalType == UserType {
		var userAccounts []account
		searchURL := fmt.Sprintf("%s/%ss?search=%s", sURL, UserType, searchTerm)
		search := URLEncoded(searchURL)

		b, statusCode, err := k.getFromKeyCloak(accessToken, search)
		if statusCode == 401 {
			return nil, nil
		}
		if err != nil {
			return accounts, fmt.Errorf("[keycloak oidc]: GET request failed, got status code: %d. url: %s, err: %s",
				statusCode, search, err)
		}
		if err := json.Unmarshal(b, &userAccounts); err != nil {
			logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
			return accounts, err
		}
		for _, u := range userAccounts {
			u.Type = UserType
			accounts = append(accounts, u)
		}
	}
	if principalType == "" || principalType == GroupType {
		var groups []Group
		searchURL := fmt.Sprintf("%s/%ss?search=%s", sURL, GroupType, searchTerm)
		search := URLEncoded(searchURL)

		b, statusCode, err := k.getFromKeyCloak(accessToken, search)
		if statusCode == 401 {
			return nil, nil
		}
		if err != nil {
			logrus.Errorf("[keycloak oidc]: GET url %v received error from github, err: %v", search, err)
			return accounts, err
		}
		if err := json.Unmarshal(b, &groups); err != nil {
			logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
			return accounts, err
		}
		for _, g := range groups {
			accounts = append(accounts, account{ID: g.ID, Name: g.Name, Type: GroupType})
			subGroups := getSubGroups(g)
			for _, sg := range subGroups {
				accounts = append(accounts, account{ID: sg.ID, Name: sg.Name, Type: GroupType})
			}
		}
	}
	return accounts, nil
}

func getSubGroups(group Group) []Group {
	var groups []Group
	if len(group.Subgroups) > 0 {
		for i, sub := range group.Subgroups {
			// setting an upper limit for how many subgroups we will loop through
			// this value was chosen at random so can be changed if needed
			if i < 100 {
				groups = append(groups, sub)
				groups = append(groups, getSubGroups(sub)...)
			}
		}
	}
	return groups
}

func (k *KeyCloakClient) getFromKeyCloakByID(principalID, accessToken, searchType string, config *v32.OIDCConfig) (account, error) {
	sURL, err := getSearchURL(config.Issuer)
	if err != nil {
		return account{}, nil
	}
	searchURL := fmt.Sprintf("%s/%s/%s", sURL, searchType, principalID)
	search := URLEncoded(searchURL)
	b, statusCode, err := k.getFromKeyCloak(accessToken, search)
	if err != nil {
		return account{}, fmt.Errorf("[keycloak oidc]: GET request failed, got status code: %d. url: %s, err: %s",
			statusCode, search, err)
	}
	var searchResult account
	if err := json.Unmarshal(b, &searchResult); err != nil {
		logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
		return searchResult, err
	}
	return searchResult, nil

}
func getSearchURL(issuer string) (string, error) {
	splitIssuer := strings.SplitAfter(issuer, "/auth/")
	return fmt.Sprintf(
		"%sadmin/%s",
		splitIssuer[0],
		splitIssuer[1],
	), nil
}

//URLEncoded encodes the string
func URLEncoded(str string) string {
	u, err := url.Parse(str)
	if err != nil {
		logrus.Errorf("[keycloak oidc]: Error encoding the url: %s, error: %v", str, err)
		return str
	}
	return u.String()
}

func (k *KeyCloakClient) getFromKeyCloak(accessToken, url string) ([]byte, int, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, 500, err
	}
	if accessToken == "" {
		return nil, 500, fmt.Errorf("[keycloak oidc]: GET request failed, missing authorization token")
	}
	req.Header.Add("Authorization", "Bearer "+accessToken)
	req.Header.Add("Accept", "application/json")
	resp, err := k.httpClient.Do(req)
	if err != nil {
		logrus.Errorf("[keycloak oidc]: received error from keycloak: %v", err)
		return nil, resp.StatusCode, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	switch resp.StatusCode {
	case 200:
	case 201:
	default:
		return b, resp.StatusCode, err
	}
	return b, resp.StatusCode, nil
}
<commit_msg>Add error check after reading body<commit_after>package keycloakoidc

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/sirupsen/logrus"
)

//account defines properties an account in keycloak has
type account struct {
	ID            string `json:"id,omitempty"`
	Email         string `json:"email,omitempty"`
	EmailVerified bool   `json:"emailVerified,omitempty"`
	Username      string `json:"username,omitempty"`
	Enabled       bool   `json:"enabled,omitempty"`
	Name          string `json:"firstName,omitempty"`
	LastName      string `json:"lastName,omitempty"`
	Type          string
}

//Group defines properties a group in keycloak has
type Group struct {
	ID        string  `json:"id,omitempty"`
	Name      string  `json:"name,omitempty"`
	Subgroups []Group `json:"subGroups,omitempty"`
}

//KeyCloakClient implements a httpclient for keycloak
type KeyCloakClient struct {
	httpClient *http.Client
}

func (k *KeyCloakClient) searchPrincipals(searchTerm, principalType string, accessToken string, config *v32.OIDCConfig) ([]account, error) {
	var accounts []account
	sURL, err := getSearchURL(config.Issuer)
	if err != nil {
		return accounts, err
	}
	if principalType == "" || principalType == UserType {
		var userAccounts []account
		searchURL := fmt.Sprintf("%s/%ss?search=%s", sURL, UserType, searchTerm)
		search := URLEncoded(searchURL)

		b, statusCode, err := k.getFromKeyCloak(accessToken, search)
		if statusCode == 401 {
			return nil, nil
		}
		if err != nil {
			return accounts, fmt.Errorf("[keycloak oidc]: GET request failed, got status code: %d. url: %s, err: %s",
				statusCode, search, err)
		}
		if err := json.Unmarshal(b, &userAccounts); err != nil {
			logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
			return accounts, err
		}
		for _, u := range userAccounts {
			u.Type = UserType
			accounts = append(accounts, u)
		}
	}
	if principalType == "" || principalType == GroupType {
		var groups []Group
		searchURL := fmt.Sprintf("%s/%ss?search=%s", sURL, GroupType, searchTerm)
		search := URLEncoded(searchURL)

		b, statusCode, err := k.getFromKeyCloak(accessToken, search)
		if statusCode == 401 {
			return nil, nil
		}
		if err != nil {
			logrus.Errorf("[keycloak oidc]: GET url %v received error from github, err: %v", search, err)
			return accounts, err
		}
		if err := json.Unmarshal(b, &groups); err != nil {
			logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
			return accounts, err
		}
		for _, g := range groups {
			accounts = append(accounts, account{ID: g.ID, Name: g.Name, Type: GroupType})
			subGroups := getSubGroups(g)
			for _, sg := range subGroups {
				accounts = append(accounts, account{ID: sg.ID, Name: sg.Name, Type: GroupType})
			}
		}
	}
	return accounts, nil
}

func getSubGroups(group Group) []Group {
	var groups []Group
	if len(group.Subgroups) > 0 {
		for i, sub := range group.Subgroups {
			// setting an upper limit for how many subgroups we will loop through
			// this value was chosen at random so can be changed if needed
			if i < 100 {
				groups = append(groups, sub)
				groups = append(groups, getSubGroups(sub)...)
			}
		}
	}
	return groups
}

func (k *KeyCloakClient) getFromKeyCloakByID(principalID, accessToken, searchType string, config *v32.OIDCConfig) (account, error) {
	sURL, err := getSearchURL(config.Issuer)
	if err != nil {
		return account{}, nil
	}
	searchURL := fmt.Sprintf("%s/%s/%s", sURL, searchType, principalID)
	search := URLEncoded(searchURL)
	b, statusCode, err := k.getFromKeyCloak(accessToken, search)
	if err != nil {
		return account{}, fmt.Errorf("[keycloak oidc]: GET request failed, got status code: %d. url: %s, err: %s",
			statusCode, search, err)
	}
	var searchResult account
	if err := json.Unmarshal(b, &searchResult); err != nil {
		logrus.Errorf("[keycloak oidc]: received error unmarshalling search results, err: %v", err)
		return searchResult, err
	}
	return searchResult, nil

}
func getSearchURL(issuer string) (string, error) {
	splitIssuer := strings.SplitAfter(issuer, "/auth/")
	return fmt.Sprintf(
		"%sadmin/%s",
		splitIssuer[0],
		splitIssuer[1],
	), nil
}

//URLEncoded encodes the string
func URLEncoded(str string) string {
	u, err := url.Parse(str)
	if err != nil {
		logrus.Errorf("[keycloak oidc]: Error encoding the url: %s, error: %v", str, err)
		return str
	}
	return u.String()
}

func (k *KeyCloakClient) getFromKeyCloak(accessToken, url string) ([]byte, int, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, 500, err
	}
	if accessToken == "" {
		return nil, 500, fmt.Errorf("[keycloak oidc]: GET request failed, missing authorization token")
	}
	req.Header.Add("Authorization", "Bearer "+accessToken)
	req.Header.Add("Accept", "application/json")
	resp, err := k.httpClient.Do(req)
	if err != nil {
		logrus.Errorf("[keycloak oidc]: received error from keycloak: %v", err)
		return nil, resp.StatusCode, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return b, resp.StatusCode, err
	}
	switch resp.StatusCode {
	case 200:
	case 201:
	default:
		return b, resp.StatusCode, err
	}
	return b, resp.StatusCode, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package openstack

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

	"github.com/juju/errors"
	"github.com/juju/utils"
	"gopkg.in/goose.v1/identity"
	"gopkg.in/ini.v1"

	"github.com/juju/juju/cloud"
)

type OpenstackCredentials struct{}

// CredentialSchemas is part of the environs.ProviderCredentials interface.
func (OpenstackCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
	return map[cloud.AuthType]cloud.CredentialSchema{
		cloud.UserPassAuthType: {
			{
				"username", cloud.CredentialAttr{Description: "The username to authenticate with."},
			}, {
				"password", cloud.CredentialAttr{
					Description: "The password for the specified username.",
					Hidden:      true,
				},
			}, {
				"tenant-name", cloud.CredentialAttr{Description: "The OpenStack tenant name."},
			},
			"domain-name": {
				Description: "The OpenStack domain name.",
				Optional:    true,
			},
		},
		cloud.AccessKeyAuthType: {
			{
				"access-key", cloud.CredentialAttr{Description: "The access key to authenticate with."},
			}, {
				"secret-key", cloud.CredentialAttr{
					Description: "The secret key to authenticate with.",
					Hidden:      true,
				},
			}, {
				"tenant-name", cloud.CredentialAttr{Description: "The OpenStack tenant name."},
			},
		},
	}
}

// DetectCredentials is part of the environs.ProviderCredentials interface.
func (c OpenstackCredentials) DetectCredentials() (*cloud.CloudCredential, error) {
	result := cloud.CloudCredential{
		AuthCredentials: make(map[string]cloud.Credential),
	}

	// Try just using environment variables
	creds, user, region, err := c.detectCredential()
	if err == nil {
		result.DefaultRegion = region
		result.AuthCredentials[user] = *creds
	}

	// Now look for .novarc file in home dir.
	novarc := filepath.Join(utils.Home(), ".novarc")
	novaInfo, err := ini.LooseLoad(novarc)
	if err != nil {
		return nil, errors.Annotate(err, "loading novarc file")
	}
	stripExport := regexp.MustCompile(`(?i)^\s*export\s*`)
	keyValues := novaInfo.Section(ini.DEFAULT_SECTION).KeysHash()
	if len(keyValues) > 0 {
		for k, v := range keyValues {
			k = stripExport.ReplaceAllString(k, "")
			os.Setenv(k, v)
		}
		creds, user, region, err := c.detectCredential()
		if err == nil {
			result.DefaultRegion = region
			result.AuthCredentials[user] = *creds
		}
	}
	if len(result.AuthCredentials) == 0 {
		return nil, errors.NotFoundf("openstack credentials")
	}
	return &result, nil
}

func (c OpenstackCredentials) detectCredential() (*cloud.Credential, string, string, error) {
	creds := identity.CredentialsFromEnv()
	if creds.TenantName == "" {
		return nil, "", "", errors.NewNotFound(nil, "OS_TENANT_NAME environment variable not set")
	}
	if creds.User == "" {
		return nil, "", "", errors.NewNotFound(nil, "neither OS_USERNAME nor OS_ACCESS_KEY environment variable not set")
	}
	if creds.Secrets == "" {
		return nil, "", "", errors.NewNotFound(nil, "neither OS_PASSWORD nor OS_SECRET_KEY environment variable not set")
	}

	user, err := utils.LocalUsername()
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	// If OS_USERNAME or NOVA_USERNAME is set, assume userpass.
	var credential cloud.Credential
	if os.Getenv("OS_USERNAME") != "" || os.Getenv("NOVA_USERNAME") != "" {
		user = creds.User
		credential = cloud.NewCredential(
			cloud.UserPassAuthType,
			map[string]string{
				"username":    creds.User,
				"password":    creds.Secrets,
				"tenant-name": creds.TenantName,
				"domain-name": creds.DomainName,
			},
		)
	} else {
		credential = cloud.NewCredential(
			cloud.AccessKeyAuthType,
			map[string]string{
				"access-key":  creds.User,
				"secret-key":  creds.Secrets,
				"tenant-name": creds.TenantName,
			},
		)
	}
	region := creds.Region
	if region == "" {
		region = "<unspecified>"
	}
	credential.Label = fmt.Sprintf("openstack region %q project %q user %q", region, creds.TenantName, user)
	return &credential, user, creds.Region, nil
}
<commit_msg>Fix merge issue<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package openstack

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

	"github.com/juju/errors"
	"github.com/juju/utils"
	"gopkg.in/goose.v1/identity"
	"gopkg.in/ini.v1"

	"github.com/juju/juju/cloud"
)

type OpenstackCredentials struct{}

// CredentialSchemas is part of the environs.ProviderCredentials interface.
func (OpenstackCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
	return map[cloud.AuthType]cloud.CredentialSchema{
		cloud.UserPassAuthType: {
			{
				"username", cloud.CredentialAttr{Description: "The username to authenticate with."},
			}, {
				"password", cloud.CredentialAttr{
					Description: "The password for the specified username.",
					Hidden:      true,
				},
			}, {
				"tenant-name", cloud.CredentialAttr{Description: "The OpenStack tenant name."},
			}, {
				"domain-name", cloud.CredentialAttr{
					Description: "The OpenStack domain name.",
					Optional:    true,
				},
			},
		},
		cloud.AccessKeyAuthType: {
			{
				"access-key", cloud.CredentialAttr{Description: "The access key to authenticate with."},
			}, {
				"secret-key", cloud.CredentialAttr{
					Description: "The secret key to authenticate with.",
					Hidden:      true,
				},
			}, {
				"tenant-name", cloud.CredentialAttr{Description: "The OpenStack tenant name."},
			},
		},
	}
}

// DetectCredentials is part of the environs.ProviderCredentials interface.
func (c OpenstackCredentials) DetectCredentials() (*cloud.CloudCredential, error) {
	result := cloud.CloudCredential{
		AuthCredentials: make(map[string]cloud.Credential),
	}

	// Try just using environment variables
	creds, user, region, err := c.detectCredential()
	if err == nil {
		result.DefaultRegion = region
		result.AuthCredentials[user] = *creds
	}

	// Now look for .novarc file in home dir.
	novarc := filepath.Join(utils.Home(), ".novarc")
	novaInfo, err := ini.LooseLoad(novarc)
	if err != nil {
		return nil, errors.Annotate(err, "loading novarc file")
	}
	stripExport := regexp.MustCompile(`(?i)^\s*export\s*`)
	keyValues := novaInfo.Section(ini.DEFAULT_SECTION).KeysHash()
	if len(keyValues) > 0 {
		for k, v := range keyValues {
			k = stripExport.ReplaceAllString(k, "")
			os.Setenv(k, v)
		}
		creds, user, region, err := c.detectCredential()
		if err == nil {
			result.DefaultRegion = region
			result.AuthCredentials[user] = *creds
		}
	}
	if len(result.AuthCredentials) == 0 {
		return nil, errors.NotFoundf("openstack credentials")
	}
	return &result, nil
}

func (c OpenstackCredentials) detectCredential() (*cloud.Credential, string, string, error) {
	creds := identity.CredentialsFromEnv()
	if creds.TenantName == "" {
		return nil, "", "", errors.NewNotFound(nil, "OS_TENANT_NAME environment variable not set")
	}
	if creds.User == "" {
		return nil, "", "", errors.NewNotFound(nil, "neither OS_USERNAME nor OS_ACCESS_KEY environment variable not set")
	}
	if creds.Secrets == "" {
		return nil, "", "", errors.NewNotFound(nil, "neither OS_PASSWORD nor OS_SECRET_KEY environment variable not set")
	}

	user, err := utils.LocalUsername()
	if err != nil {
		return nil, "", "", errors.Trace(err)
	}

	// If OS_USERNAME or NOVA_USERNAME is set, assume userpass.
	var credential cloud.Credential
	if os.Getenv("OS_USERNAME") != "" || os.Getenv("NOVA_USERNAME") != "" {
		user = creds.User
		credential = cloud.NewCredential(
			cloud.UserPassAuthType,
			map[string]string{
				"username":    creds.User,
				"password":    creds.Secrets,
				"tenant-name": creds.TenantName,
				"domain-name": creds.DomainName,
			},
		)
	} else {
		credential = cloud.NewCredential(
			cloud.AccessKeyAuthType,
			map[string]string{
				"access-key":  creds.User,
				"secret-key":  creds.Secrets,
				"tenant-name": creds.TenantName,
			},
		)
	}
	region := creds.Region
	if region == "" {
		region = "<unspecified>"
	}
	credential.Label = fmt.Sprintf("openstack region %q project %q user %q", region, creds.TenantName, user)
	return &credential, user, creds.Region, nil
}
<|endoftext|>


<commit_before>// +build windows

/*
Copyright 2019 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 dockershim

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"regexp"

	"golang.org/x/sys/windows/registry"

	dockertypes "github.com/docker/docker/api/types"
	dockercontainer "github.com/docker/docker/api/types/container"

	utilfeature "k8s.io/apiserver/pkg/util/feature"
	kubefeatures "k8s.io/kubernetes/pkg/features"
	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
)

type containerCreationCleanupInfo struct {
	gMSARegistryValueName string
}

// applyPlatformSpecificDockerConfig applies platform-specific configurations to a dockertypes.ContainerCreateConfig struct.
// The containerCreationCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCreationCleanup
// after the container has been created.
func (ds *dockerService) applyPlatformSpecificDockerConfig(request *runtimeapi.CreateContainerRequest, createConfig *dockertypes.ContainerCreateConfig) (*containerCreationCleanupInfo, error) {
	cleanupInfo := &containerCreationCleanupInfo{}

	if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WindowsGMSA) {
		if err := applyGMSAConfig(request.GetConfig(), createConfig, cleanupInfo); err != nil {
			return nil, err
		}
	}

	return cleanupInfo, nil
}

// applyGMSAConfig looks at the kuberuntime.GMSASpecContainerAnnotationKey container annotation; if present,
// it copies its contents to a unique registry value, and sets a SecurityOpt on the config pointing to that registry value.
// We use registry values instead of files since their location cannot change - as opposed to credential spec files,
// whose location could potentially change down the line, or even be unknown (eg if docker is not installed on the
// C: drive)
// When docker supports passing a credential spec's contents directly, we should switch to using that
// as it will avoid cluttering the registry.
func applyGMSAConfig(config *runtimeapi.ContainerConfig, createConfig *dockertypes.ContainerCreateConfig, cleanupInfo *containerCreationCleanupInfo) error {
	credSpec := config.Annotations[kuberuntime.GMSASpecContainerAnnotationKey]
	if credSpec == "" {
		return nil
	}

	valueName, err := copyGMSACredSpecToRegistryValue(credSpec)
	if err != nil {
		return err
	}

	if createConfig.HostConfig == nil {
		createConfig.HostConfig = &dockercontainer.HostConfig{}
	}

	createConfig.HostConfig.SecurityOpt = append(createConfig.HostConfig.SecurityOpt, "credentialspec=registry://"+valueName)
	cleanupInfo.gMSARegistryValueName = valueName

	return nil
}

const (
	// same as https://github.com/moby/moby/blob/93d994e29c9cc8d81f1b0477e28d705fa7e2cd72/daemon/oci_windows.go#L23
	credentialSpecRegistryLocation = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs`
	// the prefix for the registry values we write GMSA cred specs to
	gMSARegistryValueNamePrefix = "k8s-cred-spec-"
	// the number of random bytes to generate suffixes for registry value names
	gMSARegistryValueNameSuffixRandomBytes = 40
)

// registryKey is an interface wrapper around `registry.Key`,
// listing only the methods we care about here.
// It's mainly useful to easily allow mocking the registry in tests.
type registryKey interface {
	SetStringValue(name, value string) error
	DeleteValue(name string) error
	ReadValueNames(n int) ([]string, error)
	Close() error
}

var registryCreateKeyFunc = func(baseKey registry.Key, path string, access uint32) (registryKey, bool, error) {
	return registry.CreateKey(baseKey, path, access)
}

// randomReader is only meant to ever be overridden for testing purposes,
// same idea as for `registryKey` above
var randomReader = rand.Reader

// gMSARegistryValueNamesRegex is the regex used to detect gMSA cred spec
// registry values in `removeAllGMSARegistryValues` below.
var gMSARegistryValueNamesRegex = regexp.MustCompile(fmt.Sprintf("^%s[0-9a-f]{%d}$", gMSARegistryValueNamePrefix, 2*gMSARegistryValueNameSuffixRandomBytes))

// copyGMSACredSpecToRegistryKey copies the credential specs to a unique registry value, and returns its name.
func copyGMSACredSpecToRegistryValue(credSpec string) (string, error) {
	valueName, err := gMSARegistryValueName()
	if err != nil {
		return "", err
	}

	// write to the registry
	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return "", fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)
	}
	defer key.Close()
	if err = key.SetStringValue(valueName, credSpec); err != nil {
		return "", fmt.Errorf("unable to write into registry value %q/%q: %v", credentialSpecRegistryLocation, valueName, err)
	}

	return valueName, nil
}

// gMSARegistryValueName computes the name of the registry value where to store the GMSA cred spec contents.
// The value's name is a purely random suffix appended to `gMSARegistryValueNamePrefix`.
func gMSARegistryValueName() (string, error) {
	randomSuffix, err := randomString(gMSARegistryValueNameSuffixRandomBytes)

	if err != nil {
		return "", fmt.Errorf("error when generating gMSA registry value name: %v", err)
	}

	return gMSARegistryValueNamePrefix + randomSuffix, nil
}

// randomString returns a random hex string.
func randomString(length int) (string, error) {
	randBytes := make([]byte, length)

	if n, err := randomReader.Read(randBytes); err != nil || n != length {
		if err == nil {
			err = fmt.Errorf("only got %v random bytes, expected %v", n, length)
		}
		return "", fmt.Errorf("unable to generate random string: %v", err)
	}

	return hex.EncodeToString(randBytes), nil
}

// performPlatformSpecificContainerCreationCleanup is responsible for doing any platform-specific cleanup
// after a container creation. Any errors it returns are simply logged, but do not fail the container
// creation.
func (ds *dockerService) performPlatformSpecificContainerCreationCleanup(cleanupInfo *containerCreationCleanupInfo) (errors []error) {
	if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WindowsGMSA) {
		if err := removeGMSARegistryValue(cleanupInfo); err != nil {
			errors = append(errors, err)
		}
	}

	return
}

func removeGMSARegistryValue(cleanupInfo *containerCreationCleanupInfo) error {
	if cleanupInfo == nil || cleanupInfo.gMSARegistryValueName == "" {
		return nil
	}

	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)
	}
	defer key.Close()
	if err = key.DeleteValue(cleanupInfo.gMSARegistryValueName); err != nil {
		return fmt.Errorf("unable to remove registry value %q/%q: %v", credentialSpecRegistryLocation, cleanupInfo.gMSARegistryValueName, err)
	}

	return nil
}

// platformSpecificContainerCreationInitCleanup is called when dockershim
// is starting, and is meant to clean up any cruft left by previous runs
// creating containers.
// Errors are simply logged, but don't prevent dockershim from starting.
func (ds *dockerService) platformSpecificContainerCreationInitCleanup() (errors []error) {
	if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WindowsGMSA) {
		errors = removeAllGMSARegistryValues()
	}
	return
}

func removeAllGMSARegistryValues() (errors []error) {
	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return []error{fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)}
	}
	defer key.Close()

	valueNames, err := key.ReadValueNames(0)
	if err != nil {
		return []error{fmt.Errorf("unable to list values under registry key %q: %v", credentialSpecRegistryLocation, err)}
	}

	for _, valueName := range valueNames {
		if gMSARegistryValueNamesRegex.MatchString(valueName) {
			if err = key.DeleteValue(valueName); err != nil {
				errors = append(errors, fmt.Errorf("unable to remove registry value %q/%q: %v", credentialSpecRegistryLocation, valueName, err))
			}
		}
	}

	return
}
<commit_msg>Review comments<commit_after>// +build windows

/*
Copyright 2019 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 dockershim

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"regexp"

	"golang.org/x/sys/windows/registry"

	dockertypes "github.com/docker/docker/api/types"
	dockercontainer "github.com/docker/docker/api/types/container"

	runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
)

type containerCreationCleanupInfo struct {
	gMSARegistryValueName string
}

// applyPlatformSpecificDockerConfig applies platform-specific configurations to a dockertypes.ContainerCreateConfig struct.
// The containerCreationCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCreationCleanup
// after the container has been created.
func (ds *dockerService) applyPlatformSpecificDockerConfig(request *runtimeapi.CreateContainerRequest, createConfig *dockertypes.ContainerCreateConfig) (*containerCreationCleanupInfo, error) {
	cleanupInfo := &containerCreationCleanupInfo{}

	if err := applyGMSAConfig(request.GetConfig(), createConfig, cleanupInfo); err != nil {
		return nil, err
	}

	return cleanupInfo, nil
}

// applyGMSAConfig looks at the kuberuntime.GMSASpecContainerAnnotationKey container annotation; if present,
// it copies its contents to a unique registry value, and sets a SecurityOpt on the config pointing to that registry value.
// We use registry values instead of files since their location cannot change - as opposed to credential spec files,
// whose location could potentially change down the line, or even be unknown (eg if docker is not installed on the
// C: drive)
// When docker supports passing a credential spec's contents directly, we should switch to using that
// as it will avoid cluttering the registry - there is a moby PR out for this:
// https://github.com/moby/moby/pull/38777
func applyGMSAConfig(config *runtimeapi.ContainerConfig, createConfig *dockertypes.ContainerCreateConfig, cleanupInfo *containerCreationCleanupInfo) error {
	credSpec := config.Annotations[kuberuntime.GMSASpecContainerAnnotationKey]
	if credSpec == "" {
		return nil
	}

	valueName, err := copyGMSACredSpecToRegistryValue(credSpec)
	if err != nil {
		return err
	}

	if createConfig.HostConfig == nil {
		createConfig.HostConfig = &dockercontainer.HostConfig{}
	}

	createConfig.HostConfig.SecurityOpt = append(createConfig.HostConfig.SecurityOpt, "credentialspec=registry://"+valueName)
	cleanupInfo.gMSARegistryValueName = valueName

	return nil
}

const (
	// same as https://github.com/moby/moby/blob/93d994e29c9cc8d81f1b0477e28d705fa7e2cd72/daemon/oci_windows.go#L23
	credentialSpecRegistryLocation = `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs`
	// the prefix for the registry values we write GMSA cred specs to
	gMSARegistryValueNamePrefix = "k8s-cred-spec-"
	// the number of random bytes to generate suffixes for registry value names
	gMSARegistryValueNameSuffixRandomBytes = 40
)

// registryKey is an interface wrapper around `registry.Key`,
// listing only the methods we care about here.
// It's mainly useful to easily allow mocking the registry in tests.
type registryKey interface {
	SetStringValue(name, value string) error
	DeleteValue(name string) error
	ReadValueNames(n int) ([]string, error)
	Close() error
}

var registryCreateKeyFunc = func(baseKey registry.Key, path string, access uint32) (registryKey, bool, error) {
	return registry.CreateKey(baseKey, path, access)
}

// randomReader is only meant to ever be overridden for testing purposes,
// same idea as for `registryKey` above
var randomReader = rand.Reader

// gMSARegistryValueNamesRegex is the regex used to detect gMSA cred spec
// registry values in `removeAllGMSARegistryValues` below.
var gMSARegistryValueNamesRegex = regexp.MustCompile(fmt.Sprintf("^%s[0-9a-f]{%d}$", gMSARegistryValueNamePrefix, 2*gMSARegistryValueNameSuffixRandomBytes))

// copyGMSACredSpecToRegistryKey copies the credential specs to a unique registry value, and returns its name.
func copyGMSACredSpecToRegistryValue(credSpec string) (string, error) {
	valueName, err := gMSARegistryValueName()
	if err != nil {
		return "", err
	}

	// write to the registry
	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return "", fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)
	}
	defer key.Close()
	if err = key.SetStringValue(valueName, credSpec); err != nil {
		return "", fmt.Errorf("unable to write into registry value %q/%q: %v", credentialSpecRegistryLocation, valueName, err)
	}

	return valueName, nil
}

// gMSARegistryValueName computes the name of the registry value where to store the GMSA cred spec contents.
// The value's name is a purely random suffix appended to `gMSARegistryValueNamePrefix`.
func gMSARegistryValueName() (string, error) {
	randomSuffix, err := randomString(gMSARegistryValueNameSuffixRandomBytes)

	if err != nil {
		return "", fmt.Errorf("error when generating gMSA registry value name: %v", err)
	}

	return gMSARegistryValueNamePrefix + randomSuffix, nil
}

// randomString returns a random hex string.
func randomString(length int) (string, error) {
	randBytes := make([]byte, length)

	if n, err := randomReader.Read(randBytes); err != nil || n != length {
		if err == nil {
			err = fmt.Errorf("only got %v random bytes, expected %v", n, length)
		}
		return "", fmt.Errorf("unable to generate random string: %v", err)
	}

	return hex.EncodeToString(randBytes), nil
}

// performPlatformSpecificContainerCreationCleanup is responsible for doing any platform-specific cleanup
// after a container creation. Any errors it returns are simply logged, but do not fail the container
// creation.
func (ds *dockerService) performPlatformSpecificContainerCreationCleanup(cleanupInfo *containerCreationCleanupInfo) (errors []error) {
	if err := removeGMSARegistryValue(cleanupInfo); err != nil {
		errors = append(errors, err)
	}

	return
}

func removeGMSARegistryValue(cleanupInfo *containerCreationCleanupInfo) error {
	if cleanupInfo == nil || cleanupInfo.gMSARegistryValueName == "" {
		return nil
	}

	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)
	}
	defer key.Close()
	if err = key.DeleteValue(cleanupInfo.gMSARegistryValueName); err != nil {
		return fmt.Errorf("unable to remove registry value %q/%q: %v", credentialSpecRegistryLocation, cleanupInfo.gMSARegistryValueName, err)
	}

	return nil
}

// platformSpecificContainerCreationInitCleanup is called when dockershim
// is starting, and is meant to clean up any cruft left by previous runs
// creating containers.
// Errors are simply logged, but don't prevent dockershim from starting.
func (ds *dockerService) platformSpecificContainerCreationInitCleanup() (errors []error) {
	return removeAllGMSARegistryValues()
}

func removeAllGMSARegistryValues() (errors []error) {
	key, _, err := registryCreateKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.SET_VALUE)
	if err != nil {
		return []error{fmt.Errorf("unable to open registry key %q: %v", credentialSpecRegistryLocation, err)}
	}
	defer key.Close()

	valueNames, err := key.ReadValueNames(0)
	if err != nil {
		return []error{fmt.Errorf("unable to list values under registry key %q: %v", credentialSpecRegistryLocation, err)}
	}

	for _, valueName := range valueNames {
		if gMSARegistryValueNamesRegex.MatchString(valueName) {
			if err = key.DeleteValue(valueName); err != nil {
				errors = append(errors, fmt.Errorf("unable to remove registry value %q/%q: %v", credentialSpecRegistryLocation, valueName, err))
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package collectors

import (
	"strings"
	"testing"

	"github.com/prometheus/client_golang/prometheus"

	"k8s.io/component-base/metrics/testutil"
	statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
)

func TestNoMetricsCollected(t *testing.T) {
	ch := make(chan prometheus.Metric)

	collector := &logMetricsCollector{
		podStats: func() ([]statsapi.PodStats, error) {
			return []statsapi.PodStats{}, nil
		},
	}
	collector.Collect(ch)

	num := len(ch)
	if num != 0 {
		t.Fatalf("Channel expected to be empty, but received %d", num)
	}
}

func TestMetricsCollected(t *testing.T) {
	size := uint64(18)
	collector := &logMetricsCollector{
		podStats: func() ([]statsapi.PodStats, error) {
			return []statsapi.PodStats{
				{
					PodRef: statsapi.PodReference{
						Namespace: "some-namespace",
						Name:      "podName1",
						UID:       "UID_some_id",
					},
					Containers: []statsapi.ContainerStats{
						{
							Name: "containerName1",
							Logs: &statsapi.FsStats{
								UsedBytes: &size,
							},
						},
					},
				},
			}, nil
		},
	}

	err := testutil.CollectAndCompare(collector, strings.NewReader(`
		# HELP kubelet_container_log_filesystem_used_bytes Bytes used by the container's logs on the filesystem.
		# TYPE kubelet_container_log_filesystem_used_bytes gauge
		kubelet_container_log_filesystem_used_bytes{container="containerName1",namespace="some-namespace",pod="podName1",uid="UID_some_id"} 18
`), "kubelet_container_log_filesystem_used_bytes")
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Refactor UT with testutil from k/k.<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package collectors

import (
	"strings"
	"testing"

	"k8s.io/component-base/metrics/testutil"
	statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
)

func TestNoMetricsCollected(t *testing.T) {
	collector := &logMetricsCollector{
		podStats: func() ([]statsapi.PodStats, error) {
			return []statsapi.PodStats{}, nil
		},
	}

	if err := testutil.CollectAndCompare(collector, strings.NewReader(""), ""); err != nil {
		t.Fatal(err)
	}
}

func TestMetricsCollected(t *testing.T) {
	size := uint64(18)
	collector := &logMetricsCollector{
		podStats: func() ([]statsapi.PodStats, error) {
			return []statsapi.PodStats{
				{
					PodRef: statsapi.PodReference{
						Namespace: "some-namespace",
						Name:      "podName1",
						UID:       "UID_some_id",
					},
					Containers: []statsapi.ContainerStats{
						{
							Name: "containerName1",
							Logs: &statsapi.FsStats{
								UsedBytes: &size,
							},
						},
					},
				},
			}, nil
		},
	}

	err := testutil.CollectAndCompare(collector, strings.NewReader(`
		# HELP kubelet_container_log_filesystem_used_bytes Bytes used by the container's logs on the filesystem.
		# TYPE kubelet_container_log_filesystem_used_bytes gauge
		kubelet_container_log_filesystem_used_bytes{container="containerName1",namespace="some-namespace",pod="podName1",uid="UID_some_id"} 18
`), "kubelet_container_log_filesystem_used_bytes")
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor 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 transport

import (
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/syserr"
	"gvisor.dev/gvisor/pkg/tcpip"
	"gvisor.dev/gvisor/pkg/waiter"
)

// connectionlessEndpoint is a unix endpoint for unix sockets that support operating in
// a connectionless fashon.
//
// Specifically, this means datagram unix sockets not created with
// socketpair(2).
//
// +stateify savable
type connectionlessEndpoint struct {
	baseEndpoint
}

var (
	_ = BoundEndpoint((*connectionlessEndpoint)(nil))
	_ = Endpoint((*connectionlessEndpoint)(nil))
)

// NewConnectionless creates a new unbound dgram endpoint.
func NewConnectionless(ctx context.Context) Endpoint {
	ep := &connectionlessEndpoint{baseEndpoint{Queue: &waiter.Queue{}}}
	q := queue{ReaderQueue: ep.Queue, WriterQueue: &waiter.Queue{}, limit: defaultBufferSize}
	q.InitRefs()
	ep.receiver = &queueReceiver{readQueue: &q}
	ep.ops.InitHandler(ep, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits)
	ep.ops.SetSendBufferSize(defaultBufferSize, false /* notify */)
	ep.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */)
	return ep
}

// isBound returns true iff the endpoint is bound.
func (e *connectionlessEndpoint) isBound() bool {
	return e.path != ""
}

// Close puts the endpoint in a closed state and frees all resources associated
// with it.
func (e *connectionlessEndpoint) Close(ctx context.Context) {
	e.Lock()
	if e.connected != nil {
		e.connected.Release(ctx)
		e.connected = nil
	}

	if e.isBound() {
		e.path = ""
	}

	e.receiver.CloseRecv()
	r := e.receiver
	e.receiver = nil
	e.Unlock()

	r.CloseNotify()
	r.Release(ctx)
}

// BidirectionalConnect implements BoundEndpoint.BidirectionalConnect.
func (e *connectionlessEndpoint) BidirectionalConnect(ctx context.Context, ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error {
	return syserr.ErrConnectionRefused
}

// UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect.
func (e *connectionlessEndpoint) UnidirectionalConnect(ctx context.Context) (ConnectedEndpoint, *syserr.Error) {
	e.Lock()
	r := e.receiver
	e.Unlock()
	if r == nil {
		return nil, syserr.ErrConnectionRefused
	}
	q := r.(*queueReceiver).readQueue
	if !q.TryIncRef() {
		return nil, syserr.ErrConnectionRefused
	}
	return &connectedEndpoint{
		endpoint:   e,
		writeQueue: q,
	}, nil
}

// SendMsg writes data and a control message to the specified endpoint.
// This method does not block if the data cannot be written.
func (e *connectionlessEndpoint) SendMsg(ctx context.Context, data [][]byte, c ControlMessages, to BoundEndpoint) (int64, *syserr.Error) {
	if to == nil {
		return e.baseEndpoint.SendMsg(ctx, data, c, nil)
	}

	connected, err := to.UnidirectionalConnect(ctx)
	if err != nil {
		return 0, syserr.ErrInvalidEndpointState
	}
	defer connected.Release(ctx)

	e.Lock()
	n, notify, err := connected.Send(ctx, data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)})
	e.Unlock()

	if notify {
		connected.SendNotify()
	}

	return n, err
}

// Type implements Endpoint.Type.
func (e *connectionlessEndpoint) Type() linux.SockType {
	return linux.SOCK_DGRAM
}

// Connect attempts to connect directly to server.
func (e *connectionlessEndpoint) Connect(ctx context.Context, server BoundEndpoint) *syserr.Error {
	connected, err := server.UnidirectionalConnect(ctx)
	if err != nil {
		return err
	}

	e.Lock()
	if e.connected != nil {
		e.connected.Release(ctx)
	}
	e.connected = connected
	e.Unlock()

	return nil
}

// Listen starts listening on the connection.
func (*connectionlessEndpoint) Listen(context.Context, int) *syserr.Error {
	return syserr.ErrNotSupported
}

// Accept accepts a new connection.
func (*connectionlessEndpoint) Accept(context.Context, *tcpip.FullAddress) (Endpoint, *syserr.Error) {
	return nil, syserr.ErrNotSupported
}

// Bind binds the connection.
//
// For Unix endpoints, this _only sets the address associated with the socket_.
// Work associated with sockets in the filesystem or finding those sockets must
// be done by a higher level.
//
// Bind will fail only if the socket is connected, bound or the passed address
// is invalid (the empty string).
func (e *connectionlessEndpoint) Bind(addr tcpip.FullAddress) *syserr.Error {
	e.Lock()
	defer e.Unlock()
	if e.isBound() {
		return syserr.ErrAlreadyBound
	}
	if addr.Addr == "" {
		// The empty string is not permitted.
		return syserr.ErrBadLocalAddress
	}

	// Save the bound address.
	e.path = string(addr.Addr)
	return nil
}

// Readiness returns the current readiness of the endpoint. For example, if
// waiter.EventIn is set, the endpoint is immediately readable.
func (e *connectionlessEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
	e.Lock()
	defer e.Unlock()

	ready := waiter.EventMask(0)
	if mask&waiter.ReadableEvents != 0 && e.receiver.Readable() {
		ready |= waiter.ReadableEvents
	}

	if e.Connected() {
		if mask&waiter.WritableEvents != 0 && e.connected.Writable() {
			ready |= waiter.WritableEvents
		}
	}

	return ready
}

// State implements socket.Socket.State.
func (e *connectionlessEndpoint) State() uint32 {
	e.Lock()
	defer e.Unlock()

	switch {
	case e.isBound():
		return linux.SS_UNCONNECTED
	case e.Connected():
		return linux.SS_CONNECTING
	default:
		return linux.SS_DISCONNECTING
	}
}

// OnSetSendBufferSize implements tcpip.SocketOptionsHandler.OnSetSendBufferSize.
func (e *connectionlessEndpoint) OnSetSendBufferSize(v int64) (newSz int64) {
	e.Lock()
	defer e.Unlock()
	if e.Connected() {
		return e.baseEndpoint.connected.SetSendBufferSize(v)
	}
	return v
}

// WakeupWriters implements tcpip.SocketOptionsHandler.WakeupWriters.
func (e *connectionlessEndpoint) WakeupWriters() {}
<commit_msg>sentry/socket: don't release a connected enpoint under the endpoint mutex<commit_after>// Copyright 2018 The gVisor 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 transport

import (
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/syserr"
	"gvisor.dev/gvisor/pkg/tcpip"
	"gvisor.dev/gvisor/pkg/waiter"
)

// connectionlessEndpoint is a unix endpoint for unix sockets that support operating in
// a connectionless fashon.
//
// Specifically, this means datagram unix sockets not created with
// socketpair(2).
//
// +stateify savable
type connectionlessEndpoint struct {
	baseEndpoint
}

var (
	_ = BoundEndpoint((*connectionlessEndpoint)(nil))
	_ = Endpoint((*connectionlessEndpoint)(nil))
)

// NewConnectionless creates a new unbound dgram endpoint.
func NewConnectionless(ctx context.Context) Endpoint {
	ep := &connectionlessEndpoint{baseEndpoint{Queue: &waiter.Queue{}}}
	q := queue{ReaderQueue: ep.Queue, WriterQueue: &waiter.Queue{}, limit: defaultBufferSize}
	q.InitRefs()
	ep.receiver = &queueReceiver{readQueue: &q}
	ep.ops.InitHandler(ep, &stackHandler{}, getSendBufferLimits, getReceiveBufferLimits)
	ep.ops.SetSendBufferSize(defaultBufferSize, false /* notify */)
	ep.ops.SetReceiveBufferSize(defaultBufferSize, false /* notify */)
	return ep
}

// isBound returns true iff the endpoint is bound.
func (e *connectionlessEndpoint) isBound() bool {
	return e.path != ""
}

// Close puts the endpoint in a closed state and frees all resources associated
// with it.
func (e *connectionlessEndpoint) Close(ctx context.Context) {
	e.Lock()
	connected := e.connected
	e.connected = nil

	if e.isBound() {
		e.path = ""
	}

	e.receiver.CloseRecv()
	r := e.receiver
	e.receiver = nil
	e.Unlock()

	if connected != nil {
		connected.Release(ctx)
	}
	r.CloseNotify()
	r.Release(ctx)
}

// BidirectionalConnect implements BoundEndpoint.BidirectionalConnect.
func (e *connectionlessEndpoint) BidirectionalConnect(ctx context.Context, ce ConnectingEndpoint, returnConnect func(Receiver, ConnectedEndpoint)) *syserr.Error {
	return syserr.ErrConnectionRefused
}

// UnidirectionalConnect implements BoundEndpoint.UnidirectionalConnect.
func (e *connectionlessEndpoint) UnidirectionalConnect(ctx context.Context) (ConnectedEndpoint, *syserr.Error) {
	e.Lock()
	r := e.receiver
	e.Unlock()
	if r == nil {
		return nil, syserr.ErrConnectionRefused
	}
	q := r.(*queueReceiver).readQueue
	if !q.TryIncRef() {
		return nil, syserr.ErrConnectionRefused
	}
	return &connectedEndpoint{
		endpoint:   e,
		writeQueue: q,
	}, nil
}

// SendMsg writes data and a control message to the specified endpoint.
// This method does not block if the data cannot be written.
func (e *connectionlessEndpoint) SendMsg(ctx context.Context, data [][]byte, c ControlMessages, to BoundEndpoint) (int64, *syserr.Error) {
	if to == nil {
		return e.baseEndpoint.SendMsg(ctx, data, c, nil)
	}

	connected, err := to.UnidirectionalConnect(ctx)
	if err != nil {
		return 0, syserr.ErrInvalidEndpointState
	}
	defer connected.Release(ctx)

	e.Lock()
	n, notify, err := connected.Send(ctx, data, c, tcpip.FullAddress{Addr: tcpip.Address(e.path)})
	e.Unlock()

	if notify {
		connected.SendNotify()
	}

	return n, err
}

// Type implements Endpoint.Type.
func (e *connectionlessEndpoint) Type() linux.SockType {
	return linux.SOCK_DGRAM
}

// Connect attempts to connect directly to server.
func (e *connectionlessEndpoint) Connect(ctx context.Context, server BoundEndpoint) *syserr.Error {
	connected, err := server.UnidirectionalConnect(ctx)
	if err != nil {
		return err
	}

	e.Lock()
	if e.connected != nil {
		e.connected.Release(ctx)
	}
	e.connected = connected
	e.Unlock()

	return nil
}

// Listen starts listening on the connection.
func (*connectionlessEndpoint) Listen(context.Context, int) *syserr.Error {
	return syserr.ErrNotSupported
}

// Accept accepts a new connection.
func (*connectionlessEndpoint) Accept(context.Context, *tcpip.FullAddress) (Endpoint, *syserr.Error) {
	return nil, syserr.ErrNotSupported
}

// Bind binds the connection.
//
// For Unix endpoints, this _only sets the address associated with the socket_.
// Work associated with sockets in the filesystem or finding those sockets must
// be done by a higher level.
//
// Bind will fail only if the socket is connected, bound or the passed address
// is invalid (the empty string).
func (e *connectionlessEndpoint) Bind(addr tcpip.FullAddress) *syserr.Error {
	e.Lock()
	defer e.Unlock()
	if e.isBound() {
		return syserr.ErrAlreadyBound
	}
	if addr.Addr == "" {
		// The empty string is not permitted.
		return syserr.ErrBadLocalAddress
	}

	// Save the bound address.
	e.path = string(addr.Addr)
	return nil
}

// Readiness returns the current readiness of the endpoint. For example, if
// waiter.EventIn is set, the endpoint is immediately readable.
func (e *connectionlessEndpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
	e.Lock()
	defer e.Unlock()

	ready := waiter.EventMask(0)
	if mask&waiter.ReadableEvents != 0 && e.receiver.Readable() {
		ready |= waiter.ReadableEvents
	}

	if e.Connected() {
		if mask&waiter.WritableEvents != 0 && e.connected.Writable() {
			ready |= waiter.WritableEvents
		}
	}

	return ready
}

// State implements socket.Socket.State.
func (e *connectionlessEndpoint) State() uint32 {
	e.Lock()
	defer e.Unlock()

	switch {
	case e.isBound():
		return linux.SS_UNCONNECTED
	case e.Connected():
		return linux.SS_CONNECTING
	default:
		return linux.SS_DISCONNECTING
	}
}

// OnSetSendBufferSize implements tcpip.SocketOptionsHandler.OnSetSendBufferSize.
func (e *connectionlessEndpoint) OnSetSendBufferSize(v int64) (newSz int64) {
	e.Lock()
	defer e.Unlock()
	if e.Connected() {
		return e.baseEndpoint.connected.SetSendBufferSize(v)
	}
	return v
}

// WakeupWriters implements tcpip.SocketOptionsHandler.WakeupWriters.
func (e *connectionlessEndpoint) WakeupWriters() {}
<|endoftext|>