2019-12-09 01:42:51 +00:00
use super ::{ Context , Module , RootModuleConfig } ;
use crate ::configs ::terraform ::TerraformConfig ;
2020-07-07 22:45:32 +00:00
use crate ::formatter ::StringFormatter ;
2019-12-09 01:42:51 +00:00
use crate ::utils ;
2020-07-07 22:45:32 +00:00
2021-04-29 21:22:20 +00:00
use crate ::formatter ::VersionFormatter ;
2019-12-09 01:42:51 +00:00
use std ::io ;
use std ::path ::PathBuf ;
/// Creates a module with the current Terraform version and workspace
pub fn module < ' a > ( context : & ' a Context ) -> Option < Module < ' a > > {
2021-02-17 17:57:40 +00:00
let mut module = context . new_module ( " terraform " ) ;
let config : TerraformConfig = TerraformConfig ::try_load ( module . config ) ;
2019-12-09 01:42:51 +00:00
let is_terraform_project = context
. try_begin_scan ( ) ?
2021-02-17 17:57:40 +00:00
. set_files ( & config . detect_files )
. set_folders ( & config . detect_folders )
. set_extensions ( & config . detect_extensions )
2019-12-09 01:42:51 +00:00
. is_match ( ) ;
if ! is_terraform_project {
return None ;
}
2020-07-07 22:45:32 +00:00
let parsed = StringFormatter ::new ( config . format ) . and_then ( | formatter | {
formatter
. map_meta ( | variable , _ | match variable {
" symbol " = > Some ( config . symbol ) ,
_ = > None ,
} )
. map_style ( | variable | match variable {
" style " = > Some ( Ok ( config . style ) ) ,
_ = > None ,
} )
. map ( | variable | match variable {
2021-04-29 21:22:20 +00:00
" version " = > {
let terraform_version = get_terraform_version (
& context . exec_cmd ( " terraform " , & [ " version " ] ) ? . stdout . as_str ( ) ,
) ? ;
VersionFormatter ::format_module_version (
module . get_name ( ) ,
& terraform_version ,
config . version_format ,
)
}
2020-07-07 22:45:32 +00:00
. map ( Ok ) ,
2020-08-07 19:13:12 +00:00
" workspace " = > get_terraform_workspace ( context ) . map ( Ok ) ,
2020-07-07 22:45:32 +00:00
_ = > None ,
} )
. parse ( None )
} ) ;
2019-12-09 01:42:51 +00:00
2020-07-07 22:45:32 +00:00
module . set_segments ( match parsed {
Ok ( segments ) = > segments ,
Err ( error ) = > {
log ::warn! ( " Error in module `terraform`: \n {} " , error ) ;
return None ;
}
} ) ;
2019-12-09 01:42:51 +00:00
Some ( module )
}
// Determines the currently selected workspace (see https://github.com/hashicorp/terraform/blob/master/command/meta.go for the original implementation)
2020-08-07 19:13:12 +00:00
fn get_terraform_workspace ( context : & Context ) -> Option < String > {
2019-12-09 01:42:51 +00:00
// Workspace can be explicitly overwritten by an env var
2020-08-07 19:13:12 +00:00
let workspace_override = context . get_env ( " TF_WORKSPACE " ) ;
if workspace_override . is_some ( ) {
return workspace_override ;
2019-12-09 01:42:51 +00:00
}
// Data directory containing current workspace can be overwritten by an env var
2020-08-07 19:13:12 +00:00
let datadir = match context . get_env ( " TF_DATA_DIR " ) {
Some ( s ) = > PathBuf ::from ( s ) ,
None = > context . current_dir . join ( " .terraform " ) ,
2019-12-09 01:42:51 +00:00
} ;
match utils ::read_file ( datadir . join ( " environment " ) ) {
Err ( ref e ) if e . kind ( ) = = io ::ErrorKind ::NotFound = > Some ( " default " . to_string ( ) ) ,
Ok ( s ) = > Some ( s ) ,
_ = > None ,
}
}
2021-04-29 21:22:20 +00:00
fn get_terraform_version ( version : & str ) -> Option < String > {
2019-12-09 01:42:51 +00:00
// `terraform version` output looks like this
// Terraform v0.12.14
// With potential extra output if it detects you are not running the latest version
Some (
version
. lines ( )
. next ( ) ?
. trim_start_matches ( " Terraform " )
. trim ( )
2021-04-29 21:22:20 +00:00
. trim_start_matches ( 'v' )
2019-12-09 01:42:51 +00:00
. to_owned ( )
+ " " ,
)
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-08-07 19:13:12 +00:00
use crate ::test ::ModuleRenderer ;
2020-07-09 00:24:38 +00:00
use ansi_term ::Color ;
use std ::fs ::{ self , File } ;
2020-08-07 19:13:12 +00:00
use std ::io ::{ self , Write } ;
2019-12-09 01:42:51 +00:00
#[ test ]
2021-04-29 21:22:20 +00:00
fn test_get_terraform_version_release ( ) {
2019-12-09 01:42:51 +00:00
let input = " Terraform v0.12.14 " ;
2021-04-29 21:22:20 +00:00
assert_eq! ( get_terraform_version ( input ) , Some ( " 0.12.14 " . to_string ( ) ) ) ;
2019-12-09 01:42:51 +00:00
}
#[ test ]
2021-04-29 21:22:20 +00:00
fn test_get_terraform_version_prerelease ( ) {
2019-12-09 01:42:51 +00:00
let input = " Terraform v0.12.14-rc1 " ;
assert_eq! (
2021-04-29 21:22:20 +00:00
get_terraform_version ( input ) ,
Some ( " 0.12.14-rc1 " . to_string ( ) )
2019-12-09 01:42:51 +00:00
) ;
}
#[ test ]
2021-04-29 21:22:20 +00:00
fn test_get_terraform_version_development ( ) {
2019-12-09 01:42:51 +00:00
let input = " Terraform v0.12.14-dev (cca89f74) " ;
assert_eq! (
2021-04-29 21:22:20 +00:00
get_terraform_version ( input ) ,
Some ( " 0.12.14-dev (cca89f74) " . to_string ( ) )
2019-12-09 01:42:51 +00:00
) ;
}
#[ test ]
2021-04-29 21:22:20 +00:00
fn test_get_terraform_version_multiline ( ) {
2019-12-09 01:42:51 +00:00
let input = " Terraform v0.12.13
Your version of Terraform is out of date ! The latest version
is 0.1 2.1 4. You can update by downloading from www . terraform . io / downloads . html
" ;
2021-04-29 21:22:20 +00:00
assert_eq! ( get_terraform_version ( input ) , Some ( " 0.12.13 " . to_string ( ) ) ) ;
2019-12-09 01:42:51 +00:00
}
2020-07-09 00:24:38 +00:00
#[ test ]
fn folder_with_dotterraform_with_version_no_environment ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
let tf_dir = dir . path ( ) . join ( " .terraform " ) ;
fs ::create_dir ( & tf_dir ) ? ;
2020-08-07 19:13:12 +00:00
let actual = ModuleRenderer ::new ( " terraform " )
. path ( dir . path ( ) )
. config ( toml ::toml! {
2020-07-09 00:24:38 +00:00
[ terraform ]
format = " via [$symbol$version$workspace]($style) "
2020-08-07 19:13:12 +00:00
} )
. collect ( ) ;
2020-07-09 00:24:38 +00:00
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 v0.12.14 default " )
) ) ;
assert_eq! ( expected , actual ) ;
2020-08-07 19:13:12 +00:00
dir . close ( )
2020-07-09 00:24:38 +00:00
}
#[ test ]
fn folder_with_dotterraform_with_version_with_environment ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
let tf_dir = dir . path ( ) . join ( " .terraform " ) ;
fs ::create_dir ( & tf_dir ) ? ;
let mut file = File ::create ( tf_dir . join ( " environment " ) ) ? ;
file . write_all ( b " development " ) ? ;
file . sync_all ( ) ? ;
2020-08-07 19:13:12 +00:00
let actual = ModuleRenderer ::new ( " terraform " )
. path ( dir . path ( ) )
. config ( toml ::toml! {
2020-07-09 00:24:38 +00:00
[ terraform ]
format = " via [$symbol$version$workspace]($style) "
2020-08-07 19:13:12 +00:00
} )
. collect ( ) ;
2020-07-09 00:24:38 +00:00
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 v0.12.14 development " )
) ) ;
assert_eq! ( expected , actual ) ;
2020-08-07 19:13:12 +00:00
dir . close ( )
}
#[ test ]
fn folder_without_dotterraform ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
let actual = ModuleRenderer ::new ( " terraform " ) . path ( dir . path ( ) ) . collect ( ) ;
let expected = None ;
assert_eq! ( expected , actual ) ;
dir . close ( )
}
#[ test ]
fn folder_with_tf_file ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
File ::create ( dir . path ( ) . join ( " main.tf " ) ) ? ;
let actual = ModuleRenderer ::new ( " terraform " ) . path ( dir . path ( ) ) . collect ( ) ;
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 default " )
) ) ;
assert_eq! ( expected , actual ) ;
dir . close ( )
}
#[ test ]
fn folder_with_workspace_override ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
File ::create ( dir . path ( ) . join ( " main.tf " ) ) ? ;
let actual = ModuleRenderer ::new ( " terraform " )
. path ( dir . path ( ) )
. env ( " TF_WORKSPACE " , " development " )
. collect ( ) ;
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 development " )
) ) ;
assert_eq! ( expected , actual ) ;
dir . close ( )
}
#[ test ]
fn folder_with_datadir_override ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
File ::create ( dir . path ( ) . join ( " main.tf " ) ) ? ;
let datadir = tempfile ::tempdir ( ) ? ;
let mut file = File ::create ( datadir . path ( ) . join ( " environment " ) ) ? ;
file . write_all ( b " development " ) ? ;
file . sync_all ( ) ? ;
let actual = ModuleRenderer ::new ( " terraform " )
. path ( dir . path ( ) )
. env ( " TF_DATA_DIR " , datadir . path ( ) . to_str ( ) . unwrap ( ) )
. collect ( ) ;
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 development " )
) ) ;
assert_eq! ( expected , actual ) ;
dir . close ( ) ? ;
datadir . close ( )
}
#[ test ]
fn folder_with_dotterraform_no_environment ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
let tf_dir = dir . path ( ) . join ( " .terraform " ) ;
fs ::create_dir ( & tf_dir ) ? ;
let actual = ModuleRenderer ::new ( " terraform " ) . path ( dir . path ( ) ) . collect ( ) ;
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 default " )
) ) ;
assert_eq! ( expected , actual ) ;
dir . close ( )
}
#[ test ]
fn folder_with_dotterraform_with_environment ( ) -> io ::Result < ( ) > {
let dir = tempfile ::tempdir ( ) ? ;
let tf_dir = dir . path ( ) . join ( " .terraform " ) ;
fs ::create_dir ( & tf_dir ) ? ;
let mut file = File ::create ( tf_dir . join ( " environment " ) ) ? ;
file . write_all ( b " development " ) ? ;
file . sync_all ( ) ? ;
let actual = ModuleRenderer ::new ( " terraform " ) . path ( dir . path ( ) ) . collect ( ) ;
let expected = Some ( format! (
" via {} " ,
Color ::Fixed ( 105 ) . bold ( ) . paint ( " 💠 development " )
) ) ;
assert_eq! ( expected , actual ) ;
dir . close ( )
2020-07-09 00:24:38 +00:00
}
2019-12-09 01:42:51 +00:00
}