Hlavní navigace

Další možnosti nabízené projektem MinIO

V dalším článku se seznámíme s vlastnostmi této alternativy k AWS S3. Ukážeme si přístup do úložiště Minia z jazyka Python, popíšeme si řízení přístupu k objektům a řekneme si, které vlastnosti S3 Minio nepodporuje.
Pavel Tišnovský
Doba čtení: 48 minut

Sdílet

11. Programové přečtení obsahu vybraného objektu

12. Načtení textového obsahu z vybraného objektu

13. Uložení obsahu souboru do Minia

14. Přístupová práva k bucketu

15. Přečtení informací o přístupových právech k bucketu

16. Změna přístupových práv k bucketu

17. Přiřazení verze k objektům, uložení více verzí objektu se stejným jménem

18. Konfigurace životního cyklu objektů

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

20. Odkazy na Internetu

1. Další možnosti nabízené projektem MinIO

V úvodním článku o projektu MinIO, který vyšel v úterý, jsme se seznámili se základními koncepty, na nichž je toto objektové úložiště postaveno. Taktéž jsme si řekli, že MinIO lze ovládat podobným způsobem jako známé cloudové úložiště AWS S3 (S3=Simple Storage Service) a je tak možné tento projekt nasadit do privátního clusteru, použít ho jako lokální úložiště, využít MinIO pro vývoj a testování aplikací, které v reálném nasazení použijí AWS S3 atd. atd.

Všechny demonstrační příklady ukázané minule byly vyvinuty v programovacím jazyku Go, což však v žádném případě neznamená, že se jedná o jediný jazyk, ve kterém mohou být psány aplikace, které k Miniu přistupují. K dispozici je šest oficiálních klientů pro šest platforem:

Poznámka: na tomto místě je dobré upozornit na to, že mezi jednotlivými klienty existují rozdíly. Například některé metody dostupné pro Go nenajdeme v klientovi pro Python atd. Ovšem všechny základní operace popsané dnes i minule jsou podporovány napříč jazyky/platformami.

Obrázek 1: Služba MinIO aktivně upozorňuje administrátory ve chvíli, kdy se pokouší pracovat se starší verzí, což nastalo i v našem případě – verze, která byla ještě v době vydání první verze článku aktuální, je dnes již považována za zastaralou a je nabídnuta možnost reinstalace (která je mimochodem bezproblémová).

2. Spuštění Minia

Všechny dnes ukázané demonstrační příklady vyžadují, aby na systému běžela instance služby Minia s nastaveným úložištěm. Minio lze spustit z Dockeru (k dispozici je oficiální obraz) či přímo s využitím binárního spustitelného souboru – viz popis instalace uvedený minule:

$ ./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

3. Nejdůležitější funkce a metody z SDK Minia pro Go

V úvodním článku o Miniu jsme se seznámili s některými funkcemi a metodami nabízenými SDK určeným pro programovací jazyk Go. Připomeňme si ve stručnosti, o jaké funkce a metody se jednalo:

# Funkce/metoda Stručný popis
1 minio.New vytvoří novou instanci klienta, který reprezentuje připojení k Miniu (ostatní metody tuto instanci používají jako příjemce – receiver)
2 minio.Client.ListBuckets získání seznamu všech dostupných bucketů
3 minio.Client.ListObjects získání seznamu objektů v určitém bucketu
4 minio.Client.GetObject přečtení obsahu vybraného objektu, obsah je reprezentován objektem Reader
5 minio.Client.FGetObject uložení obsahu vybraného objektu z úložiště do lokálního souboru
6 minio.Client.FPutObject kopie dat ze souboru do vybraného objektu v úložišti
7 minio.Client.CopyObject kopie objektu v rámci úložiště (i mezi různými buckety)

Funkce minio.New vytvoří novou instanci klienta, který reprezentuje připojení k Miniu (tedy ke službě obsluhující úložiště). Je možné, aby se v jedné aplikaci použilo větší množství klientů, protože se například může jednat o implementaci message brokera s více úložišti, synchronizační službu atd.:

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

Dále jsme si popsali metodu nazvanou minio.Client.ListBucket, která vrátí seznam všech dostupných bucketů. Použití této metody je většinou snadné (i když je nutné kontrolovat, zda nedošlo k chybě):

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)
}

Třetí důležitou metodou je metoda pojmenovaná minio.Client.ListObjects sloužící pro získání seznamu objektů v určitém bucketu. Ovšem tato metoda nabízí i další možnosti použití, protože lze specifikovat prefix klíče objektu a taktéž příznak, zda se má vyhledávání provádět rekurzivně. Navíc tato metoda používá i kanál (channel) pro oznámení, že již došlo k ukončení vyhledávání:

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)
}

Další metoda se jmenuje minio.Client.FGetObject a slouží pro přečtení obsahu vybraného objektu a uložení tohoto obsahu do lokálního souboru:

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

Opakem metody minio.Client.FGetObject je metoda minio.Client.FPutObject, která naopak obsah lokálního souboru přenese do úložiště pod zadaným jménem bucketu a klíče (=jména objektu):

length, err := minioClient.FPutObject("foo", "minio9.go", "minio9.go", minio.PutObjectOptions{
        ContentType: "text/plain;charset=UTF-8",
})
if err != nil {
        fmt.Println(err)
}

A nakonec nesmíme zapomenout na metodu minio.Client.GetObject, která slouží pro přečtení obsahu objektu. Z pohledu aplikace se vrací objekt implementující rozhraní Reader, takže lze použít prakticky všechny funkce, které s Readerem pracují (včetně komprimace streamu atd. atd.):

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)
}

4. Přečtení informací o objektu uloženém v Miniu

V některých případech je nutné zjistit informace o objektu, který je v Miniu uložen. Připomeňme si, že objekt je určen jménem bucketu a klíčem (což je taktéž jméno), přičemž na jména bucketů jsou kladena podobná omezení, jako na URL či cesty v adresářové struktuře. Samotný objekt přitom není reprezentován pouze svým obsahem (sekvence bajtů), ale i dalšími metadaty, typicky příznaky přístupu, časovými razítky, určením typu dat (content-type) a v S3 i verzí (což je ovšem vlastnost, kterou Minio prozatím nepodporuje). Pro přečtení těchto informací slouží metoda minio.Client.StatObject:

# Funkce/metoda Stručný popis
1 minio.Client.StatObject získání podrobnějších informací o objektu

Přečtení informací o objektu je jednoduché, pouze nesmíme zapomenout na správnou reakci na případný chybový stav:

info, err := minioClient.StatObject(bucket, name, minio.StatObjectOptions{})
if err != nil {
        log.Fatalln(err)
}

Nejprve se podívejme na zdrojový kód demonstračního příkladu, posléze si ukážeme, jaké informace jsme získali:

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 getObjectInfo(minioClient *minio.Client, bucket string, name string) {
        fmt.Printf("Info for object %s in bucket %s\n", name, bucket)
 
        info, err := minioClient.StatObject(bucket, name, minio.StatObjectOptions{})
        if err != nil {
                log.Fatalln(err)
        }
 
        fmt.Printf("Key:          %s\nSize:         %d\nTag:          %s\nContent-type: %s\n", info.Key, info.Size, info.ETag, info.ContentType)
}
 
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)
        fmt.Println()
        getObjectInfo(minioClient, "foo", "minio9.go")
        fmt.Println()
        getObjectInfo(minioClient, "foo", "logos.jpg")
        fmt.Println()
        getObjectInfo(minioClient, "foo", "something_else")
}

Po spuštění tohoto příkladu by se měly zobrazit informace o dvou existujících objektech se jmény „minio9.go“ a „logos.jpg“ a o neexistujícím objektu „something_else“:

$ ./minio11 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/17 10: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}
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}
 
Info for object minio9.go in bucket foo
Key:          minio9.go
Size:         1304
Tag:          34a37b73d9402b51201f42569f506d4b-1
Content-type: text/plain;charset=UTF-8
 
Info for object logos.jpg in bucket foo
Key:          logos.jpg
Size:         48913
Tag:          f95e4a85dafc56313883f8571cfc8143-1
Content-type: image/jpeg
 
Info for object something_else in bucket foo
2019/12/17 10:28:06 The specified key does not exist.
Poznámka: povšimněte si, že se zobrazil klíč objektu, jeho tag (ve skutečnosti MD5 heš) a především pak typ obsahu, který je ovšem nutné nastavit při zakládání objektu.

5. Přístup do úložiště Minia z aplikací naprogramovaných v Pythonu

Ve druhé části dnešního článku si ukážeme, jakým způsobem lze přistupovat do úložiště Minia z aplikací, které jsou naprogramované v Pythonu. Uvidíme, že až na několik rozdílů je přístup z Pythonu prakticky totožný jako přístup z aplikací naprogramovaných v jazyku Go, pochopitelně s přihlédnutím k tomu, že v Pythonu se používají odlišné jmenné konvence, než je tomu v Go. Taktéž se liší způsob zpracování chyb – v Pythonu se používají klasické výjimky, zatímco Go se spoléhá na struktury typu error.

Před spuštěním demonstračních příkladů zmíněných v navazujících kapitolách je nutné mít nainstalován Pythonovský balíček nazvaný jednoduše minio. Tento balíček je dostupný na PyPi, takže je jeho instalace jednoduchá. Použijeme nástroj pip3 určený pro Python 3 (protože pro Python 2 se již odpočítává posledních několik dnů jeho oficiální podpory):

$ pip3 install --user minio
 
Collecting minio
  Downloading https://files.pythonhosted.org/packages/14/46/60bff78df1b112cc50f95c5ffb2e14aaf9aa279a5219845b55c56f214383/minio-5.0.5-py2.py3-none-any.whl      (62kB)
      100% |████████████████████████████████| 71kB 1.7MB/s
      Requirement already satisfied: certifi in /usr/lib/python3.7/site-packages (from minio)
      Requirement already satisfied: urllib3 in /usr/lib/python3.7/site-packages (from minio)
      Requirement already satisfied: pytz in /usr/lib/python3.7/site-packages (from minio)
      Requirement already satisfied: python-dateutil in ./.local/lib/python3.7/site-packages (from minio)
      Requirement already satisfied: six>=1.5 in ./.local/lib/python3.7/site-packages (from python-dateutil->minio)
      Installing collected packages: minio
      Successfully installed minio-5.0.5

Většina funkcí a metod, které jsme si již popsali v souvislosti s SDK pro programovací jazyk Go, existuje i v Pythonu. Ovšem kvůli tomu, že jmenné konvence jsou v Pythonu odlišné a liší se i možnosti volání funkcí/metod (nepovinné parametry atd.), je ovládání Minia z Pythonu poněkud rozdílné. Ostatně se stačí podívat na následující tabulku s porovnáním jmen funkcí/metod v jazyku Go a jazyku Python:

# Funkce/metoda v Go Funkce/metoda v Pythonu
1 minio.New (konstruktor) minio.Minio (de facto konstruktor)
2 minio.Client.ListBuckets minio.Minio.list_buckets
3 minio.Client.ListObjects minio.Minio.list_objects
4 minio.Client.GetObject minio.Minio.get_object
5 minio.Client.FGetObject minio.Minio.fget_object
6 minio.Client.FPutObject minio.Minio.fput_object
7 minio.Client.CopyObject minio.Minio.copy_object
8 minio.Client.StatObject minio.Minio.stat_object
Poznámka: v předchozí tabulce jsou vypsány pouze ty funkce, které jsou skutečně v demonstračních příkladech použity.

6. Vytvoření instance klienta pro připojení k Miniu

Nejprve si ukažme aplikaci, které pouze vytvoří instanci klienta sloužícího pro připojení k Miniu. Jedná se o obdobu prvního demonstračního příkladu z předchozího článku:

from minio import Minio
from minio.error import ResponseError
 
endpoint = "127.0.0.1:9000"
accessKeyID = "3V8WMANF061SGOIVR7AA"
secretAccessKey = "AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE"
useSSL = True
 
minioClient = Minio(endpoint,
                    access_key=accessKeyID,
                    secret_key=secretAccessKey,
                    secure=useSSL)
 
print(minioClient)

Po spuštění se pouze zobrazí základní informace o nově vytvořeném objektu, a to bez toho, aby se klient pokoušel o připojení k Miniu:

$ python3 minio1.py
 
<minio.api.Minio object at 0x7fa8955c5550>

7. Nastavení údajů nutných pro připojení k Miniu z příkazového řádku

Předchozí demonstrační příklad náležitě 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 argparse, který se však používá odlišným způsobem než balíček flag v jazyce Go:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
 
print(minioClient)

Opět si můžeme ukázat chování příkladu po jeho spuštění, tentokrát se specifikací všech potřebných údajů:

$ python3 minio2.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl
 
<minio.api.Minio object at 0x7fec0933c710>

8. Vytištění všech bucketů, které jsou klientovi dostupné

Třetí demonstrační příklad naprogramovaný v Pythonu 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 metodu list_buckets, která vrátí seznam bucketů (chyba je zde hlášena formou výjimky, na rozdíl od Go):

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
def list_buckets(minioClient):
    buckets = minioClient.list_buckets()
    for bucket in buckets:
        print(bucket.name, bucket.creation_date)
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
list_buckets(minioClient)

Příklad výstupu pro platné klíče a existující úložiště:

$ python3 minio3.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl
 
bar 2019-12-14 10:30:57.918000+00:00
foo 2019-12-14 17:13:38.282000+00:00

Chyba v případě, že je zadán neplatný klíč:

$ python3 minio3.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIl --disable-ssl
 
Traceback (most recent call last):
  File "minio3.py", line 30, in
    list_buckets(minioClient)
  File "minio3.py", line 8, in list_buckets
    buckets = minioClient.list_buckets()
  File "/home/ptisnovs/.local/lib/python3.7/site-packages/minio/api.py", line 385, in list_buckets
    raise ResponseError(response, method).get_exception()
minio.error.SignatureDoesNotMatch: SignatureDoesNotMatch: message: The request signature we calculated does not match the signature you provided.

Chyba v případě, že vyžadujeme připojení přes SSL/TSL, ovšem služba je nakonfigurována odlišným způsobem:

$ python3 minio3.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --enable-ssl
 
Traceback (most recent call last):
...
...
...
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='127.0.0.1', port=9000): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:866)'),))

9. Zjištění základních informací o objektech uložených do zvoleného bucketu

Další skript je obdobou třetího příkladu z předchozího článku. Po jeho spuštění se vypíšou všechny objekty uložené ve zvoleném bucketu. Pro získání seznamu objektů se používá metoda list_objects objektu typu Minio:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
  
def list_buckets(minioClient):
    buckets = minioClient.list_buckets()
    for bucket in buckets:
        print(bucket.name, bucket.creation_date)
 
 
def list_objects(minioClient, bucket):
    print("List of objects for bucket:", bucket)
    objects = minioClient.list_objects(bucket, prefix="", recursive=False)
    for object in objects:
        print(object.bucket_name, object.last_modified, object.etag,
              object.size, object.object_name)
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
list_buckets(minioClient)
list_objects(minioClient, "foo")

Následuje ukázka, jakým způsobem se informace o objektech vypíšou. Nejprve je vypsáno jméno bucketu, potom čas modifikace objektu, MD5 heš, velikost objektu v bajtech a nakonec i jméno objektu:

$ python3 minio4.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl
 
bar 2019-12-14 10:30:57.918000+00:00
foo 2019-12-14 17:13:38.282000+00:00
 
List of objects for bucket: foo
foo 2019-12-14 12:01:10.533000+00:00 f95e4a85dafc56313883f8571cfc8143-1 48913 logos.jpg
foo 2019-12-14 17:13:38.282000+00:00 f3025fabac6f9648eb6408911379b595-1 1304 minio10.go
foo 2019-12-14 17:09:38.517000+00:00 34a37b73d9402b51201f42569f506d4b-1 1304 minio9.go
foo 2019-12-14 16:10:38.411000+00:00 f2042bf5780d07253480fb8c64c60850-1 56 t.go

10. Přečtení objektu z Minia a uložení jeho obsahu do souboru

V dalším demonstračním příkladu se setkáme s metodou nazvanou fget_object, která je určena pro přečtení obsahu objektu a jeho uložení do lokálního souboru. Této metodě, která je obdobou Go metody FGetObject, je nutné předat název bucketu, klíč objektu, jméno lokálního souboru a popř. další parametry předané v nepovinných argumentech:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
 
minioClient.fget_object("foo", "logos.jpg", "logos.jpg")

Po spuštění tohoto příkladu by se měl v aktuálním adresáři objevit soubor se jménem „logos.go“ obsahující šestici log programovacího jazyka Go:

$ python3 minio6.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl

Pro jistotu se o výsledku přesvědčíme:

$ 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

11. Programové přečtení obsahu vybraného objektu

Ve chvíli, kdy je nutné obsah objektu z úložiště přečíst a nějakým způsobem zpracovat, použijeme namísto metody fget_object metodu nazvanou get_object (tedy bez „f“ na začátku). Tato metoda umožňuje, aby se s její návratovou hodnotou pracovalo stejným způsobem, jakoby se jednalo o otevřený soubor:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
def print_object(minioClient, bucket, object_name):
    try:
        data = minioClient.get_object(bucket, object_name)
        for line in data.readlines():
            print(line)
    except ResponseError as err:
        print(err)
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)

print_object(minioClient, "foo", "t.go")

Po spuštění tohoto demonstračního příkladu ovšem zjistíme, že je obsah zpracováván jako pole bajtů, nikoli jako skutečné (Unicode) řetězce:

$ python3 minio7.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl
 
b'package main\n'
b'\n'
b'func main() {\n'
b'\tprintln(`foo "bar" baz`)\n'
b'}\n'

12. Načtení textového obsahu z vybraného objektu

Ve chvíli, kdy objekt obsahuje textová data, je nutné provést (alespoň v Pythonu 3) jejich dekódování do řetězce, a to konkrétně s využitím metody decode. V případě objektu obsahujícího zdrojové kódy v Go je situace jednoduchá, protože kódování je definitoricky nastaveno na UTF-8:

data = minioClient.get_object(bucket, object_name)
for line in data.readlines():
    print(line.decode('utf-8')[:-1])
Poznámka: pomocí [:-1] zajistíme odstranění konce řádku, neboť ten je tištěn automaticky funkcí print.

Dekódování by mělo zajistit, že se obsah vybraného objektu skutečně zpracuje (zde jen vytiskne) jako sekvence textových řádků a nikoli sekvence polí bajtů:

$ python3 minio7B.py --accessKeyID=3V8WMANF061SGOIVR7AA --secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE --disable-ssl
package main
 
func main() {
        println(`foo "bar" baz`)
}

Opět se podívejme na kompletní zdrojový kód demonstračního příkladu, který výše popsané dekódování používá:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
def print_object(minioClient, bucket, object_name):
    try:
        data = minioClient.get_object(bucket, object_name)
        for line in data.readlines():
            print(line.decode('utf-8')[:-1])
    except ResponseError as err:
        print(err)
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
 
print_object(minioClient, "foo", "t.go")

13. Uložení obsahu souboru do Minia

V posledním příkladu naprogramovaném v Pythonu použijeme metodu fput_object pro uložení obsahu lokálního souboru do úložiště Minia. V tomto konkrétním případě do Minia pošleme přímo zdrojové kódy spuštěného skriptu:

import argparse
 
from minio import Minio
from minio.error import ResponseError
 
 
parser = argparse.ArgumentParser()
parser.add_argument("--endpoint", default="127.0.0.1:9000",
                    help="MinIO service endpoint")
parser.add_argument("--accessKeyID", default="",
                    help="Access key ID for MinIO")
parser.add_argument("--secretAccessKey", default="",
                    help="Secret access key for MinIO")
parser.add_argument("--enable-ssl", dest="useSSL", action="store_true",
                    help="Use SSL for communication with MinIO")
parser.add_argument("--disable-ssl", dest="useSSL", action="store_false",
                    help="Don't SSL for communication with MinIO")
args = parser.parse_args()
 
minioClient = Minio(args.endpoint,
                    access_key=args.accessKeyID,
                    secret_key=args.secretAccessKey,
                    secure=args.useSSL)
 
minioClient.fput_object("foo", "minio9.py", "minio9.py", content_type="text/plain")

14. Přístupová práva k bucketu

Z webového rozhraní lze nastavit základní přístupová práva k vybranému bucketu:

Obrázek 2: Dvojice bucketů vytvořených v rámci předchozího článku.

Obrázek 3: Dialog pro nastavení přístupových práv k bucketu může být poněkud matoucí. Poslední řádek není ukládán, slouží pro nastavení nových práv.

Obrázek 4: Takto vypadají konkrétně nastavená práva.

Ve skutečnosti jsou ovšem přístupová práva přiřazována pro jednotlivé operace:

  1. PutObject
  2. DeleteObject
  3. GetBucketLocation
  4. ListBucketMultipartUploads
  5. AbortMultipartUpload

atd. Tato práva lze nastavit metodou SetBucketPolicy a přečíst metodou GetBucketPolicy, což je téma, kterému se budeme věnovat v navazujících dvou kapitolách.

15. Přečtení informací o přístupových právech k bucketu

Ve třetí části článku se budeme věnovat některým dalším užitečným funkcím a metodám, tentokrát opět při použití klienta resp. aplikace vytvářené v programovacím jazyce Go. Použijeme zde metodu nazvanou GetBucketPolicy, která vrátí informace o přístupových právech k objektům v zadaném bucketu:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func printBucketPolicy(minioClient *minio.Client, bucket string) {
        fmt.Printf("Policy for bucket: %s\n", bucket)
        policy, err := minioClient.GetBucketPolicy(bucket)
        if err != nil {
                log.Fatalln(err)
        }
        fmt.Println(policy)
        fmt.Println()
}
 
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)
 
        printBucketPolicy(minioClient, "foo")
        printBucketPolicy(minioClient, "readonly")
        printBucketPolicy(minioClient, "writeonly")
        printBucketPolicy(minioClient, "readwrite")
}

Po spuštění příkladu se zobrazí informace o bucketem, které jsme vytvořili a nastavili v rámci předchozí kapitoly:

$ ./minio13 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/18 09:44:27 &minio.Client{endpointURL:(*url.URL)(0xc000176000), credsProvider:(*credentials.Credentials)(0xc0000b47e0), overrideSignerType:0, appInfo:struct { appName string; appVersion string }{appName:"", appVersion:""}, secure:false, httpClient:(*http.Client)(0xc00009b8c0), bucketLocCache:(*minio.bucketLocationCache)(0xc0000a6780), isTraceEnabled:false, traceErrorsOnly:false, traceOutput:io.Writer(nil), s3AccelerateEndpoint:"", region:"", random:(*rand.Rand)(0xc00009b920), lookup:0}
Policy for bucket: foo
 
 
Policy for bucket: readonly
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::readonly"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::readonly/*"]}]}
 
Policy for bucket: writeonly
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::writeonly"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::writeonly/*"]}]}
 
Policy for bucket: readwrite
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::readwrite"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:DeleteObject","s3:GetObject","s3:ListMultipartUploadParts","s3:PutObject","s3:AbortMultipartUpload"],"Resource":["arn:aws:s3:::readwrite/*"]}]}

Vidíme, že práva jsou vrácena ve formě řetězce obsahujícího strukturu reprezentovanou v JSONu. Tuto strukturu je výhodné poslat do filtru pro pretty-printing JSONu, aby byly výsledky čitelnější.

Bucket „readonly“:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::readonly"
      ]
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::readonly/*"
      ]
    }
  ]
}

Bucket „writeonly“:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": [
        "arn:aws:s3:::writeonly"
      ]
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::writeonly/*"
      ]
    }
  ]
}

Bucket „readwrite“:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": [
        "arn:aws:s3:::readwrite"
      ]
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "*"
        ]
      },
      "Action": [
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListMultipartUploadParts",
        "s3:PutObject",
        "s3:AbortMultipartUpload"
      ],
      "Resource": [
        "arn:aws:s3:::readwrite/*"
      ]
    }
  ]
}
Poznámka: povšimněte si, že práva jsou nastavována pro jednotlivé konkrétní operace, nejedná se tedy o pouhé čtení či zápis.

16. Změna přístupových práv k bucketu

Změnu přístupových práv k bucketu na úrovni jednotlivých operací lze realizovat metodou SetBucketPolicy. Této metodě je nutné předat řetězec obsahující validní JSON se specifikací povolených operací. Pro jednoduchost tento JSON vytvoříme skutečně v řetězci, ovšem v praxi je lepší použít marshalling (zajímavé je, že tato operace není přímo podporována v SDK pro Minio v Go):

policyReadOnly := `{"Version": "2012-10-17","Statement": [{"Effect":"Allow","Principal":{"AWS": ["*"]},"Resource": ["arn:aws:s3:::readonly/*"],"Sid": "", "Action":["s3:GetObject","s3:PutObject"]}]}`
 
err = minioClient.SetBucketPolicy("readonly", policyReadOnly)
if err != nil {
        fmt.Println(err)
        return
}

V dalším příkladu je tato možnost ukázána – nastavíme v něm nová práva pro bucket „readonly“, který byl původně určen skutečně pouze pro čtení:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func printBucketPolicy(minioClient *minio.Client, bucket string) {
        fmt.Printf("Policy for bucket: %s\n", bucket)
        policy, err := minioClient.GetBucketPolicy(bucket)
        if err != nil {
                log.Fatalln(err)
        }
        fmt.Println(policy)
        fmt.Println()
}
 
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)
 
        policyFoo := `{"Version": "2012-10-17","Statement": [{"Effect":"Allow","Principal":{"AWS": ["*"]},"Resource": ["arn:aws:s3:::foo/*"],"Sid": "", "Action":["s3:PutObject"]}]}`
        err = minioClient.SetBucketPolicy("foo", policyFoo)
        if err != nil {
                fmt.Println(err)
                return
        }
 
        policyReadOnly := `{"Version": "2012-10-17","Statement": [{"Effect":"Allow","Principal":{"AWS": ["*"]},"Resource": ["arn:aws:s3:::readonly/*"],"Sid": "", "Action":["s3:GetObject","s3:PutObject"]}]}`
        err = minioClient.SetBucketPolicy("readonly", policyReadOnly)
        if err != nil {
                fmt.Println(err)
                return
        }
 
        printBucketPolicy(minioClient, "foo")
        printBucketPolicy(minioClient, "readonly")
        printBucketPolicy(minioClient, "writeonly")
        printBucketPolicy(minioClient, "readwrite")
}

Spuštění příkladu:

$ ./minio14 -accessKeyID=3V8WMANF061SGOIVR7AA -secretAccessKey=AHTM6+74n1Z8DZRZ4V7o83QcnYRnTEVblVb8sIlE
 
2019/12/18 10:09:31 &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}
 
Policy for bucket: foo
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::foo/*"]}]}
 
Policy for bucket: readonly
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject","s3:GetObject"],"Resource":["arn:aws:s3:::readonly/*"]}]}
 
Policy for bucket: writeonly
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::writeonly"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::writeonly/*"]}]}
 
Policy for bucket: readwrite
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket","s3:ListBucketMultipartUploads"],"Resource":["arn:aws:s3:::readwrite"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:AbortMultipartUpload","s3:DeleteObject","s3:GetObject","s3:ListMultipartUploadParts","s3:PutObject"],"Resource":["arn:aws:s3:::readwrite/*"]}]}

17. Přiřazení verze k objektům, uložení více verzí objektu se stejným jménem

V této kapitole si ukážeme, že některé vlastnosti, které uživatelům nabízí AWS S3, prozatím v Miniu nenajdeme. Ostatně si můžeme ocitovat slova jednoho z vývojářů Minia:

„Amazon keeps introducing new features to stay ahead of others. Minio will instead focus on minimalist design. We only need to implement what we see as a core functionality for object storage. „Core“ is defined as bare essential features to store and retrieve objects.“

Jednou z vlastností, kterou v Miniu nenalezneme, je podpora pro „verzování“ objektů, tj. možnost mít jeden objekt uložený ve více verzích. V Miniu je nutné použít jiný přístup, například:

„Idea of application layer versioning is to show that „versioning“ doesn't belong to the core. Saving newer objects with .1, .2 and so on (or with a timestamp suffix) is a simple idea that applications may do it themselves. I proposed to do it inside Minio library just for convenience.“

Na druhou stranu ovšem SDK pro Go umožňuje verzování povolit, ovšem funkční bude pouze při připojení do AWS S3. V dalším příkladu je jeden objekt uložen v deseti verzích:

package main
 
import (
        "flag"
        "fmt"
        "github.com/minio/minio-go/v6"
        "log"
)
 
func createBucket(minioClient *minio.Client, bucket string) {
        err := minioClient.MakeBucket(bucket, "us-east-1")
        if err != nil {
                log.Fatalln(err)
        }
        log.Println("New bucket has been created")
}
 
func enableVersioning(minioClient *minio.Client, bucket string) {
        err := minioClient.EnableVersioning(bucket)
        if err != nil {
                log.Fatalln(err)
        }
        log.Println("Versioning has been enabled")
}
 
func uploadFile(minioClient *minio.Client, bucket string, filename string) {
        length, err := minioClient.FPutObject(bucket, filename, filename, minio.PutObjectOptions{
                ContentType: "text/plain;charset=UTF-8",
        })
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Println("Successfully uploaded bytes: ", length)
}
 
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)
 
        createBucket(minioClient, "versioned")
        enableVersioning(minioClient, "versioned")
 
        for i := 1; i < 10; i++ {
                uploadFile(minioClient, "versioned", "minio13.go")
        }
}

18. Konfigurace životního cyklu objektů

Další vlastnost, kterou v Miniu nelze použít, je konfigurace životního cyklu objektů. V AWS S3 je totiž možné určit, po jakou dobu budou objekty v S3 uložené platné. Je tak možné rozlišit například mezi logy, zdrojovými daty pro analýzu AI/ML, skutečná data (například obsah CRM) atd. Pro úplnost si ukažme, jak se takové nastavení může provést. Tentokrát se nepoužívá formát JSON, ale XML:

Hacking v praxi

<LifecycleConfiguration>
    <Rule>
        <ID>expire-bucket</ID>
    <Prefix></Prefix>
    <Status>Enabled</Status>
    <Expiration>
        <Days>` + strconv.Itoa(days) + `</Days>
    </Expiration>
    </Rule>
</LifecycleConfiguration>`
 

Celý příklad (nefunkční v Miniu):

package main
 
import (
    "flag"
    "github.com/minio/minio-go/v6"
    "log"
    "strconv"
)
 
func createBucket(minioClient *minio.Client, bucket string) {
    err := minioClient.MakeBucket(bucket, "us-east-1")
    if err != nil {
        log.Fatalln(err)
    }
    log.Println("New bucket has been created")
}
 
func setExpiration(minioClient *minio.Client, bucket string, days int) {
    rules := `
<LifecycleConfiguration>
    <Rule>
        <ID>expire-bucket</ID>
    <Prefix></Prefix>
    <Status>Enabled</Status>
    <Expiration>
        <Days>` + strconv.Itoa(days) + `</Days>
    </Expiration>
    </Rule>
</LifecycleConfiguration>`
 
    println(rules)
    err := minioClient.SetBucketLifecycle(bucket, rules)
    if err != nil {
        log.Fatalln(err)
    }
    log.Println("Expiration has been configured")
}
 
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)
 
    //createBucket(minioClient, "temporary")
    setExpiration(minioClient, "temporary", 1)
}

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 minio11 přečtení metainformací o objektu https://github.com/tisnik/go-root/blob/master/article47/mi­nio11/minio11.go
2 minio12 použití metody ComposeObject https://github.com/tisnik/go-root/blob/master/article47/mi­nio12/minio12.go
3 minio13 přečtení informací o přístupových právech k bucketu https://github.com/tisnik/go-root/blob/master/article47/mi­nio13/minio13.go
4 minio14 změna přístupových práv k bucketu https://github.com/tisnik/go-root/blob/master/article47/mi­nio14/minio14.go
5 minio15 povolení verzování objektů https://github.com/tisnik/go-root/blob/master/article47/mi­nio15/minio15.go
6 minio16 přečtení „lifecycle“ bucketu https://github.com/tisnik/go-root/blob/master/article47/mi­nio16/minio16.go
7 minio17 nastavení doby expirace objektů v bucketu https://github.com/tisnik/go-root/blob/master/article47/mi­nio17/minio17.go
       
8 python/minio1.py kostra aplikace, která provede inicializaci klienta služby Minio https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio1.py
9 python/minio2.py zadání parametrů připojení z příkazového řádku https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio2.py
10 python/minio3.py zadání parametrů připojení z příkazového řádku, výpis bucketů https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio3.py
11 python/minio4.py výpis všech objektů ve zvoleném bucketu https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio4.py
12 python/minio6.py uložení objektu z bucketu do lokálního souboru https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio6.py
13 python/minio7.py programové přečtení obsahu vybraného objektu https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio7.py
14 python/minio8.py zpracování objektu s textovým obsahem https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio8.py
15 python/minio9.py uložení obsahu souboru do Minia https://github.com/tisnik/go-root/blob/master/article47/pyt­hon/minio9.py

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