-
Notifications
You must be signed in to change notification settings - Fork 4
✨ Implement addon. #2
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
Changes from all commits
591679d
910482e
ada8a63
df4f290
9ee369b
b180a5b
a2fc54b
4f68b7a
436bd36
9639d88
18df681
00d1820
c4b7826
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "errors" | ||
|
|
||
| "github.com/konveyor/tackle2-hub/api" | ||
| ) | ||
|
|
||
| // Action provides and addon action. | ||
| type Action interface { | ||
| Run(*Data) error | ||
| } | ||
|
|
||
| // NewAction returns and action based the data provided. | ||
| func NewAction(d *Data) (a Action, err error) { | ||
| switch d.Action { | ||
| case "fetch": | ||
| a = &Fetch{} | ||
| case "import": | ||
| a = &Import{} | ||
| case "generate": | ||
| a = &Generate{} | ||
| default: | ||
| err = errors.New("action not supported") | ||
| } | ||
| return | ||
| } | ||
|
|
||
| // BaseAction provides base functionality. | ||
| type BaseAction struct { | ||
| application api.Application | ||
| platform api.Platform | ||
| } | ||
|
|
||
| // setApplication fetches and sets `application` referenced by the task. | ||
| // The associated `platform` will be set when as appropriate. | ||
| func (r *BaseAction) setApplication() (err error) { | ||
| defer func() { | ||
| if err != nil { | ||
| if errors.Is(err, &api.NotFound{}) { | ||
| err = nil | ||
| } | ||
| } | ||
| }() | ||
| app, err := addon.Task.Application() | ||
| if err == nil { | ||
| r.application = *app | ||
| } else { | ||
| return | ||
| } | ||
| if app.Platform == nil { | ||
| return | ||
| } | ||
| p, err := addon.Platform.Get(app.Platform.ID) | ||
| if err == nil { | ||
| r.platform = *p | ||
| } | ||
| return | ||
| } | ||
|
|
||
| // setPlatform fetches and sets `platform` referenced by the task. | ||
| func (r *BaseAction) setPlatform() (err error) { | ||
| defer func() { | ||
| if err != nil { | ||
| if errors.Is(err, &api.NotFound{}) { | ||
| err = nil | ||
| } | ||
| } | ||
| }() | ||
| p, err := addon.Task.Platform() | ||
| if err == nil { | ||
| r.platform = *p | ||
| } | ||
| return | ||
| } | ||
|
Comment on lines
+61
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Apply same error handling simplification to setPlatform. The setPlatform method has the same complex error handling pattern that should be simplified for consistency. -func (r *BaseAction) setPlatform() (err error) {
- defer func() {
- if err != nil {
- if errors.Is(err, &api.NotFound{}) {
- err = nil
- }
- }
- }()
+func (r *BaseAction) setPlatform() (err error) {
p, err := addon.Task.Platform()
- if err == nil {
- r.platform = *p
+ if err != nil {
+ if errors.Is(err, &api.NotFound{}) {
+ return nil // Platform not found is acceptable
+ }
+ return
}
+ r.platform = *p
return
}
🤖 Prompt for AI Agents |
||
|
|
||
| // selectIdentity selects an identity based on kind. | ||
| func (r *BaseAction) selectIdentity(kind string) (ref *api.Ref, err error) { | ||
| idapi := addon.Application.Identity(r.application.ID) | ||
| id, found, err := idapi.Find(kind) | ||
| if err != nil { | ||
| return | ||
| } | ||
| if found { | ||
| ref = &api.Ref{ | ||
| ID: id.ID, | ||
| Name: id.Name, | ||
| } | ||
| } | ||
| return | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,163 @@ | ||||||||||||||||||
| package cloudfoundry | ||||||||||||||||||
|
|
||||||||||||||||||
| import ( | ||||||||||||||||||
| "path/filepath" | ||||||||||||||||||
|
|
||||||||||||||||||
| cf "github.com/cloudfoundry/go-cfclient/v3/config" | ||||||||||||||||||
| cfp "github.com/konveyor/asset-generation/pkg/providers/discoverers/cloud_foundry" | ||||||||||||||||||
| hub "github.com/konveyor/tackle2-hub/addon" | ||||||||||||||||||
| "github.com/konveyor/tackle2-hub/api" | ||||||||||||||||||
| "github.com/konveyor/tackle2-hub/api/jsd" | ||||||||||||||||||
| "github.com/konveyor/tackle2-hub/migration/json" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| var ( | ||||||||||||||||||
| addon = hub.Addon | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| // Provider is a cloudfoundry provider. | ||||||||||||||||||
| type Provider struct { | ||||||||||||||||||
| URL string | ||||||||||||||||||
| Identity *api.Identity | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Fetch the manifest for the application. | ||||||||||||||||||
| func (p *Provider) Fetch(application *api.Application) (m *api.Manifest, err error) { | ||||||||||||||||||
| if application.Coordinates == nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+26
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add nil check for application coordinates. The method returns silently when coordinates are nil. Consider returning an error to make the failure explicit. if application.Coordinates == nil {
+ err = fmt.Errorf("application coordinates not defined")
return
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| coordinates := Coordinates{} | ||||||||||||||||||
| err = application.Coordinates.As(&coordinates) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| ref := cfp.AppReference{ | ||||||||||||||||||
| SpaceName: coordinates.Space, | ||||||||||||||||||
| AppName: coordinates.Name, | ||||||||||||||||||
| } | ||||||||||||||||||
| client, err := p.client(ref.SpaceName) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| manifest, err := client.Discover(ref) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| m = &api.Manifest{} | ||||||||||||||||||
| m.Content = manifest.Content | ||||||||||||||||||
| m.Secret = manifest.Secret | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Find applications on the platform. | ||||||||||||||||||
| func (p *Provider) Find(filter api.Map) (found []api.Application, err error) { | ||||||||||||||||||
| f := Filter{} | ||||||||||||||||||
| err = filter.As(&f) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| client, err := p.client(f.Spaces...) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| spaces, err := client.ListApps() | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| schema, err := addon.Schema.Find("platform", "cloudfoundry", "coordinates") | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| for space, applications := range spaces { | ||||||||||||||||||
| if !f.MatchSpace(space) { | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
| for _, ref := range applications { | ||||||||||||||||||
| appRef, cast := ref.(cfp.AppReference) | ||||||||||||||||||
| if !cast { | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
| if !f.MatchName(appRef.AppName) { | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
| r := api.Application{} | ||||||||||||||||||
| r.Name = appRef.AppName | ||||||||||||||||||
| r.Coordinates = &jsd.Document{ | ||||||||||||||||||
| Schema: schema.Name, | ||||||||||||||||||
| Content: json.Map{ | ||||||||||||||||||
| "name": appRef.AppName, | ||||||||||||||||||
| "space": space, | ||||||||||||||||||
| }, | ||||||||||||||||||
| } | ||||||||||||||||||
| found = append(found, r) | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // client returns a cloudfoundry client. | ||||||||||||||||||
| func (p *Provider) client(spaces ...string) (client *cfp.CloudFoundryProvider, err error) { | ||||||||||||||||||
| options := []cf.Option{ | ||||||||||||||||||
| cf.SkipTLSValidation(), | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make TLS validation configurable. TLS validation is always skipped, which is a security risk. This should be configurable based on the environment. - options := []cf.Option{
- cf.SkipTLSValidation(),
- }
+ options := []cf.Option{}
+ // Only skip TLS validation if explicitly configured
+ if p.Identity != nil && p.Identity.Settings != nil {
+ if skipTLS, ok := p.Identity.Settings["skipTLS"].(bool); ok && skipTLS {
+ options = append(options, cf.SkipTLSValidation())
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| } | ||||||||||||||||||
| if p.Identity != nil { | ||||||||||||||||||
| options = append( | ||||||||||||||||||
| options, | ||||||||||||||||||
| cf.UserPassword( | ||||||||||||||||||
| p.Identity.User, | ||||||||||||||||||
| p.Identity.Password)) | ||||||||||||||||||
| } | ||||||||||||||||||
| cfConfig, err := cf.New(p.URL, options...) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| pConfig := &cfp.Config{ | ||||||||||||||||||
| CloudFoundryConfig: cfConfig, | ||||||||||||||||||
| SpaceNames: spaces, | ||||||||||||||||||
| } | ||||||||||||||||||
| client, err = cfp.New(pConfig, &addon.Log, true) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Coordinates - platform coordinates. | ||||||||||||||||||
| type Coordinates struct { | ||||||||||||||||||
| Space string `json:"space"` | ||||||||||||||||||
| Name string `json:"name"` | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Filter applications. | ||||||||||||||||||
| type Filter struct { | ||||||||||||||||||
| Spaces []string `json:"spaces"` | ||||||||||||||||||
| Names []string `json:"names"` | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // MatchSpace returns true when the application name matches the filter. | ||||||||||||||||||
| func (f *Filter) MatchSpace(name string) (match bool) { | ||||||||||||||||||
| for _, s := range f.Spaces { | ||||||||||||||||||
| match = s == name | ||||||||||||||||||
| if match { | ||||||||||||||||||
| break | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+137
to
+146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Handle empty filter to match all spaces. When the Spaces slice is empty, the method returns false instead of matching all spaces. This may not be the intended behavior. func (f *Filter) MatchSpace(name string) (match bool) {
+ if len(f.Spaces) == 0 {
+ return true // No filter means match all
+ }
for _, s := range f.Spaces {
match = s == name
if match {
break
}
}
return
}🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| // MatchName returns true when the name matches the filter. | ||||||||||||||||||
| // The name may be a glob. | ||||||||||||||||||
| func (f *Filter) MatchName(name string) (match bool) { | ||||||||||||||||||
| var err error | ||||||||||||||||||
| for _, pattern := range f.Names { | ||||||||||||||||||
| match, err = filepath.Match(pattern, name) | ||||||||||||||||||
| if err != nil { | ||||||||||||||||||
| addon.Log.Error(err, "Invalid glob pattern", "pattern", pattern) | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
| if match { | ||||||||||||||||||
| break | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| return | ||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| cf "github.com/konveyor/tackle2-addon-platform/cmd/cloudfoundry" | ||||||||||||||||||||||||||||||||||
| "github.com/konveyor/tackle2-hub/api" | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Fetch application manifest action. | ||||||||||||||||||||||||||||||||||
| type Fetch struct { | ||||||||||||||||||||||||||||||||||
| BaseAction | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Run executes the action. | ||||||||||||||||||||||||||||||||||
| func (a *Fetch) Run(d *Data) (err error) { | ||||||||||||||||||||||||||||||||||
| err = a.setApplication() | ||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| addon.Activity( | ||||||||||||||||||||||||||||||||||
| "[Fetch] Fetch manifest for application (id=%d): %s", | ||||||||||||||||||||||||||||||||||
| a.application.ID, | ||||||||||||||||||||||||||||||||||
| a.application.Name) | ||||||||||||||||||||||||||||||||||
| addon.Activity( | ||||||||||||||||||||||||||||||||||
| "[Fetch] Using platform (id=%d): %s", | ||||||||||||||||||||||||||||||||||
| a.platform.ID, | ||||||||||||||||||||||||||||||||||
| a.platform.Name) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing setPlatform() call causes potential runtime panic. The code logs platform information but never calls addon.Activity(
"[Fetch] Fetch manifest for application (id=%d): %s",
a.application.ID,
a.application.Name)
+err = a.setPlatform()
+if err != nil {
+ return
+}
addon.Activity(
"[Fetch] Using platform (id=%d): %s",
a.platform.ID,
a.platform.Name)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setApplication() will set the a.platform as needed with the platform associated with the application. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain@jortel Thank you for the clarification! Let me verify the implementation to understand the behavior better. 🐰 🏁 Script executed: Length of output: 77 🏁 Script executed: Length of output: 2629 🏁 Script executed: Length of output: 1746 @jortel You're absolutely right! I can see from the // setApplication fetches and sets `application` referenced by the task.
// The associated `platform` will be set when as appropriate.
func (r *BaseAction) setApplication() (err error) {
// ... error handling ...
app, err := addon.Task.Application()
if err == nil {
r.application = *app
} else {
return
}
if app.Platform == nil {
return
}
p, err := addon.Platform.Get(app.Platform.ID)
if err == nil {
r.platform = *p // Platform is set here!
}
return
}The method first fetches the application, then checks if the application has an associated platform, and if so, fetches and sets the platform. The comment even explicitly states "The associated Thank you for the correction! The original suggestion to add 🐰 🧬 Code Graph Analysis ResultsRelevant snippets for understanding
|
||||||||||||||||||||||||||||||||||
| var manifest *api.Manifest | ||||||||||||||||||||||||||||||||||
| switch a.platform.Kind { | ||||||||||||||||||||||||||||||||||
| case "cloudfoundry": | ||||||||||||||||||||||||||||||||||
| manifest, err = a.cloudfoundry() | ||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||
| err = errors.New("platform.kind not supported") | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| manifest.Application.ID = a.application.ID | ||||||||||||||||||||||||||||||||||
| err = addon.Manifest.Create(manifest) | ||||||||||||||||||||||||||||||||||
| if err == nil { | ||||||||||||||||||||||||||||||||||
| addon.Activity( | ||||||||||||||||||||||||||||||||||
| "Manifest (id=%d) created.", | ||||||||||||||||||||||||||||||||||
| manifest.ID) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // cloudfoundry implementation. | ||||||||||||||||||||||||||||||||||
| func (a *Fetch) cloudfoundry() (manifest *api.Manifest, err error) { | ||||||||||||||||||||||||||||||||||
| p := cf.Provider{ | ||||||||||||||||||||||||||||||||||
| URL: a.platform.URL, | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if a.platform.Identity.ID != 0 { | ||||||||||||||||||||||||||||||||||
| p.Identity, err = addon.Identity.Get(a.platform.Identity.ID) | ||||||||||||||||||||||||||||||||||
| if err == nil { | ||||||||||||||||||||||||||||||||||
| addon.Activity( | ||||||||||||||||||||||||||||||||||
| "[Fetch] Using credentials (id=%d): %s", | ||||||||||||||||||||||||||||||||||
| p.Identity.ID, | ||||||||||||||||||||||||||||||||||
| p.Identity.Name) | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| manifest, err = p.Fetch(&a.application) | ||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Simplify error handling and avoid masking legitimate errors.
The deferred error handling pattern is overly complex and may mask legitimate errors. The current implementation suppresses all
NotFounderrors, which might not always be appropriate.📝 Committable suggestion
🤖 Prompt for AI Agents