Hlavní navigace

Projekt MinIO: jedna z nejužitečnějších aplikací naprogramovaných v Go

Pavel Tišnovský

Ve stručnosti se seznámíme s projektem nazvaným MinIO. Jedná se o sadu několika služeb a nástrojů, které uživatelům poskytují distribuované datové úložiště určené pro ukládání obecných (nestrukturovaných) dat.

Doba čtení: 48 minut

Sdílet

11. Výpis všech objektů ve zvoleném bucketu

12. Podrobnější informace o objektech ve zvoleném bucketu

13. Uložení objektu z bucketu do lokálního souboru

14. Přečtení obsahu objektu s textem

15. Alternativní způsob přístupu k obsahu objektu

16. Poslání dat do Minia s jejich uložením do objektu

17. Kopie objektu v rámci Minia bez jejich přesunu na lokální počítač

18. Obsah následující části seriálu

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Projekt MinIO – jedna z nejužitečnějších aplikací naprogramovaných v Go

V dnešním článku se ve stručnosti seznámíme s projektem nazvaným MinIO. Jedná se o sadu několika služeb a nástrojů, které uživatelům poskytují distribuované datové úložiště určené pro ukládání obecných (nestrukturovaných) dat. Typicky se jedná o soubory používané v oblasti AI (Artifical Intelligence) a ML (Machine Learning), ovšem kromě těchto populárních (a vlastně do značné míry i módních) oblastí IT je pochopitelně možné službu MinIO použít i pro ukládání logů, souborů, k nimž je zapotřebí rychle přistupovat z mnoha různých, mnohdy vzájemně vzdálených oblastí (zde využijeme možnost distribuovaného systému), jako centrální úložiště dokumentů, obrázků, videí, pochopitelně i obrazů souborových systémů pro Docker apod. MinIO dosahuje velmi slušné rychlosti přístupu k datům (při vhodně nadimenzované síti, která je většinou limitujícím faktorem) a mj. i díky velmi dobré stabilitě ukazuje přednosti programovacího jazyka Go, v němž je celý systém naprogramován.

Obrázek 1: Logo používané projektem MinIO.

Dnes se s projektem MinIO seznámíme především z pohledu vývojáře. Nebudeme se tedy do všech podrobností zabývat tím, jak MinIO nasadit do rozsáhlého systému s geograficky vzdálenými serverovnami, jak nastavit distribuci dat atd. Zajímat nás budou především způsoby ukládání a načítání dat, a to jak s využitím konzole ovládané z příkazového řádku, tak i přes webové rozhraní. Ovšem prakticky vždy je nutné k datům přistupovat i programově, což je obsahem druhé části dnešního článku, kde si ukážeme několik příkladů používajících poměrně snadno ovladatelnou knihovnu nazvanou MinIO Client SDK.

Obrázek 2: Porovnání používání projektu MinIO a dalších podobných technologií. Tato statistika je ovšem založena na počtu stažení, nikoli na celkovém počtu instalací.

Poznámka: v dalším textu budu mnohdy namísto MinIO psát pouze Minio (a to i v jiných pádech), což by – doufejme – mělo vést k méně kostrbatým větám.

2. MinIO a AWS S3

Jedním z nejdůležitějších a v důsledku i nejpraktičtějších vlastností projektu MinIO je fakt, že se pro přístup k datům používá stejná technologie, jaká je implementována i v populární službě Amazon S3 či možná přesněji AWS S3. To mj. znamená, že dodávaný MinIO Client SDK popsaný v navazujících kapitolách může sloužit jak pro přístup k datům uloženým v Miniu, tak i k datům uloženým ve cloudu na S3. Díky tomu lze například snadněji nastavit konfiguraci pro vývoj, konfiguraci CI, zajistit si možnost využití veřejného cloudu (S3) nebo naopak privátního cloudu (založeného na Miniu) atd. Navíc je MinIO Client SDK určen jen pro přístup k datům a nikoli pro ovládání dalších služeb, takže je jeho zahrnutí do vyvíjené aplikace méně náročné na systémové prostředky. Musíme si totiž uvědomit, že přístup k datům je mnohdy zapotřebí i z relativně málo výkonných zařízení IoT atd. (mj. i z tohoto důvodu se MinIO co do snadnosti integrace porovnává s Redisem, i když oblasti nasazení těchto dvou technologií jsou mnohdy značně odlišné).

Poznámka: zkratka S3 vznikla ze sousloví „Simple Storage Service“ a skutečně se jedná o implementaci služby, která je snadno použitelná, a to prakticky ve všech mainstreamových programovacích jazycích.

3. Instalace služby MinIO i ovládací konzole mc

Instalace služby (přesněji řečeno serverové části) projektu MinIO je snadná a přímočará. Jelikož se jedná o aplikaci naprogramovanou v jazyce Go, je služba dodávána ve formě jediného (i když relativně objemného) spustitelného souboru. K dispozici je ovšem i obraz pro Docker, překlad lze provést ze zdrojových souborů atd. Dnes se zaměříme na první způsob, tedy na stažení již připravených souborů projektu MinIO. Musíme si pouze vybrat soubor pro právě používaný operační systém a procesorovou architekturu. Pro testování budu používat Linux a architekturu x86–64. Službu MinIO, přesněji řečeno spustitelný binární soubor, který po svém spuštění službu nabídne, získáme jednoduše jediným příkazem:

$ wget https://dl.min.io/server/minio/release/linux-amd64/minio
Poznámka: celková velikost tohoto souboru přesahuje 40 megabajtů a stahování může být relativně pomalé. Může se stát, že budete potřebovat i několik minut.

Následně je nutné nastavit příznak „x“ pro stažený soubor, aby bylo možné službu spustit přímo z příkazového řádku:

$ chmod +x minio

Dále pro jistotu otestujeme, zda je stažený soubor skutečně spustitelný:

$ ./minio version
Version: 2019-10-12T01:39:57Z
Release-Tag: RELEASE.2019-10-12T01-39-57Z
Commit-ID: bd106408462ecef70debf51f1e6179de950c5812

Podobným způsobem lokálně nainstalujeme i konzoli projektu MinIO. Ta se jmenuje mc. Nejdříve stáhneme příslušný spustitelný soubor pro zvolený operační systém a architekturu mikroprocesoru:

$ wget https://dl.min.io/client/mc/release/linux-amd64/mc
Poznámka: opět se připravte na spíše pomalejší stahování.

Následně, podobně jako v předchozích krocích, nastavíme příznak „x“, aby byla konzole spustitelná:

$ chmod +x mc

A ověříme si, že tomu tak skutečně je:

$ ./mc version
 
Version: 2019-10-09T22:54:57Z
Release-tag: RELEASE.2019-10-09T22-54-57Z
Commit-id: f93fe1330a3647b1afaff0ed8c188d2897bf391e
Poznámka: jméno konzole mc může kolidovat se známým souborovým manažerem Midnight Commander. To nám však prozatím nebude příliš vadit, protože jsme provedli pouze lokální instalaci a budeme tedy rozlišovat mezi těmito příkazy:
$ mc
$ ./mc
Poznámka 2: ovšem pochopitelně nám nic nebrání si spustitelný soubor s konzolí služby MinIO přejmenovat.

4. Spuštění služby MinIO na lokálním počítači

Pokud již máme připravený spustitelný soubor nazvaný minio, je inicializace a následné spuštění služby MinIO na lokálním počítači otázkou jediného příkazu. Musíme pouze specifikovat, že se má spustit server a na jakém disku a adresáři budou umístěny soubory spravované službou MinIO:

$ ./minio server /tmp/minio
 
Endpoint:  http://10.0.0.29:9000  http://127.0.0.1:9000
AccessKey: WDGGENVCJDQVFM3TBM88
SecretKey: 8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C
 
Browser Access:
   http://10.0.0.29:9000  http://127.0.0.1:9000
 
Command-line Access: https://docs.min.io/docs/minio-client-quickstart-guide
   $ mc config host add myminio http://10.0.0.29:9000 WDGGENVCJDQVFM3TBM88 8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C
 
Object API (Amazon S3 compatible):
   Go:         https://docs.min.io/docs/golang-client-quickstart-guide
   Java:       https://docs.min.io/docs/java-client-quickstart-guide
   Python:     https://docs.min.io/docs/python-client-quickstart-guide
   JavaScript: https://docs.min.io/docs/javascript-client-quickstart-guide
   .NET:       https://docs.min.io/docs/dotnet-client-quickstart-guide

Povšimněte si, že se po spuštění zobrazily všechny informace nutné pro spuštění webového rozhraní, pro použití konzole ovládané z příkazového řádku i pro instalaci SDK pro podporované programovací jazyky. Dále jsme získali i dvojici klíčů, které použijeme v následujících kapitolách, a to jak při přístupu přes webovou konzoli, tak i v demonstračních příkladech založených na SDK Minia.

Poznámka: pokud používáte Docker (a obraz se serverem MinIO), ujistěte se, že je správně nastavené mapování portů, aby se bylo možné k běžící instanci služby zvenku připojit.

Jestliže se spuštění služby Minio nepovedlo, je vhodné zkontrolovat, zda adresář předaný při spuštění neobsahuje podadresáře mapované (přesněji řečeno připojené) z jiného disku či souborového systému. Celý adresář by měl (z pohledu Minia) ležet v jediném diskoFGetObject, popř. ve svazku spravovaném vlastním operačním systémem.

5. Ovládání Minia z webového prohlížeče

Minio je možné částečně ovládat z webového prohlížeče, konkrétně ze stránky, která byla zobrazena při spouštění služby:

$ ./minio server /tmp/minio
 
...
...
...
 
Browser Access:
   http://10.0.0.29:9000  http://127.0.0.1:9000
 
...
...
...

Po otevření adresy (v našem případě lokální 127.0.0.1:9000) je nutné zadat AccessKey (20 znaků) a SecretKey (40 znaků), které taktéž známe, protože se opět zobrazily při spuštění služby:

$ ./minio server /tmp/minio
 
...
AccessKey: WDGGENVCJDQVFM3TBM88
SecretKey: 8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C
 
...
...
...

Obrázek 3: Přihlašovací obrazovka, do které je nutné přenést AccessKey a SecretKey.

Obrázek 4: Běžná copy&paste procedura :-)

Obrázek 5: Data jsou v Miniu představována objekty, které jsou ukládány do bucketů. Zpočátku je úložiště zcela prázdné.

Obrázek 6: Přes ikonu + lze vytvořit nový bucket. V příkladech budeme používat buckety „foo“ a „bar“.

Obrázek 7: V pravém dolním rohu je patrné kontextové menu získané po rozkliku ikony +.

Obrázek 8: Uložení objektu do úložiště. Jedná se o obrázek dostupný na adrese https://blog.golang.org/go-brand/logos.jpg.

6. Ovládání Minia z příkazového řádku přes konzoli

Pro ovládání Minia, tj. především pro práci s buckety a objekty, lze použít i nástroj pojmenovaný mc, který jsme nainstalovali v rámci třetí kapitoly. Tento nástroj po svém spuštění bez parametrů vypíše všechny podporované příkazy:

ls       list buckets and objects
tree     list buckets and objects in a tree format
mb       make a bucket
rb       remove a bucket
cat      display object contents
head     display first 'n' lines of an object
pipe     stream STDIN to an object
share    generate URL for temporary access to an object
cp       copy objects
mirror   synchronize objects to a remote site
find     search for objects
sql      run sql queries on objects
stat     stat contents of objects
diff     list differences in object name, size, and date between buckets
rm       remove objects
event    manage object notifications
watch    watch for object events
policy   manage anonymous access to objects
admin    manage MinIO servers
session  manage saved sessions for cp command
config   manage mc configuration file
update   check for a new software update
version  print version info

Před použitím většiny operací, které ovládají službu Minio je nutné se přihlásit, a to následujícím způsobem:

$ ./mc config host add myminio http://10.0.0.29:9000 WDGGENVCJDQVFM3TBM88 8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C
 
Added `myminio` successfully.

Další příkazy budou používat jméno „myminio“. Díky pojmenování „sezení“ je možné z jediného počítače v danou chvíli ovládat i několik instancí Minia.

Poznámka: tento nástroj ve větší míře využijeme v navazující části tohoto seriálu.

7. Instalace knihovny s rozhraním pro Minio (Minio Client SDK)

V této kapitole si ve stručnosti ukážeme, jakým způsobem lze nainstalovat Minio Client SDK, což je poněkud dlouhý název pro knihovnu, jenž programátorům zajišťuje rozhraní mezi programovacím jazykem a aplikací v něm vyvíjené na jedné straně a službou Minio na straně druhé. Zaměříme se na použití klienta v programovacím jazyku Go, i když je pochopitelně možné Minio ovládat i z aplikací naprogramovaných v jiných programovacích jazycích. Touto problematikou se však budeme zabývat až příště.

SDK pro Go je postaveno na použití modulů, takže instalaci budeme muset provést z projektu, který má systém modulů povolen. Vytvoření takového projektu je ve skutečnosti velmi snadné, protože nám bude postačovat vytvoření adresáře s prázdným projektem a inicializace systému modulů v tomto adresáři:

$ mkdir minio1
 
$ cd minio1
 
$ go mod init minio1
 
go: creating new go.mod: module minio1

Po zadání předchozích tří příkazů by měl v novém adresáři „minio1“ vzniknout soubor s názvem „go.mod“. V následujícím kroku již nainstalujeme celé SDK:

$ go get github.com/minio/minio-go/v6
 
go: finding github.com/minio/minio-go/v6 v6.0.44
go: finding github.com/minio/minio-go v6.0.14+incompatible
go: downloading github.com/minio/minio-go/v6 v6.0.44
go: downloading github.com/minio/minio-go v6.0.14+incompatible
go: extracting github.com/minio/minio-go v6.0.14+incompatible
go: extracting github.com/minio/minio-go/v6 v6.0.44
go: downloading golang.org/x/net v0.0.0-20190522155817-f3200d17e092
go: downloading golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
go: downloading github.com/minio/sha256-simd v0.1.1
go: downloading gopkg.in/ini.v1 v1.42.0
go: extracting github.com/minio/sha256-simd v0.1.1
go: extracting gopkg.in/ini.v1 v1.42.0
go: extracting golang.org/x/net v0.0.0-20190522155817-f3200d17e092
go: extracting golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
go: downloading golang.org/x/sys v0.0.0-20190422165155-953cdadca894
go: extracting golang.org/x/sys v0.0.0-20190422165155-953cdadca894
go: finding github.com/minio/sha256-simd v0.1.1
go: finding golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
go: finding gopkg.in/ini.v1 v1.42.0
go: finding golang.org/x/sys v0.0.0-20190422165155-953cdadca894
Poznámka: předchozí příkaz „go get“ je skutečně nutné zavolat z adresáře, v němž je připravena kostra projektu založeného na modulech. V opačném případě bude „go get“ vypisovat poměrně kryptická chybová hlášení.

8. Kostra aplikace, která provede inicializaci klienta služby Minio

Nyní, když již máme SDK pro službu Minio nainstalovaný, si můžeme vytvořit kostru aplikace, po jejímž spuštění se provede inicializace klienta. Pro úspěšné připojení je nutné znát minimálně tři údaje: URL, na němž je služba Minio spuštěna, dále Access Key ID, což je řetězec o délce dvaceti znaků a nakonec i Secret Access Key představovaný řetězcem o délce čtyřiceti znaků. Všechny tři údaje již známe, protože je služba Minio vypsala při svém spuštění (viz též třetí kapitolu):

Endpoint:  http://10.0.0.29:9000  http://127.0.0.1:9000
AccessKey: WDGGENVCJDQVFM3TBM88
SecretKey: 8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C

V prvním demonstračním příkladu tyto údaje pro jednoduchost přímo zadáme do zdrojového kódu (což pochopitelně neznamená, že se jedná o doporučení, kam podobné údaje ukládat :-):

endpoint := "127.0.0.1:9000"
 
accessKeyID := "WDGGENVCJDQVFM3TBM88"
secretAccessKey := "8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C"
Poznámka: povšimněte si, že endpoint se zadává bez udání protokolu.

Pro inicializaci klienta je nutné zavolat funkci New z balíčku minio-go. Této funkci se předají všechny tři výše zmíněné údaje a navíc i příznak oznamující, zda se má pro komunikaci použít SSL/TLS či nikoli:

minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)

Funkce New vrací referenci na vytvořeného klienta, popř. strukturu s informací o chybě, která při inicializaci klienta nastala. Prozatím si tuto strukturu, popř. chybu pouze vypíšeme, což je ostatně patrné i při pohledu na úplný zdrojový kód příkladu:

package main
 
import (
        "github.com/minio/minio-go/v6"
        "log"
)
 
func main() {
        endpoint := "127.0.0.1:9000"
 
        // it is needed to change the following two keys
        accessKeyID := "WDGGENVCJDQVFM3TBM88"
        secretAccessKey := "8YxAW5qxYKBzo7qLGuqxuVDwK5NekY2k7v9ZIZ9C"
 
        useSSL := true
 
        // initialize minio client object
        minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
}

Po překladu (go build) a spuštění příkladu by se měly vypsat informace o inicializovaném klientovi služby Minio:

2019/12/14 11:05:58 &minio.Client{endpointURL:(*url.URL)(0xc000174000), credsProvider:(*credentials.Credentials)(0xc0000ae780), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:true, httpClient:(*http.Client)(0xc000192c90), bucketLocCache:(*minio.bucketLocationCache)(0xc000284100), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000192cf0), lookup:0}
Poznámka: ve skutečnosti dojde k inicializaci klienta i ve chvíli, kdy budou klíče zadány špatně, protože se prozatím neprovedlo připojení s ověřením klíčů.

9. Úprava příkladu – zadání parametrů připojení z příkazového řádku, výpis bucketů

Předchozí demonstrační příklad byl sice jednoduchý, ovšem používali jsme v něm údaje o připojení zapsané přímo do zdrojového kódu, což není praktické. Proto příklad upravíme, a to takovým způsobem, aby se údaje nutné pro připojení do služby Minio získávaly z příkazového řádku. Pro tento účel použijeme standardní balíček flag:

var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
flag.Parse()
Poznámka: zapomenout nesmíme především na zavolání funkce flag.Parse().

Inicializace klienta je snadná, jen si musíme uvědomit, že příznaky získané z příkazové řádky jsou ukazateli na řetězce a nikoli přímo řetězce:

minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
if err != nil {
        log.Fatalln(err)
}

Dále demonstrační příklad rozšíříme takovým způsobem, aby se vypsaly všechny buckety, které jsou v Miniu uloženy a jsou dostupné pro zvoleného uživatele (specifikovaného klíčem). Použijeme funkci ListBuckets, která vrátí buď chybu nebo seznam bucketů:

func listBuckets(minioClient *minio.Client) {
        fmt.Println("List of buckets:")
 
        buckets, err := minioClient.ListBuckets()
        if err != nil {
                log.Fatalln(err)
                return
        }
        for i, bucket := range buckets {
                fmt.Printf("%d\t%+v\n", i, bucket)
        }
}

Úplný zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func listBuckets(minioClient *minio.Client) {
        fmt.Println("List of buckets:")
 
        buckets, err := minioClient.ListBuckets()
        if err != nil {
                log.Fatalln(err)
                return
        }
        for i, bucket := range buckets {
                fmt.Printf("%d\t%+v\n", i, bucket)
        }
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        listBuckets(minioClient)
}

10. Typické chyby, které mohou nastat

Nyní, když můžeme parametry připojení do Minia specifikovat na příkazovém řádku, je možné se seznámit s typickými chybami, které mohou nastat.

Pokus o připojení s nekorektním AccessKey:

$ ./minio3
 
2019/12/14 11:52:53 &minio.Client{endpointURL:(*url.URL)(0xc000158000), credsProvider:(*credentials.Credentials)(0xc00006c840), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc00008f860), bucketLocCache:(*minio.bucketLocationCache)(0xc00000e7c0), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc00008f8c0), lookup:0}
List of buckets:
2019/12/14 11:52:53 Access Denied.

Chybný SecretKey:

$ ./minio3 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIl
 
2019/12/14 13:02:11 &minio.Client{endpointURL:(*url.URL)(0xc000178000), credsProvider:(*credentials.Credentials)(0xc0000ae7e0), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000938c0), bucketLocCache:(*minio.bucketLocationCache)(0xc0000a0780), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000093920), lookup:0}
List of buckets:
2019/12/14 13:02:11 The request signature we calculated does not match the signature you provided. Check your key and signing method.

Pokus o použití SSL/TLS u služby běžící přes HTTP:

$ ./minio3 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE -useSSL=true
 
2019/12/14 13:02:27 &minio.Client{endpointURL:(*url.URL)(0xc000158000), credsProvider:(*credentials.Credentials)(0xc00006c840), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:true, httpClient:(*http.Client)(0xc00012db60), bucketLocCache:(*minio.bucketLocationCache)(0xc000239ae0), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc00012dbc0), lookup:0}
List of buckets:
2019/12/14 13:02:27 Get https://127.0.0.1:9000/: http: server gave HTTP response to HTTPS client

Úspěšné připojení s výpisem bucketů (existují dva):

$ ./minio3 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/14 13:01:54 &minio.Client{endpointURL:(*url.URL)(0xc000172000), credsProvider:(*credentials.Credentials)(0xc0000ac7e0), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000938c0), bucketLocCache:(*minio.bucketLocationCache)(0xc00009e780), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000093920), lookup:0}
List of buckets:
0       {Name:bar CreationDate:2019-12-14 10:30:57.918 +0000 UTC}
1       {Name:foo CreationDate:2019-12-14 12:01:10.533 +0000 UTC}

11. Výpis všech objektů ve zvoleném bucketu

Ve třetím demonstračním příkladu si ukážeme, jakým způsobem je možné získat seznam všech objektů, které jsou uloženy ve zvoleném bucketu. Tuto funkcionalitu nabízí metoda listObjects, které je nutné předat jméno bucketu, prefix (lze ho použít pro rychlou filtraci na straně služby), příznak, zda se mají procházet i objekty v podadresářích a posledním parametrem je kanál, který lze použít pro synchronizaci gorutiny, která by objekty načítala. V našem jednoduchém příkladu však kanál na konci funkce listObjects pouze uzavřeme a nebudeme se starat o to, zda do něj byla zapsána hodnota či nikoli:

func listObjects(minioClient *minio.Client, bucket string) {
        fmt.Println("List of objects for bucket:", bucket)
 
        done := make(chan struct{})
        defer close(done)
 
        objects := minioClient.ListObjects(bucket, "", false, done)
        for object := range objects {
                if object.Err != nil {
                        log.Println(object.Err)
                        return
                }
                fmt.Println(object)
        }
}

Výpis bude proveden v tomto (prozatím nijak neupraveném) formátu:

{"f2042bf5780d07253480fb8c64c60850-1" t.go 2019-12-14 16:10:38.411 +0000 UTC 56  0001-01-01 00:00:00 +0000 UTC map[] map[] { 02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4} [] STANDARD <nil<}

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func listBuckets(minioClient *minio.Client) {
        fmt.Println("List of buckets:")
 
        buckets, err := minioClient.ListBuckets()
        if err != nil {
                log.Fatalln(err)
                return
        }
        for i, bucket := range buckets {
                fmt.Printf("%d\t%+v\n", i, bucket)
        }
}
 
func listObjects(minioClient *minio.Client, bucket string) {
        fmt.Println("List of objects for bucket:", bucket)
 
        done := make(chan struct{})
        defer close(done)
 
        objects := minioClient.ListObjects(bucket, "", false, done)
        for object := range objects {
                if object.Err != nil {
                        log.Println(object.Err)
                        return
                }
                fmt.Println(object)
        }
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        listBuckets(minioClient)
        listObjects(minioClient, "foo")
}

Po spuštění tohoto příkladu by se nejdříve měly vypsat všechny buckety a následně i objekty z bucketu „foo“ (ty jsme přidali přes webové rozhraní):

09:17 $ ./minio4 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/16 09:17:45 &minio.Client{endpointURL:(*url.URL)(0xc00015a000), credsProvider:(*credentials.Credentials)(0xc00006e840), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000138f0), bucketLocCache:(*minio.bucketLocationCache)(0xc00000e7a0), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000013950), lookup:0}
 
List of buckets:
0       {Name:bar CreationDate:2019-12-14 10:30:57.918 +0000 UTC}
1       {Name:foo CreationDate:2019-12-14 17:13:38.282 +0000 UTC}
 
List of objects for bucket: foo
{"f2042bf5780d07253480fb8c64c60850-1" t.go 2019-12-14 16:10:38.411 +0000 UTC 56  0001-01-01 00:00:00 +0000 UTC map[] map[] { 02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4} [] STANDARD <nil<}

12. Podrobnější informace o objektech ve zvoleném bucketu

O objektech, které jsou uloženy ve vybraném bucketu, je možné získat i podrobnější informace. Ve struktuře popisující každý objekt, nalezneme mj. i tyto atributy:

# Atribut Význam
1 Key klíč (jednoznačný v rámci bucketu)
2 Size velikost v bajtech (nikoli ve znacích, i když jde o JSON, XML atd.)
3 LastModified čas poslední modifikace
4 ETag MD5 heš (128 bitů reprezentovaných třiceti dvěma hexadecimálními číslicemi)

Z tohoto důvodu je možné upravit funkci pro výpis informací o objektech tak, aby se vypsaly potřebné atributy (viz zvýrazněný řádek):

func listObjects(minioClient *minio.Client, bucket string, prefix string) {
        fmt.Println("List of objects for bucket:", bucket)
 
        done := make(chan struct{})
        defer close(done)
 
        objects := minioClient.ListObjects(bucket, prefix, false, done)
        for object := range objects {
                if object.Err != nil {
                        log.Println(object.Err)
                        return
                }
                fmt.Printf("Key: %s,  Size: %d,  Tag: %s\n", object.Key, object.Size, object.ETag)
        }
}

Takto upravený příklad zobrazí podrobnější informace o objektu s klíčem „logos.jpg“, délkou 48913 bajtů a tagem nastaveným na „f95e4a85dafc56313883f8571cfc8143“:

$ ./minio5 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/14 16:43:51 &minio.Client{endpointURL:(*url.URL)(0xc00015a000), credsProvider:(*credentials.Credentials)(0xc00006e840), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000138f0), bucketLocCache:(*minio.bucketLocationCache)(0xc00000e7c0), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000013950), lookup:0}
List of buckets:
0       {Name:bar CreationDate:2019-12-14 10:30:57.918 +0000 UTC}
1       {Name:foo CreationDate:2019-12-14 12:01:10.533 +0000 UTC}
List of objects for bucket: foo
Key: logos.jpg,  Size: 48913,  Tag: "f95e4a85dafc56313883f8571cfc8143-1"

Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func listBuckets(minioClient *minio.Client) {
        fmt.Println("List of buckets:")
 
        buckets, err := minioClient.ListBuckets()
        if err != nil {
                log.Fatalln(err)
                return
        }
        for i, bucket := range buckets {
                fmt.Printf("%d\t%+v\n", i, bucket)
        }
}
 
func listObjects(minioClient *minio.Client, bucket string, prefix string) {
        fmt.Println("List of objects for bucket:", bucket)
 
        done := make(chan struct{})
        defer close(done)
 
        objects := minioClient.ListObjects(bucket, prefix, false, done)
        for object := range objects {
                if object.Err != nil {
                        log.Println(object.Err)
                        return
                }
                fmt.Printf("Key: %s,  Size: %d,  Tag: %s\n", object.Key, object.Size, object.ETag)
        }
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        var objectPrefix = flag.String("prefix", "", "Prefix for objects to be listed")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        listBuckets(minioClient)
        listObjects(minioClient, "foo", *objectPrefix)
}

13. Uložení objektu z bucketu do lokálního souboru

Velmi často se setkáme s požadavkem, aby se nějaký objekt zkopíroval z Minia do lokálního souboru. K tomuto problému lze přistoupit dvěma způsoby – buď se použijí obecné funkce pro přístup k obsahu objektu, nebo se zavolá specializovaná metoda nazvaná FGetObject, která se o přečtení obsahu objektu a jeho uložení do lokálního souboru automaticky postará. Této metodě je nutné předat název bucketu, klíč objektu, jméno lokálního souboru a popř. další parametry (ty jsou uloženy ve struktuře, která může být prázdná):

err = minioClient.FGetObject("foo", "logos.jpg", "logos.jpg", minio.GetObjectOptions{})
if err != nil {
        log.Fatalln(err)
}

Po spuštění této metody je vhodné se přesvědčit, zda se stažení skutečně podařilo:

$ file logos.jpg 
 
logos.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 1600x878, frames 3

Úplný zdrojový kód demonstračního příkladu, který kopii objektu do lokálního souboru provede, může vypadat následovně:

package main
 
import (
        "flag"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        err = minioClient.FGetObject("foo", "logos.jpg", "logos.jpg", minio.GetObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
}

14. Přečtení obsahu objektu s textem

Mnohdy potřebujeme obsah objektu programově přečíst z úložiště Minia a ihned zpracovat. V takovém případě ovšem není vhodné použít výše zmíněnou metodu FGetObject a posléze soubor znovu načíst do aplikace. Jak z paměťového, tak i výkonnostního hlediska je lepší obsah objektu přečíst přímo (bez meziuložení do souboru), k čemuž slouží metoda nazvaná GetObject s prakticky stejnými parametry, jaké má metoda FGetObject (pochopitelně chybí jméno souboru):

object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
if err != nil {
        log.Fatalln(err)
}
defer object.Close()

Výše uvedeným postupem získáme strukturu implementující standardní rozhraní Reader, což nám například umožňuje zpracovávat obsah (textového) objektu řádek po řádku:

scanner := bufio.NewScanner(object)
scanner.Split(bufio.ScanLines)
 
for scanner.Scan() {
        fmt.Println(scanner.Text())
}

Obrázek 9: Založení nového objektu pod klíčem „t.go“, jehož obsahem je text. Tento soubor použijeme v příkladu popsaném v této kapitole

Opět se podívejme na úplný výpis demonstračního příkladu, který načte nově vytvořený objekt s obsahem získaným ze zdrojového (textového) souboru:

package main
 
import (
        "bufio"
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func printObject(minioClient *minio.Client, bucket string, objectName string) {
        object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
        defer object.Close()
 
        scanner := bufio.NewScanner(object)
        scanner.Split(bufio.ScanLines)
 
        for scanner.Scan() {
                fmt.Println(scanner.Text())
        }
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        printObject(minioClient, "foo", "t.go")
}
17:28 $ ./minio7 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/14 17:28:06 &minio.Client{endpointURL:(*url.URL)(0xc000172000), credsProvider:(*credentials.Credentials)(0xc0000ac7e0), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000938c0), bucketLocCache:(*minio.bucketLocationCache)(0xc00009e780), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000093920), lookup:0}
 
package main
 
func main() {
        println(`foo "bar" baz`)
}

15. Alternativní způsob přístupu k obsahu objektu

Obsah zvoleného objektu, který získáme z Minia, lze zpracovat různými způsoby. Stačí si uvědomit, že stále pracujeme s objektem implementujícím rozhraní Reader, takže lze použít všechny funkce a metody, které s tímto rozhraním dokáží spolupracovat. V dalším demonstračním příkladu je ukázáno použití funkce ioutil.ReadAll, která nám umožní načíst všechna data poskytovaná Readerem a vrátit je jako řez (slice) bajtů:

$ go doc ioutil.ReadAll
 
func ReadAll(r io.Reader) ([]byte, error)
    ReadAll reads from r until an error or EOF and returns the data it read. A
    successful call returns err == nil, not err == EOF. Because ReadAll is
    defined to read from src until EOF, it does not treat an EOF from Read as an
    error to be reported.

Celý řez bajtů posléze snadno převedeme na řetězec a vytiskneme:

object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
if err != nil {
        log.Fatalln(err)
}
defer object.Close()
 
bytes, err := ioutil.ReadAll(object)
if err != nil {
        log.Fatalln(err)
}
fmt.Println(string(bytes))

Opět si ukažme úplný zdrojový kód tohoto demonstračního příkladu:

package main

import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "io/ioutil"
        "log"
)
 
func printObject(minioClient *minio.Client, bucket string, objectName string) {
        object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
        defer object.Close()
 
        bytes, err := ioutil.ReadAll(object)
        if err != nil {
                log.Fatalln(err)
        }
        fmt.Println(string(bytes))
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        printObject(minioClient, "foo", "t.go")
}

16. Poslání dat do Minia s jejich uložením do objektu

V předchozích kapitolách jsme si ukázali, jak lze získat obsah objektu z úložiště Minia. Víme, jak se obsah objektu uloží do souboru metodou FGetObject popř. získá ve formě sekvence bajtů metodou GetObject. Existuje pochopitelně i opačný postup, tj. poslání dat do Minia s jejich uložením. Pokud jsou data uložená v lokálním souboru, je situace jednoduchá, neboť lze použít metodu FPutObject:

length, err := minioClient.FPutObject("foo", "minio9.go", "minio9.go", minio.PutObjectOptions{
        ContentType: "text/plain;charset=UTF-8",
})
if err != nil {
        fmt.Println(err)
        return
}
fmt.Println("Successfully uploaded bytes: ", length)
Poznámka: povšimněte si, že je možné uvést i typ obsahu, což může být užitečné při jeho dalším zpracování.

V dalším demonstračním příkladu se nejdříve do úložiště Minia pošle textový soubor se zdrojovým textem a následně se zpětně přečte a vypíše jeho obsah:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "io/ioutil"
        "log"
)
 
func printObject(minioClient *minio.Client, bucket string, objectName string) {
        object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
        defer object.Close()
 
        bytes, err := ioutil.ReadAll(object)
        if err != nil {
                log.Fatalln(err)
        }
        fmt.Println(string(bytes))
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        length, err := minioClient.FPutObject("foo", "minio9.go", "minio9.go", minio.PutObjectOptions{
                ContentType: "text/plain;charset=UTF-8",
        })
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Println("Successfully uploaded bytes: ", length)
 
        printObject(minioClient, "foo", "minio9.go")
}

Výsledek může vypadat takto (je zkrácený):

17:42 $ ./minio9 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/14 17:47:29 &minio.Client{endpointURL:(*url.URL)(0xc00015a000), credsProvider:(*credentials.Credentials)(0xc00006e840), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc0000138f0), bucketLocCache:(*minio.bucketLocationCache)(0xc00000e7a0), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc000013950), lookup:0}
 
Successfully uploaded bytes:  1299
...
...
...

Obrázek 10: V úložišti byl vytvořen nový objekt uložený pod klíčem „minio9.go“.

17. Kopie objektu v rámci Minia bez jejich přesunu na lokální počítač

Poslední operací, s níž se dnes seznámíme, je kopie objektu v rámci Minia, bez nutnosti přesunu dat na lokální počítač (což je obecně velmi pomalá operace, protože pro Minio je úzkým hrdlem síťové rozhraní). Samotnou kopii objektu zajišťuje metoda CopyObject:

err = minioClient.CopyObject(dst, src)
if err != nil {
        log.Fatalln(err)
}

Této metodě je nutné předat dvě struktury reprezentující cílový a zdrojový objekt. Pro vytvoření těchto struktur existují funkce–konstruktory nazvané NewSourceInfo a NewDestinationInfo, kterým je nutné předat minimálně jméno bucketu a jméno objektu:

src := minio.NewSourceInfo(bucket, from, nil)
dst, err := minio.NewDestinationInfo(bucket, to, nil, nil)
Poznámka: je tedy možné, aby se kopie provedla mezi dvěma rozdílnými buckety.

Úplný zdrojový kód dnešního posledního demonstračního příkladu, který zkopíruje objekt s klíčem „minio9.go“ do objektu „minio10.go“, vypadá následovně:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "io/ioutil"
        "log"
)
 
func printObject(minioClient *minio.Client, bucket string, objectName string) {
        object, err := minioClient.GetObject(bucket, objectName, minio.GetObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
        defer object.Close()
 
        bytes, err := ioutil.ReadAll(object)
        if err != nil {
                log.Fatalln(err)
        }
        fmt.Println(string(bytes))
}
 
func copyObject(minioClient *minio.Client, bucket string, from string, to string) {
        src := minio.NewSourceInfo(bucket, from, nil)
        dst, err := minio.NewDestinationInfo(bucket, to, nil, nil)
        if err != nil {
                log.Fatalln(err)
        }
 
        err = minioClient.CopyObject(dst, src)
        if err != nil {
                log.Fatalln(err)
        }
}
 
func main() {
        var endpoint = flag.String("endpoint", "127.0.0.1:9000", "MinIO service endpoint")
        var accessKeyID = flag.String("accessKeyID", "", "Access key ID for MinIO")
        var secretAccessKey = flag.String("secretAccessKey", "", "Secret access key for MinIO")
        var useSSL = flag.Bool("useSSL", false, "Use SSL for communication with MinIO")
        flag.Parse()
 
        // initialize minio client object
        minioClient, err := minio.New(*endpoint, *accessKeyID, *secretAccessKey, *useSSL)
        if err != nil {
                log.Fatalln(err)
        }
 
        // everything seems to be ok
        log.Printf("%#v\n", minioClient)
 
        printObject(minioClient, "foo", "minio9.go")
        copyObject(minioClient, "foo", "minio9.go", "minio10.go")
        printObject(minioClient, "foo", "minio10.go")
}

Obrázek 11: Objekty „minio9.go“ a „minio10.go“ by měly mít totožný obsah.

18. Obsah následující části seriálu

V navazující části tohoto seriálu si ukážeme další (již poněkud komplikovanější) operace nabízené SDK, ovšem nezapomeneme ani na použití nástroje mc a na ukázku využití služby Minia z dalších programovacích jazyků, zejména z Pythonu, jenž se v souvislosti s Miniem i s AWS S3 používá velmi často.

Obrázek 12: Vybraný objekt či objekty je možné z úložiště smazat. Prozatím jsme nenastavovali uživatelská práva, takže smazání je proveditelné.

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně pět až šest megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 minio1.go kostra aplikace, která provede inicializaci klienta služby Minio https://github.com/tisnik/go-root/blob/master/article46/mi­nio01/minio1.go
2 minio2.go zadání parametrů připojení z příkazového řádku https://github.com/tisnik/go-root/blob/master/article46/mi­nio02/minio2.go
3 minio3.go zadání parametrů připojení z příkazového řádku, výpis bucketů https://github.com/tisnik/go-root/blob/master/article46/mi­nio03/minio3.go
4 minio4.go výpis všech objektů ve zvoleném bucketu https://github.com/tisnik/go-root/blob/master/article46/mi­nio04/minio4.go
5 minio5.go podrobnější informace o objektech ve zvoleném bucketu https://github.com/tisnik/go-root/blob/master/article46/mi­nio05/minio5.go
6 minio6.go uložení objektu z bucketu do lokálního souboru https://github.com/tisnik/go-root/blob/master/article46/mi­nio06/minio6.go
7 minio7.go přečtení obsahu objektu s textem https://github.com/tisnik/go-root/blob/master/article46/mi­nio07/minio7.go
8 minio8.go alternativní způsob přístupu k obsahu objektu https://github.com/tisnik/go-root/blob/master/article46/mi­nio08/minio8.go
9 minio9.go poslání dat do Minia s jejich uložením do objektu https://github.com/tisnik/go-root/blob/master/article46/mi­nio09/minio9.go
10 minio10.go kopie objektu v rámci Minia bez jejich přesunu na lokální počítač https://github.com/tisnik/go-root/blob/master/article46/mi­nio10/minio10.go

20. Odkazy na Internetu

  1. Stránky projektu MinIO
    https://min.io/
  2. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  3. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  4. Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
    https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/
  5. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  6. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  7. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  8. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  9. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  10. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  11. Ginkgo
    http://onsi.github.io/ginkgo/
  12. Gomega
    https://onsi.github.io/gomega/
  13. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  14. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  15. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  16. Balíček goexpect
    https://github.com/google/goexpect
  17. Balíček go-expect
    https://github.com/Netflix/go-expect
  18. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  19. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  20. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  21. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  22. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  23. goblin na GitHubu
    https://github.com/franela/goblin
  24. Mocha framework
    https://mochajs.org/
  25. frisby na GitHubu
    https://github.com/verdverm/frisby
  26. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  27. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  28. Cucumber for golang
    https://github.com/DATA-DOG/godog
  29. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  30. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  31. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  32. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  33. xml.org
    http://www.xml.org/
  34. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  35. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  36. JSON to YAML
    https://www.json2yaml.com/
  37. Data Format Converter
    https://toolkit.site/format.html
  38. Viper na GitHubu
    https://github.com/spf13/viper
  39. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  40. The fantastic ORM library for Golang
    http://gorm.io/
  41. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  42. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  43. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  44. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  45. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  46. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  47. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  48. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  49. Sémantické verzování
    https://semver.org/
  50. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  51. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  52. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  53. Modules
    https://github.com/golang/go/wi­ki/Modules
  54. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  55. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  56. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  57. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  58. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  59. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  60. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  61. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  62. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  63. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  64. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  65. gopls
    https://github.com/golang/go/wi­ki/gopls
  66. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  67. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  68. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  69. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  70. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  71. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  72. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  73. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  74. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  75. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  76. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  77. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  78. Package trace
    https://golang.org/pkg/runtime/trace/
  79. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  80. Command trace
    https://golang.org/cmd/trace/
  81. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  82. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  83. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  84. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  85. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  86. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  87. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  88. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  89. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  90. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  91. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  92. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  93. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  94. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  95. The LLDB Debugger
    http://lldb.llvm.org/
  96. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  97. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  98. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  99. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  100. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  101. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  102. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  103. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  104. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  105. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  106. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  107. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  108. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  109. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  110. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  111. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  112. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  113. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  114. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  115. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  116. go-cron
    https://github.com/rk/go-cron
  117. gocron
    https://github.com/jasonlvhit/gocron
  118. clockwork
    https://github.com/whiteShtef/cloc­kwork
  119. clockwerk
    https://github.com/onatm/clockwerk
  120. JobRunner
    https://github.com/bamzi/jobrunner
  121. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  122. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  123. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  124. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  125. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  126. go-prompt
    https://github.com/c-bata/go-prompt
  127. readline
    https://github.com/chzyer/readline
  128. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  129. go-readline
    https://github.com/fiorix/go-readline
  130. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  131. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  132. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  133. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  134. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  135. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  136. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  137. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  138. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  139. Editline Library (libedit)
    http://thrysoee.dk/editline/
  140. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  141. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  142. WinEditLine
    http://mingweditline.sourceforge.net/
  143. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  144. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  145. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  146. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  147. history(3) – Linux man page
    https://linux.die.net/man/3/history
  148. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  149. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  150. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  151. Balíček ogletest
    https://github.com/jacobsa/ogletest
  152. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  153. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  154. package testing
    https://golang.org/pkg/testing/
  155. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  156. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  157. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  158. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  159. GoConvey
    http://goconvey.co/
  160. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  161. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  162. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  163. package gg
    https://godoc.org/github.com/fo­gleman/gg
  164. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  165. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  166. The Go image package
    https://blog.golang.org/go-image-package
  167. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  168. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  169. YAML
    https://yaml.org/
  170. edn
    https://github.com/edn-format/edn
  171. Smile
    https://github.com/FasterXML/smile-format-specification
  172. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  173. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  174. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  175. Introducing JSON
    http://json.org/
  176. Package json
    https://golang.org/pkg/encoding/json/
  177. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  178. Go by Example: JSON
    https://gobyexample.com/json
  179. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  180. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  181. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  182. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  183. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  184. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  185. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  186. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  187. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  188. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  189. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  190. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  191. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  192. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  193. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  194. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  195. Algorithms to Go
    https://yourbasic.org/
  196. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  197. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  198. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  199. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  200. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  201. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  202. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  203. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  204. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  205. The Go Programming Language (home page)
    https://golang.org/
  206. GoDoc
    https://godoc.org/
  207. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  208. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  209. The Go Programming Language Specification
    https://golang.org/ref/spec
  210. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  211. Package builtin
    https://golang.org/pkg/builtin/
  212. Package fmt
    https://golang.org/pkg/fmt/
  213. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  214. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  215. Learning Go
    https://www.miek.nl/go/
  216. Go Bootcamp
    http://www.golangbootcamp.com/
  217. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  218. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  219. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  220. The Go Blog
    https://blog.golang.org/
  221. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  222. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  223. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  224. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  225. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  226. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  227. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  228. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  229. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  230. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  231. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  232. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  233. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  234. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  235. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  236. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  237. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  238. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  239. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  240. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  241. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  242. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  243. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  244. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  245. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  246. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  247. Go vs. Python
    https://www.peterbe.com/plog/govspy
  248. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  249. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  250. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  251. Go by Example: Slices
    https://gobyexample.com/slices
  252. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  253. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  254. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  255. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  256. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  257. nils In Go
    https://go101.org/article/nil.html
  258. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  259. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  260. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  261. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  262. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  263. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  264. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  265. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  266. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  267. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  268. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  269. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  270. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  271. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  272. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  273. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  274. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  275. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  276. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  277. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  278. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  279. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  280. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  281. Selectors
    https://golang.org/ref/spec#Selectors
  282. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  283. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  284. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  285. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  286. Part 21: Goroutines
    https://golangbot.com/goroutines/
  287. Part 22: Channels
    https://golangbot.com/channels/
  288. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  289. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  290. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  291. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  292. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  293. Control Structures
    https://www.golang-book.com/books/intro/5
  294. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  295. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  296. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  297. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  298. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  299. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  300. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  301. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  302. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  303. Effective Go
    https://golang.org/doc/ef­fective_go.html
  304. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  305. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  306. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  307. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  308. Nils in Go
    https://www.doxsey.net/blog/nils-in-go