mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 01:06:27 +00:00
Merge pull request #19447 from Clarifai/ecr
Add ECR credential provider
This commit is contained in:
commit
3a15a374ba
15
Godeps/Godeps.json
generated
15
Godeps/Godeps.json
generated
@ -62,6 +62,16 @@
|
|||||||
"Comment": "v1.0.7",
|
"Comment": "v1.0.7",
|
||||||
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
|
||||||
|
"Comment": "v1.0.7",
|
||||||
|
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
|
||||||
|
"Comment": "v1.0.7",
|
||||||
|
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
|
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||||
"Comment": "v1.0.7",
|
"Comment": "v1.0.7",
|
||||||
@ -97,6 +107,11 @@
|
|||||||
"Comment": "v1.0.7",
|
"Comment": "v1.0.7",
|
||||||
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/aws/aws-sdk-go/service/ecr",
|
||||||
|
"Comment": "v1.0.7",
|
||||||
|
"Rev": "bf2f8fe7f45e68017086d069498638893feddf64"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/aws/aws-sdk-go/service/elb",
|
"ImportPath": "github.com/aws/aws-sdk-go/service/elb",
|
||||||
"Comment": "v1.0.7",
|
"Comment": "v1.0.7",
|
||||||
|
243
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
generated
vendored
Normal file
243
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go
generated
vendored
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
// Package jsonutil provides JSON serialisation of AWS requests and responses.
|
||||||
|
package jsonutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeType = reflect.ValueOf(time.Time{}).Type()
|
||||||
|
var byteSliceType = reflect.ValueOf([]byte{}).Type()
|
||||||
|
|
||||||
|
// BuildJSON builds a JSON string for a given object v.
|
||||||
|
func BuildJSON(v interface{}) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
err := buildAny(reflect.ValueOf(v), &buf, "")
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
value = reflect.Indirect(value)
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vtype := value.Type()
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch vtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
// also it can't be a time object
|
||||||
|
if value.Type() != timeType {
|
||||||
|
t = "structure"
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// also it can't be a byte slice
|
||||||
|
if _, ok := value.Interface().([]byte); !ok {
|
||||||
|
t = "list"
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := vtype.FieldByName("_"); ok {
|
||||||
|
tag = field.Tag
|
||||||
|
}
|
||||||
|
return buildStruct(value, buf, tag)
|
||||||
|
case "list":
|
||||||
|
return buildList(value, buf, tag)
|
||||||
|
case "map":
|
||||||
|
return buildMap(value, buf, tag)
|
||||||
|
default:
|
||||||
|
return buildScalar(value, buf, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap payloads
|
||||||
|
if payload := tag.Get("payload"); payload != "" {
|
||||||
|
field, _ := value.Type().FieldByName(payload)
|
||||||
|
tag = field.Tag
|
||||||
|
value = elemOf(value.FieldByName(payload))
|
||||||
|
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteByte('{')
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
first := true
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
member := value.Field(i)
|
||||||
|
if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
|
||||||
|
continue // ignore unset fields
|
||||||
|
}
|
||||||
|
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
if field.Tag.Get("json") == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if field.Tag.Get("location") != "" {
|
||||||
|
continue // ignore non-body elements
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what this field is called
|
||||||
|
name := field.Name
|
||||||
|
if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%q:", name)
|
||||||
|
|
||||||
|
err := buildAny(member, buf, field.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("}")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
buf.WriteString("[")
|
||||||
|
|
||||||
|
for i := 0; i < value.Len(); i++ {
|
||||||
|
buildAny(value.Index(i), buf, "")
|
||||||
|
|
||||||
|
if i < value.Len()-1 {
|
||||||
|
buf.WriteString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("]")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type sortedValues []reflect.Value
|
||||||
|
|
||||||
|
func (sv sortedValues) Len() int { return len(sv) }
|
||||||
|
func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||||
|
func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() }
|
||||||
|
|
||||||
|
func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
buf.WriteString("{")
|
||||||
|
|
||||||
|
var sv sortedValues = value.MapKeys()
|
||||||
|
sort.Sort(sv)
|
||||||
|
|
||||||
|
for i, k := range sv {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(buf, "%q:", k)
|
||||||
|
buildAny(value.MapIndex(k), buf, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString("}")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
writeString(value.String(), buf)
|
||||||
|
case reflect.Bool:
|
||||||
|
buf.WriteString(strconv.FormatBool(value.Bool()))
|
||||||
|
case reflect.Int64:
|
||||||
|
buf.WriteString(strconv.FormatInt(value.Int(), 10))
|
||||||
|
case reflect.Float64:
|
||||||
|
buf.WriteString(strconv.FormatFloat(value.Float(), 'f', -1, 64))
|
||||||
|
default:
|
||||||
|
switch value.Type() {
|
||||||
|
case timeType:
|
||||||
|
converted := value.Interface().(time.Time)
|
||||||
|
buf.WriteString(strconv.FormatInt(converted.UTC().Unix(), 10))
|
||||||
|
case byteSliceType:
|
||||||
|
if !value.IsNil() {
|
||||||
|
converted := value.Interface().([]byte)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
if len(converted) < 1024 {
|
||||||
|
// for small buffers, using Encode directly is much faster.
|
||||||
|
dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted)))
|
||||||
|
base64.StdEncoding.Encode(dst, converted)
|
||||||
|
buf.Write(dst)
|
||||||
|
} else {
|
||||||
|
// for large buffers, avoid unnecessary extra temporary
|
||||||
|
// buffer space.
|
||||||
|
enc := base64.NewEncoder(base64.StdEncoding, buf)
|
||||||
|
enc.Write(converted)
|
||||||
|
enc.Close()
|
||||||
|
}
|
||||||
|
buf.WriteByte('"')
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeString(s string, buf *bytes.Buffer) {
|
||||||
|
buf.WriteByte('"')
|
||||||
|
for _, r := range s {
|
||||||
|
if r == '"' {
|
||||||
|
buf.WriteString(`\"`)
|
||||||
|
} else if r == '\\' {
|
||||||
|
buf.WriteString(`\\`)
|
||||||
|
} else if r == '\b' {
|
||||||
|
buf.WriteString(`\b`)
|
||||||
|
} else if r == '\f' {
|
||||||
|
buf.WriteString(`\f`)
|
||||||
|
} else if r == '\r' {
|
||||||
|
buf.WriteString(`\r`)
|
||||||
|
} else if r == '\t' {
|
||||||
|
buf.WriteString(`\t`)
|
||||||
|
} else if r == '\n' {
|
||||||
|
buf.WriteString(`\n`)
|
||||||
|
} else if r < 32 {
|
||||||
|
fmt.Fprintf(buf, "\\u%0.4x", r)
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('"')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the reflection element of a value, if it is a pointer.
|
||||||
|
func elemOf(value reflect.Value) reflect.Value {
|
||||||
|
for value.Kind() == reflect.Ptr {
|
||||||
|
value = value.Elem()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
213
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go
generated
vendored
Normal file
213
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package jsonutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnmarshalJSON reads a stream and unmarshals the results in object v.
|
||||||
|
func UnmarshalJSON(v interface{}, stream io.Reader) error {
|
||||||
|
var out interface{}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(stream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalAny(reflect.ValueOf(v), out, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
vtype := value.Type()
|
||||||
|
if vtype.Kind() == reflect.Ptr {
|
||||||
|
vtype = vtype.Elem() // check kind of actual element type
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tag.Get("type")
|
||||||
|
if t == "" {
|
||||||
|
switch vtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
// also it can't be a time object
|
||||||
|
if _, ok := value.Interface().(*time.Time); !ok {
|
||||||
|
t = "structure"
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
// also it can't be a byte slice
|
||||||
|
if _, ok := value.Interface().([]byte); !ok {
|
||||||
|
t = "list"
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
t = "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "structure":
|
||||||
|
if field, ok := vtype.FieldByName("_"); ok {
|
||||||
|
tag = field.Tag
|
||||||
|
}
|
||||||
|
return unmarshalStruct(value, data, tag)
|
||||||
|
case "list":
|
||||||
|
return unmarshalList(value, data, tag)
|
||||||
|
case "map":
|
||||||
|
return unmarshalMap(value, data, tag)
|
||||||
|
default:
|
||||||
|
return unmarshalScalar(value, data, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapData, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a structure (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := value.Type()
|
||||||
|
if value.Kind() == reflect.Ptr {
|
||||||
|
if value.IsNil() { // create the structure if it's nil
|
||||||
|
s := reflect.New(value.Type().Elem())
|
||||||
|
value.Set(s)
|
||||||
|
value = s
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value.Elem()
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap any payloads
|
||||||
|
if payload := tag.Get("payload"); payload != "" {
|
||||||
|
field, _ := t.FieldByName(payload)
|
||||||
|
return unmarshalAny(value.FieldByName(payload), data, field.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue // ignore unexported fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out what this field is called
|
||||||
|
name := field.Name
|
||||||
|
if locName := field.Tag.Get("locationName"); locName != "" {
|
||||||
|
name = locName
|
||||||
|
}
|
||||||
|
|
||||||
|
member := value.FieldByIndex(field.Index)
|
||||||
|
err := unmarshalAny(member, mapData[name], field.Tag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
listData, ok := data.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a list (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsNil() {
|
||||||
|
l := len(listData)
|
||||||
|
value.Set(reflect.MakeSlice(value.Type(), l, l))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range listData {
|
||||||
|
err := unmarshalAny(value.Index(i), c, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mapData, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("JSON value is not a map (%#v)", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.IsNil() {
|
||||||
|
value.Set(reflect.MakeMap(value.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range mapData {
|
||||||
|
kvalue := reflect.ValueOf(k)
|
||||||
|
vvalue := reflect.New(value.Type().Elem()).Elem()
|
||||||
|
|
||||||
|
unmarshalAny(vvalue, v, "")
|
||||||
|
value.SetMapIndex(kvalue, vvalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||||
|
errf := func() error {
|
||||||
|
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch d := data.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil // nothing to do here
|
||||||
|
case string:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *string:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
case []byte:
|
||||||
|
b, err := base64.StdEncoding.DecodeString(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value.Set(reflect.ValueOf(b))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *int64:
|
||||||
|
di := int64(d)
|
||||||
|
value.Set(reflect.ValueOf(&di))
|
||||||
|
case *float64:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
case *time.Time:
|
||||||
|
t := time.Unix(int64(d), 0).UTC()
|
||||||
|
value.Set(reflect.ValueOf(&t))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
switch value.Interface().(type) {
|
||||||
|
case *bool:
|
||||||
|
value.Set(reflect.ValueOf(&d))
|
||||||
|
default:
|
||||||
|
return errf()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported JSON value (%v)", data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
99
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go
generated
vendored
Normal file
99
Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Package jsonrpc provides JSON RPC utilities for serialisation of AWS
|
||||||
|
// requests and responses.
|
||||||
|
package jsonrpc
|
||||||
|
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/json.json build_test.go
|
||||||
|
//go:generate go run ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/json/jsonutil"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var emptyJSON = []byte("{}")
|
||||||
|
|
||||||
|
// Build builds a JSON payload for a JSON RPC request.
|
||||||
|
func Build(req *request.Request) {
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
if req.ParamsFilled() {
|
||||||
|
buf, err = jsonutil.BuildJSON(req.Params)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed encoding JSON RPC request", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf = emptyJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ClientInfo.TargetPrefix != "" || string(buf) != "{}" {
|
||||||
|
req.SetBufferBody(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ClientInfo.TargetPrefix != "" {
|
||||||
|
target := req.ClientInfo.TargetPrefix + "." + req.Operation.Name
|
||||||
|
req.HTTPRequest.Header.Add("X-Amz-Target", target)
|
||||||
|
}
|
||||||
|
if req.ClientInfo.JSONVersion != "" {
|
||||||
|
jsonVersion := req.ClientInfo.JSONVersion
|
||||||
|
req.HTTPRequest.Header.Add("Content-Type", "application/x-amz-json-"+jsonVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal unmarshals a response for a JSON RPC service.
|
||||||
|
func Unmarshal(req *request.Request) {
|
||||||
|
defer req.HTTPResponse.Body.Close()
|
||||||
|
if req.DataFilled() {
|
||||||
|
err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC response", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalMeta unmarshals headers from a response for a JSON RPC service.
|
||||||
|
func UnmarshalMeta(req *request.Request) {
|
||||||
|
rest.UnmarshalMeta(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalError unmarshals an error response for a JSON RPC service.
|
||||||
|
func UnmarshalError(req *request.Request) {
|
||||||
|
defer req.HTTPResponse.Body.Close()
|
||||||
|
bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed reading JSON RPC error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bodyBytes) == 0 {
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New("SerializationError", req.HTTPResponse.Status, nil),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var jsonErr jsonErrorResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||||
|
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC error response", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
codes := strings.SplitN(jsonErr.Code, "#", 2)
|
||||||
|
req.Error = awserr.NewRequestFailure(
|
||||||
|
awserr.New(codes[len(codes)-1], jsonErr.Message, nil),
|
||||||
|
req.HTTPResponse.StatusCode,
|
||||||
|
req.RequestID,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonErrorResponse struct {
|
||||||
|
Code string `json:"__type"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
1433
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/api.go
generated
vendored
Normal file
1433
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/api.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
78
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/ecriface/interface.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/ecriface/interface.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Package ecriface provides an interface for the Amazon EC2 Container Registry.
|
||||||
|
package ecriface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ECRAPI is the interface type for ecr.ECR.
|
||||||
|
type ECRAPI interface {
|
||||||
|
BatchCheckLayerAvailabilityRequest(*ecr.BatchCheckLayerAvailabilityInput) (*request.Request, *ecr.BatchCheckLayerAvailabilityOutput)
|
||||||
|
|
||||||
|
BatchCheckLayerAvailability(*ecr.BatchCheckLayerAvailabilityInput) (*ecr.BatchCheckLayerAvailabilityOutput, error)
|
||||||
|
|
||||||
|
BatchDeleteImageRequest(*ecr.BatchDeleteImageInput) (*request.Request, *ecr.BatchDeleteImageOutput)
|
||||||
|
|
||||||
|
BatchDeleteImage(*ecr.BatchDeleteImageInput) (*ecr.BatchDeleteImageOutput, error)
|
||||||
|
|
||||||
|
BatchGetImageRequest(*ecr.BatchGetImageInput) (*request.Request, *ecr.BatchGetImageOutput)
|
||||||
|
|
||||||
|
BatchGetImage(*ecr.BatchGetImageInput) (*ecr.BatchGetImageOutput, error)
|
||||||
|
|
||||||
|
CompleteLayerUploadRequest(*ecr.CompleteLayerUploadInput) (*request.Request, *ecr.CompleteLayerUploadOutput)
|
||||||
|
|
||||||
|
CompleteLayerUpload(*ecr.CompleteLayerUploadInput) (*ecr.CompleteLayerUploadOutput, error)
|
||||||
|
|
||||||
|
CreateRepositoryRequest(*ecr.CreateRepositoryInput) (*request.Request, *ecr.CreateRepositoryOutput)
|
||||||
|
|
||||||
|
CreateRepository(*ecr.CreateRepositoryInput) (*ecr.CreateRepositoryOutput, error)
|
||||||
|
|
||||||
|
DeleteRepositoryRequest(*ecr.DeleteRepositoryInput) (*request.Request, *ecr.DeleteRepositoryOutput)
|
||||||
|
|
||||||
|
DeleteRepository(*ecr.DeleteRepositoryInput) (*ecr.DeleteRepositoryOutput, error)
|
||||||
|
|
||||||
|
DeleteRepositoryPolicyRequest(*ecr.DeleteRepositoryPolicyInput) (*request.Request, *ecr.DeleteRepositoryPolicyOutput)
|
||||||
|
|
||||||
|
DeleteRepositoryPolicy(*ecr.DeleteRepositoryPolicyInput) (*ecr.DeleteRepositoryPolicyOutput, error)
|
||||||
|
|
||||||
|
DescribeRepositoriesRequest(*ecr.DescribeRepositoriesInput) (*request.Request, *ecr.DescribeRepositoriesOutput)
|
||||||
|
|
||||||
|
DescribeRepositories(*ecr.DescribeRepositoriesInput) (*ecr.DescribeRepositoriesOutput, error)
|
||||||
|
|
||||||
|
GetAuthorizationTokenRequest(*ecr.GetAuthorizationTokenInput) (*request.Request, *ecr.GetAuthorizationTokenOutput)
|
||||||
|
|
||||||
|
GetAuthorizationToken(*ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
|
||||||
|
|
||||||
|
GetDownloadUrlForLayerRequest(*ecr.GetDownloadUrlForLayerInput) (*request.Request, *ecr.GetDownloadUrlForLayerOutput)
|
||||||
|
|
||||||
|
GetDownloadUrlForLayer(*ecr.GetDownloadUrlForLayerInput) (*ecr.GetDownloadUrlForLayerOutput, error)
|
||||||
|
|
||||||
|
GetRepositoryPolicyRequest(*ecr.GetRepositoryPolicyInput) (*request.Request, *ecr.GetRepositoryPolicyOutput)
|
||||||
|
|
||||||
|
GetRepositoryPolicy(*ecr.GetRepositoryPolicyInput) (*ecr.GetRepositoryPolicyOutput, error)
|
||||||
|
|
||||||
|
InitiateLayerUploadRequest(*ecr.InitiateLayerUploadInput) (*request.Request, *ecr.InitiateLayerUploadOutput)
|
||||||
|
|
||||||
|
InitiateLayerUpload(*ecr.InitiateLayerUploadInput) (*ecr.InitiateLayerUploadOutput, error)
|
||||||
|
|
||||||
|
ListImagesRequest(*ecr.ListImagesInput) (*request.Request, *ecr.ListImagesOutput)
|
||||||
|
|
||||||
|
ListImages(*ecr.ListImagesInput) (*ecr.ListImagesOutput, error)
|
||||||
|
|
||||||
|
PutImageRequest(*ecr.PutImageInput) (*request.Request, *ecr.PutImageOutput)
|
||||||
|
|
||||||
|
PutImage(*ecr.PutImageInput) (*ecr.PutImageOutput, error)
|
||||||
|
|
||||||
|
SetRepositoryPolicyRequest(*ecr.SetRepositoryPolicyInput) (*request.Request, *ecr.SetRepositoryPolicyOutput)
|
||||||
|
|
||||||
|
SetRepositoryPolicy(*ecr.SetRepositoryPolicyInput) (*ecr.SetRepositoryPolicyOutput, error)
|
||||||
|
|
||||||
|
UploadLayerPartRequest(*ecr.UploadLayerPartInput) (*request.Request, *ecr.UploadLayerPartOutput)
|
||||||
|
|
||||||
|
UploadLayerPart(*ecr.UploadLayerPartInput) (*ecr.UploadLayerPartOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ECRAPI = (*ecr.ECR)(nil)
|
95
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/service.go
generated
vendored
Normal file
95
Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/ecr/service.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||||
|
|
||||||
|
package ecr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
|
||||||
|
"github.com/aws/aws-sdk-go/private/signer/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The Amazon EC2 Container Registry makes it easier to manage public and private
|
||||||
|
// Docker images for AWS or on-premises environments. Amazon ECR supports resource-level
|
||||||
|
// permissions to control who can create, update, use, or delete images. Users
|
||||||
|
// and groups can be created individually in AWS Identity and Access Management
|
||||||
|
// (IAM) or federated with enterprise directories such as Microsoft Active Directory.
|
||||||
|
// Images are stored on highly durable AWS infrastructure and include built-in
|
||||||
|
// caching so that starting hundreds of containers is as fast as starting a
|
||||||
|
// single container.
|
||||||
|
//The service client's operations are safe to be used concurrently.
|
||||||
|
// It is not safe to mutate any of the client's properties though.
|
||||||
|
type ECR struct {
|
||||||
|
*client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for custom client initialization logic
|
||||||
|
var initClient func(*client.Client)
|
||||||
|
|
||||||
|
// Used for custom request initialization logic
|
||||||
|
var initRequest func(*request.Request)
|
||||||
|
|
||||||
|
// A ServiceName is the name of the service the client will make API calls to.
|
||||||
|
const ServiceName = "ecr"
|
||||||
|
|
||||||
|
// New creates a new instance of the ECR client with a session.
|
||||||
|
// If additional configuration is needed for the client instance use the optional
|
||||||
|
// aws.Config parameter to add your extra config.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Create a ECR client from just a session.
|
||||||
|
// svc := ecr.New(mySession)
|
||||||
|
//
|
||||||
|
// // Create a ECR client with additional configuration
|
||||||
|
// svc := ecr.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
|
||||||
|
func New(p client.ConfigProvider, cfgs ...*aws.Config) *ECR {
|
||||||
|
c := p.ClientConfig(ServiceName, cfgs...)
|
||||||
|
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newClient creates, initializes and returns a new service client instance.
|
||||||
|
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *ECR {
|
||||||
|
svc := &ECR{
|
||||||
|
Client: client.New(
|
||||||
|
cfg,
|
||||||
|
metadata.ClientInfo{
|
||||||
|
ServiceName: ServiceName,
|
||||||
|
SigningRegion: signingRegion,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
APIVersion: "2015-09-21",
|
||||||
|
JSONVersion: "1.1",
|
||||||
|
TargetPrefix: "AmazonEC2ContainerRegistry_V20150921",
|
||||||
|
},
|
||||||
|
handlers,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
svc.Handlers.Sign.PushBack(v4.Sign)
|
||||||
|
svc.Handlers.Build.PushBack(jsonrpc.Build)
|
||||||
|
svc.Handlers.Unmarshal.PushBack(jsonrpc.Unmarshal)
|
||||||
|
svc.Handlers.UnmarshalMeta.PushBack(jsonrpc.UnmarshalMeta)
|
||||||
|
svc.Handlers.UnmarshalError.PushBack(jsonrpc.UnmarshalError)
|
||||||
|
|
||||||
|
// Run custom client initialization if present
|
||||||
|
if initClient != nil {
|
||||||
|
initClient(svc.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequest creates a new request for a ECR operation and runs any
|
||||||
|
// custom request initialization.
|
||||||
|
func (c *ECR) newRequest(op *request.Operation, params, data interface{}) *request.Request {
|
||||||
|
req := c.NewRequest(op, params, data)
|
||||||
|
|
||||||
|
// Run custom request initialization if present
|
||||||
|
if initRequest != nil {
|
||||||
|
initRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
@ -22,6 +22,19 @@
|
|||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Action": "ec2:DetachVolume",
|
"Action": "ec2:DetachVolume",
|
||||||
"Resource": "*"
|
"Resource": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"ecr:GetAuthorizationToken",
|
||||||
|
"ecr:BatchCheckLayerAvailability",
|
||||||
|
"ecr:GetDownloadUrlForLayer",
|
||||||
|
"ecr:GetRepositoryPolicy",
|
||||||
|
"ecr:DescribeRepositories",
|
||||||
|
"ecr:ListImages",
|
||||||
|
"ecr:BatchGetImage"
|
||||||
|
],
|
||||||
|
"Resource": "*"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package app
|
|||||||
// This file exists to force the desired plugin implementations to be linked.
|
// This file exists to force the desired plugin implementations to be linked.
|
||||||
import (
|
import (
|
||||||
// Credential providers
|
// Credential providers
|
||||||
|
_ "k8s.io/kubernetes/pkg/credentialprovider/aws"
|
||||||
_ "k8s.io/kubernetes/pkg/credentialprovider/gcp"
|
_ "k8s.io/kubernetes/pkg/credentialprovider/gcp"
|
||||||
// Network plugins
|
// Network plugins
|
||||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||||
|
@ -171,7 +171,11 @@ The nodes do not need a lot of access to the AWS APIs. They need to download
|
|||||||
a distribution file, and then are responsible for attaching and detaching EBS
|
a distribution file, and then are responsible for attaching and detaching EBS
|
||||||
volumes from itself.
|
volumes from itself.
|
||||||
|
|
||||||
The node policy is relatively minimal. The master policy is probably overly
|
The node policy is relatively minimal. In 1.2 and later, nodes can retrieve ECR
|
||||||
|
authorization tokens, refresh them every 12 hours if needed, and fetch Docker
|
||||||
|
images from it, as long as the appropriate permissions are enabled. Those in
|
||||||
|
[AmazonEC2ContainerRegistryReadOnly](http://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html#AmazonEC2ContainerRegistryReadOnly),
|
||||||
|
without write access, should suffice. The master policy is probably overly
|
||||||
permissive. The security conscious may want to lock-down the IAM policies
|
permissive. The security conscious may want to lock-down the IAM policies
|
||||||
further ([#11936](http://issues.k8s.io/11936)).
|
further ([#11936](http://issues.k8s.io/11936)).
|
||||||
|
|
||||||
@ -180,7 +184,7 @@ are correctly configured ([#14226](http://issues.k8s.io/14226)).
|
|||||||
|
|
||||||
### Tagging
|
### Tagging
|
||||||
|
|
||||||
All AWS resources are tagged with a tag named "KuberentesCluster", with a value
|
All AWS resources are tagged with a tag named "KubernetesCluster", with a value
|
||||||
that is the unique cluster-id. This tag is used to identify a particular
|
that is the unique cluster-id. This tag is used to identify a particular
|
||||||
'instance' of Kubernetes, even if two clusters are deployed into the same VPC.
|
'instance' of Kubernetes, even if two clusters are deployed into the same VPC.
|
||||||
Resources are considered to belong to the same cluster if and only if they have
|
Resources are considered to belong to the same cluster if and only if they have
|
||||||
|
@ -47,6 +47,7 @@ The `image` property of a container supports the same syntax as the `docker` com
|
|||||||
- [Updating Images](#updating-images)
|
- [Updating Images](#updating-images)
|
||||||
- [Using a Private Registry](#using-a-private-registry)
|
- [Using a Private Registry](#using-a-private-registry)
|
||||||
- [Using Google Container Registry](#using-google-container-registry)
|
- [Using Google Container Registry](#using-google-container-registry)
|
||||||
|
- [Using AWS EC2 Container Registry](#using-aws-ec2-container-registry)
|
||||||
- [Configuring Nodes to Authenticate to a Private Repository](#configuring-nodes-to-authenticate-to-a-private-repository)
|
- [Configuring Nodes to Authenticate to a Private Repository](#configuring-nodes-to-authenticate-to-a-private-repository)
|
||||||
- [Pre-pulling Images](#pre-pulling-images)
|
- [Pre-pulling Images](#pre-pulling-images)
|
||||||
- [Specifying ImagePullSecrets on a Pod](#specifying-imagepullsecrets-on-a-pod)
|
- [Specifying ImagePullSecrets on a Pod](#specifying-imagepullsecrets-on-a-pod)
|
||||||
@ -97,6 +98,21 @@ Google service account. The service account on the instance
|
|||||||
will have a `https://www.googleapis.com/auth/devstorage.read_only`,
|
will have a `https://www.googleapis.com/auth/devstorage.read_only`,
|
||||||
so it can pull from the project's GCR, but not push.
|
so it can pull from the project's GCR, but not push.
|
||||||
|
|
||||||
|
### Using AWS EC2 Container Registry
|
||||||
|
|
||||||
|
Kubernetes has native support for the [AWS EC2 Container
|
||||||
|
Registry](https://aws.amazon.com/ecr/), when nodes are AWS instances.
|
||||||
|
|
||||||
|
Simply use the full image name (e.g. `ACCOUNT.dkr.ecr.REGION.amazonaws.com/imagename:tag`)
|
||||||
|
in the Pod definition.
|
||||||
|
|
||||||
|
All users of the cluster who can create pods will be able to run pods that use any of the
|
||||||
|
images in the ECR registry.
|
||||||
|
|
||||||
|
The kubelet will fetch and periodically refresh ECR credentials. It needs the
|
||||||
|
`ecr:GetAuthorizationToken` permission to do this.
|
||||||
|
|
||||||
|
|
||||||
### Configuring Nodes to Authenticate to a Private Repository
|
### Configuring Nodes to Authenticate to a Private Repository
|
||||||
|
|
||||||
**Note:** if you are running on Google Container Engine (GKE), there will already be a `.dockercfg` on each node
|
**Note:** if you are running on Google Container Engine (GKE), there will already be a `.dockercfg` on each node
|
||||||
|
163
pkg/credentialprovider/aws/aws_credentials.go
Normal file
163
pkg/credentialprovider/aws/aws_credentials.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 aws_credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecr"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||||
|
aws_cloud "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
||||||
|
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registryUrls = []string{"*.dkr.ecr.*.amazonaws.com"}
|
||||||
|
|
||||||
|
// awsHandlerLogger is a handler that logs all AWS SDK requests
|
||||||
|
// Copied from cloudprovider/aws/log_handler.go
|
||||||
|
func awsHandlerLogger(req *request.Request) {
|
||||||
|
service := req.ClientInfo.ServiceName
|
||||||
|
|
||||||
|
name := "?"
|
||||||
|
if req.Operation != nil {
|
||||||
|
name = req.Operation.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(4).Infof("AWS request: %s %s", service, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An interface for testing purposes.
|
||||||
|
type tokenGetter interface {
|
||||||
|
GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The canonical implementation
|
||||||
|
type ecrTokenGetter struct {
|
||||||
|
svc *ecr.ECR
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ecrTokenGetter) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
|
||||||
|
return p.svc.GetAuthorizationToken(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecrProvider is a DockerConfigProvider that gets and refreshes 12-hour tokens
|
||||||
|
// from AWS to access ECR.
|
||||||
|
type ecrProvider struct {
|
||||||
|
getter tokenGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
// init registers the various means by which ECR credentials may
|
||||||
|
// be resolved.
|
||||||
|
func init() {
|
||||||
|
credentialprovider.RegisterCredentialProvider("aws-ecr-key",
|
||||||
|
&credentialprovider.CachingDockerConfigProvider{
|
||||||
|
Provider: &ecrProvider{},
|
||||||
|
// Refresh credentials a little earlier before they expire
|
||||||
|
Lifetime: 11*time.Hour + 55*time.Minute,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled implements DockerConfigProvider.Enabled for the AWS token-based implementation.
|
||||||
|
// For now, it gets activated only if AWS was chosen as the cloud provider.
|
||||||
|
// TODO: figure how to enable it manually for deployments that are not on AWS but still
|
||||||
|
// use ECR somehow?
|
||||||
|
func (p *ecrProvider) Enabled() bool {
|
||||||
|
provider, err := cloudprovider.GetCloudProvider(aws_cloud.ProviderName, nil)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("while initializing AWS cloud provider %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if provider == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
zones, ok := provider.Zones()
|
||||||
|
if !ok {
|
||||||
|
glog.Errorf("couldn't get Zones() interface")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
zone, err := zones.GetZone()
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("while getting zone %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if zone.Region == "" {
|
||||||
|
glog.Errorf("Region information is empty")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
getter := &ecrTokenGetter{svc: ecr.New(session.New(&aws.Config{
|
||||||
|
Credentials: nil,
|
||||||
|
Region: &zone.Region,
|
||||||
|
}))}
|
||||||
|
getter.svc.Handlers.Sign.PushFrontNamed(request.NamedHandler{
|
||||||
|
Name: "k8s/logger",
|
||||||
|
Fn: awsHandlerLogger,
|
||||||
|
})
|
||||||
|
p.getter = getter
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide implements DockerConfigProvider.Provide, refreshing ECR tokens on demand
|
||||||
|
func (p *ecrProvider) Provide() credentialprovider.DockerConfig {
|
||||||
|
cfg := credentialprovider.DockerConfig{}
|
||||||
|
|
||||||
|
// TODO: fill in RegistryIds?
|
||||||
|
params := &ecr.GetAuthorizationTokenInput{}
|
||||||
|
output, err := p.getter.GetAuthorizationToken(params)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("while requesting ECR authorization token %v", err)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
if output == nil {
|
||||||
|
glog.Errorf("Got back no ECR token")
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range output.AuthorizationData {
|
||||||
|
if data.ProxyEndpoint != nil &&
|
||||||
|
data.AuthorizationToken != nil {
|
||||||
|
decodedToken, err := base64.StdEncoding.DecodeString(aws.StringValue(data.AuthorizationToken))
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("while decoding token for endpoint %s %v", data.ProxyEndpoint, err)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(string(decodedToken), ":", 2)
|
||||||
|
user := parts[0]
|
||||||
|
password := parts[1]
|
||||||
|
entry := credentialprovider.DockerConfigEntry{
|
||||||
|
Username: user,
|
||||||
|
Password: password,
|
||||||
|
// ECR doesn't care and Docker is about to obsolete it
|
||||||
|
Email: "not@val.id",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our entry for each of the supported container registry URLs
|
||||||
|
for _, k := range registryUrls {
|
||||||
|
cfg[k] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
108
pkg/credentialprovider/aws/aws_credentials_test.go
Normal file
108
pkg/credentialprovider/aws/aws_credentials_test.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
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 aws_credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ecr"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/credentialprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
const user = "foo"
|
||||||
|
const password = "1234567890abcdef"
|
||||||
|
const email = "not@val.id"
|
||||||
|
|
||||||
|
// Mock implementation
|
||||||
|
type testTokenGetter struct {
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
endpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *testTokenGetter) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
|
||||||
|
|
||||||
|
expiration := time.Now().Add(1 * time.Hour)
|
||||||
|
creds := []byte(fmt.Sprintf("%s:%s", p.user, p.password))
|
||||||
|
data := &ecr.AuthorizationData{
|
||||||
|
AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString(creds)),
|
||||||
|
ExpiresAt: &expiration,
|
||||||
|
ProxyEndpoint: aws.String(p.endpoint),
|
||||||
|
}
|
||||||
|
output := &ecr.GetAuthorizationTokenOutput{
|
||||||
|
AuthorizationData: []*ecr.AuthorizationData{data},
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil //p.svc.GetAuthorizationToken(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEcrProvide(t *testing.T) {
|
||||||
|
registry := "123456789012.dkr.ecr.lala-land-1.amazonaws.com"
|
||||||
|
otherRegistries := []string{"private.registry.com",
|
||||||
|
"gcr.io",
|
||||||
|
}
|
||||||
|
image := "foo/bar"
|
||||||
|
|
||||||
|
provider := &ecrProvider{
|
||||||
|
getter: &testTokenGetter{
|
||||||
|
user: user,
|
||||||
|
password: password,
|
||||||
|
endpoint: registry},
|
||||||
|
}
|
||||||
|
|
||||||
|
keyring := &credentialprovider.BasicDockerKeyring{}
|
||||||
|
keyring.Add(provider.Provide())
|
||||||
|
|
||||||
|
// Verify that we get the expected username/password combo for
|
||||||
|
// an ECR image name.
|
||||||
|
fullImage := path.Join(registry, image)
|
||||||
|
creds, ok := keyring.Lookup(fullImage)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Didn't find expected URL: %s", fullImage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(creds) > 1 {
|
||||||
|
t.Errorf("Got more hits than expected: %s", creds)
|
||||||
|
}
|
||||||
|
val := creds[0]
|
||||||
|
|
||||||
|
if user != val.Username {
|
||||||
|
t.Errorf("Unexpected username value, want: _token, got: %s", val.Username)
|
||||||
|
}
|
||||||
|
if password != val.Password {
|
||||||
|
t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
|
||||||
|
}
|
||||||
|
if email != val.Email {
|
||||||
|
t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that we get an error for other images.
|
||||||
|
for _, otherRegistry := range otherRegistries {
|
||||||
|
fullImage = path.Join(otherRegistry, image)
|
||||||
|
creds, ok = keyring.Lookup(fullImage)
|
||||||
|
if ok {
|
||||||
|
t.Errorf("Unexpectedly found image: %s", fullImage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user