Skip to content

Commit

Permalink
pkg/pagination: init
Browse files Browse the repository at this point in the history
This change refactors a lot of the code dealing with pagination so that
fernet implementation details do not leak.

- Deletes database/pgsql/token
- Introduces a pagination package
- Renames idPageNumber to Page and add a constructor and method.
  • Loading branch information
jzelinskie committed Sep 7, 2018
1 parent b20482e commit d193b46
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 113 deletions.
13 changes: 4 additions & 9 deletions cmd/clair/config.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017 clair authors
// Copyright 2018 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,6 @@ import (
"os"
"time"

"github.com/fernet/fernet-go"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"

Expand All @@ -31,6 +30,7 @@ import (
"github.com/coreos/clair/ext/featurens"
"github.com/coreos/clair/ext/notification"
"github.com/coreos/clair/ext/vulnsrc"
"github.com/coreos/clair/pkg/pagination"
)

// ErrDatasourceNotLoaded is returned when the datasource variable in the
Expand Down Expand Up @@ -108,15 +108,10 @@ func LoadConfig(path string) (config *Config, err error) {
// Generate a pagination key if none is provided.
if v, ok := config.Database.Options["paginationkey"]; !ok || v == nil || v.(string) == "" {
log.Warn("pagination key is empty, generating...")
var key fernet.Key
if err = key.Generate(); err != nil {
return
}
config.Database.Options["paginationkey"] = key.Encode()
config.Database.Options["paginationkey"] = pagination.Must(pagination.NewKey()).String()
} else {
_, err = fernet.DecodeKey(config.Database.Options["paginationkey"].(string))
_, err = pagination.KeyFromString(config.Database.Options["paginationkey"].(string))
if err != nil {
err = errors.New("Invalid Pagination key; must be 32-bit URL-safe base64")
return
}
}
Expand Down
12 changes: 4 additions & 8 deletions database/pgsql/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ func (tx *pgSession) FindNewNotification(notifiedBefore time.Time) (database.Not

func (tx *pgSession) findPagedVulnerableAncestries(vulnID int64, limit int, currentPage database.PageNumber) (database.PagedVulnerableAncestries, error) {
vulnPage := database.PagedVulnerableAncestries{Limit: limit}
current := idPageNumber{0}
current := Page{0}
if currentPage != "" {
var err error
current, err = decryptPage(currentPage, tx.paginationKey)
current, err = PageFromPageNumber(tx.key, currentPage)
if err != nil {
return vulnPage, err
}
Expand Down Expand Up @@ -211,11 +211,7 @@ func (tx *pgSession) findPagedVulnerableAncestries(vulnID int64, limit int, curr
} else {
// Use the last ancestry's ID as the next PageNumber.
lastIndex = len(ancestries) - 1
vulnPage.Next, err = encryptPage(
idPageNumber{
ancestries[len(ancestries)-1].id,
}, tx.paginationKey)

vulnPage.Next, err = Page{ancestries[len(ancestries)-1].id}.PageNumber(tx.key)
if err != nil {
return vulnPage, err
}
Expand All @@ -226,7 +222,7 @@ func (tx *pgSession) findPagedVulnerableAncestries(vulnID int64, limit int, curr
vulnPage.Affected[int(ancestry.id)] = ancestry.name
}

vulnPage.Current, err = encryptPage(current, tx.paginationKey)
vulnPage.Current, err = current.PageNumber(tx.key)
if err != nil {
return vulnPage, err
}
Expand Down
24 changes: 12 additions & 12 deletions database/pgsql/notification_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2017 clair authors
// Copyright 2018 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -73,22 +73,22 @@ func TestPagination(t *testing.T) {
if assert.Nil(t, err) && assert.True(t, ok) {
assert.Equal(t, "test", noti.Name)
if assert.NotNil(t, noti.Old) && assert.NotNil(t, noti.New) {
oldPageNum, err := decryptPage(noti.Old.Current, tx.paginationKey)
oldPage, err := PageFromPageNumber(tx.key, noti.Old.Current)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}

assert.Equal(t, int64(0), oldPageNum.StartID)
newPageNum, err := decryptPage(noti.New.Current, tx.paginationKey)
assert.Equal(t, int64(0), oldPage.StartID)
newPage, err := PageFromPageNumber(tx.key, noti.New.Current)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}
newPageNextNum, err := decryptPage(noti.New.Next, tx.paginationKey)
newPageNext, err := PageFromPageNumber(tx.key, noti.New.Next)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}
assert.Equal(t, int64(0), newPageNum.StartID)
assert.Equal(t, int64(4), newPageNextNum.StartID)
assert.Equal(t, int64(0), newPage.StartID)
assert.Equal(t, int64(4), newPageNext.StartID)

noti.Old.Current = ""
noti.New.Current = ""
Expand All @@ -98,26 +98,26 @@ func TestPagination(t *testing.T) {
}
}

page1, err := encryptPage(idPageNumber{0}, tx.paginationKey)
pageNum1, err := Page{0}.PageNumber(tx.key)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}

page2, err := encryptPage(idPageNumber{4}, tx.paginationKey)
pageNum2, err := Page{4}.PageNumber(tx.key)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}

noti, ok, err = tx.FindVulnerabilityNotification("test", 1, page1, page2)
noti, ok, err = tx.FindVulnerabilityNotification("test", 1, pageNum1, pageNum2)
if assert.Nil(t, err) && assert.True(t, ok) {
assert.Equal(t, "test", noti.Name)
if assert.NotNil(t, noti.Old) && assert.NotNil(t, noti.New) {
oldCurrentPage, err := decryptPage(noti.Old.Current, tx.paginationKey)
oldCurrentPage, err := PageFromPageNumber(tx.key, noti.Old.Current)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}

newCurrentPage, err := decryptPage(noti.New.Current, tx.paginationKey)
newCurrentPage, err := PageFromPageNumber(tx.key, noti.New.Current)
if !assert.Nil(t, err) {
assert.FailNow(t, "")
}
Expand Down
45 changes: 45 additions & 0 deletions database/pgsql/page.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2018 clair authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pgsql

import (
"github.com/coreos/clair/database"
"github.com/coreos/clair/pkg/pagination"
)

// Page is the representation of a page for the Postgres schema.
type Page struct {
// StartID is the ID being used as the basis for pagination across database
// results. It is used to search for an ancestry with ID >= StartID.
//
// StartID is required to be unique to every ancestry and always increasing.
StartID int64
}

// PageNumber converts a Page to a database.PageNumber.
func (p Page) PageNumber(key pagination.Key) (pn database.PageNumber, err error) {
token, err := key.MarshalToken(p)
if err != nil {
return pn, err
}
pn = database.PageNumber(token)
return pn, nil
}

// PageFromPageNumber converts a database.PageNumber into a Page.
func PageFromPageNumber(key pagination.Key, pn database.PageNumber) (p Page, err error) {
err = key.UnmarshalToken(string(pn), &p)
return
}
36 changes: 8 additions & 28 deletions database/pgsql/pgsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import (

"github.com/coreos/clair/database"
"github.com/coreos/clair/database/pgsql/migrations"
"github.com/coreos/clair/database/pgsql/token"
"github.com/coreos/clair/pkg/commonerr"
"github.com/coreos/clair/pkg/pagination"
)

var (
Expand Down Expand Up @@ -91,41 +91,21 @@ type pgSQL struct {
type pgSession struct {
*sql.Tx

paginationKey string
key pagination.Key
}

type idPageNumber struct {
// StartID is an implementation detail for paginating by an ID required to
// be unique to every ancestry and always increasing.
//
// StartID is used to search for ancestry with ID >= StartID
StartID int64
}

func encryptPage(page idPageNumber, paginationKey string) (result database.PageNumber, err error) {
resultBytes, err := token.Marshal(page, paginationKey)
if err != nil {
return result, err
}
result = database.PageNumber(resultBytes)
return result, nil
}

func decryptPage(page database.PageNumber, paginationKey string) (result idPageNumber, err error) {
err = token.Unmarshal(string(page), paginationKey, &result)
return
}

// Begin initiates a transaction to database. The expected transaction isolation
// level in this implementation is "Read Committed".
// Begin initiates a transaction to database.
//
// The expected transaction isolation level in this implementation is "Read
// Committed".
func (pgSQL *pgSQL) Begin() (database.Session, error) {
tx, err := pgSQL.DB.Begin()
if err != nil {
return nil, err
}
return &pgSession{
Tx: tx,
paginationKey: pgSQL.config.PaginationKey,
Tx: tx,
key: pagination.Must(pagination.KeyFromString(pgSQL.config.PaginationKey)),
}, nil
}

Expand Down
9 changes: 2 additions & 7 deletions database/pgsql/pgsql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import (
"strings"
"testing"

fernet "github.com/fernet/fernet-go"
"github.com/pborman/uuid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
yaml "gopkg.in/yaml.v2"

"github.com/coreos/clair/database"
"github.com/coreos/clair/pkg/pagination"
)

var (
Expand Down Expand Up @@ -215,18 +215,13 @@ func generateTestConfig(testName string, loadFixture bool, manageLife bool) data
source = fmt.Sprintf(sourceEnv, dbName)
}

var key fernet.Key
if err := key.Generate(); err != nil {
panic("failed to generate pagination key" + err.Error())
}

return database.RegistrableComponentConfig{
Options: map[string]interface{}{
"source": source,
"cachesize": 0,
"managedatabaselifecycle": manageLife,
"fixturepath": fixturePath,
"paginationkey": key.Encode(),
"paginationkey": pagination.MustGenerateNewKey().String(),
},
}
}
Expand Down
49 changes: 0 additions & 49 deletions database/pgsql/token/token.go

This file was deleted.

Loading

0 comments on commit d193b46

Please sign in to comment.