Skip to content

Commit 4984835

Browse files
authored
Refactor app/pkg init logic (#913)
* Scope logic dealing with vendir.yml to VendirConfig * Refactor how Fetch is configured. Move build scoped fucntions to build. * Split VendirStep into VendirRunner and VendirConfigBuilder. Remove deadcode * Refactor git step. Remove deadcode. * Refactor GithubStep * Remove unused create step and refactor necessarily in app init * Refactor GitStep * Dedup app init logic. configureAppBuild => getAppBuildName * Refactor TemplateStep * Remove step interface and move Build interface * Refactoring package init * Add missing deffered cleanup * Remove file_utils. Use builins instead * Add check while running vendir sync to handle local directory case * Merge VendirCOnfiguration into FetchConfiguation * Move PackageBuild and AppBuild to buildconfigs * Move vendir config and related objects to sources package * Move init command files out of a separate package * Remove dependency of annotation for storing fetch mode * Moving constants to appropriate locations * Returning a non-pointer value from GetExport as it is always dereferenced. Removing stale comments * Stricter checks before running vendir sync. Making not exists check on files cleaner * Move source specific configuration to source.go. fetch.go => source.go. Refactoring init files * Remove unnecessary dependency on carvel-kapp-controller/.../exec package * Use vendirConfig.Contents while configuring package build instead of passing contents down the function tree * Move logic for initialising deploy section to build interface. Remove duplicate dependencies * TemplateConfiguration => Template. Othere refactoring.
1 parent 050ba8a commit 4984835

28 files changed

+1276
-1359
lines changed

cli/pkg/kctrl/cmd/app/init.go

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright 2022 VMware, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package app
5+
6+
import (
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/cppforlife/go-cli-ui/ui"
12+
"github.com/spf13/cobra"
13+
cmdcore "github.com/vmware-tanzu/carvel-kapp-controller/cli/pkg/kctrl/cmd/core"
14+
buildconfigs "github.com/vmware-tanzu/carvel-kapp-controller/cli/pkg/kctrl/local/buildconfigs"
15+
sources "github.com/vmware-tanzu/carvel-kapp-controller/cli/pkg/kctrl/local/sources"
16+
"github.com/vmware-tanzu/carvel-kapp-controller/cli/pkg/kctrl/logger"
17+
kcv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/kappctrl/v1alpha1"
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
"sigs.k8s.io/yaml"
20+
)
21+
22+
const (
23+
AppFileName = "app.yml"
24+
//TODO: Where should this constant reside, it is also used by package init
25+
LocalFetchAnnotationKey = "kctrl.carvel.dev/local-fetch-0"
26+
)
27+
28+
type InitOptions struct {
29+
ui cmdcore.AuthoringUI
30+
depsFactory cmdcore.DepsFactory
31+
logger logger.Logger
32+
chdir string
33+
}
34+
35+
func NewInitOptions(ui ui.UI, depsFactory cmdcore.DepsFactory, logger logger.Logger) *InitOptions {
36+
return &InitOptions{ui: cmdcore.NewAuthoringUIImpl(ui), depsFactory: depsFactory, logger: logger}
37+
}
38+
39+
func NewInitCmd(o *InitOptions) *cobra.Command {
40+
cmd := &cobra.Command{
41+
Use: "init",
42+
Short: "Initialize App",
43+
RunE: func(_ *cobra.Command, _ []string) error { return o.Run() },
44+
}
45+
46+
cmd.Flags().StringVar(&o.chdir, "chdir", "", "Working directory with app-build and other config")
47+
return cmd
48+
}
49+
50+
func (o *InitOptions) Run() error {
51+
if o.chdir != "" {
52+
err := os.Chdir(o.chdir)
53+
if err != nil {
54+
return err
55+
}
56+
}
57+
58+
o.ui.PrintHeaderText("\nPre-requisite")
59+
o.ui.PrintInformationalText("Welcome! Before we start on the app creation journey, please ensure the following pre-requites are met:\n* The Carvel suite of tools are installed. Do get familiar with the following Carvel tools: ytt, imgpkg, vendir, and kbld.\n* You have access to an OCI registry, and authenticated locally so that images can be pushed. e.g. docker login <REGISTRY URL>\n")
60+
61+
appBuild, err := buildconfigs.NewAppBuild()
62+
if err != nil {
63+
return err
64+
}
65+
66+
err = o.getAppBuildName(appBuild)
67+
if err != nil {
68+
return err
69+
}
70+
71+
err = o.writeAppFile(appBuild)
72+
if err != nil {
73+
return err
74+
}
75+
76+
fetchMode, err := sources.NewSource(o.ui, appBuild).Configure()
77+
if err != nil {
78+
return err
79+
}
80+
81+
err = sources.NewVendirRunner(o.ui).Sync(fetchMode)
82+
if err != nil {
83+
return err
84+
}
85+
86+
err = sources.NewTemplate(o.ui, appBuild).Configure(fetchMode)
87+
if err != nil {
88+
return err
89+
}
90+
91+
appBuild.InitializeOrKeepDeploySection()
92+
buildconfigs.ConfigureExportSection(appBuild, fetchMode == sources.LocalDirectory, sources.VendirSyncDirectory)
93+
err = appBuild.Save()
94+
if err != nil {
95+
return err
96+
}
97+
return nil
98+
}
99+
100+
func (o *InitOptions) getAppBuildName(appBuild *buildconfigs.AppBuild) error {
101+
o.ui.PrintHeaderText("\nBasic Information")
102+
wd, err := os.Getwd()
103+
if err != nil {
104+
return err
105+
}
106+
defaultAppName := filepath.Base(wd)
107+
appBuildObjectMeta := appBuild.GetObjectMeta()
108+
if appBuildObjectMeta == nil {
109+
appBuildObjectMeta = &metav1.ObjectMeta{}
110+
}
111+
if len(appBuildObjectMeta.Name) != 0 {
112+
defaultAppName = appBuildObjectMeta.Name
113+
}
114+
115+
textOpts := ui.TextOpts{
116+
Label: "Enter the app name",
117+
Default: defaultAppName,
118+
ValidateFunc: nil,
119+
}
120+
appName, err := o.ui.AskForText(textOpts)
121+
if err != nil {
122+
return err
123+
}
124+
appBuildObjectMeta.Name = appName
125+
appBuild.SetObjectMeta(appBuildObjectMeta)
126+
127+
err = appBuild.Save()
128+
if err != nil {
129+
return err
130+
}
131+
return nil
132+
}
133+
134+
func (o *InitOptions) writeAppFile(appBuild *buildconfigs.AppBuild) error {
135+
appConfig, err := o.generateApp(appBuild)
136+
if err != nil {
137+
return err
138+
}
139+
140+
appContent, err := yaml.Marshal(appConfig)
141+
if err != nil {
142+
return err
143+
}
144+
err = os.WriteFile(AppFileName, appContent, os.ModePerm)
145+
if err != nil {
146+
return err
147+
}
148+
o.ui.PrintHeaderText("\nOutput")
149+
o.ui.PrintInformationalText("Successfully updated app-build.yml\n")
150+
o.ui.PrintInformationalText("Successfully updated app.yml\n")
151+
o.ui.PrintHeaderText("\n**Next steps**")
152+
o.ui.PrintInformationalText("Created files can be consumed in following ways:\n1. Optionally, use 'kctrl dev deploy' to iterate on the app and deploy locally.\n2. Use 'kctrl app release' to release the app.\n")
153+
return nil
154+
}
155+
156+
func (o *InitOptions) generateApp(appBuild *buildconfigs.AppBuild) (kcv1alpha1.App, error) {
157+
var app kcv1alpha1.App
158+
_, err := os.Stat(AppFileName)
159+
if err != nil {
160+
if os.IsNotExist(err) {
161+
return o.createAppFromAppBuild(appBuild), nil
162+
}
163+
return kcv1alpha1.App{}, err
164+
}
165+
166+
data, err := os.ReadFile(AppFileName)
167+
if err != nil {
168+
return kcv1alpha1.App{}, err
169+
}
170+
err = yaml.Unmarshal(data, &app)
171+
if err != nil {
172+
return kcv1alpha1.App{}, err
173+
}
174+
175+
return app, nil
176+
177+
}
178+
179+
func (o *InitOptions) createAppFromAppBuild(appBuild *buildconfigs.AppBuild) kcv1alpha1.App {
180+
appName := "microservices-demo"
181+
serviceAccountName := fmt.Sprintf("%s-sa", appName)
182+
appAnnotation := map[string]string{
183+
LocalFetchAnnotationKey: ".",
184+
}
185+
appTemplateSection := appBuild.GetAppSpec().Template
186+
appDeploySection := appBuild.GetAppSpec().Deploy
187+
return kcv1alpha1.App{
188+
TypeMeta: metav1.TypeMeta{
189+
APIVersion: "kappctrl.k14s.io/v1alpha1",
190+
Kind: "App",
191+
},
192+
ObjectMeta: metav1.ObjectMeta{
193+
Name: appName,
194+
Annotations: appAnnotation,
195+
},
196+
Spec: kcv1alpha1.AppSpec{
197+
ServiceAccountName: serviceAccountName,
198+
Template: appTemplateSection,
199+
Deploy: appDeploySection,
200+
},
201+
}
202+
}

cli/pkg/kctrl/cmd/app/init/app_build.go

-150
This file was deleted.

0 commit comments

Comments
 (0)