Skip to content

Commit 0316bb3

Browse files
Trevor Niermantnierman
authored andcommitted
Add 'self' install option
1 parent b24bcf6 commit 0316bb3

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

pkg/tool/self/self.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package self
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"runtime"
8+
"strings"
9+
10+
gogithub "github.com/google/go-github/v51/github"
11+
"github.com/openshift/backplane-tools/pkg/source/github"
12+
"github.com/openshift/backplane-tools/pkg/utils"
13+
)
14+
15+
// Tool implements the interface to manage the 'backplane-tools' binary
16+
type Tool struct {
17+
source *github.Source
18+
}
19+
20+
func NewTool() *Tool {
21+
t := &Tool{
22+
source: github.NewSource("openshift", "backplane-tools"),
23+
}
24+
return t
25+
}
26+
27+
func (t *Tool) Name() string {
28+
return "backplane-tools"
29+
}
30+
31+
func (t *Tool) Install(rootDir, latestDir string) error {
32+
// Pull latest release from GH
33+
release, err := t.source.FetchLatestRelease()
34+
if err != nil {
35+
return err
36+
}
37+
38+
// Determine which assets to download
39+
var checksumAsset *gogithub.ReleaseAsset
40+
var backplaneArchiveAsset *gogithub.ReleaseAsset
41+
for _, asset := range release.Assets {
42+
if strings.Contains(asset.GetName(), "checksums.txt") {
43+
if checksumAsset.GetName() != "" {
44+
return fmt.Errorf("detected duplicate backplane-tools checksum assets")
45+
}
46+
checksumAsset = asset
47+
continue
48+
}
49+
// Exclude assets that do not match system architecture
50+
if !strings.Contains(asset.GetName(), runtime.GOARCH) {
51+
continue
52+
}
53+
// Exclude assets that do not match system OS
54+
if !strings.Contains(strings.ToLower(asset.GetName()), strings.ToLower(runtime.GOOS)) {
55+
continue
56+
}
57+
58+
if backplaneArchiveAsset.GetName() != "" {
59+
return fmt.Errorf("detected duplicate backplane-tools binary assets")
60+
}
61+
backplaneArchiveAsset = asset
62+
}
63+
// Ensure both checksum and binary were retrieved
64+
if backplaneArchiveAsset.GetName() == "" {
65+
return fmt.Errorf("failed to find valid backplane-tools binary asset")
66+
}
67+
if checksumAsset.GetName() == "" {
68+
return fmt.Errorf("failed to find valid backplane-tools checksum asset")
69+
}
70+
71+
// Download the arch- & os-specific assets
72+
toolDir := t.toolDir(rootDir)
73+
versionedDir := filepath.Join(toolDir, release.GetTagName())
74+
err = os.MkdirAll(versionedDir, os.FileMode(0755))
75+
if err != nil {
76+
return fmt.Errorf("failed to create version-specific directory '%s': %w", versionedDir, err)
77+
}
78+
79+
err = t.source.DownloadReleaseAssets([]*gogithub.ReleaseAsset{checksumAsset, backplaneArchiveAsset}, versionedDir)
80+
if err != nil {
81+
return fmt.Errorf("failed to download one or more assets: %w", err)
82+
}
83+
84+
// Verify checksum of downloaded assets
85+
backplaneArchiveFilepath := filepath.Join(versionedDir, backplaneArchiveAsset.GetName())
86+
binarySum, err := utils.Sha256sum(backplaneArchiveFilepath)
87+
if err != nil {
88+
return fmt.Errorf("failed to calculate checksum for '%s': %w", backplaneArchiveFilepath, err)
89+
}
90+
91+
checksumFilePath := filepath.Join(versionedDir, checksumAsset.GetName())
92+
checksumLine, err := utils.GetLineInFile(checksumFilePath, backplaneArchiveAsset.GetName())
93+
if err != nil {
94+
return fmt.Errorf("failed to retrieve checksum from file '%s': %w", checksumFilePath, err)
95+
}
96+
checksumTokens := strings.Fields(checksumLine)
97+
if len(checksumTokens) != 2 {
98+
return fmt.Errorf("the checksum file '%s' is invalid: expected 2 fields, got %d", checksumFilePath, len(checksumTokens))
99+
}
100+
actual := checksumTokens[0]
101+
102+
if strings.TrimSpace(binarySum) != strings.TrimSpace(actual) {
103+
return fmt.Errorf("WARNING: Checksum for backplane-tools does not match the calculated value. Please retry installation. If issue persists, this tool can be downloaded manually at %s\n", backplaneArchiveAsset.GetBrowserDownloadURL())
104+
}
105+
106+
// Untar binary bundle
107+
err = utils.Unarchive(backplaneArchiveFilepath, versionedDir)
108+
if err != nil {
109+
return fmt.Errorf("failed to unarchive the backplane-tools asset file '%s': %w", backplaneArchiveFilepath, err)
110+
}
111+
112+
// Link as latest
113+
latestFilePath := t.symlinkPath(latestDir)
114+
err = os.Remove(latestFilePath)
115+
if err != nil && !os.IsNotExist(err) {
116+
return fmt.Errorf("failed to remove existing 'backplane-tools' binary at '%s': %w", latestDir, err)
117+
}
118+
119+
backplaneBinaryFilepath := filepath.Join(versionedDir, "backplane-tools")
120+
err = os.Symlink(backplaneBinaryFilepath, latestFilePath)
121+
if err != nil {
122+
return fmt.Errorf("failed to link new 'backplane-tools' binary to '%s': %w", latestDir, err)
123+
}
124+
return nil
125+
}
126+
127+
func (t Tool) Installed(rootDir string) (bool, error) {
128+
toolDir := t.toolDir(rootDir)
129+
return utils.FileExists(toolDir)
130+
}
131+
132+
// toolDir returns this tool's specific directory given the root directory all tools are installed in
133+
func (t *Tool) toolDir(rootDir string) string {
134+
return filepath.Join(rootDir, "backplane-tools")
135+
}
136+
137+
// symlinkPath returns the path to the symlink created by this tool, given the latest directory
138+
func (t *Tool) symlinkPath(latestDir string) string {
139+
return filepath.Join(latestDir, "backplane-tools")
140+
}
141+
142+
// Remove completely removes this tool from the provided locations
143+
func (t *Tool) Remove(rootDir, latestDir string) error {
144+
// Remove all binaries owned by this tool
145+
toolDir := t.toolDir(rootDir)
146+
err := os.RemoveAll(toolDir)
147+
if err != nil {
148+
return fmt.Errorf("failed to remove %s: %w", toolDir, err)
149+
}
150+
151+
// Remove all symlinks owned by this tool
152+
latestFilePath := t.symlinkPath(latestDir)
153+
err = os.Remove(latestFilePath)
154+
if err != nil {
155+
return fmt.Errorf("failed to remove symlinked file %s: %w", latestFilePath, err)
156+
}
157+
return nil
158+
}
159+
160+
func (t *Tool) Configure() error {
161+
return nil
162+
}

pkg/tool/tool.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
awscli "github.com/openshift/backplane-tools/pkg/tool/aws-cli"
1010
backplanecli "github.com/openshift/backplane-tools/pkg/tool/backplane-cli"
11+
"github.com/openshift/backplane-tools/pkg/tool/self"
1112
"github.com/openshift/backplane-tools/pkg/tool/oc"
1213
"github.com/openshift/backplane-tools/pkg/tool/ocm"
1314
"github.com/openshift/backplane-tools/pkg/tool/osdctl"
@@ -48,6 +49,11 @@ var toolMap Map
4849
func newMap() Map {
4950
toolMap = Map{}
5051

52+
// Self-management
53+
self := self.NewTool()
54+
toolMap[self.Name()] = self
55+
56+
// 3rd party tools
5157
awsTool := awscli.NewTool()
5258
toolMap[awsTool.Name()] = awsTool
5359

0 commit comments

Comments
 (0)