cloudfoundry/stratos

View on GitHub
src/jetstream/plugins/monocular/store/chart_store_db.go

Summary

Maintainability
A
0 mins
Test Coverage
package store

import (
    "database/sql"
    "errors"
    "fmt"
    "sort"

    "github.com/cloudfoundry-incubator/stratos/src/jetstream/datastore"
    log "github.com/sirupsen/logrus"
)

var (
    saveChartVersion   = `INSERT INTO helm_charts (endpoint, name, repo_name, version, created, app_version, description, icon_url, chart_url, source_url, digest, is_latest, update_batch) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`
    updateChartVersion = `UPDATE helm_charts SET created=$1, app_version=$2, description=$3, icon_url=$4, chart_url=$5, source_url=$6, digest=$7, is_latest=$8, update_batch=$9 WHERE endpoint=$10 AND name=$11 AND repo_name=$12 AND version=$13`
    deleteChartVersion = `DELETE FROM helm_charts WHERE endpoint = $1 AND name = $2 and version = $3`
    deleteForEndpoint  = `DELETE FROM helm_charts WHERE endpoint = $1`
    deleteForBatch     = `DELETE FROM helm_charts WHERE endpoint = $1 AND name = $2 and update_batch != $3`
    renameEndpoint     = `UPDATE helm_charts SET repo_name=$1 WHERE endpoint=$2`
    getLatestCharts    = `SELECT endpoint, name, repo_name, version, created, app_version, description, icon_url, chart_url, source_url, digest, is_latest FROM helm_charts WHERE is_latest = true`
    getLatestChart     = `SELECT endpoint, name, repo_name, version, created, app_version, description, icon_url, chart_url, source_url, digest, is_latest FROM helm_charts WHERE repo_name = $1 AND name = $2 AND is_latest = true`
    getChartVersion    = `SELECT endpoint, name, repo_name, version, created, app_version, description, icon_url, chart_url, source_url, digest, is_latest FROM helm_charts WHERE repo_name = $1 AND name = $2 AND version = $3`
    getChartVersions   = `SELECT endpoint, name, repo_name, version, created, app_version, description, icon_url, chart_url, source_url, digest, is_latest FROM helm_charts WHERE repo_name = $1 AND name = $2`
    getEndpointIDs     = `SELECT DISTINCT endpoint FROM helm_charts`
    updateChartDigest  = `UPDATE helm_charts SET created=$1, is_latest=$2, update_batch=$3 WHERE endpoint=$4 AND name=$5 AND repo_name=$6 AND version=$7`
)

// InitRepositoryProvider - One time init for the given DB Provider
func InitRepositoryProvider(databaseProvider string) {
    saveChartVersion = datastore.ModifySQLStatement(saveChartVersion, databaseProvider)
    updateChartVersion = datastore.ModifySQLStatement(updateChartVersion, databaseProvider)
    updateChartVersion = datastore.ModifySQLStatement(updateChartVersion, databaseProvider)
    updateChartDigest = datastore.ModifySQLStatement(updateChartDigest, databaseProvider)
    deleteForEndpoint = datastore.ModifySQLStatement(deleteForEndpoint, databaseProvider)
    deleteForBatch = datastore.ModifySQLStatement(deleteForBatch, databaseProvider)
    renameEndpoint = datastore.ModifySQLStatement(renameEndpoint, databaseProvider)
    getLatestCharts = datastore.ModifySQLStatement(getLatestCharts, databaseProvider)
    getLatestChart = datastore.ModifySQLStatement(getLatestChart, databaseProvider)
    getChartVersion = datastore.ModifySQLStatement(getChartVersion, databaseProvider)
    getChartVersions = datastore.ModifySQLStatement(getChartVersions, databaseProvider)
    getEndpointIDs = datastore.ModifySQLStatement(getEndpointIDs, databaseProvider)
}

// HelmChartDBStore is a DB-backed Helm Chart repository
type HelmChartDBStore struct {
    db *sql.DB
}

// NewHelmChartDBStore will create a new instance of the AnalysisDBStore
func NewHelmChartDBStore(dcp *sql.DB) (ChartStore, error) {
    return &HelmChartDBStore{db: dcp}, nil
}

func truncate(in string) string {
    return fmt.Sprintf("%.255s", in)
}

// Save a Helm Chart to the database
func (p *HelmChartDBStore) Save(chart ChartStoreRecord, batchID string) error {

    sourceURL := ""
    if len(chart.Sources) > 0 {
        sourceURL = chart.Sources[0]
    }

    // Get the existing record - if it has the same digest, then no need to store it
    record, err := p.GetChart(chart.Repository, chart.Name, chart.Version)
    if err == nil && record.Digest == chart.Digest {
        log.Debugf("Chart already exists %s/%s-%s with digest %s", chart.Repository, chart.Name, chart.Version, chart.Digest)
        _, err := p.db.Exec(updateChartDigest, chart.Created, chart.IsLatest, batchID, chart.EndpointID, chart.Name, chart.Repository, chart.Version)
        return err
    }

    if err == nil {
        log.Debugf("Chart already exists %s/%s-%s with different digest %s", chart.Repository, chart.Name, chart.Version, chart.Digest)
        // The record already exists, so update it
        _, err := p.db.Exec(updateChartVersion, chart.Created, chart.AppVersion, truncate(chart.Description), truncate(chart.IconURL), truncate(chart.ChartURL), truncate(sourceURL), chart.Digest, chart.IsLatest, batchID, chart.EndpointID, chart.Name, chart.Repository, chart.Version)
        return err
    }

    if _, err := p.db.Exec(saveChartVersion, chart.EndpointID, chart.Name, chart.Repository, chart.Version, chart.Created, chart.AppVersion, truncate(chart.Description), truncate(chart.IconURL), truncate(chart.ChartURL), truncate(sourceURL), chart.Digest, chart.IsLatest, batchID); err != nil {
        return fmt.Errorf("Unable to save Helm Chart Version: %v", err)
    }
    return nil
}

// DeleteBatch will remove all chart versions not with the given batch id
func (p *HelmChartDBStore) DeleteBatch(endpointID, chart, batchID string) error {
    if _, err := p.db.Exec(deleteForBatch, endpointID, chart, batchID); err != nil {
        return fmt.Errorf("Unable to delete Helm Chart Versions for batch ID: %s %v", batchID, err)
    }
    return nil
}

// DeleteForEndpoint will remove all Helm Charts for a given endpoint guid
func (p *HelmChartDBStore) DeleteForEndpoint(endpointID string) error {
    if _, err := p.db.Exec(deleteForEndpoint, endpointID); err != nil {
        return fmt.Errorf("Unable to delete Helm Charts for endpoint: %s %v", endpointID, err)
    }
    return nil
}

// RenameEndpoint will update all charts for a given endpoint to have the new repository name
func (p *HelmChartDBStore) RenameEndpoint(endpointID, name string) error {
    if _, err := p.db.Exec(renameEndpoint, name, endpointID); err != nil {
        return fmt.Errorf("Unable to rename Helm Chart repository for endpoint: %s %v", endpointID, err)
    }
    return nil
}

// GetLatestCharts will get only the info for the latest version of each chart
func (p *HelmChartDBStore) GetLatestCharts() ([]*ChartStoreRecord, error) {

    rows, err := p.db.Query(getLatestCharts)
    if err != nil {
        return nil, fmt.Errorf("Unable to retrieve Helm Charts: %v", err)
    }
    defer rows.Close()

    var chartList []*ChartStoreRecord

    for rows.Next() {
        chart := new(ChartStoreRecord)
        sourceURL := ""
        err := rows.Scan(&chart.EndpointID, &chart.Name, &chart.Repository, &chart.Version, &chart.Created, &chart.AppVersion, &chart.Description, &chart.IconURL, &chart.ChartURL, &sourceURL, &chart.Digest, &chart.IsLatest)
        if err != nil {
            return nil, fmt.Errorf("Unable to scan Helm Chart records: %v", err)
        }
        chart.SemVer = NewSemanticVersion(chart.Version)
        addSources(chart, sourceURL)
        chartList = append(chartList, chart)
    }

    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("Unable to list Helm Chart records: %v", err)
    }

    return chartList, nil
}

// GetChart gets a single helm chart
func (p *HelmChartDBStore) GetChart(repo, name, version string) (*ChartStoreRecord, error) {

    var row *sql.Row
    chart := new(ChartStoreRecord)

    if len(version) == 0 {
        row = p.db.QueryRow(getLatestChart, repo, name)
    } else {
        row = p.db.QueryRow(getChartVersion, repo, name, version)
    }

    sourceURL := ""
    err := row.Scan(&chart.EndpointID, &chart.Name, &chart.Repository, &chart.Version, &chart.Created, &chart.AppVersion, &chart.Description, &chart.IconURL, &chart.ChartURL, &sourceURL, &chart.Digest, &chart.IsLatest)
    switch {
    case err == sql.ErrNoRows:
        return chart, errors.New("No match for that chart")
    case err != nil:
        return chart, fmt.Errorf("Error trying to find chart record: %v", err)
    default:
        // do nothing
    }

    chart.SemVer = NewSemanticVersion(chart.Version)
    addSources(chart, sourceURL)

    return chart, nil
}

// GetChartVersions will get all of the versions for a given chart
func (p *HelmChartDBStore) GetChartVersions(repo, name string) ([]*ChartStoreRecord, error) {
    rows, err := p.db.Query(getChartVersions, repo, name)
    if err != nil {
        return nil, fmt.Errorf("Unable to retrieve Helm Charts: %v", err)
    }
    defer rows.Close()

    var chartList ChartStoreRecordList

    for rows.Next() {
        chart := new(ChartStoreRecord)
        sourceURL := ""
        err := rows.Scan(&chart.EndpointID, &chart.Name, &chart.Repository, &chart.Version, &chart.Created, &chart.AppVersion, &chart.Description, &chart.IconURL, &chart.ChartURL, &sourceURL, &chart.Digest, &chart.IsLatest)
        if err != nil {
            return nil, fmt.Errorf("Unable to scan Helm Chart records: %v", err)
        }
        chart.SemVer = NewSemanticVersion(chart.Version)
        addSources(chart, sourceURL)
        chartList = append(chartList, chart)
    }

    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("Unable to list Helm Chart records: %v", err)
    }

    // Sort list by version
    sort.Sort(chartList)
    return chartList, nil
}

// GetEndpointIDs will get all unique endpoint IDs from the database
func (p *HelmChartDBStore) GetEndpointIDs() ([]string, error) {
    rows, err := p.db.Query(getEndpointIDs)
    if err != nil {
        return nil, fmt.Errorf("Unable to retrieve Endpoint IDs: %v", err)
    }
    defer rows.Close()

    list := make([]string, 0)

    for rows.Next() {
        var endpoint string
        err := rows.Scan(&endpoint)
        if err != nil {
            return nil, fmt.Errorf("Unable to scan Helm Chart records for endpoints: %v", err)
        }
        list = append(list, endpoint)
    }

    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("Unable to list Helm Chart endpoints: %v", err)
    }

    return list, nil
}

func addSources(record *ChartStoreRecord, sourceURL string) {
    record.Sources = make([]string, 0)
    if len(sourceURL) > 0 {
        record.Sources = append(record.Sources, sourceURL)
    }
}