Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions acceptance/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,16 @@ func NewCceV1Client() (*golangsdk.ServiceClient, error) {
})
}

func NewCceV2Client() (*golangsdk.ServiceClient, error) {
cc, err := CloudAndClient()
if err != nil {
return nil, err
}
return openstack.NewCCEv2(cc.ProviderClient, golangsdk.EndpointOpts{
Region: cc.RegionName,
})
}

func NewCceV3Client() (*golangsdk.ServiceClient, error) {
cc, err := CloudAndClient()
if err != nil {
Expand Down
110 changes: 110 additions & 0 deletions acceptance/openstack/cce/v2/charts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package v2

import (
"archive/tar"
"compress/gzip"
"io"
"os"
"path/filepath"
"testing"

"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
"github.com/opentelekomcloud/gophertelekomcloud/openstack/cce/v2/charts"
th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
)

func TestChartLifecycle(t *testing.T) {
t.Skip("Too long. Non reproducible in CI")
client, err := clients.NewCceV2Client()
th.AssertNoErr(t, err)

// Step 1: Create minimal chart directory
chartDir := "mychart"
os.Mkdir(chartDir, 0755)

Check failure on line 23 in acceptance/openstack/cce/v2/charts_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value of `os.Mkdir` is not checked (errcheck)
t.Cleanup(func() {
os.RemoveAll(chartDir)
})

os.WriteFile(filepath.Join(chartDir, "Chart.yaml"), []byte(`apiVersion: v2

Check failure on line 28 in acceptance/openstack/cce/v2/charts_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value of `os.WriteFile` is not checked (errcheck)
name: mychart
version: 0.1.0
description: A minimal chart
`), 0644)

os.WriteFile(filepath.Join(chartDir, "values.yaml"), []byte(`replicaCount: 1

Check failure on line 34 in acceptance/openstack/cce/v2/charts_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value of `os.WriteFile` is not checked (errcheck)
`), 0644)

// Step 2: Tar + gzip the chart folder
tgzPath := "mychart-0.1.0.tgz"
if err := createTarGz(tgzPath, chartDir); err != nil {
t.Fatalf("Failed to create tgz: %v", err)
}
t.Cleanup(func() {
os.Remove(tgzPath)
})

parameters := `{"skip_lint":true,"override":true,"source":"package"}`

opts := charts.UploadChartOpts{
FilePath: tgzPath,
Parameters: parameters,
}

res, err := charts.UploadChart(client, opts)
th.AssertNoErr(t, err)
t.Cleanup(func() {
t.Logf("Deleting chart : %s", res.ID)
charts.Delete(client, res.ID)

Check failure on line 57 in acceptance/openstack/cce/v2/charts_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Error return value of `charts.Delete` is not checked (errcheck)
})

t.Logf("Chart uploaded successfully: %+v", res)
}

func createTarGz(tgzPath, srcDir string) error {
f, err := os.Create(tgzPath)
if err != nil {
return err
}
defer f.Close()

gw := gzip.NewWriter(f)
defer gw.Close()

tw := tar.NewWriter(gw)
defer tw.Close()

return filepath.Walk(srcDir, func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}

if fi.Mode().IsDir() {
return nil
}

relPath, err := filepath.Rel(srcDir, file)
if err != nil {
return err
}

hdr := &tar.Header{
Name: relPath,
Mode: int64(fi.Mode().Perm()),
Size: fi.Size(),
ModTime: fi.ModTime(),
}

if err := tw.WriteHeader(hdr); err != nil {
return err
}

fh, err := os.Open(file)
if err != nil {
return err
}
defer fh.Close()

_, err = io.Copy(tw, fh)
return err
})
}
14 changes: 14 additions & 0 deletions openstack/cce/v2/charts/Delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package charts

import (
golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
)

// This function is used to delete a Helm chart.
func Delete(client *golangsdk.ServiceClient, chartId string) error {
// DELETE /v2/charts/{chart_id}
_, err := client.Delete(client.ServiceURL("charts", chartId), &golangsdk.RequestOpts{
OkCodes: []int{200},
})
return err
}
92 changes: 92 additions & 0 deletions openstack/cce/v2/charts/Upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package charts

import (
"fmt"
"io"
"mime/multipart"
"os"

golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
)

type UploadChartOpts struct {
Parameters string // Chart Params
FilePath string // Path to the chart (.tgz)
}

// UploadChart uploads a Helm chart to the CCE
func UploadChart(client *golangsdk.ServiceClient, opts UploadChartOpts) (*ChartResponse, error) {
// Open chart file
file, err := os.Open(opts.FilePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()

// Pipe for streaming multipart content
pr, pw := io.Pipe()
writer := multipart.NewWriter(pw)

// Start goroutine to write multipart data
go func() {
defer pw.Close()
defer writer.Close()

// File part, must be named "content"
part, err := writer.CreateFormFile("content", opts.FilePath)
if err != nil {
pw.CloseWithError(err)
return
}

if _, err := io.Copy(part, file); err != nil {
pw.CloseWithError(err)
return
}

_ = writer.WriteField("parameters", opts.Parameters)
}()

raw, err := client.Post(client.ServiceURL("charts"), pr, nil, &golangsdk.RequestOpts{
MoreHeaders: map[string]string{
"Content-Type": writer.FormDataContentType(),
},
OkCodes: []int{200, 201},
})
if err != nil {
return nil, fmt.Errorf("failed to upload chart: %w", err)
}

var res ChartResponse
return &res, extract.Into(raw.Body, &res)
}

type ChartResponse struct {
// Chart ID.
ID string `json:"id"`
// Chart name.
Name string `json:"name"`
// Chart value.
Values string `json:"values"`
// Chart translation resources.
Translate string `json:"translate"`
// Chart description (instruction).
Instruction string `json:"instruction"`
// Chart version.
Version string `json:"version"`
// Chart description.
Description string `json:"description"`
// Chart source.
Source string `json:"source"`
// URL to chart icons.
IconURL string `json:"icon_url"`
// Whether the chart is public.
Public bool `json:"public"`
// URL to the chart.
ChartURL string `json:"chart_url"`
// Created at.
CreateAt string `json:"create_at"`
// Updated at.
UpdateAt string `json:"update_at"`
}
10 changes: 10 additions & 0 deletions openstack/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,16 @@ func NewCCE(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golan
return sc, err
}

// NewCCEv2 creates a ServiceClient that may be used to access the CCE v2 service.
func NewCCEv2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
sc, err := initClientOpts(client, eo, "ccev2.0")
if err != nil {
return nil, err
}
sc.ResourceBase = sc.Endpoint + "api/v2/"
return sc, err
}

// NewWAFV1 creates a ServiceClient that may be used to access the WAF service.
func NewWAFV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
sc, err := initClientOpts(client, eo, "waf")
Expand Down
Loading