initial commit

This commit is contained in:
Eric Chiang
2016-07-26 15:51:24 -07:00
commit cab271f304
1438 changed files with 335968 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
bin
dist
+55
View File
@@ -0,0 +1,55 @@
PROJ="poke"
ORG_PATH="github.com/coreos"
REPO_PATH="$(ORG_PATH)/$(PROJ)"
export GOBIN=$(PWD)/bin
export GO15VENDOREXPERIMENT=1
GOOS=$(shell go env GOOS)
GOARCH=$(shell go env GOARCH)
COMMIT=$(shell git rev-parse HEAD)
# check if the current commit has a matching tag
TAG=$(shell git describe --exact-match --abbrev=0 --tags $(COMMIT) 2> /dev/null || true)
ifeq ($(TAG),)
VERSION=$(TAG)
else
VERSION=$(COMMIT)
endif
build: bin/poke bin/pokectl
bin/poke: FORCE
@go install $(REPO_PATH)/cmd/poke
bin/pokectl: FORCE
@go install $(REPO_PATH)/cmd/pokectl
test:
@go test -v $(shell go list ./... | grep -v '/vendor/')
testrace:
@go test -v --race $(shell go list ./... | grep -v '/vendor/')
vet:
@go vet $(shell go list ./... | grep -v '/vendor/')
fmt:
@go fmt $(shell go list ./... | grep -v '/vendor/')
lint:
@for package in $(shell go list ./... | grep -v '/vendor/'); do \
golint $$package; \
done
clean:
rm bin/poke bin/pokectl
testall: testrace vet fmt lint
FORCE:
.PHONY: test testrace vet fmt lint testall
+126
View File
@@ -0,0 +1,126 @@
package main
import (
"fmt"
"github.com/coreos/poke/connector"
"github.com/coreos/poke/connector/github"
"github.com/coreos/poke/connector/ldap"
"github.com/coreos/poke/connector/mock"
"github.com/coreos/poke/storage"
"github.com/coreos/poke/storage/kubernetes"
)
// Config is the config format for the main application.
type Config struct {
Issuer string `yaml:"issuer"`
Storage Storage `yaml:"storage"`
Connectors []Connector `yaml:"connectors"`
Web Web `yaml:"web"`
}
// Web is the config format for the HTTP server.
type Web struct {
HTTP string `yaml:"http"`
HTTPS string `yaml:"https"`
TLSCert string `yaml:"tlsCert"`
TLSKey string `yaml:"tlsKey"`
}
// Storage holds app's storage configuration.
type Storage struct {
Type string `yaml:"type"`
Config StorageConfig `yaml:"config"`
}
// UnmarshalYAML allows Storage to unmarshal its config field dynamically
// depending on the type of storage.
func (s *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error {
var storageMeta struct {
Type string `yaml:"type"`
}
if err := unmarshal(&storageMeta); err != nil {
return err
}
s.Type = storageMeta.Type
var c struct {
Config StorageConfig `yaml:"config"`
}
switch storageMeta.Type {
case "kubernetes":
c.Config = &kubernetes.Config{}
default:
return fmt.Errorf("unknown storage type %q", storageMeta.Type)
}
if err := unmarshal(c); err != nil {
return err
}
s.Config = c.Config
return nil
}
// StorageConfig is a configuration that can create a storage.
type StorageConfig interface {
Open() (storage.Storage, error)
}
// Connector is a magical type that can unmarshal YAML dynamically. The
// Type field determines the connector type, which is then customized for Config.
type Connector struct {
Type string `yaml:"type"`
Name string `yaml:"name"`
ID string `yaml:"id"`
Config ConnectorConfig `yaml:"config"`
}
// ConnectorConfig is a configuration that can open a connector.
type ConnectorConfig interface {
Open() (connector.Connector, error)
}
// UnmarshalYAML allows Connector to unmarshal its config field dynamically
// depending on the type of connector.
func (c *Connector) UnmarshalYAML(unmarshal func(interface{}) error) error {
var connectorMetadata struct {
Type string `yaml:"type"`
Name string `yaml:"name"`
ID string `yaml:"id"`
}
if err := unmarshal(&connectorMetadata); err != nil {
return err
}
c.Type = connectorMetadata.Type
c.Name = connectorMetadata.Name
c.ID = connectorMetadata.ID
switch c.Type {
case "mock":
var config struct {
Config mock.Config `yaml:"config"`
}
if err := unmarshal(&config); err != nil {
return err
}
c.Config = &config.Config
case "ldap":
var config struct {
Config ldap.Config `yaml:"config"`
}
if err := unmarshal(&config); err != nil {
return err
}
c.Config = &config.Config
case "github":
var config struct {
Config github.Config `yaml:"config"`
}
if err := unmarshal(&config); err != nil {
return err
}
c.Config = &config.Config
default:
return fmt.Errorf("unknown connector type %q", c.Type)
}
return nil
}
+1
View File
@@ -0,0 +1 @@
package main
+28
View File
@@ -0,0 +1,28 @@
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func commandRoot() *cobra.Command {
rootCmd := &cobra.Command{
Use: "poke",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
os.Exit(2)
},
}
rootCmd.AddCommand(commandServe())
rootCmd.AddCommand(commandVersion())
return rootCmd
}
func main() {
if err := commandRoot().Execute(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(2)
}
}
+111
View File
@@ -0,0 +1,111 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
yaml "gopkg.in/yaml.v2"
"github.com/coreos/poke/server"
"github.com/spf13/cobra"
)
func commandServe() *cobra.Command {
return &cobra.Command{
Use: "serve [ config file ]",
Short: "Connect to the storage and begin serving requests.",
Long: ``,
Example: "poke serve c.yaml",
RunE: serve,
}
}
func serve(cmd *cobra.Command, args []string) error {
switch len(args) {
default:
return errors.New("surplus arguments")
case 0:
// TODO(ericchiang): Consider having a default config file location.
return errors.New("no config file specified")
case 1:
}
configFile := args[0]
configData, err := ioutil.ReadFile(configFile)
if err != nil {
return fmt.Errorf("read config file %s: %v", configFile, err)
}
var c Config
if err := yaml.Unmarshal(configData, &c); err != nil {
return fmt.Errorf("parse config file %s: %v", configFile, err)
}
// Fast checks. Perform these first for a more responsive CLI.
checks := []struct {
bad bool
errMsg string
}{
{c.Issuer == "", "no issuer specified in config file"},
{len(c.Connectors) == 0, "no connectors supplied in config file"},
{c.Storage.Config == nil, "no storage suppied in config file"},
{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS address to listen on"},
{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
}
for _, check := range checks {
if check.bad {
return errors.New(check.errMsg)
}
}
connectors := make([]server.Connector, len(c.Connectors))
for i, conn := range c.Connectors {
if conn.Config == nil {
return fmt.Errorf("no config field for connector %q", conn.ID)
}
c, err := conn.Config.Open()
if err != nil {
return fmt.Errorf("open %s: %v", conn.ID, err)
}
connectors[i] = server.Connector{
ID: conn.ID,
DisplayName: conn.Name,
Connector: c,
}
}
s, err := c.Storage.Config.Open()
if err != nil {
return fmt.Errorf("initializing storage: %v", err)
}
serverConfig := server.Config{
Issuer: c.Issuer,
Connectors: connectors,
Storage: s,
}
serv, err := server.New(serverConfig)
if err != nil {
return fmt.Errorf("initializing server: %v", err)
}
errc := make(chan error, 2)
if c.Web.HTTP != "" {
go func() {
log.Printf("listening on %s", c.Web.HTTP)
errc <- http.ListenAndServe(c.Web.HTTP, serv)
}()
}
if c.Web.HTTPS != "" {
go func() {
log.Printf("listening on %s", c.Web.HTTPS)
errc <- http.ListenAndServeTLS(c.Web.HTTPS, c.Web.TLSCert, c.Web.TLSKey, serv)
}()
}
return <-errc
}
+19
View File
@@ -0,0 +1,19 @@
package main
import (
"fmt"
"runtime"
"github.com/coreos/poke/version"
"github.com/spf13/cobra"
)
func commandVersion() *cobra.Command {
return &cobra.Command{
Use: "version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf(`v%s %s %s %s
`, version.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
},
}
}
+24
View File
@@ -0,0 +1,24 @@
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "pokectl",
RunE: func(cmd *cobra.Command, args []string) error {
return nil
},
}
func init() {}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
}
+32
View File
@@ -0,0 +1,32 @@
// Package connector defines interfaces for federated identity strategies.
package connector
import (
"net/http"
"github.com/coreos/poke/storage"
)
// Connector is a mechanism for federating login to a remote identity service.
//
// Implementations are expected to implement either the PasswordConnector or
// CallbackConnector interface.
type Connector interface {
Close() error
}
// PasswordConnector is an optional interface for password based connectors.
type PasswordConnector interface {
Login(username, password string) (identity storage.Identity, validPassword bool, err error)
}
// CallbackConnector is an optional interface for callback based connectors.
type CallbackConnector interface {
LoginURL(callbackURL, state string) (string, error)
HandleCallback(r *http.Request) (identity storage.Identity, state string, err error)
}
// GroupsConnector is an optional interface for connectors which can map a user to groups.
type GroupsConnector interface {
Groups(identity storage.Identity) ([]string, error)
}
+177
View File
@@ -0,0 +1,177 @@
// Package github provides authentication strategies using GitHub.
package github
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
"github.com/coreos/poke/connector"
"github.com/coreos/poke/storage"
)
const baseURL = "https://api.github.com"
// Config holds configuration options for github logins.
type Config struct {
ClientID string `yaml:"clientID"`
ClientSecret string `yaml:"clientSecret"`
RedirectURI string `yaml:"redirectURI"`
Org string `yaml:"org"`
}
// Open returns a strategy for logging in through GitHub.
func (c *Config) Open() (connector.Connector, error) {
return &githubConnector{
redirectURI: c.RedirectURI,
org: c.Org,
oauth2Config: &oauth2.Config{
ClientID: os.ExpandEnv(c.ClientID),
ClientSecret: os.ExpandEnv(c.ClientSecret),
Endpoint: github.Endpoint,
Scopes: []string{
"user:email", // View user's email
"read:org", // View user's org teams.
},
},
}, nil
}
type connectorData struct {
// GitHub's OAuth2 tokens never expire. We don't need a refresh token.
AccessToken string `json:"accessToken"`
}
var (
_ connector.CallbackConnector = (*githubConnector)(nil)
_ connector.GroupsConnector = (*githubConnector)(nil)
)
type githubConnector struct {
redirectURI string
org string
oauth2Config *oauth2.Config
ctx context.Context
cancel context.CancelFunc
}
func (c *githubConnector) Close() error {
return nil
}
func (c *githubConnector) LoginURL(callbackURL, state string) (string, error) {
if c.redirectURI != callbackURL {
return "", fmt.Errorf("expected callback URL did not match the URL in the config")
}
return c.oauth2Config.AuthCodeURL(state), nil
}
type oauth2Error struct {
error string
errorDescription string
}
func (e *oauth2Error) Error() string {
if e.errorDescription == "" {
return e.error
}
return e.error + ": " + e.errorDescription
}
func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) {
q := r.URL.Query()
if errType := q.Get("error"); errType != "" {
return identity, "", &oauth2Error{errType, q.Get("error_description")}
}
token, err := c.oauth2Config.Exchange(c.ctx, q.Get("code"))
if err != nil {
return identity, "", fmt.Errorf("github: failed to get token: %v", err)
}
resp, err := c.oauth2Config.Client(c.ctx, token).Get(baseURL + "/user")
if err != nil {
return identity, "", fmt.Errorf("github: get URL %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return identity, "", fmt.Errorf("github: read body: %v", err)
}
return identity, "", fmt.Errorf("%s: %s", resp.Status, body)
}
var user struct {
Name string `json:"name"`
Login string `json:"login"`
ID int `json:"id"`
Email string `json:"email"`
}
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return identity, "", fmt.Errorf("failed to decode response: %v", err)
}
data := connectorData{AccessToken: token.AccessToken}
connData, err := json.Marshal(data)
if err != nil {
return identity, "", fmt.Errorf("marshal connector data: %v", err)
}
username := user.Name
if username == "" {
username = user.Login
}
identity = storage.Identity{
UserID: strconv.Itoa(user.ID),
Username: username,
Email: user.Email,
EmailVerified: true,
ConnectorData: connData,
}
return identity, q.Get("state"), nil
}
func (c *githubConnector) Groups(identity storage.Identity) ([]string, error) {
var data connectorData
if err := json.Unmarshal(identity.ConnectorData, &data); err != nil {
return nil, fmt.Errorf("decode connector data: %v", err)
}
token := &oauth2.Token{AccessToken: data.AccessToken}
resp, err := c.oauth2Config.Client(c.ctx, token).Get(baseURL + "/user/teams")
if err != nil {
return nil, fmt.Errorf("github: get teams: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("github: read body: %v", err)
}
return nil, fmt.Errorf("%s: %s", resp.Status, body)
}
// https://developer.github.com/v3/orgs/teams/#response-12
var teams []struct {
Name string `json:"name"`
Org struct {
Login string `json:"login"`
} `json:"organization"`
}
if err := json.NewDecoder(resp.Body).Decode(&teams); err != nil {
return nil, fmt.Errorf("github: unmarshal groups: %v", err)
}
groups := []string{}
for _, team := range teams {
if team.Org.Login == c.org {
groups = append(groups, team.Name)
}
}
return groups, nil
}
+59
View File
@@ -0,0 +1,59 @@
// Package ldap implements strategies for authenticating using the LDAP protocol.
package ldap
import (
"errors"
"fmt"
"gopkg.in/ldap.v2"
"github.com/coreos/poke/connector"
"github.com/coreos/poke/storage"
)
// Config holds the configuration parameters for the LDAP connector.
type Config struct {
Host string `yaml:"host"`
BindDN string `yaml:"bindDN"`
}
// Open returns an authentication strategy using LDAP.
func (c *Config) Open() (connector.Connector, error) {
if c.Host == "" {
return nil, errors.New("missing host parameter")
}
if c.BindDN == "" {
return nil, errors.New("missing bindDN paramater")
}
return &ldapConnector{*c}, nil
}
type ldapConnector struct {
Config
}
func (c *ldapConnector) do(f func(c *ldap.Conn) error) error {
// TODO(ericchiang): Connection pooling.
conn, err := ldap.Dial("tcp", c.Host)
if err != nil {
return fmt.Errorf("failed to connect: %v", err)
}
defer conn.Close()
return f(conn)
}
func (c *ldapConnector) Login(username, password string) (storage.Identity, error) {
err := c.do(func(conn *ldap.Conn) error {
return conn.Bind(fmt.Sprintf("uid=%s,%s", username, c.BindDN), password)
})
if err != nil {
return storage.Identity{}, err
}
return storage.Identity{Username: username}, nil
}
func (c *ldapConnector) Close() error {
return nil
}
+53
View File
@@ -0,0 +1,53 @@
// Package mock implements a mock connector which requires no user interaction.
package mock
import (
"fmt"
"net/http"
"net/url"
"github.com/coreos/poke/connector"
"github.com/coreos/poke/storage"
)
// New returns a mock connector which requires no user interaction. It always returns
// the same (fake) identity.
func New() connector.Connector {
return mockConnector{}
}
type mockConnector struct{}
func (m mockConnector) Close() error { return nil }
func (m mockConnector) LoginURL(callbackURL, state string) (string, error) {
u, err := url.Parse(callbackURL)
if err != nil {
return "", fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err)
}
v := u.Query()
v.Set("state", state)
u.RawQuery = v.Encode()
return u.String(), nil
}
func (m mockConnector) HandleCallback(r *http.Request) (storage.Identity, string, error) {
return storage.Identity{
UserID: "0-385-28089-0",
Username: "Kilgore Trout",
Email: "kilgore@kilgore.trout",
EmailVerified: true,
}, r.URL.Query().Get("state"), nil
}
func (m mockConnector) Groups(identity storage.Identity) ([]string, error) {
return []string{"authors"}, nil
}
// Config holds the configuration parameters for the mock connector.
type Config struct{}
// Open returns an authentication strategy which requires no user interaction.
func (c *Config) Open() (connector.Connector, error) {
return New(), nil
}
+2
View File
@@ -0,0 +1,2 @@
// Package oidc implements logging in through OpenID Connect providers.
package oidc
+9
View File
@@ -0,0 +1,9 @@
kind: OAuth2Client
apiVersion: oauth2clients.oidc.coreos.com/v1
metadata:
name: example-app
namespace: default
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
redirectURIs:
- http://127.0.0.1:5555/callback
name: Example App
+20
View File
@@ -0,0 +1,20 @@
issuer: http://127.0.0.1:5556
storage:
type: kubernetes
web:
http: 127.0.0.1:5556
connectors:
- type: mock
id: mock
name: Mock
- type: github
id: github
name: GitHub
config:
clientID: "$GITHUB_CLIENT_ID"
clientSecret: "$GITHUB_CLIENT_SECRET"
redirectURI: http://127.0.0.1:5556/callback/github
org: kubernetes
+48
View File
@@ -0,0 +1,48 @@
# NOTE: Because of a bug in third party resources, each resource must be in it's
# own API Group.
#
# See fix at https://github.com/kubernetes/kubernetes/pull/28414
metadata:
name: auth-code.authcodes.oidc.coreos.com
apiVersion: extensions/v1beta1
kind: ThirdPartyResource
description: "A code which can be claimed for an access token."
versions:
- name: v1
---
metadata:
name: auth-request.authrequests.oidc.coreos.com
apiVersion: extensions/v1beta1
kind: ThirdPartyResource
description: "A request for an end user to authorize a client."
versions:
- name: v1
---
metadata:
name: o-auth2-client.oauth2clients.oidc.coreos.com
apiVersion: extensions/v1beta1
kind: ThirdPartyResource
description: "An OpenID Connect client."
versions:
- name: v1
---
metadata:
name: signing-key.signingkeies.oidc.coreos.com
apiVersion: extensions/v1beta1
kind: ThirdPartyResource
description: "Keys used to sign and verify OpenID Connect tokens."
versions:
- name: v1
---
metadata:
name: refresh-token.refreshtokens.oidc.coreos.com
apiVersion: extensions/v1beta1
kind: ThirdPartyResource
description: "Refresh tokens for clients to continuously act on behalf of an end user."
versions:
- name: v1
Generated
+67
View File
@@ -0,0 +1,67 @@
hash: 8b33b8abf5bca183ffa7108b03f4001f4d23f7f2e26f773f5bcb4bfefa51a26f
updated: 2016-07-22T23:25:33.173188655-07:00
imports:
- name: github.com/ericchiang/oidc
version: 69fec81d167d815f4f455c741b2a94ffaf547ed2
- name: github.com/golang/protobuf
version: 874264fbbb43f4d91e999fecb4b40143ed611400
subpackages:
- proto
- name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/gorilla/mux
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
- name: github.com/gtank/cryptopasta
version: e7e23673cac3f529f49e22f94e4af6d12bb49dba
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/mitchellh/go-homedir
version: 756f7b183b7ab78acdbbee5c7f392838ed459dda
- name: github.com/pquerna/cachecontrol
version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868
subpackages:
- cacheobject
- name: github.com/spf13/cobra
version: bc81c21bd0d8be5ba2d6630a505d79d4467566e7
- name: github.com/spf13/pflag
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
- name: golang.org/x/crypto
version: 2c99acdd1e9b90d779ca23f632aad86af9909c62
subpackages:
- bcrypt
- blowfish
- name: golang.org/x/net
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab
subpackages:
- context
- name: golang.org/x/oauth2
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
subpackages:
- internal
- name: google.golang.org/appengine
version: 267c27e7492265b84fc6719503b14a1e17975d79
subpackages:
- urlfetch
- internal
- internal/urlfetch
- internal/base
- internal/datastore
- internal/log
- internal/remote_api
- name: gopkg.in/asn1-ber.v1
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- name: gopkg.in/ldap.v2
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
- name: gopkg.in/square/go-jose.v1
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
subpackages:
- cipher
- json
- name: gopkg.in/square/go-jose.v2
version: f209f41628247c56938cb20ef51d589ddad6c30b
subpackages:
- cipher
- json
- name: gopkg.in/yaml.v2
version: a83829b6f1293c91addabc89d0571c246397bbf4
testImports: []
+63
View File
@@ -0,0 +1,63 @@
package: github.com/coreos/poke
import:
- package: github.com/spf13/cobra
version: bc81c21bd0d8be5ba2d6630a505d79d4467566e7
- package: github.com/spf13/pflag
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
- package: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- package: gopkg.in/ldap.v2
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
- package: gopkg.in/asn1-ber.v1
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
- package: gopkg.in/square/go-jose.v2
version: f209f41628247c56938cb20ef51d589ddad6c30b
subpackages:
- cipher
- json
- package: gopkg.in/yaml.v2
version: a83829b6f1293c91addabc89d0571c246397bbf4
- package: golang.org/x/net/context
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab
- package: github.com/gorilla/mux
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
- package: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- package: github.com/gtank/cryptopasta
version: e7e23673cac3f529f49e22f94e4af6d12bb49dba
- package: golang.org/x/crypto
version: 2c99acdd1e9b90d779ca23f632aad86af9909c62
subpackages:
- bcrypt
- package: github.com/ericchiang/oidc
version: 69fec81d167d815f4f455c741b2a94ffaf547ed2
- package: github.com/pquerna/cachecontrol
version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868
- package: gopkg.in/square/go-jose.v1
version: v1.0.2
- package: golang.org/x/oauth2
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
- package: google.golang.org/appengine
version: 267c27e7492265b84fc6719503b14a1e17975d79
subpackages:
- urlfetch
- internal
- internal/urlfetch
- internal/base
- internal/datastore
- internal/log
- internal/remote_api
- package: github.com/golang/protobuf
version: 874264fbbb43f4d91e999fecb4b40143ed611400
subpackages:
- proto
- package: github.com/mitchellh/go-homedir
verison: 756f7b183b7ab78acdbbee5c7f392838ed459dda
+112
View File
@@ -0,0 +1,112 @@
package main
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"gopkg.in/yaml.v2"
)
type glideLock struct {
Imports []struct {
Name string `yaml:"name"`
Subpackages []string `yaml:"subpackages,omitempty"`
} `yaml:"imports"`
TestImports []struct {
Name string `yaml:"name"`
Subpackages []string `yaml:"subpackages,omitempty"`
} `yaml:"testImports"`
}
type glideYAML struct {
Imports []struct {
Name string `yaml:"package"`
} `yaml:"import"`
}
func loadYAML(t *testing.T, file string, v interface{}) {
data, err := ioutil.ReadFile(file)
if err != nil {
t.Fatalf("read file %s: %v", file, err)
}
if err := yaml.Unmarshal(data, v); err != nil {
t.Fatalf("unmarshal file %s: %v", file, err)
}
return
}
// TestGlideYAMLPinsAllDependencies ensures that all packages listed in glide.lock also
// appear in glide.yaml which can get out of sync if glide.yaml fails to list transitive
// dependencies.
//
// Testing this ensures developers can update individual packages without grabbing the HEAD
// of an unspecified dependency.
func TestGlideYAMLPinsAllDependencies(t *testing.T) {
var (
lockPackages glideLock
yamlPackages glideYAML
)
loadYAML(t, "glide.lock", &lockPackages)
loadYAML(t, "glide.yaml", &yamlPackages)
if len(yamlPackages.Imports) == 0 {
t.Fatalf("no packages found in glide.yaml")
}
pkgs := make(map[string]bool)
for _, pkg := range yamlPackages.Imports {
pkgs[pkg.Name] = true
}
for _, pkg := range lockPackages.Imports {
if pkgs[pkg.Name] {
continue
}
if len(pkg.Subpackages) == 0 {
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkg.Name)
continue
}
for _, subpkg := range pkg.Subpackages {
pkgName := path.Join(pkg.Name, subpkg)
if !pkgs[pkgName] {
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkgName)
}
}
}
for _, pkg := range lockPackages.TestImports {
if pkgs[pkg.Name] {
continue
}
if len(pkg.Subpackages) == 0 {
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkg.Name)
continue
}
for _, subpkg := range pkg.Subpackages {
pkgName := path.Join(pkg.Name, subpkg)
if !pkgs[pkgName] {
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkgName)
}
}
}
}
func TestRemoveVersionControl(t *testing.T) {
err := filepath.Walk("vendor", func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatalf("walk: stat path %s failed: %v", path, err)
}
if info.IsDir() && filepath.Base(path) == ".git" {
t.Fatalf(".git directory detected in vendor: %s. Revendor packages and remove version control data with 'glide update -s -v -u'", path)
}
return nil
})
if err != nil {
t.Fatalf("walk: %v", err)
}
}
+2
View File
@@ -0,0 +1,2 @@
// Package server implements an OpenID Connect server with federated logins.
package server

Some files were not shown because too many files have changed in this diff Show More