Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ Below is a list with included features, click on the link for more details.
| [eclipse-deps](./features/src/eclipse-deps/README.md) | Installs all system dependencies required for running Eclipse IDE in a dev container. |
| [git-lfs](./features/src/git-lfs/README.md) | A feature which installs Git LFS. |
| [go](./features/src/go/README.md) | A feature which installs Go. |
| [instant-client](./features/src/instant-client/README.md) | A feature which installs the Oracle Instant Client basic package |
| [jfrog-cli](./features/src/jfrog-cli/README.md) | A feature which installs the JFrog CLI. |
| [make](./features/src/make/README.md) | A feature which installs GNU Make. |
| [mingw](./features/src/mingw/README.md) | A feature which installs MinGW (Minimalist GNU for Windows) cross-compilers and tools. |
| [node](./features/src/node/README.md) | A package which installs Node.js. |
| [jfrog-cli](./features/src/jfrog-cli/README.md) | A feature which installs the JFrog CLI. |
| [vault-cli](./features/src/vault-cli/README.md) | A feature which installs the Vault CLI. |
| [zig](./features/src/zig/README.md) | A feature which installs Zig. |

Expand Down
12 changes: 12 additions & 0 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var featureList = []string{
"eclipse-deps",
"git-lfs",
"go",
"instant-client",
"jfrog-cli",
"make",
"mingw",
Expand Down Expand Up @@ -122,6 +123,17 @@ func init() {
return publishFeature("go")
})

////////// instant-client
gotaskr.Task("Feature:instant-client:Package", func() error {
return packageFeature("instant-client")
})
gotaskr.Task("Feature:instant-client:Test", func() error {
return testFeature("instant-client")
})
gotaskr.Task("Feature:instant-client:Publish", func() error {
return publishFeature("instant-client")
})

////////// jfrog-cli
gotaskr.Task("Feature:jfrog-cli:Package", func() error {
return packageFeature("jfrog-cli")
Expand Down
9 changes: 9 additions & 0 deletions features/src/instant-client/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Notes

Restrictions:
* Versions **below 19** are not supported!
* Version 21 is not supported for ARM!

### System Compatibility

Debian, Ubuntu
33 changes: 33 additions & 0 deletions features/src/instant-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Instant client (instant-client)

A package which installs the Oracle Instant Client Basic package.

## Example Usage

```json
"features": {
"ghcr.io/postfinance/devcontainer-features/instant-client:0.1.0": {
"version": "latest",
"downloadUrl": "",
"versionsUrl": ""
}
}
```

## Options

| Option | Description | Type | Default Value | Proposals |
|-----|-----|-----|-----|-----|
| version | The version of Instant Client to install. | string | latest | latest, 23, 21, 23.8.0.25.04 |
| downloadUrl | The download URL to use for Instant Client binaries. | string | <empty> | https://mycompany.com/artifactory/oracle-generic-remote |
| versionsUrl | The URL to use to check for available Instant Client versions. | string | <empty> | |

## Notes

Restrictions:
* Versions **below 19** are not supported!
* Version 21 is not supported for ARM!

### System Compatibility

Debian, Ubuntu
32 changes: 32 additions & 0 deletions features/src/instant-client/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"id": "instant-client",
"version": "0.1.0",
"name": "Instant client",
"description": "A package which installs the Oracle Instant Client Basic package.",
"options": {
"version": {
"type": "string",
"proposals": [
"latest",
"23",
"21",
"23.8.0.25.04"
],
"default": "latest",
"description": "The version of Instant Client to install."
},
"downloadUrl": {
"type": "string",
"default": "",
"proposals": [
"https://mycompany.com/artifactory/oracle-generic-remote"
],
"description": "The download URL to use for Instant Client binaries."
},
"versionsUrl": {
"type": "string",
"default": "",
"description": "The URL to use to check for available Instant Client versions."
}
}
}
6 changes: 6 additions & 0 deletions features/src/instant-client/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
. ./functions.sh

"./installer_$(detect_arch)" \
-version="${VERSION:-"latest"}" \
-downloadUrl="${DOWNLOADURL:-""}" \
-versionsUrl="${VERSIONSURL:-""}"
147 changes: 147 additions & 0 deletions features/src/instant-client/installer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package main

import (
"builder/installer"
"flag"
"fmt"
"os"
"path"
"regexp"
"strings"

"github.com/roemer/gotaskr/execr"
"github.com/roemer/gover"
)

//////////
// Configuration
//////////

var versionRegexp *regexp.Regexp = regexp.MustCompile(`^(\d+).(\d+).(\d+).(\d+).(\d+)$`)
var indexLineRegexp *regexp.Regexp = regexp.MustCompile(`^.*<a.*href='.*download\.oracle\.com(.*instantclient-basic-.*-(\d+(?:\.\d+){4})\w*\.zip)'.*$`)

//////////
// Main
//////////

func main() {
if err := runMain(); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
}

func runMain() error {
// Handle the flags
version := flag.String("version", "latest", "The version of Instant Client to install.")
versionsUrl := flag.String("versionsUrl", "", "")
downloadUrl := flag.String("downloadUrl", "", "")
flag.Parse()

// Load settings from an external file (global/per-feature overrides)
if err := installer.LoadOverrides(); err != nil {
return err
}

installer.HandleOverride(versionsUrl, "https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html", "instant-client-versions-url")
installer.HandleOverride(downloadUrl, "https://download.oracle.com", "instant-client-download-url")

// Create and process the feature
feature := installer.NewFeature("Oracle Instant Client", false,
&instantClientComponent{
ComponentBase: installer.NewComponentBase("Basic Package", *version),
versionsUrl: *versionsUrl,
downloadUrl: *downloadUrl,
})
return feature.Process()
}

//////////
// Implementation
//////////

type instantClientComponent struct {
*installer.ComponentBase
versionsUrl string
downloadUrl string
}

func (c *instantClientComponent) IsFullVersion(referenceVersion *gover.Version) bool {
return len(referenceVersion.Segments) == 5 && referenceVersion.DefinedSegmentCount() == 5
}

func (c *instantClientComponent) createDownloadURLForVersion(version *gover.Version) (string, error) {
zipVersion := version.Raw
// Versions below 23 have a dbru suffix
if version.Major() < 23 {
zipVersion = fmt.Sprintf("%sdbru", version.Raw)
}
majorMinor := fmt.Sprintf("%d%d", version.Major(), version.Minor())
archPart, err := installer.Tools.System.MapArchitecture(map[string]string{
installer.AMD64: "x64",
installer.ARM64: "arm64",
})
if err != nil {
return "", err
}
return fmt.Sprintf(
"%s/otn_software/linux/instantclient/%s/instantclient-basic-linux.%s-%s.zip",
c.downloadUrl,
fmt.Sprintf("%s%s", majorMinor, strings.Repeat("0", 7-len(majorMinor))),
archPart,
zipVersion,
), nil
}

func (c *instantClientComponent) GetAllVersions() ([]*gover.Version, error) {
// Parse the latest versions from download page
versions := []*gover.Version{}
stableVersions, err := installer.Tools.Http.GetVersionsFromHtmlIndexFunc(c.versionsUrl, c.lineExtractFunc)
if err != nil {
return nil, err
}
versions = append(versions, stableVersions...)
return versions, nil
}

func (c *instantClientComponent) InstallVersion(version *gover.Version) error {
fileName := "instant-client.zip"
downloadUrl, err := c.createDownloadURLForVersion(version)
if err != nil {
return err
}
if err := installer.Tools.Download.ToFile(downloadUrl, fileName, "Instant Client"); err != nil {
return err
}
rootFolder, err := installer.Tools.Compression.GetRootFolderFromZip(fileName)
if err != nil {
return err
}
if err := installer.Tools.Compression.ExtractZip(fileName, "/opt/oracle", false); err != nil {
return err
}
if err := installer.Tools.Apt.InstallDependencies("libaio1"); err != nil {
return err
}
if err := os.WriteFile("/etc/ld.so.conf.d/oracle-instantclient.conf", []byte(path.Join("/opt/oracle", rootFolder)), 0644); err != nil {
return err
}
if err := execr.Run(true, "ldconfig"); err != nil {
return err
}
// Cleanup
if err := os.RemoveAll(fileName); err != nil {
return err
}
return nil
}

func (c *instantClientComponent) lineExtractFunc(url, line string) (*gover.Version, error) {
if match := indexLineRegexp.FindStringSubmatch(line); match != nil {
fullName := match[2]

version := gover.MustParseVersionFromRegex(fullName, versionRegexp)
return version, nil
}
return nil, nil
}
8 changes: 8 additions & 0 deletions features/test/instant-client/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
set -e

[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh"
[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh"

check_file_exists "/opt/oracle/instantclient_23_9/BASIC_README"
check_version "$(ldconfig -v)" "/opt/oracle/instantclient_23_9"
15 changes: 15 additions & 0 deletions features/test/instant-client/scenarios.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"install": {
"build": {
"dockerfile": "Dockerfile",
"options": [
"--add-host=host.docker.internal:host-gateway"
]
},
"features": {
"./instant-client": {
"version": "23.9.0.25.07"
}
}
}
}
5 changes: 5 additions & 0 deletions features/test/instant-client/test-images.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"mcr.microsoft.com/devcontainers/base:debian-11",
"mcr.microsoft.com/devcontainers/base:debian-12",
"mcr.microsoft.com/devcontainers/base:ubuntu-22.04"
]
21 changes: 21 additions & 0 deletions installer/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ func (c *compression) ExtractZip(filePath string, dstPath string, withoutRootFol
return nil
}

// Returns the name of the root folder in a ZIP file, ignoring META-INF
func (c compression) GetRootFolderFromZip(zipPath string) (string, error) {
r, err := zip.OpenReader(zipPath)
if err != nil {
return "", err
}
defer r.Close()

for _, f := range r.File {
if strings.HasPrefix(f.Name, "META-INF") {
continue
}
parts := strings.Split(f.Name, "/")
if len(parts) > 1 {
return parts[0], nil // Return the first folder name
}
}

return "", fmt.Errorf("no root folder found in zip")
}

// Extracts a tar.gz file into a folder.
func (c *compression) ExtractTarGz(filePath string, dstPath string, withoutRootFolder bool) error {
// Open the file
Expand Down