summaryrefslogtreecommitdiffstats
path: root/external/meta-updater/classes/image_types_ostree.bbclass
blob: 0b928a37e964595e3389322ba22a33a0855a5e93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# OSTree deployment
inherit features_check

REQUIRED_DISTRO_FEATURES = "usrmerge"

OSTREE_ROOTFS ??= "${WORKDIR}/ostree-rootfs"
OSTREE_COMMIT_SUBJECT ??= "Commit-id: ${IMAGE_NAME}"
OSTREE_COMMIT_BODY ??= ""
OSTREE_COMMIT_VERSION ??= "${DISTRO_VERSION}"
OSTREE_UPDATE_SUMMARY ??= "0"

BUILD_OSTREE_TARBALL ??= "1"

SYSTEMD_USED = "${@oe.utils.ifelse(d.getVar('VIRTUAL-RUNTIME_init_manager') == 'systemd', 'true', '')}"

IMAGE_CMD_TAR = "tar --xattrs --xattrs-include=*"
CONVERSION_CMD_tar = "touch ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}; ${IMAGE_CMD_TAR} --numeric-owner -cf ${IMGDEPLOYDIR}/${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}.tar -C ${TAR_IMAGE_ROOTFS} . || [ $? -eq 1 ]"
CONVERSIONTYPES_append = " tar"

TAR_IMAGE_ROOTFS_task-image-ostree = "${OSTREE_ROOTFS}"

python prepare_ostree_rootfs() {
    import oe.path
    import shutil

    ostree_rootfs = d.getVar("OSTREE_ROOTFS")
    if os.path.lexists(ostree_rootfs):
        bb.utils.remove(ostree_rootfs, True)

    # Copy required as we change permissions on some files.
    image_rootfs = d.getVar("IMAGE_ROOTFS")
    oe.path.copyhardlinktree(image_rootfs, ostree_rootfs)
}

do_image_ostree[dirs] = "${OSTREE_ROOTFS}"
do_image_ostree[prefuncs] += "prepare_ostree_rootfs"
do_image_ostree[depends] = "coreutils-native:do_populate_sysroot virtual/kernel:do_deploy ${INITRAMFS_IMAGE}:do_image_complete"
IMAGE_CMD_ostree () {
    for d in var/*; do
      if [ "${d}" != "var/local" ]; then
        rm -rf ${d}
      fi
    done

    # Create sysroot directory to which physical sysroot will be mounted
    mkdir sysroot
    ln -sf sysroot/ostree ostree

    mkdir -p usr/rootdirs

    mv etc usr/

    if [ -n "${SYSTEMD_USED}" ]; then
        mkdir -p usr/etc/tmpfiles.d
        tmpfiles_conf=usr/etc/tmpfiles.d/00ostree-tmpfiles.conf
        echo "d /var/rootdirs 0755 root root -" >>${tmpfiles_conf}
    else
        mkdir -p usr/etc/init.d
        tmpfiles_conf=usr/etc/init.d/tmpfiles.sh
        echo '#!/bin/sh' > ${tmpfiles_conf}
        echo "mkdir -p /var/rootdirs; chmod 755 /var/rootdirs" >> ${tmpfiles_conf}

        ln -s ../init.d/tmpfiles.sh usr/etc/rcS.d/S20tmpfiles.sh
    fi

    # Preserve OSTREE_BRANCHNAME for future information
    mkdir -p usr/share/sota/
    echo -n "${OSTREE_BRANCHNAME}" > usr/share/sota/branchname

    # home directories get copied from the OE root later to the final sysroot
    # Create a symlink to var/rootdirs/home to make sure the OSTree deployment
    # redirects /home to /var/rootdirs/home.
    rm -rf home/
    ln -sf var/rootdirs/home home

    # Move persistent directories to /var
    dirs="opt mnt media srv"

    for dir in ${dirs}; do
        if [ -d ${dir} ] && [ ! -L ${dir} ]; then
            if [ "$(ls -A $dir)" ]; then
                bbwarn "Data in /$dir directory is not preserved by OSTree. Consider moving it under /usr"
            fi
            rm -rf ${dir}
        fi

        if [ -n "${SYSTEMD_USED}" ]; then
            echo "d /var/rootdirs/${dir} 0755 root root -" >>${tmpfiles_conf}
        else
            echo "mkdir -p /var/rootdirs/${dir}; chown 755 /var/rootdirs/${dir}" >>${tmpfiles_conf}
        fi
        ln -sf var/rootdirs/${dir} ${dir}
    done

    if [ -d root ] && [ ! -L root ]; then
        if [ "$(ls -A root)" ]; then
            bbfatal "Data in /root directory is not preserved by OSTree."
        fi

        if [ -n "${SYSTEMD_USED}" ]; then
            echo "d /var/roothome 0700 root root -" >>${tmpfiles_conf}
        else
            echo "mkdir -p /var/roothome; chown 700 /var/roothome" >>${tmpfiles_conf}
        fi

        rm -rf root
        ln -sf var/roothome root
    fi

    if [ -d usr/local ] && [ ! -L usr/local ]; then
        if [ "$(ls -A usr/local)" ]; then
            bbfatal "Data in /usr/local directory is not preserved by OSTree."
        fi
        rm -rf usr/local
    fi

    if [ -n "${SYSTEMD_USED}" ]; then
        echo "d /var/usrlocal 0755 root root -" >>${tmpfiles_conf}
    else
        echo "mkdir -p /var/usrlocal; chown 755 /var/usrlocal" >>${tmpfiles_conf}
    fi

    dirs="bin etc games include lib man sbin share src"

    for dir in ${dirs}; do
        if [ -n "${SYSTEMD_USED}" ]; then
            echo "d /var/usrlocal/${dir} 0755 root root -" >>${tmpfiles_conf}
        else
            echo "mkdir -p /var/usrlocal/${dir}; chown 755 /var/usrlocal/${dir}" >>${tmpfiles_conf}
        fi
    done

    ln -sf ../var/usrlocal usr/local

    # Copy image manifest
    cat ${IMAGE_MANIFEST} | cut -d " " -f1,3 > usr/package.manifest
}

IMAGE_TYPEDEP_ostreecommit = "ostree"
do_image_ostreecommit[depends] += "ostree-native:do_populate_sysroot"
do_image_ostreecommit[lockfiles] += "${OSTREE_REPO}/ostree.lock"
IMAGE_CMD_ostreecommit () {
    if ! ostree --repo=${OSTREE_REPO} refs 2>&1 > /dev/null; then
        ostree --repo=${OSTREE_REPO} init --mode=archive-z2
    fi

    # Commit the result
    ostree_target_hash=$(ostree --repo=${OSTREE_REPO} commit \
           --tree=dir=${OSTREE_ROOTFS} \
           --skip-if-unchanged \
           --branch=${OSTREE_BRANCHNAME} \
           --subject="${OSTREE_COMMIT_SUBJECT}" \
           --body="${OSTREE_COMMIT_BODY}" \
           --add-metadata-string=version="${OSTREE_COMMIT_VERSION}" \
           ${EXTRA_OSTREE_COMMIT})

    echo $ostree_target_hash > ${WORKDIR}/ostree_manifest

    if [ ${@ oe.types.boolean('${OSTREE_UPDATE_SUMMARY}')} = True ]; then
        ostree --repo=${OSTREE_REPO} summary -u
    fi
}

IMAGE_TYPEDEP_ostreepush = "ostreecommit"
do_image_ostreepush[depends] += "aktualizr-native:do_populate_sysroot ca-certificates-native:do_populate_sysroot"
IMAGE_CMD_ostreepush () {
    # send a copy of the repo manifest to backend if available
    local SEND_MANIFEST=""
    # check if garage-push supports the --repo-manifest option before trying
    if $(garage-push --help | grep -q '^\s*--repo-manifest') && [ -f ${IMAGE_ROOTFS}${sysconfdir}/manifest.xml ]; then
        SEND_MANIFEST="--repo-manifest ${IMAGE_ROOTFS}${sysconfdir}/manifest.xml"
    fi

    if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
        if [ -e ${SOTA_PACKED_CREDENTIALS} ]; then
            garage-push --loglevel 0 --repo=${OSTREE_REPO} \
                        --ref=${OSTREE_BRANCHNAME} \
                        --credentials=${SOTA_PACKED_CREDENTIALS} \
                        --cacert=${STAGING_ETCDIR_NATIVE}/ssl/certs/ca-certificates.crt \
                        $SEND_MANIFEST
        else
            bbwarn "SOTA_PACKED_CREDENTIALS file does not exist."
        fi
    else
        bbwarn "SOTA_PACKED_CREDENTIALS not set. Please add SOTA_PACKED_CREDENTIALS."
    fi
}

IMAGE_TYPEDEP_garagesign = "ostreepush"
do_image_garagesign[depends] += "unzip-native:do_populate_sysroot"
# This lock solves OTA-1866, which is that removing GARAGE_SIGN_REPO while using
# garage-sign simultaneously for two images often causes problems.
do_image_garagesign[lockfiles] += "${DEPLOY_DIR_IMAGE}/garagesign.lock"
IMAGE_CMD_garagesign () {
    if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
        # if credentials are issued by a server that doesn't support offline signing, exit silently
        unzip -p ${SOTA_PACKED_CREDENTIALS} root.json targets.pub targets.sec tufrepo.url 2>&1 >/dev/null || exit 0

        java_version=$( java -version 2>&1 | awk -F '"' '/version/ {print $2}' )
        if [ "${java_version}" = "" ]; then
            bbfatal "Java is required for synchronization with update backend, but is not installed on the host machine"
        elif [ "${java_version}" \< "1.8" ]; then
            bbfatal "Java version >= 8 is required for synchronization with update backend"
        fi

        rm -rf ${GARAGE_SIGN_REPO}
        garage-sign init --repo tufrepo \
                         --home-dir ${GARAGE_SIGN_REPO} \
                         --credentials ${SOTA_PACKED_CREDENTIALS}

        ostree_target_hash=$(cat ${WORKDIR}/ostree_manifest)

        # Use OSTree target hash as version if none was provided by the user
        target_version=${ostree_target_hash}
        if [ -n "${GARAGE_TARGET_VERSION}" ]; then
            target_version=${GARAGE_TARGET_VERSION}
            bbwarn "Target version is overriden with GARAGE_TARGET_VERSION variable. This is a dangerous operation! See https://docs.ota.here.com/ota-client/latest/build-configuration.html#_overriding_target_version"
        elif [ -e "${STAGING_DATADIR_NATIVE}/target_version" ]; then
            target_version=$(cat "${STAGING_DATADIR_NATIVE}/target_version")
            bbwarn "Target version is overriden with target_version file. This is a dangerous operation! See https://docs.ota.here.com/ota-client/latest/build-configuration.html#_overriding_target_version"
        fi

        # Push may fail due to race condition when multiple build machines try to push simultaneously
        #   in which case targets.json should be pulled again and the whole procedure repeated
        push_success=0
        target_url=""
        if [ -n "${GARAGE_TARGET_URL}" ]; then
            target_url="--url ${GARAGE_TARGET_URL}"
        fi
        target_expiry=""
        if [ -n "${GARAGE_TARGET_EXPIRES}" ] && [ -n "${GARAGE_TARGET_EXPIRE_AFTER}" ]; then
            bbfatal "Both GARAGE_TARGET_EXPIRES and GARAGE_TARGET_EXPIRE_AFTER are set. Only one can be set at a time."
        elif [ -n "${GARAGE_TARGET_EXPIRES}" ]; then
            target_expiry="--expires ${GARAGE_TARGET_EXPIRES}"
        elif [ -n "${GARAGE_TARGET_EXPIRE_AFTER}" ]; then
            target_expiry="--expire-after ${GARAGE_TARGET_EXPIRE_AFTER}"
        else
            target_expiry="--expire-after 1M"
        fi

        for push_retries in $( seq 3 ); do
            garage-sign targets pull --repo tufrepo \
                                     --home-dir ${GARAGE_SIGN_REPO}
            garage-sign targets add --repo tufrepo \
                                    --home-dir ${GARAGE_SIGN_REPO} \
                                    --name ${GARAGE_TARGET_NAME} \
                                    --format OSTREE \
                                    --version ${target_version} \
                                    --length 0 \
                                    ${target_url} \
                                    --sha256 ${ostree_target_hash} \
                                    --hardwareids ${SOTA_HARDWARE_ID}
            if [ -n "${GARAGE_CUSTOMIZE_TARGET}" ]; then
                bbplain "Running command(${GARAGE_CUSTOMIZE_TARGET}) to customize target"
                ${GARAGE_CUSTOMIZE_TARGET} \
                    ${GARAGE_SIGN_REPO}/tufrepo/roles/unsigned/targets.json \
                    ${GARAGE_TARGET_NAME}-${target_version}
            fi
            garage-sign targets sign --repo tufrepo \
                                     --home-dir ${GARAGE_SIGN_REPO} \
                                     ${target_expiry} \
                                     --key-name=targets
            errcode=0
            garage-sign targets push --repo tufrepo \
                                     --home-dir ${GARAGE_SIGN_REPO} || errcode=$?
            if [ "$errcode" -eq "0" ]; then
                push_success=1
                break
            else
                bbwarn "Push to garage repository has failed, retrying"
            fi
        done
        rm -rf ${GARAGE_SIGN_REPO}

        if [ "$push_success" -ne "1" ]; then
            bbfatal "Couldn't push to garage repository"
        fi
    fi
}

IMAGE_TYPEDEP_garagecheck = "garagesign"
IMAGE_CMD_garagecheck () {
    if [ -n "${SOTA_PACKED_CREDENTIALS}" ]; then
        # if credentials are issued by a server that doesn't support offline signing, exit silently
        unzip -p ${SOTA_PACKED_CREDENTIALS} root.json targets.pub targets.sec tufrepo.url 2>&1 >/dev/null || exit 0

        ostree_target_hash=$(cat ${WORKDIR}/ostree_manifest)

        garage-check --ref=${ostree_target_hash} \
                     --credentials=${SOTA_PACKED_CREDENTIALS} \
                     --cacert=${STAGING_ETCDIR_NATIVE}/ssl/certs/ca-certificates.crt
    fi
}
# vim:set ts=4 sw=4 sts=4 expandtab: