Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Stash] Implement gitprovider interfaces #114

Merged
merged 2 commits into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ jobs:
[ -n "${{ secrets.GITLAB_TEST_SUBGROUP }}" ] && export GITLAB_TEST_SUBGROUP=${{ secrets.GITLAB_TEST_SUBGROUP }} || echo "using default GITLAB_TEST_SUBGROUP"
[ -n "${{ secrets.TEST_VERBOSE }}" ] && export TEST_VERBOSE=${{ secrets.TEST_VERBOSE }} || echo "TEST_VERBOSE not set"
[ -n "${{ secrets.CLEANUP_ALL }}" ] && export CLEANUP_ALL=${{ secrets.CLEANUP_ALL }} || echo "CLEANUP_ALL not set"
[ -n "${{ secrets.STASH_TOKEN }}" ] && export STASH_TOKEN=${{ secrets.STASH_TOKEN }} || echo "using default STASH_TOKEN"
[ -n "${{ secrets.STASH_USER }}" ] && export STASH_USER=${{ secrets.STASH_USER }} || echo "using default STASH_USER"
[ -n "${{ secrets.STASH_DOMAIN }}" ] && export STASH_DOMAIN=${{ secrets.STASH_DOMAIN }} || echo "using default STASH_DOMAIN"
[ -n "${{ secrets.STASH_TEST_TEAM_NAME }}" ] && export STASH_TEST_TEAM_NAME=${{ secrets.STASH_TEST_TEAM_NAME }} || echo "using default STASH_TEST_TEAM_NAME"
[ -n "${{ secrets.STASH_TEST_REPO_NAME }}" ] && export STASH_TEST_REPO_NAME=${{ secrets.STASH_TEST_REPO_NAME }} || echo "using default STASH_TEST_REPO_NAME"
make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./coverage.txt

file: ./coverage.txt
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
VER?=0.0.1
TEST_VERBOSE?=
TEST_PATTERN?=./...
TEST_STOP_ON_ERROR?=
PKG_CONFIG_PATH?=${PKG_CONFIG_PATH}

all: test

Expand All @@ -13,10 +16,8 @@ vet:
go vet ./...

test: tidy fmt vet
go test ${TEST_VERBOSE} -race -coverprofile=coverage.txt -covermode=atomic ./...
go test ${TEST_VERBOSE} ${TEST_STOP_ON_ERROR} -race -coverprofile=coverage.txt -covermode=atomic ${TEST_PATTERN}

release:
git checkout main
git pull
git tag "v$(VER)"
git push origin "v$(VER)"
git pull
1 change: 1 addition & 0 deletions github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ var permissionScopes = map[gitprovider.TokenPermission]string{
gitprovider.TokenPermissionRWRepository: "repo",
}

// HasTokenPermission returns true if the given token has the given permissions.
func (c *Client) HasTokenPermission(ctx context.Context, permission gitprovider.TokenPermission) (bool, error) {
requestedScope, ok := permissionScopes[permission]
if !ok {
Expand Down
4 changes: 2 additions & 2 deletions github/example_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func ExampleOrgRepositoriesClient_Get() {
checkErr(err)

// Parse the URL into an OrgRepositoryRef
ref, err := gitprovider.ParseOrgRepositoryURL("https://github.com/fluxcd/flux")
ref, err := gitprovider.ParseOrgRepositoryURL("https://github.com/fluxcd/flux2")
checkErr(err)

// Get public information about the flux repository.
Expand All @@ -29,5 +29,5 @@ func ExampleOrgRepositoriesClient_Get() {
internalRepo := repo.APIObject().(*gogithub.Repository)

fmt.Printf("Description: %s. Homepage: %s", *repoInfo.Description, internalRepo.GetHomepage())
// Output: Description: The GitOps Kubernetes operator. Homepage: https://docs.fluxcd.io
// Output: Description: Open and extensible continuous delivery solution for Kubernetes. Powered by GitOps Toolkit.. Homepage: https://fluxcd.io
}
2 changes: 1 addition & 1 deletion github/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ var _ = Describe("GitHub Provider", func() {
path := "setup/config.txt"
content := "yaml content"
files := []gitprovider.CommitFile{
gitprovider.CommitFile{
{
Path: &path,
Content: &content,
},
Expand Down
1 change: 1 addition & 0 deletions gitlab/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func (c *Client) UserRepositories() gitprovider.UserRepositoriesClient {
return c.userRepos
}

// HasTokenPermission returns true if the given token has the given permissions.
func (c *Client) HasTokenPermission(ctx context.Context, permission gitprovider.TokenPermission) (bool, error) {
return false, gitprovider.ErrNoProviderSupport
}
2 changes: 1 addition & 1 deletion gitlab/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ var _ = Describe("GitLab Provider", func() {
path := "setup/config.txt"
content := "yaml content"
files := []gitprovider.CommitFile{
gitprovider.CommitFile{
{
Path: &path,
Content: &content,
},
Expand Down
3 changes: 2 additions & 1 deletion gitprovider/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,11 @@ func LicenseTemplateVar(t LicenseTemplate) *LicenseTemplate {
return &t
}

// TokenPermission is an enum specifying the permissions for a token.
type TokenPermission int

const (
// Read/Write permission for public/private repositories.
// TokenPermissionRWRepository Read/Write permission for public/private repositories.
TokenPermissionRWRepository TokenPermission = iota + 1
)

Expand Down
2 changes: 1 addition & 1 deletion gitprovider/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (opts *RepositoryCreateOptions) ApplyToRepositoryCreateOptions(target *Repo
}
}

// ValidateInfo validates that the options are valid.
// ValidateOptions validates that the options are valid.
func (opts *RepositoryCreateOptions) ValidateOptions() error {
errs := validation.New("RepositoryCreateOptions")
if opts.LicenseTemplate != nil {
Expand Down
57 changes: 57 additions & 0 deletions gitprovider/repositoryref.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ type IdentityRef interface {
String() string
}

// Keyer is an interface that can be used to get a unique key for an object.
type Keyer interface {
// Key returns a unique key for this object.
Key() string
}

// RepositoryRef describes a reference to a repository owned by either a user account or organization.
type RepositoryRef interface {
// RepositoryRef is a superset of IdentityRef.
Expand All @@ -71,6 +77,12 @@ type RepositoryRef interface {
GetCloneURL(transport TransportType) string
}

// Slugger is an interface that can be used to get a unique slug for an object.
type Slugger interface {
// Slug returns the unique slug for this object.
Slug() string
}

// UserRef represents a user account in a Git provider.
type UserRef struct {
// Domain returns e.g. "github.com", "gitlab.com" or a custom domain like "self-hosted-gitlab.com" (GitLab)
Expand Down Expand Up @@ -133,6 +145,11 @@ type OrganizationRef struct {
// +required
Organization string `json:"organization"`

// key specifies the URL-friendly, lowercase key of the organization,
// e.g. "fluxcd" or "kubernetes-sigs".
// +optional
key string

// SubOrganizations point to optional sub-organizations (or sub-groups) of the given top-level organization
// in the Organization field. E.g. "gitlab.com/fluxcd/engineering/frontend" would yield ["engineering", "frontend"]
// +optional
Expand All @@ -150,6 +167,16 @@ func (o OrganizationRef) GetIdentity() string {
return strings.Join(orgParts, "/")
}

// Key returns the unique key for this OrganizationRef.
func (o OrganizationRef) Key() string {
return o.key
}

// SetKey sets the unique key for this OrganizationRef.
func (o *OrganizationRef) SetKey(key string) {
o.key = key
}

// GetType marks this UserRef as being a IdentityTypeUser.
func (o OrganizationRef) GetType() IdentityType {
if len(o.SubOrganizations) > 0 {
Expand Down Expand Up @@ -184,6 +211,11 @@ type OrgRepositoryRef struct {
// e.g. "kubernetes" or "cluster-api-provider-aws".
// +required
RepositoryName string `json:"repositoryName"`

// slug specifies the Git repository slug. This field is URL-friendly,
// e.g. "kubernetes" or "cluster-api-provider-aws".
// +optional
slug string
}

// String returns the HTTPS URL to access the repository.
Expand All @@ -196,6 +228,16 @@ func (r OrgRepositoryRef) GetRepository() string {
return r.RepositoryName
}

// Slug returns the unique slug for this object.
func (r OrgRepositoryRef) Slug() string {
return r.slug
}

// SetSlug sets the unique slug for this object.
func (r *OrgRepositoryRef) SetSlug(slug string) {
r.slug = slug
}

// ValidateFields validates its own fields for a given validator.
func (r OrgRepositoryRef) ValidateFields(validator validation.Validator) {
// First, validate the embedded OrganizationRef
Expand All @@ -220,6 +262,11 @@ type UserRepositoryRef struct {
// e.g. "kubernetes" or "cluster-api-provider-aws".
// +required
RepositoryName string `json:"repositoryName"`

// slug specifies the Git repository slug. This field is URL-friendly,
// e.g. "kubernetes" or "cluster-api-provider-aws".
// +optional
slug string
}

// String returns the URL to access the repository.
Expand All @@ -232,6 +279,16 @@ func (r UserRepositoryRef) GetRepository() string {
return r.RepositoryName
}

// GetSlug returns the unique slug for this object.
func (r UserRepositoryRef) GetSlug() string {
return r.slug
}

// SetSlug sets the unique slug for this object.
func (r *UserRepositoryRef) SetSlug(slug string) {
r.slug = slug
}

// ValidateFields validates its own fields for a given validator.
func (r UserRepositoryRef) ValidateFields(validator validation.Validator) {
// First, validate the embedded OrganizationRef
Expand Down
1 change: 1 addition & 0 deletions gitprovider/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ type Commit interface {
Get() CommitInfo
}

// PullRequest represents a pull request.
type PullRequest interface {
// Object implements the Object interface,
// allowing access to the underlying object returned from the API.
Expand Down
1 change: 1 addition & 0 deletions gitprovider/types_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ type CommitFile struct {
Content *string `json:"content"`
}

// PullRequestInfo contains high-level information about a pull request.
type PullRequestInfo struct {
// Merged specifes whether or not this pull request has been merged
Merged bool `json:"merged"`
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/google/go-github/v32 v32.1.0
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.6.4
github.com/ktrysmt/go-bitbucket v0.6.2
github.com/onsi/ginkgo v1.14.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
Expand Down
73 changes: 73 additions & 0 deletions stash/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Copyright 2021 The Flux authors

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

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

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

package stash

import (
"errors"
"net/url"

"github.com/fluxcd/go-git-providers/gitprovider"
"github.com/go-logr/logr"
)

// NewStashClient creates a new Client instance for Stash API endpoints.
// The client accepts a username+token as an argument, which is used to authenticate.
// The host name is used to construct the base URL for the Stash API.
// Variadic parameters gitprovider.ClientOption are used to pass additional options to the gitprovider.Client.
func NewStashClient(username, token string, optFns ...gitprovider.ClientOption) (*ProviderClient, error) {
url := &url.URL{}

opts, err := gitprovider.MakeClientOptions(optFns...)
if err != nil {
return nil, err
}

// Create a *http.Client using the transport chain
client, err := gitprovider.BuildClientFromTransportChain(opts.GetTransportChain())
if err != nil {
return nil, err
}

if opts.Domain == nil {
return nil, errors.New("host is required")
}

host := *opts.Domain

logger := logr.Discard()
if opts.Logger != nil {
logger = *opts.Logger
}

url, err = url.Parse(host)
if err != nil {
return nil, err
}

st, err := NewClient(client, host, nil, &logger, WithAuth(username, token))
if err != nil {
return nil, err
}

// By default, turn destructive actions off. But allow overrides.
destructiveActions := false
if opts.EnableDestructiveAPICalls != nil {
destructiveActions = *opts.EnableDestructiveAPICalls
}

return newClient(st, host, token, destructiveActions, logger), nil
}
Loading