Search code examples
pythonfabric

Error message: No idea what 'doit(cf_key)' is! (fab, fabfile)


A client I have insists the command fab doit('my_ssh_key') refresh('dev') works on windows (updates the remote dev server).

So I tried the command in linux (mint), and got "bash: syntax error near unexpected token ('"`.

So I added quotes, fab "doit('my_ssh_key')" "refresh('dev')" and I got the error message in the title (same as when I escaped the parentheses with slashes).

How do I get this to work?

Here is the fabfile. fabfile.py:

###################################################################
#
# Usage:
# fab doit(path_to_ssh_key) refresh('dev|staging')
# fab doit(path_to_ssh_key) maintenanceon('dev|staging')
# fab doit(path_to_ssh_key) maintenanceoff('dev|staging')
#
# fab doit(path_to_ssh_key) productionrefresh
#
# Example: fab doit('c:\users\tom\.ssh\id_rsa.pem') maintenanceon('dev')
#
# If you use a passphrase then add --prompt-for-passphrase
####################################################################
from fabric import task, Connection


@task
def doit(ctx, keypath):
    ctx.user = 'django'
    ctx.host = '<servername>'
    ctx.connect_kwargs.key_filename = ''.format(keypath)


@task
def maintenanceon(ctx, server):
    conn = Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs)
    # create ln to maintenance file
    print('maintenance on')
    with conn.cd('/usr/share/nginx/html/'):
        conn.run('sudo ln -sf /home/django/sites/{0}.<servername>/src/project/templates/maintenance.html {0}-maintenance.html'.format(server))


@task
def maintenanceoff(ctx, server):
    conn = Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs)
    # create ln to maintenance file
    print('maintenance off')
    with conn.cd('/usr/share/nginx/html/'):
        conn.run('sudo unlink {}-maintenance.html'.format(server))


@task
def refresh(ctx, server):
    env_command = '. /home/django/sites/{0}.<servername>.com/{0}/bin/activate'.format(server)

    conn = Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs)

    # set to maintenance mode
    with conn.cd('/usr/share/nginx/html/'):
        print('maintenance on')
        conn.run('sudo ln -sf /home/django/sites/{0}.<servername>.com/src/project/templates/maintenance.html {0}-maintenance.html'.format(server))
    # refresh install
    with conn.cd('/home/django/sites/{}.<servername>.com/src/'.format(server)):
        print('git pull')
        conn.run('git pull')
    # check requirements
    with conn.cd('/home/django/sites/{}.<servername>.com/src/requirements/'.format(server)):
        print('pip-sync')
        conn.run(env_command + '&&' + 'pip-sync {}.txt'.format(server))
    # run migrations and collectstatic
    with conn.cd('/home/django/sites/{}.<servername>.com/src/project/'.format(server)):
        print('migrate')
        conn.run(env_command + '&&' + 'python manage.py migrate')
        print('collecstatic')
        conn.run(env_command + '&&' + 'python manage.py collectstatic --no-input')
    # restart server
    print('restart server')
    conn.sudo('systemctl restart {}.service'.format(server), pty=True)

    # maintenance mode off
    with conn.cd('/usr/share/nginx/html/'):
        print('maintenance off)')
        conn.run('sudo unlink {}-maintenance.html'.format(server))


@task
def productionrefresh(ctx):
    env_command = '. /home/django/sites/www.<servername>.com/www/bin/activate'

    conn = Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs)

    # set to maintenance mode
    with conn.cd('/usr/share/nginx/html/'):
        print('Set to maintenance mode')
        conn.run('sudo ln -sf /home/django/sites/www.<servername>.com/src/project/templates/maintenance.html prod-maintenance.html')
    # refresh install
    with conn.cd('/home/django/sites/www.<servername>.com/src/'):
        print('Git pull')
        conn.run('git pull')
    # check requirements
    with conn.cd('/home/django/sites/www.<servername>.com/src/requirements/'):
        print('pip-sync production.txt')
        conn.run(env_command + '&&' + 'pip-sync production.txt')
    # run migrations and collectstatic
    with conn.cd('/home/django/sites/www.<servername>.com/src/project/'):
        print('python manage.py migrate')
        conn.run(env_command + '&&' + 'python manage.py migrate')
        print('python manage.py collectstatic')
        conn.run(env_command + '&&' + 'python manage.py collectstatic --no-input')
    # restart server
    print('restart production service')
    conn.sudo('systemctl restart production.service', pty=True)
    # maintenance mode off
    with conn.cd('/usr/share/nginx/html/'):
        print('maintenance off')
        conn.run('sudo unlink prod-maintenance.html')


@task
def productioncollect(ctx):
    env_command = '. /home/django/sites/www.<servername>.com/www/bin/activate'

    conn = Connection(ctx.host, ctx.user, connect_kwargs=ctx.connect_kwargs)
    with conn.cd('/home/django/sites/www.<servername>.com/src/project/'):
        conn.run(env_command + '&&' + 'python manage.py collectstatic --no-input')

Update:

I followed RedKrieg's answer and got the following error:

maintenance on
Traceback (most recent call last):
  File "/home/michael/projects/campaignfinances/venv/bin/fab", line 8, in <module>
    sys.exit(program.run())
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/invoke/program.py", line 384, in run
    self.execute()
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/invoke/program.py", line 566, in execute
    executor.execute(*self.tasks)
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/invoke/executor.py", line 129, in execute
    result = call.task(*args, **call.kwargs)
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/invoke/tasks.py", line 127, in __call__
    result = self.body(*args, **kwargs)
  File "/home/michael/projects/campaignfinances/fabfile.py", line 51, in refresh
    conn.run('sudo ln -sf /home/django/sites/{0}.<servername>.com/src/project/templates/maintenance.html {0}-maintenance.html'.format(server))
  File "<decorator-gen-3>", line 2, in run
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/fabric/connection.py", line 29, in opens
    self.open()
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/fabric/connection.py", line 634, in open
    self.client.connect(**kwargs)
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/paramiko/client.py", line 340, in connect
    to_try = list(self._families_and_addresses(hostname, port))
  File "/home/michael/projects/campaignfinances/venv/lib/python3.6/site-packages/paramiko/client.py", line 204, in _families_and_addresses
    hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
  File "/usr/lib/python3.6/socket.py", line 745, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known

I got this error doing both fab doit --keypath='cf_key' refresh --server='dev' and fab doit 'cf_key' refresh 'dev'


Solution

  • Your problem has nothing to do with fabric. Your client is lying to you because that syntax is never (and can never be) used with the fab command line tool. To pass arguments to a fab task in fabric2 (which you are using in your python code here) you must use invoke style task arguments. For your examples, the following would be examples of the correct syntax:

    #positional arguments
    fab doit 'c:\users\tom\.ssh\id_rsa.pem' refresh 'dev|staging'
    
    #named arguments, equals is optional
    fab doit --keypath='c:\users\tom\.ssh\id_rsa.pem' maintenanceon --server='dev|staging'
    

    http://docs.pyinvoke.org/en/latest/concepts/invoking-tasks.html#task-command-line-arguments