66import typer
77from decouple import config
88from pathlib import Path
9- from sh import jq
10- # from sh import terraform
9+ from subprocess import Popen , PIPE , STDOUT
1110from typing import List , Optional
1211from typing_extensions import Annotated
1312
1918tenant_id = config ('TENANT_ID' )
2019
2120tf = "terraform"
22- tf_dir = str (Path ("terraform" ).resolve ())
21+ tld = "terraform"
22+ tf_dir = str (Path (f"{ tld } /linux" ).resolve ())
2323ch_dir = f"-chdir={ tf_dir } "
2424
2525ans_dir = str (Path ("ansible" ).resolve ())
2929)
3030
3131
32- def run_cmd (args , verbose = True ):
32+ def run_cmd (args , shell = False ):
3333 """Run command, transfer stdout/stderr back into this process's stdout/stderr"""
3434 print ("Running command: %s" % " " .join (args ))
35- if verbose :
36- p = subprocess .Popen (
37- args ,
38- stdout = subprocess .PIPE ,
39- stderr = subprocess .STDOUT )
40- out = []
41- with p .stdout :
42- for line in iter (p .stdout .readline , b'' ):
43- out += [line .decode ('utf-8' ).rstrip ()]
44- p .wait ()
45- return "\n " .join ([str (i ) for i in out ])
46- else :
47- p = subprocess .Popen (
48- args ,
49- stdout = subprocess .DEVNULL ,
50- stderr = subprocess .STDOUT )
51- p .wait ()
52- return p .returncode
35+ p = Popen (
36+ args ,
37+ shell = shell ,
38+ stdin = PIPE ,
39+ stdout = PIPE ,
40+ stderr = STDOUT )
41+ out = []
42+ with p .stdout :
43+ for line in iter (p .stdout .readline , b'' ):
44+ out += [line .decode ('utf-8' ).rstrip ()]
45+ p .wait ()
46+ stdout = "\n " .join ([str (i ) for i in out ])
47+ print (stdout )
48+ return stdout
5349
5450
5551@app .command ("hello" )
5652def hello_world ():
5753 """Debugging command"""
5854 cmd = ["echo" , "hello world" ]
59- return print ( f"Command exited with code: { run_cmd (cmd , verbose = False ) } " )
55+ return run_cmd (cmd )
6056
6157
6258@app .command ()
@@ -79,7 +75,7 @@ def tfa():
7975 if not Path (f"{ tf_dir } /tfplan" ).exists ():
8076 print ("tfplan does not exist, running plan first" )
8177 tfp ()
82- cmd = [tf , ch_dir , "apply" , "tfplan" , "-auto-approve" ]
78+ cmd = [tf , ch_dir , "apply" , "tfplan" ]
8379 return run_cmd (cmd )
8480
8581
@@ -93,49 +89,67 @@ def tfs():
9389@app .command ()
9490def get_ip ():
9591 """Get Azure instance IP"""
96- # tf_state = terraform.show("-json", _cwd=tf_dir)
97- tf_state = tfs ()
98- az_ip = jq (
99- "-r" ,
100- '.values.root_module.resources[] | select(.address == "azurerm_linux_virtual_machine.my_terraform_vm").values.public_ip_address' ,
101- _in = tf_state ,
92+ tf_state = Popen (
93+ [tf , ch_dir , "show" , "-json" ],
94+ stdin = PIPE ,
95+ stdout = PIPE ,
10296 )
97+
98+ out = []
99+ with tf_state .stdout :
100+ for line in iter (tf_state .stdout .readline , b'' ):
101+ out += [line .decode ('utf-8' ).rstrip ()]
102+
103+ az_ip = subprocess .run (
104+ ["jq" , "-r" , '.values.root_module.resources[] | select(.address == "azurerm_linux_virtual_machine.my_terraform_vm").values.public_ip_address' ],
105+ input = "\n " .join ([str (i ) for i in out ]),
106+ capture_output = True ,
107+ text = True ,
108+ ).stdout .strip ()
109+
103110 print (az_ip )
104- return az_ip . strip ()
111+ return az_ip
105112
106113
107- # TODO: debug args + match statement in main
108114@app .command ()
109- def add_ip (* args : List [ str ] ):
115+ def add_ip (* args ):
110116 """Add Azure instance IP to Ansible inventory"""
111117 if args :
112118 az_ip = args [0 ]
113119 else :
114120 az_ip = get_ip ()
115121 hosts = Path (f"{ ans_dir } /hosts" ).read_text ()
116122 if az_ip not in hosts :
117- hosts = hosts . replace ( " [azure] " , f"[azure]\n { az_ip } " )
123+ hosts = re . sub ( r"\ [azure\]\n " , f"[azure]\n { az_ip } \n " , hosts )
118124 Path (f"{ ans_dir } /hosts" ).write_text (hosts )
119125 return print (Path (f"{ ans_dir } /hosts" ).read_text ())
120126 else :
121127 print (f"{ az_ip } already in hosts file" )
122128
123129
124130@app .command ()
125- def rm_ip (ip_address : str ):
131+ def rm_ip (* args ):
126132 """Remove Azure instance IP from Ansible inventory"""
127133 hosts = Path (f"{ ans_dir } /hosts" ).read_text ()
134+ if not args :
135+ ip_address = get_ip ()
136+ elif args :
137+ ip_address = args [0 ]
138+ else :
139+ print ("No IP address specified" )
140+ raise typer .Abort ()
141+
128142 if ip_address in hosts :
129- # delete line with list comprehension
130- hosts = "\n " .join ([i for i in hosts .split ("\n " ) if i != ip_address ])
143+ hosts = re .sub (rf"{ ip_address } \n" , "" , hosts )
131144 Path (f"{ ans_dir } /hosts" ).write_text (hosts )
132145 return print (Path (f"{ ans_dir } /hosts" ).read_text ())
133146 else :
134147 print (f"{ ip_address } not in hosts file" )
135148
136149
150+ # TODO: match multiple cases (case guard?)
137151@app .callback (invoke_without_command = True )
138- def main (command : Annotated [ Optional [str ], "command to run" ] = typer .Argument (None )):
152+ def main (command : Optional [str ] = typer .Argument (None )):
139153 match command :
140154 case "tfi" :
141155 tfi ()
@@ -150,8 +164,7 @@ def main(command: Annotated[Optional[str], "command to run"] = typer.Argument(No
150164 case "add-ip" :
151165 add_ip ()
152166 case "rm-ip" :
153- ip = typer .prompt ("IP address to remove" )
154- rm_ip (ip )
167+ rm_ip ()
155168 case "hello" :
156169 hello_world ()
157170 case _:
0 commit comments