Conceptual Introduction
2023-04-03
concepts

How does the Peace framework shape automation to be resilient and provide a good user experience?

Automation Model

The following is a simplified model of what is used in the Peace framework, and is for teaching, not for accuracy.

  1. Imagine we have the following automation steps:

    1. Compile an application.
    2. Launch a server.
    3. Upload the application to the server.
    G cluster_a cluster_b cluster_c a a b b a->b a_text app compile c c b->c b_text server launch c_text file upload
  2. Each of these steps is a write function, connected together:

    fn app_compile()   -> _ { sh!("cargo build"); .. }
    fn srvr_launch()   -> _ { sh!("ec2 run-instances .."); .. }
    fn file_upload(..) -> _ { sh!("scp {src} user@{ip}:{dest}"); .. }
    
    // Invoke the functions
    let path = app_compile();
    let ip   = server_launch();
    let _    = file_upload(path, dest, ip);
    
  3. Instead of only having a write function for each step, we define read functions:

    The following show 3 functions for each step:

    1. Current state.
    2. Goal state.
    3. Logic to change the current state into the goal state.
    // Application
    fn app_last_ts(..) -> Time { sh!("stat -c '%Y' target/debug/app"); .. }
    fn app_src_ts(..)  -> Time { sh!("stat -c '%Y' **/*.rs"); .. }
    fn app_compile(..) -> _    { sh!("cargo build"); .. }
    
    // Server
    fn srvr_status(..) -> Srvr { sh!("ec2 describe-instances .."); .. }
    fn srvr_spec(..)   -> Srvr { sh!("cat server.yaml"); .. }
    fn srvr_launch(..) -> _    { sh!("ec2 run-instances .."); .. }
    
    // File Upload
    fn file_dest(..)   -> Md5Sum { sh!("ssh user@{ip} -C 'md5sum {dest}'"); .. }
    fn file_src(..)    -> Md5Sum { sh!("md5sum {src}"); .. }
    fn file_upload(..) -> _      { sh!("scp {src} user@{ip}:{dest}"); .. }
    
  4. With the current and goal states, we can calculate a diff, and whether we need to do work:

    // Application
    fn app_ts_diff(Time, Time)      -> TimeDiff { .. }
    fn app_compile_needed(TimeDiff) -> bool { .. }
    
    // Server
    fn srvr_diff(Srvr, Srvr)        -> SrvrDiff { .. }
    fn srvr_launch_needed(SrvrDiff) -> bool { .. }
    
    // File Upload
    fn file_diff(Md5Sum, Md5Sum)      -> Md5SumDiff { .. }
    fn file_upload_needed(Md5SumDiff) -> bool       { .. }
    
  5. Put these functions and data types into a trait:

    // Specific functions
    fn file_dest(..)                  -> Md5Sum { sh!("ssh user@{ip} -C 'md5sum {dest}'"); .. }
    fn file_src(..)                   -> Md5Sum { sh!("md5sum {src}"); .. }
    fn file_diff(Md5Sum, Md5Sum)      -> Md5SumDiff { .. }
    fn file_upload_needed(Md5SumDiff) -> bool       { .. }
    fn file_upload(..)                -> _      { sh!("scp {src} user@{ip}:{dest}"); .. }
    
    // Genericized grouping of step functions, and data.
    trait ItemSpec {
        type State;
        type StateDiff;
    
        fn state_current() -> Self::State;
        fn state_goal() -> Self::State;
        fn state_diff(Self::State, Self::State) -> Self::StateDiff;
        fn check(Self::StateDiff) -> bool;
        fn apply(Self::State, Self::State, Self::StateDiff);
    }
    
  6. Peace groups all the items into a graph:

    G a a b b a->b c c a->c d d b->d e e b->e c->e
  7. Peace provides commands to run different combinations of each item's functions:

    G cluster_ensure_exec apply cluster_clean_exec clean cluster_state_current current state cluster_state_goal goal state cluster_state_clean clean state ensure_exec_a ensure_exec_b ensure_exec_a->ensure_exec_b ensure_exec_c ensure_exec_a->ensure_exec_c ensure_exec_d ensure_exec_b->ensure_exec_d ensure_exec_e ensure_exec_b->ensure_exec_e ensure_exec_c->ensure_exec_e clean_exec_a clean_exec_b clean_exec_a->clean_exec_b clean_exec_c clean_exec_a->clean_exec_c clean_exec_d clean_exec_b->clean_exec_d clean_exec_e clean_exec_b->clean_exec_e clean_exec_c->clean_exec_e state_current_a state_current_b state_current_a->state_current_b state_current_c state_current_a->state_current_c state_goal_a state_clean_a state_current_d state_current_b->state_current_d state_current_e state_current_b->state_current_e state_current_c->state_current_e state_goal_b state_goal_a->state_goal_b state_goal_c state_goal_a->state_goal_c state_goal_d state_goal_b->state_goal_d state_goal_e state_goal_b->state_goal_e state_goal_c->state_goal_e state_clean_b state_clean_a->state_clean_b state_clean_c state_clean_a->state_clean_c state_clean_d state_clean_b->state_clean_d state_clean_e state_clean_b->state_clean_e state_clean_c->state_clean_e
    • StatesDiscoverCmd: Runs state_current for all items.
    • ApplyCmd: Given a target state, runs the following for all items:
      1. state_current
      2. state_goal
      3. diff
      4. check
      5. apply

Ending Note

Peace implements other concepts to provide resilience and good user experience, and these will be written in future posts.

Peace is still evolving, and is not ready for general adoption.

< Prev
Next >