Skip to content

Commit

Permalink
func write: write localsettings to .func/local.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
vyasgun committed Jan 18, 2024
1 parent a4e4885 commit 824941c
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 16 deletions.
4 changes: 2 additions & 2 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ EXAMPLES
"Git revision (branch) to be used when deploying via the Git repository ($FUNC_GIT_BRANCH)")
cmd.Flags().StringP("git-dir", "d", f.Build.Git.ContextDir,
"Directory in the Git repository containing the function (default is the root) ($FUNC_GIT_DIR)")
cmd.Flags().BoolP("remote", "R", f.Deploy.Remote,
cmd.Flags().BoolP("remote", "R", f.Local.Remote,
"Trigger a remote deployment. Default is to deploy and build from the local system ($FUNC_REMOTE)")
cmd.Flags().String("pvc-size", f.Build.PVCSize,
"When triggering a remote deployment, set a custom volume size to allocate for the build operation ($FUNC_PVC_SIZE)")
Expand Down Expand Up @@ -506,8 +506,8 @@ func (c deployConfig) Configure(f fn.Function) (fn.Function, error) {
f.Build.Git.ContextDir = c.GitDir
f.Build.Git.Revision = c.GitBranch // TODO: should match; perhaps "refSpec"
f.Deploy.Namespace = c.Namespace
f.Deploy.Remote = c.Remote
f.Deploy.ServiceAccountName = c.ServiceAccountName
f.Local.Remote = c.Remote

// PVCSize
// If a specific value is requested, ensure it parses as a resource.Quantity
Expand Down
6 changes: 3 additions & 3 deletions cmd/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if !f.Deploy.Remote {
if !f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}

Expand All @@ -1449,7 +1449,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if !f.Deploy.Remote {
if !f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}

Expand All @@ -1464,7 +1464,7 @@ func TestDeploy_RemotePersists(t *testing.T) {
if f, err = fn.NewFunction(root); err != nil {
t.Fatal(err)
}
if f.Deploy.Remote {
if f.Local.Remote {
t.Fatalf("value of remote flag not persisted")
}
}
Expand Down
56 changes: 50 additions & 6 deletions pkg/functions/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ const (

// RunDataDir holds transient runtime metadata
// By default it is excluded from source control.
RunDataDir = ".func"
RunDataDir = ".func"
RunDataLocalFile = "local.yaml"
)

// Local represents the transient runtime metadata which
// is only relevant to the local copy of the function
type Local struct {
// Remote indicates the deployment (and possibly build) process are to
// be triggered in a remote environment rather than run locally.
Remote bool `yaml:"remote,omitempty"`
}

// Function
type Function struct {
// SpecVersion at which this function is known to be compatible.
Expand Down Expand Up @@ -87,6 +96,8 @@ type Function struct {

// Deploy defines the deployment properties for a function
Deploy DeploySpec `yaml:"deploy,omitempty"`

Local Local `yaml:"-"`
}

// BuildSpec
Expand Down Expand Up @@ -137,10 +148,6 @@ type DeploySpec struct {
// Namespace into which the function is deployed on supported platforms.
Namespace string `yaml:"namespace,omitempty"`

// Remote indicates the deployment (and possibly build) process are to
// be triggered in a remote environment rather than run locally.
Remote bool `yaml:"remote,omitempty"`

// Map containing user-supplied annotations
// Example: { "division": "finance" }
Annotations map[string]string `yaml:"annotations,omitempty"`
Expand Down Expand Up @@ -257,6 +264,7 @@ func NewFunction(root string) (f Function, err error) {
errorText += "\n" + "Migration: " + functionMigrationError.Error()
return Function{}, errors.New(errorText)
}
f.Local, err = f.newLocal()
return
}

Expand Down Expand Up @@ -385,7 +393,23 @@ func (f Function) Write() (err error) {
}
// TODO: open existing file for writing, such that existing permissions
// are preserved?
return os.WriteFile(filepath.Join(f.Root, FunctionFile), bb, 0644)
err = os.WriteFile(filepath.Join(f.Root, FunctionFile), bb, 0644)
if err != nil {
return
}

// Write local settings
err = ensureRunDataDir(f.Root)
if err != nil {
return
}
if bb, err = yaml.Marshal(&f.Local); err != nil {
return
}
localConfigPath := filepath.Join(f.Root, RunDataDir, RunDataLocalFile)

err = os.WriteFile(localConfigPath, bb, 0644)
return
}

type stampOptions struct{ journal bool }
Expand Down Expand Up @@ -666,3 +690,23 @@ func (f Function) BuildStamp() string {
}
return string(b)
}

// localSettings returns the local settings set for the function
func (f Function) newLocal() (localConfig Local, err error) {
err = ensureRunDataDir(f.Root)
if err != nil {
return
}
localSettingsPath := filepath.Join(f.Root, RunDataDir, RunDataLocalFile)
if _, err = os.Stat(localSettingsPath); os.IsNotExist(err) {
err = nil
return
}
b, err := os.ReadFile(localSettingsPath)
if err != nil {
return
}

err = yaml.Unmarshal(b, &localConfig)
return
}
149 changes: 149 additions & 0 deletions pkg/functions/function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
"testing"
"time"

"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/google/go-cmp/cmp"

fn "knative.dev/func/pkg/functions"
Expand Down Expand Up @@ -398,3 +401,149 @@ func TestFunction_Stamp(t *testing.T) {
t.Fatal("expected journal log not found")
}
}

// TestFunction_Local checks if writing a function with custom Local spec
// stays the same for the current system. The test does the following:
//
// create a new function
// set Local.Remote to true
// write it to the disk
// load it again into a new function object
//
// The load should be successful and Local.Remote should be true
func TestFunction_Local(t *testing.T) {
root, rm := Mktemp(t)
defer rm()
fConfig := fn.Function{Root: root, Runtime: "go", Name: "f"}
client := fn.New(fn.WithBuilder(mock.NewBuilder()), fn.WithRegistry(TestRegistry))
f, err := client.Init(fConfig)
if err != nil {
t.Fatal(err)
}
f.Local.Remote = true

err = f.Write()
if err != nil {
t.Fatal(err)
}

// Load the function from the same location
f, err = fn.NewFunction(root)
if err != nil {
t.Fatal(err)
}

if !f.Local.Remote {
t.Fatal("expected remote flag to be set")
}
}

// TestFunction_LocalTransient ensures that the Local field is transient and
// is not serialised in a way that affects other clones of the function.
// The test does the following:
//
// create a function (with Local.Remote set)
// push the function to a remote repo (locally setup for the test)
// clone the function from the remote repo into a new location
//
// The new function should not have Local.Remote set (as it is a transient field)
func TestFunction_LocalTransient(t *testing.T) {

// Initialise a new function
root, rm := Mktemp(t)
defer rm()

fConfig := fn.Function{Root: root, Runtime: "go", Name: "f", Image: "test:latest"}
client := fn.New(fn.WithBuilder(mock.NewBuilder()))
f, err := client.Init(fConfig)
if err != nil {
t.Fatal(err)
}
f.Local.Remote = true

err = f.Write()
if err != nil {
t.Fatal(err)
}

// Initialise the function directory as a git repo
repo, err := git.PlainInit(root, false)
if err != nil {
t.Fatal(err)
}

// commit the function files
wt, err := repo.Worktree()
if err != nil {
t.Fatal(err)
}
if _, err = wt.Add("."); err != nil {
t.Fatal(err)
}
if _, err = wt.Commit("init", &git.CommitOptions{
All: true,
AllowEmptyCommits: false,
Author: &object.Signature{
Name: "xyz",
Email: "xyz@abc.com",
When: time.Now(),
},
Committer: &object.Signature{
Name: "xyz",
Email: "xyz@abc.com",
When: time.Now(),
},
}); err != nil {
t.Fatal(err)
}

// Create a remote and push the function
remotePath, remoteRm := Mktemp(t)
defer remoteRm()
if _, err = git.PlainInit(remotePath, true); err != nil {
t.Fatal(err)
}

_, err = repo.CreateRemote(&config.RemoteConfig{
Name: "origin",
URLs: []string{remotePath},
Mirror: false,
Fetch: nil,
})
if err != nil {
t.Fatal(err)
}

err = repo.Push(&git.PushOptions{
RemoteName: "origin",
RemoteURL: remotePath,
InsecureSkipTLS: true,
})
if err != nil {
t.Fatal()
}

// Create a new directory to clone the function in
newRoot, newRm := Mktemp(t)
defer newRm()

// Clone the pushed function
_, err = git.PlainClone(newRoot, false, &git.CloneOptions{
URL: remotePath,
RemoteName: "origin",
InsecureSkipTLS: true,
})
if err != nil {
t.Fatal(err)
}

// Read the function from the new location
newFunc, err := fn.NewFunction(newRoot)
if err != nil {
t.Fatal(newFunc, err)
}

if newFunc.Local.Remote {
t.Fatal("Remote not supposed to be set")
}
}
2 changes: 1 addition & 1 deletion pkg/pipelines/tekton/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ func TestGitlab(t *testing.T) {
PVCSize: "256Mi",
},
Deploy: fn.DeploySpec{
Remote: true,
Namespace: ns,
},
Local: fn.Local{Remote: true},
}
f = fn.NewFunctionWith(f)
err = f.Write()
Expand Down
4 changes: 0 additions & 4 deletions schema/func_yaml-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@
"type": "string",
"description": "Namespace into which the function is deployed on supported platforms."
},
"remote": {
"type": "boolean",
"description": "Remote indicates the deployment (and possibly build) process are to\nbe triggered in a remote environment rather than run locally."
},
"annotations": {
"patternProperties": {
".*": {
Expand Down

0 comments on commit 824941c

Please sign in to comment.