blob: b96ac6fb28f6e72d4c9902075149e3bf694a676d [file] [log] [blame]
package main
import (
"fmt"
"io/ioutil"
"net"
"os"
"time"
"github.com/giolekva/pcloud/core/installer"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
"sigs.k8s.io/yaml"
)
const appDirName = "apps"
var installFlags struct {
sshKey string
config string
appName string
repoAddr string
}
func installCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
RunE: installCmdRun,
}
cmd.Flags().StringVar(
&installFlags.sshKey,
"ssh-key",
"",
"",
)
cmd.Flags().StringVar(
&installFlags.config,
"config",
"",
"",
)
cmd.Flags().StringVar(
&installFlags.appName,
"app",
"",
"",
)
cmd.Flags().StringVar(
&installFlags.repoAddr,
"repo-addr",
"",
"",
)
return cmd
}
type inMemoryAppRepository struct {
apps []installer.App
}
func NewInMemoryAppRepository(apps []installer.App) installer.AppRepository {
return &inMemoryAppRepository{
apps,
}
}
func (r inMemoryAppRepository) Find(name string) (*installer.App, error) {
for _, a := range r.apps {
if a.Name == name {
return &a, nil
}
}
return nil, fmt.Errorf("Application not found: %s", name)
}
func installCmdRun(cmd *cobra.Command, args []string) error {
cfg, err := readConfig(installFlags.config)
if err != nil {
return err
}
sshKey, err := os.ReadFile(installFlags.sshKey)
if err != nil {
return err
}
signer, err := ssh.ParsePrivateKey(sshKey)
if err != nil {
return err
}
repo, err := cloneRepo(installFlags.repoAddr, signer)
if err != nil {
return err
}
wt, err := repo.Worktree()
if err != nil {
return err
}
appRoot, err := wt.Filesystem.Chroot(appDirName)
if err != nil {
return err
}
m, err := installer.NewAppManager(
appRoot,
cfg,
NewInMemoryAppRepository(installer.CreateAllApps()),
)
if err != nil {
return err
}
if err := m.Install(installFlags.appName); err != nil {
return err
}
if st, err := wt.Status(); err != nil {
return err
} else {
fmt.Printf("%+v\n", st)
}
wt.AddGlob("*")
if st, err := wt.Status(); err != nil {
return err
} else {
fmt.Printf("%+v\n", st)
}
if _, err := wt.Commit(fmt.Sprintf("install: %s", installFlags.appName), &git.CommitOptions{
Author: &object.Signature{
Name: "pcloud-appmanager",
When: time.Now(),
},
}); err != nil {
return err
}
return repo.Push(&git.PushOptions{
RemoteName: "origin",
Auth: auth(signer),
})
}
func readConfig(config string) (installer.Config, error) {
var cfg installer.Config
inp, err := ioutil.ReadFile(config)
if err != nil {
return cfg, err
}
err = yaml.UnmarshalStrict(inp, &cfg)
return cfg, err
}
func cloneRepo(address string, signer ssh.Signer) (*git.Repository, error) {
return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
URL: address,
Auth: auth(signer),
RemoteName: "origin",
InsecureSkipTLS: true,
})
}
func auth(signer ssh.Signer) *gitssh.PublicKeys {
return &gitssh.PublicKeys{
Signer: signer,
HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
// TODO(giolekva): verify server public key
// fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
return nil
},
},
}
}