blob: b96ac6fb28f6e72d4c9902075149e3bf694a676d [file] [log] [blame]
giolekva8aa73e82022-07-09 11:34:39 +04001package main
2
3import (
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04004 "fmt"
giolekva8aa73e82022-07-09 11:34:39 +04005 "io/ioutil"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04006 "net"
giolekva8aa73e82022-07-09 11:34:39 +04007 "os"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04008 "time"
giolekva8aa73e82022-07-09 11:34:39 +04009
10 "github.com/giolekva/pcloud/core/installer"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040011 "github.com/go-git/go-billy/v5/memfs"
12 "github.com/go-git/go-git/v5"
13 "github.com/go-git/go-git/v5/plumbing/object"
14 gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
15 "github.com/go-git/go-git/v5/storage/memory"
giolekva8aa73e82022-07-09 11:34:39 +040016 "github.com/spf13/cobra"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040017 "golang.org/x/crypto/ssh"
giolekva8aa73e82022-07-09 11:34:39 +040018 "sigs.k8s.io/yaml"
19)
20
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040021const appDirName = "apps"
22
giolekva8aa73e82022-07-09 11:34:39 +040023var installFlags struct {
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040024 sshKey string
25 config string
26 appName string
27 repoAddr string
giolekva8aa73e82022-07-09 11:34:39 +040028}
29
30func installCmd() *cobra.Command {
31 cmd := &cobra.Command{
32 Use: "install",
33 RunE: installCmdRun,
34 }
35 cmd.Flags().StringVar(
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040036 &installFlags.sshKey,
37 "ssh-key",
38 "",
39 "",
40 )
41 cmd.Flags().StringVar(
giolekva8aa73e82022-07-09 11:34:39 +040042 &installFlags.config,
43 "config",
44 "",
45 "",
46 )
47 cmd.Flags().StringVar(
48 &installFlags.appName,
49 "app",
50 "",
51 "",
52 )
53 cmd.Flags().StringVar(
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040054 &installFlags.repoAddr,
55 "repo-addr",
giolekva8aa73e82022-07-09 11:34:39 +040056 "",
57 "",
58 )
59 return cmd
60}
61
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040062type inMemoryAppRepository struct {
63 apps []installer.App
64}
65
66func NewInMemoryAppRepository(apps []installer.App) installer.AppRepository {
67 return &inMemoryAppRepository{
68 apps,
69 }
70}
71
72func (r inMemoryAppRepository) Find(name string) (*installer.App, error) {
73 for _, a := range r.apps {
74 if a.Name == name {
75 return &a, nil
76 }
77 }
78 return nil, fmt.Errorf("Application not found: %s", name)
79}
80
giolekva8aa73e82022-07-09 11:34:39 +040081func installCmdRun(cmd *cobra.Command, args []string) error {
82 cfg, err := readConfig(installFlags.config)
83 if err != nil {
84 return err
85 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040086 sshKey, err := os.ReadFile(installFlags.sshKey)
87 if err != nil {
88 return err
giolekva8aa73e82022-07-09 11:34:39 +040089 }
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040090 signer, err := ssh.ParsePrivateKey(sshKey)
91 if err != nil {
92 return err
93 }
94 repo, err := cloneRepo(installFlags.repoAddr, signer)
95 if err != nil {
96 return err
97 }
98 wt, err := repo.Worktree()
99 if err != nil {
100 return err
101 }
102 appRoot, err := wt.Filesystem.Chroot(appDirName)
103 if err != nil {
104 return err
105 }
106 m, err := installer.NewAppManager(
107 appRoot,
108 cfg,
109 NewInMemoryAppRepository(installer.CreateAllApps()),
110 )
111 if err != nil {
112 return err
113 }
114 if err := m.Install(installFlags.appName); err != nil {
115 return err
116 }
117 if st, err := wt.Status(); err != nil {
118 return err
119 } else {
120 fmt.Printf("%+v\n", st)
121 }
122 wt.AddGlob("*")
123 if st, err := wt.Status(); err != nil {
124 return err
125 } else {
126 fmt.Printf("%+v\n", st)
127 }
128 if _, err := wt.Commit(fmt.Sprintf("install: %s", installFlags.appName), &git.CommitOptions{
129 Author: &object.Signature{
130 Name: "pcloud-appmanager",
131 When: time.Now(),
132 },
133 }); err != nil {
134 return err
135 }
136 return repo.Push(&git.PushOptions{
137 RemoteName: "origin",
138 Auth: auth(signer),
139 })
giolekva8aa73e82022-07-09 11:34:39 +0400140}
141
142func readConfig(config string) (installer.Config, error) {
143 var cfg installer.Config
144 inp, err := ioutil.ReadFile(config)
145 if err != nil {
146 return cfg, err
147 }
148 err = yaml.UnmarshalStrict(inp, &cfg)
149 return cfg, err
150}
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400151
152func cloneRepo(address string, signer ssh.Signer) (*git.Repository, error) {
153 return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
154 URL: address,
155 Auth: auth(signer),
156 RemoteName: "origin",
157 InsecureSkipTLS: true,
158 })
159}
160
161func auth(signer ssh.Signer) *gitssh.PublicKeys {
162 return &gitssh.PublicKeys{
163 Signer: signer,
164 HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
165 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
166 // TODO(giolekva): verify server public key
167 // fmt.Printf("## %s || %s -- \n", serverPubKey, ssh.MarshalAuthorizedKey(key))
168 return nil
169 },
170 },
171 }
172}