# -*- mode:org; coding:utf-8; -*-

#+title: Demo 📦
#+language: fi
#+HTML_HEAD: <link rel="stylesheet" href="itka203_demo_custom.css" type="text/css" />


Docker esimerkkinä, eli pienen kuvatiedoston luominen ja kontin
käynnistys.

Hyöty: Pystyt tekemään demoja kotona laitteella kuin laitteella,
ainakin osittain.

* Dockerin perusrakenne ja toiminta

Docker on asiakas-palvelinohjelmisto, jossa asiakasohjelmalla ohjataan
Docker-palvelinta.  Docker-palvelin ajaa yhtä tai useampaa konttia.

Kontti on eristetty prosessi, joka suoritetaan Docker-palvelinta
ajavassa tietokoneessa.  Kontin sisältämät ohjelmat ja data ovat
muuttuvasisältöistöisessä tiedostossa, jota myös monesti kutsutaan
kontiksi.

Kontti luodaan kuvatiedoston perusteella.  Kuvatiedosto sisältää
kontin tarvitsemat tiedostot alkutilassaan, muuttumattomassa
tiedostossa, josta sitten luodaan kontti.  Kuvatiedosto taas luodaan
koontitiedoston avulla.

Koontitiedosto, jota kutsutaan yleensä sen nimellä Dockerfile,
sisältää komennot, joiden ohjaamana Docker-järjestelmä luo
kuvatiedoston.

Docker-asiakasohjelmalla sitten käsketään Docker-palvelinta luomaan
kontti.  Luotu kontti myös käynnistetään asiakasohjelmistolla.  Kontti
pyörii rajoitussa ja suljetussa prosessissa.  Kontin sisällölle
prosessi näyttää sille, kuin se olisi ainut tietokoneessa pyörivä
käyttöjärjestelmä.  Yleensä kontteja ajetaan useita
samanaikaisesti yhdessä Docker-palvelimessa.

Kontin käyttöjärjestelmä on yleensä sama kuin käyttöjärjestelmä, missä
Docker-palvelinta ajetaan.  Oletuksena tämä on Linux.  Jos
käyttöjärjestelmä poikkeaa Linuxista, sitä joudutaan emuloimaan
virtuaalikoneen avulla.  Näin esimerkiksi Windowsissa ajetaan
Docker-palvelimen kontteja emulaattorissa.  Jos kontin vaatima ja
Docker-palvelimen ajoympäristön käyttöjärjetelmä on sama, selvitään
usein kevyemmällä virtualisoinnilla, jossa ohjelmat ajetaan
tietokoneen omalla prosessorilla.  Jos taas prosessoriarkkitehtuurit
eriävät, esimerkiksi ajetaan AMD64-konttia ARM-prosessorilla
toteutetussa Raspberry Pi -tietokoneessa tai Applen ARM-pohjaista
prosessoria käyttävillä tietokoneilla, joudutaan emuloimaan myös
AMD64-prosessoria, mikä hidastaa kontin toimintaa.  Koska kuitenkin
Dockerfile on monesti tarjolla, voidaan jokaiselle
prosessoriarkkitehtuurille ja ajoympäristön käyttöjärjestelmälle tehdä
omat kuvatiedostonsa.

Ajettaessa Linux-lähtöisiä Docker-kontteja muissa käyttöjärjestelmissä
joudutaan emuloimaan Linuxia virtuaalikoneen kanssa.  Windowsille ja
macOS:lle saatavilla oleva Docker Desktop on sovellus, joka yhdistää
tämän virtuaalikoneen Dockerin asiakasohjelmistoon ja palvelimeen.
Tästä käytetään myös termiä /Docker engine/, moottori, joka ehkä suomentuisi
paremmin sanalla /järjestelmä/.  Myös Linuxille löytyy Docker Desktop ja
se käyttää myös virtuaalikonetta palvelimen ajamiseen.

Linux ei kuitenkaan tarvitse virtuaalikonetta, sehän vain ajaisi
virtuaalikoneessa itseään, joten Linuxille on olemassa myös
Docker-järjestelmä nimeltä Docker CE.  Se on Linuxissa kevyempi
käyttää resursseiltaan kuin Docker Desktop on.

Huh, selvennetään tuo: Docker-järjestelmässä, kuten Docker Desktop tai
Docker CE, voi ajaa kontteihin rajoitettuja ohjelmistoja palvelimella.
Palvelinta voi ohjata asiakasohjelmistosta.  Docker-kontti luodaan
Docker-kuvatiedoston perusteella.  Docker-kuvatiedosto taas
rakennetaan Dockerfile-tiedoston ohjeiden mukaan.  Ajamisen ja
rakentamisen voi kätevästi hoitaa Docker-järjestelmän, kuten Docker
Desktop, avulla.

(Selkenikö?  Kommentteja vastaanotetaan Vertaistukikanavalla Moodlessa!)

* Koontitiedosto, eli Dockerfile

Koontitiedosto ohjaa Docker-järjestelmää kuvatiedoston rakentamisessa.

Demojen kannalta kuvatiedostoon tarvitaan seuraavia osia:
 - Käyttöjärjestelmä: Linux-jakelu, kokeillaan Alpinea.
 - Bash yms. perustyökalut
 - GCC C-kielen kääntämiseen
 - Gnu Assembler konekielikäännöksiin
 - GDB virheiden jäljitykseen
 - make kääntämisen ja rakentamisen automatisointiin
 - Editori (Emacs ja Vim) lähdekoodin muokkaamiseen
 - hexdump binääritiedoston sisällön tutkimiseen
 - git tottakai
 - SSH-palvelin yhteydenottoon kontin sisälle
 - curl, wget tiedostojen hakemiseen kontin ulkopuolelta
 - locales lokalisointiin
 - man-sivut kaikelle mahdolliselle
 - EI GUI-kilkkeitä, säästetään tilaa!

Dockerfile koostuu komennoista, joilla kuvatiedoston luontia ohjataan.
Demoissa käytämme seuraavia komentoja:
 - FROM valitsee valmiin kuvatiedoston uuden pohjaksi
 - RUN ajaa komennon kuvatiedoston valmistamiseksi
 - EXPOSE avaa portin sisääntulevalle tietoliikenteelle
 - CMD määrittää komennon, joka ajetaan, kun kuvatiedoston
   perusteella luodaan ja käynnistetään kontin ajo

** Sopiva Dockerfile?
#+begin_src dockerfile :file Dockerfile.itka203 :export no
  # -*- coding: utf-8 -*-
  # Lähdetään liikkeelle kevyestä Linux-jakelusta
  FROM alpine

  # --no-cache --update  lisää alle jokaisen add jälkeen, jos aina haluat ladata kaiken

  # Ladataan demojen kannalta tarvittavat paketit.
  # Voisi ehkä olla hyvä, jos olisivat yhdellä rivillä,
  # mutta näin on hieman selkeämpää.

  # Käyttöjärjestelmän perusosat
  RUN apk add bash binutils binutils-doc openrc procps
  RUN apk add musl musl-utils musl-locales tzdata
  RUN apk add coreutils coreutils-doc less less-doc
  RUN apk add mandoc mandoc-doc man-pages man-pages-posix
  RUN apk add texinfo texinfo-doc

  # C ja Assembler -kehitys
  RUN apk add make make-doc gcc gcc-doc gdb gdb-doc musl-dev make make-doc

  # Tietoliikennelisät
  RUN apk add openssh openssh-doc openssl openssl-doc ca-certificates
  RUN apk add curl curl-doc wget wget-doc

  # Demojen tekemisen kannalta tarpeelliset lisät ja editorit
  RUN apk add screen screen-doc  emacs-nox emacs-doc vim vim-doc unzip unzip-doc

  # Varmistetaan turvallisuuspäivitykset.
  RUN apk update && apk upgrade

  # Kello oikeaan aikaan
  ENV TZ=Europe/Helsinki
  RUN cp /usr/share/zoneinfo/Europe/Helsinki /etc/localtime
  # TODO Ei pelitä RUN ln -shf /usr/share/zoneinfo/Europe/Helsinki /etc/localtime

  # Luodaan käyttäjä ja asetetaan salasana
  RUN adduser -D -s /bin/bash user
  RUN echo "user:kissa123" | chpasswd

  # XXX  HUOMAA JULKINEN SALASANA, eli älä tee näin tuotannossa!  XXX

  # Asetetaan oikeudet  (tarvitaanko, jos lisätään demot tai muuta sälää?)
  # RUN chown -R user:user /home/user

  # SSHd alpinen ehdoin...
  # lähde: https://github.com/oadiazm/alpine-ssh/blob/master/Dockerfile

  RUN sed -i s/#PermitRootLogin.*/PermitRootLogin\ yes/ /etc/ssh/sshd_config \
    && echo "root:root" | chpasswd \
    && rm -rf /var/cache/apk/* \
    && sed -ie 's/#Port 22/Port 22/g' /etc/ssh/sshd_config \
    && sed -ri 's/#HostKey \/etc\/ssh\/ssh_host_key/HostKey \/etc\/ssh\/ssh_host_key/g' /etc/ssh/sshd_config \
    && sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_rsa_key/HostKey \/etc\/ssh\/ssh_host_rsa_key/g' /etc/ssh/sshd_config \
    && sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_dsa_key/HostKey \/etc\/ssh\/ssh_host_dsa_key/g' /etc/ssh/sshd_config \
    && sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_ecdsa_key/HostKey \/etc\/ssh\/ssh_host_ecdsa_key/g' /etc/ssh/sshd_config \
    && sed -ir 's/#HostKey \/etc\/ssh\/ssh_host_ed25519_key/HostKey \/etc\/ssh\/ssh_host_ed25519_key/g' /etc/ssh/sshd_config \
    && /usr/bin/ssh-keygen -A \
    && ssh-keygen -t rsa -b 4096 -f  /etc/ssh/ssh_host_key

  # SSH:n portti auki
  EXPOSE 22

  # SSHD käyntiin
  CMD ["/usr/sbin/sshd","-D"]

  # SSHD:n pyöriessä edustalla ei konttikaan sammu, ennen kuin se sammutetaan.
#+end_src

** Kuvatiedoston luonti

Kuvatiedosto sisältää kaiken sen, mitä koontitiedostossa on pyydetty
sinne asetettavan, sekä mahdolliset lisämuutokset ja asetukset, kuten
vaikka aikavyöhykkeen ja SSHD:n asetukset yllä.  Kuvatiedoston avulla
Docker-järjestelmä luo myöhemmin Docker-kontin.  Kuvatiedostoa ei
muuteta sen luomisen jälkeen, vaan mahdolliset ajonaikaiset muutokset
ovat kontissa.

Kuvatiedoston luot ajamalla hakemistossa, missä haluamasi Dockerfile
sijaitsee, seuraavan komennon:

~docker build -t kuvan-nimi -f Dockerfile .~

Varmista, että olet käynnistänyt Docker-palvelimen ennen kuin annat
tämän komennon komentoriviltä.  Selkein tapa saada Docker-palvelin
käyntiin on käynnistää Docker Desktop.

Kuvatiedosto luodaan Docker-järjestelmään, ei siihen hakemistoon, missä
ajoit kuvatiedoston Dockerfile-tiedostosta rakentavan komennon.

** Dockerin käynnistys

Docker Desktop -ohjelman avulla näet kuvatiedostot, mitä
Docker-järjestelmässäsi on tarjolla.  Docker Destopista käsin pystyt
myös käynnistämään kontteja Docker-palvelimessa.  Docker-palvelin
pyörii pyörii taustalla ja sille kommunikoidaan asiakasohjelmistojen,
kuten Docker Desktop tai yllä käytetty komentoriviohjelma ~docker~,
avulla.

Valitse oikea kuvatiedosto, klikkaa kuvan ajonappia, jonka
symboli (▶️) on kuten soitto-painikkeessa esim. nauhureissa,
musiikinsoitto-ohjelmissa, Blu-ray soittimessa.

Muista käynnistyksen yhteydessä avautuvasta valintaikkunasta asettaa
porttiohjaus (Optional Settings → Ports → Host port) lokaalista portista 2222
kontin portiin 22!

Saman voi tehdä komentoriviltä komentamalla:  ~docker run -p 2222:22 kuvan-nimi:latest~

Jep, tuo komentorivin ~docker~-ohjelma keskustelee ja teettää työt
Docker-järjestelmällä.

** Käyttö

Kun Docker-kontti on käynnistetty Docker Desktopissa, voit käyttää
sitä yksinkertaisesti ottamalla SSH:lla yhteys lokaaliin porttiin
2222: ~ssh -p 2222 user@localhost~

Oletettavasti sinulla on ssh tai joku vastaava, kuten kitty tai putty
asennettuna Windowsissa jo aiempien kurssien johdosta.  Linux ja macOS
sisältävät SSH:n jo oletuksena.

Windowsissa voi tehokäyttäjän olla järkevää asentaa /Windows Subsystem
for Linux/, jossa tulee mukana POSIX- ja Unix-ympäristö komentoineen.
Koska Docker on Windowsissa toteutettu virtuaalikoneen avulla, WSL:stä
SSH:lla yhteyden käynnistäessäsi Docker-konttiin olet tavallaan
ajamassa kahta eri virtuaalikonetta Windowsissasi.  Otat vain toisesta
yhteyden toiseen.

** Assembler-demot ja jokin muu kuin AMD64-suoritin

Kurssilla käytetään AMD64-arkkitehtuuria, joten jos sinulla on
käytössäsi tietokone, jossa on jokin muu suoritin, on
Assembler-demojen tekeminen kontissa hieman haastavampaa.  Yleensä
tämä suoritin lienee ARM-pohjainen ja tietokone esim. Raspberry Pi tai
Applen AppleSilicon-Macit.

Tällöin sinulla on kaksi vaihtoehtoa: joko käytät itsetekemääsi kuvaa
ja muutat mielessäsi AMD64:n ARM-assembleriksi tai sitten käytät
emuloituna AMD64-kuvaa, joka on tarjolla alla ilmoitetussa
osoitteessa.  Tai sitten et vaan tee tehtäviä konttia apuna käyttäen.

Debuggerin käyttö kontissa, jos ajat AMD64:lle tehtyä konttia muussa
kuin AMD64-arkkitehtuurissa, on aivan liian hankalaa ja monimutkaista
tälle kurssille.  Käytä vuonna 2025 yhteiskäyttökoneita viimeisissä
demoissa, ehkä jonain tulevana vuonna tilanne paranee.


* Tehtävät, osa 1

 1. Hae Docker Desktop ja asenna se käyttöjärjestelmäsi ohjeiden
  mukaan.
 2. Hae yllä oleva Dockerfile-tiedosto osoitteesta
    https://itka203.it.jyu.fi/demovedokset/p/Dockerfile tai kopioi se tuosta yläpuolelta.
 3. Luo docker-kuvatiedosto yllä olevien ohjeiden mukaisesti.
 4. Aja docker-kontti Docker Desktopista tai komentoriviltä.
 5. Ota yhteys SSH:n avulla kontin sisälle.
 6. Suorita niin monta demoa kuin pystyt.  Kirjoita raporttiin kuvaus
  jokaisesta, jossa kerrot onnistuiko vai ei demon tekeminen.  Jos ei
  onnistu, liitä mukaan selitys, mikä ei toiminut.  Myös
  korjausehdotukset otetaan ilolla vastaan.

Voit tehdä tämän samalla kun teet demoja tai vasta sen jälkeen, kun
olet tehnyt demot.

** Valmis kuvatiedosto

Vaihtoehtoisesti voit hakea valmiin n. 200 megatavun kuvatiedoston
osoitteesta https://itka203.it.jyu.fi/demovedokset/p/itka203demo.img ja vain ajaa
sen, korvaten täten yltä kohdat 2-3.

Kuvatiedostosta on kuitenkin hyötyä vain, jos haluat ajaa
AMD64-ohjelmia tietokoneessa, jossa on jokin toinen
prosessoriarkkitehtuuri.  Tällöin pääset näkemään ja ajamaan AMD64:n
konekieltä, kuten kurssin monisteessa on käytetty,
esim. ARM-pohjaisessa tietokoneessa.  Valitettavasti debuggaaminen ei
onnistu helposti, joten sitä et voi kokeilla tämän kuvatiedoston
avulla.  Muuten voit kyllä ohjelmia kehittää ja ajaa, myös AMD64:n
Assembler-kielellä.

* Tehtävät, osa 2

Tulossa.
#+begin_comment
Jotain tyyliin kun syöttää aikavyöhykkeen, se palauttaa tämän
hetken päivän ja kellonajan siellä.
#+end_comment

* Muutokset

Tässä on listattuna muutokset, mitä ensimmäisenä julkaistuun
Dockerfileen on tullut.  Muutoksia voi tehdä myös jälkeenpäin,
ohjeet tuossa alla.

Jokaista muutosta ennen ja välillä muutenkin kannattanee kuitenkin
ajaa ylläpitäjänä (root) seuraavat komennot: ~apk update && apk
upgrade~

Pakettidemojen kannalta tuo ~apk~ on helpointa ajaa Docker Desktopista
käsin kontin terminaalissa.  Kun klikkaat kontin riviltä kolmea
päällekkäin olevaa pistettä, ⋮ , sieltä löytyy valinta ~Open
terminal~, joka aukaisee terminaalin.  Tässä terminaalissa olet
oletuksena ylläpitäjä, joten ~apk~-komennon ajaminenkin
onnistuu.[fn:1]

Kuvatiedosto ei välttämättä päivity samantien, vaan välissä voi olla
päiviäkin, jos muutos on pieni.  Voit aina korjata kontin alla
olevilla ohjeilla.

** Paketteihin lisätty procps, jotta ps toimii kunnolla (2025-03-27)

Voit jälkikäteen root-käyttäjänä lisätä tuon ajamalla komennon ~apk
add procps~

* Footnotes

[fn:1] Hyvä idea, mutta ei, sinun ei kannata käyttää tätä terminaalia
normaaliin käyttöön, vain ylläpitäjän toimiin!


* COMMENT Aputyökalut                                              :noexport:
# (setq org-confirm-babel-evaluate nil)

#+BEGIN_SRC elisp
    (defun my-push-zip-scp-pop  (pplist)
      (shell-command "pushd /tmp && zip -v -r itka203-org.zip itka203/* && scp itka203-org.zip itka203.it.jyu.fi:itka203-org-2025.zip && popd"))

    (defun rsync-html (pplist)
      (let ((bdir (plist-get pplist :publishing-directory)))
        (shell-command
          (concat "rsync -aq  -- " bdir "*.html " ; -e 'ssh -J nazrat.it.jyu.fi'
                  "itka203.it.jyu.fi:/var/www/html/demovedokset/p/"))))


    ;;; tehdään sitten joskus fiksummin
    (defun rsync-img (pplist)
      (let ((bdir (plist-get pplist :base-directory)))
        (shell-command
          (concat "rsync -aq -- " bdir "*.png "
                  "itka203.it.jyu.fi:/var/www/html/demovedokset/p/"))))
      ; *.jpg "


    (setq org-confirm-babel-evaluate nil)
    ;; (setq org-latex-packages-alist '(("" "tikz")))
    (setq org-publish-project-alist
          (list
           `("itka203-org"
             :language "fi"
             :base-directory ,default-directory
             :base-extension "org"
             :publishing-directory "/tmp/itka203/" ;;; ,(concat default-directory "pub/") ;; "/ssh:user@host:~/html/notebook/"
             :publishing-function org-html-publish-to-html; (org-html-publish-to-tufte-html

                                   ;;; org-latex-publish-to-pdf
                                   ;;; org-org-publish-to-org
                                   ;)
             :exclude "*" ;;; <- KLUDGE "itka203-[ihl].*\.org"   ;; regexp
  	   :include "d#_docker.html" ;;; <- KLUDGE
             :html-html5-fancy t
             :html-doctype "html5"
             :html-validation-link nil
             :with-timestamp t
             :with-toc t
             :with-author nil
             :with-creator nil
             :with-email nil
             :html-head "<link rel=\"stylesheet\" href=\"itka203_demo_custom.css\" type=\"text/css\" />"
             :headline-levels 3
             :section-numbers nil
             :auto-sitemap nil
             :sitemap-title "Sivuston sivut"
             :sitemap-ignore-case t
             ;; :style "<link rel=\"stylesheet\"
             ;;               href=\"../other/mystyle.css\" type=\"text/css\"/>"
             :html-preamble nil
             ;; :completion-function my-push-zip-scp-pop
             :completion-function rsync-html
             )

           `("itka203-images"
             :base-directory ,default-directory
             :base-extension "jpg\\|png"
             :publishing-directory "/tmp/itka203/" ;; ,(concat default-directory "pub/") ;; "/ssh:user@host:~/html/images/"
             :publishing-function org-publish-attachment
             :completion-function rsync-img
             ;; :completion-function (lambda () (shell-command
             ;;       (concat "rsync -av /tmp/itka203/*.png /tmp/itka203/*.jpg "
             ;;               "halava.cc.jyu.fi:www/opetus/itka203/"
             ;;            ;; "itka203.it.jyu.fi:kurssit/itka203/html/"
             ;;            ;; "/tmp/www-itka203/"
             ;;            )))
             )

           `("itka203-html"
             :language "fi"
             :base-directory ,default-directory
             :publishing-directory "/tmp/itka203-html/"
             :publishing-function org-html-publish-to-html ; ox-slimhtml-publish-to-html
             :exclude "*" ;;; KLUDGE "itka203-[ihl].*\.org"   ;; regexp
  	   :include "d#_docker.html" ;;; KLUDGE
             :html-html5-fancy t
             :html-doctype "html5"
             :html-validation-link nil
             :with-timestamp t
             :with-toc t
             :with-author nil
             :with-creator nil
             :with-email nil
             :html-head-include-default-style nil
             :html-head "<link id=\"hed\" rel=\"stylesheet\" href=\"itka203_demo_custom.css\" type=\"text/css\" />"
             #:style "<link id=\"sty\" rel=\"stylesheet\" href=\"css/tufte.css\" type=\"text/css\" />"
             :headline-levels 3
             :section-numbers nil
             :auto-sitemap nil
             :sitemap-title "Sivuston sivut"
             :sitemap-ignore-case t)
           ;; (list "itka203-esimerkki"
           ;;       :base-directory (concat default-directory "itka203-esimerkki/")
           ;;       :recursive t
           ;;       :base-extension "html\\|jpg\\|png\\|wmf\\|txt\\|xml\\|htaccess"
           ;;       :publishing-directory "/tmp/itka203/itka203-esimerkki/" ;; (concat default-directory "pub/itka203-esimerkki/")
           ;;       :publishing-function 'org-publish-attachment
           ;;       ;; :completion-function #'(lambda () (shell-command
           ;;       ;;                                    (concat "rsync -av /tmp/itka203/itka203-esimerkki/* "
           ;;       ;;                                      "halava.cc.jyu.fi:www/opetus/itka203/itka203-esimerkki"
           ;;       ;;                                      ;; "/tmp/www-itka203/itka203-esimerkki/"
           ;;                                            ))))
           ;;       ("other"
           ;;        :base-directory "~/other/"
           ;;        :base-extension "css\\|el"
           ;;        :publishing-directory "/ssh:user@host:~/html/other/"
           ;;        :publishing-function org-publish-attachment)
           '("itka203" :components ("itka203-org" "itka203-images"))  ; "itka203-esimerkki" ))
    ))
#+END_SRC

#+RESULTS:
| itka203-org    | :language       | fi                                                                        | :base-directory | /Users/ji/Documents/opetus/itka203-kurssimateriaali-avoin/2015/demot/tim/ | :base-extension       | org                   | :publishing-directory | /tmp/itka203/            | :publishing-function   | org-html-publish-to-html | :exclude  | *              | :include          | d#_docker.html | :html-html5-fancy | t     | :html-doctype         | html5 | :html-validation-link | nil | :with-timestamp | t | :with-toc    | t   | :with-author  | nil | :with-creator | nil | :with-email                      | nil | :html-head | <link rel="stylesheet" href="itka203_demo_custom.css" type="text/css" />          | :headline-levels |                                                                       3 | :section-numbers | nil | :auto-sitemap    | nil | :sitemap-title | Sivuston sivut | :sitemap-ignore-case | t              | :html-preamble       | nil | :completion-function | rsync-html |
| itka203-images | :base-directory | /Users/ji/Documents/opetus/itka203-kurssimateriaali-avoin/2015/demot/tim/ | :base-extension | jpg\                                                                      | png                   | :publishing-directory | /tmp/itka203/         | :publishing-function     | org-publish-attachment | :completion-function     | rsync-img |                |                   |                |                   |       |                       |       |                       |     |                 |   |              |     |               |     |               |     |                                  |     |            |                                                                                   |                  |                                                                         |                  |     |                  |     |                |                |                      |                |                      |     |                      |            |
| itka203-html   | :language       | fi                                                                        | :base-directory | /Users/ji/Documents/opetus/itka203-kurssimateriaali-avoin/2015/demot/tim/ | :publishing-directory | /tmp/itka203-html/    | :publishing-function  | org-html-publish-to-html | :exclude               | *                        | :include  | d#_docker.html | :html-html5-fancy | t              | :html-doctype     | html5 | :html-validation-link | nil   | :with-timestamp       | t   | :with-toc       | t | :with-author | nil | :with-creator | nil | :with-email   | nil | :html-head-include-default-style | nil | :html-head | <link id="hed" rel="stylesheet" href="itka203_demo_custom.css" type="text/css" /> | style            | <link id="sty" rel="stylesheet" href="css/tufte.css" type="text/css" /> | :headline-levels |   3 | :section-numbers | nil | :auto-sitemap  | nil            | :sitemap-title       | Sivuston sivut | :sitemap-ignore-case | t   |                      |            |
| itka203        | :components     | (itka203-org itka203-images)                                              |                 |                                                                           |                       |                       |                       |                          |                        |                          |           |                |                   |                |                   |       |                       |       |                       |     |                 |   |              |     |               |     |               |     |                                  |     |            |                                                                                   |                  |                                                                         |                  |     |                  |     |                |                |                      |                |                      |     |                      |            |