Go to main content

Packaging and Delivering Software With the Image Packaging System in Oracle® Solaris 11.4

Exit Print View

Updated: November 2020
 
 

Delivering Content to a Shared Area

To share data across BEs, use a shared dataset and a symbolic link from a directory structure inside the BE pointing into that shared dataset. An IPS package delivers a symbolic link inside the BE. The same package or another package delivers an SMF service that creates and mounts the shared dataset. The link from the BE where the package is installed into the new shared dataset is similar to the links to /var/share shown in Existing Shared Content in Oracle Solaris. Applications running in the BE write to and read from the shared area via the link.

Best practice is to create one dataset where many applications running in different BEs can share content. Creating a separate dataset for each directory of data that you want to share results in creating many datasets in each non-global zone, and creating many datasets per zone is not desirable. For example, you could create an OPTSHARE dataset mounted at /opt/share. Different applications could share data in different directories under /opt/share.

While you could use a separate package and service to create the shared dataset, note that such a package could be installed from a different BE. The shared dataset might be available even though the package and service that created the dataset are not installed in the current BE. The service delivered by the application package shown in these examples checks whether the dataset already exists and creates the dataset if it does not already exist.

How to Enable Your Application to Use a Shared Area

This procedure shows how to provide a shared dataset and a link from the current BE into the shared dataset to enable this application to share data with multiple applications in multiple BEs. The application package delivers the following:

  • An SMF service that creates the shared dataset

  • A link in the current BE whose target is in the shared dataset

The package in this example only shows the actions needed to create and link to the shared dataset. Other actions for the application, such as executables and configuration files, are omitted for this example.

  1. Create a package development area.

    Create an area for your package development that contains the directories that you need in the BE and a link to the shared area outside the BE.

    1. Create the structure needed to deliver the service manifest and start method that will create the shared dataset.
      $ mkdir -p proto/lib/svc/manifest/site
      $ mkdir -p proto/lib/svc/method
    2. Deliver a link that applications can use to access the shared dataset.
      $ mkdir -p proto/opt/myapp
      $ ln -s ../../opt/share/myapp/logfiles proto/opt/myapp/logfiles
  2. Create the service start method.

    In proto/lib/svc/method, create a script that performs the following tasks:

    • Create the dataset, rpool/OPTSHARE, that is shared across BEs. Creating the shared dataset needs to be done only one time for each pool. All current and future BEs in the pool can access that dataset. Check whether the dataset already exists before you create it.

    • Create the directory structure you want in the shared dataset, including the target of the link in this example: /opt/share/myapp/logfiles.

    This script is the start method of the service. In this example, the script is named myapp-share-files.sh. You will need this file name when you create the service manifest in the next step.

    The script needs the elements shown in the following prototype. Recall that by default, sh is ksh93. The smf_include.sh file is needed for smf_method_exit. The third argument to smf_method_exit will appear in the service log file and in output from the svcs command.

    #!/bin/sh
    
    # Load SMF shell support definitions
    . /lib/svc/share/smf_include.sh
    
    # Create rpool/OPTSHARE with mount point /opt/share if it does not already exist
    # Create /opt/share/myapp/logfiles if it does not already exist
    
    # After this script runs, the service does not need to remain online.
    smf_method_exit $SMF_EXIT_TEMP_DISABLE done "shared area created"
  3. Create the service manifest.
    1. Create the initial service manifest.

      In proto/lib/svc/manifest/site, use the svcbundle command to create the service manifest. In the service-name, include the category site because this is a custom service. The value of start-method is the name of the script that was created in the previous step.

      $ svcbundle -o ./myapp-share-files.xml -s service-name=site/myapp-share-files \
      > -s start-method=/lib/svc/method/myapp-share-files.sh -s start-timeout=120

      By default, the instance that is created is named default and is enabled. If you want the instance to have a different name, specify the instance-name property. You can also specify instance-property, service-property, enabled, and other properties on the svcbundle command line. See the svcbundle(8) man page for descriptions.

    2. Make any necessary changes to the service manifest.

      The ./myapp-share-files.xml manifest is shown below.

      Verify that the content of the manifest is what you need. You might need to edit the manifest to add a dependency or make some other change.

      Add common_name and description information in the template data area. You can also add documentation and other template data.

      <?xml version="1.0" ?>
      <!DOCTYPE service_bundle
        SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
      <!--
          Manifest created by svcbundle (2020-Oct-11 22:07:39-0700)
      -->
      <service_bundle name="site/myapp-share-files" type="manifest">
          <service name="site/myapp-share-files" version="1" type="service">
              <!--
                  The following dependency keeps us from starting until the
                  multi-user milestone is reached.
              -->
              <dependency name="multi_user_dependency" grouping="require_all"
                  restart_on="none" type="service">
                  <service_fmri value="svc:/milestone/multi-user"/>
              </dependency>
              <exec_method name="start" type="method" timeout_seconds="120"
                  exec="/lib/svc/method/myapp-share-files.sh"/>
              <!--
                  The exec attribute below can be changed to a command that SMF
                  should execute to stop the service.  Use svcbundle -s
                  stop-method to set the attribute.
              -->
              <exec_method name="stop" type="method" timeout_seconds="60"
                  exec=":true"/>
              <!--
                  The exec attribute below can be changed to a command that SMF
                  should execute when the service is refreshed.  Use svcbundle -s
                  refresh-method to set the attribute.
              -->
              <exec_method name="refresh" type="method" timeout_seconds="60"
                  exec=":true"/>
              <property_group name="startd" type="framework">
                  <propval name="duration" type="astring" value="transient"/>
              </property_group>
              <instance name="default" enabled="true"/>
              <template>
                  <common_name>
                      <!--
                          Replace loctext content with a short name for the
                          service.
                      -->
                      <loctext xml:lang="C">
                              site/myapp-share-files
                      </loctext>
                  </common_name>
                  <description>
                      <!--
                          Replace loctext content with a brief description of the
                          service
                      -->
                      <loctext xml:lang="C">
                              The site/myapp-share-files service.
                      </loctext>
                  </description>
              </template>
          </service>
      </service_bundle>
    3. Verify that the service manifest is valid.
      $ svccfg validate myapp-share-files.xml
  4. Generate the initial package manifest.

    Use the pkgsend generate command to create the initial package manifest from your package development area.

    $ pkgsend generate proto | pkgfmt > share.p5m.1
    $ cat share.p5m.1
    dir  path=lib owner=root group=bin mode=0755
    dir  path=lib/svc owner=root group=bin mode=0755
    dir  path=lib/svc/manifest owner=root group=bin mode=0755
    dir  path=lib/svc/manifest/site owner=root group=bin mode=0755
    file lib/svc/manifest/site/myapp-share-files.xml \
        path=lib/svc/manifest/site/myapp-share-files.xml owner=root group=bin \
        mode=0644
    dir  path=lib/svc/method owner=root group=bin mode=0755
    file lib/svc/method/myapp-share-files.sh \
        path=lib/svc/method/myapp-share-files.sh owner=root group=bin mode=0755
    dir  path=opt owner=root group=bin mode=0755
    dir  path=opt/myapp owner=root group=bin mode=0755
    link path=opt/myapp/logfiles target=../../opt/share/myapp/logfiles
  5. Add metadata and actuators.
    1. Create the following pkgmogrify input file named share.mog.
      • Give the package a name, version, summary, and description.

      • Delete the opt, lib/svc/manifest/site, and lib/svc/method directory actions because these directories are already delivered by other packages.

      • Change the group for the service manifest to sys to match other manifests in /lib/svc/manifest.

      • Change the mode of the service manifest to 0444 and the mode of the service method to 0555 to match other manifests and methods on the system.

      • Add actuators for the service manifest and method files to restart the manifest-import service whenever those files are installed or updated.

      set name=pkg.fmri value=myapp@2.0
      set name=pkg.summary value="Deliver shared directory"
      set name=pkg.description value="This example package delivers a directory \
      and link that allows myapp content to be shared across BEs."
      set name=variant.arch value=$(ARCH)
      set name=info.classification \
          value=org.opensolaris.category.2008:Applications/Accessories
      <transform dir path=opt$->drop>
      <transform dir path=lib$->drop>
      <transform dir path=lib/svc$->drop>
      <transform dir path=lib/svc/manifest$->drop>
      <transform dir path=lib/svc/manifest/site$->drop>
      <transform dir path=lib/svc/method$->drop>
      <transform file path=lib/svc/manifest/site/myapp-share-files.xml -> \
          edit group bin sys>
      <transform file path=lib/svc/manifest/site/myapp-share-files.xml -> \
          edit mode 0644 0444>
      <transform file path=lib/svc/manifest/site/myapp-share-dir.xml -> \
          add restart_fmri svc:/system/manifest-import:default>
      <transform file path=lib/svc/method/myapp-share-files.sh -> \
          edit mode 0755 0555>
      <transform file path=lib/svc/method/myapp-share-dir.sh -> \
          add restart_fmri svc:/system/manifest-import:default>
    2. Run pkgmogrify on the share.p5m.1 manifest with the share.mog changes.
      $ pkgmogrify -DARCH=`uname -p` share.p5m.1 share.mog | pkgfmt > share.p5m.2
      $ cat share.p5m.2
      set name=pkg.fmri value=myapp@2.0
      set name=pkg.summary value="Deliver shared directory"
      set name=pkg.description \
          value="This example package delivers a directory and link that allows myapp 
      content to be shared across BEs."
      set name=info.classification \
          value=org.opensolaris.category.2008:Applications/Accessories
      set name=variant.arch value=i386
      file lib/svc/manifest/site/myapp-share-files.xml \
          path=lib/svc/manifest/site/myapp-share-files.xml owner=root group=sys \
          mode=0444
      file lib/svc/method/myapp-share-files.sh \
          path=lib/svc/method/myapp-share-files.sh owner=root group=bin mode=0755
      dir  path=opt/myapp owner=root group=bin mode=0555
      link path=opt/myapp/logfiles target=../../opt/share/myapp/logfiles
  6. Evaluate and resolve package dependencies.

    Use the pkgdepend command to automatically generate and resolve dependencies for the package. The output from resolving dependencies is automatically stored in a file with suffix .res.

    In this example, dependencies are generated for ksh93. Additional dependencies might be generated, depending on what your start method does. Also, the service and service instance are declared in org.opensolaris.smf.fmri.

    $ pkgdepend generate -md proto share.p5m.2 | pkgfmt > share.p5m.3
    $ pkgdepend resolve -m share.p5m.3

    The following lines are added in the output share.p5m.3.res file:

    set name=org.opensolaris.smf.fmri value=svc:/site/myapp-share-files \
        value=svc:/site/myapp-share-files:default
    
    depend fmri=pkg:/shell/ksh93@93.21.1.20120801-11.4.12.0.1.2.0 type=require
    depend fmri=pkg:/system/core-os@11.4-11.4.14.0.1.5.0 type=require
  7. Verify the package.
    $ pkglint share.p5m.3.res
  8. Publish the package.
    $ pkgsend -s site publish -d proto share.p5m.3.res
    pkg://site/myapp@2.0,5.11:20140417T000014Z
    PUBLISHED
  9. Test the package.

    Install the package.

    $ pkg install -g ./site myapp

    Verify that the dataset and link exist.

    $ zfs list rpool/OPTSHARE
    NAME             USED  AVAIL  REFER  MOUNTPOINT
    rpool/OPTSHARE  38.5K  24.8G  38.5K  /opt/share
    $ ls -l /opt/myapp
    lrwxrwxrwx   1 root   root   21 Apr 16 17:32 logfiles -> /opt/share/myapp/logfiles

    Uninstall the package. The /opt/myapp/logfiles link should be gone, and the service manifest and method script should be gone. The rpool/OPTSHARE dataset should still exist because that is not packaged content: It was created by the service.

How to Migrate Unshared Content to a Shared Area

This procedure extends the previous procedure. In this example, some data that needs to be shared already exists. The application package delivers a staging area and copies the data to be shared to the staging area. The SMF service moves the data from the staging area to the shared area.

  • In addition to the link, the package delivers a staging area in the BE to save any unpackaged content that already exists in the directory that will be redefined to be a link.

  • In addition to creating the shared dataset, the SMF service moves any content that exists in the staging area to the shared area.

  1. Create a package development area.

    Change all occurrences of logfiles to logs.

    $ mkdir -p proto/lib/svc/manifest/site
    $ mkdir -p proto/lib/svc/method
    $ mkdir -p proto/opt/myapp
    $ ln -s ../../opt/share/myapp/logs proto/opt/myapp/logs

    In this example, the myapp@1.0 package installed /opt/myapp/logs as a directory and the myapp application wrote content to this directory. When this new myapp@3.0 package installs /opt/myapp/logs as a link, any content in the /opt/myapp/logs directory will be saved in /var/pkg/lost+found. To instead save that content to the new shared area, deliver an area to hold a copy of that content.

    $ mkdir -p proto/opt/myapp/.migrate/logs

    The service will move the content from this staging area in the BE to the shared area.

    Content that the myapp@3.0 package writes to /opt/myapp/logs will go directly to the shared area through the link, as in the previous example.

  2. Create the service start method.

    Add the following task to the proto/lib/svc/method/myapp-share-files.sh script that you created in the previous procedure: Move content from the staging area to the shared area.

    Do not use the service to remove the empty staging area. The staging area is packaged content and should only be removed by uninstalling the package.

    #!/bin/sh
    
    # Load SMF shell support definitions
    . /lib/svc/share/smf_include.sh
    
    # Create rpool/OPTSHARE with mount point /opt/share if it does not already exist
    # Create /opt/share/myapp/logfiles if it does not already exist
    # Move any content from /opt/myapp/.migrate/logs to /opt/share/myapp/logs
    
    # After this script runs, the service does not need to remain online.
    smf_method_exit $SMF_EXIT_TEMP_DISABLE done "myapp shared files moved"
  3. Create the service manifest.

    See How to Enable Your Application to Use a Shared Area for more details about this step.

    $ svcbundle -o ./myapp-share-files.xml -s service-name=site/myapp-share-files \
    > -s start-method=/lib/svc/method/myapp-share-files.sh -s start-timeout=180

    Use the svccfg validate command to make sure the service manifest is valid.

  4. Generate the initial package manifest.

    The manifest is the same as the package manifest in the previous example with the following modifications:

    • All occurrences of logfiles are changed to logs.

    • The following two actions are added:

      dir  path=opt/myapp/.migrate owner=root group=bin mode=0755
      dir  path=opt/myapp/.migrate/logs owner=root group=bin mode=0755
  5. Add metadata and actuators.

    Add the following lines to your share.mog input file for pkgmogrify from the previous example. The salvage-from attribute moves any unpackaged content in the /opt/myapp/logs directory to the /opt/myapp/.migrate/logs directory. The service then moves the content from /opt/myapp/.migrate/logs to /opt/share/myapp/logs.

    <transform dir path=opt/myapp/.migrate/logfiles -> \
        add salvage-from /opt/myapp/logfiles>

    Name this package myapp@3.0.

    Run pkgmogrify as in the previous example.

  6. Evaluate and resolve package dependencies.
  7. Verify the package.
  8. Publish the package.
  9. Test the package.

    Create /opt/myapp/logs as a regular directory and put some files in it.

    Install the myapp@3.0 package. Verify that the existence of the dataset was correctly detected and handled, the new link exists, the /opt/myapp/logs directory is empty, the /opt/myapp/.migrate/logs directory exists and is empty, and the /opt/share/myapp/logs directory exists and contains the content that was initially in the /opt/myapp/logs directory.