Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export functionality for project configuration #432

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions assets/assets.go

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions plugins/codeamp/graphql/project_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"strings"

"github.com/codeamp/circuit/plugins/codeamp/resourceconfig"
yaml "gopkg.in/yaml.v2"

auth "github.com/codeamp/circuit/plugins/codeamp/auth"
db_resolver "github.com/codeamp/circuit/plugins/codeamp/db"

Expand Down Expand Up @@ -122,3 +125,35 @@ func (u *ProjectResolverQuery) Projects(ctx context.Context, args *struct {
},
}, nil
}

func (u *ProjectResolverQuery) ExportProject(ctx context.Context, args *struct {
ID string
EnvironmentID string
}) (*string, error) {
// confirm environment and project object existence
project := model.Project{}
if err := u.DB.Where("id = ?", args.ID).Find(&project).Error; err != nil {
return nil, err
}

env := model.Environment{}
if err := u.DB.Where("id = ?", args.EnvironmentID).Find(&env).Error; err != nil {
return nil, err
}

// get exported project object from project resource config
exportedProject, err := resourceconfig.CreateProjectConfig(nil, u.DB, &project, &env).Export()
if err != nil {
return nil, err
}

// marshal exported project output to YAML string
exportedProjectYAMLBytes, err := yaml.Marshal(exportedProject)
if err != nil {
return nil, err
}

exportedProjectYAMLString := string(exportedProjectYAMLBytes)

return &exportedProjectYAMLString, nil
}
9 changes: 9 additions & 0 deletions plugins/codeamp/graphql/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ func (r *Resolver) Project(ctx context.Context, args *struct {
return initializer.Project(ctx, args)
}

// ExportProject
func (r *Resolver) ExportProject(ctx context.Context, args *struct {
ID string
EnvironmentID string
}) (*string, error) {
initializer := ProjectResolverQuery{DB: r.DB}
return initializer.ExportProject(ctx, args)
}

// Projects
func (r *Resolver) Projects(ctx context.Context, args *struct {
ProjectSearch *model.ProjectSearchInput
Expand Down
2 changes: 2 additions & 0 deletions plugins/codeamp/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type Query {
users(): [User]!
# Project
project(id: ID, slug: String, name: String, environmentID: String): Project
# ExportProject
exportProject(id: String!, environmentID: String!): String
# Projects
projects(projectSearch: ProjectSearchInput, params: PaginatorInput): ProjectList!
# Features
Expand Down
6 changes: 6 additions & 0 deletions plugins/codeamp/resourceconfig/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package resourceconfig

const (
NilDependencyForExportErr = "required dependency for exporting missing %s"
FailedTypeAssertionErr = "failed to assert type %s"
)
104 changes: 104 additions & 0 deletions plugins/codeamp/resourceconfig/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package resourceconfig

import (
"fmt"

"github.com/codeamp/circuit/plugins/codeamp/model"
"github.com/jinzhu/gorm"
)

// example struct that implements ResourceConfig
type ProjectConfig struct {
BaseResourceConfig // inherits methods from BaseConfig
db *gorm.DB
project *model.Project
environment *model.Environment
}

// For exporting purposes
type Project struct {
ProjectSettings ProjectSettings `yaml:"settings"`
ProjectExtensions []ProjectExtension `yaml:"extensions"`
Secrets []Secret `yaml:"secrets"`
Services []Service `yaml:"services"`
}

func CreateProjectConfig(config *string, db *gorm.DB, project *model.Project, env *model.Environment) *ProjectConfig {
return &ProjectConfig{
db: db,
project: project,
environment: env,
BaseResourceConfig: BaseResourceConfig{
config: config,
},
}
}

func (p *ProjectConfig) Export() (*Project, error) {
var project Project

if p.project == nil || p.environment == nil || p.db == nil {
return nil, fmt.Errorf(NilDependencyForExportErr, "project, environment, db")
}

childObjectQuery := p.db.Where("project_id = ? and environment_id = ?", p.project.Model.ID, p.environment.Model.ID)

// Collect services inside project
var services []model.Service
if err := childObjectQuery.Find(&services).Error; err != nil {
return nil, err
}

for _, service := range services {
exportedSvc, err := CreateServiceConfig(nil, p.db, &service, nil, nil).Export()
if err != nil {
return nil, err
}

project.Services = append(project.Services, *exportedSvc)
}

// Collect project extensions inside project
var pExtensions []model.ProjectExtension
if err := childObjectQuery.Find(&pExtensions).Error; err != nil {
return nil, err
}

for _, pExtension := range pExtensions {
exportedProjectExtension, err := CreateProjectExtensionConfig(nil, p.db, &pExtension, nil, nil).Export()
if err != nil {
return nil, err
}

project.ProjectExtensions = append(project.ProjectExtensions, *exportedProjectExtension)
}

var secrets []model.Secret
if err := childObjectQuery.Find(&secrets).Error; err != nil {
return nil, err
}

// Collect services inside project
for _, secret := range secrets {
exportedSecret, err := CreateSecretConfig(nil, p.db, &secret, nil, nil).Export()
if err != nil {
return nil, err
}

project.Secrets = append(project.Secrets, *exportedSecret)
}

projectSettings := model.ProjectSettings{}
if err := childObjectQuery.Find(&projectSettings).Error; err != nil {
return nil, err
}

exportedProjectSettings, err := CreateProjectSettingsConfig(nil, p.db, &projectSettings, nil, nil).Export()
if err != nil {
return nil, err
}

project.ProjectSettings = *exportedProjectSettings

return &project, nil
}
90 changes: 90 additions & 0 deletions plugins/codeamp/resourceconfig/projectextension.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package resourceconfig

import (
"encoding/json"
"fmt"

log "github.com/codeamp/logger"
"github.com/codeamp/transistor"

"github.com/codeamp/circuit/plugins/codeamp/model"
"github.com/jinzhu/gorm"
yaml "gopkg.in/yaml.v2"
)

type ProjectExtensionConfig struct {
projectExtension *model.ProjectExtension
ProjectConfig
}

type Extension struct {
Key string `yaml:"name"`
}

type ProjectExtension struct {
CustomConfig string `yaml:"customConfig"`
Config string `yaml:"config"`
Key string `yaml:"key"`
}

func CreateProjectExtensionConfig(config *string, db *gorm.DB, projectExtension *model.ProjectExtension, project *model.Project, env *model.Environment) *ProjectExtensionConfig {
return &ProjectExtensionConfig{
projectExtension: projectExtension,
ProjectConfig: ProjectConfig{
db: db,
project: project,
environment: env,
BaseResourceConfig: BaseResourceConfig{
config: config,
},
},
}
}

func (p *ProjectExtensionConfig) Export() (*ProjectExtension, error) {
extension := model.Extension{}

if p.projectExtension == nil || p.db == nil {
return nil, fmt.Errorf(NilDependencyForExportErr, "projectExtension, db")
}

if err := p.db.Where("id = ?", p.projectExtension.ExtensionID).First(&extension).Error; err != nil {
return nil, err
}

configArtifacts := []transistor.Artifact{}

unmarshaledProjectExtensionConfig := []transistor.Artifact{}
err := json.Unmarshal(p.projectExtension.Config.RawMessage, &unmarshaledProjectExtensionConfig)
if err != nil {
return nil, err
}

for _, artifact := range unmarshaledProjectExtensionConfig {
secretValue := model.SecretValue{}
// config artifact value is a reference to the actual secret object
secretID := artifact.Value
if p.db.Where("secret_id = ?", secretID).Order("created_at desc").First(&secretValue).RecordNotFound() {
log.InfoWithFields("secret value not found", log.Fields{
"secret_id": secretID,
})
}

configArtifact := transistor.Artifact{
Key: artifact.Key,
Value: secretValue.Value,
}
configArtifacts = append(configArtifacts, configArtifact)
}

artifactsBytes, err := yaml.Marshal(configArtifacts)
if err != nil {
return nil, err
}

return &ProjectExtension{
CustomConfig: string(p.projectExtension.CustomConfig.RawMessage),
Config: string(artifactsBytes),
Key: extension.Key,
}, nil
}
41 changes: 41 additions & 0 deletions plugins/codeamp/resourceconfig/projectsettings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package resourceconfig

import (
"fmt"

"github.com/codeamp/circuit/plugins/codeamp/model"
"github.com/jinzhu/gorm"
)

type ProjectSettingsConfig struct {
ProjectConfig
projectSettings *model.ProjectSettings
}

type ProjectSettings struct {
GitBranch string `yaml:"gitBranch"`
ContinuousDeploy bool `yaml:"continuousDeploy"`
}

func CreateProjectSettingsConfig(config *string, db *gorm.DB, projectSettings *model.ProjectSettings, project *model.Project, env *model.Environment) *ProjectSettingsConfig {
return &ProjectSettingsConfig{
projectSettings: projectSettings,
ProjectConfig: ProjectConfig{
db: db,
project: project,
environment: env,
BaseResourceConfig: BaseResourceConfig{config: config},
},
}
}

func (p *ProjectSettingsConfig) Export() (*ProjectSettings, error) {
if p.projectSettings == nil {
return nil, fmt.Errorf(NilDependencyForExportErr, "projectSettings")
}

return &ProjectSettings{
GitBranch: p.projectSettings.GitBranch,
ContinuousDeploy: p.projectSettings.ContinuousDeploy,
}, nil
}
21 changes: 21 additions & 0 deletions plugins/codeamp/resourceconfig/resourceconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package resourceconfig

// A ResourceConfig is an object that is used for importing and exporting
// resources in CodeAmp. Its purpose is to decouple implementation behaviors
// of Export and Import from objects when the respective operation is occurring
// and provide a general-purpose interface for any resource that wants to implement
// import/export behaviors.
type ResourceConfig interface {
Import() error
Export() (interface{}, error)
GetConfig() *string
}

type BaseResourceConfig struct {
ResourceConfig
config *string
}

func (b *BaseResourceConfig) GetConfig() *string {
return b.config
}
Loading