storage/kubernetes: manage third party resources and drop support for 1.3
This commit is contained in:
		@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gtank/cryptopasta"
 | 
						"github.com/gtank/cryptopasta"
 | 
				
			||||||
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
	yaml "gopkg.in/yaml.v2"
 | 
						yaml "gopkg.in/yaml.v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/coreos/dex/storage"
 | 
						"github.com/coreos/dex/storage"
 | 
				
			||||||
@@ -27,27 +28,17 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type client struct {
 | 
					type client struct {
 | 
				
			||||||
	client     *http.Client
 | 
						client    *http.Client
 | 
				
			||||||
	baseURL    string
 | 
						baseURL   string
 | 
				
			||||||
	namespace  string
 | 
						namespace string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// API version of the oidc resources. For example "oidc.coreos.com". This is
 | 
				
			||||||
 | 
						// currently not configurable, but could be in the future.
 | 
				
			||||||
	apiVersion string
 | 
						apiVersion string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	now func() time.Time
 | 
						// This is called once the client's Close method is called to signal goroutines,
 | 
				
			||||||
 | 
						// such as the one creating third party resources, to stop.
 | 
				
			||||||
	// BUG: currently each third party API group can only have one resource in it,
 | 
						cancel context.CancelFunc
 | 
				
			||||||
	// so for each resource this storage uses, it need a unique API group.
 | 
					 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	// Prepend the name of each resource to the API group for a predictable mapping.
 | 
					 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	// See: https://github.com/kubernetes/kubernetes/pull/28414
 | 
					 | 
				
			||||||
	prependResourceNameToAPIGroup bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *client) apiVersionForResource(resource string) string {
 | 
					 | 
				
			||||||
	if !c.prependResourceNameToAPIGroup {
 | 
					 | 
				
			||||||
		return c.apiVersion
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return resource + "." + c.apiVersion
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
 | 
					func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
 | 
				
			||||||
@@ -56,10 +47,6 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
 | 
				
			|||||||
		basePath = "api/"
 | 
							basePath = "api/"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.prependResourceNameToAPIGroup && apiVersion != "" && resource != "" {
 | 
					 | 
				
			||||||
		apiVersion = resource + "." + apiVersion
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var p string
 | 
						var p string
 | 
				
			||||||
	if namespace != "" {
 | 
						if namespace != "" {
 | 
				
			||||||
		p = path.Join(basePath, apiVersion, "namespaces", namespace, resource, name)
 | 
							p = path.Join(basePath, apiVersion, "namespaces", namespace, resource, name)
 | 
				
			||||||
@@ -72,15 +59,28 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
 | 
				
			|||||||
	return c.baseURL + "/" + p
 | 
						return c.baseURL + "/" + p
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Define an error interface so we can get at the underlying status code if it's
 | 
				
			||||||
 | 
					// absolutely necessary. For instance when we need to see if an error indicates
 | 
				
			||||||
 | 
					// a resource already exists.
 | 
				
			||||||
 | 
					type httpError interface {
 | 
				
			||||||
 | 
						StatusCode() int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ httpError = (*httpErr)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type httpErr struct {
 | 
					type httpErr struct {
 | 
				
			||||||
	method string
 | 
						method string
 | 
				
			||||||
	url    string
 | 
						url    string
 | 
				
			||||||
	status string
 | 
						status int
 | 
				
			||||||
	body   []byte
 | 
						body   []byte
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *httpErr) StatusCode() int {
 | 
				
			||||||
 | 
						return e.status
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *httpErr) Error() string {
 | 
					func (e *httpErr) Error() string {
 | 
				
			||||||
	return fmt.Sprintf("%s %s %s: response from server \"%s\"", e.method, e.url, e.status, bytes.TrimSpace(e.body))
 | 
						return fmt.Sprintf("%s %s %s: response from server \"%s\"", e.method, e.url, http.StatusText(e.status), bytes.TrimSpace(e.body))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkHTTPErr(r *http.Response, validStatusCodes ...int) error {
 | 
					func checkHTTPErr(r *http.Response, validStatusCodes ...int) error {
 | 
				
			||||||
@@ -100,7 +100,7 @@ func checkHTTPErr(r *http.Response, validStatusCodes ...int) error {
 | 
				
			|||||||
		method = r.Request.Method
 | 
							method = r.Request.Method
 | 
				
			||||||
		url = r.Request.URL.String()
 | 
							url = r.Request.URL.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = &httpErr{method, url, r.Status, body}
 | 
						err = &httpErr{method, url, r.StatusCode, body}
 | 
				
			||||||
	log.Printf("%s", err)
 | 
						log.Printf("%s", err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r.StatusCode == http.StatusNotFound {
 | 
						if r.StatusCode == http.StatusNotFound {
 | 
				
			||||||
@@ -134,12 +134,16 @@ func (c *client) list(resource string, v interface{}) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *client) post(resource string, v interface{}) error {
 | 
					func (c *client) post(resource string, v interface{}) error {
 | 
				
			||||||
 | 
						return c.postResource(c.apiVersion, c.namespace, resource, v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *client) postResource(apiVersion, namespace, resource string, v interface{}) error {
 | 
				
			||||||
	body, err := json.Marshal(v)
 | 
						body, err := json.Marshal(v)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("marshal object: %v", err)
 | 
							return fmt.Errorf("marshal object: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	url := c.urlFor(c.apiVersion, c.namespace, resource, "")
 | 
						url := c.urlFor(apiVersion, namespace, resource, "")
 | 
				
			||||||
	resp, err := c.client.Post(url, "application/json", bytes.NewReader(body))
 | 
						resp, err := c.client.Post(url, "application/json", bytes.NewReader(body))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -277,8 +281,6 @@ func newClient(cluster k8sapi.Cluster, user k8sapi.AuthInfo, namespace string) (
 | 
				
			|||||||
		baseURL:    cluster.Server,
 | 
							baseURL:    cluster.Server,
 | 
				
			||||||
		namespace:  namespace,
 | 
							namespace:  namespace,
 | 
				
			||||||
		apiVersion: "oidc.coreos.com/v1",
 | 
							apiVersion: "oidc.coreos.com/v1",
 | 
				
			||||||
		now:        time.Now,
 | 
					 | 
				
			||||||
		prependResourceNameToAPIGroup: true,
 | 
					 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,13 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	homedir "github.com/mitchellh/go-homedir"
 | 
						homedir "github.com/mitchellh/go-homedir"
 | 
				
			||||||
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/coreos/dex/storage"
 | 
						"github.com/coreos/dex/storage"
 | 
				
			||||||
	"github.com/coreos/dex/storage/kubernetes/k8sapi"
 | 
						"github.com/coreos/dex/storage/kubernetes/k8sapi"
 | 
				
			||||||
@@ -45,7 +47,6 @@ func (c *Config) Open() (storage.Storage, error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cli, nil
 | 
						return cli, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,10 +82,57 @@ func (c *Config) open() (*client, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return newClient(cluster, user, namespace)
 | 
						cli, err := newClient(cluster, user, namespace)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("create client: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Don't try to synchronize this because creating third party resources is not
 | 
				
			||||||
 | 
						// a synchronous event. Even after the API server returns a 200, it can still
 | 
				
			||||||
 | 
						// take several seconds for them to actually appear.
 | 
				
			||||||
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								if err := cli.createThirdPartyResources(); err != nil {
 | 
				
			||||||
 | 
									log.Printf("failed creating third party resources: %v", err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case <-ctx.Done():
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								case <-time.After(30 * time.Second):
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the client is closed, stop trying to create third party resources.
 | 
				
			||||||
 | 
						cli.cancel = cancel
 | 
				
			||||||
 | 
						return cli, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cli *client) createThirdPartyResources() error {
 | 
				
			||||||
 | 
						for _, r := range thirdPartyResources {
 | 
				
			||||||
 | 
							err := cli.postResource("extensions/v1beta1", "", "thirdpartyresources", r)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if e, ok := err.(httpError); ok {
 | 
				
			||||||
 | 
									if e.StatusCode() == http.StatusConflict {
 | 
				
			||||||
 | 
										log.Printf("third party resource already created %q", r.ObjectMeta.Name)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							log.Printf("create third party resource %q", r.ObjectMeta.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cli *client) Close() error {
 | 
					func (cli *client) Close() error {
 | 
				
			||||||
 | 
						if cli.cancel != nil {
 | 
				
			||||||
 | 
							cli.cancel()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -108,7 +156,7 @@ func (cli *client) CreateRefresh(r storage.RefreshToken) error {
 | 
				
			|||||||
	refresh := RefreshToken{
 | 
						refresh := RefreshToken{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindRefreshToken,
 | 
								Kind:       kindRefreshToken,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourceRefreshToken),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      r.RefreshToken,
 | 
								Name:      r.RefreshToken,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ func TestURLFor(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
		c := &client{baseURL: test.baseURL, prependResourceNameToAPIGroup: false}
 | 
							c := &client{baseURL: test.baseURL}
 | 
				
			||||||
		got := c.urlFor(test.apiVersion, test.namespace, test.resource, test.name)
 | 
							got := c.urlFor(test.apiVersion, test.namespace, test.resource, test.name)
 | 
				
			||||||
		if got != test.want {
 | 
							if got != test.want {
 | 
				
			||||||
			t.Errorf("(&client{baseURL:%q}).urlFor(%q, %q, %q, %q): expected %q got %q",
 | 
								t.Errorf("(&client{baseURL:%q}).urlFor(%q, %q, %q, %q): expected %q got %q",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,64 @@ import (
 | 
				
			|||||||
	"github.com/coreos/dex/storage/kubernetes/k8sapi"
 | 
						"github.com/coreos/dex/storage/kubernetes/k8sapi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var tprMeta = k8sapi.TypeMeta{
 | 
				
			||||||
 | 
						APIVersion: "extensions/v1beta1",
 | 
				
			||||||
 | 
						Kind:       "ThirdPartyResource",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The set of third party resources required by the storage. These are managed by
 | 
				
			||||||
 | 
					// the storage so it can migrate itself by creating new resources.
 | 
				
			||||||
 | 
					var thirdPartyResources = []k8sapi.ThirdPartyResource{
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "auth-code.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "A code which can be claimed for an access token.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "auth-request.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "A request for an end user to authorize a client.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "o-auth2-client.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "An OpenID Connect client.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "signing-key.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "Keys used to sign and verify OpenID Connect tokens.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "refresh-token.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "Refresh tokens for clients to continuously act on behalf of an end user.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
 | 
								Name: "password.oidc.coreos.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							TypeMeta:    tprMeta,
 | 
				
			||||||
 | 
							Description: "Passwords managed by the OIDC server.",
 | 
				
			||||||
 | 
							Versions:    []k8sapi.APIVersion{{Name: "v1"}},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// There will only ever be a single keys resource. Maintain this by setting a
 | 
					// There will only ever be a single keys resource. Maintain this by setting a
 | 
				
			||||||
// common name.
 | 
					// common name.
 | 
				
			||||||
const keysName = "openid-connect-keys"
 | 
					const keysName = "openid-connect-keys"
 | 
				
			||||||
@@ -45,7 +103,7 @@ func (cli *client) fromStorageClient(c storage.Client) Client {
 | 
				
			|||||||
	return Client{
 | 
						return Client{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindClient,
 | 
								Kind:       kindClient,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourceClient),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      c.ID,
 | 
								Name:      c.ID,
 | 
				
			||||||
@@ -162,7 +220,7 @@ func (cli *client) fromStorageAuthRequest(a storage.AuthRequest) AuthRequest {
 | 
				
			|||||||
	req := AuthRequest{
 | 
						req := AuthRequest{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindAuthRequest,
 | 
								Kind:       kindAuthRequest,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourceAuthRequest),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      a.ID,
 | 
								Name:      a.ID,
 | 
				
			||||||
@@ -216,7 +274,7 @@ func (cli *client) fromStoragePassword(p storage.Password) Password {
 | 
				
			|||||||
	return Password{
 | 
						return Password{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindPassword,
 | 
								Kind:       kindPassword,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourcePassword),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      emailToID(email),
 | 
								Name:      emailToID(email),
 | 
				
			||||||
@@ -270,7 +328,7 @@ func (cli *client) fromStorageAuthCode(a storage.AuthCode) AuthCode {
 | 
				
			|||||||
	return AuthCode{
 | 
						return AuthCode{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindAuthCode,
 | 
								Kind:       kindAuthCode,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourceAuthCode),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      a.ID,
 | 
								Name:      a.ID,
 | 
				
			||||||
@@ -346,7 +404,7 @@ func (cli *client) fromStorageKeys(keys storage.Keys) Keys {
 | 
				
			|||||||
	return Keys{
 | 
						return Keys{
 | 
				
			||||||
		TypeMeta: k8sapi.TypeMeta{
 | 
							TypeMeta: k8sapi.TypeMeta{
 | 
				
			||||||
			Kind:       kindKeys,
 | 
								Kind:       kindKeys,
 | 
				
			||||||
			APIVersion: cli.apiVersionForResource(resourceKeys),
 | 
								APIVersion: cli.apiVersion,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ObjectMeta: k8sapi.ObjectMeta{
 | 
							ObjectMeta: k8sapi.ObjectMeta{
 | 
				
			||||||
			Name:      keysName,
 | 
								Name:      keysName,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user