jeudi 28 août 2025

Systemd healtcheck with side service and monotonic timer, auto-healing

What : bypass the lack of healthcheck of systemd

  • systemd service "what-service.service"
  • systemd timer  "what-service-healthcheck.timer"
    • triggers a systemd service "what-service-healthcheck.service"
       which lanches a script "
      service_health_check.sh"
    • script that :
      • curl's heal-tcheck URL "HEALTH_CHECK_URL"
      • if KO, restart the targetted service



what-service-healthcheck.timer

[Unit]
Description=Run health check every 15 seconds
[Timer]
# Wait 1 minute after boot before the first check
OnBootSec=1min
# Run the check 15 seconds after the last time it finished
OnUnitActiveSec=15s
[Install]
WantedBy=timers.target


By default the timer service will trigger the unit service with the same name, no need to specify it.

what-service-healthcheck.service.j2
[Unit]
Description=Health Check for {{ what_service }}
Requires={{ what_service }}.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/service_health_check.sh
Restart=on-failure
OnFailure={{ what_service }}


service_health_check.sh 
#!/bin/bash
# The health check endpoint
HEALTH_CHECK_URL="http://localhost:{{ running_port }}/health_check"
# Use curl to check the endpoint.
# --fail: Makes curl exit with a non-zero status code on server errors (4xx or 5xx).
# --silent: Hides the progress meter.
# --output /dev/null: Discards the response body.
if ! curl --silent --fail --max-time 2 --output /dev/null "$HEALTH_CHECK_URL"; then
echo "Health check failed for {{ service_name }}. Restarting..."
# Restart is performed on failure from healthcheck service
exit 1
fi



Adding through ansible (to do : fix indentation, blog isn't great for this)

<role>/tasks/main.yml
--- 

- name: Generate what-service systemd file
ansible.builtin.template:
src: what-service.service.j2
dest: /etc/systemd/system/what-service.service
mode: "0755"
notify: Restart what-service

 - name: Copy the health check script
ansible.builtin.copy:
src: service_health_check.sh
dest: /usr/local/bin/service_health_check.sh
owner: root
group: root
mode: '0755'
vars: 
  service_name: what-service

- name: Copy the health check systemd service file
ansible.builtin.copy:
src: what-service-healthcheck.service
dest: /etc/systemd/system/what-service-healthcheck.service
owner: root
group: root
mode: '0644'
notify: Reload systemd

- name: Copy the health check systemd timer file
ansible.builtin.copy:
src: what-service-healthcheck.timer
dest: /etc/systemd/system/what-service-healthcheck.timer
owner: root
group: root
mode: '0644'
notify: Reload systemd

- name: Enable and start the health check timer
ansible.builtin.systemd:
name: healthcheck.timer
state: started
enabled: yes
daemon_reload: yes # Ensures systemd is reloaded before starting


<role>/handlers/main.yml
---
- name: Restart what-service 
ansible.builtin.service:
name: what-service
state: restarted
daemon_reload: true

- name: Reload systemd
ansible.builtin.service:
daemon_reload: yes

- name: Restart what-service-healthcheck.timer
ansible.builtin.service:
name: what-service-healthcheck.timer
state: restarted
daemon_reload: true

jeudi 24 juillet 2025

apt info - ansible tasks + roles to install apt_info.py automatically along node-exporter + Grafana dashboard

Create a file with openmetrics values, so that it be exporter along node-exporter metrics.

=> script runs every 12h to report the status of apt packages to upgrade writes it in  /var/lib/node_exporter/apt_info.prom 

which is ingested by prometheus when calling node-exporter.


The metrics are used by a grafana dashboard available here : https://grafana.com/grafana/dashboards/23777-apt-ugrades/


```

---
- name: Monitoring probes - setup exporters running on each server
hosts: all
vars:
become_user: root
become: true

tasks:
# https://github.com/ncabatoff/process-exporter
- name: Install .deb package of process-exporter
ansible.builtin.apt:
deb: https://github.com/ncabatoff/process-exporter/releases/download/v0.8.3/process-exporter_0.8.3_linux_amd64.deb
become: true

- name: Download and install apt_info.py
ansible.builtin.get_url:
url: https://raw.githubusercontent.com/prometheus-community/node-exporter-textfile-collector-scripts/refs/heads/master/apt_info.py
dest: /usr/local/bin/apt_info.py
mode: '0755'
become: true

- name: Install apt_info.py dependencies via apt
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: true
become: true
with_items:
- python3-prometheus-client
- python3-apt
- cron

- name: Add a cron job to run apt_info.py every 12 hours
ansible.builtin.cron:
name: "Run apt_info.py every 12 hours"
minute: "0"
hour: "*/12"
job: "/usr/local/bin/apt_info.py > /var/lib/node_exporter/apt_info.prom"
become: true
ignore_errors: "{{ ansible_check_mode }}"

- name: Ensure APT auto update is enabled
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/99_auto_apt_update.conf
content: 'APT::Periodic::Update-Package-Lists "1";'
owner: root
group: root
mode: '0644'
become: true

roles:
# https://github.com/prometheus-community/ansible/tree/main/roles/node_exporter
- name: prometheus.prometheus.node_exporter

# node_exporter_textfile_dir: "/var/lib/node_exporter" # default
```

jeudi 29 mai 2025

SRE & monitoring of distributed systems


2 different type of recommended monitoring : USE and RED

mercredi 28 mai 2025

Some cryptographic references and blockchain applications

 

Preface: [...] This book is about exactly that: constructing practical cryptosystems for which we can argue security under plausible assumptions. The book covers many constructions for different tasks in cryptography. For each task we define a precise security goal that we aim to achieve and then present constructions that achieve the required goal. To analyze the constructions, we develop a unified framework for doing cryptographic proofs. A reader who masters this framework will be capable of applying it to new constructions that may not be covered in the book.[...]

 

Abstract: We construct new multi-signature schemes that provide new
functionality. Our schemes are designed to reduce the size of the Bitcoin
blockchain, but are useful in many other settings where multi-signatures
are needed. All our constructions support both signature compression
and public-key aggregation. Hence, to verify that a number of parties
signed a common message m, the verifier only needs a short multi-
signature, a short aggregation of their public keys, and the message m.
We give new constructions that are derived from Schnorr signatures and
from BLS signatures. Our constructions are in the plain public key model,
meaning that users do not need to prove knowledge or possession of their
secret key.

 

Intro: Consensus algorithm is one of the most important components in blockchain. Harmony Blockchain achieves consensus through the Fast Byzantine Fault Tolerance (FBFT) algorithm. In FBFT, instead of asking all validators to broadcast their votes, the leader runs a multi-signature signing process to collect the validators’ votes in a O(1)-sized multi-signature and then broadcast it to all validators. Consensus is reached when all the validators validate the aggregated signature against the aggregated public keys for this round of consensus.


 

(vrac / to edit / to format) Prometheus sandbox - demo / prometheus relabeling tool & ref / grafana demo


* The Art of Metric Relabeling in Prometheus: 

https://heiioncall.com/guides/the-art-of-metric-relabeling-in-prometheus


* relabeler online testing tool : 
https://relabeler.promlabs.com/



* relabeling cookbook (mostly compatible with prometheus too) https://docs.victoriametrics.com/victoriametrics/relabeling/#how-to-remove-labels-from-targets 


* open / demo instance of grafana :  https://play.grafana.org/


* Grafana dashboards directory : https://grafana.com/grafana/dashboards/


* Open / demo instance of prometheus :


https://prometheus.demo.prometheus.io/query


 https://prometheus.demo.prometheus.io/config


global: scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 evaluation_interval: 15s external_labels: environment: demo-prometheus-io.c.macro-mile-203600.internal runtime: gogc: 75 alerting: alertmanagers: - follow_redirects: true enable_http2: true scheme: http timeout: 10s api_version: v2 static_configs: - targets: - demo.prometheus.io:9093 rule_files: - /etc/prometheus/rules/*.yml - /etc/prometheus/rules/*.yaml - /etc/prometheus/rules/*.rules scrape_config_files: - /etc/prometheus/scrape_configs/* scrape_configs: - job_name: prometheus honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true static_configs: - targets: - demo.prometheus.io:9090 - job_name: random honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true file_sd_configs: - files: - /etc/prometheus/file_sd/random.yml refresh_interval: 5m - job_name: caddy honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true static_configs: - targets: - localhost:2019 - job_name: grafana honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true static_configs: - targets: - demo.prometheus.io:3000 - job_name: node honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true file_sd_configs: - files: - /etc/prometheus/file_sd/node.yml refresh_interval: 5m - job_name: alertmanager honor_timestamps: true track_timestamps_staleness: false scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true file_sd_configs: - files: - /etc/prometheus/file_sd/alertmanager.yml refresh_interval: 5m - job_name: cadvisor honor_timestamps: true track_timestamps_staleness: true scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /metrics scheme: http enable_compression: true follow_redirects: true enable_http2: true file_sd_configs: - files: - /etc/prometheus/file_sd/cadvisor.yml refresh_interval: 5m - job_name: blackbox honor_timestamps: true track_timestamps_staleness: false params: module: - http_2xx scrape_interval: 15s scrape_timeout: 10s scrape_protocols: - OpenMetricsText1.0.0 - OpenMetricsText0.0.1 - PrometheusText1.0.0 - PrometheusText0.0.4 metrics_path: /probe scheme: http enable_compression: true follow_redirects: true enable_http2: true relabel_configs: - source_labels: [__address__] separator: ; target_label: __param_target replacement: $1 action: replace - source_labels: [__param_target] separator: ; target_label: instance replacement: $1 action: replace - separator: ; target_label: __address__ replacement: 127.0.0.1:9115 action: replace static_configs: - targets: - http://localhost:9100

mardi 4 mars 2025

terraform Associate certification notes & links

Terraform Associate exam is a 60-minute, multiple choice exam for beginner-level learners. 

Terraform Authoring and Operations Professional exam is 4 hours long, lab-based with some MCQs, and intended for very experienced Terraform users.



Notice which questions have checkboxes, and for those, make sure you check the right number of boxes.  This goes back to reading the whole question.


Some HCP topics covered  at a basic level, specifically in objective 9, "Understanding HCP Terraform capabilities".  You can learn more about exactly what topics are covered and how to study for them by reviewing our study materials: https://developer.hashicorp.com/terraform/tutorials/certification-003




Are the questions focused on AWS provider? exams are provider-agnostic when possible. 

exam would be on which tf version? Product version tested: Terraform 1.0 or higher



  • You will need a GitHub account to register for your exam appointment.


If you already hold an unexpired Terraform Associate certification, and then pass Terraform Pro, you’re Associate certification expiration date will be extended.

  • What is the validity for this certification ? certifications are valid for two years

  • How many questions are on the 1-hour exam? We don't publish these details about the exam, but for the average test-taker, 60 minutes is enough time to answer every question we present. If you are concerned about time, you can see if you qualify for an accommodation here: https://hashicorp-certifications.zendesk.com/hc/en-us/articles/360046049832


Other certs

mardi 19 novembre 2024

removing unwanted *typo* systemd instance'd template units

 Systemd has this great feature : 


file: foo@.service 

contains the definition, as usual, with "%i"

=> will replace anything in the service file 

for example:


systemctl start foo@bar.service

systemctl start foo@resto.service


=> will start  service foo@bar  and foo@resto


but if resto does not make a valid service, it fails and can't be easily removed from the list of apparently formerly existing services.

(cf. for example https://bbs.archlinux.org/viewtopic.php?id=286912&p=1 )


As you can see with the command : 

 systemctl list-units --type=service | grep 'foo'

  UNIT                                                                                      LOAD   ACTIVE SUB     DESCRIPTION

  foo@alpha.service                                                               loaded active running Foo for protocol alpha

● foo@lish.service                                                              masked failed failed  foo@lish.service



=> Solution (on ubuntu, but no reason it does not work elsewhere) 

 systemctl mask foo@resto.service #this creates a symlink / replaces the symlink with one pointing to /dev/null, and marks the service as failed => no restart will happen again

 systemctl reset-failed # cleans failed services




vendredi 15 novembre 2024

workshop - Intro to Creative Coding & Generative Art, by Ahmad Moussa (links)

 https://slides.com/ahmadmoussa-3/creative-coding-generative-art

by https://x.com/gorillasu


https://timeline.lerandom.art Generative Art Timeline


https://editor.p5js.org 


https://x.com/gorillasu/status/1857444190910664749 example with p5js


https://openprocessing.org/sketch/2320206


example on fxHash https://www.fxhash.xyz/marketplace/generative/15063

colors palette : https://ronikaufman.github.io/color_pals/

colors manipulation js library : https://gka.github.io/chroma.js/

+ https://encycolorpedia.com/2b3c00


https://iquilezles.org/articles/distfunctions/ shapes and math



doc on fxHash : https://docs.fxhash.xyz/


Tricks : 

- when playing with RGB, start from the median value 127.5 and move around +/- 127.5

- RGB + 2 chars => 2 chars of transparency

- cos/sin/tan [0..1] with frameCount native variable of p5js : use pi to divide values and have the last frame coincide with the first frame of the loop.




--- first example

let a = 1;

var b;

const windowWidth = 800;

const windowHeitgh = 800;


function setup() {

  // attach a canvas element to the dom

  createCanvas(windowWidth, windowHeitgh);

  

}


// runs continously for each frame of the video, unless noLoop() inside

function draw() {

  // "#efface"

  // "#decede"

  background("#decede");

  ellipse(100, 150, 100)


  fill ("#efface")

  rect (300, 200, 200)

  noStroke()

  stroke("blue")

  

}



-- animated


let a = 1;

var b;

const windowWidth = 400;

const windowHeitgh = windowWidth;


function setup() {

  // attach a canvas element to the dom

  createCanvas(windowWidth, windowHeitgh);

  

}


// runs continously for each frame of the video, unless noLoop() inside

function draw() {

  // "#efface"

  // "#decede"

  background("#decede");

  noStroke()

  let t = frameCount 

  // for (let y = 0; y < 400; y=y+1){

  //   ellipse (200, y, 5)

  // }

  

  // console.log(frameCount/50)


  for (let y = 0; y < 400; y=y+1){

    fill(127.5 + sin (-frameCount/50 + y/20) * 127.5, 

         127.5 + sin (-frameCount/50 + y/20) * 127.5, 

         127.5 + sin (-frameCount/50 + y/20) * 127.5)

    

    ellipse (200 + sin (-frameCount/50 + y/20) * 20, 

             y,

             20 + cos (frameCount/30 + y/10) * 10)

  }  

  

}

--- moving, and colors changing (by me, inspired by GorillaSun)

let a = 1;

var b;

const windowWidth = 400;

const windowHeitgh = windowWidth;


let variation = 0

let variation2 = 2


function setup() {

  // attach a canvas element to the dom

  createCanvas(windowWidth, windowHeitgh);

  frameRate(50) // frame / s

  

  randomSeed(0) // so that random always does the same

  variation = random(0, 80)

  variation2 = random(100, 80)

}


let loopLength = 75



// runs continously for each frame of the video, unless noLoop() inside

function draw() {

  // "#efface"

  // "#decede"

  background("#decede");

  

  // trick to 

  let t = ( frameCount % loopLength ) / loopLength  * 2 * PI;

  

  // console.log(sin(PI))

  

  // noStroke()


  

  // for (let y = 0; y < 400; y=y+1){

  //   ellipse (200, y, 5)

  // }

  

  // console.log(frameCount/50)


  for (let y = 0; y < 400; y=y+1){

    stroke (127.5 + sin (-t + y/variation) * 127.5, 

            127.5 + sin (-t + y/20) * 127.5, 

            127.5 + sin (-t + y/variation2) * 127.5)

    

    // fill(127.5 + sin (-t/50 + y/20) * 127.5, 

    //      127.5 + sin (-t/50 + y/20), 

    //      127.5 + sin (-t/50 + y/20) * 127.5)

    noFill()

    

    square (200 + sin (-t/50 + y/variation) * 20, 

            200 + tan (-t/50 + y/variation) * variation*variation/3,

             20 + cos (t/30 + y/variation) * variation)

  }  

  

}


function keyPressed(){

  console.log(keyCode)

  if (keyCode == 56){

  saveGif('annimation', 4)

  }

}




--- other links and code from the presentation




other code from the presentation :

Sinus Wave by Ahmad Moussa || Gorilla Sun  https://openprocessing.org/sketch/1784279

function setup() {
createCanvas(400, 400);

}

function draw() {
background(0);
noStroke();
let t = millis()/500

for(let y = 20; y < 380; y++){
  fill(
    127.5 + 127.5 * sin(t + y/40),
    127.5 - 127.5 * cos(t + y/40),
    127.5 + 127.5 * cos(t + y/40)
  )
  ellipse(
    200 + sin(t + y/80 +
              sin(t + y/400) +
              cos(t + y/100)*2
             ) * 30, 
    y + sin(t + y/80 +
              sin(t + y/400))*15,
    20 + sin(t + y/10) * 15)
}
}

variation square by Ahmad Moussa || Gorilla Sun 
function setup() {
createCanvas(400, 400);
}

function draw() {
background(0);
noStroke();
let t = millis()/500

for(let y = 20; y < 380; y++){
  fill(
    127.5 + 127.5 * sin(t + y/40),
    127.5 + 127.5 * cos(t + y/40),
    127.5 - 127.5 * sin(t + y/80)
  )
  ellipse(
    200 + sin(t + y/80 +
              sin(t + y/400) +
              cos(t + y/50)*2
             ) * 30, 
    y + sin(t + y/80 +
              sin(t + y/400))*15,
    20 + sin(t + y/30) * 15)
}
}




LINKS

Zach Lieberman, Talk: Poetic Computation + atlas of blobs




https://openprocessing.org/sketch/2320206 simple circles packing by Ahmad Moussa || Gorilla Sun 

(with : <script src="https://openprocessing.org/openprocessing_sketch.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.4/lib/p5.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.4/lib/addons/p5.sound.min.js"></script> )


let circles = []
let numAttempts = 20000

let canvas_width = 800
let canvas_height = 800
let padding = 50

function setup() {
  createCanvas(canvas_width, canvas_height);
  pixelDensity(4)
  
  background(0)
  stroke(255)
  
  for(let n = 0; n < numAttempts; n++){
    
    let randX = random(padding, canvas_width-padding)
    let randY = random(padding, canvas_height-padding)
    let randR = random(5, 120)
    
    let placeable = true
    for(let circle of circles){
      let d = dist(
        randX, randY, circle.x, circle.y
      )
      
      if(d < randR + circle.r + 5){
        placeable = false
      }
    }
    
    if(
      (randX + randR > canvas_width-padding) ||
      (randX - randR < padding) ||
      (randY + randR > canvas_height-padding) ||
      (randY - randR < padding)
    ){
        placeable = false
    }
    
    if(placeable){
      circles.push(
        {
          x: randX, y: randY, r: randR
        }
      )
    }
  }
  
  strokeWeight(2)
noFill()
  for(let circle of circles){
    ellipse(
      circle.x,
      circle.y ,
      circle.r*2
    )
  }
  
  noLoop()
}




 Particle System with Grid Lookup
by Ahmad Moussa || Gorilla Sun 
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.min.js"></script> 
p5.disableFriendlyErrors = true;
let N_PARTICLES = 50;
let particles = [];
let grid;

function setup() {
  createCanvas(windowWidth, windowHeight);

  grid = new Grid(windowWidth, windowHeight, 22);

  for (let n = 0; n < N_PARTICLES; n++) {
    let particle = new Particle(new createVector(random(windowWidth), random(windowHeight)))
    particles.push(particle);
    grid.addParticle(particle);
  }
}

function draw() {
  background(220);

  if (mouseIsPressed) {
    for(let n = 0; n < 5; n++){
let particle = new Particle(new createVector(mouseX+random(-1,1), mouseY+random(-1,1)))
    particles.push(particle);
    grid.addParticle(particle);
}
  }

  textSize(30)
  textAlign(LEFT, TOP)
  let numCollisionChecks = 0
  let startneigh = performance.now();
  for (let p of particles) {
    let neighbors = grid.getNeighbors(p)
    numCollisionChecks += p.checkCollision(neighbors)
p.updateState();
  }
  let endneigh = performance.now();
  let coll = `${numCollisionChecks} collisions computed in ${ endneigh - startneigh} ms`

  let startup = Date.now();
  for (let p of particles) {
p.checkEdges();
    grid.removeParticle(p)
    grid.addParticle(p)
  }
  let endup = Date.now();
  let up = 'Grid updated in ' + round(endup - startup, 2) + ' ms'

noFill()
  let startd = Date.now();
  for (let p of particles) {
    p.display();
  }
  let endd = Date.now();
  let dr = 'Draw in ' + round(endd - startd, 2) + ' ms'


  fill(0)
  text('Particles: ' + particles.length, 10, 10)
  text(coll, 10, 50);
  text(up, 10, 90);
  text(dr, 10, 130);
}

class Particle {
  constructor(pos) {
    this.pos = pos;
    this.velocity = createVector(random(-1.25, 1.25), random(-1.25, 1.25));
    this.acceleration = createVector(0, 0);

    this.mass = random(3, 10)
    this.radius = this.mass;
    this.maxSpeed = 10;
  }

  updateState(newPos) {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.maxSpeed); // Limit the particle's speed
    this.pos.add(this.velocity);
  }

  checkEdges() {
    if (this.pos.x - this.radius < 0) {
      this.pos.x = this.radius; // Prevent from leaving the canvas from the left side
      this.velocity.x *= -1;
    } else if (this.pos.x + this.radius > width) {
      this.pos.x = width - this.radius; // Prevent from leaving the canvas from the right side
      this.velocity.x *= -1;
    }

    if (this.pos.y - this.radius < 0) {
      this.pos.y = this.radius; // Prevent from leaving the canvas from the top
      this.velocity.y *= -1;
    } else if (this.pos.y + this.radius > height) {
      this.pos.y = height - this.radius; // Prevent from leaving the canvas from the bottom
      this.velocity.y *= -1;
    }
  }

  checkCollision(otherParticles) {
let counter = 0
    for (let other of otherParticles) {
      if (this != other) {
        let distance = this.pos.dist(other.pos);
        let minDistance = this.radius + other.radius + 1;

        if (distance <= minDistance) {
          // Calculate collision response
          let normal = p5.Vector.sub(other.pos, this.pos).normalize();
          let relativeVelocity = p5.Vector.sub(other.velocity, this.velocity);
          let impulse = p5.Vector.mult(normal, 2 * p5.Vector.dot(relativeVelocity, normal) / 2);

          // Apply repulsion force to prevent sticking
          let repulsion = p5.Vector.mult(normal, minDistance - distance + 2).mult(1);

          // Update velocities
          this.velocity.add(p5.Vector.div(impulse, this.mass));
          other.velocity.sub(p5.Vector.div(impulse, other.mass));

          // Apply repulsion force
          this.pos.sub(p5.Vector.div(repulsion, this.mass));
          other.pos.add(p5.Vector.div(repulsion, other.mass));
        }
counter++
      }
    }
return counter
  }

  display() {
    ellipse(this.pos.x, this.pos.y, this.radius * 2);
  }
}

/*
  The lookup grid
*/
class Grid {
  constructor(i, t, s) {
    (this.cellSize = s),
    (this.numCols = Math.ceil(i / s)),
    (this.numRows = Math.ceil(t / s)),
    (this.cells = []);
    for (let e = 0; e < this.numCols; e++) {
      this.cells[e] = [];
      for (let l = 0; l < this.numRows; l++) {
        this.cells[e][l] = [];
      }
    }
  }

  addParticle(i) {
    let t = Math.floor(i.pos.x / this.cellSize);
    let s = Math.floor(i.pos.y / this.cellSize);

    this.cells[t][s].push(i)
    i.gridCell = {
      col: t,
      row: s
    }
  }

  removeParticle(i) {
    let {
      col: t,
      row: s
    } = i.gridCell
    let e = this.cells[t][s];
    let l = e.indexOf(i);
    e.splice(l, 1);
  }

  determineCell(i) {
    let t = Math.floor(i.pos.x / this.cellSize);
    let s = Math.floor(i.pos.y / this.cellSize);
    return {
      col: t,
      row: s
    }
  }

  getNeighbors(particle) {
    let top_left = [
      floor((particle.pos.x - particle.radius) / this.cellSize),
      floor((particle.pos.y - particle.radius) / this.cellSize),
    ]

    let bottom_right = [
      floor((particle.pos.x + particle.radius) / this.cellSize),
      floor((particle.pos.y + particle.radius) / this.cellSize),
    ]

    let neighbors = []
    for (let i = top_left[0]; i <= bottom_right[0]; i++) {
      for (let j = top_left[1]; j <= bottom_right[1]; j++) {
        if (i < 0 || j < 0 || i >= this.numCols || j >= this.numRows) continue
        let c = this.cells[i][j]
        for (let p of c) {
          // don't add the particle itself
          if (p != particle) neighbors.push(p)
        }
      }
    }

    return neighbors
  }
}

jeudi 3 octobre 2024

json_exporter prometheus debug

 JSON url to convert : http://json_url/foobar.json

json_exporter running locally : localhost:7979 or json_exporter:7979 



Status of the json_exporter, does NOT contain the metrics from the targets, only related to the process itself (useful to know if it's up or not for example).

 localhost:7979/metrics


Check the result of a JSON URL to be converted / aka. what prometheus should scrape

curl http://json_url/foobar.json => JSON (original)

curl localhost:7979/probe?target=https://json_url/foobar.json => openmetrics converted by json_exporter



Other useful commands :


* edit, restart, wait, check json_exporter output

sudo vi /etc/json_exporter/config.yml && sudo systemctl restart json_exporter && sleep 5 && curl http://localhost:7979/probe?target=http://json_url/foobar.json


* edit, check and restart prometheus : 

sudo vi /etc/prometheus/prometheus.yml && sudo promtool check config /etc/prometheus/prometheus.yml && sudo systemctl restart prometheus.service