Skip to content

Commit

Permalink
feat: expose is_onboarded and get cloud_accounts endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
mlabouardy committed Sep 19, 2023
1 parent 549fe7e commit 237dd76
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 10 deletions.
2 changes: 2 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"errors"
"fmt"
"time"

"github.com/getsentry/sentry-go"
Expand Down Expand Up @@ -61,6 +62,7 @@ var startCmd = &cobra.Command{

err = internal.Exec(address, port, file, telemetry, analytics, regions, cmd)
if err != nil {
fmt.Println(err)
return err
}

Expand Down
43 changes: 43 additions & 0 deletions handlers/accounts_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package handlers

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/tailwarden/komiser/models"
)

func (handler *ApiHandler) IsOnboardedHandler(c *gin.Context) {
output := struct {
Onboarded bool `json:"onboarded"`
}{
Onboarded: false,
}

accounts := make([]models.Account, 0)
err := handler.db.NewRaw("SELECT * FROM accounts").Scan(handler.ctx, &accounts)
if err != nil {
logrus.WithError(err).Error("scan failed")
c.JSON(http.StatusInternalServerError, gin.H{"error": "scan failed"})
return
}

if len(accounts) > 0 {
output.Onboarded = true
}

c.JSON(http.StatusOK, output)
}

func (handler *ApiHandler) ListCloudAccountsHandler(c *gin.Context) {
accounts := make([]models.Account, 0)
err := handler.db.NewRaw("SELECT * FROM accounts").Scan(handler.ctx, &accounts)
if err != nil {
logrus.WithError(err).Error("scan failed")
c.JSON(http.StatusInternalServerError, gin.H{"error": "scan failed"})
return
}

c.JSON(http.StatusOK, accounts)
}
3 changes: 3 additions & 0 deletions internal/api/v1/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func Endpoints(ctx context.Context, telemetry bool, analytics utils.Analytics, d
router.POST("/alerts/test", api.TestEndpointHandler)

router.GET("/telemetry", api.TelemetryHandler)
router.GET("/is_onboarded", api.IsOnboardedHandler)

router.GET("/cloud_accounts", api.ListCloudAccountsHandler)

router.NoRoute(gin.WrapH(http.FileServer(assetFS())))

Expand Down
135 changes: 128 additions & 7 deletions internal/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/BurntSushi/toml"
Expand All @@ -19,13 +20,15 @@ import (
"github.com/mongodb-forks/digest"
"github.com/oracle/oci-go-sdk/common"
"github.com/scaleway/scaleway-sdk-go/scw"
"github.com/tailwarden/komiser/models"
. "github.com/tailwarden/komiser/models"
"github.com/tailwarden/komiser/providers"
"github.com/tailwarden/komiser/utils"
tccommon "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
tccvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
"github.com/uptrace/bun"
"go.mongodb.org/atlas/mongodbatlas"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
Expand Down Expand Up @@ -62,27 +65,39 @@ func loadConfigFromBytes(b []byte) (*Config, error) {
return &config, nil
}

func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config, []providers.ProviderClient, error) {
func Load(configPath string, telemetry bool, analytics utils.Analytics, db *bun.DB) (*Config, []providers.ProviderClient, []models.Account, error) {
config, err := loadConfigFromFile(configPath)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

if len(config.SQLite.File) == 0 && config.Postgres.URI == "" {
return nil, nil, errors.New("postgres URI or sqlite file is missing")
return nil, nil, nil, errors.New("postgres URI or sqlite file is missing")
}

clients := make([]providers.ProviderClient, 0)
accounts := make([]models.Account, 0)

if len(config.AWS) > 0 {
for _, account := range config.AWS {
cloudAccount := models.Account{
Provider: "AWS",
Name: account.Name,
Credentials: map[string]string{
"profile": account.Profile,
"path": account.Path,
"source": account.Source,
},
}
accounts = append(accounts, cloudAccount)

if account.Source == "CREDENTIALS_FILE" {
if len(account.Path) > 0 {
cfg, err := awsConfig.LoadDefaultConfig(context.Background(), awsConfig.WithSharedConfigProfile(account.Profile), awsConfig.WithSharedCredentialsFiles(
[]string{account.Path},
))
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
clients = append(clients, providers.ProviderClient{
AWSClient: &cfg,
Expand All @@ -91,7 +106,7 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config
} else {
cfg, err := awsConfig.LoadDefaultConfig(context.Background(), awsConfig.WithSharedConfigProfile(account.Profile))
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
clients = append(clients, providers.ProviderClient{
AWSClient: &cfg,
Expand Down Expand Up @@ -119,6 +134,15 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.DigitalOcean) > 0 {
for _, account := range config.DigitalOcean {
cloudAccount := models.Account{
Provider: "DigitalOcean",
Name: account.Name,
Credentials: map[string]string{
"token": account.Token,
},
}
accounts = append(accounts, cloudAccount)

client := godo.NewFromToken(account.Token)
clients = append(clients, providers.ProviderClient{
DigitalOceanClient: client,
Expand All @@ -135,6 +159,16 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Oci) > 0 {
for _, account := range config.Oci {
cloudAccount := models.Account{
Provider: "OCI",
Name: account.Name,
Credentials: map[string]string{
"profile": account.Profile,
"source": account.Source,
},
}
accounts = append(accounts, cloudAccount)

if account.Source == "CREDENTIALS_FILE" {
client := common.DefaultConfigProvider()
clients = append(clients, providers.ProviderClient{
Expand All @@ -153,6 +187,15 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Civo) > 0 {
for _, account := range config.Civo {
cloudAccount := models.Account{
Provider: "Civo",
Name: account.Name,
Credentials: map[string]string{
"token": account.Token,
},
}
accounts = append(accounts, cloudAccount)

client, err := civogo.NewClient(account.Token, "LON1")
if err != nil {
log.Fatal(err)
Expand All @@ -172,6 +215,17 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Kubernetes) > 0 {
for _, account := range config.Kubernetes {
cloudAccount := models.Account{
Provider: "Kubernetes",
Name: account.Name,
Credentials: map[string]string{
"path": account.Path,
"contexts": strings.Join(account.Contexts, ";"),
},
}

accounts = append(accounts, cloudAccount)

kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: account.Path},
&clientcmd.ConfigOverrides{}).ClientConfig()
Expand Down Expand Up @@ -199,6 +253,16 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Linode) > 0 {
for _, account := range config.Linode {
cloudAccount := models.Account{
Provider: "Linode",
Name: account.Name,
Credentials: map[string]string{
"token": account.Token,
},
}

accounts = append(accounts, cloudAccount)

tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: account.Token})
oauth2Client := &http.Client{
Transport: &oauth2.Transport{
Expand All @@ -222,6 +286,17 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Tencent) > 0 {
for _, account := range config.Tencent {
cloudAccount := models.Account{
Provider: "Tencent",
Name: account.Name,
Credentials: map[string]string{
"secretId": account.SecretID,
"secretKey": account.SecretKey,
},
}

accounts = append(accounts, cloudAccount)

credential := tccommon.NewCredential(account.SecretID, account.SecretKey)
cpf := profile.NewClientProfile()
cpf.Language = "en-US"
Expand All @@ -245,6 +320,19 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Azure) > 0 {
for _, account := range config.Azure {
cloudAccount := models.Account{
Provider: "Azure",
Name: account.Name,
Credentials: map[string]string{
"clientId": account.ClientId,
"clientSecret": account.ClientSecret,
"tenantId": account.TenantId,
"subscriptionId": account.SubscriptionId,
},
}

accounts = append(accounts, cloudAccount)

creds, err := azidentity.NewClientSecretCredential(account.TenantId, account.ClientId, account.ClientSecret, &azidentity.ClientSecretCredentialOptions{})
if err != nil {
log.Fatal(err)
Expand All @@ -270,6 +358,18 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.Scaleway) > 0 {
for _, account := range config.Scaleway {
cloudAccount := models.Account{
Provider: "Scaleway",
Name: account.Name,
Credentials: map[string]string{
"accessKey": account.AccessKey,
"secretKey": account.SecretKey,
"organizationId": account.OrganizationId,
},
}

accounts = append(accounts, cloudAccount)

client, err := scw.NewClient(
scw.WithDefaultOrganizationID(account.OrganizationId),
scw.WithAuth(account.AccessKey, account.SecretKey),
Expand All @@ -293,6 +393,18 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config

if len(config.MongoDBAtlas) > 0 {
for _, account := range config.MongoDBAtlas {
cloudAccount := models.Account{
Provider: "MongoDB",
Name: account.Name,
Credentials: map[string]string{
"publicKey": account.PublicApiKey,
"privateKey": account.PrivateApiKey,
"organizationId": account.OrganizationID,
},
}

accounts = append(accounts, cloudAccount)

t := digest.NewTransport(account.PublicApiKey, account.PrivateApiKey)
tc, err := t.Client()
if err != nil {
Expand All @@ -308,8 +420,17 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config
}

if len(config.GCP) > 0 {
// Initialize a GCP client
for _, account := range config.GCP {
cloudAccount := models.Account{
Provider: "GCP",
Name: account.Name,
Credentials: map[string]string{
"accountKey": account.ServiceAccountKeyPath,
},
}

accounts = append(accounts, cloudAccount)

data, err := ioutil.ReadFile(account.ServiceAccountKeyPath)
if err != nil {
log.Fatal(err)
Expand All @@ -329,5 +450,5 @@ func Load(configPath string, telemetry bool, analytics utils.Analytics) (*Config
}
}

return config, clients, nil
return config, clients, accounts, nil
}
18 changes: 15 additions & 3 deletions internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ func Exec(address string, port int, configPath string, telemetry bool, a utils.A

ctx := context.Background()

cfg, clients, err := config.Load(configPath, telemetry, analytics)
cfg, clients, accounts, err := config.Load(configPath, telemetry, analytics, db)
if err != nil {
return err
}

err = setupSchema(cfg)
err = setupSchema(cfg, accounts)
if err != nil {
return err
}
Expand Down Expand Up @@ -179,7 +179,7 @@ func runServer(address string, port int, telemetry bool, cfg models.Config) erro
return nil
}

func setupSchema(c *models.Config) error {
func setupSchema(c *models.Config, accounts []models.Account) error {
var sqldb *sql.DB
var err error

Expand Down Expand Up @@ -216,6 +216,18 @@ func setupSchema(c *models.Config) error {
return err
}

_, err = db.NewCreateTable().Model((*models.Account)(nil)).IfNotExists().Exec(context.Background())
if err != nil {
return err
}

for _, account := range accounts {
_, err = db.NewInsert().Model(&account).Exec(context.Background())
if err != nil {
log.Warnf("%s account cannot be inserted to database", account.Provider)
}
}

// Created pre-defined views
untaggedResourcesView := models.View{
Name: "Untagged resources",
Expand Down
8 changes: 8 additions & 0 deletions models/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package models

type Account struct {
Id int64 `json:"id" bun:"id,pk,autoincrement"`
Provider string `json:"provider"`
Name string `json:"name"`
Credentials map[string]string `json:"credentials" bun:"credentials,unique"`
}

0 comments on commit 237dd76

Please sign in to comment.