linode / linode_api4-python

Official Python bindings for the Linode API
https://linode-api4.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
136 stars 74 forks source link

Failed to update cert on nodebalancer config #206

Closed dangarthwaite closed 3 years ago

dangarthwaite commented 3 years ago

The following code returned a success but did NOT update the cert.

#!/usr/bin/env python3
"""Updates the nodebalancer with a cert"""
import sys
from linode_api4 import (LinodeClient, NodeBalancerConfig)

API_KEY = "your_api_key_here"
NODE_BALANCER_ID = 999
NODE_BALANCER_CONFIG_ID = 11111
CERT_FILE = "/etc/letsencrypt/live/example.com/fullchain.pem",
KEY_FILE = "/etc/letsencrypt/live/example.com/privkey.pem"

def main():
    client = LinodeClient(API_KEY)
    config = NodeBalancerConfig(client, NODE_BALANCER_ID, NODE_BALANCER_CONFIG_ID)
    config.load_ssl_data(cert_file=CERT_FILE, key_file=KEY_FILE)
    saved =  config.save()

    if not saved:
        print("Failed to update NodeBalancer")
        sys.exit(1)
    print("NodeBalancer updated with new certificate")

if __name__ == "__main__":
    main()
Dorthu commented 3 years ago

This was a tricky one. The python library uses lazy-loading for API objects almost everywhere. It looks like the issue was that the python library hadn't actually loaded the NodeBalacnerConfig object from the API when load_ssl_data was called, but it did when save was called; the loading overrwrote the data populated by load_ssl_data.

This snippet will do what you expect:

#!/usr/bin/env python3
"""Updates the nodebalancer with a cert"""
import sys
from linode_api4 import (LinodeClient, NodeBalancerConfig)

API_KEY = "your_api_key_here"
NODE_BALANCER_ID = 999
NODE_BALANCER_CONFIG_ID = 11111
CERT_FILE = "/etc/letsencrypt/live/example.com/fullchain.pem",
KEY_FILE = "/etc/letsencrypt/live/example.com/privkey.pem"

def main():
    client = LinodeClient(API_KEY)
    # using `load` will cause the python library to immediately load the object, instead of lazy-loading it
    config = client.load(NodeBalancerConfig, NODE_BALANCER_CONFIG_ID, NODE_BALANCER_ID)
    config.load_ssl_data(cert_file=CERT_FILE, key_file=KEY_FILE)
    config.protocol = "https" # without setting the protocol, this won't save successfully
    saved =  config.save()

    if not saved:
        print("Failed to update NodeBalancer")
        sys.exit(1)
    print("NodeBalancer updated with new certificate")

if __name__ == "__main__":
    main()

I'm going to put up a patch that ensures the object is loaded before load_ssl_data executes as well, to prevent future instances of confusing behavior like this. Once that's shipped, your original code should work as expected too.

Dorthu commented 3 years ago

The fix for this was released in version 5.2.1