Skip to content

Add infrahubctl object command #328

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

Merged
merged 1 commit into from
Apr 16, 2025
Merged

Add infrahubctl object command #328

merged 1 commit into from
Apr 16, 2025

Conversation

dgarros
Copy link
Contributor

@dgarros dgarros commented Mar 30, 2025

Add infrahub object load and infrahub object validate commands to load objects defined in Yaml format into Infrahub.

These commands are meant to be used in an idempotent way and as such they would work better for models with a Human Friendly ID (hfid) defined.

File format

All object files must start with the following format, all other format will be automatically ignored
Each file is intended for one specific top level kind, but one file can include multiple nested objects of any kind.
The kind of the top level object must be defined in spec/kind

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: <NamespaceName>
  data:
    - [,,,]

Below is a more detailed example with nested objects and with related objects referenced by their HFIDs

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TestingPerson
  data:
    - name: Alex Thompson
      height: 180
      animals:  # Relationship of cardinality many that will create/update nodes as needed
                      # The owner of the dog will be automatically inserted as long as their is a bidirectional relationship
                      # between TestingPerson and TestingDog
        kind: TestingDog         # The kind must be provided when the relationship is based on a generic
        data:
          - name: Max
            weight: 25
            breed: Golden Retriever
            color: "#FFD700"
      tags:           # The kind is optional here because there is only one option possible (not a generic)
        data:
          - name: Dog Lover    # New Tag will be created if it doesn't exist


    - name: Mike Johnson
      height: 175
      best_friends: # Relationship of cardinality many that referenced existing nodes based on their HFID
        - [Jane Smith, Max]
        - [Sarah Williams, Charlie]
      tags:
        - Veterinarian    # Existing Node referenced by its HFID in string format
        - [Breeder]       # Existing Node referenced by its HFID in list format

@github-actions github-actions bot added the type/documentation Improvements or additions to documentation label Mar 30, 2025
Copy link

codecov bot commented Mar 30, 2025

Codecov Report

Attention: Patch coverage is 73.92638% with 85 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
infrahub_sdk/spec/object.py 85.00% 18 Missing and 15 partials ⚠️
infrahub_sdk/ctl/object.py 14.28% 30 Missing ⚠️
infrahub_sdk/ctl/utils.py 23.07% 10 Missing ⚠️
infrahub_sdk/ctl/menu.py 16.66% 5 Missing ⚠️
infrahub_sdk/exceptions.py 73.33% 2 Missing and 2 partials ⚠️
infrahub_sdk/yaml.py 91.17% 2 Missing and 1 partial ⚠️
@@             Coverage Diff             @@
##           develop     #328      +/-   ##
===========================================
+ Coverage    73.05%   73.83%   +0.78%     
===========================================
  Files           92       92              
  Lines         8202     8486     +284     
  Branches      1582     1657      +75     
===========================================
+ Hits          5992     6266     +274     
+ Misses        1797     1787      -10     
- Partials       413      433      +20     
Flag Coverage Δ
integration-tests 25.25% <61.04%> (+2.95%) ⬆️
python-3.10 47.08% <39.57%> (-0.09%) ⬇️
python-3.11 47.08% <39.57%> (-0.11%) ⬇️
python-3.12 47.08% <39.57%> (-0.11%) ⬇️
python-3.13 47.08% <39.57%> (-0.11%) ⬇️
python-3.9 45.46% <39.38%> (-0.06%) ⬇️
python-filler-3.12 24.26% <1.53%> (-0.80%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
infrahub_sdk/ctl/cli_commands.py 68.59% <100.00%> (ø)
infrahub_sdk/schema/main.py 88.71% <100.00%> (+4.66%) ⬆️
infrahub_sdk/testing/schemas/animal.py 97.10% <100.00%> (+0.04%) ⬆️
infrahub_sdk/yaml.py 79.43% <91.17%> (+0.95%) ⬆️
infrahub_sdk/exceptions.py 80.90% <73.33%> (-1.39%) ⬇️
infrahub_sdk/ctl/menu.py 58.06% <16.66%> (-7.33%) ⬇️
infrahub_sdk/ctl/utils.py 66.66% <23.07%> (-4.77%) ⬇️
infrahub_sdk/ctl/object.py 37.50% <14.28%> (-27.89%) ⬇️
infrahub_sdk/spec/object.py 85.55% <85.00%> (+62.36%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dgarros dgarros requested review from a team and removed request for a team March 31, 2025 06:07
@dgarros dgarros marked this pull request as ready for review March 31, 2025 06:59
Copy link
Contributor

@ogenstad ogenstad left a comment

Choose a reason for hiding this comment

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

One thing around this that isn't part of this PR the code already looked like this before but if we look at the example from the PR description:

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TestingPerson
  data:
    - name: Mike Johnson
      height: 175
      best_friends: # Relationship of cardinality many that referenced existing nodes based on their HFID
        - [Jane Smith, Max]
        - [Sarah Williams, Charlie]

If TestPerson "Mike Johnson" existed going into this and had other best_friends relationships defined I think that those earlier relationships would be replaced with the ones defined here instead of just ensuring that the ones above also exist.

I'm not certain how I'd want it to work here, just highlighting to raise the issue. I think there are merits to both approaches, just not sure what users would expect to happen. Any thoughts?

@dgarros dgarros force-pushed the dga-20250330-object-cmd branch from d1ff928 to b74995b Compare April 1, 2025 16:29
for item in file.spec.data:
await file.spec.create_node(client=client, schema=schema, data=item, branch=branch)
for file in files:
await file.process(client=client, branch=branch)
Copy link
Contributor

Choose a reason for hiding this comment

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

same SOLID comment here as for validate_format a couple lines above
also, process seems more like a create method to me

@BaptisteGi
Copy link
Contributor

BaptisteGi commented Apr 7, 2025

(infrahub-sdk-py3.12) ➜  bug-hunting-space infrahubctl schema check oneoone.yml
 schema 'oneoone.yml' is Valid!
diff:
    added:
        RandomStuff:
            added: {}
            changed: {}
            removed: {}
    changed: {}
    removed: {}

(infrahub-sdk-py3.12) ➜  bug-hunting-space infrahubctl object validate objects/server-template.yml
(infrahub-sdk-py3.12) ➜  bug-hunting-space 
  • Validate command doesn't return anything when file is valid => Could be great to add a object 'server-template.yml' is Valid!
  • For schema the command is called check here it's validate => I believe we need to align one way or the other to avoid confusions
  • Maybe for an upcoming version, to have the number of node created/deleted returned by validate command if it makes sense

@BaptisteGi
Copy link
Contributor

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test_pve
      interfaces:
        kind: TemplateInterfacePhysical
        data:
          - template_name: eth0
            name: eth0
            enabled: false
            not: valid
            description: This interface comes from CLI
            l2_mode: tunnel
(infrahub-sdk-py3.12) ➜  bug-hunting-space infrahubctl object load objects/server-template.yml
[10:29:01] INFO     Node: Test_pve                                                                                                                                                                                                               object.py:217
Error: Object is not valid
Traceback (most recent call last):
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/ctl/utils.py", line 89, in async_wrapper
    return await func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/ctl/object.py", line 46, in load
    await file.process(client=client, branch=branch)
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/spec/object.py", line 312, in process
    await self.spec.process(client=client, branch=branch)
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/spec/object.py", line 88, in process
    await self.create_node(client=client, schema=schema, data=item, branch=branch)
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/spec/object.py", line 232, in create_node
    await cls.create_related_nodes(
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/spec/object.py", line 273, in create_related_nodes
    node = await cls.create_node(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/Baptiste/Workspaces/Infrahub/infrahub-sdk-python/infrahub_sdk/spec/object.py", line 154, in create_node
    raise ValidationError(identifier=schema.kind, message="Object is not valid")
infrahub_sdk.exceptions.ValidationError: Object is not valid

(infrahub-sdk-py3.12) ➜  bug-hunting-space 

When creating invalid files, it's hard to tell where the issue comes from just by looking at the command output.

If possible, it would be helpful to guide users by:

  • Indicating which instance is invalid (e.g. eth0 of kind TemplateInterfacePhysical isn't valid)
  • Explaining why it's invalid (e.g. key not doesn't exist on kind TemplateInterfacePhysical)

@BaptisteGi
Copy link
Contributor

In some situation we could have multiple kinds hidden behind a single relationship:

image

Defining the kind directly within the relationship might limit us in scenarios like this.

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test_pve
      interfaces:
        kind: TemplateInterfacePhysical
        data:
          - template_name: eth0
            name: eth0
            enabled: false
            not: valid
            description: This interface comes from CLI
            l2_mode: tunnel

I believe there's still a workaround by splitting interfaces into multiple files...

@dgarros
Copy link
Contributor Author

dgarros commented Apr 7, 2025

Defining the kind directly within the relationship might limit us in scenarios like this.

This one has been bothering me as well and this is one of the reason I haven't merged this PR yet

there are a few options possible but I'm not sure which one is the best

Option 1: each item in the list includes kind and data

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test_pve
      interfaces:
        - kind: TemplateInterfacePhysical
           data:
               template_name: eth0
               name: eth0
               enabled: false
        - kind: TemplateInterfaceLogical
           data:
               template_name: eth1
               name: eth1
               enabled: false

if the relationship doesn't use a generic we could have a simpler format too

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test_pve
      interfaces:
        - template_name: eth0
           name: eth0
           enabled: false
        - template_name: eth1
           name: eth1
           enabled: false

Option 2: same format as previously but with a list

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test_pve
      interfaces:
        - kind: TemplateInterfacePhysical
          data:
            - template_name: eth0
               name: eth0
               enabled: false
        - kind: TemplateInterfaceLogical
          data:
            - template_name: eth1
               name: eth1
               enabled: false

@BaptisteGi
Copy link
Contributor

Hmm, this one's tricky. I'd say option 1 is easier for users to understand. Option 2 is a bit more complex (a list within a list), but it's consistent with the rest of the file and could save users some time by not having to repeat the kind each time.

So I'd lean toward option 1, but I don't have any strong objections to option 2.

@dgarros dgarros force-pushed the dga-20250330-object-cmd branch from b74995b to 3e52900 Compare April 8, 2025 05:53
Copy link

cloudflare-workers-and-pages bot commented Apr 8, 2025

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: cd9ee51
Status: ✅  Deploy successful!
Preview URL: https://2617037e.infrahub-sdk-python.pages.dev
Branch Preview URL: https://dga-20250330-object-cmd.infrahub-sdk-python.pages.dev

View logs

@wvandeun
Copy link
Contributor

wvandeun commented Apr 9, 2025

Option 1 has my preference. It is more verbose, but it is very clear and explicit on what is going to happen.

I'm not certain how I'd want it to work here, just highlighting to raise the issue. I think there are merits to both approaches, just not sure what users would expect to happen. Any thoughts?

I think it should update the relationship with the related nodes that are declared in the file. If not, we will need to find a way for a user to declare that he wants to remove a related node from an object. AKA we will have to do something very similar to how we allow people to remove things in the schema files. Most of our users don't seem to like that behavior.

@github-actions github-actions bot added the group/ci Issue related to the CI pipeline label Apr 9, 2025
@BaptisteGi
Copy link
Contributor

I must be missing something because I can't make it work with the new syntax:

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: TemplateComputePhysicalServer
  data:
    - template_name: Test123
      interfaces:
        - kind: TemplateInterfacePhysical
          data:
            template_name: eth0
            name: eth0
            enabled: false
            l2_mode: tunnel
infrahub_sdk.exceptions.ValidationError: Object is not valid

I updated SDK to the latest version on this branch...

@dgarros dgarros force-pushed the dga-20250330-object-cmd branch 2 times, most recently from 434a7bd to feb5b1e Compare April 13, 2025 13:24
@dgarros
Copy link
Contributor Author

dgarros commented Apr 13, 2025

@BaptisteGi

  • The error messages for object validate have been updated to include more information
  • The error Object is not valid when using the new format has been fixed
  • object validate now display something when the files are valid

Regarding check vs validate

  • validate is meant to be used only to validate the format of the local file(s), we are already using it in a few places. There is even a infrahubctl validate command that is meant to centralize all validations tasks
  • check is used only for the schema and it's meant to show the diff calculated by the backend before updating a schema

having said that , I understand your point but not sure what is the best way proceed here

@BaptisteGi
Copy link
Contributor

When I try to add more than one component object I have an error message:

---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: ComputePhysicalServer
  data:
    - name: Server1234
      status: active
      location: rack00
      interfaces:
        - kind: InterfacePhysical
          data:
            name: eth0
            enabled: false
            l2_mode: access
        - kind: InterfacePhysical
          data:
            name: eth1
            enabled: false
            l2_mode: access
(infrahub-sdk-py3.12) ➜  bug-hunting-space infrahubctl object load objects/server.yml
[10:59:55] INFO     Node: Server1234                                                                                                                                                                                                                                                                          object.py:425
           INFO     Node: InterfacePhysical : 183623bb-e28a-9ea8-32b3-c51e3dd3f375                                                                                                                                                                                                                            object.py:425
['InterfacePhysicalUpsert'] InterfacePhysical - 183623bb-e28a-9ea8-32b3-c51e3dd3f375 cannot be added to relationship, must be of type: ['ComputePhysicalServer', 'DcimDevice', 'VirtualizationVirtualMachine'] at device

This works perfectly fine if I only have one row in the interfaces section:

(infrahub-sdk-py3.12) ➜  bug-hunting-space infrahubctl object load objects/server.yml         
[11:05:35] INFO     Node: Server1234                                                                                                                                                                                                                                                                          object.py:425
           INFO     Node: InterfacePhysical : 183623bb-e28a-9ea8-32b3-c51e3dd3f375                                                                                                                                                                                                                            object.py:425

@dgarros dgarros force-pushed the dga-20250330-object-cmd branch from 523d965 to ef91f25 Compare April 16, 2025 04:04
@dgarros dgarros force-pushed the dga-20250330-object-cmd branch from e05ef4f to cd9ee51 Compare April 16, 2025 04:35
@dgarros
Copy link
Contributor Author

dgarros commented Apr 16, 2025

Thanks @BaptisteGi, this one was also reported by the integration tests, it has been fixed

@dgarros dgarros merged commit 96013cf into develop Apr 16, 2025
18 checks passed
@dgarros dgarros deleted the dga-20250330-object-cmd branch April 16, 2025 06:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
group/ci Issue related to the CI pipeline type/documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants