Skip to content

Commit df4817a

Browse files
committed
[v3] Add the Kptfile struct to render.
There are two approaches to read Kptfile. One is via templating which we can add comments to help users understand the Kptfile. The other is via go struct which is more accurate and less error-prone. Considering the reliability of the Kpt deprecation policies (the api schema is not expected to change and if it is changed it will be backward compatible. If not backward compatible, the deprecation has a year-long period)
1 parent fafdcd7 commit df4817a

File tree

3 files changed

+333
-15
lines changed

3 files changed

+333
-15
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
Copyright 2021 The Skaffold Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Note: This is copied from kpt schema version v1alpha2.
18+
package kptfile
19+
20+
import (
21+
"fmt"
22+
23+
"sigs.k8s.io/kustomize/kyaml/yaml"
24+
)
25+
26+
const (
27+
KptFileName = "Kptfile"
28+
KptFileKind = "Kptfile"
29+
KptFileGroup = "kpt.dev"
30+
KptFileVersion = "v1alpha2"
31+
KptFileAPIVersion = KptFileGroup + "/" + KptFileVersion
32+
)
33+
34+
// TypeMeta is the TypeMeta for KptFile instances.
35+
var TypeMeta = yaml.ResourceMeta{
36+
TypeMeta: yaml.TypeMeta{
37+
APIVersion: KptFileAPIVersion,
38+
Kind: KptFileName,
39+
},
40+
}
41+
42+
// KptFile contains information about a package managed with kpt.Frank Farzan, 4 months ago: • Define schema for Kptfile v1alpha2 (#1417)
43+
type KptFile struct {
44+
yaml.ResourceMeta `yaml:",inline"`
45+
46+
Upstream *Upstream `yaml:"upstream,omitempty"`
47+
48+
// UpstreamLock is a resolved locator for the last fetch of the package.
49+
UpstreamLock *UpstreamLock `yaml:"upstreamLock,omitempty"`
50+
51+
// Info contains metadata such as license, documentation, etc.
52+
Info *PackageInfo `yaml:"info,omitempty"`
53+
54+
// Pipeline declares the pipeline of functions.
55+
Pipeline *Pipeline `yaml:"pipeline,omitempty"`
56+
57+
// Inventory contains parameters for the inventory object used in apply.
58+
Inventory *Inventory `yaml:"inventory,omitempty"`
59+
}
60+
61+
// OriginType defines the type of origin for a package.
62+
type OriginType string
63+
64+
const (
65+
// GitOrigin specifies a package as having been cloned from a git repository.
66+
GitOrigin OriginType = "git"
67+
)
68+
69+
// UpdateStrategyType defines the strategy for updating a package from upstream.
70+
type UpdateStrategyType string
71+
72+
// ToUpdateStrategy takes a string representing an update strategy and will
73+
// return the strategy as an UpdateStrategyType. If the provided string does
74+
// not match any known update strategies, an error will be returned.
75+
func ToUpdateStrategy(strategy string) (UpdateStrategyType, error) {
76+
switch strategy {
77+
case string(ResourceMerge):
78+
return ResourceMerge, nil
79+
case string(FastForward):
80+
return FastForward, nil
81+
case string(ForceDeleteReplace):
82+
return ForceDeleteReplace, nil
83+
default:
84+
return "", fmt.Errorf("unknown update strategy %q", strategy)
85+
}
86+
}
87+
88+
const (
89+
// ResourceMerge performs a structural schema-aware comparison and
90+
// merges the changes into the local package.
91+
ResourceMerge UpdateStrategyType = "resource-merge"
92+
// FastForward fails without updating if the local package was modified
93+
// since it was fetched.
94+
FastForward UpdateStrategyType = "fast-forward"
95+
// ForceDeleteReplace wipes all local changes to the package.
96+
ForceDeleteReplace UpdateStrategyType = "force-delete-replace"
97+
)
98+
99+
// UpdateStrategies is a slice with all the supported update strategies.
100+
var UpdateStrategies = []UpdateStrategyType{
101+
ResourceMerge,
102+
FastForward,
103+
ForceDeleteReplace,
104+
}
105+
106+
// UpdateStrategiesAsStrings returns a list of update strategies as strings.
107+
func UpdateStrategiesAsStrings() []string {
108+
var strs []string
109+
for _, s := range UpdateStrategies {
110+
strs = append(strs, string(s))
111+
}
112+
return strs
113+
}
114+
115+
// Upstream is a user-specified upstream locator for a package.
116+
type Upstream struct {
117+
// Type is the type of origin.
118+
Type OriginType `yaml:"type,omitempty"`
119+
120+
// Git is the locator for a package stored on Git.
121+
Git *Git `yaml:"git,omitempty"`
122+
123+
// UpdateStrategy declares how a package will be updated from upstream.
124+
UpdateStrategy UpdateStrategyType `yaml:"updateStrategy,omitempty"`
125+
}
126+
127+
// Git is the user-specified locator for a package on Git.
128+
type Git struct {
129+
// Repo is the git repository the package.
130+
// e.g. 'https://github.com/kubernetes/examples.git'
131+
Repo string `yaml:"repo,omitempty"`
132+
133+
// Directory is the sub directory of the git repository.
134+
// e.g. 'staging/cockroachdb'
135+
Directory string `yaml:"directory,omitempty"`
136+
137+
// Ref can be a Git branch, tag, or a commit SHA-1.
138+
Ref string `yaml:"ref,omitempty"`
139+
}
140+
141+
// UpstreamLock is a resolved locator for the last fetch of the package.
142+
type UpstreamLock struct {
143+
// Type is the type of origin.
144+
Type OriginType `yaml:"type,omitempty"`
145+
146+
// Git is the resolved locator for a package on Git.
147+
Git *GitLock `yaml:"git,omitempty"`
148+
}
149+
150+
// GitLock is the resolved locator for a package on Git.
151+
type GitLock struct {
152+
// Repo is the git repository that was fetched.
153+
// e.g. 'https://github.com/kubernetes/examples.git'
154+
Repo string `yaml:"repo,omitempty"`
155+
156+
// Directory is the sub directory of the git repository that was fetched.
157+
// e.g. 'staging/cockroachdb'
158+
Directory string `yaml:"directory,omitempty"`
159+
160+
// Ref can be a Git branch, tag, or a commit SHA-1 that was fetched.
161+
// e.g. 'master'
162+
Ref string `yaml:"ref,omitempty"`
163+
164+
// Commit is the SHA-1 for the last fetch of the package.
165+
// This is set by kpt for bookkeeping purposes.
166+
Commit string `yaml:"commit,omitempty"`
167+
}
168+
169+
// PackageInfo contains optional information about the package such as license, documentation, etc.
170+
// These fields are not consumed by any functionality in kpt and are simply passed through.
171+
// Note that like any other KRM resource, humans and automation can also use `metadata.labels` and
172+
// `metadata.annotations` as the extrension mechanism.
173+
type PackageInfo struct {
174+
// Site is the URL for package web page.
175+
Site string `yaml:"site,omitempty"`
176+
177+
// Email is the list of emails for the package authors.
178+
Emails []string `yaml:"emails,omitempty"`
179+
180+
// SPDX license identifier (e.g. "Apache-2.0"). See: https://spdx.org/licenses/
181+
License string `yaml:"license,omitempty"`
182+
183+
// Relative slash-delimited path to the license file (e.g. LICENSE.txt)
184+
LicenseFile string `yaml:"licenseFile,omitempty"`
185+
186+
// Doc is the path to documentation about the package.
187+
Doc string `yaml:"doc,omitempty"`
188+
189+
// Description contains a short description of the package.
190+
Description string `yaml:"description,omitempty"`
191+
192+
// Keywords is a list of keywords for this package.
193+
Keywords []string `yaml:"keywords,omitempty"`
194+
195+
// Man is the path to documentation about the package
196+
Man string `yaml:"man,omitempty"`
197+
}
198+
199+
// Subpackages declares a local or remote subpackage.
200+
type Subpackage struct {
201+
// Name of the immediate subdirectory relative to this Kptfile where the suppackage
202+
// either exists (local subpackages) or will be fetched to (remote subpckages).
203+
// This must be unique across all subpckages of a package.
204+
LocalDir string `yaml:"localDir,omitempty"`
205+
206+
// Upstream is a reference to where the subpackage should be fetched from.
207+
// Whether a subpackage is local or remote is determined by whether Upstream is specified.
208+
Upstream *Upstream `yaml:"upstream,omitempty"`
209+
}
210+
211+
// Pipeline declares a pipeline of functions used to mutate or validate resources.
212+
type Pipeline struct {
213+
// Sources defines the source packages to resolve as input to the pipeline. Possible values:
214+
// a) A slash-separated, OS-agnostic relative package path which may include '.' and '..' e.g. './base', '../foo'
215+
// The source package is resolved recursively.
216+
// b) Resources in this package using '.'. Meta resources such as the Kptfile, Pipeline, and function configs
217+
// are excluded.
218+
// c) Resources in this package AND all resolved subpackages using './*'
219+
//
220+
// Resultant list of resources are ordered:
221+
// - According to the order of sources specified in this array.
222+
// - When using './*': Subpackages are resolved in alphanumerical order before package resources.
223+
//
224+
// When omitted, defaults to './*'.
225+
// Sources []string `yaml:"sources,omitempty"`
226+
227+
// Following fields define the sequence of functions in the pipeline.
228+
// Input of the first function is the resolved sources.
229+
// Input of the second function is the output of the first function, and so on.
230+
// Order of operation: mutators, validators
231+
232+
// Mutators defines a list of of KRM functions that mutate resources.
233+
Mutators []Function `yaml:"mutators,omitempty"`
234+
235+
// Validators defines a list of KRM functions that validate resources.
236+
// Validators are not permitted to mutate resources.
237+
Validators []Function `yaml:"validators,omitempty"`
238+
}
239+
240+
// String returns the string representation of Pipeline struct
241+
// The string returned is the struct content in Go default format.
242+
func (p *Pipeline) String() string {
243+
return fmt.Sprintf("%+v", *p)
244+
}
245+
246+
// IsEmpty returns true if the pipeline doesn't contain any functions in any of
247+
// the function chains (mutators, validators).
248+
func (p *Pipeline) IsEmpty() bool {
249+
if p == nil {
250+
return true
251+
}
252+
if len(p.Mutators) == 0 && len(p.Validators) == 0 {
253+
return true
254+
}
255+
return false
256+
}
257+
258+
// Function specifies a KRM function.
259+
type Function struct {
260+
// `Image` specifies the function container image.
261+
// It can either be fully qualified, e.g.:
262+
//
263+
// image: gcr.io/kpt-fn/set-labels
264+
//
265+
// Optionally, kpt can be configured to use a image
266+
// registry host-path that will be used to resolve the image path in case
267+
// the image path is missing (Defaults to gcr.io/kpt-fn).
268+
// e.g. The following resolves to gcr.io/kpt-fn/set-labels:
269+
//
270+
// image: set-labels
271+
Image string `yaml:"image,omitempty"`
272+
273+
// `ConfigPath` specifies a slash-delimited relative path to a file in the current directory
274+
// containing a KRM resource used as the function config. This resource is
275+
// excluded when resolving 'sources', and as a result cannot be operated on
276+
// by the pipeline.
277+
ConfigPath string `yaml:"configPath,omitempty"`
278+
279+
// `ConfigMap` is a convenient way to specify a function config of kind ConfigMap.
280+
ConfigMap map[string]string `yaml:"configMap,omitempty"`
281+
}
282+
283+
// Inventory encapsulates the parameters for the inventory resource applied to a cluster.
284+
// All of the the parameters are required if any are set.
285+
type Inventory struct {
286+
// Namespace for the inventory resource.
287+
Namespace string `yaml:"namespace,omitempty"`
288+
// Name of the inventory resource.
289+
Name string `yaml:"name,omitempty"`
290+
// Unique label to identify inventory resource in cluster.
291+
InventoryID string `yaml:"inventoryID,omitempty"`
292+
Labels map[string]string `yaml:"labels,omitempty"`
293+
Annotations map[string]string `yaml:"annotations,omitempty"`
294+
}

pkg/skaffold/render/renderer/renderer.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,51 +24,56 @@ import (
2424
"path/filepath"
2525

2626
"github.com/sirupsen/logrus"
27+
"gopkg.in/yaml.v2"
2728

2829
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
2930
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
3031
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/generate"
32+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/kptfile"
3133
latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2"
3234
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
3335
)
3436

3537
const (
3638
DefaultHydrationDir = ".kpt-pipeline"
3739
dryFileName = "manifests.yaml"
38-
KptFileName = "Kptfile"
3940
)
4041

4142
type Renderer interface {
4243
Render(context.Context, io.Writer, []graph.Artifact) error
4344
}
4445

46+
// NewSkaffoldRenderer creates a new Renderer object from the latestV2 API schema.
4547
func NewSkaffoldRenderer(config *latestV2.RenderConfig, workingDir string) Renderer {
4648
// TODO(yuwenma): return instance of kpt-managed mode or skaffold-managed mode defer to the config.Path fields.
4749
// The alpha implementation only has skaffold-managed mode.
50+
// TODO(yuwenma): The current work directory may not be accurate if users use --filepath flag.
51+
hydrationDir := filepath.Join(workingDir, DefaultHydrationDir)
4852
generator := generate.NewGenerator(workingDir, *config.Generate)
49-
return &SkaffoldRenderer{Generator: *generator, workingDir: workingDir}
53+
return &SkaffoldRenderer{Generator: *generator, workingDir: workingDir, hydrationDir: hydrationDir}
5054
}
5155

5256
type SkaffoldRenderer struct {
5357
generate.Generator
54-
workingDir string
55-
labels map[string]string
58+
workingDir string
59+
hydrationDir string
60+
labels map[string]string
5661
}
5762

5863
// prepareHydrationDir guarantees the existence of a kpt-initialized temporary directory.
5964
// This directory is used to cache DRY config and hydrates the DRY config to WET config in-place.
6065
// This is needed because kpt v1 only supports in-place config while users may not want to have their config be
6166
// hydrated in place.
6267
func (r *SkaffoldRenderer) prepareHydrationDir(ctx context.Context) error {
63-
// TODO(yuwenma): The current work directory may not be accurate if users use --filepath flag.
64-
outputPath := filepath.Join(r.workingDir, DefaultHydrationDir)
65-
logrus.Debugf("creating render directory: %v", outputPath)
66-
67-
if err := os.MkdirAll(outputPath, os.ModePerm); err != nil {
68-
return fmt.Errorf("creating cache directory for hydration: %w", err)
68+
if _, err := os.Stat(r.hydrationDir); os.IsNotExist(err) {
69+
logrus.Debugf("creating render directory: %v", r.hydrationDir)
70+
if err := os.MkdirAll(r.hydrationDir, os.ModePerm); err != nil {
71+
return fmt.Errorf("creating cache directory for hydration: %w", err)
72+
}
6973
}
70-
if _, err := os.Stat(filepath.Join(outputPath, KptFileName)); os.IsNotExist(err) {
71-
cmd := exec.CommandContext(ctx, "kpt", "pkg", "init", outputPath)
74+
kptFilePath := filepath.Join(r.hydrationDir, kptfile.KptFileName)
75+
if _, err := os.Stat(kptFilePath); os.IsNotExist(err) {
76+
cmd := exec.CommandContext(ctx, "kpt", "pkg", "init", r.hydrationDir)
7277
if _, err := util.RunCmdOut(cmd); err != nil {
7378
return err
7479
}
@@ -92,11 +97,24 @@ func (r *SkaffoldRenderer) Render(ctx context.Context, out io.Writer, builds []g
9297
manifests.SetLabels(r.labels)
9398

9499
// cache the dry manifests to the temp directory. manifests.yaml will be truncated if already exists.
95-
dryConfigPath := filepath.Join(r.workingDir, DefaultHydrationDir, dryFileName)
100+
dryConfigPath := filepath.Join(r.hydrationDir, dryFileName)
96101
if err := manifest.Write(manifests.String(), dryConfigPath, out); err != nil {
97102
return err
98103
}
99104

100-
// TODO: mutate and validate
105+
// Read the existing Kptfile content. Kptfile is guaranteed to be exist in prepareHydrationDir.
106+
kptFilePath := filepath.Join(r.hydrationDir, kptfile.KptFileName)
107+
file, err := os.Open(kptFilePath)
108+
if err != nil {
109+
return err
110+
}
111+
defer file.Close()
112+
kfConfig := &kptfile.KptFile{}
113+
if err := yaml.NewDecoder(file).Decode(&kfConfig); err != nil {
114+
return err
115+
}
116+
117+
// TODO: Update the Kptfile with the new validators.
118+
// TODO: Update the Kptfile with the new mutators.
101119
return nil
102120
}

0 commit comments

Comments
 (0)