#variant - April 2020

variantArchived
306 messages

Discuss variant (the “Universal CLI”) https://github.com/mumoshu/variant

Archive: https://archive.sweetops.com/variant/

Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@mumoshu on variant2, is this where you're currently headed? looks really nice.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
we're considering variant for the next phase of a project and wondering if we should invest in variant1 or variant 2
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
does variant2 support nesting of commands?
mumoshu
mumoshuover 6 years ago
I would definitely recommend variant 2 for your next project.

I think it's almost complete(I can't come up with anything to add anymore for almost a month) but please feel free to submit feature requests when you find something is still missing
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@mumoshu I didn’t see an example of how to nest commands
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
...like in variant 1
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
E.g. “mycli command subcommand”
mumoshu
mumoshuover 6 years ago
the last major thing I'd like to decide until releasing variant 2 is wether we add a variant 1 compatibility https://archive.sweetops.com/variant/2020/01/#a3343add-6c11-4f17-8ed0-5b7e3a98e2f0 cc/ @Alexander Tolstikov
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I am okay with no backwards compatibility at this point. It’s a radical departure.
mumoshu
mumoshuover 6 years ago
@Erik Osterman (Cloud Posse) You should use

job "bar" {
  import = "./path/to/dir"`
}

to nest all imported commands under "bar"
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Hrmm would about with in a fully self-contained script?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Without importing other files
mumoshu
mumoshuover 6 years ago
I don't generally recommend it as it makes a single variant 2 script too big to read but
mumoshu
mumoshuover 6 years ago
job "command subcommand" would define variant run command subcommand.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Ohhhhhhhhhh
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
Easy enough. I assumed it would be expressed with nesting somehow
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@johncblandii ☝️
johncblandii
johncblandiiover 6 years ago
@johncblandii has joined the channel
mumoshu
mumoshuover 6 years ago
I didn't like we had been forced to nest task to define nested commands in variant 1
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I agree that this will look more readable in the long run
mumoshu
mumoshuover 6 years ago
also - this syntax makes it more searchable. just grep for job "foo bar" for any subcommand. this wasn't possible with variant 1
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
True - that’s a plus
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@Igor Rodionov ☝️
Igor Rodionov
Igor Rodionovover 6 years ago
@Igor Rodionov has joined the channel
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@mumoshu this begs the question - have you considered HCL2 for Helmfile?
mumoshu
mumoshuover 6 years ago
Yeah! Just hesitated to create a feature request for that in the helmfile repo. (I think) there's so many people who use k8s but not tf and it seemed not appealing at all for non-tf users
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Ya could be controversial
mumoshu
mumoshuover 6 years ago
also i'm not yet sure on which project i'd build something for managing set of helm releases w/ hcl2
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Btw have you see the new kpt tool?
mumoshu
mumoshuover 6 years ago
yep
mumoshu
mumoshuover 6 years ago
i was a bit disappointed on it - i was expecting something similar to helm but for kustomize
mumoshu
mumoshuover 6 years ago(edited)
apparently it is not
mumoshu
mumoshuover 6 years ago
kpt live apply seems very useful tho
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
That was my interpretation of of it - only from reading faq - that it was “helm for kustomize”; surprised it’s not
mumoshu
mumoshuover 6 years ago(edited)
so a possible feature would be that kpy live apply is integrated into helmfile apply, which allows us to deploy helmfile managed apps without helm...
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Hrmmm maybe not time for helmfile? I mean why not build libs for variant2?
mumoshu
mumoshuover 6 years ago
yeah i think that's a valid question
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Could maybe reimplement a lot of this without some of the tech debt
mumoshu
mumoshuover 6 years ago
so perhaps i'd enhance variant 2 to add hcl2 syntax on top of something similar to helmfile
mumoshu
mumoshuover 6 years ago
and use kpt live apply with that? just brain storming
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Yep - but maybe not corner your self by making it generalized
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
E.g. this is the tool to replace Terragrunt for terraform
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Helmfile for helm
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
“Kptfile” for kpt
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
This is a way to declaratively express all of that in one tool
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
What we can do though is make certain things easier to express
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
With less boilerplate
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
So Helmfile like business logic expressed in a variant2 lib
mumoshu
mumoshuover 6 years ago
that makes sense
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
The next thing to consider is the common characteristic behind all succesful language alike this: a registry component (ideally just GitHub)
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Does the import functionality just use go getter?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
So we can import GitHub repos?
mumoshu
mumoshuover 6 years ago
its local - but you can use https://github.com/variantdev/mod integrated into variant2 for dependency management
mumoshu
mumoshuover 6 years ago(edited)
let's say you'd want a external variant2 lib hosted under <http://github.com/cloudposse/variant2libs|github.com/cloudposse/variant2libs>, you'll write a yourapp.variantmod like

module "yourapp" {
  dependency "github_release" "variant2libs" {
    source = "cloudposse/variant2libs"
    version = "> 1.0.0"
  }

   directory "lib/variant2libs" {
     source = "<http://github.com/cloudposse/variant2libs?ref=${dep.variant2libs.version}|github.com/cloudposse/variant2libs?ref=${dep.variant2libs.version}>"
   }

running variant mod build resolves the latest variant2libs release that mathces the semver constraint > 1.0.0 and downloads the whole tree for the release under lib/variant2libs
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Ok, that is pretty rad
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Wow
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
You have like generalized terraform. This is magical.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Does it have to be in a separate file like that?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
can we just stick it in the main variant file?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Aha so for right now, I need to let go the drive to have it self contained. I mean, it makes sense as you have it, and we obviously break out out our terraform code. Will wait to use it in practice before making and changes.
johncblandii
johncblandiiover 6 years ago
this is absolutely lovely
johncblandii
johncblandiiover 6 years ago
going to dig into variant much more, @mumoshu
loren
lorenover 6 years ago
The dependency management is intriguing... One of the problems we always have is managing versions of tools used in a project pipeline... We pin anywhere we can use dependabot to automate the ref update through a pr, but there are a lot of release apis/targets and it just doesn't work for everything. Any thought to a service or tool that would work with the lockfile and help open a pr for new versions?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@mumoshu shouldn't positional parameters in variant2 should up in help? I see the flags, but not the parameters.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Error: accepts between 1 and 1 arg(s), received 0
Usage:
  mycli terraform init [flags]

Flags:
      --cachedir string   Module cache folder
  -h, --help              help for init

Global Flags:
      --env string   Environment to operate on
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
job "terraform init" {
  parameter "project" {
    type = string
    description = "Project to provision"
  }
...
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Also, it appears it's not possible to use parameters in defaults for options?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
  parameter "project" {
    type = string
    description = "Project to provision"
  }

  option "cachedir" {
    type = string
    default = try(".modules/${param.project}")
    description = "Module cache folder"
  }
mumoshu
mumoshuover 6 years ago
Yes it's not possible. I couldn't find out how this should work:

job "x" {
  parameter "a" {
    type = string
    default = "foo"
  }
  
  parameter "b" {
    type = string
  }
}

what should ./mycmd x bar do then? is bar intented for specifying a, or b?
mumoshu
mumoshuover 6 years ago
I believe in many cases it would be better to use option for anything that can be defaulted.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
I discovered variables too - so that's good.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
is there something like locals in terraform? (a more terse way to express a lot of variables)
mumoshu
mumoshuover 6 years ago
what would you use locals for?
mumoshu
mumoshuover 6 years ago
i mean why variables can't be used instead?
mumoshu
mumoshuover 6 years ago

a more terse way to express a lot of variables

ah so you want it just for a short-hand syntax for a lot of variables?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
locals {
   moduledir = "${opt.cachedir}"
   id = "${opt.namespace}-${opt.stage}-${opt.name}"
....
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
more terse.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
than
variable "moduledir" {
   value = "${opt.cachedir}"
}

variable "id" {
  value = "${opt.namespace}-${opt.stage}-${opt.name}"
}
mumoshu
mumoshuover 6 years ago
seems true!
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
(not a deal breaker, just a nice to have)
mumoshu
mumoshuover 6 years ago
i just prefer a TYPE RESOURCE { ATTRS } syntax
mumoshu
mumoshuover 6 years ago
is locals widely used in terraform?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
oh ya! all over the place.
mumoshu
mumoshuover 6 years ago
oh really. then we should add it to variant2 as well
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
(because in terraform variable default values cannot have interpolations)
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
so it's a bit unfair to compare! haha
mumoshu
mumoshuover 6 years ago
ah good point
mumoshu
mumoshuover 6 years ago
perhaps we can deprecate variant2 variables in favor of locals?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I'm working on passing a list to another job. I get the following error:
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

Error: handler for type tuple not implemneted yet
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Note, no line numbers (like variant2 usually outputs)
mumoshu
mumoshuover 6 years ago
try list(["foo", "bar"])
mumoshu
mumoshuover 6 years ago
in hcl2 tuples and lists are different.

can you use either of them in terraform?(then perhaps terraform has an automatic type conversion between list <-> tuple?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
so I think there's a bug maybe? let me show you....
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
job "shell" {
  description = "Run a command in a shell"
  parameter "commands" {
    description = "List of commands to execute"
    type = list(string)
  }

  exec {
    command = "bash"
    args = ["-c", join("\n", param.commands)]
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
so I have defined it as list(string) not a tuple
mumoshu
mumoshuover 6 years ago
i didn't consider it as a bug
mumoshu
mumoshuover 6 years ago
in hcl2 ["-c", join("\n", param.commands)] is a tuple
mumoshu
mumoshuover 6 years ago
so you need to convert it to a list: list(["-c", join("\n", param.commands)])
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Hrmm...
erik
erikover 6 years ago
mumoshu
mumoshuover 6 years ago
ah well, give me a minute
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
no list in the example (e.g. list(["-e", opt.script]) )
mumoshu
mumoshuover 6 years ago
i think i misread your example. yeah exec.args can take either types
mumoshu
mumoshuover 6 years ago
well, are you trying to call the job shell from another job, or from the command-line?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
But I don't like HEREDOC syntax b/c the closing demark needs to be at character zero on a new line
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
so I thought I'd just convert it to use a list of commands instead
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
So your example is:
job "shell" {
  parameter "script" {
    type = string
  }

  parameter "path" {
    type = string
  }

  exec {
    command = "bash"
    args = ["-c", param.script]
    env = {
      PATH = param.path
    }
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I thought I could just do:
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
job "shell" {
  parameter "commands" {
    type = list(string)
  }

  parameter "path" {
    type = string
  }

  exec {
    command = "bash"
    args = ["-c", join("\n", param.commands)]
    env = {
      PATH = param.path
    }
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
And call it like this:
job "app deploy" {
  option "path" {
    type = string
    default = ".:${abspath("${context.sourcedir}/mocks/kubectl")}:/bin:/usr/bin"
  }
  run "shell" {
    commands = [
    "kubectl -n ${opt.namespace} apply -f ${context.sourcedir}/manifests/"
    ]
    path = opt.path
  }

  assert "path" {
    condition = opt.path != ""
  }
}
mumoshu
mumoshuover 6 years ago
yeah but variant2 doesn't support passing list(string) from command-line args so i was just curious how you tried to call it
mumoshu
mumoshuover 6 years ago
ah okay
mumoshu
mumoshuover 6 years ago
try

commands = list([
    "kubectl -n ${opt.namespace} apply -f ${context.sourcedir}/manifests/"
    ])

mumoshu
mumoshuover 6 years ago
i think we don't have a type conversion from tuple -> list there today.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Ok, so I changed it to this
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
...same error
erik
erikover 6 years ago
mumoshu
mumoshuover 6 years ago
ok i reproduced it on my machine, too
mumoshu
mumoshuover 6 years ago
hmm
mumoshu
mumoshuover 6 years ago
ahhhh
mumoshu
mumoshuover 6 years ago(edited)
please try:

commands = list(
    "kubectl", "-n", opt.namespace, "apply", "-f", "${context.sourcedir}/manifests/"
    )
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I see some test for chdir, but no example
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I noticed that exec is not valid in step "..." { ... } blocks
mumoshu
mumoshuover 6 years ago
there's no chdir in variant2 dsl. how would you use it?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
so not all terraform commands support a directory argument
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
e.g. terraform output
mumoshu
mumoshuover 6 years ago
yes, exec isn't available under step. use run instead to delegate to a job
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
of course, I can wrap this in a script block, but was hoping not to.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
not a blocker, just a nice to have
mumoshu
mumoshuover 6 years ago(edited)
the general recommendation is to wrap any shell script within exec called from within a job, so that your variant command can be easily tested
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I saw you had a nice example of writing tests for variants
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
haven't looked closer yet though at that.
mumoshu
mumoshuover 6 years ago
so you'd better create a terraform job that cd before executing terraform
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
if one job cd's into a directory, will the next job be run there?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
(doubt that)
mumoshu
mumoshuover 6 years ago(edited)
job `terraform` {
  paramter "workdir" {
    type = string
  }

  exec {
    command = "bash"
    args = ["-c", "cd ${param.workdir}; terraform ...."]
  }
}
mumoshu
mumoshuover 6 years ago

if one job cd's into a directory, will the next job be run there?

no. and i believe it's a great thing. previous jobs shouldn't affect later jobs whereas possible.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
bug: if type = "string" (syntax error) variant exits non-zero, but prints no error messages. obviously, this was my user error.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
is it deliberate that global parameters and options do not get passed by run automatically? I need to explicitly pass them everywhere.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@mumoshu is there something like need but for jobs rather than steps?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
In a Makefile it's very easy to have a dynamic list of target dependencies. I want to do the same with job dependencies.

e.g.
# static deps (like steps in variant)
target: dep1 dep2 dep3

could be expressed
# dynamically compute deps
DEPS = $(shell echo dep1 dep2 dep3) 
target: $(DEPS)

How to do this is variant2?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
E.g. define a job for "all" that runs a list of jobs (dynamically discovered from the yaml configuration file)
mumoshu
mumoshuover 6 years ago
@Erik Osterman (Cloud Posse) variant2 equivalent of that would be concurrent steps:

job "dep1" {
}

job "dep2" {
}

job "terraform init all" {
  concurrency = 2

  step "run-dep1" {
    run "dep1" {
    ...
    }
  }

  step "run-dep2" {
    run "dep2" {
    ...
    }
  }

  step "do something with dep1 and dep2" {
    needs = ["run-dep1", "run-dep2"]
    run "something" {
    }
  }
}
mumoshu
mumoshuover 6 years ago
would you prefer a shorthand syntax for this?
mumoshu
mumoshuover 6 years ago
which has indeed limited usage but easier to write
mumoshu
mumoshuover 6 years ago(edited)
job "terraform init all" {
  needs = ["dep1", "dep2"]
}

hmm but how should we choose which parameters/options to be passed to dep1 and dep2 then?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
So what I was trying to do was create a project configuration in yaml
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
then use variant to operate on that configuration
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
then create an all job (e.g. mycli terraform init all) that would run terraform init $project on each of the projects in the config in order
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
with the step notation, I have to know ahead of time each project
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
but i was hoping to create my own "schema" if you will, and then using the same job to operate on each one in an unattended fashion using an all job
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I have it working now being explicit
mumoshu
mumoshuover 6 years ago
probably an ideal and better shorthand syntax for your specific example would be

job "terraform init all" {
   need "dep1" {
     param1 = "paramvalue"
     opt1 = "optvalue"
  }
  need "dep2" {
    param1 = "paramvalue"}
  }

   run "foo" {
     ...
   }
}

which will be translated to todays

job "terraform init all" {
  concurrency = 2
   step "run-dep1" {
     run "dep1" {
       param1 = "paramvalue"
       opt1 = "optvalue"
     }
  }
  step "run-dep2" {
    run "dep2" {
      param1 = "paramvalue"}
    }
  }
  step "run-foo" {
    needs = ["run-dep1", "run-dep2"]
    run "foo" {
      ...
    }
  }
}

mumoshu
mumoshuover 6 years ago

with the step notation, I have to know ahead of time each project

ah gotcha.
mumoshu
mumoshuover 6 years ago
this is what's missing in the variant2 today
mumoshu
mumoshuover 6 years ago(edited)
so we don't have a kind of for expression in variant2 dsl
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
yea, and I am not crazy about that convention for terraform.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
i think it was a necessary evil, but maybe for variant there's another syntax.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Maybe it doesn't need to be an iterator.
mumoshu
mumoshuover 6 years ago
i hope so.. but have no concrete idea yet
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Let me write up how I thought it could work maybe...
mumoshu
mumoshuover 6 years ago(edited)
how about adding items to the step syntax, so that it will "expanded" into a multiple concurrent steps?

variable "projects" {
  value = [
    map("dir", "web"),
    map("dir", "infra"),
  }
}

step "all-deps" {
  items = var.projects
  run "terraform init" {
    dir = item.dir
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
job "terraform init" {
  parameter "project" {
    ...
  }
  option "extra_args" {
    ... 
  }
  exec {
    ...
    args = opt.extra_args
  }
}

job "terraform init all" {
  # note this list of "depends on" can be calculated dynamically
  depends_on = ["terrraform init eks", "terraform init efs"]
  extra_args = ["-refresh=true"]
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
☝️ and globals would be passed automatically 😃
mumoshu
mumoshuover 6 years ago
the last space-separated item in each depends_on entry gets turned into the project parameter value?
mumoshu
mumoshuover 6 years ago
how about options?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
sec
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Updated example.
mumoshu
mumoshuover 6 years ago
thanks!
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
The key is that when you depends_on , you can only pass all the same parameters or options
mumoshu
mumoshuover 6 years ago
okay i see how it works
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
depends_on could maybe be steps and it expands to a set of inline steps
mumoshu
mumoshuover 6 years ago
how about this then?

job "terraform init all" {
   depends_on "terrraform init" {
     project = "eks"
  }
  depends_on  "terrraform init" {
     project = "efs"
  }

Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Right, so that's what it should expand to.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
The thing is eks and efs come from the config.
mumoshu
mumoshuover 6 years ago
ah no, i mean this is the short-hand syntax to be expanded.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
For reference, here's a sample config I'm working with
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
projects:
  eks:
    module: "git::<https://github.com/cloudposse/terraform-root-modules.git//aws/eks?ref=tags/0.122.0>"
    min_nodes: 3
    max_nodes: 10
  efs:
    module: "git::<https://github.com/cloudposse/terraform-root-modules.git//aws/efs?ref=tags/0.122.0>"
mumoshu
mumoshuover 6 years ago
what i worry about is this requires me to write a command parser that should behave equivalent to shells 🙂

depends_on = ["terrraform init eks", "terraform init efs"]

extra_args = ["-refresh=true"]

mumoshu
mumoshuover 6 years ago
hopefully i could avoid it. that's the same reason why i've added exec { dir = "..." } yesterday
mumoshu
mumoshuover 6 years ago
but we definitely need a way to dynamically generate dependent job runs
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)

write a command parser that should behave equivalent to shells

So, it's not unheard of in make to have a target call make ; not crazy about it, but I guess that's always a possibility to have variant job call $0
mumoshu
mumoshuover 6 years ago
yeah true. but i won't add a syntax sugar to just call variant run <whatever> from within variant
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
😃
mumoshu
mumoshuover 6 years ago
so a few possible options in my mind now:

job "terraform init all" {
  exec {
    # exec will be expanded to multiple concurrent execs by replacing `item` in `args` to each item in this tuple:
    items = ["efs", "eks"]
    cmd = "variant"
    args = ["run", "terraform", "init", item, "--refresh=true"]
  }
}

mumoshu
mumoshuover 6 years ago(edited)
job "terraform init all" {
  need {
    # this step will be expanded to multiple concurrent steps by replacing `item` in the run body to each item in this tuple:
    items = ["efs", "eks"]

    run "terraform init" {
      project = item
      refresh = true
    }
  }
}
mumoshu
mumoshuover 6 years ago
job "terraform init all" {
  depends_on {
    # this step will be expanded to multiple concurrent steps by replacing `item` in the run body to each item in this tuple:
    items = ["efs", "eks"]

    run "terraform init" {
      project = item
      refresh = true
    }
  }
}

mumoshu
mumoshuover 6 years ago
@Erik Osterman (Cloud Posse) depends_on under job is available since v0.20.0 🙂
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
whoot! I got it working now with depends_on and dynamically getting the list of items by calling keys on my configuration.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
@mumoshu will keys always be returned in the same order as in the configuration file?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
job "terraform init all" {
  description = "Init all projects"
  depends_on "terraform init" {
    items = keys(conf.terraform.projects)
    args = {
      project = item
      env = opt.env
    }
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
😃
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

The keys are returned in lexicographical order, ensuring that the result will be identical as long as the keys in the map don't change.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

I’m not sure if integrations with external services (e.g. Slack) should be inside variant core

I get what you're saying - but this is so rad. In the end, we all keep saying "I want a to do chatops, just never get around to it." Well, no more excuses. 😃
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

a lot of half-baked integrations inside the core (Docker runner, Github actions, etc), which are not maintained for the obvious reasons

that's true...
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
My guess is this is a peak in to @mumoshu personal laboratory. What I love about it is he doesn't hold back, inhibited by figuring out where to put something.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

as v1 is not supported anymore
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
@mumoshu is this true? @Igor Rodionov has a massive variant1 script he's working on right now. It would be really hard to convert it to variant2.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
understand if no new feature requests in v1. but maybe bugfixes, if they come up? 😃 (none right now)
johncblandii
johncblandiiover 6 years ago(edited)
how can i properly type env to allow it to be optional here:

job "shell" {
  description = "Run a command in a shell"
  private = true

  option "dir" {
    default = ""
    description = "Directory to run the command"
    type = string
  }

  option "env" {
    default = {}
    description = "Directory to run the command"
    type = map(string)
  }

  parameter "commands" {
    description = "List of commands to execute"
    type = list(string)
  }

  exec {
    dir = opt.dir
    env = opt.env
    command = "bash"
    args = ["-c", join("\n", param.commands)]
  }
}

for context, this is a simple wrapper around shell commands so we can simply do things like this (note: this uses a similar wrapper used to set a dir specific to tf runs):

job "tf clean" {
  run "tfshell" {
    project  = opt.project
    tenant   = opt.tenant
    commands = ["rm -rf .terraform *.planfile"]
  }
}
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Just add a default value for it
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
It’s a map I believe
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Vs parameter is a list
johncblandii
johncblandiiover 6 years ago
I tried a default. I'll post the errors when I get back to things.
johncblandii
johncblandiiover 6 years ago

Error: handler for type object not implemneted yet

@mumoshu ☝️🏽 happens with 👇️🏽 in 0.21.1

job "shell" {
  description = "Run a command in a shell"
  private = true

  option "dir" {
    default = ""
    description = "Directory to run the command"
    type = string
  }

  option "env" {
    default = {}
    description = "Directory to run the command"
    type = map(string)
  }

  parameter "commands" {
    description = "List of commands to execute"
    type = list(string)
  }

  exec {
    dir = opt.dir
    env = opt.env
    command = "bash"
    args = ["-c", join("\n", param.commands)]
  }
}
johncblandii
johncblandiiover 6 years ago
quick spelling fix in the error: https://github.com/mumoshu/variant2/pull/14
johncblandii
johncblandiiover 6 years ago
@mumoshu any thoughts on the above exception?
mumoshu
mumoshuover 6 years ago
@johncblandii hey! thx - im trying to reproduce it and fixing a few bugs along the way. you can expect me to cut a new release soon :)
johncblandii
johncblandiiover 6 years ago
💥
johncblandii
johncblandiiover 6 years ago
i’m around for a bit so ping whenever
mumoshu
mumoshuover 6 years ago
maybe you can reproduce it only when you cann the shell job from another job, right? i got different issues when tried to run from variant run shell whatever
johncblandii
johncblandiiover 6 years ago
hrmm…i didn’t try it from run
johncblandii
johncblandiiover 6 years ago
btw, VARIANT_TRACE fails for short options. have you seen that?
mumoshu
mumoshuover 6 years ago
on my machine variant run shell 'echo foo' 'echo bar' is working fine
johncblandii
johncblandiiover 6 years ago
it is the map part that’s the problem
johncblandii
johncblandiiover 6 years ago
1 sec
johncblandii
johncblandiiover 6 years ago
here is a run from a job.

  run "shell" {
    commands = param.commands
    dir      = "../projects/helmfiles/${opt.project}"
    project  = opt.project
    tenant   = opt.tenant

    env = {
      AWS_PROFILE = "nbo-${opt.tenant}-helm"
      TENANT      = opt.tenant
      PROJECT     = opt.project
      KUBECONFIG  = "~/.kube/kubecfg.${opt.tenant}-helm"
    }
  }
johncblandii
johncblandiiover 6 years ago
here’s an interesting one; details to follow:

in ./helmfile.yaml: error during ../environments.yaml.part.0 parsing: template: stringTemplate:22:9: executing "stringTemplate" at <exec "yq" (list "read" (printf "../../../tenants/%v.yaml" (env "TENANT")) (printf "projects.helmfile.%v.values" (env "PROJECT")))>: error calling exec: exec: "yq": executable file not found in $PATH

COMMAND:
  yq read, ../../../tenants/acme.yaml, projects.helmfile.reloader.values

ERROR:
  exec: "yq": executable file not found in $PATH
Error: command "helmfile --environment tenant diff" in "../projects/helmfiles/reloader": exit status 1
johncblandii
johncblandiiover 6 years ago
got a sigsegv when using an empty list args = []

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1f9bafe]

goroutine 1 [running]:
<http://github.com/mumoshu/variant2/pkg/app.ctyToGo(0x283bb40|github.com/mumoshu/variant2/pkg/app.ctyToGo(0x283bb40>, 0xc0004b3da0, 0x21295e0, 0xc0004b3dc0, 0xc0004b3d80, 0x0, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:1271 +0x2be
<http://github.com/mumoshu/variant2/pkg/app.(*App).execRunInternal(0xc0000a38c0|github.com/mumoshu/variant2/pkg/app.(*App).execRunInternal(0xc0000a38c0>, 0xc000031280, 0xc0004b3c80, 0xc00000c3c0, 0x0, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:1048 +0x1bf
<http://github.com/mumoshu/variant2/pkg/app.(*App).execRun(0xc0000a38c0|github.com/mumoshu/variant2/pkg/app.(*App).execRun(0xc0000a38c0>, 0xc000031280, 0xc0004b3c80, 0xc00000c3c0, 0x28268c0, 0xc00049dc70, 0x0, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:1187 +0x81
<http://github.com/mumoshu/variant2/pkg/app.(*App).execJob(0xc0000a38c0|github.com/mumoshu/variant2/pkg/app.(*App).execJob(0xc0000a38c0>, 0xc000031280, 0xc0005e6fd0, 0x7, 0x0, 0x283b980, 0xc00045f740, 0x0, 0x0, 0x0, ...)
        /home/runner/work/variant2/variant2/pkg/app/app.go:812 +0x5fa
<http://github.com/mumoshu/variant2/pkg/app.(*App).Job.func1(0x0|github.com/mumoshu/variant2/pkg/app.(*App).Job.func1(0x0>, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:626 +0x9d2
<http://github.com/mumoshu/variant2/pkg/app.(*App).Run(0xc0000a38c0|github.com/mumoshu/variant2/pkg/app.(*App).Run(0xc0000a38c0>, 0xc0005e6fd0, 0x7, 0xc00052c5d0, 0xc00052c600, 0xc00058b9e0, 0x1, 0x1, 0x0, 0x0, ...)
        /home/runner/work/variant2/variant2/pkg/app/app.go:497 +0xc3
<http://github.com/mumoshu/variant2.(*Runner).Cobra.func1(0xc0005e1180|github.com/mumoshu/variant2.(*Runner).Cobra.func1(0xc0005e1180>, 0xc000520740, 0x0, 0x4, 0x0, 0x0)
        /home/runner/work/variant2/variant2/variant.go:624 +0x114
<http://github.com/spf13/cobra.(*Command).execute(0xc0005e1180|github.com/spf13/cobra.(*Command).execute(0xc0005e1180>, 0xc000520700, 0x4, 0x4, 0xc0005e1180, 0xc000520700)
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:826 +0x460
<http://github.com/spf13/cobra.(*Command).ExecuteC(0xc000032780|github.com/spf13/cobra.(*Command).ExecuteC(0xc000032780>, 0x2812d00, 0xc000098010, 0x2812d00)
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:914 +0x2fb
<http://github.com/spf13/cobra.(*Command).Execute(...)|github.com/spf13/cobra.(*Command).Execute(...)>
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:864
<http://github.com/mumoshu/variant2.(*Runner).Run(0xc000483090|github.com/mumoshu/variant2.(*Runner).Run(0xc000483090>, 0xc00024e0f0, 0x6, 0x6, 0xc00058bd78, 0x1, 0x1, 0x0, 0x0)
        /home/runner/work/variant2/variant2/variant.go:754 +0x2cc
<http://github.com/mumoshu/variant2.Main.Run(0x7ffeefbff81f|github.com/mumoshu/variant2.Main.Run(0x7ffeefbff81f>, 0x3, 0x0, 0x0, 0x0, 0x7ffeefbff818, 0xa, 0x2812d00, 0xc000098008, 0x2812d00, ...)
        /home/runner/work/variant2/variant2/variant.go:359 +0x12d
main.main()
        /home/runner/work/variant2/variant2/pkg/cmd/main.go:13 +0xb8
johncblandii
johncblandiiover 6 years ago
can we run dynamic depends_on like this? depends_on "${var.cmd} init" {
johncblandii
johncblandiiover 6 years ago
along the previous lines above, I’m passing in cmd.project as a string, splitting it, then using it. can I have a variable depend on a variable?

  variable "asplit" {
    type = list(string)
    value = split(".", opt.a)
  }

  variable "cmd" {
    type = string
    value = var.asplit[0]
  }

  variable "project" {
    type = string
    value = var.asplit[1]
  }

I get an error when I do:

Error: Unknown variable

  on ../cli/main.variant line 51:
  (source code not available)

There is no variable named "var".

Error: Unknown variable

  on ../cli/main.variant line 51:
  (source code not available)

There is no variable named "var".

Error: ../cli/main.variant:51,13-16: Unknown variable; There is no variable named "var".
johncblandii
johncblandiiover 6 years ago
btw, I can create github issues for these things at any point. just let me know.
mumoshu
mumoshuover 6 years ago

can I have a variable depend on a variable?

No, but seems valid and feasible to add support for it.

Would u mind creating a github issue for that?
johncblandii
johncblandiiover 6 years ago
will do
johncblandii
johncblandiiover 6 years ago
gonna crash, but i’ll check in tomorrow. variant2 is good stuff, @mumoshu. there is a lot of potential here. thx for all the work
johncblandii
johncblandiiover 6 years ago
@mumoshu does this work?

option "dry-run" {
  default     = false
  description = "simulate an install"
  type        = bool
}

I’m getting this error no matter what I do:

job "deploy switch": option "dry-run": can't convert Go string to boolError: job "deploy switch": option "dry-run": can't convert Go string to bool

it happens because of:

  depends_on "deploy switch" {
    items = conf.file.order

    args = {
      dry-run = opt.dry-run
      item    = item
      tenant  = param.tenant
    }
  }

I tried to run it with tobool(opt.dry-run), but I get that error every time I try to pass the option or just plain ol’ false to the run
johncblandii
johncblandiiover 6 years ago
@mumoshu it looks like indirect run works in a step

fails:
  step "deploy run" {
    run {
      job = "${var.subcommand} deploy"

      with = {
        project = var.project
        tenant  = param.tenant
      }
    }
  } 

works:
  # step "deploy run" {
    run {
      job = "${var.subcommand} deploy"

      with = {
        project = var.project
        tenant  = param.tenant
      }
    }
  # }

Use case:
I added a previous step to do a simple log message and thought that was the problem, but ran into this.
mumoshu
mumoshuover 6 years ago
indirect run in step isn't implemented yet. will make it work later!
mumoshu
mumoshuover 6 years ago
would you be okay if it removed the older syntax run "NAME" { ... } ?
johncblandii
johncblandiiover 6 years ago
🤔
johncblandii
johncblandiiover 6 years ago
i think it is more verbose for normal runs
mumoshu
mumoshuover 6 years ago
true
mumoshu
mumoshuover 6 years ago
it's just that direct/indirect run distinction makes it a bit harder to explain and maintain for me 🙂
johncblandii
johncblandiiover 6 years ago
it might make since overall since the normal syntax is type name
johncblandii
johncblandiiover 6 years ago
this is run "name of another type"
johncblandii
johncblandiiover 6 years ago
understandable
mumoshu
mumoshuover 6 years ago(edited)
also to me, any block with label(s) like someblock "LABEL1" "LABEL2" { } makes me think that if it can be referenced from within hcl expressions with someattr = someblock.LABEL1.LABEL2
johncblandii
johncblandiiover 6 years ago
right and you can’t with run
mumoshu
mumoshuover 6 years ago
exactly.
johncblandii
johncblandiiover 6 years ago(edited)
so that seems like a valid reason to go with job =
johncblandii
johncblandiiover 6 years ago
btw, i have this all automated. 🙂 the latest changes are indeed working
mumoshu
mumoshuover 6 years ago(edited)
yes. but on the other hand i do think that the newer syntax is verbose. not sure what i should do
mumoshu
mumoshuover 6 years ago
great!
johncblandii
johncblandiiover 6 years ago
sometimes verbose is a must 🙂
mumoshu
mumoshuover 6 years ago
that's very insightful 🙂
mumoshu
mumoshuover 6 years ago
thx. i'll try to build a more complex example variant2 command myself and see if the verbosity is acceptable.
johncblandii
johncblandiiover 6 years ago
cool
johncblandii
johncblandiiover 6 years ago
@mumoshu what’s the relationship to a job’s run status code and the next step running?
mumoshu
mumoshuover 6 years ago
@johncblandii are you talking about the case that you've two or more steps in a job?

job "example" {
  step "one" {
    run "x" {
    }
  }

  step "two" {
    run "y" {
    }
  }
}

mumoshu
mumoshuover 6 years ago
the second step "two" runs only when the first step exists with 0
mumoshu
mumoshuover 6 years ago
i.e. job example exits on the first step with non-zero exit code
mumoshu
mumoshuover 6 years ago
and example inherits the exit status of the step if it returned a non-zero exit status
mumoshu
mumoshuover 6 years ago
for example, x exited with 1 results in variant run example exits with 1
mumoshu
mumoshuover 6 years ago
@johncblandii Thanks for all your feedbacks!

I think I've finished all the important bugs reported/features requested so far. But please feel free to poke me if I'm missing something 🙂
johncblandii
johncblandiiover 6 years ago
@mumoshu is there a reason you have to manually pass a global option into tertiary runs?

Ex: run cli b --namespace hi
option "namespace"
  default = "cp"

job "b"
  echo opt.namespace // "hi"

  run "c" // no namespace set here

  run "d"
    namespace = opt.namespace

job "c"
  echo opt.namespace // "cp"

job "d"
  echo opt.namespace // "hi"

johncblandii
johncblandiiover 6 years ago
@mumoshu is there a way to exec a cli and allow the user to enter a response to the cli?

ex: mycli terraform apply will prompt to apply unless I tell it to automatically say “yes” and mycli just exits skipping over the prompt.
johncblandii
johncblandiiover 6 years ago
@mumoshu another one; can we control the exit status in the event we’re looping over something?

ex (pseudo code):

loop item as [call1, call2, call3]
  item()

Let’s say call1 passes, call2 fails, I would expect call3 to not run. Right now it continues to try and run 3 even though I need 2 to pass before 3 runs.
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
I think this what what “need” solves
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
Call3 needs call2
johncblandii
johncblandiiover 6 years ago
yeah
mumoshu
mumoshuover 6 years ago

Right now it continues to try and run 3 even though I need 2 to pass before 3 runs.

What did you observe this behavior with? steps ?
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago(edited)
(also, was this using the need keyword @johncblandii?)
mumoshu
mumoshuover 6 years ago
FYI: The needs attribute is documented here: https://github.com/mumoshu/variant2#concurrency
johncblandii
johncblandiiover 6 years ago
Will that work within a dynamic loop?
johncblandii
johncblandiiover 6 years ago
And yes, it was with a step/run
johncblandii
johncblandiiover 6 years ago
I'll bbiab and can provide code
mumoshu
mumoshuover 6 years ago(edited)
pls! an example code that doesn't work would always be helpful
mumoshu
mumoshuover 6 years ago
well do we have a dynamic loop thing? oh, is it depends_on "JOB" { items = ... }?
mumoshu
mumoshuover 6 years ago
depends_on is for multiple independent dependencies of the parent job
mumoshu
mumoshuover 6 years ago
so trying to build a dependency graph across the items of a depends_on seems conceptually wrong
johncblandii
johncblandiiover 6 years ago
Yes, it using that part for the looping. It isn't intended for a dependency graph. I just need to exec jobs in a loop and stop when they fail
johncblandii
johncblandiiover 6 years ago
Is there a concept of looping outside of depends on?
mumoshu
mumoshuover 6 years ago

exec jobs in a loop and stop when they fail

this seems like building a dependency graph that each next job depends on its previous job? 🤔

but anyways,

[10:35 AM] Is there a concept of looping outside of depends on?

no
mumoshu
mumoshuover 6 years ago
and depends_on items should be executed serially and stop on the first error
mumoshu
mumoshuover 6 years ago
are you seeing different behavior?
johncblandii
johncblandiiover 6 years ago
yes, i saw different behavior. i’ll try to replicate
johncblandii
johncblandiiover 6 years ago

this seems like building a dependency graph that each next job depends on its previous job?

i guess you could call it one, but that’s not the intent. it isn’t a dependency as in the cli depends on job 1 to run for 2. it is a dependency in how we’re managing our config file for looping
johncblandii
johncblandiiover 6 years ago
Context:

order:
  - terraform.account
  - terraform.iam-tenant-roles
  - terraform.cloudtrail
  - terraform.vpc

deploy job:

  depends_on "deploy switch" {
    items = conf.file.order

    args = {
      item   = item
      tenant = param.tenant
    }
  }

switch is the job that splits the left/right of the . and calls an internal job

job "deploy switch" {
  run {
    job = "${var.subcommand} deploy"

    with = {
      project = var.project
      tenant  = param.tenant
    }
  }
}

johncblandii
johncblandiiover 6 years ago
Issue created for building with a . as the path: https://github.com/mumoshu/variant2/issues/19
mumoshu
mumoshuover 6 years ago
thx!
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago
@Zachary Loeber you have a pulse on everything. Question for you: https://github.com/mumoshu/variant2/issues/17#issuecomment-617835218
Erik Osterman (Cloud Posse)
Erik Osterman (Cloud Posse)over 6 years ago

Where's/What's the silver bullet for building an enterprise-grade Web UI today?
Zachary Loeber
Zachary Loeberover 6 years ago
@Zachary Loeber has joined the channel
Zachary Loeber
Zachary Loeberover 6 years ago
I've been seeking such a bullet myself. I was going to look towards some of the fairwinds projects for inspiration (https://github.com/FairwindsOps/polaris for instance) as they seem to use pure Go based solutions but I haven't gotten that far yet. Most solutions I've seen incorporate some java frameworks that instantly turn me off.
Zachary Loeber
Zachary Loeberover 6 years ago
I think they use buffalo framwork behind the scenes but, again, I'm barely scratching at this particular itch of mine yet
Zachary Loeber
Zachary Loeberover 6 years ago
sorry 😞
johncblandii
johncblandiiover 6 years ago
@mumoshu I’m working with tests and it seems null should be allowed for err as opposed to requiring an empty string.

  case "ok" {
    tenant = "acme"
    exitstatus = 0
    err = ""
  }

^ this works just fine.

take err out and you get an error: This object does not have an attribute named "err".

set it to err = null and you get:

panic: handler for type dynamic not implemented yet [recovered]
        panic: handler for type dynamic not implemented yet

goroutine 10 [running]:
testing.tRunner.func1(0xc0000cb500)
        /opt/hostedtoolcache/go/1.13.10/x64/src/testing/testing.go:874 +0x3a3
panic(0x21ebee0, 0xc0000968e0)
        /opt/hostedtoolcache/go/1.13.10/x64/src/runtime/panic.go:679 +0x1b2
<http://github.com/mumoshu/variant2/pkg/app.(*App).execAssert(0xc00020b080|github.com/mumoshu/variant2/pkg/app.(*App).execAssert(0xc00020b080>, 0xc0005cc980, 0xc0004dda0a, 0x5, 0x2840660, 0xc000598a80, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:965 +0x9d0
<http://github.com/mumoshu/variant2/pkg/app.(*App).execTestCase(0xc00020b080|github.com/mumoshu/variant2/pkg/app.(*App).execTestCase(0xc00020b080>, 0xc000035740, 0x12, 0xc0005b5ec0, 0x1, 0x1, 0xc0005ab5e0, 0x1, 0x1, 0xc000035820, ...)
        /home/runner/work/variant2/variant2/pkg/app/app.go:1091 +0x659
<http://github.com/mumoshu/variant2/pkg/app.(*App).execTest.func1(0xc0000cb500)|github.com/mumoshu/variant2/pkg/app.(*App).execTest.func1(0xc0000cb500)>
        /home/runner/work/variant2/variant2/pkg/app/app.go:1037 +0xc2
testing.tRunner(0xc0000cb500, 0xc0005be790)
        /opt/hostedtoolcache/go/1.13.10/x64/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
        /opt/hostedtoolcache/go/1.13.10/x64/src/testing/testing.go:960 +0x350
johncblandii
johncblandiiover 6 years ago(edited)
@mumoshu I’d like to dynamically run different parameters to run and reference them in my case without manually typing them again. case.* access seems only available in a run or assert and not within the case itself or in variable.

example from https://github.com/mumoshu/variant2/blob/9753895bdea9bd949596159aaf2f3f9f489575dc/examples/concurrency/concurrency_test.variant:

  case "ng1" {
    concurrency = 0
    err = "delay of ${case.delayone} is less than ${case.delaytwo}"
    stdout = ""
    delayone = 0
    delaytwo = 1
  }

  run "test" {
    concurrency = case.concurrency
    delayone = case.delayone
    delaytwo = case.delaytwo
  }

instead of having type type:

err = "delay of 0 is less than 1"

…which then requires me to change 0 in multiple places if I change the value of delayone to anything else.
johncblandii
johncblandiiover 6 years ago
@mumoshu can we get some output clean-up on test failures? separating the runs + their failures and maybe some color coding would really help with readability.

https://github.com/mumoshu/variant2/issues/20
johncblandii
johncblandiiover 6 years ago
@mumoshu i think something is up with global options again. this bool option I can echo at the top level and it is true. then in jobs run from that job it is false.

deploy (dry-run = true) -> helmfile deploy (dry-run = false)

➜ ./nbo deploy acme --tenants-dir=../tenants --dry-run --kubeconfig-path=~/.kube

INTERNAL: dry? false. kc-path? /dev/shm

TOP-LEVEL: dry? true. kc-path? ~/.kube

the kc opt is a string and dry is a bool. those should both internally be different values.
johncblandii
johncblandiiover 6 years ago
I can’t pinpoint why this is failing. Any thoughts here, @mumoshu? I tried wrapping the run.res.exitstatus in trimspace since case.exitstatus used it too.

 (string)--- FAIL: deploy (0.06s)
    --- FAIL: deploy/ok (0.05s)
        app.go:1040: case "ok": assertion "out" failed: this expression must be true, but was false: run.res.stdout == case.out
            , where run.res.stdout=------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project1
            ------------------------------------------------------------------------
            terraform deploy project1 
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project2
            ------------------------------------------------------------------------
            terraform deploy project2 
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying helmfile project: project1
            ------------------------------------------------------------------------
            helmfile apply project1 
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying helmfile project: project2
            ------------------------------------------------------------------------
            helmfile apply project2 
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project3
            ------------------------------------------------------------------------
            terraform deploy project3 
            
             (string) case.out=------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project1
            ------------------------------------------------------------------------
            terraform deploy project1
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project2
            ------------------------------------------------------------------------
            terraform deploy project2
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying helmfile project: project1
            ------------------------------------------------------------------------
            helmfile apply project1
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying helmfile project: project2
            ------------------------------------------------------------------------
            helmfile apply project2
            
            ------------------------------------------------------------------------
            [CLIENT] Deploying terraform project: project3
            ------------------------------------------------------------------------
            terraform deploy project3
             (string)
FAIL
Error: test exited with code 1
johncblandii
johncblandiiover 6 years ago
(sorry about the flood today; digging into a new area with variant2) 🙂
johncblandii
johncblandiiover 6 years ago(edited)
btw, @mumoshu, I was able to completely write full CLI coverage with 0 knowledge of the test approach within a day of work for about 15+ commands
johncblandii
johncblandiiover 6 years ago
I think there is a regression in 0.24.2. the tests worked in 0.24.0 and now fail with:

  26:       aws --profile ${var.namespace}-${var.tenant}-helm eks update-kubeconfig --name=${var.namespace}-${var.tenant}-eks-cluster --region=${var.region} --kubeconfig=${var.kubeconfig-path}/kubecfg.${var.tenant}-helm

There is no variable named "var".
johncblandii
johncblandiiover 6 years ago
@mumoshu was talking with @Erik Osterman (Cloud Posse) about a new cli command and it’d be clean if I could use an if statement on an internal depends_on. is that possible?
johncblandii
johncblandiiover 6 years ago
@mumoshu along the lines of the previous question, can you do a source dynamically (only pull if param.x exists) or try/catch on a failed source load?
johncblandii
johncblandiiover 6 years ago
@mumoshu it seems options are not available to a job when it is used as a source of a config

  config "state" {
    source job {
      name = "state"
      args = {
        tenant = param.tenant
      }
      key = "key"
      format = "text"
    }
  }

that works, but it does not recognize the opts.tenants-dir in the state job and it does recognize the opt when I call it directly
johncblandii
johncblandiiover 6 years ago
@mumoshu can a variable not reference a config?
nian
nianover 6 years ago
For variant … this tool appears to be running locally, as opposed to something in a cluster from the local terminal. Is that correct?
johncblandii
johncblandiiover 6 years ago
0.28 seems to be pretty solid. tests are passing with no changes.
johncblandii
johncblandiiover 6 years ago
@mumoshu I’m getting weird results from simply reading my file.

contents:

triggers:
  smoke-test:
    description: Smoke testing the CLI
    order:
      - job: terraform plan eks
        args:
          - -detailed-exitcode

error:

panic: inconsistent map element types (cty.List(cty.String) then cty.String)

goroutine 1 [running]:
<http://github.com/zclconf/go-cty/cty.MapVal(0xc0009ea880|github.com/zclconf/go-cty/cty.MapVal(0xc0009ea880>, 0xc0009ea880, 0xc0009d20f8, 0x3, 0xc0009ea9b8)
        /home/runner/go/pkg/mod/github.com/zclconf/go-cty@v1.2.1/cty/value_init.go:207 +0x4b3
<http://github.com/mumoshu/variant2/pkg/app.goToCty(0x1767100|github.com/mumoshu/variant2/pkg/app.goToCty(0x1767100>, 0xc0004d38f0, 0x0, 0x0, 0x1, 0xc0009bb8e0, 0x0, 0x1)
        /home/runner/work/variant2/variant2/pkg/app/go_to_cty.go:28 +0x60e
<http://github.com/mumoshu/variant2/pkg/app.(*App).execMultiRun(0xc0000e3140|github.com/mumoshu/variant2/pkg/app.(*App).execMultiRun(0xc0000e3140>, 0xc000239580, 0xc000819280, 0xc0009eadf8, 0x16, 0xc0009d1160, 0x16)
        /home/runner/work/variant2/variant2/pkg/app/app.go:1214 +0x13f
<http://github.com/mumoshu/variant2/pkg/app.(*App).execJob(0xc0000e3140|github.com/mumoshu/variant2/pkg/app.(*App).execJob(0xc0000e3140>, 0xc000239580, 0xc0000bd997, 0x7, 0x0, 0x205fbc0, 0xc0006fc540, 0xc0000c7610, 0xc000577ea0, 0x2, ...)
        /home/runner/work/variant2/variant2/pkg/app/app.go:816 +0x246
<http://github.com/mumoshu/variant2/pkg/app.(*App).Job.func1(0x0|github.com/mumoshu/variant2/pkg/app.(*App).Job.func1(0x0>, 0x0, 0x0)
        /home/runner/work/variant2/variant2/pkg/app/app.go:644 +0x939
<http://github.com/mumoshu/variant2/pkg/app.(*App).Run(0xc0000e3140|github.com/mumoshu/variant2/pkg/app.(*App).Run(0xc0000e3140>, 0xc0000bd997, 0x7, 0xc00080adb0, 0xc00080ade0, 0xc00020b9e0, 0x1, 0x1, 0x0, 0x0, ...)
        /home/runner/work/variant2/variant2/pkg/app/app.go:513 +0xc3
<http://github.com/mumoshu/variant2.(*Runner).Cobra.func1(0xc00037d180|github.com/mumoshu/variant2.(*Runner).Cobra.func1(0xc00037d180>, 0xc00080acf0, 0x2, 0x3, 0x0, 0x0)
        /home/runner/work/variant2/variant2/variant.go:661 +0x114
<http://github.com/spf13/cobra.(*Command).execute(0xc00037d180|github.com/spf13/cobra.(*Command).execute(0xc00037d180>, 0xc00080ac00, 0x3, 0x3, 0xc00037d180, 0xc00080ac00)
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:826 +0x460
<http://github.com/spf13/cobra.(*Command).ExecuteC(0xc0000e9b80|github.com/spf13/cobra.(*Command).ExecuteC(0xc0000e9b80>, 0x2034ba0, 0xc0000be010, 0x2034ba0)
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:914 +0x2fb
<http://github.com/spf13/cobra.(*Command).Execute(...)|github.com/spf13/cobra.(*Command).Execute(...)>
        /home/runner/go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:864
<http://github.com/mumoshu/variant2.(*Runner).Run(0xc0007c3040|github.com/mumoshu/variant2.(*Runner).Run(0xc0007c3040>, 0xc0000f8c90, 0x4, 0x4, 0xc00020bd78, 0x1, 0x1, 0x0, 0x0)
        /home/runner/work/variant2/variant2/variant.go:791 +0x2cc
<http://github.com/mumoshu/variant2.Main.Run(0x7ffe211094bb|github.com/mumoshu/variant2.Main.Run(0x7ffe211094bb>, 0x3, 0x0, 0x0, 0x0, 0x7ffe211094b4, 0xa, 0x2034ba0, 0xc0000be008, 0x2034ba0, ...)
        /home/runner/work/variant2/variant2/variant.go:396 +0x12d
main.main()
        /home/runner/work/variant2/variant2/pkg/cmd/main.go:13 +0xb8
johncblandii
johncblandiiover 6 years ago
another interesting one, @mumoshu.

Parsing this YAML works fine:

    order:
      - job: terraform plan eks
        args: ""
      - job: helmfile diff teleport
        args: --selector chart=teleport-node

Parsing this YAML seems to have a problem with:

    order:
      - job: terraform plan eks
        # args: ""
      - job: helmfile diff teleport
        args: --selector chart=teleport-node

It throws the following error, but the issue is a missing args not a missing job:

Error: Missing map element

  on ../cli/trigger.variant line 36:
  (source code not available)

This map does not have an element with the key "job".

Error: ../cli/trigger.variant:36,29-33: Missing map element; This map does not have an element with the key "job".