Skip to content

Commit

Permalink
Merge branch 'main' into createAzApp_AzSDK
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgamero authored May 15, 2024
2 parents dfdbeed + 8db86bd commit bbfdf52
Show file tree
Hide file tree
Showing 46 changed files with 316 additions and 86 deletions.
51 changes: 50 additions & 1 deletion cmd/generate-workflow.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package cmd

import (
"fmt"
"strings"

"github.com/manifoldco/promptui"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"

"github.com/Azure/draft/pkg/prompts"
"github.com/Azure/draft/pkg/templatewriter"
"github.com/Azure/draft/pkg/templatewriter/writers"
"github.com/Azure/draft/pkg/workflows"
"github.com/Azure/draft/template"
)

type generateWorkflowCmd struct {
Expand Down Expand Up @@ -34,7 +41,7 @@ with draft on AKS. This command assumes the 'setup-gh' command has been run prop
flagValuesMap = gwCmd.workflowConfig.SetFlagValuesToMap()
}
log.Info("--> Generating Github workflow")
if err := workflows.CreateWorkflows(gwCmd.dest, gwCmd.deployType, gwCmd.flagVariables, gwCmd.templateWriter, flagValuesMap); err != nil {
if err := gwCmd.generateWorkflows(gwCmd.dest, gwCmd.deployType, gwCmd.flagVariables, gwCmd.templateWriter, flagValuesMap); err != nil {
return err
}

Expand All @@ -61,3 +68,45 @@ with draft on AKS. This command assumes the 'setup-gh' command has been run prop
func init() {
rootCmd.AddCommand(newGenerateWorkflowCmd())
}

func (gwc *generateWorkflowCmd) generateWorkflows(dest string, deployType string, flagVariables []string, templateWriter templatewriter.TemplateWriter, flagValuesMap map[string]string) error {
if flagValuesMap == nil {
return fmt.Errorf("flagValuesMap is nil")
}
var err error
for _, flagVar := range flagVariables {
flagVarName, flagVarValue, ok := strings.Cut(flagVar, "=")
if !ok {
return fmt.Errorf("invalid variable format: %s", flagVar)
}
flagValuesMap[flagVarName] = flagVarValue
log.Debugf("flag variable %s=%s", flagVarName, flagVarValue)
}

if deployType == "" {
selection := &promptui.Select{
Label: "Select k8s Deployment Type",
Items: []string{"helm", "kustomize", "manifests"},
}

_, deployType, err = selection.Run()
if err != nil {
return err
}
}

workflow := workflows.CreateWorkflowsFromEmbedFS(template.Workflows, dest)
workflowConfig, err := workflow.GetConfig(deployType)
if err != nil {
return fmt.Errorf("get config: %w", err)
}

customInputs, err := prompts.RunPromptsFromConfigWithSkips(workflowConfig, maps.Keys(flagValuesMap))
if err != nil {
return err
}

maps.Copy(customInputs, flagValuesMap)

return workflow.CreateWorkflowFiles(deployType, customInputs, templateWriter)
}
8 changes: 8 additions & 0 deletions cmd/setup-gh.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"
"github.com/Azure/draft/pkg/cred"
"github.com/manifoldco/promptui"
Expand Down Expand Up @@ -50,6 +51,13 @@ application and service principle, and will configure that application to trust

sc.AzClient.GraphClient = graphClient

roleAssignmentClient, err := armauthorization.NewRoleAssignmentsClient(sc.SubscriptionID, azCred, nil)
if err != nil {
return fmt.Errorf("getting role assignment client: %w", err)
}

sc.AzClient.RoleAssignClient = roleAssignmentClient

fillSetUpConfig(sc)

s := spinner.CreateSpinner("--> Setting up Github OIDC...")
Expand Down
4 changes: 2 additions & 2 deletions example/dockerfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ func TestWriteDockerfile(t *testing.T) {
expectError: false,
},
{
name: "Test Invalid Go Dockerfile Generation",
name: "Test Valid Go Dockerfile Generation with deafult",
inputVariables: map[string]string{
"PORT": "8080",
},
generationLanguage: "go",
expectError: true,
expectError: false,
},
{
name: "Test Invalid GenerationLanguage",
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.0
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0
github.com/briandowns/spinner v1.23.0
github.com/cenkalti/backoff/v4 v4.3.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0 h1:qtRcg5Y7jNJ4jEzPq4GpWLfTspHdNe2ZK6LjwGcjgmU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0/go.mod h1:lPneRe3TwsoDRKY4O6YDLXHhEWrD+TIRa8XrV/3/fqw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 h1:UrGzkHueDwAWDdjQxC+QaXHd4tVCkISYE9j7fSSXF8k=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0/go.mod h1:qskvSQeW+cxEE2bcKYyKimB1/KiQ9xpJ99bcHY0BX6c=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
Expand Down
2 changes: 2 additions & 0 deletions pkg/addons/addons.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func GenerateAddon(addons embed.FS, provider, addon, dest string, userInputs map
return err
}

addOnConfig.ApplyDefaultVariables(userInputs)

if err = osutil.CopyDir(addons, selectedAddonPath, addonDestPath, &addOnConfig.DraftConfig, userInputs, templateWriter); err != nil {
return err
}
Expand Down
19 changes: 15 additions & 4 deletions pkg/config/draftconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ type BuilderVar struct {
Description string `yaml:"description"`
VarType string `yaml:"type"`
ExampleValues []string `yaml:"exampleValues"`
IsPromptDisabled bool `yaml:"disablePrompt"`
}

type BuilderVarDefault struct {
Name string `yaml:"name"`
Value string `yaml:"value"`
ReferenceVar string `yaml:"referenceVar"`
Name string `yaml:"name"`
Value string `yaml:"value"`
ReferenceVar string `yaml:"referenceVar"`
IsPromptDisabled bool `yaml:"disablePrompt"`
}

func (d *DraftConfig) GetVariableExampleValues() map[string][]string {
Expand Down Expand Up @@ -64,6 +64,17 @@ func (d *DraftConfig) GetNameOverride(path string) string {
return prefix
}

// ApplyDefaultVariables will apply the defaults to variables that are not already set
func (d *DraftConfig) ApplyDefaultVariables(customConfig map[string]string) {
for _, variable := range d.VariableDefaults {
// handle where variable is not set or is set to an empty string from cli handling
if defaultVal, ok := customConfig[variable.Name]; !ok || defaultVal == "" {
log.Infof("Variable %s defaulting to value %s", variable.Name, variable.Value)
customConfig[variable.Name] = variable.Value
}
}
}

// TemplateVariableRecorder is an interface for recording variables that are used read using draft configs
type TemplateVariableRecorder interface {
Record(key, value string)
Expand Down
2 changes: 2 additions & 0 deletions pkg/deployments/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ func (d *Deployments) CopyDeploymentFiles(deployType string, customInputs map[st
deployConfig, ok := d.configs[deployType]
if !ok {
deployConfig = nil
} else {
deployConfig.ApplyDefaultVariables(customInputs)
}

if err := osutil.CopyDir(d.deploymentTemplates, srcDir, d.dest, deployConfig, customInputs, templateWriter); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/languages/languages.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func (l *Languages) CreateDockerfileForLanguage(lang string, customInputs map[st
draftConfig, ok := l.configs[lang]
if !ok {
draftConfig = nil
} else {
draftConfig.ApplyDefaultVariables(customInputs)
}

if err := osutil.CopyDir(l.dockerfileTemplates, srcDir, l.dest, draftConfig, customInputs, templateWriter); err != nil {
Expand Down
11 changes: 10 additions & 1 deletion pkg/prompts/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func RunPromptsFromConfigWithSkipsIO(config *config.DraftConfig, varsToSkip []st
log.Debugf("Skipping prompt for %s", promptVariableName)
continue
}
if customPrompt.IsPromptDisabled {
if GetIsPromptDisabled(customPrompt.Name, config.VariableDefaults) {
log.Debugf("Skipping prompt for %s as it has IsPromptDisabled=true", promptVariableName)
noPromptDefaultValue := GetVariableDefaultValue(promptVariableName, config.VariableDefaults, inputs)
if noPromptDefaultValue == "" {
Expand Down Expand Up @@ -90,6 +90,15 @@ func GetVariableDefaultValue(variableName string, variableDefaults []config.Buil
return defaultValue
}

func GetIsPromptDisabled(variableName string, variableDefaults []config.BuilderVarDefault) bool {
for _, variableDefault := range variableDefaults {
if variableDefault.Name == variableName {
return variableDefault.IsPromptDisabled
}
}
return false
}

func RunBoolPrompt(customPrompt config.BuilderVar, Stdin io.ReadCloser, Stdout io.WriteCloser) (string, error) {
newSelect := &promptui.Select{
Label: "Please select " + customPrompt.Description,
Expand Down
6 changes: 3 additions & 3 deletions pkg/prompts/prompts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ func TestRunPromptsFromConfigWithSkipsIO(t *testing.T) {
{
Name: "var1",
Description: "var1 description",
IsPromptDisabled: true,
},
},
VariableDefaults: []config.BuilderVarDefault{
{
Name: "var1",
Value: "defaultValue",
IsPromptDisabled: true,
},
},
},
Expand All @@ -176,14 +176,12 @@ func TestRunPromptsFromConfigWithSkipsIO(t *testing.T) {
{
Name: "var1-no-prompt",
Description: "var1 has IsPromptDisabled and should skip prompt and use default value",
IsPromptDisabled: true,
}, {
Name: "var2-default",
Description: "var2 has a default value and will receive an empty value, so it should use the default value",
}, {
Name: "var3-no-prompt",
Description: "var3 has IsPromptDisabled and should skip prompt and use default value",
IsPromptDisabled: true,
}, {
Name: "var4",
Description: "var4 has a default value, but has a value entered, so it should use the entered value",
Expand All @@ -193,12 +191,14 @@ func TestRunPromptsFromConfigWithSkipsIO(t *testing.T) {
{
Name: "var1-no-prompt",
Value: "defaultValueNoPrompt1",
IsPromptDisabled: true,
}, {
Name: "var2-default",
Value: "defaultValue2",
}, {
Name: "var3-no-prompt",
Value: "defaultValueNoPrompt3",
IsPromptDisabled: true,
}, {
Name: "var4",
Value: "defaultValue4",
Expand Down
14 changes: 12 additions & 2 deletions pkg/providers/az-client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import (
"context"
"errors"
"fmt"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"

msgraph "github.com/microsoftgraph/msgraph-sdk-go"
graphapp "github.com/microsoftgraph/msgraph-sdk-go/applications"
)

type AzClient struct {
AzTenantClient azTenantClient
GraphClient GraphClient
AzTenantClient azTenantClient
GraphClient GraphClient
RoleAssignClient RoleAssignClient
}

//go:generate mockgen -source=./az-client.go -destination=./mock/az-client.go .
Expand All @@ -39,3 +43,9 @@ func GetApplicationObjectId(ctx context.Context, appId string, graphClient Graph
}
return *appObjectId, nil
}

type RoleAssignClient interface {
CreateByID(ctx context.Context, roleAssignmentID string, parameters armauthorization.RoleAssignmentCreateParameters, options *armauthorization.RoleAssignmentsClientCreateByIDOptions) (armauthorization.RoleAssignmentsClientCreateByIDResponse, error)
}

var _ RoleAssignClient = &armauthorization.RoleAssignmentsClient{}
23 changes: 16 additions & 7 deletions pkg/providers/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"
graphmodels "github.com/microsoftgraph/msgraph-sdk-go/models"
"os/exec"
Expand Down Expand Up @@ -62,7 +63,7 @@ func InitiateAzureOIDCFlow(ctx context.Context, sc *SetUpCmd, s spinner.Spinner)
return err
}

if err := sc.assignSpRole(); err != nil {
if err := sc.assignSpRole(ctx); err != nil {
return err
}

Expand Down Expand Up @@ -156,14 +157,22 @@ func (sc *SetUpCmd) CreateServicePrincipal() error {
return nil
}

func (sc *SetUpCmd) assignSpRole() error {
func (sc *SetUpCmd) assignSpRole(ctx context.Context) error {
log.Debug("Assigning contributor role to service principal...")
scope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", sc.SubscriptionID, sc.ResourceGroupName)
assignSpRoleCmd := exec.Command("az", "role", "assignment", "create", "--role", "contributor", "--subscription", sc.SubscriptionID, "--assignee-object-id", sc.spObjectId, "--assignee-principal-type", "ServicePrincipal", "--scope", scope, "--only-show-errors")
out, err := assignSpRoleCmd.CombinedOutput()

objectID := sc.spObjectId
roleID := "contributor"

parameters := armauthorization.RoleAssignmentCreateParameters{
Properties: &armauthorization.RoleAssignmentProperties{
PrincipalID: &objectID,
RoleDefinitionID: &roleID,
},
}

_, err := sc.AzClient.RoleAssignClient.CreateByID(ctx, roleID, parameters, nil)
if err != nil {
log.Printf("%s\n", out)
return err
return fmt.Errorf("creating role assignment: %w", err)
}

log.Debug("Role assigned successfully!")
Expand Down
Loading

0 comments on commit bbfdf52

Please sign in to comment.