installer: fully automate initial bootstrap and env creation
diff --git a/core/installer/soft/client.go b/core/installer/soft/client.go
index e07532e..59e94a8 100644
--- a/core/installer/soft/client.go
+++ b/core/installer/soft/client.go
@@ -1,28 +1,26 @@
package soft
import (
+ "encoding/base64"
"fmt"
- "io/ioutil"
+ "golang.org/x/crypto/ssh"
"log"
"net"
"os"
- "time"
+ "strings"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
- // "github.com/go-git/go-git/v5/config"
- "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"
- "golang.org/x/crypto/ssh"
- "sigs.k8s.io/yaml"
)
type Client struct {
- ip string
- port int
- Signer ssh.Signer
- log *log.Logger
+ IP string
+ port int
+ Signer ssh.Signer
+ log *log.Logger
+ pemBytes []byte
}
func NewClient(ip string, port int, clientPrivateKey []byte, log *log.Logger) (*Client, error) {
@@ -31,28 +29,45 @@
return nil, err
}
log.SetPrefix("SOFT-SERVE: ")
+ log.Printf("Created signer")
+ pub := signer.PublicKey().Marshal()
+ b := make([]byte, 100)
+ base64.StdEncoding.Encode(b, pub)
+ log.Printf("%s\n", string(b))
return &Client{
ip,
port,
signer,
log,
+ clientPrivateKey,
}, nil
}
func (ss *Client) AddUser(name, pubKey string) error {
log.Printf("Adding user %s", name)
- if err := ss.RunCommand(fmt.Sprintf("user create %s", name)); err != nil {
+ if err := ss.RunCommand("user", "create", name); err != nil {
return err
}
- return ss.RunCommand(fmt.Sprintf("user add-pubkey %s %s", name, pubKey))
+ return ss.AddPublicKey(name, pubKey)
}
func (ss *Client) MakeUserAdmin(name string) error {
log.Printf("Making user %s admin", name)
- return ss.RunCommand(fmt.Sprintf("user set-admin %s true", name))
+ return ss.RunCommand("user", "set-admin", name, "true")
}
-func (ss *Client) RunCommand(cmd string) error {
+func (ss *Client) AddPublicKey(user string, pubKey string) error {
+ log.Printf("Adding public key: %s %s\n", user, pubKey)
+ return ss.RunCommand("user", "add-pubkey", user, pubKey)
+}
+
+func (ss *Client) RemovePublicKey(user string, pubKey string) error {
+ log.Printf("Adding public key: %s %s\n", user, pubKey)
+ return ss.RunCommand("user", "remove-pubkey", user, pubKey)
+}
+
+func (ss *Client) RunCommand(args ...string) error {
+ cmd := strings.Join(args, " ")
log.Printf("Running command %s", cmd)
client, err := ssh.Dial("tcp", ss.addressSSH(), ss.sshClientConfig())
if err != nil {
@@ -63,84 +78,25 @@
return err
}
defer session.Close()
+ session.Stdout = os.Stdout
+ session.Stderr = os.Stderr
return session.Run(cmd)
}
func (ss *Client) AddRepository(name, readme string) error {
log.Printf("Adding repository %s", name)
- return ss.RunCommand(fmt.Sprintf("repo create %s -d \"%s\"", name, readme))
+ return ss.RunCommand("repo", "create", name, "-d", fmt.Sprintf("\"%s\"", readme))
}
func (ss *Client) AddCollaborator(repo, user string) error {
log.Printf("Adding collaborator %s %s", repo, user)
- return ss.RunCommand(fmt.Sprintf("repo collab add %s %s", repo, user))
-}
-
-func (ss *Client) CreateRepository(name string) error {
- log.Printf("Creating repository %s", name)
- configRepo, err := ss.getConfigRepo()
- if err != nil {
- return err
- }
- wt, err := configRepo.Worktree()
- if err != nil {
- return err
- }
- if err = wt.Checkout(&git.CheckoutOptions{
- Branch: "refs/heads/master",
- }); err != nil {
- return err
- }
- f, err := wt.Filesystem.Open("config.yaml")
- if err != nil {
- return err
- }
- defer f.Close()
- configBytes, err := ioutil.ReadAll(f)
- if err != nil {
- return err
- }
- config := make(map[string]interface{})
- if err := yaml.Unmarshal(configBytes, &config); err != nil {
- return err
- }
- repos := config["repos"].([]interface{})
- repos = append(repos, map[string]interface{}{
- "name": name,
- "repo": name,
- "private": true,
- "note": fmt.Sprintf("PCloud env for %s", name),
- })
- config["repos"] = repos
- configBytes, err = yaml.Marshal(config)
- if err != nil {
- return err
- }
- if err := ss.writeFile(wt, "config.yaml", string(configBytes)); err != nil {
- return err
- }
- if err := ss.Commit(wt, fmt.Sprintf("add-repo: %s", name)); err != nil {
- return err
- }
- return ss.Push(configRepo)
-}
-
-func (ss *Client) getConfigRepo() (*git.Repository, error) {
- return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
- URL: ss.addressGit(),
- Auth: ss.authGit(),
- RemoteName: "origin",
- ReferenceName: "refs/heads/master",
- Depth: 1,
- InsecureSkipTLS: true,
- Progress: os.Stdout,
- })
+ return ss.RunCommand("repo", "collab", "add", repo, user)
}
func (ss *Client) GetRepo(name string) (*git.Repository, error) {
return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
- URL: fmt.Sprintf("%s/%s", ss.addressGit(), name),
- Auth: ss.authGit(),
+ URL: ss.GetRepoAddress(name),
+ Auth: ss.authSSH(),
RemoteName: "origin",
ReferenceName: "refs/heads/master",
Depth: 1,
@@ -149,57 +105,38 @@
})
}
-func (ss *Client) repoPathByName(name string) string {
- return fmt.Sprintf("%s/%s", ss.addressGit(), name)
-}
-
-func (ss *Client) Commit(wt *git.Worktree, message string) error {
- _, err := wt.Commit(message, &git.CommitOptions{
- Author: &object.Signature{
- Name: "pcloud",
- Email: "pcloud@installer",
- When: time.Now(),
- },
- })
- return err
-}
-
-func (ss *Client) Push(repo *git.Repository) error {
- return repo.Push(&git.PushOptions{
- RemoteName: "origin",
- Auth: ss.authGit(),
- })
-}
-
-func (ss *Client) writeFile(wt *git.Worktree, path, contents string) error {
- f, err := wt.Filesystem.Create(path)
+func (ss *Client) authSSH() gitssh.AuthMethod {
+ a, err := gitssh.NewPublicKeys("git", ss.pemBytes, "")
if err != nil {
- return err
+ panic(err)
}
- defer f.Close()
- if _, err = f.Write([]byte(contents)); err != nil {
- return err
+ a.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ // TODO(giolekva): verify server public key
+ ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
+ return nil
}
- _, err = wt.Add(path)
- return err
-}
-
-func (ss *Client) CloneRepository(name string) (*git.Repository, error) {
- return git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
- URL: ss.repoPathByName(name),
- Auth: ss.authGit(),
- RemoteName: "origin",
- InsecureSkipTLS: true,
- })
+ return a
+ // return &gitssh.PublicKeys{
+ // User: "git",
+ // Signer: ss.Signer,
+ // HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
+ // HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ // // TODO(giolekva): verify server public key
+ // ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
+ // return nil
+ // },
+ // },
+ // }
}
func (ss *Client) authGit() *gitssh.PublicKeys {
return &gitssh.PublicKeys{
+ User: "git",
Signer: ss.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))
+ ss.log.Printf("--- %+v\n", ssh.MarshalAuthorizedKey(key))
return nil
},
},
@@ -238,10 +175,14 @@
}
}
+func (ss *Client) GetRepoAddress(name string) string {
+ return fmt.Sprintf("%s/%s", ss.addressGit(), name)
+}
+
func (ss *Client) addressGit() string {
- return fmt.Sprintf("ssh://%s:%d", ss.ip, ss.port)
+ return fmt.Sprintf("ssh://%s", ss.addressSSH())
}
func (ss *Client) addressSSH() string {
- return fmt.Sprintf("%s:%d", ss.ip, ss.port)
+ return fmt.Sprintf("%s:%d", ss.IP, ss.port)
}