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

feat: Allow to collect only selected CF apps metadata through yaml; Support query depth parameter and spaceguid as filters #1026

Merged
merged 8 commits into from
May 31, 2023

Conversation

Akash-Nayak
Copy link
Contributor

Currently move2kube collect fetches the metadata of all the apps that are running on Cloud Foundry instance. With this PR, we provide the user the flexibility to collect the metadata of a single app, or multiple apps, or all the apps that is/are running on Cloud Foundry.

To collect metadata of all the apps there is no change required while running move2kube collect.

$ move2kube collect -a cf

If the user wants to collect metadata of only specific application(s), then the user needs to create a simple yaml file and provide Move2Kube the path of the folder containing this yaml file while running the collect phase.

$ move2kube collect -a cf -s cfcollect
$ ls cfcollect
cf_target_apps.yaml

Here is a sample cf_target_apps.yaml file. Currently, it is optional to include guid, spaceguid, and organizationguid.

apiVersion: move2kube.konveyor.io/v1alpha1
kind: CfCollectApps
spec:
  applications:
    - name: cfnodejsapp
      guid: b48f67fb
      spaceguid: 74faba82
      organizationguid: 11387077-d81c
    - name: customers
      guid: 445b24f7
      spaceguid: 74faba82
      organizationguid: 11387077-d81c
    - name: inventory
      guid: 445b24f7
      spaceguid: 74faba82
      organizationguid: 11387077-d81c

Also, I have introduced depth constant that can be changed when required, to change the inline-relations-depth query parameter.

Signed-off-by: Akash Nayak akash19nayak@gmail.com

@github-actions
Copy link

Thanks for making a pull request! 😃
One of the maintainers will review and advise on the next steps.

@github-actions github-actions bot added the feat label Apr 12, 2023
@codecov
Copy link

codecov bot commented Apr 12, 2023

Codecov Report

Patch coverage: 2.89% and project coverage change: -1.25 ⚠️

Comparison is base (f3b48c3) 15.90% compared to head (b77a7a3) 14.66%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1026      +/-   ##
==========================================
- Coverage   15.90%   14.66%   -1.25%     
==========================================
  Files          81       86       +5     
  Lines        7405     8035     +630     
==========================================
  Hits         1178     1178              
- Misses       5922     6552     +630     
  Partials      305      305              
Impacted Files Coverage Δ
collector/cfappscollector.go 0.00% <0.00%> (ø)
common/utils.go 0.99% <0.00%> (-0.02%) ⬇️
transformer/cloudfoundrytransformer.go 0.00% <0.00%> (ø)
collector/cfutils.go 12.19% <35.71%> (ø)

... and 4 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Member

@ashokponkumar ashokponkumar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we allow for a hybrid approach:

  1. By default we get all info, but provide a way to specify depth and get the level of info we need.
  2. Also, we can allow users to filter based on space, org, etc..
  3. if users specify a list of apps we get the info for them, which could be in the similar format as the output of above, so that users can edit the above output and use it as input.


// CfCollectApp defines CfCollectApp information
type CfCollectApp struct {
Name string `yaml:"name"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we make name also optional. Say somebody wants to specify just the guids.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have made name optional and the user can specify just the guids.

@Akash-Nayak Akash-Nayak force-pushed the cf-collect-per-app branch 2 times, most recently from 85029df to 980c202 Compare May 25, 2023 04:50
@ashokponkumar
Copy link
Member

@seshapad can we review and merge this soon?

@Akash-Nayak
Copy link
Contributor Author

Akash-Nayak commented May 25, 2023

Can we allow for a hybrid approach:

1. By default we get all info, but provide a way to specify depth and get the level of info we need.

2. Also, we can allow users to filter based on space, org, etc..

3. if users specify a list of apps we get the info for them, which could be in the similar format as the output of above, so that users can edit the above output and use it as input.

This is the new yaml format that we support now with move2kube collect.

apiVersion: move2kube.konveyor.io/v1alpha1
kind: CfCollectApps
spec:
  filters:
    query_depth: "0"
    spaceguid: 74faba82-c6db-40af-9179-4f4c288efdde
  applications:
    - application:
        # guid: b3ed607d-17df-4b74-a4b3-ddb88d7d1965
        name: inventory
    - application:
        # name: gateway
        guid: 445b24f7-0877-4447-ab01-e96af6741a91
    - application:
        name: cfnodejsapp
        guid: b48f67fb-f046-4700-bd65-e43170d802aa

This would collect the metadata of inventory, 445b24f7-0877-4447-ab01-e96af6741a91 (gateway), cfnodejsapp with query depth "0".

apiVersion: move2kube.konveyor.io/v1alpha1
kind: CfCollectApps
spec:
  filters:
    spaceguid: 74faba82-c6db-40af-9179-4f4c288efdde

The above yaml can be used to fetch all apps from the given spaceguid.

apiVersion: move2kube.konveyor.io/v1alpha1
kind: CfCollectApps
spec:
  filters:
    query_depth: "1"

With the above yaml, move2kube collect will collect info of all the apps for the given query_depth.

(1 & 2) It allows to filter based on query depth and spaceguid.
(3) move2kube collect output can now be fed as input to move2kube collect by changing the kind from CfApps to CfCollectApps. The query_depth/spaceguid filters can be added to the cfapps.yaml and apps can be added/removed from the yaml to only fetch the metadata of specific apps.

@Akash-Nayak Akash-Nayak changed the title feat: Allow to collect only selected CF apps metadata; Use depth parameter as variable feat: Allow to collect only selected CF apps metadata through yaml; Support query depth parameter and spaceguid as filters May 25, 2023

// CfCollectFilters stores the spaceguid and querydepth to be used to filter while collecting metadata
type CfCollectFilters struct {
SpaceGuid string `yaml:"spaceguid,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is spaceguid used in filter? What is the difference between guid and spaceguid?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A guid is for an app, and there can be multiple apps deployed to a CF space, and a CF org can contain multiple spaces which will have different spaceguids. So, we also want to support filtering apps based on spaceguid to narrow down the search results.

https://docs.cloudfoundry.org/concepts/roles.html

A space provides users with access to a shared location for app development, deployment, and maintenance.
An org can contain multiple spaces. Every app, service, and route is scoped to a space. 

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not also do the same for orgs?
https://cli.cloudfoundry.org/en-US/v6/org.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not using orgs as filter because we cannot fetch the app data using q query parameter through /v2/organizations/, whereas /v2/spaces/:spaceguid supports fetching single/all app(s) data using query parameter (for ex: cf curl /v2/spaces/74faba82-c6db-40af-9179-4f4c288efdde/apps\?q=name%3Aorders).

func listAppsBySpaceGuid(client *cfclient.Client, spaceGuid string, queryDepth string, collectApps []App, numCfCollectApps int) (string, []App) {
query := setQueryDepth(queryDepth)
path := ""
if spaceGuid != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have shorter return from conditions? I.e. test for spaceGuid == ""?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure. Done.

func getAppByGuid(client *cfclient.Client, guid string, queryDepth string) (App, error) {
var appResource AppResource
query := setQueryDepth(queryDepth)
requestUrl := getRequestUrl("/v2/apps/"+guid, query) // /v2/apps/:appGuid fetches the app with given guid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you have used listAppPaths constant? If this is used only here, could you do away with constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the requestUrl to use the listAppsPaths constant. We are also using the listAppPaths in two other places too.

path = listAppsPath
}
appSpec := cfCollectApp.AppSpec
if appSpec.Guid != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use shorter if returns. Please see previous comment on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have update the code. First we check if app Guid is given, if the app data is collected successfully for the app Guid, then we continue the for loop for the next app. Otherwise, we check if app Name is given and try to collect the data of the app.

Also, I have made changes in the code to use shorter return in some other places too.

func setQueryDepth(queryDepth string) url.Values {
query := url.Values{}
if queryDepth != "" {
query.Set(inlineDepthRelations, queryDepth)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not have been better to have a single if block to assign depth (initialized to default from global depth value) query_depth if not empty?

The set function could have used this resultant depth value.

Do you see a need for a if-else block with two different invocations of set function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sure. Done.

…meter as variable

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
…th parameter as variable

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
…ry depth and space guid filters

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
…ort query depth and space guid filters

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
…nd support query depth and space guid filters

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
…adata and support query depth and space guid filters

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
@Akash-Nayak Akash-Nayak requested a review from seshapad May 30, 2023 07:16
return mergeAppResource(client, appResource), nil
}

func getAppsByNameOrGuid(client *cfclient.Client, path string, query url.Values, collectApps []App, appName string, appGuid string) []App {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the appName and appGuid used for? Is it only for error messages? Are these values already captured in the query? If so, why not remove them and print these values at the calling site of this function?

Copy link
Contributor Author

@Akash-Nayak Akash-Nayak May 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the function and also renamed it, and I have removed the appGuid which is not needed now in this function. The appName value is captured in the query but it is in form of a string like name:inventory (query- map[inline-relations-depth:[0] q:[name:inventory]]). The appName can be extracted using query.Get("q")[5:]. So, please suggest if I should pass the appName as parameter or should I use query.Get("q")[5:] inside the function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Akash-Nayak appName is only used for error logging right? if so, better not pass it as parameter. Also, dont extract it from the query either. You could always log the appName if getAppsByNameOrGuid() returns an error (append or wrap it with another error message with appName in it).

}

// stringifyMap stringifies the map values
func stringifyMap(inputMap map[string]interface{}) map[string]interface{} {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two comments here:

  1. This function is not just stringifying the map, isnt it? It is using the json format, right? If so, please name the function to reflect this function.
  2. Should the function be moved to common utils package? @ashokponkumar what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it a function that is reusable and not specific to this one flow, then move it to commons.utils. else it's fine to have it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two comments here:

1. This function is not just stringifying the map, isnt it? It is using the json format, right? If so, please name the function to reflect this function.
  1. I have renamed the function.
2. Should the function be moved to `common utils` package? @ashokponkumar what do you think?
  1. I have moved the function to common utils package.


// CfCollectFilters stores the spaceguid and querydepth to be used to filter while collecting metadata
type CfCollectFilters struct {
SpaceGuid string `yaml:"spaceguid,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not also do the same for orgs?
https://cli.cloudfoundry.org/en-US/v6/org.html

…pps metadata and support query depth and space guid filters

Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
Signed-off-by: Akash Nayak <akash19nayak@gmail.com>
@Akash-Nayak Akash-Nayak requested a review from seshapad May 31, 2023 03:21
@seshapad seshapad merged commit 5113d4c into konveyor:main May 31, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants