Wait on condition in Taskfile - taskfile

Is there a way to use a precondition in a Taskfile.yaml to wait for a condition to be true, before running cmds? (precondition makes the task fail, not wait)
For example, I would like to wait for a Kubernetes CRD to be fully available before running a task.

I think the best way there is right now, is to do
# ...
version: '3'
tasks:
deploy:
cmds:
- task: await
vars:
- { CRD: "postgrescluster" }
await:
cmds:
- |
until kubectl wait --for condition=established --timeout=60s crd/{{.CRD}}; do \
sleep 1; \
done
As per https://taskfile.dev/usage/#task-dependencies

Related

How to run multiple ECS tasks on the same server

I am trying to get multiple ECS tasks to run on the same EC2 server. It is a g4dn.xlarge which has 1GPU, 4CPU, and 16GB of memory.
I am using this workaround to allow the GPU to be shared between tasks. https://github.com/aws/containers-roadmap/issues/327
However, when I launch multiple tasks, the second one gets stuck in a provisioning state until the first one finishes.
CloudWatch shows that the CPUUtilization is below 50% for the entire duration of each task.
This is my current CDK:
const taskDefinition = new TaskDefinition(this, 'TaskDefinition', {
compatibility: Compatibility.EC2
})
const container = taskDefinition.addContainer('Container', {
image: ContainerImage.fromEcrRepository(<image>),
entryPoint: ["python", "src/script.py"],
workingDirectory: "/root/repo",
startTimeout: Duration.minutes(5),
stopTimeout: Duration.minutes(60),
memoryReservationMiB: 8192,
logging: LogDriver.awsLogs({
logGroup: logGroup,
streamPrefix: 'prefix',
}),
})
const startUpScript = UserData.forLinux()
// Hack for allowing tasks to share the same GPU
// https://github.com/aws/containers-roadmap/issues/327
startUpScript.addCommands(
`(grep -q ^OPTIONS=\\"--default-runtime /etc/sysconfig/docker && echo '/etc/sysconfig/docker needs no changes') || (sed -i 's/^OPTIONS="/OPTIONS="--default-runtime nvidia /' /etc/sysconfig/docker && echo '/etc/sysconfig/docker updated to have nvidia runtime as default' && systemctl restart docker && echo 'Restarted docker')`
)
const launchTemplate = new LaunchTemplate(this, 'LaunchTemplate', {
machineImage: EcsOptimizedImage.amazonLinux2(
AmiHardwareType.GPU
),
detailedMonitoring: false,
instanceType: InstanceType.of(InstanceClass.G4DN, InstanceSize.XLARGE),
userData: startUpScript,
role: <launchTemplateRole>,
})
const autoScalingGroup = new AutoScalingGroup(this, 'AutoScalingGroup', {
vpc: vpc,
minCapacity: 0,
maxCapacity: 1,
desiredCapacity: 0,
launchTemplate: launchTemplate,
})
const capacityProvider = new AsgCapacityProvider(this, 'AsgCapacityProvider', {
autoScalingGroup: autoScalingGroup,
})
cluster.addAsgCapacityProvider(capacityProvider)
Edit:
Issue still persists after assigning task definition the CPU and Memory amounts.
Got it working by setting both the task sizes and container sizes to less than the sum that is available on the instance. So, although the instance has 16gb RAM and 4vCPUs, there must be leftover RAM and CPU for the instance in order to assign new tasks. So 2 tasks that have 2vCPU & 8gb RAM won't work but if they both have 1vCPU and 4gb RAM, that will work.

Ansible - How to do integer comparison in a conditional?

I am trying to do a simple integer comparison in a conditional but something is up, and it is not evaluating properly.
Code within playbook:
- name: Check the version of the current sql database on both dispatchers and save that value.
command: /usr/bin/mysql -V
changed_when: false
register: sqlversion
- name: Print to the screen the current sql database version.
debug:
var: "{{ sqlversion.stdout.split()[4] | regex_search('[0-9]+\\.[0-9]+') | replace(\".\",'') }}"
register: w
- name: Show the value of the variable.
debug:
var: w
- name: Test result
command: w
when: ( w | int < 55 )
The output of the command module (ultimately to get the 5.5 part of the 5.5.43 number:
mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2
My actual run in which the Test result task "fails" as in it runs when it should not, because of the evaluation problem:
TASK [Check the version of the current sql database on both dispatchers and save that value.] ***********
ok: [server2]
ok: [server1]
TASK [Print to the screen the current sql database version.] ********************************************
ok: [server1] => {
"55": "55"
}
ok: [server2] => {
"55": "55"
}
TASK [Show the value of the variable.] ******************************************************************
ok: [server1] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
ok: [server2] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
TASK [Test result] **************************************************************************************
changed: [server1]
changed: [server2]
This is not right or expected, clearly the last task shows "changed" in that it ran, and evaluated the condition as true when it shouldn't be. Instead with how I coded the conditional, it should be skipped instead! Additionally, if I take away the " | int" then it always skips no matter what the number(s) are.
What's the issue here? There's got to be a way to make this work.
Simplify the parsing
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
gives
w: 5.5.43
Use the test version. See Comparing versions. For example,
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
Example of a complete playbook for testing
- hosts: localhost
vars:
sqlversion:
stdout: "mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2"
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
tasks:
- debug:
var: w
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
If the filter split is not available use the method twice
w: "{{ (sqlversion.stdout.split(',')|first).split()|last }}"
... but I also am forced on this series of machines to use Ansible 2.9 ...
As mentioned by Vladimir Botka
If the filter split is not available use the method twice. I added an example. The filter split is available since 2.11
the split filter for manipulation strings is available as of Ansible v2.11 but you can just use the Python method .split().
As a workround in Ansible v2.9 or 2.10 one could also implement a Custom Filter Plugin and so a simple Ansible Jinja2 filter to split a string into a list. By doing this a filter like | split() can made be available in older versions.

A playbook with two roles: running role B complains with role A's code which successfully ran

I am experiencing a strange behavior: when I run role B, it complains role A's code which I can successfully run! I have reproduced this to this minimal example:
$ cat playbooka.yml
- hosts:
- host_a
roles:
- role: rolea
tags:
- taga
- role: roleb
tags:
- tagb
I have tagged two roles because I want to selectively run role A or role B, they consist simple tasks as shown below in this minimal example:
$ cat roles/rolea/tasks/main.yml
- name: Get service_facts
service_facts:
- debug:
msg: '{{ ansible_facts.services["amazon-ssm-agent"]["state"] }}'
- when: ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
meta: end_play
$ cat roles/roleb/tasks/main.yml
- debug:
msg: "I am roleb"
The preview confirms that I can run individual roles as specified by tags:
$ ansible-playbook playbooka.yml -t taga -D -C --list-hosts --list-tasks
playbook: playbooka.yml
play #1 (host_a): host_a TAGS: []
pattern: ['host_a']
hosts (1):
3.11.111.4
tasks:
rolea : Get service_facts TAGS: [taga]
debug TAGS: [taga]
$ ansible-playbook playbooka.yml -t tagb -D -C --list-hosts --list-tasks
playbook: playbooka.yml
play #1 (host_a): host_a TAGS: []
pattern: ['host_a']
hosts (1):
3.11.111.4
tasks:
debug TAGS: [tagb]
I can run role A OK:
$ ansible-playbook playbooka.yml -t taga -D -C
PLAY [host_a] *************************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]
TASK [rolea : Get service_facts] ******************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]
TASK [rolea : debug] ******************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4] => {
"msg": "running"
}
PLAY RECAP ****************************************************************************************************************************************************************************************************************************************
3.11.111.4 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But when I run role B, it complains the code in role A which I just successfully ran!
$ ansible-playbook playbooka.yml -t tagb -D -C
PLAY [host_a] *************************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]
ERROR! The conditional check 'ansible_facts.services["amazon-ssm-agent"]["state"] != "running"' failed. The error was: error while evaluating conditional (ansible_facts.services["amazon-ssm-agent"]["state"] != "running"): 'dict object' has no attribute 'services'
The error appears to be in '<path>/roles/rolea/tasks/main.yml': line 9, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- when: ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
^ here
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:
foo: "bad" "wolf"
Could be written as:
foo: '"bad" "wolf"'
I have two questions:
Why role A's code should be involved at all?
Even it gets involved, ansible_facts has services, and the service is "running" as shown above by running role A.
PS: I am using the latest Ansible 2.10.2 and latest python 3.9.1 locally on a MacOS. The remote python can be either 2.7.12 or 3.5.2 (Ubuntu 16_04). I worked around the problem by testing if the dictionary has the services key:
ansible_facts.services is not defined or ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
but it still surprises me that role B will interpret role A's code and interpreted it incorrectly. Is this a bug that I should report?
From the notes in meta module documentation:
Skipping meta tasks with tags is not supported before Ansible 2.11.
Since you run ansible 2.10, the when condition for your meta task in rolea is always evaluated, whatever tag you use. When you use -t tagb, ansible_facts.services["amazon-ssm-agent"] does not exist as you skipped service_facts, and you then get the error you reported.
You can either:
upgrade to ansible 2.11 (might be a little soon as I write this answer since it is not yet available over pip...)
rewrite your condition so that the meta task skips when the var does not exists e.g.
when:
- ansible_facts.services["amazon-ssm-agent"]["state"] is defined
- ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
The second solution is still a good practice IMO in whatever condition (e.g. share your work with someone running an older version, running accidentally against a host without the agent installed....).
One other possibility in your specific case is to move the service_facts tasks to an other role higher in play order, or in the pre_tasks section of your playbook, and tag it always. In this case the task will always play and the fact will always exists, whatever tag you use.

Gitlab CI pipeline to run jobs parallel in same stage and invoke/trigger other jobs of same stage

I am trying to create a automation pipeline for data load. I have a scenario as explained below:
stages
- stage1
- stage2
job1:
stage: stage1
script:
- echo "stage 1 job 1"
job2:
stage: stage1
script:
- echo "stage 1 job 2"
job3:
stage: stage1
script:
- echo "stage 1 job 3"
job4:
stage: stage1
script:
- echo "stage 1 job 4"
I want to run the job1 and job2 parallel in the same stage. So, after Job1 and job2 success
job1 will invoke/trigger the job3. that means job3 will start automatically when job1 successes
job2 will invoke/trigger the job4 that means job4 will start automatically when job2 successes
I am writing the pipeline in the .gitlab-ci.yml.
Can anyone help me to implement this?
Strict implementation of your requirements is not possible (according to my knowledge), the jobs 3 and 4 would need to be in a separate stage (although support for putting them in the same stage is planned). To be clear: the other functional requirements can be fulfilled, i.e:
job1 and job2 start in parallel
job1 will trigger the job3 (immediately, without waiting for the job2 to finish)
job2 will trigger the job4 (immediately, without waiting for the job1 to finish)
The key is using needs keyword to convert the pipieline to a directed acyclic graph:
stages:
- stage-1
- stage-2
job-1:
stage: stage-1
needs: []
script:
- echo "job-1 started"
- sleep 5
- echo "job-1 done"
job-2:
stage: stage-1
needs: []
script:
- echo "job-2 started"
- sleep 60
- echo "job-2 done"
job-3:
stage: stage-2
needs: [job-1]
script:
- echo "job-3 started"
- sleep 5
- echo "job-3 done"
job-4:
stage: stage-2
needs: [job-2]
script:
- echo "job-4 started"
- sleep 5
- echo "job-4 done"
As you can see on the screenshot, the job 3 is started, even though the job 2 is still running.

Ansible until loop with regex in conditional statement

I need to check command stdout with a regex pattern in until loop. When I using regex_search to display debug msg all works fine:
- name: Checking if node is ready
shell: "kubectl --kubeconfig {{kubeconf}} get nodes"
register: k_output
- debug:
msg: "{{k_output.stdout | regex_search(node_hostname | string + '\\s+Ready')}}"
The message is
ok: [any_node_hostname] => {
"msg": "any_node_hostname Ready"
}
But if I trying to use that construction inside until conditional statement task fails with syntax error.
- set_fact:
regexp_pattern: "{{node_hostname | string + '\\s+Ready'}}"
- debug:
msg: "{{regexp_pattern}}"
- name: Checking if node is ready
shell: "kubectl --kubeconfig {{kubeconf}} get nodes"
register: k_output
until: "{{k_output.stdout | regex_search(regexp_pattern)}}" != ""
retries: 5
delay: 10
The same behaviour without set_fact when I just copying and pasting full string {{k_output.stdout | regex_search(node_hostname | string + '\\s+Ready')}} to until conditional statement.
So, how can I use regex_search or something that fits this case with until?
you have syntax error at until: statement : you must not quote the vars in expression, like in example here : Retrying a task until a condition is met
until: k_output.stdout | regex_search(regexp_pattern) != ""
I hope this will help