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

feat(cloud): Add intermediate github deployer #1750

Open
wants to merge 3 commits into
base: staging
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/gobwas/glob v0.2.3
github.com/google/go-containerregistry v0.19.2
github.com/google/go-github/v32 v32.1.0
github.com/google/go-github/v39 v39.2.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/henvic/httpretty v0.1.3
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@ github.com/google/go-containerregistry v0.19.2 h1:TannFKE1QSajsP6hPWb5oJNgKe1IKj
github.com/google/go-containerregistry v0.19.2/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ=
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
Expand Down Expand Up @@ -1318,6 +1320,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
Expand Down Expand Up @@ -1622,6 +1625,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
Expand Down
1 change: 1 addition & 0 deletions internal/cli/kraft/cloud/deploy/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type deployer interface {
func deployers() []deployer {
return []deployer{
&deployerImageName{},
&deployerKraftfileRepo{},
&deployerKraftfileRuntime{},
&deployerKraftfileUnikraft{},
}
Expand Down
204 changes: 204 additions & 0 deletions internal/cli/kraft/cloud/deploy/deployer_kraftfile_github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package deploy

import (
"context"
"fmt"
"path/filepath"
"sort"
"strings"

"github.com/google/go-github/v39/github"
"golang.org/x/oauth2"

"kraftkit.sh/config"
"kraftkit.sh/internal/ghrepo"
"kraftkit.sh/manifest"
"kraftkit.sh/pack"
kcclient "sdk.kraft.cloud/client"
kcinstances "sdk.kraft.cloud/instances"
kcservices "sdk.kraft.cloud/services"
)

const treeSeparator = "/tree/"

type deployerKraftfileRepo struct {
args []string
url string
}

func (d *deployerKraftfileRepo) Name() string {
return "kraftfile-repo"
}

func (d *deployerKraftfileRepo) String() string {
if len(d.args) == 0 {
return "run the given link with a Kraftfile"
}

return fmt.Sprintf("run the detected Kraftfile in the given link after cloning and use '%s' as arg(s)", strings.Join(d.args, " "))
}

func (d *deployerKraftfileRepo) Deployable(ctx context.Context, opts *DeployOptions, args ...string) (bool, error) {
url := args[0]

if !strings.Contains(url, "github.com") {
return false, nil
}

if strings.Contains(url, treeSeparator) {
url = strings.Split(url, treeSeparator)[0]
}

_, err := ghrepo.NewFromURL(url)
if err != nil {
return false, err
}

d.url = args[0]
d.args = args[1:]

return true, nil
}

// getAllBranchesSorted returns all branches of a given repository sorted
// by size in descending order.
// If no token is specified, it will only have access to public repositories
func getAllBranchesSorted(ctx context.Context, owner, repo, token string) ([]string, error) {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)

client := github.NewClient(tc)

var allBranches []*github.Branch
opt := &github.BranchListOptions{ListOptions: github.ListOptions{PerPage: 100}}
for {
branches, resp, err := client.Repositories.ListBranches(ctx, owner, repo, opt)
if err != nil {
return nil, err
}
allBranches = append(allBranches, branches...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}

var branchNames []string
for _, branch := range allBranches {
branchNames = append(branchNames, branch.GetName())
}

// Sort all branches names by size in descending order
// This is done to ensure that the longest name is the first one
sort.Slice(branchNames, func(i, j int) bool {
return len(branchNames[i]) > len(branchNames[j])
})

return branchNames, nil
}

func (d *deployerKraftfileRepo) Deploy(ctx context.Context, opts *DeployOptions, _ ...string) (*kcclient.ServiceResponse[kcinstances.GetResponseItem], *kcclient.ServiceResponse[kcservices.GetResponseItem], error) {
var err error
var ghProvider manifest.Provider

link := d.url
branch := ""
path := "."

if strings.Contains(d.url, treeSeparator) {
split1 := strings.SplitN(d.url, treeSeparator, 2)
link = split1[0]
}

repo, err := ghrepo.NewFromURL(link)
if err != nil {
return nil, nil, err
}

if strings.Contains(d.url, treeSeparator) {
branchPath := strings.SplitN(d.url, treeSeparator, 2)[1]

token := ""
for key, auth := range config.G[config.KraftKit](ctx).Auth {
if auth.Endpoint == "github.com" || key == "github.com" {
token = auth.Token
break
}
}
branches, err := getAllBranchesSorted(ctx, repo.RepoOwner(), repo.RepoName(), token)
if err != nil {
return nil, nil, err
}

for _, branchName := range branches {
if strings.HasPrefix(branchPath, branchName) {
branch = branchName
break
}
}

if branch == "" {
return nil, nil, fmt.Errorf("could not match branch from given url, are you sure the url is correct?")
}

path = strings.SplitN(branchPath, branch+"/", 2)[1]
}

ghProvider, err = manifest.NewGitHubProvider(
ctx,
link,
manifest.WithAuthConfig(config.G[config.KraftKit](ctx).Auth),
manifest.WithUpdate(true))
if err != nil {
return nil, nil, err
}

var m *manifest.Manifest = &manifest.Manifest{
Type: "app",
Name: repo.RepoName(),
Origin: link,
Provider: ghProvider,
Channels: []manifest.ManifestChannel{
{
Name: branch,
Default: true,
Resource: link,
},
},
}

p, err := manifest.NewPackageFromManifest(
m,
manifest.WithAuthConfig(config.G[config.KraftKit](ctx).Auth),
manifest.WithUpdate(true),
)
if err != nil {
return nil, nil, err
}

err = p.Pull(
ctx,
pack.WithPullWorkdir(opts.Workdir),
pack.WithPullUnstructured(true),
)
if err != nil {
return nil, nil, err
}

opts.Workdir = filepath.Join(opts.Workdir, repo.RepoName(), path)

deployers := []deployer{
&deployerKraftfileRuntime{},
&deployerKraftfileUnikraft{},
}

for _, deployer := range deployers {
if deployable, _ := deployer.Deployable(ctx, opts, d.args...); deployable {
return deployer.Deploy(ctx, opts, d.args...)
}
}

return nil, nil, fmt.Errorf("no deployer found for the given project link")
}
17 changes: 10 additions & 7 deletions manifest/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,16 @@ func (dp DirectoryProvider) PullManifest(ctx context.Context, manifest *Manifest
return fmt.Errorf("cannot determine channel for directory provider")
}

local, err := unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %s", err)
local := manifest.Name
if !popts.Unstructured() {
local, err = unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %s", err)
}
}

f, err := os.Lstat(local)
Expand Down
17 changes: 10 additions & 7 deletions manifest/pack_pull_archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,16 @@ func pullArchive(ctx context.Context, manifest *Manifest, opts ...pack.PullOptio

local := cache
if len(popts.Workdir()) > 0 {
local, err = unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %s", err)
local = manifest.Name
if !popts.Unstructured() {
local, err = unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %s", err)
}
}
}

Expand Down
17 changes: 10 additions & 7 deletions manifest/pack_pull_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,16 @@ func pullGit(ctx context.Context, manifest *Manifest, opts ...pack.PullOption) e
copts.ReferenceName = gitplumbing.NewBranchReferenceName(version)
}

local, err := unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %w", err)
local := manifest.Name
if !popts.Unstructured() {
local, err = unikraft.PlaceComponent(
popts.Workdir(),
manifest.Type,
manifest.Name,
)
if err != nil {
return fmt.Errorf("could not place component package: %w", err)
}
}

entry := log.G(ctx).
Expand Down
15 changes: 15 additions & 0 deletions pack/pull_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type PullOptions struct {
onProgress func(progress float64)
workdir string
useCache bool
unstructured bool
}

// Auths returns the set authentication config for a given domain or nil if the
Expand Down Expand Up @@ -53,6 +54,11 @@ func (ppo *PullOptions) UseCache() bool {
return ppo.useCache
}

// Unstructured returns whether the pull should happen to the workdir directly.
func (ppo *PullOptions) Unstructured() bool {
return ppo.unstructured
}

// PullOption is an option function which is used to modify PullOptions.
type PullOption func(opts *PullOptions) error

Expand Down Expand Up @@ -120,3 +126,12 @@ func WithPullCache(cache bool) PullOption {
return nil
}
}

// WithPullUnstructured to set whether the pull should happen to the workdir
// directly.
func WithPullUnstructured(unstructured bool) PullOption {
return func(opts *PullOptions) error {
opts.unstructured = unstructured
return nil
}
}
Loading