Skip to content

Commit ad4eac7

Browse files
author
Aniruddha Basak
authored
Add github client support (#51)
Signed-off-by: Aniruddha Basak <[email protected]>
1 parent da50834 commit ad4eac7

File tree

11 files changed

+396
-82
lines changed

11 files changed

+396
-82
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
.envrc
22
# Binaries for programs and plugins
33
*.exe
44
*.exe~

github-release/docker-ferrol-1-27-v2/hashes.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

github-release/docker-ferrol-1-27-v2/metadata.yaml

Lines changed: 0 additions & 6 deletions
This file was deleted.

pkg/clusterstack/metadata.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
// Component contains component.
2828
type Component struct {
2929
ClusterAddon string `yaml:"clusterAddon"`
30-
NodeImage string `yaml:"nodeImage"`
30+
NodeImage string `yaml:"nodeImage,omitempty"`
3131
}
3232

3333
// Versions contains version information.
@@ -45,16 +45,7 @@ type MetaData struct {
4545

4646
// ParseMetaData parse the metadata file.
4747
func ParseMetaData(path string) (MetaData, error) {
48-
entries, err := os.ReadDir(path)
49-
if err != nil {
50-
return MetaData{}, fmt.Errorf("failed to read metadata directory: %w", err)
51-
}
52-
53-
if len(entries) != 1 {
54-
return MetaData{}, fmt.Errorf("ambiguous release found")
55-
}
56-
57-
metadataPath := filepath.Join(path, entries[0].Name(), "metadata.yaml")
48+
metadataPath := filepath.Join(path, "metadata.yaml")
5849
fileInfo, err := os.ReadFile(filepath.Clean(metadataPath))
5950
if err != nil {
6051
return MetaData{}, fmt.Errorf("failed to read metadata file: %w", err)

pkg/cmd/create.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"context"
2021
"encoding/json"
2122
"fmt"
2223
"os"
2324
"path/filepath"
2425

2526
"github.com/SovereignCloudStack/csctl/pkg/clusterstack"
2627
"github.com/SovereignCloudStack/csctl/pkg/github"
28+
"github.com/SovereignCloudStack/csctl/pkg/github/client"
2729
"github.com/SovereignCloudStack/csctl/pkg/hash"
2830
"github.com/SovereignCloudStack/csctl/pkg/template"
2931
"github.com/spf13/cobra"
@@ -50,8 +52,6 @@ var (
5052
var (
5153
mode string
5254
outputDirectory string
53-
// TODO: remove this later.
54-
githubReleasePath string
5555
)
5656

5757
// CreateOptions contains config for creating a release.
@@ -77,12 +77,10 @@ var createCmd = &cobra.Command{
7777
func init() {
7878
createCmd.Flags().StringVarP(&mode, "mode", "m", "stable", "It defines the mode of the cluster stack manager")
7979
createCmd.Flags().StringVarP(&outputDirectory, "output", "o", "./releases", "It defines the output directory in which the release artifacts will be generated")
80-
// TODO: remove this later
81-
createCmd.Flags().StringVar(&githubReleasePath, "github-release", "github-release", "It is used to get the path to local github release (for stable mode only)")
8280
}
8381

8482
// GetCreateOptions create a Create Option for create command.
85-
func GetCreateOptions(clusterStackPath string) (*CreateOptions, error) {
83+
func GetCreateOptions(ctx context.Context, clusterStackPath string) (*CreateOptions, error) {
8684
createOption := &CreateOptions{}
8785

8886
// ClusterAddon config
@@ -106,20 +104,35 @@ func GetCreateOptions(clusterStackPath string) (*CreateOptions, error) {
106104
return nil, fmt.Errorf("failed to handle hash mode: %w", err)
107105
}
108106
case stableMode:
109-
createOption.Metadata, err = clusterstack.HandleStableMode(githubReleasePath, createOption.CurrentReleaseHash, createOption.LatestReleaseHash)
107+
gc, err := client.NewFactory().NewClient(ctx)
110108
if err != nil {
111-
return nil, fmt.Errorf("failed to handle stable mode: %w", err)
109+
return nil, fmt.Errorf("failed to create new github client: %w", err)
112110
}
113111

114112
// update the metadata kubernetes version with the csmctl.yaml config
115113
createOption.Metadata.Versions.Kubernetes = config.Config.KubernetesVersion
116114

117-
// TODO: remove
118-
latestReleaseInfoWithHash, err := github.GetLocalReleaseInfoWithHash(githubReleasePath)
115+
latestRepoRelease, err := github.GetLatestReleaseFromRemoteRepository(ctx, mode, &config, gc)
119116
if err != nil {
120-
return nil, fmt.Errorf("failed to get latest github local release: %w", err)
117+
return nil, fmt.Errorf("failed to get latest release form remote repository: %w", err)
118+
}
119+
fmt.Printf("latest release found: %q\n", latestRepoRelease)
120+
121+
if latestRepoRelease == "" {
122+
createOption.Metadata.APIVersion = "metadata.clusterstack.x-k8s.io/v1alpha1"
123+
createOption.Metadata.Versions.Kubernetes = config.Config.KubernetesVersion
124+
createOption.Metadata.Versions.ClusterStack = "v1"
125+
createOption.Metadata.Versions.Components.ClusterAddon = "v1"
126+
} else {
127+
if err := github.DownloadReleaseAssets(ctx, latestRepoRelease, "./tmp/releases/", gc); err != nil {
128+
return nil, fmt.Errorf("failed to download release asset: %w", err)
129+
}
130+
131+
createOption.Metadata, err = clusterstack.HandleStableMode("./tmp/releases/", createOption.CurrentReleaseHash, createOption.LatestReleaseHash)
132+
if err != nil {
133+
return nil, fmt.Errorf("failed to handle stable mode: %w", err)
134+
}
121135
}
122-
createOption.LatestReleaseHash = latestReleaseInfoWithHash.Hash
123136
}
124137

125138
releaseDirName, err := clusterstack.GetClusterStackReleaseDirectoryName(&createOption.Metadata, &createOption.Config)
@@ -132,7 +145,7 @@ func GetCreateOptions(clusterStackPath string) (*CreateOptions, error) {
132145
return createOption, nil
133146
}
134147

135-
func createAction(_ *cobra.Command, args []string) error {
148+
func createAction(cmd *cobra.Command, args []string) error {
136149
if len(args) != 1 {
137150
return fmt.Errorf("please provide a valid command, create only accept one argument to path to the cluster stacks")
138151
}
@@ -143,7 +156,7 @@ func createAction(_ *cobra.Command, args []string) error {
143156
return fmt.Errorf("mode is not supported please choose from - stable, hash")
144157
}
145158

146-
createOpts, err := GetCreateOptions(clusterStackPath)
159+
createOpts, err := GetCreateOptions(cmd.Context(), clusterStackPath)
147160
if err != nil {
148161
return fmt.Errorf("failed to create create options: %w", err)
149162
}

pkg/github/github.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sort"
7+
8+
csoclusterstack "github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusterstack"
9+
"github.com/SovereignCloudStack/cluster-stack-operator/pkg/version"
10+
"github.com/SovereignCloudStack/csctl/pkg/clusterstack"
11+
githubclient "github.com/SovereignCloudStack/csctl/pkg/github/client"
12+
)
13+
14+
// GetLatestReleaseFromRemoteRepository returns the latest release from the github repository.
15+
func GetLatestReleaseFromRemoteRepository(ctx context.Context, mode string, config *clusterstack.CsctlConfig, gc githubclient.Client) (string, error) {
16+
ghReleases, resp, err := gc.ListRelease(ctx)
17+
if err != nil {
18+
return "", fmt.Errorf("failed to list releases on remote Git repository: %w", err)
19+
}
20+
if resp != nil && resp.StatusCode != 200 {
21+
return "", fmt.Errorf("got unexpected status from call to remote Git repository: %s", resp.Status)
22+
}
23+
24+
var clusterStacks csoclusterstack.ClusterStacks
25+
26+
for _, ghRelease := range ghReleases {
27+
clusterStackObject, matches, err := matchesSpec(ghRelease.GetTagName(), mode, config)
28+
if err != nil {
29+
return "", fmt.Errorf("failed to get match release tag %q with spec of ClusterStack: %w", ghRelease.GetTagName(), err)
30+
}
31+
32+
if matches {
33+
clusterStacks = append(clusterStacks, clusterStackObject)
34+
}
35+
}
36+
37+
if len(clusterStacks) == 0 {
38+
return "", nil
39+
}
40+
41+
sort.Sort(clusterStacks)
42+
43+
str := clusterStacks.Latest().String()
44+
return str, nil
45+
}
46+
47+
func matchesSpec(releaseTagName, mode string, cs *clusterstack.CsctlConfig) (csoclusterstack.ClusterStack, bool, error) {
48+
csObject, err := csoclusterstack.NewFromClusterStackReleaseProperties(releaseTagName)
49+
if err != nil {
50+
return csoclusterstack.ClusterStack{}, false, fmt.Errorf("failed to get clusterstack object from string %q: %w", releaseTagName, err)
51+
}
52+
53+
kubernetesVersion, err := cs.ParseKubernetesVersion()
54+
if err != nil {
55+
return csoclusterstack.ClusterStack{}, false, fmt.Errorf("failed to parse kubernetes version %q: %w", cs.Config.ClusterStackName, err)
56+
}
57+
58+
return csObject, csObject.Version.Channel == version.Channel(mode) &&
59+
csObject.KubernetesVersion.StringWithDot() == kubernetesVersion.StringWithDot() &&
60+
csObject.Name == cs.Config.ClusterStackName &&
61+
csObject.Provider == cs.Config.Provider.Type, nil
62+
}

pkg/github/release.go

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,33 @@ limitations under the License.
1818
package github
1919

2020
import (
21-
"encoding/json"
21+
"context"
2222
"fmt"
23+
"net/http"
2324
"os"
24-
"path/filepath"
25-
"strings"
2625

27-
"github.com/SovereignCloudStack/csctl/pkg/hash"
26+
githubclient "github.com/SovereignCloudStack/csctl/pkg/github/client"
2827
)
2928

30-
// GHRelease contains fields for a release.
31-
type GHRelease struct {
32-
Provider string
33-
ClusterStackName string
34-
KubernetesVersionMajor string
35-
KubernetesVersionMinor string
36-
ClusterStackVersion string
37-
38-
Hash hash.ReleaseHash
39-
}
40-
41-
// GetLocalReleaseInfoWithHash gets the local release info.
42-
// TODO: Later replaced by the original github release fetching code.
43-
func GetLocalReleaseInfoWithHash(path string) (GHRelease, error) {
44-
entries, err := os.ReadDir(path)
29+
// DownloadReleaseAssets downloads the specified release in the specified download path.
30+
func DownloadReleaseAssets(ctx context.Context, releaseTag, downloadPath string, gc githubclient.Client) error {
31+
repoRelease, resp, err := gc.GetReleaseByTag(ctx, releaseTag)
4532
if err != nil {
46-
return GHRelease{}, fmt.Errorf("failed to read github release path: %w", err)
33+
return fmt.Errorf("failed to fetch release tag %q: %w", releaseTag, err)
4734
}
48-
49-
if len(entries) != 1 {
50-
return GHRelease{}, fmt.Errorf("ambiguous release found")
35+
if resp.StatusCode != http.StatusOK {
36+
return fmt.Errorf("failed to fetch release tag %s with status code %d", releaseTag, resp.StatusCode)
5137
}
5238

53-
splittedReleaseName := strings.Split(entries[0].Name(), "-")
54-
55-
if len(splittedReleaseName) != 5 {
56-
return GHRelease{}, fmt.Errorf("wrong release found")
57-
}
58-
59-
ghRelease := GHRelease{
60-
Provider: splittedReleaseName[0],
61-
ClusterStackName: splittedReleaseName[1],
62-
KubernetesVersionMajor: splittedReleaseName[2],
63-
KubernetesVersionMinor: splittedReleaseName[3],
64-
ClusterStackVersion: splittedReleaseName[4],
65-
}
66-
67-
hashPath := filepath.Join(path, entries[0].Name(), "hashes.json")
68-
hashFile, err := os.ReadFile(filepath.Clean(hashPath))
69-
if err != nil {
70-
return GHRelease{}, fmt.Errorf("failed to read hash.json: %w", err)
71-
}
39+
assetlist := []string{"metadata.yaml", "cluster-addon-values.yaml", "cluster-addon", "cluster-class"}
7240

73-
var data hash.ReleaseHash
74-
if err := json.Unmarshal(hashFile, &data); err != nil {
75-
return GHRelease{}, fmt.Errorf("failed to unmarshal release hash: %w", err)
41+
if err := gc.DownloadReleaseAssets(ctx, repoRelease, downloadPath, assetlist); err != nil {
42+
// if download failed for some reason, delete the release directory so that it can be retried in the next reconciliation
43+
if err := os.RemoveAll(downloadPath); err != nil {
44+
return fmt.Errorf("failed to remove release: %w", err)
45+
}
46+
return fmt.Errorf("failed to download release assets: %w", err)
7647
}
77-
ghRelease.Hash = data
7848

79-
return ghRelease, nil
49+
return nil
8050
}

0 commit comments

Comments
 (0)