Skip to content
This repository has been archived by the owner on Oct 16, 2020. It is now read-only.

Environment substitution for cloud-config #801

Closed
crawford opened this issue Sep 24, 2015 · 22 comments
Closed

Environment substitution for cloud-config #801

crawford opened this issue Sep 24, 2015 · 22 comments

Comments

@crawford
Copy link
Contributor

Issue by ForbiddenEra
Friday Mar 06, 2015 at 12:14 GMT
Originally opened as https://github.com/coreos/coreos-cloudinit/issues/325


I was trying to do something along the lines of..

fleet:
public-ip: \$private_ipv4 # used for fleetctl ssh command
metadata: bandwidth_limit=$BANDWIDTH_LIMIT,cpus=$PROCESSORS,host=$HOSTNAME,memory=$MEMORY,provider=$PROVIDER,region=$REGION,storage_size=$INTERNAL_STORAGE,storage_type=$INTERNAL_STORAGE_TYPE,role=$ROLE,block_storage_size=$BLOCK_STORAGE_SIZE,block_storage_type=$BLOCK_STORAGE_TYPE

With a "double init" where I init a basic system, run scripts to define all those variables, and then re-init with my real cloud-config..

I thought I could simply put them in /etc/environment and include that with my unit?

[Unit]
Description=Run coreos-cloudinit with actual cloud-config after environment has been set up
Requires=setup-environment.service
After=setup-environment.service

[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=/etc/environment
ExecStop=/usr/bin/true
ExecStart=/usr/bin/coreos-cloudinit --from-file=/opt/cloud-config.yml 

I'm guessing systemd doesn't do this (Kinda doing what's mentioned in https://github.com/coreos/coreos-cloudinit/issues/205 - the double init)

As a work around for now, I can have my scripts that set those variables push them to the cloud-init..

@crawford
Copy link
Contributor Author

Comment by crawford
Friday Mar 06, 2015 at 18:10 GMT


I think the variable names will be wrong. /etc/environment uses "COREOS_{PUBLIC,PRIVATE}_IPV4", but you are using "$private_ipv4" in your service (systemctl cat fleet.service will show the drop-in that coreos-cloudinit generates). I think this is what you want:

  fleet:
    public-ip: $COREOS_PUBLIC_IPV4 # used for fleetctl ssh command
    metadata: bandwidth_limit=$BANDWIDTH_LIMIT,cpus=$PROCESSORS,host=$HOSTNAME,memory=$MEMORY,provider=$PROVIDER,region=$REGION,storage_size=$INTERNAL_STORAGE,storage_type=$INTERNAL_STORAGE_TYPE,role=$ROLE,block_storage_size=$BLOCK_STORAGE_SIZE,block_storage_type=$BLOCK_STORAGE_TYPE

You shouldn't need to escape the variable because we don't do variable expansion sanely. The variable is only replaced if it exists (and this one doesn't).

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Friday Mar 06, 2015 at 18:36 GMT


Hi,

I need to escape it as it's part of a double-init.. The cloud config is delivered to the system within a writefiles inside another cloud-config - this, $public_ipv4 has to be escaped or else it's erased (replaced with an uninitialized value)! #205 states this, maybe you didn't read it but that's part of why I referenced it.

Also, AFAIK none of the variables are being substituted - $public_ipv4 is supposed to be done as part of cloud-init.

I'm pretty sure this functionality just doesn't exist - though, I think you are saying it does? - coreos-cloudinit does not seem use the environment to replace variables inside a cloud config.

systemctl cat fleet.service after the second init provides this:

# /run/systemd/system/fleet.service.d/20-cloudinit.conf
[Service]
Environment="FLEET_METADATA=bandwidth_limit=$BANDWIDTH_LIMIT,cpus=$PROCESSORS,host=$HOSTNAME,memory=$MEMORY,provider=$PROVIDER,region=$REGION,storage_size=$INTERNAL_STORAGE,storage_type=$INTERNAL_STORAGE_TYPE,role=$ROLE,block_storage_size
Environment="FLEET_PUBLIC_IP=172.22.2.46"

So, $public_ipv4 is being replaced - no other variables are. Is it perhaps because of how I'm doing the double-init? I'm passing the environment in the unit and running ExecStart=/usr/bin/coreos-cloudinit --from-file=/opt/cloud-config.yml - I assumed this would use the environment, replace the variables, and re-init - though, I guess, wouldn't the variables normally be replaced on the boot subsequently? If that's the case, how do I provide the dynamically environment to the next boot?

@crawford
Copy link
Contributor Author

Comment by crawford
Friday Mar 06, 2015 at 18:57 GMT


I misunderstood. You want to:

  1. write a cloud-config via coreos-cloudinit
  2. determine the public and private addresses via a script and write them to /my/file
  3. re-run coreos-cloudinit with /my/file sourced into the service

I accomplish this a bit differently, but you can apply the same concept to a cloud-config: https://gist.github.com/crawford/ed7e954bfddd3394c775.

To answer you original question (this is what threw me off), systemd does not do any of these substitutions. They are all performed by coreos-cloudinit. It is looking for COREOS_{PUBLIC,PRIVATE}_IPV{4,6}.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Friday Mar 06, 2015 at 19:03 GMT


Hi,

I'm trying to do substitutions for MORE than just the IP address - maybe I didn't make that clear? See the metadata line.

So you're saying.. coreos-cloudinit substitutes COREOS_{PUBLIC,PRIVATE}_IPV{4,6} with it's variable wherever ${public,private}_ipv{4,6} appears?

Actually, the IP address IS substituted as you can see from my systemctl cat fleet.service after it's init'd...

I'm suggesting that you should be able to provide OTHER environment variables and have them replaced in your cloud-config as well. I need to set more than IP dynamically, hostname, metadata (which will eventually pull from a metadata service)

Is there a way at all for even OEMs to achieve this?

@crawford
Copy link
Contributor Author

Comment by crawford
Friday Mar 06, 2015 at 19:41 GMT


So you're saying.. coreos-cloudinit substitutes COREOS_{PUBLIC,PRIVATE}_IPV{4,6} with it's variable wherever ${public,private}_ipv{4,6} appears?

Yes. Only these variables are substituted.

If you need other variables, you'll need to leverage systemd for variable substitution. I think it would be easiest if you ignored the magic that coreos-cloudinit provides with its partial address substitution. You can use coreos.units to add a drop-in for fleet, which is what coreos-cloudinit does for you. This will allow you to source any environment files you need and let systemd do the substitutions.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Friday Mar 06, 2015 at 22:07 GMT


Hi,

Thanks, that makes perfect sense and is probably easier than loading & substituting the file myself, though that's entirely possible since I'm scripting this all anyway - I just thought somehow, it would magically work - I was hoping the variable substitution in coreos-cloudinit was dynamic I guess.

However, I'm substituting variables throughout the entire cloud-config, not just the metadata for the node, creating files based off the host name and such. I'm not sure that option would work in this scenario.

I could see some advantages though if it were made that way, would you be against considering that? I could fork, test & maybe submit a PR.

In the meantime, I may try what you suggested, if you think it will work in my use case. If you wouldn't mind an example would be appreciated, though your time is anyway! Thanks.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Friday Mar 06, 2015 at 22:16 GMT


What I should have done, is posted my gist of exactly what I'm doing - I don't know why I didn't.

https://gist.github.com/ForbiddenEra/ee2d49b661c3066f4efd

#cloud-config

ssh_authorized_keys:
    - ssh-rsa key

write_files:
    - path: /opt/cloud-config.yml
      permissions: 0644
      content: |
        #cloud-config

        ssh_authorized_keys:
        - ssh-rsa key

        hostname: $HOSTNAME

        coreos:
          etcd:
            name: $HOSTNAME
            discovery: $DISCOVERY
            addr: \$private_ipv4:4001
            peer-addr: \$private_ipv4:7001
            election_timeout: 1500
            heartbeat_interval: 500
          fleet:
            public-ip: \$private_ipv4   # used for fleetctl ssh command
            metadata: bandwidth_limit=$BANDWIDTH_LIMIT,cpus=$PROCESSORS,host=$HOSTNAME,memory=$MEMORY,provider=$PROVIDER,region=$REGION,storage_size=$INTERNAL_STORAGE,storage_type=$INTERNAL_STORAGE_TYPE,role=$ROLE,block_storage_size=$BLOCK_STORAGE_SIZE,block_storage_type=$BLOCK_STORAGE_TYPE
          units:
            - name: etcd.service
              command: start
            - name: fleet.service
              command: start
            - name: rpc-statd.service
              command: start
              enable: true
            - name: example-setup.service
              command: start
              enable: true
              content: |
                [Unit]
                Requires=network-online.target
                Requires=docker.service
                After=network-online.target
                After=docker.service
                Description=Shaped Setup
                Documentation=http://shaped.ca

                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStartPre=/usr/bin/wget -N -P /opt/bin \
                  https://gist.githubusercontent.com/ForbiddenEra/../raw/.../volume-setup
                ExecStartPre=/usr/bin/chmod +x /opt/bin/volume-setup
                ExecStartPre=/usr/bin/wget -N -P /opt/bin \
                  https://gist.githubusercontent.com/ForbiddenEra/.../volume-unmount
                ExecStartPre=/usr/bin/chmod +x /opt/bin/volume-unmount
                ExecStartPre=/usr/bin/wget -N -P /opt/bin \
                  https://gist.githubusercontent.com/ForbiddenEra/../raw/.../volume-wait
                ExecStartPre=/usr/bin/chmod +x /opt/bin/volume-wait
                ExecStartPre=/usr/bin/update_engine_client -check_for_update
                ExecStart=/bin/bash -c 'echo x.x.x.x docker-registry > /etc/hosts'
            - name: volume-storage-online-wait.service
              command: start
              enabled: true
              content: |
                [Unit]
                Requires=shaped-setup.service
                Requires=network-online.target
                After=shaped-setup.service
                After=network-online.target
                Before=volume-storage.service
                Description=Wait for External Attached Volumes (for GlusterFS)
                Documentation=http://shaped.ca/

                [Service]
                Type=oneshot
                ExecStart=/opt/bin/volume-wait
                RemainAfterExit=yes 
            - name: volume-storage.service
              command: start
              enabled: true
              content: |
                [Unit]
                Requires=shaped-setup.service
                Requires=network-online.target
                Requires=volume-storage-online-wait.service
                After=shaped-setup.service
                After=network-online.target
                After=volume-storage-online-wait.service
                Description=Setup External Attached Volumes (for GlusterFS)
                Documentation=http://shaped.ca/ 

                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStart=/opt/bin/volume-setup
                ExecStop=/opt/bin/volume-unmount

                [X-Fleet]
                MachineMetadata=block_storage_type=attached 
            - name: swap-auto.service
              command: start
              enabled: true
              content: |
                [Unit]
                Description=Setup Swap Service
                Documentation=http://shaped.ca

                [Service]
                Type=oneshot
                Environment="SWAP_PATH=/swap" "SWAP_FILE=swap.fs"
                ExecStartPre=-/usr/bin/rm -rf ${SWAP_PATH}
                ExecStartPre=/usr/bin/mkdir ${SWAP_PATH}
                ExecStartPre=/usr/bin/touch ${SWAP_PATH}/${SWAP_FILE}
                ExecStartPre=/usr/bin/chattr +C ${SWAP_PATH}/${SWAP_FILE}
                ExecStartPre=/bin/bash -c "fallocate -l $(free -h --si|awk '/^Mem:/{print $2}') ${SWAP_PATH}/${SWAP_FILE}"
                ExecStartPre=/usr/bin/chmod 600 ${SWAP_PATH}/${SWAP_FILE}
                ExecStartPre=/usr/sbin/mkswap ${SWAP_PATH}/${SWAP_FILE}
                ExecStartPre=/usr/sbin/losetup -f ${SWAP_PATH}/${SWAP_FILE}
                ExecStart=/usr/bin/sh -c "/sbin/swapon $(/usr/sbin/losetup -j ${SWAP_PATH}/${SWAP_FILE} | /usr/bin/cut -d : -f 1)"
                ExecStop=/usr/bin/sh -c "/sbin/swapoff $(/usr/sbin/losetup -j ${SWAP_PATH}/${SWAP_FILE} | /usr/bin/cut -d : -f 1)"
                ExecStopPost=/usr/bin/sh -c "/usr/sbin/losetup -d $(/usr/sbin/losetup -j ${SWAP_PATH}/${SWAP_FILE} | /usr/bin/cut -d : -f 1)"
                ExecStopPost=-/usr/bin/rm -rf ${SWAP_PATH}
                RemainAfterExit=true
            - name: install-busybox.service
              command: start
              enable: true
              content: |
                [Unit]
                Requires=docker.service
                Requires=shaped-setup.service
                Requires=network-online.target
                After=network-online.target
                After=docker.service
                After=shaped-setup.service
                Description=Install BusyBox (for weave)
                Documentation=http://zettio.github.io/weave/

                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStart=/usr/bin/docker pull busybox:latest
            - name: install-pipework.service
              command: start
              enable: true
              content: |
                [Unit]
                Requires=docker.service
                Requires=shaped-setup.service
                Requires=network-online.target
                After=network-online.target
                After=docker.service
                After=shaped-setup.service
                Description=Install Pipework
                Documentation=https://github.com/jpetazzo/pipework
                Requires=docker.service

                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStartPre=/usr/bin/wget -N -P /opt/bin \
                  https://raw.github.com/jpetazzo/pipework/master/pipework
                ExecStartPre=/usr/bin/chmod +x /opt/bin/pipework
                ExecStart=/bin/echo Pipework Installed
            - name: install-weave.service
              command: start
              enable: true
              content: |
                [Unit]
                Requires=docker.service
                Requires=shaped-setup.service
                Requires=network-online.target
                After=network-online.target
                After=docker.service
                After=shaped-setup.service
                Description=Install Weave
                Documentation=http://zettio.github.io/weave/
                Requires=docker.service

                [Service]
                Type=oneshot
                RemainAfterExit=yes
                ExecStartPre=/usr/bin/wget -N -P /opt/bin \
                  https://raw.github.com/zettio/weave/master/weave 
                ExecStartPre=/usr/bin/chmod +x /opt/bin/weave
                ExecStartPre=/usr/bin/docker pull zettio/weave:latest
                ExecStart=/bin/echo Weave Installed
            - name: 50-docker.network
              runtime: false
              content: |
                [Match]
                Type=bridge
                Name=docker*

                [Network]
                Address=10.0.0.1/16
            - name: dhcp.network
              runtime: false
              content: |
                [Match]
                Name=eth*

                [Network]
                DHCP=v4
            - name: docker.netdev
              runtime: false
              content: |
                [NetDev]
                Name=docker0
                Kind=bridge
            - name: ethdocker.network
              runtime: false
              content: |
                [Match]
                Name=eth*

                [Network]
                Bridge=docker0
        write_files:
          - path: /home/core/.ssh/id_rsa.pub
            permissions: 0600
            content: |
              - ssh-rsa key
          - path: /home/core/.ssh/id_rsa
            permissions: 0600
            content: |
              -----BEGIN RSA PRIVATE KEY-----
              -----END RSA PRIVATE KEY-----
          - path: /etc/conf.d/nfs
            permissions: 0644
            content: |
              OPTS_RPC_MOUNTD=""
          - path: /home/core/nfs/example.mount
            permissions: 0644
            content: |
              [Mount]
              What=nfshost.domain.com:/vol2/data
              Where=/mnt/data
              Type=nfs
          - path: /etc/weave.$HOSTNAME.env
            permissions: 0644
            owner: core
            content: |
              WEAVE_LAUNCH_ARGS="-password key ip"
          - path: /home/core/units/weave.service
            permissions: 0644
            owner: core
            content: |
              [Unit]
              Description=Weave Network
              Documentation=http://zettio.github.io/weave/
              After=install-weave.service
              Requires=install-weave.service

              [Service]
              EnvironmentFile=/etc/weave.%H.env
              ExecStartPre=/opt/bin/weave launch $WEAVE_LAUNCH_ARGS
              ExecStart=/usr/bin/docker logs -f weave
              SuccessExitStatus=2
              ExecStop=/opt/bin/weave stop

              [X-Fleet]
              Global=true
    - path: /run/setup-environment.sh
      permissions: 0755
      content: |
        #!/bin/bash

        ENV="/etc/environment"

        # Test for RW access to $1
        touch $ENV
        if [ $? -ne 0 ]; then
            echo exiting, unable to modify: $ENV
            exit 1
        fi

        # Setup environment target
        sed -i -e '/^COREOS_PUBLIC_IPV4=/d' \
            -e '/^COREOS_PRIVATE_IPV4=/d' \
            "${ENV}"

        # We spin loop until the the IP addresses are set
        function get_iface_ip () {
            IF=$1
            IP=
            while [ 1 ]; do
                IP=$(ifconfig $IF | awk '/inet / {print $2}')
                if [ "$IP" != "" ]; then
                    break
                fi
                sleep .1
            done
            echo $IP  
        }

        function get_nat_ip () {
          while [ 1 ]; do
             _out=$(curl -s icanhazip.com)
             if [ -z "$_out" ]; then
               sleep 1
             else
               echo $_out
               exit
             fi
          done
        }
        # Echo results of IP queries to environment file as soon as network interfaces 
        # get assigned IPs
        echo getting private ipv4 from iface eth0..
        echo COREOS_PUBLIC_IPV4=$(get_iface_ip eth0) >> $ENV # Also assigned to same IP
        echo getting public-facing ipv4 from icanhazip.com..
        echo COREOS_PRIVATE_IPV4=$(get_nat_ip) >> $ENV #eno1 should be changed to your device name
    - path: /opt/bin/node-setup-environment
      permissions: 0755
      owner: root
      content: |
        #!/bin/bash

        ENV="/etc/environment"

        # Test for RW access to $1
        touch $ENV
        if [ $? -ne 0 ]; then
            echo exiting, unable to modify: $ENV
            exit 1
        fi        

        # we should be able to get most of this info from the
        # open-stack meta-data service, however, not every providor has that
        # so a. we can run our own meta-data service (lame)
        # b. we can script as much meta-data as we can (current, lame)
        # c. use openstack meta data (not so lame) but create our own elsewhere? (meh)
        # d. figure out something better

        mkdir -p /opt/bin
        cd /opt

        curl -s example.com/coreos/host.tgz | tar xvz

        LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/lib

        # get list of subdomains matching core from our domain
        host -l example.com example.com | grep ^core | cut -d ' ' -f 1,4 > /tmp/dns

        while read l; do
          _HOST=$(echo $l | cut -d ' ' -f 1)
          _IP=$(echo $l | cut -d ' ' -f 2)

          if [ $public_ipv4 == $_IP  ]; then
            echo Found my hostname: $_HOST
            echo HOSTNAME=$_HOST >> $ENV
          fi

        done < /tmp/dns

        rm /tmp/dns
        export CLUSTER='dev001'
        echo CLUSTER='dev001' >> $ENV
        echo DISCOVERY=$(curl -s http://example.com/coreos/$CLUSTER.discovery)
        echo PROCESSORS=$(nproc) >> $ENV 
        echo MEMORY=$(expr $(free -h --si|awk '/^Mem:/{print $2}' | cut -d '.' -f 1) \* 1024)  >> $ENV 
        #echo INTERNAL_STORAGE=$(df -h /dev/vda1 | grep vda1 | cut -d ' ' -f 9 | cut -d 'G' -f 1)  >> $ENV 
        echo INTERNAL_STORAGE=$(expr $(blockdev --getsize64 /dev/vda) / 1024 / 1024 / 1024)  >> $ENV 
        echo INTERNAL_STORAGE_TYPE=ssd  >> $ENV 
        echo BANDWIDTH_LIMIT=0  >> $ENV 
        echo BLOCK_STORAGE_SIZE=10 >> $ENV 
        echo BLOCK_STORAGE_TYPE=attached >> $ENV 
        echo PROVIDER=auro.io >> $ENV 
        echo REGION=ca-west >> $ENV 
    - path: /etc/resolv.conf
      permissions: 0644
      owner: root
      content: |
        nameserver 8.8.8.8
        nameserver 8.8.4.4
        domain example.com
hostname: intermediate
coreos:
  units:
    - name: setup-environment.service
      command: start
      runtime: true
      content: |
        [Unit]
        Description=Setup configuration environment with private (and public) IP addresses

        [Service]
        Type=oneshot
        RemainAfterExit=yes
        ExecStartPre=/run/setup-environment.sh
        ExecStart=/opt/bin/node-setup-environment
    - name: second-stage-cloudinit.service
      runtime: true
      command: start
      content: |
        [Unit]
        Description=Run coreos-cloudinit with actual cloud-config after environment has been set up
        Requires=setup-environment.service
        After=setup-environment.service
        #Requires=user-cloudinit-proc-cmdline.service
        #After=user-cloudinit-proc-cmdline.service

        [Service]
        Type=oneshot
        RemainAfterExit=yes
        EnvironmentFile=/etc/environment
        ExecStart=/usr/bin/true
        ExecStop=/usr/bin/coreos-cloudinit --from-file=/opt/cloud-config.yml

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Monday Mar 09, 2015 at 20:53 GMT


I've written my own app to do this, but would still be awesome if I could just pass an environment to coreos-cloudinit and and it and it would handle replacement.

@crawford
Copy link
Contributor Author

Comment by crawford
Monday Mar 09, 2015 at 21:01 GMT


Long term, I want to move people off of variable substitution in cloudinit and have them just use systemd instead. systemd is much better suited to do the substitution and it simplifies everything.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Monday Mar 09, 2015 at 21:31 GMT


I don't disagree with that idea, but how's that going to work?

If I need variables replaced in my Metadata for example.. I suppose I could make a drop in for fleet with my environment, but can I specify things like Metadata in the environment? 

It would be nice to not have to double init, but I'm not sure I see a way around that in this situ.

-------- Original message --------
From: Alex Crawford notifications@github.com
Date: 03-09-2015 3:01 PM (GMT-07:00)
To: coreos/coreos-cloudinit coreos-cloudinit@noreply.github.com
Cc: Jai Boudreau jason@shaped.ca
Subject: Re: [coreos-cloudinit] Environment substitution for cloud-config
(#325)

Long term, I want to move people off of variable substitution in cloudinit and have them just use systemd instead. systemd is much better suited to do the substitution and it simplifies everything.


Reply to this email directly or view it on GitHub.

@crawford
Copy link
Contributor Author

Comment by crawford
Monday Mar 09, 2015 at 21:36 GMT


As the system exists today, you are right, you need to double-init and there is no way around it. Right now I am working on a new config evaluation utility that runs before the system boots. This will remove the need for the double init and will leverage systemd for variable substitution instead of (incorrectly) attempting to do it.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Tuesday Mar 10, 2015 at 04:10 GMT


Hi,

Please keep me updated on this!

In the meantime, slightly on topic but out of scope, I've written some tools to do cloud-config templates..

First one (called starship), will substitute variables in your cloud-config (inside the double-init phase) with either the system environment, or an environment you specify (by command line or EnvironmentFile in it's unit) which is generated by the second tool.

The second tool (really just a script to gather metadata) waits on data sources (eg. waits for you to attach a public IP) before configuring the environment. The second tool Requires= and is After= the first tool, so your template wont be rendered before the data you require is available.

Also, in creating the template, I had the idea to use template directories, for example, you could create a directory structure like:

./                                      - template directory
./cloud-config.yml                      - main file (optional, contains #cloud-config header but would be added if doesn’t exist)
./hostname.yml                          - the hostname: section
./ssh_authorized_keys.yml               - the ssh_authorized_keys: section
./coreos.yml                            - the coreos: section, alternatively can use a drop in dir as below:
./coreos.d/                             - drop-ins for coreos: section
./coreos.d/etcd.yml                     - etcd: section
./coreos.d/fleet.yml                    - fleet: section
./coreos.d/units.d/                     - drop-ins for units: section
./coreos.d/units.d/docker.network.yml   - unit for docker network
./coreos.d/units.d/docker.service.yml   - unit for docker service
./coreos.d/units.d/docker.service.d/*   - drop-ins for docker service
./write_files.yml                       - any files you dont want to put as drop ins
./write_files.d/                        - drop in files for write_files directive
./write_files.d/etc/hosts               - an example drop in file

And, it would automatically generate a cloud-config based off that structure. Especially with write_files it would make it really easy to add files, especially binaries, as they would be added and generated as an encoded form.

Your thoughts?

@crawford
Copy link
Contributor Author

Comment by crawford
Tuesday Mar 10, 2015 at 23:33 GMT


Honestly? It's too complicated. Here is how I imagine "etcd.service.d/metadata.conf" looks:

[Service]
EnvironmentFile=/etc/environment
ExecStart=
ExecStart=/usr/bin/etcd --addr=${COREOS_PUBLIC_IPV4}:2379 --peer-addr=${COREOS_PRIVATE_IPV4}:2380

This way, cloud-init provides "/etc/environment", and "etcd.service" sources it. Let systemd do the substitutions for you.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Wednesday Mar 11, 2015 at 06:07 GMT


Ah, I understand what you mean now - and that makes sense, and I suppose the same could be done with fleet metadata instead of using the created helper units.

There's not much else in the cloud-config then that would really need this that wouldn't mostly be done in a unit I suppose, maybe hostname: except, that could be done in a unit just to set the hostname after - though - I personally find this not over-complicated and actually helpful - but I'm always open to other / new / hopefully better ways of doing things!

The idea of the directory structure still appeals to me though, I don't know if you were referencing that part or not, the rest does seem to be a little redundant / complicated though - though it would really, really depend on how much/what you were stuffing into your cloud-config - personally, I say why not use it to bootstrap everything you can possibly stuff in it - that's what it's meant for.

Thus, having directories I can easily drop in files/remove/copy for each cluster seems a lot easier than editing the file every time - technically less risky too.

Though - if your cloud-config is only a few lines or pulls a script that does the rest - it really is pointless.

Your final thoughts appreciated (and all other input too of course) otherwise - since I was trying to use something in a way it wasn't designed - this isn't really an issue - other than the double init problem which you say you are building a separate tool for, correct?

Feel free to close at your discretion.

@crawford
Copy link
Contributor Author

Comment by crawford
Wednesday Mar 11, 2015 at 18:31 GMT


I always envisioned cloud-init to be used for initial system bootstrapping. Setting up things like networking and mounting disks. The configuration of running services is best left to another tool (e.g. fleet, puppet, ansible). Unfortunately, cloud-init was given too much capability and magic early on and, given our auto-updating nature, we couldn't prune it back.

I'm confident that this new tool will make the distinction between machine configuration and application configuration more clear.

@crawford
Copy link
Contributor Author

Comment by ForbiddenEra
Thursday Mar 12, 2015 at 06:59 GMT


Awesome. However, on this issue I just noticed something - I should really look at the source but..

I just accidentally pasted my template instead of my bootstrap into a machine, one of the first lines is
hostname: ${HOSTNAME}

That gets replaced by my template script - however - this WAS replaced by something? coreos-cloudinit? It was using the hostname directly from the OpenStack metadata service somehow..

core@core0 ~ $ systemctl status oem-cloudinit
● oem-cloudinit.service - Cloudinit from EC2-style metadata
   Loaded: loaded (/run/systemd/system/oem-cloudinit.service; static)
   Active: failed (Result: exit-code) since Thu 2015-03-12 06:42:28 UTC; 9min ago
  Process: 519 ExecStart=/usr/bin/coreos-cloudinit --oem=ec2-compat (code=exited, status=1/FAILURE)
 Main PID: 519 (code=exited, status=1/FAILURE)

Mar 12 06:42:27 localhost coreos-cloudinit[519]: 2015/03/12 06:42:27 Parsing user-data as cloud-config
Mar 12 06:42:27 localhost coreos-cloudinit[519]: Failed to parse user-data: YAML error: line 22: did not find expected key
Mar 12 06:42:27 localhost coreos-cloudinit[519]: Continuing...
Mar 12 06:42:27 localhost coreos-cloudinit[519]: Processing cloud-config from meta-data
Mar 12 06:42:27 core0.shaped.ca.novalocal coreos-cloudinit[519]: 2015/03/12 06:42:27 Set hostname to core0.shaped.ca.novalocal
Mar 12 06:42:28 core0.shaped.ca.novalocal coreos-cloudinit[519]: 2015/03/12 06:42:28 Authorized SSH keys for core user
Mar 12 06:42:28 core0.shaped.ca.novalocal coreos-cloudinit[519]: 2015/03/12 06:42:28 Updated /etc/environment
Mar 12 06:42:28 core0.shaped.ca.novalocal systemd[1]: oem-cloudinit.service: main process exited, code=exited, status=1/FAILURE
Mar 12 06:42:28 core0.shaped.ca.novalocal systemd[1]: Failed to start Cloudinit from EC2-style metadata.
Mar 12 06:42:28 core0.shaped.ca.novalocal systemd[1]: Unit oem-cloudinit.service entered failed state.

The hostname SHOULD have been literally ${HOSTNAME} in this instance. Why was it replaced!? I thought the {public,private}_ip{4,6} variables were the only ones replaced..?

Mar 12 06:42:27 localhost coreos-cloudinit[519]: Processing cloud-config from meta-data

Also, where's (specifically) it getting this metadata, from the metadata service as it's set to --oem=ec2-compat?

Most of the metadata I need does come from the metadata service in this instance (though the flexibility is nice) - if all this time, I could have used any variables set in metadata, I could've saved significant time..

Though, some processing and maybe double-init still needed - at least, in this provider? as you can see, the domain is core0.shaped.ca.novalocal - .novalocal is appended and that would need to be removed...

Just noticed this by accident, I thought I even tried that before and it wasn't replaced.. odd?!

@crawford
Copy link
Contributor Author

Comment by crawford
Thursday Mar 12, 2015 at 18:58 GMT


When I ran your config, I get:

coreos-cloudinit[578]: 2015/03/12 18:42:39 Set hostname to $HOSTNAME

Is the config being processed by a shell at any point?

--oem=ec2-compat is expanded to --from-ec2-metadata=http://169.254.169.254/ --from-configdrive=/media/configdrive. This can be found here.

@crawford
Copy link
Contributor Author

Comment by jgriessen
Monday Apr 06, 2015 at 18:40 GMT


I've just tried writing a cloud config and to use variables and hit a wall -- http://pastie.org/10076641 I'd like to hear when your new tool is testable. Since ai was trying etcd2.0 in a container, maybe it's not the use case you see in the future though -- etcd2.0 will be "just there" and putting in a container not needed.

@crawford
Copy link
Contributor Author

Comment by JeanMertz
Friday Apr 17, 2015 at 13:07 GMT


I am also looking forward to this new way of bootstrapping coreos machines.

Currently we are doing things like:

  - name: etcd.service
    command: start
    drop-ins:
    - name: 50-public-ip.conf
      content: |
        [Service]
        EnvironmentFile=/etc/environment
        ExecStart=
        ExecStart=/usr/bin/env ETCD_ADDR=${COREOS_PRIVATE_IPV4}:4001      \
                               ETCD_PEER_ADDR=${COREOS_PRIVATE_IPV4}:7001 \
                               /usr/bin/etcd

but this will break of course, if the default ExecStart of Etcd ever changes in a CoreOS update.

@crawford
Copy link
Contributor Author

Comment by hookenz
Wednesday Apr 29, 2015 at 08:55 GMT


What about adding mustache support to cloud init? So the variables come from a file or url and are merged with cloud-config.yml using mustache tags. If you need things like ip addresses then you can write helpers.

@crawford
Copy link
Contributor Author

Comment by gbougeard
Wednesday Apr 29, 2015 at 12:18 GMT


👍

@crawford
Copy link
Contributor Author

Comment by crawford
Wednesday Sep 09, 2015 at 01:31 GMT


Ignition exists, for those interested. We won't be adding mustache support to the YAML configs. They are bad enough.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant