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

✨ Inject location #143

Merged
merged 5 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
docker save -o /tmp/tackle2-addon-analyzer.tar quay.io/konveyor/tackle2-addon-analyzer:latest

- name: Upload tackle2-addon-analyzer image as artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: tackle2-addon-analyzer
path: /tmp/tackle2-addon-analyzer.tar
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ fmt: $(GOIMPORTS)
vet:
go vet $(PKG)

test:
go test -count=1 -v ./cmd/...

# Ensure goimports installed.
$(GOIMPORTS):
go install golang.org/x/tools/cmd/goimports@v0.24
2 changes: 1 addition & 1 deletion cmd/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (r *Analyzer) Run() (issueBuilder *builder.Issues, depBuilder *builder.Deps
// options builds Analyzer options.
func (r *Analyzer) options(output, depOutput string) (options command.Options, err error) {
settings := &Settings{}
err = settings.AppendExtensions()
err = settings.AppendExtensions(&r.Mode)
if err != nil {
return
}
Expand Down
39 changes: 37 additions & 2 deletions cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"testing"

"github.com/konveyor/analyzer-lsp/provider"
"github.com/onsi/gomega"
)

Expand Down Expand Up @@ -130,7 +131,8 @@ func TestIncidentSelector(t *testing.T) {

func TestInjectorDefaults(t *testing.T) {
g := gomega.NewGomegaWithT(t)
inj := ResourceInjector{dict: make(map[string]any)}
inj := ResourceInjector{}
inj.dict = make(map[string]any)
r := &Resource{
Fields: []Field{
{
Expand All @@ -152,7 +154,8 @@ func TestInjectorDefaults(t *testing.T) {

func TestInjectorTypeCast(t *testing.T) {
g := gomega.NewGomegaWithT(t)
inj := ResourceInjector{dict: make(map[string]any)}
inj := ResourceInjector{}
inj.dict = make(map[string]any)
r := &Resource{
Fields: []Field{
{
Expand Down Expand Up @@ -207,3 +210,35 @@ func TestInjectorTypeCast(t *testing.T) {
err = inj.addDefaults(r)
g.Expect(errors.Is(err, &TypeError{})).To(gomega.BeTrue())
}

func TestInject(t *testing.T) {
g := gomega.NewGomegaWithT(t)

key := "location"
path := "/tmp/x"
inj := Injector{}
inj.Use(make(map[string]any))
inj.dict[key] = path
md := &Metadata{}
md.Provider.InitConfig = []provider.InitConfig{
{Location: "$(" + key + ")"},
}
err := inj.Inject(md)
g.Expect(err).To(gomega.BeNil())
g.Expect(md.Provider.InitConfig[0].Location).To(gomega.Equal(path))
}

func TestRawInject(t *testing.T) {
g := gomega.NewGomegaWithT(t)

key := "location"
path := "/tmp/x"
inj := Injector{}
inj.Use(make(map[string]any))
inj.dict[key] = path
md := map[string]any{
"Location": "$(" + key + ")",
}
md2 := inj.inject(md).(map[string]any)
g.Expect(md2["Location"]).To(gomega.Equal(path))
}
241 changes: 144 additions & 97 deletions cmd/injector.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,25 @@ func (e *TypeError) Is(err error) (matched bool) {
return
}

// KeyConflictError reports key redefined errors.
type KeyConflictError struct {
Key string
Value any
}

func (e *KeyConflictError) Error() (s string) {
return fmt.Sprintf(
"Key: '%s' = '%v' cannot be redefined.",
e.Key,
e.Value)
}

func (e *KeyConflictError) Is(err error) (matched bool) {
var inst *KeyConflictError
matched = errors.As(err, &inst)
return
}

// Field injection specification.
type Field struct {
Name string `json:"name"`
Expand Down Expand Up @@ -179,56 +198,141 @@ func (p *ParsedSelector) With(s string) {
}
}

// ResourceInjector inject resources into extension metadata.
// Example:
// metadata:
// provider:
// address: localhost:$(PORT)
// initConfig:
// - providerSpecificConfig:
// mavenInsecure: $(maven.insecure)
// mavenSettingsFile: $(maven.settings.path)
// name: java
// resources:
// - selector: identity:kind=maven
// fields:
// - key: maven.settings.path
// name: settings
// path: /shared/creds/maven/settings.xml
// - selector: setting:key=mvn.insecure.enabled
// fields:
// - key: maven.insecure
// name: value
type ResourceInjector struct {
// Injector replaces variables in the object.
// format: $(variable).
type Injector struct {
dict map[string]any
}

// Inject resources into extension metadata.
// Returns injected provider (settings).
func (r *ResourceInjector) Inject(extension *api.Extension) (p *provider.Config, err error) {
mp := r.asMap(extension.Metadata)
md := Metadata{}
err = r.object(mp, &md)
func (r *Injector) Inject(md *Metadata) (err error) {
r.init()
mp := r.asMap(md)
mp = r.inject(mp).(map[string]any)
err = r.object(mp, md)
if err != nil {
return
}
err = r.build(&md)
if err != nil {
return
}

// Use map.
func (r *Injector) Use(d map[string]any) {
r.dict = d
}

// constructor.
func (r *Injector) init() {
if r.dict == nil {
r.dict = make(map[string]any)
}
}

// inject replaces `dict` variables referenced in metadata.
func (r *Injector) inject(in any) (out any) {
if r.dict == nil {
return
}
mp = r.asMap(&md.Provider)
mp = r.inject(mp).(map[string]any)
err = r.object(mp, &md.Provider)
switch node := in.(type) {
case map[string]any:
for k, v := range node {
node[k] = r.inject(v)
}
out = node
case []any:
var injected []any
for _, n := range node {
injected = append(
injected,
r.inject(n))
}
out = injected
case string:
for {
match := KeyRegex.FindStringSubmatch(node)
if len(match) < 3 {
break
}
v := r.dict[match[2]]
if len(node) > len(match[0]) {
node = strings.Replace(
node,
match[0],
r.string(v),
-1)
} else {
out = v
return
}
}
out = node
default:
out = node
}
return
}

// objectMap returns a map for a resource object.
func (r *Injector) asMap(object any) (mp map[string]any) {
b, _ := json.Marshal(object)
mp = make(map[string]any)
_ = json.Unmarshal(b, &mp)
return
}

// objectMap returns a map for a resource object.
func (r *Injector) object(mp map[string]any, object any) (err error) {
b, _ := json.Marshal(mp)
err = json.Unmarshal(b, object)
return
}

// string returns a string representation of a field value.
func (r *Injector) string(object any) (s string) {
if object != nil {
s = fmt.Sprintf("%v", object)
}
return
}

// ResourceInjector inject resources into extension metadata.
// Example:
//
// metadata:
// provider:
// address: localhost:$(PORT)
// initConfig:
// - providerSpecificConfig:
// mavenInsecure: $(maven.insecure)
// mavenSettingsFile: $(maven.settings.path)
// name: java
// resources:
// - selector: identity:kind=maven
// fields:
// - key: maven.settings.path
// name: settings
// path: /shared/creds/maven/settings.xml
// - selector: setting:key=mvn.insecure.enabled
// fields:
// - key: maven.insecure
// name: value
type ResourceInjector struct {
Injector
}

// Inject resources into extension metadata.
func (r *ResourceInjector) Inject(md *Metadata) (err error) {
r.init()
err = r.build(md)
if err != nil {
return
}
p = &md.Provider
err = r.Injector.Inject(md)
return
}

// build builds resource dictionary.
func (r *ResourceInjector) build(md *Metadata) (err error) {
r.dict = make(map[string]any)
application, err := addon.Task.Application()
if err != nil {
return
Expand Down Expand Up @@ -320,6 +424,13 @@ func (r *ResourceInjector) addField(f *Field, v any) (err error) {
return
}
}
if _, found := r.dict[f.Key]; found {
err = &KeyConflictError{
Key: f.Key,
Value: v,
}
return
}
r.dict[f.Key] = v
return
}
Expand All @@ -341,67 +452,3 @@ func (r *ResourceInjector) write(path string, object any) (err error) {
_, err = f.Write([]byte(s))
return
}

// string returns a string representation of a field value.
func (r *ResourceInjector) string(object any) (s string) {
if object != nil {
s = fmt.Sprintf("%v", object)
}
return
}

// objectMap returns a map for a resource object.
func (r *ResourceInjector) asMap(object any) (mp map[string]any) {
b, _ := json.Marshal(object)
mp = make(map[string]any)
_ = json.Unmarshal(b, &mp)
return
}

// objectMap returns a map for a resource object.
func (r *ResourceInjector) object(mp map[string]any, object any) (err error) {
b, _ := json.Marshal(mp)
err = json.Unmarshal(b, object)
return
}

// inject replaces `dict` variables referenced in metadata.
func (r *ResourceInjector) inject(in any) (out any) {
switch node := in.(type) {
case map[string]any:
for k, v := range node {
node[k] = r.inject(v)
}
out = node
case []any:
var injected []any
for _, n := range node {
injected = append(
injected,
r.inject(n))
}
out = injected
case string:
for {
match := KeyRegex.FindStringSubmatch(node)
if len(match) < 3 {
break
}
v := r.dict[match[2]]
if len(node) > len(match[0]) {
node = strings.Replace(
node,
match[0],
r.string(v),
-1)
} else {
out = v
return
}
}
out = node
default:
out = node
}
return
}
9 changes: 7 additions & 2 deletions cmd/mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ func (r *Mode) AddOptions(options *command.Options, settings *Settings) (err err
settings.Mode(provider.SourceOnlyAnalysisMode)
options.Add("--no-dependency-rules")
}
return
}

// Location returns the location to be analyzed.
func (r *Mode) Location() (path string) {
if r.Binary {
settings.Location(r.path.binary)
path = r.path.binary
} else {
settings.Location(r.path.appDir)
path = r.path.appDir
}
return
}
Expand Down
Loading
Loading