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

chaincode builder #121

Closed
wants to merge 10 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: '1.16'
go-version: '1.17'

- name: Check out code
uses: actions/checkout@v2
Expand Down
72 changes: 72 additions & 0 deletions builder/chaincode/fetcher/fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package fetcher

import (
"context"
"errors"
"fmt"
"net/url"
"strings"

"go.uber.org/zap"
)

const (
FileProtocolPrefix = `file://`
GitProtocolPrefix = `https://`
LocalMountProtocolPrefix = `local://`
)

var (
ErrUnknownProtocol = errors.New(`unknown protocol`)
)

type Fetcher interface {
// Fetch code by repo uri and version, returns tar representation
Fetch(ctx context.Context, repo, version string) ([]byte, error)
}

// Fetch repo from file path , git repo
// file - file://path/to/repo
// git repo with basic auth and token - https://{user}:{pass}@github.com/s7techlab/{repo}.git
// git repo with token - https://{token}@github.com/s7techlab/{repo}.git
func Fetch(ctx context.Context, repo, version string, logger *zap.Logger) ([]byte, error) {
f, err := Create(repo, logger)
if err != nil {
return nil, fmt.Errorf("fetcher get: %w", err)
}

return f.Fetch(ctx, repo, version)
}

// Create returns Fetcher instance by repo scheme
// Scheme file:// returns FileFetcher
// Scheme https:// returns GitFetcher
// Scheme local:// returns Local mount fetcher
// git repo with basic auth and token - https://{user}:{pass}@github.com/s7techlab/{repo}.git
// git repo with token - https://{token}@github.com/s7techlab/{repo}.git
// Returns non-fetcher for unrecognized scheme
func Create(repo string, l *zap.Logger) (Fetcher, error) {
switch {
case strings.HasPrefix(repo, FileProtocolPrefix):
return NewFile(l), nil
case strings.HasPrefix(repo, GitProtocolPrefix):
var opts []GitOpt
// Set auth options for repository if provided
u, err := url.Parse(repo)
if err != nil {
return nil, fmt.Errorf("parse repo url: %w", err)
}
if p, set := u.User.Password(); set {
opts = append(opts, GitBasicAuth(u.User.Username(), p))
} else if u.User.Username() != `` {
opts = append(opts, GitTokenAuth(u.User.Username()))
}
opts = append(opts, WithLogger(l))
return NewGit(opts...), nil
case strings.HasPrefix(repo, LocalMountProtocolPrefix):
return NewNoneFetcher(), nil

default:
return nil, ErrUnknownProtocol
}
}
47 changes: 47 additions & 0 deletions builder/chaincode/fetcher/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package fetcher

import (
"archive/tar"
"bytes"
"context"
"fmt"
"strings"

"github.com/go-git/go-billy/v5/osfs"
"go.uber.org/zap"
)

type file struct {
Logger *zap.Logger
}

func NewFile(l *zap.Logger) *file {
return &file{
Logger: l,
}
}

func (f *file) Fetch(ctx context.Context, path, _ string) (code []byte, err error) {
bf := new(bytes.Buffer)
tw := tar.NewWriter(bf)

defer func() {
twErr := tw.Close()
if err == nil && twErr != nil {
err = fmt.Errorf("close tar writer: %w", err)
}
}()

path = strings.TrimPrefix(path, FileProtocolPrefix)

fs := osfs.New(path)

f.Logger.Debug(`adding path to tar`, zap.String(`path`, path))

if err = addFileToTar(tw, `/`, fs); err != nil {
err = fmt.Errorf("fetch filepath=%s to tar: %w", path, err)
return
}

return bf.Bytes(), nil
}
44 changes: 44 additions & 0 deletions builder/chaincode/fetcher/file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package fetcher_test

import (
"archive/tar"
"bytes"
"context"
"io"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"go.uber.org/zap"

"github.com/s7techlab/hlf-sdk-go/builder/chaincode/fetcher"
)

func expectTar(b []byte) error {
r := bytes.NewReader(b)
tr := tar.NewReader(r)
for {
_, err := tr.Next()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
}
}

func TestFileFetcher_Fetch(t *testing.T) {
var f fetcher.Fetcher

f = fetcher.NewFile(zap.NewNop())
assert.NotNil(t, f)

abs, err := filepath.Abs(`../`)
assert.NoError(t, err)

code, err := f.Fetch(context.Background(), abs+`/`, `some ver`)
assert.NoError(t, err)
assert.NotNil(t, code)
assert.NoError(t, expectTar(code))
}
132 changes: 132 additions & 0 deletions builder/chaincode/fetcher/gogit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package fetcher

import (
"archive/tar"
"bytes"
"context"
"fmt"
"net/url"

"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage"
"github.com/go-git/go-git/v5/storage/memory"
"go.uber.org/zap"
)

const defaultMode = 0755

type (
Git struct {
s storage.Storer
auth transport.AuthMethod
Logger *zap.Logger
}

GitOpt func(*Git)
)

func WithLogger(l *zap.Logger) GitOpt {
return func(g *Git) {
g.Logger = l
}
}

func GitBasicAuth(username, password string) GitOpt {
return func(g *Git) {
g.auth = &http.BasicAuth{
Username: username,
Password: password,
}
}
}

func GitTokenAuth(token string) GitOpt {
return func(g *Git) {
g.auth = &http.TokenAuth{Token: token}
}
}

func NewGit(opts ...GitOpt) *Git {
git := &Git{s: memory.NewStorage()}
for _, o := range opts {
o(git)
}

if git.Logger == nil {
git.Logger = zap.NewNop()
}
return git
}

func (g *Git) prepareUrl(rawUrl string) (string, error) {
u, err := url.Parse(rawUrl)
if err != nil {
return ``, fmt.Errorf("failed to parse url: %w", err)
}

if u.Scheme == `` {
u.Scheme = `https`
}

if u.User != nil {
if pass, ok := u.User.Password(); ok {
GitBasicAuth(u.User.Username(), pass)(g)
}

} else if u.Fragment != `` {
// consider as token
GitTokenAuth(u.Fragment)(g)
u.Fragment = ``
}
return u.String(), nil
}

func (g Git) Fetch(ctx context.Context, repo, version string) ([]byte, error) {

url, err := g.prepareUrl(repo)
if err != nil {
return nil, fmt.Errorf("prepare repo url: %w", err)
}

fs := memfs.New()

var refName plumbing.ReferenceName

if version != `` {
refName = plumbing.NewHashReference(plumbing.Master, plumbing.NewHash(version)).Target()
}

g.Logger.Info(`fetching git repo`,
zap.String(`url`, url),
zap.String(`auth`, fmt.Sprintf(`%T`, g.auth)))

if _, err = git.CloneContext(ctx, g.s, fs, &git.CloneOptions{

Check failure on line 107 in builder/chaincode/fetcher/gogit.go

View workflow job for this annotation

GitHub Actions / lint (1.18.x, ubuntu-latest)

undefined: git (typecheck)
URL: url,
Auth: g.auth,
SingleBranch: true,
Progress: nil,
ReferenceName: refName,
}); err != nil {
return nil, fmt.Errorf("clone repository=%s, version=%s: %w", url, version, err)
}

bf := new(bytes.Buffer)
tw := tar.NewWriter(bf)

defer func() {
twErr := tw.Close()
if err == nil && twErr != nil {
err = fmt.Errorf("close tar writer: %w", err)
}
}()

if err = addFileToTar(tw, fs.Root(), fs); err != nil {
return nil, fmt.Errorf("add file to archive: %w", err)
}

return bf.Bytes(), nil
}
28 changes: 28 additions & 0 deletions builder/chaincode/fetcher/gogit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package fetcher_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/s7techlab/hlf-sdk-go/builder/chaincode/fetcher"
"github.com/s7techlab/hlf-sdk-go/builder/chaincode/testdata"
)

func TestNewGoGitFetcher(t *testing.T) {
f := fetcher.NewGit(fetcher.GitBasicAuth(testdata.User, testdata.Password))
assert.NotNil(t, f)
}

func TestGoGitFetcher_Fetch(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}

//for _, p := range testdata.Packages {
// code, err := f.Fetch(context.Background(), p.Repository, p.Version)
// assert.NoError(t, err)
// assert.NotNil(t, code)
// assert.NoError(t, expectTar(code))
//}
}
15 changes: 15 additions & 0 deletions builder/chaincode/fetcher/none.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fetcher

import "context"

// noneFetcher do nothing
type noneFetcher struct {
}

func (n noneFetcher) Fetch(ctx context.Context, repo, version string) ([]byte, error) {
return nil, nil
}

func NewNoneFetcher() *noneFetcher {
return &noneFetcher{}
}
Loading
Loading