Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replaced fig with docker and made a new post draft #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 1 addition & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1 @@
FROM google/debian:wheezy
MAINTAINER Seth Ammons <seth.ammons@gmail.com>

RUN apt-get update && apt-get install -y curl tar python python-pip && pip install Pygments

# grab the specifc version of hugo, and raname and move it, and append to path
RUN curl -L http://github.com/spf13/hugo/releases/download/v0.13/hugo_0.13_linux_amd64.tar.gz | tar xz
RUN mv hugo_0.13_linux_amd64 hugo && mv /hugo/hugo_0.13_linux_amd64 /hugo/hugo

ENV PATH $PATH:/hugo

WORKDIR /me/hugo

EXPOSE 1313

ADD . /me
FROM publysher/hugo
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

This is the hugo project running sethammons.com. It is an excuse to try some docker things and play with Hugo.

If you have docker and fig, you can simply:
`sudo fig build; sudo fig stop; sudo fig up -d`
If you have docker:
```
$ docker run --rm -it -v $PWD:/src -p 1313:1313 -u hugo jguyomard/hugo-builder hugo server -w --bind=0.0.0.0 --theme temple
```

There is currently a hardcoded value that sets the url my my domain. If you want to do local development, you'll want to change sethammons.com to localhost.
To create a new post:
```
docker run --rm -it -v $PWD:/src -p 1313:1313 -u hugo jguyomard/hugo-builder hugo new posts/my-post.md
```

A handy alias:
```
alias hugo='docker run --rm -it -v $PWD:/src -u hugo jguyomard/hugo-builder hugo'
```
108 changes: 108 additions & 0 deletions codesamples/getter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package getter

import (
"io"
"log"
"os"

"github.com/minio/minio-go"
"github.com/pkg/errors"
)

const (
// SourceLocal signifies if a file or error came from local disk
SourceLocal = "local"
// SourceRemote signifies if a file or error came from the remote disk
SourceRemote = "remote"
)

// MessageGetter allows us to get a ReadCloser, the source (remote/local), or an error when attempting to get
// a message from either local or remote storage
type MessageGetter interface {
GetMessage(localPath, host, bucket, key string) (io.ReadCloser, string, error)
}

// Getter contains unexported fields allowing the local or remote fetching of files
type Getter struct {
logger *log.Logger
useRemoteFS bool
isRemoteFSCanaryHost bool
accessKey string
accessSecret string

RemoteGetter RemoteGetter
LocalGetter LocalGetter
}

// New creates a instatialized Getter that can get files locally or remotely.
// useRemoteFS tells us if the service is configured to use the remote file system.
// isRemoteFSCanaryHost says that this particular host in this cluster should be attempting remote file system work.
// accessKey and accessSecret are authentication parts for the remote file system.
func New(l *log.Logger, useRemoteFS, isRemoteFSCanaryHost bool, accessKey, accessSecret string) *Getter {
return &Getter{
logger: l,
useRemoteFS: useRemoteFS,
isRemoteFSCanaryHost: isRemoteFSCanaryHost,
accessKey: accessKey,
accessSecret: accessSecret,
RemoteGetter: &minioWrapper{},
LocalGetter: &osFile{},
}
}

// GetMessage will reach out to s3 / rados gateway or use the local file system to retrieve an email message
func (g *Getter) GetMessage(localPath, host, bucket, key string) (io.ReadCloser, string, error) {
if g.useRemoteFS && g.isRemoteFSCanaryHost && host != "" && key != "" && bucket != "" {
// we have everything we need to do remote fs stuff
fh, err := g.RemoteGetter.GetRemoteMessage(g.accessKey, g.accessSecret, host, bucket, key)
if err == nil {
return fh, SourceRemote, nil
}

g.logger.Printf("falling back to local source - %v", err)
} else if g.useRemoteFS && g.isRemoteFSCanaryHost {
// we want to do remote fs stuff, but host, bucket, or key are messed up
g.logger.Println("falling back to local source - missing fields")
}

fh, err := g.LocalGetter.Open(localPath)
if err != nil {
return nil, SourceLocal, err
}

return fh, SourceLocal, nil
}

type RemoteGetter interface {
GetRemoteMessage(accessKey, accessSecret, host, bucket, key string) (io.ReadCloser, error)
}

type minioWrapper struct{}

func (_ *minioWrapper) GetRemoteMessage(accessKey, accessSecret, host, bucket, key string) (io.ReadCloser, error) {
client, err := minio.NewV2(host, accessKey, accessSecret, false)
if err != nil {
return nil, errors.Wrap(err, "unable to get remote fs client")
}

obj, err := client.GetObject(bucket, key)
if err != nil {
return nil, errors.Wrap(err, "unable to get remote object")
}
_, err = obj.Stat()
if err != nil {
return nil, errors.Wrap(err, "unable to get remote file info")
}

return obj, nil
}

type LocalGetter interface {
Open(localPath string) (io.ReadCloser, error)
}

type osFile struct{}

func (f *osFile) Open(localPath string) (io.ReadCloser, error) {
return os.Open(localPath)
}
211 changes: 211 additions & 0 deletions codesamples/getter/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package getter

// TestGetMessage verifies that we can get remote and local files and that we handle errors
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGetMessage(t *testing.T) {
for _, test := range []struct {
// name gives us a test indentifier for if a test fails, we can know which one
name string
// based on config for if this system should allow for remote file system access
useRemoteFS bool
// based on configured list of "canary" servers which should access the remote file system
isCanary bool
// represents the data in the file
data []byte
// the GetMessage method will report back if it was remote or local for the source of the file
expectedSource string
// set what the remote err should be
remoteErr error
// set what the local err should be
localErr error
// the error returned from GetMessage depending on how remoteErr and localErr behaved
expectedErr error
// allow us to inspect any error logs generated
expectedLogs []string
// parameters into GetMessage. We expect errors if any are blank
bucket string
key string
host string
}{
{
name: "should use remote fs",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceRemote,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{},
},
{
name: "should use local - config dont use remote file system",
useRemoteFS: false,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{},
},
{
name: "should use local - config not a canary",
useRemoteFS: true,
isCanary: false,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{},
},
{
name: "should use local - config not a canary + config don't use remote files ystem",
useRemoteFS: false,
isCanary: false,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{},
},
{
name: "error remote - fall back to local",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: fmt.Errorf("unable to remote"),
localErr: nil,
expectedErr: nil,
expectedLogs: []string{"falling back to local source"},
},
{
name: "error remote and local - report back local error and log remote failure",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: fmt.Errorf("falling back to local source"),
localErr: fmt.Errorf("unable to read from disk"),
expectedErr: fmt.Errorf("unable to read from disk"),
expectedLogs: []string{"falling back to local source"},
},
{
name: "error remote for bad bucket, use local",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "",
key: "key",
host: "host",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{"falling back to local source - missing fields"},
},
{
name: "error remote for bad key, use local",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "",
host: "host",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{"falling back to local source - missing fields"},
},
{
name: "error remote for bad host, use local",
useRemoteFS: true,
isCanary: true,
data: []byte("file data"),
bucket: "bucket",
key: "key",
host: "",
expectedSource: SourceLocal,
remoteErr: nil,
localErr: nil,
expectedErr: nil,
expectedLogs: []string{"falling back to local source - missing fields"},
},
} {
// Set up and call GetMessage
buf := &bytes.Buffer{}
getter := New(log.New(buf, "test", log.LstdFlags), test.useRemoteFS, test.isCanary, "accesskey", "accesssecret")
getter.RemoteGetter = &fakeRemote{data: test.data, err: test.remoteErr}
getter.LocalGetter = &fakeLocal{data: test.data, err: test.localErr}
fh, source, err := getter.GetMessage("localpath", test.host, test.bucket, test.key)

// make sure that everything was as expected
assert.Equal(t, test.expectedSource, source, fmt.Sprintf("test %q", test.name))
assert.Equal(t, test.expectedErr, err, fmt.Sprintf("test %q", test.name))

for _, expected := range test.expectedLogs {
assert.Contains(t, buf.String(), expected, fmt.Sprintf("test %q", test.name))
}

if err != nil {
// no need to verify fh contents, go to next test case
continue
}

// verify message content
data, err := ioutil.ReadAll(fh)
assert.NoError(t, err, fmt.Sprintf("test %q", test.name))
assert.Equal(t, test.data, data, fmt.Sprintf("test %q", test.name))
fh.Close()
}
}

type fakeRemote struct {
data []byte
err error
}

func (f *fakeRemote) GetRemoteMessage(accessKey, accessSecret, host, bucket, key string) (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(f.data)), f.err
}

type fakeLocal struct {
data []byte
err error
}

func (f *fakeLocal) Open(localPath string) (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(f.data)), f.err
}