Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 2 additions & 0 deletions cmd/gitops/app/add/profiles/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func AddCommand(opts *config.Options, client *adapters.HTTPClient) *cobra.Comman
cmd.Flags().StringVar(&profileOpts.Version, "version", "latest", "Version of the profile specified as semver (e.g.: 0.1.0) or as 'latest'")
cmd.Flags().StringVar(&profileOpts.ConfigRepo, "config-repo", "", "URL of the external repository that contains the automation manifests")
cmd.Flags().StringVar(&profileOpts.Cluster, "cluster", "", "Name of the cluster to add the profile to")
cmd.Flags().StringVar(&profileOpts.HelmRepoName, "helm-repo-name", "weaveworks-charts", "Name of the Helm repository to use for the profile")
cmd.Flags().StringVar(&profileOpts.HelmRepoNamespace, "helm-repo-namespace", "flux-system", "Namespace of the Helm repository to use for the profile")
cmd.Flags().BoolVar(&profileOpts.AutoMerge, "auto-merge", false, "If set, 'gitops add profile' will merge automatically into the repository's branch")
internal.AddPRFlags(cmd, &profileOpts.HeadBranch, &profileOpts.BaseBranch, &profileOpts.Description, &profileOpts.Message, &profileOpts.Title)

Expand Down
45 changes: 44 additions & 1 deletion cmd/gitops/app/get/profiles/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ import (
"k8s.io/cli-runtime/pkg/printers"
)

type profileCommandFlags struct {
RepoName string
RepoNamespace string
RepoKind string
ClusterName string
ClusterNamespace string
Kind string
}

var flags profileCommandFlags

func GetCommand(opts *config.Options, client *adapters.HTTPClient) *cobra.Command {
cmd := &cobra.Command{
Use: "profile",
Expand All @@ -24,11 +35,30 @@ func GetCommand(opts *config.Options, client *adapters.HTTPClient) *cobra.Comman
Example: `
# Get all profiles
gitops get profiles

# Get all profiles for a specific cluster
gitops get profiles --cluster-name <cluster-name> --cluster-namespace <cluster-namespace>

# Get all profiles for a specific repository
gitops get profiles --repo-name <repo-name> --repo-namespace <repo-namespace> --repo-kind <repo-kind>

# Get all profiles for a specific repository and cluster
gitops get profiles --repo-name <repo-name> --repo-namespace <repo-namespace> --repo-kind <repo-kind> --cluster-name <cluster-name> --cluster-namespace <cluster-namespace>

# Get all profiles for a specific repository, cluster and kind
gitops get profiles --repo-name <repo-name> --repo-namespace <repo-namespace> --repo-kind <repo-kind> --cluster-name <cluster-name> --cluster-namespace <cluster-namespace> --kind <kind>
`,
PreRunE: getProfilesCmdPreRunE(&opts.Endpoint),
RunE: getProfilesCmdRunE(opts, client),
}

cmd.Flags().StringVar(&flags.RepoName, "repo-name", "weaveworks-charts", "Name of the repository")
cmd.Flags().StringVar(&flags.RepoNamespace, "repo-namespace", "flux-system", "Namespace of the repository")
cmd.Flags().StringVar(&flags.RepoKind, "repo-kind", "", "Kind of the repository")
cmd.Flags().StringVar(&flags.ClusterName, "cluster-name", "management", "Name of the cluster")
cmd.Flags().StringVar(&flags.ClusterNamespace, "cluster-namespace", "", "Namespace of the cluster")
cmd.Flags().StringVar(&flags.Kind, "kind", "", "Kind of the profile")

return cmd
}

Expand All @@ -53,6 +83,19 @@ func getProfilesCmdRunE(opts *config.Options, client *adapters.HTTPClient) func(

defer w.Flush()

return profiles.NewService(logger.NewCLILogger(os.Stdout)).Get(context.Background(), client, w)
opts := profiles.GetOptions{
Kind: flags.Kind,
RepositoryRef: profiles.RepositoryRef{
Name: flags.RepoName,
Namespace: flags.RepoNamespace,
Kind: flags.RepoKind,
ClusterRef: profiles.ClusterRef{
Name: flags.ClusterName,
Namespace: flags.ClusterNamespace,
},
},
}

return profiles.NewService(logger.NewCLILogger(os.Stdout)).Get(context.Background(), client, w, opts)
}
}
39 changes: 32 additions & 7 deletions cmd/gitops/pkg/adapters/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package adapters
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
Expand All @@ -13,9 +14,9 @@ import (
"github.com/go-resty/resty/v2"
"k8s.io/client-go/rest"

pb "github.com/weaveworks/weave-gitops-enterprise/cmd/clusters-service/pkg/protos/profiles"
"github.com/weaveworks/weave-gitops-enterprise/cmd/gitops/pkg/clusters"
"github.com/weaveworks/weave-gitops-enterprise/cmd/gitops/pkg/templates"
"github.com/weaveworks/weave-gitops-enterprise/pkg/services/profiles"
"github.com/weaveworks/weave-gitops/cmd/gitops/config"
kubecfg "sigs.k8s.io/controller-runtime/pkg/client/config"
)
Expand Down Expand Up @@ -598,27 +599,51 @@ func (c *HTTPClient) GetClusterKubeconfig(name string) (string, error) {
return string(b), nil
}

func (c *HTTPClient) RetrieveProfiles() (*pb.GetProfilesResponse, error) {
endpoint := "/v1/profiles"
func (c *HTTPClient) RetrieveProfiles(req profiles.GetOptions) (profiles.Profiles, error) {
endpoint := "/v1/charts/list"

result := &pb.GetProfilesResponse{}
result := profiles.Profiles{}

queryParams, err := toQueryParams(req)
if err != nil {
return result, fmt.Errorf("unable to convert request to query params: %w", err)
}

res, err := c.client.R().
SetHeader("Accept", "application/json").
SetResult(result).
SetQueryParams(queryParams).
SetResult(&result).
Get(endpoint)

if err != nil {
return nil, fmt.Errorf("unable to GET profiles from %q: %w", res.Request.URL, err)
return result, fmt.Errorf("unable to GET profiles from %q: %w", res.Request.URL, err)
}

if res.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("response status for GET %q was %d", res.Request.URL, res.StatusCode())
return result, fmt.Errorf("response status for GET %q was %d", res.Request.URL, res.StatusCode())
}

return result, nil
}

// toQueryParams converts a profiles.GetOptions struct into a map of query parameters.
func toQueryParams(req profiles.GetOptions) (map[string]string, error) {
queryParams := map[string]string{}

// Encode the req into a query string
b, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("unable to marshal request: %w", err)
}

err = json.Unmarshal(b, &queryParams)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal request: %w", err)
}

return queryParams, nil
}

// DeleteClusters deletes CAPI cluster using its name
func (c *HTTPClient) DeleteClusters(params clusters.DeleteClustersParams) (string, error) {
endpoint := "v1/clusters"
Expand Down
7 changes: 6 additions & 1 deletion pkg/services/profiles/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ func (s *ProfilesSvc) Add(ctx context.Context, r ProfilesRetriever, gitProvider
return fmt.Errorf("failed to get default branch: %w", err)
}

helmRepo, version, err := s.discoverHelmRepository(ctx, r, GetOptions{
helmRepo := types.NamespacedName{
Name: opts.HelmRepoName,
Namespace: opts.HelmRepoNamespace,
}

version, err := s.discoverHelmRepository(ctx, r, GetOptions{
Name: opts.Name,
Version: opts.Version,
Cluster: opts.Cluster,
Expand Down
70 changes: 45 additions & 25 deletions pkg/services/profiles/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import (
"io"
"strings"

pb "github.com/weaveworks/weave-gitops-enterprise/cmd/clusters-service/pkg/protos/profiles"
"github.com/weaveworks/weave-gitops-enterprise/pkg/helm/watcher/controller"
"k8s.io/apimachinery/pkg/api/errors"
)

type ProfilesRetriever interface {
Source() string
RetrieveProfiles() (*pb.GetProfilesResponse, error)
RetrieveProfiles(GetOptions) (Profiles, error)
}

type GetOptions struct {
Expand All @@ -23,10 +22,35 @@ type GetOptions struct {
Namespace string
Writer io.Writer
Port string
Kind string `json:"kind,omitempty"`
RepositoryRef
}

func (s *ProfilesSvc) Get(ctx context.Context, r ProfilesRetriever, w io.Writer) error {
profiles, err := r.RetrieveProfiles()
type RepositoryRef struct {
Name string `json:"repository.name,omitempty"`
Namespace string `json:"repository.namespace,omitempty"`
Kind string `json:"repository.kind,omitempty"`
ClusterRef
}

type ClusterRef struct {
Name string `json:"repository.cluster.name,omitempty"`
Namespace string `json:"repository.cluster.namespace,omitempty"`
}

type Profile struct {
Name string `json:"name,omitempty"`
Versions []string `json:"versions,omitempty"`
Layer string `json:"layer,omitempty"`
}

type Profiles struct {
Charts []Profile `json:"charts,omitempty"`
}

func (s *ProfilesSvc) Get(ctx context.Context, r ProfilesRetriever, w io.Writer, opts GetOptions) error {

profiles, err := r.RetrieveProfiles(opts)
if err != nil {
if e, ok := err.(*errors.StatusError); ok {
return fmt.Errorf("unable to retrieve profiles from %q: status code %d", r.Source(), e.ErrStatus.Code)
Expand All @@ -35,54 +59,50 @@ func (s *ProfilesSvc) Get(ctx context.Context, r ProfilesRetriever, w io.Writer)
return fmt.Errorf("unable to retrieve profiles from %q: %w", r.Source(), err)
}

printProfiles(profiles, w)
printProfiles(profiles.Charts, w)

return nil
}

// GetProfile returns a single available profile.
func (s *ProfilesSvc) GetProfile(ctx context.Context, r ProfilesRetriever, opts GetOptions) (*pb.Profile, string, error) {
func (s *ProfilesSvc) GetProfile(ctx context.Context, r ProfilesRetriever, opts GetOptions) (Profile, string, error) {
s.Logger.Actionf("getting available profiles from %s", r.Source())

profilesList, err := r.RetrieveProfiles()
profilesList, err := r.RetrieveProfiles(opts)
if err != nil {
return nil, "", fmt.Errorf("unable to retrieve profiles from %q: %w", r.Source(), err)
return Profile{}, "", fmt.Errorf("unable to retrieve profiles from %q: %w", r.Source(), err)
}

var version string

for _, p := range profilesList.Profiles {
for _, p := range profilesList.Charts {
if p.Name == opts.Name {
if len(p.AvailableVersions) == 0 {
return nil, "", fmt.Errorf("no version found for profile '%s' in %s/%s", p.Name, opts.Cluster, opts.Namespace)
if len(p.Versions) == 0 {
return Profile{}, "", fmt.Errorf("no version found for profile '%s' in %s/%s", p.Name, opts.Cluster, opts.Namespace)
}

switch {
case opts.Version == "latest":
versions, err := controller.ConvertStringListToSemanticVersionList(p.AvailableVersions)
versions, err := controller.ConvertStringListToSemanticVersionList(p.Versions)
if err != nil {
return nil, "", err
return Profile{}, "", err
}

controller.SortVersions(versions)
version = versions[0].String()
default:
if !foundVersion(p.AvailableVersions, opts.Version) {
return nil, "", fmt.Errorf("version '%s' not found for profile '%s' in %s/%s", opts.Version, opts.Name, opts.Cluster, opts.Namespace)
if !foundVersion(p.Versions, opts.Version) {
return Profile{}, "", fmt.Errorf("version '%s' not found for profile '%s' in %s/%s", opts.Version, opts.Name, opts.Cluster, opts.Namespace)
}

version = opts.Version
}

if p.GetHelmRepository().GetName() == "" || p.GetHelmRepository().GetNamespace() == "" {
return nil, "", fmt.Errorf("HelmRepository's name or namespace is empty")
}

return p, version, nil
}
}

return nil, "", fmt.Errorf("no available profile '%s' found in %s/%s", opts.Name, opts.Cluster, opts.Namespace)
return Profile{}, "", fmt.Errorf("no available profile '%s' found in %s/%s", opts.Name, opts.Cluster, opts.Namespace)
}

func foundVersion(availableVersions []string, version string) bool {
Expand All @@ -95,12 +115,12 @@ func foundVersion(availableVersions []string, version string) bool {
return false
}

func printProfiles(profiles *pb.GetProfilesResponse, w io.Writer) {
fmt.Fprintf(w, "NAME\tDESCRIPTION\tAVAILABLE_VERSIONS\n")
func printProfiles(profiles []Profile, w io.Writer) {
fmt.Fprintf(w, "NAME\tAVAILABLE_VERSIONS\tLAYER\n")

if profiles.Profiles != nil && len(profiles.Profiles) > 0 {
for _, p := range profiles.Profiles {
fmt.Fprintf(w, "%s\t%s\t%v", p.Name, p.Description, strings.Join(p.AvailableVersions, ","))
if len(profiles) > 0 {
for _, p := range profiles {
fmt.Fprintf(w, "%s\t%s\t%v", p.Name, strings.Join(p.Versions, ","), p.Layer)
fmt.Fprintln(w, "")
}
}
Expand Down
Loading