Skip to content

Commit 024f643

Browse files
authored
initial IBM cloud (#1439)
* initial ibm cloud support * .
1 parent c5e21c5 commit 024f643

File tree

7 files changed

+859
-0
lines changed

7 files changed

+859
-0
lines changed

provider/ibm/ibm.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package ibm
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
"os"
10+
"strings"
11+
12+
"github.com/nanovms/ops/lepton"
13+
"github.com/nanovms/ops/types"
14+
)
15+
16+
// ProviderName of the cloud platform provider
17+
const ProviderName = "ibm"
18+
19+
// IBM Provider to interact with IBM infrastructure
20+
type IBM struct {
21+
Storage *Objects
22+
token string
23+
iam string
24+
}
25+
26+
// NewProvider IBM
27+
func NewProvider() *IBM {
28+
return &IBM{}
29+
}
30+
31+
// Initialize provider
32+
func (v *IBM) Initialize(config *types.ProviderConfig) error {
33+
v.token = os.Getenv("TOKEN")
34+
if v.token == "" {
35+
return fmt.Errorf("TOKEN is not set")
36+
}
37+
38+
v.setIAMToken()
39+
return nil
40+
}
41+
42+
// Token is the return type for a new IAM token.
43+
type Token struct {
44+
AccessToken string `json:"access_token"`
45+
}
46+
47+
func (v *IBM) setIAMToken() {
48+
49+
uri := "https://iam.cloud.ibm.com/oidc/token"
50+
51+
data := url.Values{}
52+
data.Set("apikey", v.token)
53+
data.Set("response_type", "cloud_iam")
54+
data.Set("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
55+
56+
client := &http.Client{}
57+
r, err := http.NewRequest(http.MethodPost, uri, strings.NewReader(data.Encode()))
58+
if err != nil {
59+
fmt.Println(err)
60+
}
61+
62+
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
63+
r.Header.Add("Accept", "application/json")
64+
65+
res, err := client.Do(r)
66+
if err != nil {
67+
fmt.Println(err)
68+
}
69+
defer res.Body.Close()
70+
71+
body, err := io.ReadAll(res.Body)
72+
if err != nil {
73+
fmt.Println(err)
74+
}
75+
76+
it := &Token{}
77+
err = json.Unmarshal(body, &it)
78+
if err != nil {
79+
fmt.Println(err)
80+
}
81+
82+
v.iam = it.AccessToken
83+
}
84+
85+
// GetStorage returns storage interface for cloud provider
86+
func (v *IBM) GetStorage() lepton.Storage {
87+
return v.Storage
88+
}

provider/ibm/ibm_image.go

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package ibm
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
13+
"github.com/dustin/go-humanize"
14+
"github.com/nanovms/ops/lepton"
15+
"github.com/nanovms/ops/log"
16+
"github.com/nanovms/ops/types"
17+
"github.com/olekukonko/tablewriter"
18+
)
19+
20+
// BuildImage to be upload on IBM
21+
func (v *IBM) BuildImage(ctx *lepton.Context) (string, error) {
22+
c := ctx.Config()
23+
err := lepton.BuildImage(*c)
24+
if err != nil {
25+
return "", err
26+
}
27+
28+
return v.CustomizeImage(ctx)
29+
}
30+
31+
// BuildImageWithPackage to upload on IBM.
32+
func (v *IBM) BuildImageWithPackage(ctx *lepton.Context, pkgpath string) (string, error) {
33+
c := ctx.Config()
34+
err := lepton.BuildImageFromPackage(pkgpath, *c)
35+
if err != nil {
36+
return "", err
37+
}
38+
return v.CustomizeImage(ctx)
39+
}
40+
41+
func (v *IBM) destroyImage(snapshotid string) {
42+
}
43+
44+
// CreateImage - Creates image on IBM using nanos images
45+
func (v *IBM) CreateImage(ctx *lepton.Context, imagePath string) error {
46+
// also worth gzipping
47+
48+
icow := imagePath + ".qcow2"
49+
50+
args := []string{
51+
"convert", "-f", "raw", "-O", "qcow2", imagePath, icow,
52+
}
53+
54+
cmd := exec.Command("qemu-img", args...)
55+
out, err := cmd.Output()
56+
if err != nil {
57+
fmt.Println(err)
58+
fmt.Println(out)
59+
}
60+
61+
store := &Objects{
62+
token: v.iam,
63+
}
64+
65+
v.Storage = store
66+
err = v.Storage.CopyToBucket(ctx.Config(), icow)
67+
if err != nil {
68+
return err
69+
}
70+
71+
imgName := ctx.Config().CloudConfig.ImageName
72+
73+
v.createImage(ctx, icow, imgName)
74+
return nil
75+
}
76+
77+
func (v *IBM) createImage(ctx *lepton.Context, icow string, imgName string) {
78+
baseName := filepath.Base(icow)
79+
80+
c := ctx.Config()
81+
zone := c.CloudConfig.Zone
82+
83+
region := extractRegionFromZone(zone)
84+
85+
bucket := c.CloudConfig.BucketName
86+
87+
uri := "https://" + region + ".iaas.cloud.ibm.com/v1/images?version=2023-02-16&generation=2"
88+
89+
rgroup := v.getDefaultResourceGroup()
90+
91+
j := `{
92+
"name": "` + imgName + `",
93+
"operating_system": {
94+
"name": "ubuntu-18-04-amd64"
95+
},
96+
"file": {
97+
"href": "cos://` + region + `/` + bucket + `/` + baseName + `"
98+
},
99+
"resource_group": {
100+
"id": "` + rgroup + `"
101+
}
102+
}`
103+
104+
reqBody := []byte(j)
105+
106+
client := &http.Client{}
107+
req, err := http.NewRequest("POST", uri, bytes.NewBuffer(reqBody))
108+
if err != nil {
109+
fmt.Println(err)
110+
}
111+
112+
req.Header.Add("Content-Type", "application/json")
113+
req.Header.Set("Authorization", "Bearer "+v.iam)
114+
req.Header.Add("Accept", "application/json")
115+
116+
res, err := client.Do(req)
117+
if err != nil {
118+
fmt.Println(err)
119+
}
120+
defer res.Body.Close()
121+
122+
body, err := io.ReadAll(res.Body)
123+
if err != nil {
124+
fmt.Println(err)
125+
}
126+
127+
fmt.Println(string(body))
128+
}
129+
130+
// ImageListResponse is the set of instances available from IBM in an
131+
// images list call.
132+
type ImageListResponse struct {
133+
Images []Image `json:"images"`
134+
}
135+
136+
// Image represents a given IBM image configuration.
137+
type Image struct {
138+
ID string `json:"id"`
139+
Name string `json:"name"`
140+
Status string `json:"status"`
141+
CreatedAt string `json:"created_at"`
142+
}
143+
144+
// GetImages return all images on IBM
145+
// needs tags added
146+
func (v *IBM) GetImages(ctx *lepton.Context) ([]lepton.CloudImage, error) {
147+
client := &http.Client{}
148+
149+
c := ctx.Config()
150+
zone := c.CloudConfig.Zone
151+
152+
region := extractRegionFromZone(zone)
153+
154+
uri := "https://" + region + ".iaas.cloud.ibm.com/v1/images?version=2023-02-28&generation=2&visibility=private"
155+
156+
req, err := http.NewRequest("GET", uri, nil)
157+
if err != nil {
158+
fmt.Println(err)
159+
}
160+
161+
req.Header.Set("Content-Type", "application/json")
162+
req.Header.Set("Authorization", "Bearer "+v.iam)
163+
164+
res, err := client.Do(req)
165+
if err != nil {
166+
fmt.Println(err)
167+
}
168+
defer res.Body.Close()
169+
170+
body, err := io.ReadAll(res.Body)
171+
if err != nil {
172+
fmt.Println(err)
173+
}
174+
175+
ilr := &ImageListResponse{}
176+
err = json.Unmarshal(body, &ilr)
177+
if err != nil {
178+
fmt.Println(err)
179+
}
180+
181+
var images []lepton.CloudImage
182+
183+
for _, img := range ilr.Images {
184+
images = append(images, lepton.CloudImage{
185+
ID: img.ID,
186+
Name: img.Name,
187+
Status: img.Status,
188+
Path: "",
189+
})
190+
}
191+
192+
return images, nil
193+
194+
}
195+
196+
// ListImages lists images on IBM
197+
func (v *IBM) ListImages(ctx *lepton.Context) error {
198+
images, err := v.GetImages(ctx)
199+
if err != nil {
200+
fmt.Println(err)
201+
}
202+
203+
table := tablewriter.NewWriter(os.Stdout)
204+
table.SetHeader([]string{"ID", "Name", "Date created", "Size", "Status"})
205+
table.SetHeaderColor(
206+
tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor},
207+
tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor},
208+
tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor},
209+
tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor},
210+
tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor})
211+
table.SetRowLine(true)
212+
213+
for _, image := range images {
214+
var row []string
215+
row = append(row, image.ID)
216+
row = append(row, image.Name)
217+
row = append(row, "")
218+
row = append(row, humanize.Bytes(uint64(image.Size)))
219+
row = append(row, image.Status)
220+
table.Append(row)
221+
}
222+
223+
table.Render()
224+
225+
return nil
226+
}
227+
228+
// DeleteImage deletes image from v
229+
func (v *IBM) DeleteImage(ctx *lepton.Context, snapshotID string) error {
230+
return nil
231+
}
232+
233+
// SyncImage syncs image from provider to another provider
234+
func (v *IBM) SyncImage(config *types.Config, target lepton.Provider, image string) error {
235+
log.Warn("not yet implemented")
236+
return nil
237+
}
238+
239+
// ResizeImage is not supported on IBM.
240+
func (v *IBM) ResizeImage(ctx *lepton.Context, imagename string, hbytes string) error {
241+
return fmt.Errorf("operation not supported")
242+
}
243+
244+
// CustomizeImage returns image path with adaptations needed by cloud provider
245+
func (v *IBM) CustomizeImage(ctx *lepton.Context) (string, error) {
246+
imagePath := ctx.Config().RunConfig.ImageName
247+
return imagePath, nil
248+
}

0 commit comments

Comments
 (0)