pyinfra icon indicating copy to clipboard operation
pyinfra copied to clipboard

Add attribute to ssh.command, that tells that return code of the command means 'no change'

Open ratermir opened this issue 1 year ago • 3 comments

In the "ssh.command", there may be situations when I can check, whether the system is already in the desired state or not.

  1. If the system is in the desired state, I won't perform any action, only inform the caller (i.e. pyload in this case) that I did nothing.
  2. In the other case, I can apply desired changes and return normal status code (zero for the success, non-zero for an error).

In case of 1., I can return special return code and simultaneously I can inform pyload, that this code means "no change".

Let we have (for example) such SSH command:

snap list helm || ( snap install helm && exit 0 ) && exit 200

This command returns status 200 if there is no change (i.e. helm is already installed) and normal status - i.e. 0 if helm was installed or non - zero except 200 (I must guarantee that there is no other 200 than my check) in case of error.

It would be perfect to be able to instruct pyload, that the status 200 means "no change". It could be done by additional attribute of the "ssh.command" command , it can be named for example "no_change_status", so in such case the "ssh.command" can look like this:

ssh.command( name="Install heml via snap", command="snap list helm || ( snap install helm && exit 0 ) && exit 200", no_change_status=200 )

ratermir avatar Feb 11 '24 18:02 ratermir

For your particular example, you can write a custom Fact that checks whether a snap package is installed and only run snap install if needed.

vojta001 avatar Feb 12 '24 17:02 vojta001

Thank you to pointing out, but - according to my knowledge - the result will not be the same. By my proposed solution, in the result list will be seen that there is some desired state but the opertion made no change (i.e. the system is already in the required state), in your proposal there will not to be a trace about the operation in such case. Am I correct? Btw for my particular examle, the best solution would be to implement "snap" operation :) I looked how the operations are programmed, but my Python knowledge are too few and I don't feel like to be able to do i well. So I will fill new request, so I will make new feature request.

ratermir avatar Feb 13 '24 08:02 ratermir

You are right, if the control flow is a part of the top-level file, the skipped operations won't show up. You can however wrap it in a simple operation just calling other operations.

the general idea is

import pyinfra

class SnapPackageInstalled(pyinfra.api.FactBase):
  pass # TODO check whether a package is installed

@pyinfra.api.operation
def install_snap_package(name):
  exists = pyinfra.host.get_fact(SnapPackageInstalled, name)
  if not exists:
    yield from pyinfra.operations.server.shell(f"snap install {name}") #TODO escape better

install_snap_package("helm")

vojta001 avatar Feb 13 '24 09:02 vojta001